/*
   Copyright (C) 1999.  The XFree86 Project Inc.

   Written by Mark Vojkovich (mvojkovi@ucsd.edu)

   Pre-fb-write callbacks and RENDER support - Nolan Leake (nolan@vmware.com)
*/

#ifdef HAVE_XORG_CONFIG_H
#include <xorg-config.h>
#endif

#include <X11/X.h>
#include <X11/Xproto.h>
#include "misc.h"
#include "pixmapstr.h"
#include "input.h"
#include <X11/fonts/font.h>
#include "mi.h"
#include "scrnintstr.h"
#include "windowstr.h"
#include "gcstruct.h"
#include "dixfontstr.h"
#include <X11/fonts/fontstruct.h>
#include "xf86.h"
#include "xf86str.h"
#include "shadowfb.h"

#include "picturestr.h"

static Bool ShadowCloseScreen(int i, ScreenPtr pScreen);
static void ShadowCopyWindow(WindowPtr pWin,
                             DDXPointRec ptOldOrg, RegionPtr prgn);
static Bool ShadowCreateGC(GCPtr pGC);

static Bool ShadowEnterVT(int index, int flags);
static void ShadowLeaveVT(int index, int flags);

static void ShadowComposite(CARD8 op,
                            PicturePtr pSrc,
                            PicturePtr pMask,
                            PicturePtr pDst,
                            INT16 xSrc,
                            INT16 ySrc,
                            INT16 xMask,
                            INT16 yMask,
                            INT16 xDst,
                            INT16 yDst, CARD16 width, CARD16 height);

typedef struct {
    ScrnInfoPtr pScrn;
    RefreshAreaFuncPtr preRefresh;
    RefreshAreaFuncPtr postRefresh;
    CloseScreenProcPtr CloseScreen;
    CopyWindowProcPtr CopyWindow;
    CreateGCProcPtr CreateGC;
    ModifyPixmapHeaderProcPtr ModifyPixmapHeader;
    CompositeProcPtr Composite;
    Bool (*EnterVT) (int, int);
    void (*LeaveVT) (int, int);
    Bool vtSema;
} ShadowScreenRec, *ShadowScreenPtr;

typedef struct {
    GCOps *ops;
    GCFuncs *funcs;
} ShadowGCRec, *ShadowGCPtr;

static DevPrivateKeyRec ShadowScreenKeyRec;

#define ShadowScreenKey (&ShadowScreenKeyRec)

static DevPrivateKeyRec ShadowGCKeyRec;

#define ShadowGCKey (&ShadowGCKeyRec)

#define GET_SCREEN_PRIVATE(pScreen) \
    (ShadowScreenPtr)dixLookupPrivate(&(pScreen)->devPrivates, ShadowScreenKey)
#define GET_GC_PRIVATE(pGC) \
    (ShadowGCPtr)dixLookupPrivate(&(pGC)->devPrivates, ShadowGCKey)

#define SHADOW_GC_FUNC_PROLOGUE(pGC)\
    ShadowGCPtr pGCPriv = GET_GC_PRIVATE(pGC);\
    (pGC)->funcs = pGCPriv->funcs;\
    if(pGCPriv->ops)\
        (pGC)->ops = pGCPriv->ops

#define SHADOW_GC_FUNC_EPILOGUE(pGC)\
    pGCPriv->funcs = (pGC)->funcs;\
    (pGC)->funcs = &ShadowGCFuncs;\
    if(pGCPriv->ops) {\
        pGCPriv->ops = (pGC)->ops;\
        (pGC)->ops = &ShadowGCOps;\
    }

#define SHADOW_GC_OP_PROLOGUE(pGC)\
    ShadowScreenPtr pPriv = GET_SCREEN_PRIVATE(pGC->pScreen); \
    ShadowGCPtr pGCPriv = GET_GC_PRIVATE(pGC);\
    GCFuncs *oldFuncs = pGC->funcs;\
    pGC->funcs = pGCPriv->funcs;\
    pGC->ops = pGCPriv->ops

#define SHADOW_GC_OP_EPILOGUE(pGC)\
    pGCPriv->ops = pGC->ops;\
    pGC->funcs = oldFuncs;\
    pGC->ops   = &ShadowGCOps

#define IS_VISIBLE(pWin) (pPriv->vtSema && \
    (((WindowPtr)pWin)->visibility != VisibilityFullyObscured))

#define TRIM_BOX(box, pGC) { \
    BoxPtr extents = &pGC->pCompositeClip->extents;\
    if(box.x1 < extents->x1) box.x1 = extents->x1; \
    if(box.x2 > extents->x2) box.x2 = extents->x2; \
    if(box.y1 < extents->y1) box.y1 = extents->y1; \
    if(box.y2 > extents->y2) box.y2 = extents->y2; \
    }

#define TRANSLATE_BOX(box, pDraw) { \
    box.x1 += pDraw->x; \
    box.x2 += pDraw->x; \
    box.y1 += pDraw->y; \
    box.y2 += pDraw->y; \
    }

#define TRIM_AND_TRANSLATE_BOX(box, pDraw, pGC) { \
    TRANSLATE_BOX(box, pDraw); \
    TRIM_BOX(box, pGC); \
    }

#define BOX_NOT_EMPTY(box) \
    (((box.x2 - box.x1) > 0) && ((box.y2 - box.y1) > 0))

Bool
ShadowFBInit2(ScreenPtr pScreen,
              RefreshAreaFuncPtr preRefreshArea,
              RefreshAreaFuncPtr postRefreshArea)
{
    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
    ShadowScreenPtr pPriv;
    PictureScreenPtr ps = GetPictureScreenIfSet(pScreen);

    if (!preRefreshArea && !postRefreshArea)
        return FALSE;

    if (!dixRegisterPrivateKey(&ShadowScreenKeyRec, PRIVATE_SCREEN, 0))
        return FALSE;

    if (!dixRegisterPrivateKey
        (&ShadowGCKeyRec, PRIVATE_GC, sizeof(ShadowGCRec)))
        return FALSE;

    if (!(pPriv = (ShadowScreenPtr) malloc(sizeof(ShadowScreenRec))))
        return FALSE;

    dixSetPrivate(&pScreen->devPrivates, ShadowScreenKey, pPriv);

    pPriv->pScrn = pScrn;
    pPriv->preRefresh = preRefreshArea;
    pPriv->postRefresh = postRefreshArea;
    pPriv->vtSema = TRUE;

    pPriv->CloseScreen = pScreen->CloseScreen;
    pPriv->CopyWindow = pScreen->CopyWindow;
    pPriv->CreateGC = pScreen->CreateGC;
    pPriv->ModifyPixmapHeader = pScreen->ModifyPixmapHeader;

    pPriv->EnterVT = pScrn->EnterVT;
    pPriv->LeaveVT = pScrn->LeaveVT;

    pScreen->CloseScreen = ShadowCloseScreen;
    pScreen->CopyWindow = ShadowCopyWindow;
    pScreen->CreateGC = ShadowCreateGC;

    pScrn->EnterVT = ShadowEnterVT;
    pScrn->LeaveVT = ShadowLeaveVT;

    if (ps) {
        pPriv->Composite = ps->Composite;
        ps->Composite = ShadowComposite;
    }

    return TRUE;
}

