#include "X.h"
#define NEED_EVENTS
#include "Xproto.h"
#include "Xprotostr.h"

#include "misc.h"
#include "regionstr.h"
#include "scrnintstr.h"
#include "gcstruct.h"
#include "windowstr.h"
#include "pixmap.h"
#include "input.h"

#include "dixstruct.h"
#include "mi.h"
#include "Xmd.h"

extern WindowPtr _near WindowTable[];

/*
    this code is highly unlikely.  it is not haile selassie.

    there is some hair here.  we can't just use the window's
clip region as it is, because if we are painting the border,
the border is not in the client area and so we will be excluded
when we validate the GC, and if we are painting a parent-relative
background, the area we want to paint is in some other window.
since we trust the code calling us to tell us to paint only areas
that are really ours, we will temporarily give the window a
clipList the size of the whole screen and an origin at (0,0).
this more or less assumes that ddX code will do translation
based on the window's absolute position, and that ValidateGC will
look at clipList, and that no other fields from the
window will be used.  it's not possible to just draw
in the root because it may be a different depth.

to get the tile to align correctly we set the GC's tile origin to
be the (x,y) of the window's upper left corner, after which we
get the right bits when drawing into the root.

because the clip_mask is being set to None, we may call DoChangeGC with
fPointer set true, thus we no longer need to install the background or
border tile in the resource table.
*/

static RESTYPE ResType = 0;
static int numGCs = 0;
static GCPtr	screenContext[MAXSCREENS];

/*ARGSUSED*/
static void
tossGC (pGC, id)
GCPtr pGC;
GContext id;
{
    screenContext[pGC->pScreen->myNum] = (GCPtr)NULL;
    FreeGC ((pointer)pGC, id);
    numGCs--;
    if (!numGCs)
	ResType = 0;
}