Bool
ShadowFBInit(ScreenPtr pScreen, RefreshAreaFuncPtr refreshArea)
{
    return ShadowFBInit2(pScreen, NULL, refreshArea);
}

/**********************************************************/

static Bool
ShadowEnterVT(int index, int flags)
{
    ScrnInfoPtr pScrn = xf86Screens[index];
    Bool ret;
    ShadowScreenPtr pPriv = GET_SCREEN_PRIVATE(pScrn->pScreen);

    pScrn->EnterVT = pPriv->EnterVT;
    ret = (*pPriv->EnterVT) (index, flags);
    pPriv->EnterVT = pScrn->EnterVT;
    pScrn->EnterVT = ShadowEnterVT;
    if (ret) {
        pPriv->vtSema = TRUE;
        return TRUE;
    }

    return FALSE;
}

static void
ShadowLeaveVT(int index, int flags)
{
    ScrnInfoPtr pScrn = xf86Screens[index];
    ShadowScreenPtr pPriv = GET_SCREEN_PRIVATE(xf86Screens[index]->pScreen);

    pPriv->vtSema = FALSE;

    pScrn->LeaveVT = pPriv->LeaveVT;
    (*pPriv->LeaveVT) (index, flags);
    pPriv->LeaveVT = pScrn->LeaveVT;
    pScrn->LeaveVT = ShadowLeaveVT;
}

/**********************************************************/

static Bool
ShadowCloseScreen(int i, ScreenPtr pScreen)
{
    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
    ShadowScreenPtr pPriv = GET_SCREEN_PRIVATE(pScreen);
    PictureScreenPtr ps = GetPictureScreenIfSet(pScreen);

    pScreen->CloseScreen = pPriv->CloseScreen;
    pScreen->CopyWindow = pPriv->CopyWindow;
    pScreen->CreateGC = pPriv->CreateGC;
    pScreen->ModifyPixmapHeader = pPriv->ModifyPixmapHeader;

    pScrn->EnterVT = pPriv->EnterVT;
    pScrn->LeaveVT = pPriv->LeaveVT;

    if (ps) {
        ps->Composite = pPriv->Composite;
    }

    free((pointer) pPriv);

    return (*pScreen->CloseScreen) (i, pScreen);
}

static void
ShadowCopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgn)
{
    ScreenPtr pScreen = pWin->drawable.pScreen;
    ShadowScreenPtr pPriv = GET_SCREEN_PRIVATE(pScreen);
    int num = 0;
    RegionRec rgnDst;

    if (pPriv->vtSema) {
        RegionNull(&rgnDst);
        RegionCopy(&rgnDst, prgn);

        RegionTranslate(&rgnDst,
                        pWin->drawable.x - ptOldOrg.x,
                        pWin->drawable.y - ptOldOrg.y);
        RegionIntersect(&rgnDst, &pWin->borderClip, &rgnDst);
        if ((num = RegionNumRects(&rgnDst))) {
            if (pPriv->preRefresh)
                (*pPriv->preRefresh) (pPriv->pScrn, num, RegionRects(&rgnDst));
        }
        else {
            RegionUninit(&rgnDst);
        }
    }

    pScreen->CopyWindow = pPriv->CopyWindow;
    (*pScreen->CopyWindow) (pWin, ptOldOrg, prgn);
    pScreen->CopyWindow = ShadowCopyWindow;

    if (num) {
        if (pPriv->postRefresh)
            (*pPriv->postRefresh) (pPriv->pScrn, num, RegionRects(&rgnDst));
        RegionUninit(&rgnDst);
    }
}

static void
ShadowComposite(CARD8 op,
                PicturePtr pSrc,
                PicturePtr pMask,
                PicturePtr pDst,
                INT16 xSrc,
                INT16 ySrc,
                INT16 xMask,
                INT16 yMask,
                INT16 xDst, INT16 yDst, CARD16 width, CARD16 height)
{
    ScreenPtr pScreen = pDst->pDrawable->pScreen;
    ShadowScreenPtr pPriv = GET_SCREEN_PRIVATE(pScreen);
    PictureScreenPtr ps = GetPictureScreen(pScreen);
    BoxRec box;
    BoxPtr extents;
    Bool boxNotEmpty = FALSE;

    if (pPriv->vtSema && pDst->pDrawable->type == DRAWABLE_WINDOW) {

        box.x1 = pDst->pDrawable->x + xDst;
        box.y1 = pDst->pDrawable->y + yDst;
        box.x2 = box.x1 + width;
        box.y2 = box.y1 + height;

        extents = &pDst->pCompositeClip->extents;
        if (box.x1 < extents->x1)
            box.x1 = extents->x1;
        if (box.x2 > extents->x2)
            box.x2 = extents->x2;
        if (box.y1 < extents->y1)
            box.y1 = extents->y1;
        if (box.y2 > extents->y2)
            box.y2 = extents->y2;

        if (BOX_NOT_EMPTY(box)) {
            if (pPriv->preRefresh)
                (*pPriv->preRefresh) (pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }
    }

    ps->Composite = pPriv->Composite;
    (*ps->Composite) (op, pSrc, pMask, pDst, xSrc, ySrc,
                      xMask, yMask, xDst, yDst, width, height);
    ps->Composite = ShadowComposite;

    if (pPriv->postRefresh && boxNotEmpty) {
        (*pPriv->postRefresh) (pPriv->pScrn, 1, &box);
    }
}

/**********************************************************/

static void ShadowValidateGC(GCPtr, unsigned long, DrawablePtr);
static void ShadowChangeGC(GCPtr, unsigned long);
static void ShadowCopyGC(GCPtr, unsigned long, GCPtr);
static void ShadowDestroyGC(GCPtr);
static void ShadowChangeClip(GCPtr, int, pointer, int);
static void ShadowDestroyClip(GCPtr);
static void ShadowCopyClip(GCPtr, GCPtr);

GCFuncs ShadowGCFuncs = {
    ShadowValidateGC, ShadowChangeGC, ShadowCopyGC, ShadowDestroyGC,
    ShadowChangeClip, ShadowDestroyClip, ShadowCopyClip
};

extern GCOps ShadowGCOps;

static Bool
ShadowCreateGC(GCPtr pGC)
{
    ScreenPtr pScreen = pGC->pScreen;
    ShadowScreenPtr pPriv = GET_SCREEN_PRIVATE(pScreen);
    ShadowGCPtr pGCPriv = GET_GC_PRIVATE(pGC);
    Bool ret;

    pScreen->CreateGC = pPriv->CreateGC;
    if ((ret = (*pScreen->CreateGC) (pGC))) {
        pGCPriv->ops = NULL;
        pGCPriv->funcs = pGC->funcs;
        pGC->funcs = &ShadowGCFuncs;
    }
    pScreen->CreateGC = ShadowCreateGC;

    return ret;
}

static void
ShadowValidateGC(GCPtr pGC, unsigned long changes, DrawablePtr pDraw)
{
    SHADOW_GC_FUNC_PROLOGUE(pGC);
    (*pGC->funcs->ValidateGC) (pGC, changes, pDraw);
    if (pDraw->type == DRAWABLE_WINDOW)
        pGCPriv->ops = pGC->ops;        /* just so it's not NULL */
    else
        pGCPriv->ops = NULL;
    SHADOW_GC_FUNC_EPILOGUE(pGC);
}

static void
ShadowDestroyGC(GCPtr pGC)
{
    SHADOW_GC_FUNC_PROLOGUE(pGC);
    (*pGC->funcs->DestroyGC) (pGC);
    SHADOW_GC_FUNC_EPILOGUE(pGC);
}

static void
ShadowChangeGC(GCPtr pGC, unsigned long mask)
{
    SHADOW_GC_FUNC_PROLOGUE(pGC);
    (*pGC->funcs->ChangeGC) (pGC, mask);
    SHADOW_GC_FUNC_EPILOGUE(pGC);
}

static void
ShadowCopyGC(GCPtr pGCSrc, unsigned long mask, GCPtr pGCDst)
{
    SHADOW_GC_FUNC_PROLOGUE(pGCDst);
    (*pGCDst->funcs->CopyGC) (pGCSrc, mask, pGCDst);
    SHADOW_GC_FUNC_EPILOGUE(pGCDst);
}

static void
ShadowChangeClip(GCPtr pGC, int type, pointer pvalue, int nrects)
{
    SHADOW_GC_FUNC_PROLOGUE(pGC);
    (*pGC->funcs->ChangeClip) (pGC, type, pvalue, nrects);
    SHADOW_GC_FUNC_EPILOGUE(pGC);
}

static void
ShadowCopyClip(GCPtr pgcDst, GCPtr pgcSrc)
{
    SHADOW_GC_FUNC_PROLOGUE(pgcDst);
    (*pgcDst->funcs->CopyClip) (pgcDst, pgcSrc);
    SHADOW_GC_FUNC_EPILOGUE(pgcDst);
}

static void
ShadowDestroyClip(GCPtr pGC)
{
    SHADOW_GC_FUNC_PROLOGUE(pGC);
    (*pGC->funcs->DestroyClip) (pGC);
    SHADOW_GC_FUNC_EPILOGUE(pGC);
}

/**********************************************************/

static void
ShadowFillSpans(DrawablePtr pDraw,
                GC * pGC,
                int nInit, DDXPointPtr pptInit, int *pwidthInit, int fSorted)
{
    SHADOW_GC_OP_PROLOGUE(pGC);

    if (IS_VISIBLE(pDraw) && nInit) {
        DDXPointPtr ppt = pptInit;
        int *pwidth = pwidthInit;
        int i = nInit;
        BoxRec box;
        Bool boxNotEmpty = FALSE;

        box.x1 = ppt->x;
        box.x2 = box.x1 + *pwidth;
        box.y2 = box.y1 = ppt->y;

        while (--i) {
            ppt++;
            pwidth++;
            if (box.x1 > ppt->x)
                box.x1 = ppt->x;
            if (box.x2 < (ppt->x + *pwidth))
                box.x2 = ppt->x + *pwidth;
            if (box.y1 > ppt->y)
                box.y1 = ppt->y;
            else if (box.y2 < ppt->y)
                box.y2 = ppt->y;
        }

        box.y2++;

        if (!pGC->miTranslate) {
            TRANSLATE_BOX(box, pDraw);
        }
        TRIM_BOX(box, pGC);

        if (BOX_NOT_EMPTY(box)) {
            if (pPriv->preRefresh)
                (*pPriv->preRefresh) (pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }

        (*pGC->ops->FillSpans) (pDraw, pGC, nInit, pptInit, pwidthInit,
                                fSorted);

        if (boxNotEmpty && pPriv->postRefresh)
            (*pPriv->postRefresh) (pPriv->pScrn, 1, &box);
    }
    else
        (*pGC->ops->FillSpans) (pDraw, pGC, nInit, pptInit, pwidthInit,
                                fSorted);

    SHADOW_GC_OP_EPILOGUE(pGC);
}

static void
ShadowSetSpans(DrawablePtr pDraw,
               GCPtr pGC,
               char *pcharsrc,
               DDXPointPtr pptInit, int *pwidthInit, int nspans, int fSorted)
{
    SHADOW_GC_OP_PROLOGUE(pGC);

    if (IS_VISIBLE(pDraw) && nspans) {
        DDXPointPtr ppt = pptInit;
        int *pwidth = pwidthInit;
        int i = nspans;
        BoxRec box;
        Bool boxNotEmpty = FALSE;

        box.x1 = ppt->x;
        box.x2 = box.x1 + *pwidth;
        box.y2 = box.y1 = ppt->y;

        while (--i) {
            ppt++;
            pwidth++;
            if (box.x1 > ppt->x)
                box.x1 = ppt->x;
            if (box.x2 < (ppt->x + *pwidth))
                box.x2 = ppt->x + *pwidth;
            if (box.y1 > ppt->y)
                box.y1 = ppt->y;
            else if (box.y2 < ppt->y)
                box.y2 = ppt->y;
        }

        box.y2++;

        if (!pGC->miTranslate) {
            TRANSLATE_BOX(box, pDraw);
        }
        TRIM_BOX(box, pGC);

        if (BOX_NOT_EMPTY(box)) {
            if (pPriv->preRefresh)
                (*pPriv->preRefresh) (pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }

        (*pGC->ops->SetSpans) (pDraw, pGC, pcharsrc, pptInit,
                               pwidthInit, nspans, fSorted);

        if (boxNotEmpty && pPriv->postRefresh)
            (*pPriv->postRefresh) (pPriv->pScrn, 1, &box);
    }
    else
        (*pGC->ops->SetSpans) (pDraw, pGC, pcharsrc, pptInit,
                               pwidthInit, nspans, fSorted);

    SHADOW_GC_OP_EPILOGUE(pGC);
}

static void
ShadowPutImage(DrawablePtr pDraw,
               GCPtr pGC,
               int depth,
               int x, int y, int w, int h,
               int leftPad, int format, char *pImage)
{
    BoxRec box;
    Bool boxNotEmpty = FALSE;

    SHADOW_GC_OP_PROLOGUE(pGC);

    if (IS_VISIBLE(pDraw)) {
        box.x1 = x + pDraw->x;
        box.x2 = box.x1 + w;
        box.y1 = y + pDraw->y;
        box.y2 = box.y1 + h;

        TRIM_BOX(box, pGC);
        if (BOX_NOT_EMPTY(box)) {
            if (pPriv->preRefresh)
                (*pPriv->preRefresh) (pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }
    }

    (*pGC->ops->PutImage) (pDraw, pGC, depth, x, y, w, h,
                           leftPad, format, pImage);

    if (boxNotEmpty && pPriv->postRefresh)
        (*pPriv->postRefresh) (pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);

}

static RegionPtr
ShadowCopyArea(DrawablePtr pSrc,
               DrawablePtr pDst,
               GC * pGC,
               int srcx, int srcy, int width, int height, int dstx, int dsty)
{
    RegionPtr ret;
    BoxRec box;
    Bool boxNotEmpty = FALSE;

    SHADOW_GC_OP_PROLOGUE(pGC);

    if (IS_VISIBLE(pDst)) {
        box.x1 = dstx + pDst->x;
        box.x2 = box.x1 + width;
        box.y1 = dsty + pDst->y;
        box.y2 = box.y1 + height;

        TRIM_BOX(box, pGC);
        if (BOX_NOT_EMPTY(box)) {
            if (pPriv->preRefresh)
                (*pPriv->preRefresh) (pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }
    }

    ret = (*pGC->ops->CopyArea) (pSrc, pDst,
                                 pGC, srcx, srcy, width, height, dstx, dsty);

    if (boxNotEmpty && pPriv->postRefresh)
        (*pPriv->postRefresh) (pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);

    return ret;
}

static RegionPtr
ShadowCopyPlane(DrawablePtr pSrc,
                DrawablePtr pDst,
                GCPtr pGC,
                int srcx, int srcy,
                int width, int height,
                int dstx, int dsty, unsigned long bitPlane)
{
    RegionPtr ret;
    BoxRec box;
    Bool boxNotEmpty = FALSE;

    SHADOW_GC_OP_PROLOGUE(pGC);

    if (IS_VISIBLE(pDst)) {
        box.x1 = dstx + pDst->x;
        box.x2 = box.x1 + width;
        box.y1 = dsty + pDst->y;
        box.y2 = box.y1 + height;

        TRIM_BOX(box, pGC);
        if (BOX_NOT_EMPTY(box)) {
            if (pPriv->preRefresh)
                (*pPriv->preRefresh) (pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }
    }

    ret = (*pGC->ops->CopyPlane) (pSrc, pDst,
                                  pGC, srcx, srcy, width, height, dstx, dsty,
                                  bitPlane);

    if (boxNotEmpty && pPriv->postRefresh)
        (*pPriv->postRefresh) (pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);

    return ret;
}

static void
ShadowPolyPoint(DrawablePtr pDraw,
                GCPtr pGC, int mode, int nptInit, xPoint * pptInit)
{
    BoxRec box;
    Bool boxNotEmpty = FALSE;

    SHADOW_GC_OP_PROLOGUE(pGC);

    if (IS_VISIBLE(pDraw) && nptInit) {
        xPoint *ppt = pptInit;
        int npt = nptInit;

        box.x2 = box.x1 = pptInit->x;
        box.y2 = box.y1 = pptInit->y;

        /* this could be slow if the points were spread out */

        while (--npt) {
            ppt++;
            if (box.x1 > ppt->x)
                box.x1 = ppt->x;
            else if (box.x2 < ppt->x)
                box.x2 = ppt->x;
            if (box.y1 > ppt->y)
                box.y1 = ppt->y;
            else if (box.y2 < ppt->y)
                box.y2 = ppt->y;
        }

        box.x2++;
        box.y2++;

        TRIM_AND_TRANSLATE_BOX(box, pDraw, pGC);
        if (BOX_NOT_EMPTY(box)) {
            if (pPriv->preRefresh)
                (*pPriv->preRefresh) (pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }
    }

    (*pGC->ops->PolyPoint) (pDraw, pGC, mode, nptInit, pptInit);

    if (boxNotEmpty && pPriv->postRefresh)
        (*pPriv->postRefresh) (pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);
}

static void
ShadowPolylines(DrawablePtr pDraw,
                GCPtr pGC, int mode, int nptInit, DDXPointPtr pptInit)
{
    BoxRec box;
    Bool boxNotEmpty = FALSE;

    SHADOW_GC_OP_PROLOGUE(pGC);

    if (IS_VISIBLE(pDraw) && nptInit) {
        DDXPointPtr ppt = pptInit;
        int npt = nptInit;
        int extra = pGC->lineWidth >> 1;

        box.x2 = box.x1 = pptInit->x;
        box.y2 = box.y1 = pptInit->y;

        if (npt > 1) {
            if (pGC->joinStyle == JoinMiter)
                extra = 6 * pGC->lineWidth;
            else if (pGC->capStyle == CapProjecting)
                extra = pGC->lineWidth;
        }

        if (mode == CoordModePrevious) {
            int x = box.x1;
            int y = box.y1;

            while (--npt) {
                ppt++;
                x += ppt->x;
                y += ppt->y;
                if (box.x1 > x)
                    box.x1 = x;
                else if (box.x2 < x)
                    box.x2 = x;
                if (box.y1 > y)
                    box.y1 = y;
                else if (box.y2 < y)
                    box.y2 = y;
            }
        }
        else {
            while (--npt) {
                ppt++;
                if (box.x1 > ppt->x)
                    box.x1 = ppt->x;
                else if (box.x2 < ppt->x)
                    box.x2 = ppt->x;
                if (box.y1 > ppt->y)
                    box.y1 = ppt->y;
                else if (box.y2 < ppt->y)
                    box.y2 = ppt->y;
            }
        }

        box.x2++;
        box.y2++;

        if (extra) {
            box.x1 -= extra;
            box.x2 += extra;
            box.y1 -= extra;
            box.y2 += extra;
        }

        TRIM_AND_TRANSLATE_BOX(box, pDraw, pGC);
        if (BOX_NOT_EMPTY(box)) {
            if (pPriv->preRefresh)
                (*pPriv->preRefresh) (pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }
    }

    (*pGC->ops->Polylines) (pDraw, pGC, mode, nptInit, pptInit);

    if (boxNotEmpty && pPriv->postRefresh)
        (*pPriv->postRefresh) (pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);
}

static void
ShadowPolySegment(DrawablePtr pDraw,
                  GCPtr pGC, int nsegInit, xSegment * pSegInit)
{
    BoxRec box;
    Bool boxNotEmpty = FALSE;

    SHADOW_GC_OP_PROLOGUE(pGC);

    if (IS_VISIBLE(pDraw) && nsegInit) {
        int extra = pGC->lineWidth;
        xSegment *pSeg = pSegInit;
        int nseg = nsegInit;

        if (pGC->capStyle != CapProjecting)
            extra >>= 1;

        if (pSeg->x2 > pSeg->x1) {
            box.x1 = pSeg->x1;
            box.x2 = pSeg->x2;
        }
        else {
            box.x2 = pSeg->x1;
            box.x1 = pSeg->x2;
        }

        if (pSeg->y2 > pSeg->y1) {
            box.y1 = pSeg->y1;
            box.y2 = pSeg->y2;
        }
        else {
            box.y2 = pSeg->y1;
            box.y1 = pSeg->y2;
        }

        while (--nseg) {
            pSeg++;
            if (pSeg->x2 > pSeg->x1) {
                if (pSeg->x1 < box.x1)
                    box.x1 = pSeg->x1;
                if (pSeg->x2 > box.x2)
                    box.x2 = pSeg->x2;
            }
            else {
                if (pSeg->x2 < box.x1)
                    box.x1 = pSeg->x2;
                if (pSeg->x1 > box.x2)
                    box.x2 = pSeg->x1;
            }
            if (pSeg->y2 > pSeg->y1) {
                if (pSeg->y1 < box.y1)
                    box.y1 = pSeg->y1;
                if (pSeg->y2 > box.y2)
                    box.y2 = pSeg->y2;
            }
            else {
                if (pSeg->y2 < box.y1)
                    box.y1 = pSeg->y2;
                if (pSeg->y1 > box.y2)
                    box.y2 = pSeg->y1;
            }
        }

        box.x2++;
        box.y2++;

        if (extra) {
            box.x1 -= extra;
            box.x2 += extra;
            box.y1 -= extra;
            box.y2 += extra;
        }

        TRIM_AND_TRANSLATE_BOX(box, pDraw, pGC);
        if (BOX_NOT_EMPTY(box)) {
            if (pPriv->preRefresh)
                (*pPriv->preRefresh) (pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }
    }

    (*pGC->ops->PolySegment) (pDraw, pGC, nsegInit, pSegInit);

    if (boxNotEmpty && pPriv->postRefresh)
        (*pPriv->postRefresh) (pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);
}

static void
ShadowPolyRectangle(DrawablePtr pDraw,
                    GCPtr pGC, int nRectsInit, xRectangle *pRectsInit)
{
    BoxRec box;
    BoxPtr pBoxInit = NULL;
    Bool boxNotEmpty = FALSE;
    int num = 0;

    SHADOW_GC_OP_PROLOGUE(pGC);

    if (IS_VISIBLE(pDraw) && nRectsInit) {
        xRectangle *pRects = pRectsInit;
        int nRects = nRectsInit;

        if (nRects >= 32) {
            int extra = pGC->lineWidth >> 1;

            box.x1 = pRects->x;
            box.x2 = box.x1 + pRects->width;
            box.y1 = pRects->y;
            box.y2 = box.y1 + pRects->height;

            while (--nRects) {
                pRects++;
                if (box.x1 > pRects->x)
                    box.x1 = pRects->x;
                if (box.x2 < (pRects->x + pRects->width))
                    box.x2 = pRects->x + pRects->width;
                if (box.y1 > pRects->y)
                    box.y1 = pRects->y;
                if (box.y2 < (pRects->y + pRects->height))
                    box.y2 = pRects->y + pRects->height;
            }

            if (extra) {
                box.x1 -= extra;
                box.x2 += extra;
                box.y1 -= extra;
                box.y2 += extra;
            }

            box.x2++;
            box.y2++;

            TRIM_AND_TRANSLATE_BOX(box, pDraw, pGC);
            if (BOX_NOT_EMPTY(box)) {
                if (pPriv->preRefresh)
                    (*pPriv->preRefresh) (pPriv->pScrn, 1, &box);
                boxNotEmpty = TRUE;
            }
        }
        else {
            BoxPtr pbox;
            int offset1, offset2, offset3;

            offset2 = pGC->lineWidth;
            if (!offset2)
                offset2 = 1;
            offset1 = offset2 >> 1;
            offset3 = offset2 - offset1;

            pBoxInit = (BoxPtr) malloc(nRects * 4 * sizeof(BoxRec));
            pbox = pBoxInit;

            while (nRects--) {
                pbox->x1 = pRects->x - offset1;
                pbox->y1 = pRects->y - offset1;
                pbox->x2 = pbox->x1 + pRects->width + offset2;
                pbox->y2 = pbox->y1 + offset2;
                TRIM_AND_TRANSLATE_BOX((*pbox), pDraw, pGC);
                if (BOX_NOT_EMPTY((*pbox))) {
                    num++;
                    pbox++;
                }

                pbox->x1 = pRects->x - offset1;
                pbox->y1 = pRects->y + offset3;
                pbox->x2 = pbox->x1 + offset2;
                pbox->y2 = pbox->y1 + pRects->height - offset2;
                TRIM_AND_TRANSLATE_BOX((*pbox), pDraw, pGC);
                if (BOX_NOT_EMPTY((*pbox))) {
                    num++;
                    pbox++;
                }

                pbox->x1 = pRects->x + pRects->width - offset1;
                pbox->y1 = pRects->y + offset3;
                pbox->x2 = pbox->x1 + offset2;
                pbox->y2 = pbox->y1 + pRects->height - offset2;
                TRIM_AND_TRANSLATE_BOX((*pbox), pDraw, pGC);
                if (BOX_NOT_EMPTY((*pbox))) {
                    num++;
                    pbox++;
                }

                pbox->x1 = pRects->x - offset1;
                pbox->y1 = pRects->y + pRects->height - offset1;
                pbox->x2 = pbox->x1 + pRects->width + offset2;
                pbox->y2 = pbox->y1 + offset2;
                TRIM_AND_TRANSLATE_BOX((*pbox), pDraw, pGC);
                if (BOX_NOT_EMPTY((*pbox))) {
                    num++;
                    pbox++;
                }

                pRects++;
            }

            if (num) {
                if (pPriv->preRefresh)
                    (*pPriv->preRefresh) (pPriv->pScrn, num, pBoxInit);
            }
            else {
                free(pBoxInit);
            }
        }
    }

    (*pGC->ops->PolyRectangle) (pDraw, pGC, nRectsInit, pRectsInit);

    if (boxNotEmpty && pPriv->postRefresh) {
        (*pPriv->postRefresh) (pPriv->pScrn, 1, &box);
    }
    else if (num) {
        if (pPriv->postRefresh)
            (*pPriv->postRefresh) (pPriv->pScrn, num, pBoxInit);
        free(pBoxInit);
    }

    SHADOW_GC_OP_EPILOGUE(pGC);

}

static void
ShadowPolyArc(DrawablePtr pDraw, GCPtr pGC, int narcsInit, xArc * parcsInit)
{
    BoxRec box;
    Bool boxNotEmpty = FALSE;

    SHADOW_GC_OP_PROLOGUE(pGC);

    if (IS_VISIBLE(pDraw) && narcsInit) {
        int narcs = narcsInit;
        xArc *parcs = parcsInit;
        int extra = pGC->lineWidth >> 1;

        box.x1 = parcs->x;
        box.x2 = box.x1 + parcs->width;
        box.y1 = parcs->y;
        box.y2 = box.y1 + parcs->height;

        /* should I break these up instead ? */

        while (--narcs) {
            parcs++;
            if (box.x1 > parcs->x)
                box.x1 = parcs->x;
            if (box.x2 < (parcs->x + parcs->width))
                box.x2 = parcs->x + parcs->width;
            if (box.y1 > parcs->y)
                box.y1 = parcs->y;
            if (box.y2 < (parcs->y + parcs->height))
                box.y2 = parcs->y + parcs->height;
        }

        if (extra) {
            box.x1 -= extra;
            box.x2 += extra;
            box.y1 -= extra;
            box.y2 += extra;
        }

        box.x2++;
        box.y2++;

        TRIM_AND_TRANSLATE_BOX(box, pDraw, pGC);
        if (BOX_NOT_EMPTY(box)) {
            if (pPriv->preRefresh)
                (*pPriv->preRefresh) (pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }
    }

    (*pGC->ops->PolyArc) (pDraw, pGC, narcsInit, parcsInit);

    if (boxNotEmpty && pPriv->postRefresh)
        (*pPriv->postRefresh) (pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);

}

static void
ShadowFillPolygon(DrawablePtr pDraw,
                  GCPtr pGC,
                  int shape, int mode, int count, DDXPointPtr pptInit)
{
    SHADOW_GC_OP_PROLOGUE(pGC);

    if (IS_VISIBLE(pDraw) && (count > 2)) {
        DDXPointPtr ppt = pptInit;
        int i = count;
        BoxRec box;
        Bool boxNotEmpty = FALSE;

        box.x2 = box.x1 = ppt->x;
        box.y2 = box.y1 = ppt->y;

        if (mode != CoordModeOrigin) {
            int x = box.x1;
            int y = box.y1;

            while (--i) {
                ppt++;
                x += ppt->x;
                y += ppt->y;
                if (box.x1 > x)
                    box.x1 = x;
                else if (box.x2 < x)
                    box.x2 = x;
                if (box.y1 > y)
                    box.y1 = y;
                else if (box.y2 < y)
                    box.y2 = y;
            }
        }
        else {
            while (--i) {
                ppt++;
                if (box.x1 > ppt->x)
                    box.x1 = ppt->x;
                else if (box.x2 < ppt->x)
                    box.x2 = ppt->x;
                if (box.y1 > ppt->y)
                    box.y1 = ppt->y;
                else if (box.y2 < ppt->y)
                    box.y2 = ppt->y;
            }
        }

        box.x2++;
        box.y2++;

        TRIM_AND_TRANSLATE_BOX(box, pDraw, pGC);
        if (BOX_NOT_EMPTY(box)) {
            if (pPriv->preRefresh)
                (*pPriv->preRefresh) (pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }

        (*pGC->ops->FillPolygon) (pDraw, pGC, shape, mode, count, pptInit);

        if (boxNotEmpty && pPriv->postRefresh)
            (*pPriv->postRefresh) (pPriv->pScrn, 1, &box);
    }
    else
        (*pGC->ops->FillPolygon) (pDraw, pGC, shape, mode, count, pptInit);

    SHADOW_GC_OP_EPILOGUE(pGC);
}

static void
ShadowPolyFillRect(DrawablePtr pDraw,
                   GCPtr pGC, int nRectsInit, xRectangle *pRectsInit)
{
    SHADOW_GC_OP_PROLOGUE(pGC);

    if (IS_VISIBLE(pDraw) && nRectsInit) {
        BoxRec box;
        Bool boxNotEmpty = FALSE;
        xRectangle *pRects = pRectsInit;
        int nRects = nRectsInit;

        box.x1 = pRects->x;
        box.x2 = box.x1 + pRects->width;
        box.y1 = pRects->y;
        box.y2 = box.y1 + pRects->height;

        while (--nRects) {
            pRects++;
            if (box.x1 > pRects->x)
                box.x1 = pRects->x;
            if (box.x2 < (pRects->x + pRects->width))
                box.x2 = pRects->x + pRects->width;
            if (box.y1 > pRects->y)
                box.y1 = pRects->y;
            if (box.y2 < (pRects->y + pRects->height))
                box.y2 = pRects->y + pRects->height;
        }

        /* cfb messes with the pRectsInit so we have to do our
           calculations first */

        TRIM_AND_TRANSLATE_BOX(box, pDraw, pGC);
        if (BOX_NOT_EMPTY(box)) {
            if (pPriv->preRefresh)
                (*pPriv->preRefresh) (pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }

        (*pGC->ops->PolyFillRect) (pDraw, pGC, nRectsInit, pRectsInit);

        if (boxNotEmpty && pPriv->postRefresh)
            (*pPriv->postRefresh) (pPriv->pScrn, 1, &box);
    }
    else
        (*pGC->ops->PolyFillRect) (pDraw, pGC, nRectsInit, pRectsInit);

    SHADOW_GC_OP_EPILOGUE(pGC);
}

static void
ShadowPolyFillArc(DrawablePtr pDraw, GCPtr pGC, int narcsInit, xArc * parcsInit)
{
    BoxRec box;
    Bool boxNotEmpty = FALSE;

    SHADOW_GC_OP_PROLOGUE(pGC);

    if (IS_VISIBLE(pDraw) && narcsInit) {
        xArc *parcs = parcsInit;
        int narcs = narcsInit;

        box.x1 = parcs->x;
        box.x2 = box.x1 + parcs->width;
        box.y1 = parcs->y;
        box.y2 = box.y1 + parcs->height;

        /* should I break these up instead ? */

        while (--narcs) {
            parcs++;
            if (box.x1 > parcs->x)
                box.x1 = parcs->x;
            if (box.x2 < (parcs->x + parcs->width))
                box.x2 = parcs->x + parcs->width;
            if (box.y1 > parcs->y)
                box.y1 = parcs->y;
            if (box.y2 < (parcs->y + parcs->height))
                box.y2 = parcs->y + parcs->height;
        }

        TRIM_AND_TRANSLATE_BOX(box, pDraw, pGC);
        if (BOX_NOT_EMPTY(box)) {
            if (pPriv->preRefresh)
                (*pPriv->preRefresh) (pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }
    }

    (*pGC->ops->PolyFillArc) (pDraw, pGC, narcsInit, parcsInit);

    if (boxNotEmpty && pPriv->postRefresh)
        (*pPriv->postRefresh) (pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);
}

static void
ShadowTextExtent(FontPtr pFont, int count, char *chars,
                 FontEncoding fontEncoding, BoxPtr box)
{
    unsigned long n, i;
    int w;
    CharInfoPtr charinfo[255];  /* encoding only has 1 byte for count */

    GetGlyphs(pFont, (unsigned long) count, (unsigned char *) chars,
              fontEncoding, &n, charinfo);
    w = 0;
    for (i = 0; i < n; i++) {
        w += charinfo[i]->metrics.characterWidth;
    }
    if (i) {
        w += charinfo[i - 1]->metrics.rightSideBearing;
    }

    box->x1 = 0;
    if (n) {
        if (charinfo[0]->metrics.leftSideBearing < 0) {
            box->x1 = charinfo[0]->metrics.leftSideBearing;
        }
    }
    box->x2 = w;
    box->y1 = -FONTMAXBOUNDS(pFont, ascent);
    box->y2 = FONTMAXBOUNDS(pFont, descent);
}

static void
ShadowFontToBox(BoxPtr BB, DrawablePtr pDrawable, GCPtr pGC, int x, int y,
                int count, char *chars, int wide)
{
    FontPtr pFont;

    pFont = pGC->font;
    if (pFont->info.constantWidth) {
        int ascent, descent, left, right = 0;

        ascent = max(pFont->info.fontAscent, pFont->info.maxbounds.ascent);
        descent = max(pFont->info.fontDescent, pFont->info.maxbounds.descent);
        left = pFont->info.maxbounds.leftSideBearing;
        if (count > 0) {
            right = (count - 1) * pFont->info.maxbounds.characterWidth;
        }
        right += pFont->info.maxbounds.rightSideBearing;
        BB->x1 =
            max(pDrawable->x + x - left,
                RegionExtents(&((WindowPtr) pDrawable)->winSize)->x1);
        BB->y1 =
            max(pDrawable->y + y - ascent,
                RegionExtents(&((WindowPtr) pDrawable)->winSize)->y1);
        BB->x2 =
            min(pDrawable->x + x + right,
                RegionExtents(&((WindowPtr) pDrawable)->winSize)->x2);
        BB->y2 =
            min(pDrawable->y + y + descent,
                RegionExtents(&((WindowPtr) pDrawable)->winSize)->y2);
    }
    else {
        ShadowTextExtent(pFont, count, chars, wide ? (FONTLASTROW(pFont) == 0)
                         ? Linear16Bit : TwoD16Bit : Linear8Bit, BB);
        BB->x1 =
            max(pDrawable->x + x + BB->x1,
                RegionExtents(&((WindowPtr) pDrawable)->winSize)->x1);
        BB->y1 =
            max(pDrawable->y + y + BB->y1,
                RegionExtents(&((WindowPtr) pDrawable)->winSize)->y1);
        BB->x2 =
            min(pDrawable->x + x + BB->x2,
                RegionExtents(&((WindowPtr) pDrawable)->winSize)->x2);
        BB->y2 =
            min(pDrawable->y + y + BB->y2,
                RegionExtents(&((WindowPtr) pDrawable)->winSize)->y2);
    }
}

static int
ShadowPolyText8(DrawablePtr pDraw,
                GCPtr pGC, int x, int y, int count, char *chars)
{
    int width;
    BoxRec box;
    Bool boxNotEmpty = FALSE;

    SHADOW_GC_OP_PROLOGUE(pGC);

    if (IS_VISIBLE(pDraw)) {
        ShadowFontToBox(&box, pDraw, pGC, x, y, count, chars, 0);

        TRIM_BOX(box, pGC);
        if (BOX_NOT_EMPTY(box)) {
            if (pPriv->preRefresh)
                (*pPriv->preRefresh) (pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }
    }

    width = (*pGC->ops->PolyText8) (pDraw, pGC, x, y, count, chars);

    if (boxNotEmpty && pPriv->postRefresh)
        (*pPriv->postRefresh) (pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);

    return width;
}

static int
ShadowPolyText16(DrawablePtr pDraw,
                 GCPtr pGC, int x, int y, int count, unsigned short *chars)
{
    int width;
    BoxRec box;
    Bool boxNotEmpty = FALSE;

    SHADOW_GC_OP_PROLOGUE(pGC);

    if (IS_VISIBLE(pDraw)) {
        ShadowFontToBox(&box, pDraw, pGC, x, y, count, (char *) chars, 1);

        TRIM_BOX(box, pGC);
        if (BOX_NOT_EMPTY(box)) {
            if (pPriv->preRefresh)
                (*pPriv->preRefresh) (pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }
    }

    width = (*pGC->ops->PolyText16) (pDraw, pGC, x, y, count, chars);

    if (boxNotEmpty && pPriv->postRefresh)
        (*pPriv->postRefresh) (pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);

    return width;
}

static void
ShadowImageText8(DrawablePtr pDraw,
                 GCPtr pGC, int x, int y, int count, char *chars)
{
    BoxRec box;
    Bool boxNotEmpty = FALSE;

    SHADOW_GC_OP_PROLOGUE(pGC);

    if (IS_VISIBLE(pDraw) && count) {
        int top, bot, Min, Max;

        top = max(FONTMAXBOUNDS(pGC->font, ascent), FONTASCENT(pGC->font));
        bot = max(FONTMAXBOUNDS(pGC->font, descent), FONTDESCENT(pGC->font));

        Min = count * FONTMINBOUNDS(pGC->font, characterWidth);
        if (Min > 0)
            Min = 0;
        Max = count * FONTMAXBOUNDS(pGC->font, characterWidth);
        if (Max < 0)
            Max = 0;

        /* ugh */
        box.x1 = pDraw->x + x + Min + FONTMINBOUNDS(pGC->font, leftSideBearing);
        box.x2 = pDraw->x + x + Max +
            FONTMAXBOUNDS(pGC->font, rightSideBearing);

        box.y1 = pDraw->y + y - top;
        box.y2 = pDraw->y + y + bot;

        TRIM_BOX(box, pGC);
        if (BOX_NOT_EMPTY(box)) {
            if (pPriv->preRefresh)
                (*pPriv->preRefresh) (pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }
    }

    (*pGC->ops->ImageText8) (pDraw, pGC, x, y, count, chars);

    if (boxNotEmpty && pPriv->postRefresh)
        (*pPriv->postRefresh) (pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);
}

static void
ShadowImageText16(DrawablePtr pDraw,
                  GCPtr pGC, int x, int y, int count, unsigned short *chars)
{
    BoxRec box;
    Bool boxNotEmpty = FALSE;

    SHADOW_GC_OP_PROLOGUE(pGC);

    if (IS_VISIBLE(pDraw) && count) {
        int top, bot, Min, Max;

        top = max(FONTMAXBOUNDS(pGC->font, ascent), FONTASCENT(pGC->font));
        bot = max(FONTMAXBOUNDS(pGC->font, descent), FONTDESCENT(pGC->font));

        Min = count * FONTMINBOUNDS(pGC->font, characterWidth);
        if (Min > 0)
            Min = 0;
        Max = count * FONTMAXBOUNDS(pGC->font, characterWidth);
        if (Max < 0)
            Max = 0;

        /* ugh */
        box.x1 = pDraw->x + x + Min + FONTMINBOUNDS(pGC->font, leftSideBearing);
        box.x2 = pDraw->x + x + Max +
            FONTMAXBOUNDS(pGC->font, rightSideBearing);

        box.y1 = pDraw->y + y - top;
        box.y2 = pDraw->y + y + bot;

        TRIM_BOX(box, pGC);
        if (BOX_NOT_EMPTY(box)) {
            if (pPriv->preRefresh)
                (*pPriv->preRefresh) (pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }
    }

    (*pGC->ops->ImageText16) (pDraw, pGC, x, y, count, chars);

    if (boxNotEmpty && pPriv->postRefresh)
        (*pPriv->postRefresh) (pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);
}

static void
ShadowImageGlyphBlt(DrawablePtr pDraw,
                    GCPtr pGC,
                    int x, int y,
                    unsigned int nglyphInit,
                    CharInfoPtr * ppciInit, pointer pglyphBase)
{
    BoxRec box;
    Bool boxNotEmpty = FALSE;

    SHADOW_GC_OP_PROLOGUE(pGC);

    if (IS_VISIBLE(pDraw) && nglyphInit) {
        CharInfoPtr *ppci = ppciInit;
        unsigned int nglyph = nglyphInit;
        int top, bot, width = 0;

        top = max(FONTMAXBOUNDS(pGC->font, ascent), FONTASCENT(pGC->font));
        bot = max(FONTMAXBOUNDS(pGC->font, descent), FONTDESCENT(pGC->font));

        box.x1 = ppci[0]->metrics.leftSideBearing;
        if (box.x1 > 0)
            box.x1 = 0;
        box.x2 = ppci[nglyph - 1]->metrics.rightSideBearing -
            ppci[nglyph - 1]->metrics.characterWidth;
        if (box.x2 < 0)
            box.x2 = 0;

        box.x2 += pDraw->x + x;
        box.x1 += pDraw->x + x;

        while (nglyph--) {
            width += (*ppci)->metrics.characterWidth;
            ppci++;
        }

        if (width > 0)
            box.x2 += width;
        else
            box.x1 += width;

        box.y1 = pDraw->y + y - top;
        box.y2 = pDraw->y + y + bot;

        TRIM_BOX(box, pGC);
        if (BOX_NOT_EMPTY(box)) {
            if (pPriv->preRefresh)
                (*pPriv->preRefresh) (pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }
    }

    (*pGC->ops->ImageGlyphBlt) (pDraw, pGC, x, y, nglyphInit,
                                ppciInit, pglyphBase);

    if (boxNotEmpty && pPriv->postRefresh)
        (*pPriv->postRefresh) (pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);
}

static void
ShadowPolyGlyphBlt(DrawablePtr pDraw,
                   GCPtr pGC,
                   int x, int y,
                   unsigned int nglyphInit,
                   CharInfoPtr * ppciInit, pointer pglyphBase)
{
    BoxRec box;
    Bool boxNotEmpty = FALSE;

    SHADOW_GC_OP_PROLOGUE(pGC);

    if (IS_VISIBLE(pDraw) && nglyphInit) {
        CharInfoPtr *ppci = ppciInit;
        unsigned int nglyph = nglyphInit;

        /* ugh */
        box.x1 = pDraw->x + x + ppci[0]->metrics.leftSideBearing;
        box.x2 = pDraw->x + x + ppci[nglyph - 1]->metrics.rightSideBearing;

        if (nglyph > 1) {
            int width = 0;

            while (--nglyph) {
                width += (*ppci)->metrics.characterWidth;
                ppci++;
            }

            if (width > 0)
                box.x2 += width;
            else
                box.x1 += width;
        }

        box.y1 = pDraw->y + y - FONTMAXBOUNDS(pGC->font, ascent);
        box.y2 = pDraw->y + y + FONTMAXBOUNDS(pGC->font, descent);

        TRIM_BOX(box, pGC);
        if (BOX_NOT_EMPTY(box)) {
            if (pPriv->preRefresh)
                (*pPriv->preRefresh) (pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }
    }

    (*pGC->ops->PolyGlyphBlt) (pDraw, pGC, x, y, nglyphInit,
                               ppciInit, pglyphBase);

    if (boxNotEmpty && pPriv->postRefresh)
        (*pPriv->postRefresh) (pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);
}

static void
ShadowPushPixels(GCPtr pGC,
                 PixmapPtr pBitMap,
                 DrawablePtr pDraw, int dx, int dy, int xOrg, int yOrg)
{
    BoxRec box;
    Bool boxNotEmpty = FALSE;

    SHADOW_GC_OP_PROLOGUE(pGC);

    if (IS_VISIBLE(pDraw)) {
        box.x1 = xOrg;
        box.y1 = yOrg;

        if (!pGC->miTranslate) {
            box.x1 += pDraw->x;
            box.y1 += pDraw->y;
        }

        box.x2 = box.x1 + dx;
        box.y2 = box.y1 + dy;

        TRIM_BOX(box, pGC);
        if (BOX_NOT_EMPTY(box)) {
            if (pPriv->preRefresh)
                (*pPriv->preRefresh) (pPriv->pScrn, 1, &box);
            boxNotEmpty = TRUE;
        }
    }

    (*pGC->ops->PushPixels) (pGC, pBitMap, pDraw, dx, dy, xOrg, yOrg);

    if (boxNotEmpty && pPriv->postRefresh)
        (*pPriv->postRefresh) (pPriv->pScrn, 1, &box);

    SHADOW_GC_OP_EPILOGUE(pGC);
}

GCOps ShadowGCOps = {
    ShadowFillSpans, ShadowSetSpans,
    ShadowPutImage, ShadowCopyArea,
    ShadowCopyPlane, ShadowPolyPoint,
    ShadowPolylines, ShadowPolySegment,
    ShadowPolyRectangle, ShadowPolyArc,
    ShadowFillPolygon, ShadowPolyFillRect,
    ShadowPolyFillArc, ShadowPolyText8,
    ShadowPolyText16, ShadowImageText8,
    ShadowImageText16, ShadowImageGlyphBlt,
    ShadowPolyGlyphBlt, ShadowPushPixels,
};