void
miPaintWindow(pWin, prgn, what)
register WindowPtr pWin;
RegionPtr prgn;
int what;
{
    int	status;

    Bool usingScratchGC = FALSE;
    WindowPtr pRoot;
	
#define FUNCTION	0
#define FOREGROUND	1
#define TILE		2
#define FILLSTYLE	3
#define ABSX		4
#define ABSY		5
#define CLIPMASK	6
#define SUBWINDOW	7
#define COUNT_BITS	8

    XID gcval[7];
    XID newValues [COUNT_BITS];

    BITS32 gcmask, mask;
    RegionRec prgnWin;
    DDXPointRec oldCorner;
    BoxRec box;
    GCPtr pGC;
    register int i;
    register BoxPtr pbox;
    register ScreenPtr pScreen = pWin->drawable.pScreen;
    register xRectangle *prect;
    int numRects;

    gcmask = 0;

    if (what == PW_BACKGROUND)
    {
	switch (pWin->backgroundState) {
	case None:
	    return;
	case ParentRelative:
	    (*pWin->parent->drawable.pScreen->PaintWindowBackground)(pWin->parent, prgn, what);
	    return;
	case BackgroundPixel:
	    newValues[FOREGROUND] = pWin->background.pixel;
	    newValues[FILLSTYLE] = FillSolid;
	    gcmask |= GCForeground | GCFillStyle;
	    break;
	case BackgroundPixmap:
	    newValues[TILE] = (XID)pWin->background.pixmap;
	    newValues[FILLSTYLE] = FillTiled;
	    gcmask |= GCTile | GCFillStyle | GCTileStipXOrigin | GCTileStipYOrigin;
	    break;
	}
    }
    else
    {
	if (pWin->borderIsPixel)
	{
	    newValues[FOREGROUND] = pWin->border.pixel;
	    newValues[FILLSTYLE] = FillSolid;
	    gcmask |= GCForeground | GCFillStyle;
	}
	else
	{
	    newValues[TILE] = (XID)pWin->border.pixmap;
	    newValues[FILLSTYLE] = FillTiled;
	    gcmask |= GCTile | GCFillStyle | GCTileStipXOrigin | GCTileStipYOrigin;
	}
    }

    prect = (xRectangle *)ALLOCATE_LOCAL(REGION_NUM_RECTS(prgn) *
					 sizeof(xRectangle));
    if (!prect)
	return;

    newValues[FUNCTION] = GXcopy;
    gcmask |= GCFunction | GCClipMask;

    i = pScreen->myNum;
    pRoot = WindowTable[i];

    if ((pWin->drawable.depth != pRoot->drawable.depth) ||
	(pWin->drawable.bitsPerPixel != pRoot->drawable.bitsPerPixel))
    {
	usingScratchGC = TRUE;
	pGC = GetScratchGC(pWin->drawable.depth, pWin->drawable.pScreen);
	if (!pGC)
	{
	    DEALLOCATE_LOCAL(prect);
	    return;
	}
	/*
	 * mash the clip list so we can paint the border by
	 * mangling the window in place, pretending it
	 * spans the entire screen
	 */
	if (what == PW_BORDER)
	{
	    prgnWin = pWin->clipList;
	    oldCorner.x = pWin->drawable.x;
	    oldCorner.y = pWin->drawable.y;
	    pWin->drawable.x = pWin->drawable.y = 0;
	    box.x1 = 0;
	    box.y1 = 0;
	    box.x2 = pScreen->width;
	    box.y2 = pScreen->height;
	    (*pScreen->RegionInit)(&pWin->clipList, &box, 1);
	    pWin->drawable.serialNumber = NEXT_SERIAL_NUMBER;
	    newValues[ABSX] = pWin->drawable.x;
	    newValues[ABSY] = pWin->drawable.y;
	}
	else
	{
	    newValues[ABSX] = 0;
	    newValues[ABSY] = 0;
	}
    } else {
	/*
	 * draw the background to the root window
	 */
	if (screenContext[i] == (GCPtr)NULL)
	{
	    if (!ResType && !(ResType = CreateNewResourceType(tossGC)))
		return;
	    screenContext[i] = CreateGC((DrawablePtr)pWin, (BITS32) 0,
					(XID *)NULL, &status);
	    if (!screenContext[i])
		return;
	    numGCs++;
	    if (!AddResource(FakeClientID(0), ResType,
			     (pointer)screenContext[i]))
	        return;
	}
	pGC = screenContext[i];
	newValues[SUBWINDOW] = IncludeInferiors;
	newValues[ABSX] = pWin->drawable.x;
	newValues[ABSY] = pWin->drawable.y;
	gcmask |= GCSubwindowMode;
	pWin = pRoot;
    }
    
#ifdef BACKING_STORE
    if (pWin->backStorage)
	(*pWin->drawable.pScreen->DrawGuarantee) (pWin, pGC, GuaranteeVisBack);
#endif

    mask = gcmask;
    gcmask = 0;
    i = 0;
    if (mask & GCFunction &&
	(XID) pGC->alu != newValues[FUNCTION]) {
	    gcmask |= GCFunction;
	    gcval[i++] = newValues[FUNCTION];
	}
    if (mask & GCForeground &&
	(XID) pGC->fgPixel != newValues[FOREGROUND]) {
	    gcmask |= GCForeground;
	    gcval[i++] = newValues[FOREGROUND];
	}
    if (mask & GCFillStyle &&
	(XID) pGC->fillStyle != newValues[FILLSTYLE]) {
	    gcmask |= GCFillStyle;
	    gcval[i++] = newValues[FILLSTYLE];
	}
    if (mask & GCTile &&
	(pGC->tileIsPixel || (XID) pGC->tile.pixmap != newValues[TILE]))
	{
	    gcmask |= GCTile;
	    gcval[i++] = newValues[TILE];
	}
    if (mask & GCTileStipXOrigin &&
	(XID) pGC->patOrg.x != newValues[ABSX]) {
	    gcmask |= GCTileStipXOrigin;
	    gcval[i++] = newValues[ABSX];
	}
    if (mask & GCTileStipYOrigin &&
	(XID) pGC->patOrg.y != newValues[ABSY]) {
	    gcmask |= GCTileStipYOrigin;
	    gcval[i++] = newValues[ABSY];
	}
    if (mask & GCSubwindowMode &&
	(XID) pGC->subWindowMode != newValues[SUBWINDOW]) {
	    gcmask |= GCSubwindowMode;
	    gcval[i++] = newValues[SUBWINDOW];
	}
    if (mask & GCClipMask &&
	(XID) pGC->clientClipType != CT_NONE) {
	    gcmask |= GCClipMask;
	    gcval[i++] = CT_NONE;
	}

    if (gcmask)
        DoChangeGC(pGC, gcmask, gcval, 1);

    if (pWin->drawable.serialNumber != pGC->serialNumber)
	ValidateGC((DrawablePtr)pWin, pGC);

    numRects = REGION_NUM_RECTS(prgn);
    pbox = REGION_RECTS(prgn);
    for (i= numRects; --i >= 0; pbox++, prect++)
    {
	prect->x = pbox->x1 - pWin->drawable.x;
	prect->y = pbox->y1 - pWin->drawable.y;
	prect->width = pbox->x2 - pbox->x1;
	prect->height = pbox->y2 - pbox->y1;
    }
    prect -= numRects;
    (*pGC->ops->PolyFillRect)((DrawablePtr)pWin, pGC, numRects, prect);
    DEALLOCATE_LOCAL(prect);

#ifdef BACKING_STORE
    if (pWin->backStorage)
	(*pWin->drawable.pScreen->DrawGuarantee) (pWin, pGC, GuaranteeNothing);
#endif

    if (usingScratchGC)
    {
	if (what == PW_BORDER)
	{
	    (*pScreen->RegionUninit)(&pWin->clipList);
	    pWin->clipList = prgnWin;
	    pWin->drawable.x = oldCorner.x;
	    pWin->drawable.y = oldCorner.y;
	    pWin->drawable.serialNumber = NEXT_SERIAL_NUMBER;
	}
	FreeScratchGC(pGC);
    }
}
