/*
 * Copyright 1997 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that copyright
 * notice and this permission notice appear in supporting documentation, and
 * that the name of Marc Aurele La France not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  Marc Aurele La France makes no representations
 * about the suitability of this software for any purpose.  It is provided
 * "as-is" without express or implied warranty.
 *
 * MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO
 * EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * Copyright 1990,91,92,93 by Thomas Roell, Germany.
 * Copyright 1991,92,93    by SGCS (Snitily Graphics Consulting Services), USA.
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this  permission notice appear
 * in supporting documentation, and that the name of Thomas Roell nor
 * SGCS be used in advertising or publicity pertaining to distribution
 * of the software without specific, written prior permission.
 * Thomas Roell nor SGCS makes no representations about the suitability
 * of this software for any purpose. It is provided "as is" without
 * express or implied warranty.
 *
 * THOMAS ROELL AND SGCS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS, IN NO EVENT SHALL THOMAS ROELL OR SGCS BE LIABLE FOR ANY
 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */


/*
 * This thing originated from an idea of Edwin Goei and his bank switching
 * code for the DEC TX board.
 */

/*
 * Heavily modified for the XFree86 Project to turn this into an mi wrapper.
 * ---  Marc Aurele La France (tsi@xfree86.org)
 */

/*
 * "Heavily modified", indeed!  By the time this is finalized, there probably
 * won't be much left of Roell's code...
 *
 * Miscellaneous notes:
 * - Pixels with imbedded bank boundaries are required to be off-screen.  There
 *   >might< be a way to fool the underlying framebuffer into dealing with
 *   partial pixels.
 * - Plans to generalise this to do (hardware) colour plane switching have been
 *   dropped due to colour flashing concerns.
 *
 * TODO:
 * - Allow miModifyBanking() to change BankSize and nBankDepth.
 * - Re-instate shared and double banking for framebuffers whose pixmap formats
 *   don't describe how the server "sees" the screen.
 * - Remove remaining assumptions that a pixmap's devPrivate field points
 *   directly to its pixel data.
 */

/* #define NO_ALLOCA 1 */

#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif

#include "servermd.h"
#include "gcstruct.h"
#include "pixmapstr.h"
#include "scrnintstr.h"
#include "windowstr.h"
#include "mi.h"
#include "mibank.h"

#define BANK_SINGLE 0
#define BANK_SHARED 1
#define BANK_DOUBLE 2
#define BANK_NOBANK 3

typedef struct _miBankScreen
{
    miBankInfoRec BankInfo;
    unsigned int  nBankBPP;
    unsigned int  type;

    unsigned long nBitsPerBank;
    unsigned long nBitsPerScanline;
    unsigned long nPixelsPerScanlinePadUnit;

    PixmapPtr     pScreenPixmap;
    PixmapPtr     pBankPixmap;
    GCPtr         pBankGC;

    int           nBanks, maxRects;
    RegionPtr     *pBanks;

    pointer       pbits;

    /*
     * Screen Wrappers
     */
    CreateScreenResourcesProcPtr  CreateScreenResources;
    ModifyPixmapHeaderProcPtr     ModifyPixmapHeader;
    CloseScreenProcPtr            CloseScreen;
    GetImageProcPtr               GetImage;
    GetSpansProcPtr               GetSpans;
    CreateGCProcPtr               CreateGC;
    CopyWindowProcPtr             CopyWindow;
} miBankScreenRec, *miBankScreenPtr;

typedef struct _miBankGC
{
    GCOps     *wrappedOps,   *unwrappedOps;
    GCFuncs   *wrappedFuncs, *unwrappedFuncs;

    Bool      fastCopy, fastPlane;

    RegionPtr pBankedClips[1];
} miBankGCRec, *miBankGCPtr;

typedef struct _miBankQueue
{
    Bool           fastBlit;
    unsigned short srcBankNo;
    unsigned short dstBankNo;
    short          x;
    short          y;
    short          w;
    short          h;
} miBankQueue;

/*
 * CAVEAT:  This banking scheme requires that the DDX store Pixmap data in the
 *          server's address space.
 */

#define ModifyPixmap(_pPix, _width, _devKind, _pbits) \
    (*pScreen->ModifyPixmapHeader)((_pPix), \
        (_width), -1, -1, -1, (_devKind), (_pbits))

#define SET_SINGLE_BANK(_pPix, _width, _devKind, _no) \
    ModifyPixmap(_pPix, _width, _devKind, \
        (char *)pScreenPriv->BankInfo.pBankA + \
        (*pScreenPriv->BankInfo.SetSourceAndDestinationBanks)(pScreen, (_no)) - \
        (pScreenPriv->BankInfo.BankSize * (_no)))

#define SET_SOURCE_BANK(_pPix, _width, _devKind, _no) \
    ModifyPixmap(_pPix, _width, _devKind, \
        (char *)pScreenPriv->BankInfo.pBankA + \
        (*pScreenPriv->BankInfo.SetSourceBank)(pScreen, (_no)) - \
        (pScreenPriv->BankInfo.BankSize * (_no)))

#define SET_DESTINATION_BANK(_pPix, _width, _devKind, _no) \
    ModifyPixmap(_pPix, _width, _devKind, \
        (char *)pScreenPriv->BankInfo.pBankB + \
        (*pScreenPriv->BankInfo.SetDestinationBank)(pScreen, (_no)) - \
        (pScreenPriv->BankInfo.BankSize * (_no)))

#define xalloc_ARRAY(atype, ntype) \
    (atype *)xalloc((ntype) * sizeof(atype))

static DevPrivateKey miBankScreenKey = &miBankScreenKey;
static DevPrivateKey miBankGCKey = &miBankGCKey;
static unsigned long miBankGeneration = 0;

#define BANK_SCRPRIVLVAL dixLookupPrivate(&pScreen->devPrivates, miBankScreenKey)

#define BANK_SCRPRIVATE ((miBankScreenPtr)(BANK_SCRPRIVLVAL))

#define BANK_GCPRIVLVAL(pGC) dixLookupPrivate(&(pGC)->devPrivates, miBankGCKey)

#define BANK_GCPRIVATE(pGC) ((miBankGCPtr)(BANK_GCPRIVLVAL(pGC)))

#define PIXMAP_STATUS(_pPix) \
    pointer pbits = (_pPix)->devPrivate.ptr

#define PIXMAP_SAVE(_pPix) \
    PIXMAP_STATUS(_pPix); \
    if (pbits == (pointer)pScreenPriv) \
        (_pPix)->devPrivate.ptr = pScreenPriv->pbits

#define PIXMAP_RESTORE(_pPix) \
    (_pPix)->devPrivate.ptr = pbits

#define BANK_SAVE \
    int width   = pScreenPriv->pBankPixmap->drawable.width; \
    int devKind = pScreenPriv->pBankPixmap->devKind; \
    PIXMAP_SAVE(pScreenPriv->pBankPixmap)

#define BANK_RESTORE \
    pScreenPriv->pBankPixmap->drawable.width = width; \
    pScreenPriv->pBankPixmap->devKind = devKind; \
    PIXMAP_RESTORE(pScreenPriv->pBankPixmap)

#define SCREEN_STATUS \
    PIXMAP_STATUS(pScreenPriv->pScreenPixmap)

#define SCREEN_SAVE \
    PIXMAP_SAVE(pScreenPriv->pScreenPixmap)

#define SCREEN_RESTORE \
    PIXMAP_RESTORE(pScreenPriv->pScreenPixmap)

#define SCREEN_INIT \
    miBankScreenPtr pScreenPriv = BANK_SCRPRIVATE

#define SCREEN_UNWRAP(field) \
    pScreen->field = pScreenPriv->field

#define SCREEN_WRAP(field, wrapper) \
    pScreenPriv->field = pScreen->field; \
    pScreen->field     = wrapper

#define GC_INIT(pGC) \
    miBankGCPtr pGCPriv = BANK_GCPRIVATE(pGC)

#define GC_UNWRAP(pGC) \
    pGCPriv->unwrappedOps   = (pGC)->ops; \
    pGCPriv->unwrappedFuncs = (pGC)->funcs; \
    (pGC)->ops              = pGCPriv->wrappedOps; \
    (pGC)->funcs            = pGCPriv->wrappedFuncs

#define GC_WRAP(pGC) \
    pGCPriv->wrappedOps   = (pGC)->ops; \
    pGCPriv->wrappedFuncs = (pGC)->funcs; \
    (pGC)->ops            = pGCPriv->unwrappedOps; \
    (pGC)->funcs          = pGCPriv->unwrappedFuncs

#define IS_BANKED(pDrawable) \
    ((pbits == (pointer)pScreenPriv) && \
     (((DrawablePtr)(pDrawable))->type == DRAWABLE_WINDOW))

#define CLIP_SAVE \
    RegionPtr pOrigCompositeClip = pGC->pCompositeClip

#define CLIP_RESTORE \
    pGC->pCompositeClip = pOrigCompositeClip

#define GCOP_INIT \
    ScreenPtr pScreen = pGC->pScreen; \
    SCREEN_INIT; \
    GC_INIT(pGC)

#define GCOP_UNWRAP \
    GC_UNWRAP(pGC)

#define GCOP_WRAP \
    GC_WRAP(pGC)

#define GCOP_TOP_PART \
    for (i = 0;  i < pScreenPriv->nBanks;  i++) \
    { \
        if (!(pGC->pCompositeClip = pGCPriv->pBankedClips[i])) \
            continue; \
        GCOP_UNWRAP; \
        SET_SINGLE_BANK(pScreenPriv->pScreenPixmap, -1, -1, i)

#define GCOP_BOTTOM_PART \
        GCOP_WRAP; \
    }

#define GCOP_SIMPLE(statement) \
    if (nArray > 0) \
    { \
        GCOP_INIT; \
        SCREEN_SAVE; \
        if (!IS_BANKED(pDrawable)) \
        { \
            GCOP_UNWRAP; \
            statement; \
            GCOP_WRAP; \
        } \
        else \
        { \
            int i; \
            CLIP_SAVE; \
            GCOP_TOP_PART; \
            statement; \
            GCOP_BOTTOM_PART; \
            CLIP_RESTORE; \
        } \
        SCREEN_RESTORE; \
    }

#define GCOP_0D_ARGS mode,
#define GCOP_1D_ARGS
#define GCOP_2D_ARGS shape, mode,

#define GCOP_COMPLEX(aop, atype) \
    if (nArray > 0) \
    { \
        GCOP_INIT; \
        SCREEN_SAVE; \
        if (!IS_BANKED(pDrawable)) \
        { \
            GCOP_UNWRAP; \
            (*pGC->ops->aop)(pDrawable, pGC, GCOP_ARGS nArray, pArray); \
            GCOP_WRAP; \
        } \
        else \
        { \
            atype *aarg = pArray, *acopy; \
            int   i; \
            CLIP_SAVE; \
            if ((acopy = xalloc_ARRAY(atype, nArray))) \
                aarg = acopy; \
            GCOP_TOP_PART; \
            if (acopy) \
                memcpy(acopy, pArray, nArray * sizeof(atype)); \
            (*pGC->ops->aop)(pDrawable, pGC, GCOP_ARGS nArray, aarg); \
            GCOP_BOTTOM_PART; \
            xfree(acopy); \
            CLIP_RESTORE; \
        } \
        SCREEN_RESTORE; \
    }

/*********************
 * Utility functions *
 *********************/

static int
miBankOf(
    miBankScreenPtr pScreenPriv,
    int             x,
    int             y
)
{
    int iBank = ((x * (int)pScreenPriv->nBankBPP) +
                 (y * (long)pScreenPriv->nBitsPerScanline)) /
                (long)pScreenPriv->nBitsPerBank;

    if (iBank < 0)
        iBank = 0;
    else if (iBank >= pScreenPriv->nBanks)
        iBank = pScreenPriv->nBanks - 1;

    return iBank;
}

#define FirstBankOf(_x, _y) miBankOf(pScreenPriv, (_x), (_y))
#define  LastBankOf(_x, _y) miBankOf(pScreenPriv, (_x) - 1, (_y))

/* Determine banking type from the BankInfoRec */
static unsigned int
miBankDeriveType(
    ScreenPtr     pScreen,
    miBankInfoPtr pBankInfo
)
{
    unsigned int type;

    if (pBankInfo->pBankA == pBankInfo->pBankB)
    {
        if (pBankInfo->SetSourceBank == pBankInfo->SetDestinationBank)
        {
            if (pBankInfo->SetSourceAndDestinationBanks !=
                pBankInfo->SetSourceBank)
                return BANK_NOBANK;

            type = BANK_SINGLE;
        }
        else
        {
            if (pBankInfo->SetSourceAndDestinationBanks ==
                pBankInfo->SetDestinationBank)
                return BANK_NOBANK;
            if (pBankInfo->SetSourceAndDestinationBanks ==
                pBankInfo->SetSourceBank)
                return BANK_NOBANK;

            type = BANK_SHARED;
        }
    }
    else
    {
        if ((unsigned long)abs((char *)pBankInfo->pBankA -
                               (char *)pBankInfo->pBankB) < pBankInfo->BankSize)
            return BANK_NOBANK;

        if (pBankInfo->SetSourceBank == pBankInfo->SetDestinationBank)
        {
            if (pBankInfo->SetSourceAndDestinationBanks !=
                pBankInfo->SetSourceBank)
                return BANK_NOBANK;
        }
        else
        {
            if (pBankInfo->SetSourceAndDestinationBanks ==
                pBankInfo->SetDestinationBank)
                return BANK_NOBANK;
        }

        type = BANK_DOUBLE;
    }

    /*
     * Internal limitation:  Currently, only single banking is supported when
     * the pixmap format and the screen's pixel format are different.  The
     * following test is only partially successful at detecting this condition.
     */
    if (pBankInfo->nBankDepth != pScreen->rootDepth)
        type = BANK_SINGLE;

    return type;
}

/* Least common multiple */
static unsigned int
miLCM(
    unsigned int x,
    unsigned int y
)
{
    unsigned int m = x, n = y, o;

    while ((o = m % n))
    {
        m = n;
        n = o;
    }

    return (x / n) * y;
}

/******************
 * GCOps wrappers *
 ******************/

static void
miBankFillSpans(
    DrawablePtr pDrawable,
    GCPtr       pGC,
    int         nArray,
    DDXPointPtr pptInit,
    int         *pwidthInit,
    int         fSorted
)
{
    GCOP_SIMPLE((*pGC->ops->FillSpans)(pDrawable, pGC,
        nArray, pptInit, pwidthInit, fSorted));
}

static void
miBankSetSpans(
    DrawablePtr pDrawable,
    GCPtr       pGC,
    char        *psrc,
    DDXPointPtr ppt,
    int         *pwidth,
    int         nArray,
    int         fSorted
)
{
    GCOP_SIMPLE((*pGC->ops->SetSpans)(pDrawable, pGC, psrc,
        ppt, pwidth, nArray, fSorted));
}

static void
miBankPutImage(
    DrawablePtr pDrawable,
    GCPtr       pGC,
    int         depth,
    int         x,
    int         y,
    int         w,
    int         h,
    int         leftPad,
    int         format,
    char        *pImage
)
{
    if ((w > 0) && (h > 0))
    {
        GCOP_INIT;
        SCREEN_SAVE;

        if (!IS_BANKED(pDrawable))
        {
            GCOP_UNWRAP;

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

            GCOP_WRAP;
        }
        else
        {
            int i, j;

            CLIP_SAVE;

            i = FirstBankOf(x + pDrawable->x,     y + pDrawable->y);
            j =  LastBankOf(x + pDrawable->x + w, y + pDrawable->y + h);
            for (;  i <= j;  i++)
            {
                if (!(pGC->pCompositeClip = pGCPriv->pBankedClips[i]))
                    continue;

                GCOP_UNWRAP;

                SET_SINGLE_BANK(pScreenPriv->pScreenPixmap, -1, -1, i);

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

                GCOP_WRAP;
            }

            CLIP_RESTORE;
        }

        SCREEN_RESTORE;
    }
}

/*
 * Here the CopyArea/CopyPlane wrappers.  First off, we have to clip against
 * the source in order to make the minimal number of copies in case of slow
 * systems.  Also the exposure handling is quite tricky.  Special attention
 * is to be given to the way the copies are sequenced.  The list of boxes after
 * the source clip is used to build a workqueue, that contains the atomic
 * copies (i.e. only from one bank to one bank).  Doing so produces a minimal
 * list of things to do.
 */
static RegionPtr
miBankCopy(
    DrawablePtr   pSrc,
    DrawablePtr   pDst,
    GCPtr         pGC,
    int           srcx,
    int           srcy,
    int           w,
    int           h,
    int           dstx,
    int           dsty,
    unsigned long plane,
    Bool          SinglePlane
)
{
    int         cx1, cy1, cx2, cy2;
    int         ns, nd, nse, nde, dx, dy, xorg = 0, yorg = 0;
    int         maxWidth = 0, maxHeight = 0, paddedWidth = 0;
    int         nBox, nBoxClipSrc, nBoxClipDst, nQueue;
    BoxPtr      pBox, pBoxClipSrc, pBoxClipDst;
    BoxRec      fastBox, ccBox;
    RegionPtr   ret = NULL, prgnSrcClip = NULL;
    RegionRec   rgnDst;
    char        *pImage = NULL;
    miBankQueue *pQueue, *pQueueNew, *Queue;
    miBankQueue *pQueueTmp, *pQueueNext, *pQueueBase;
    Bool        fastBlit, freeSrcClip, fastClip;
    Bool        fExpose = FALSE, fastExpose = FALSE;

    GCOP_INIT;
    SCREEN_SAVE;

    if (!IS_BANKED(pSrc) && !IS_BANKED(pDst))
    {
        GCOP_UNWRAP;

        if (SinglePlane)
            ret = (*pGC->ops->CopyPlane)(pSrc, pDst, pGC,
                srcx, srcy, w, h, dstx, dsty, plane);
        else
            ret = (*pGC->ops->CopyArea)(pSrc, pDst, pGC,
                srcx, srcy, w, h, dstx, dsty);

        GCOP_WRAP;
    }
    else if (!IS_BANKED(pDst))
    {
        fExpose = pGC->fExpose;
        pGC->fExpose = FALSE;

        xorg = pSrc->x;
        yorg = pSrc->y;
        dx   = dstx - srcx;
        dy   = dsty - srcy;
        srcx += xorg;
        srcy += yorg;

        ns = FirstBankOf(srcx,     srcy);
        nse = LastBankOf(srcx + w, srcy + h);
        for (;  ns <= nse;  ns++)
        {
            if (!pScreenPriv->pBanks[ns])
                continue;

            nBox = REGION_NUM_RECTS(pScreenPriv->pBanks[ns]);
            pBox = REGION_RECTS(pScreenPriv->pBanks[ns]);

            for (;  nBox--;  pBox++)
            {
                cx1 = max(pBox->x1, srcx);
                cy1 = max(pBox->y1, srcy);
                cx2 = min(pBox->x2, srcx + w);
                cy2 = min(pBox->y2, srcy + h);

                if ((cx1 >= cx2) || (cy1 >= cy2))
                    continue;

                GCOP_UNWRAP;

                SET_SINGLE_BANK(pScreenPriv->pScreenPixmap, -1, -1, ns);

                if (SinglePlane)
                    (*pGC->ops->CopyPlane)(pSrc, pDst, pGC,
                        cx1 - xorg, cy1 - yorg,
                        cx2 - cx1, cy2 - cy1,
                        cx1 + dx - xorg, cy1 + dy - yorg, plane);
                else
                    (*pGC->ops->CopyArea)(pSrc, pDst, pGC,
                        cx1 - xorg, cy1 - yorg,
                        cx2 - cx1, cy2 - cy1,
                        cx1 + dx - xorg, cy1 + dy - yorg);

                GCOP_WRAP;
            }
        }

        pGC->fExpose = fExpose;
        srcx -= xorg;
        srcy -= yorg;
    }
    else if (!IS_BANKED(pSrc))
    {
        CLIP_SAVE;

        if (pGC->miTranslate)
        {
            xorg = pDst->x;
            yorg = pDst->y;
        }
        dx = srcx - dstx;
        dy = srcy - dsty;
        dstx += xorg;
        dsty += yorg;

        nd = FirstBankOf(dstx,     dsty);
        nde = LastBankOf(dstx + w, dsty + h);
        for (;  nd <= nde;  nd++)
        {
            if (!(pGC->pCompositeClip = pGCPriv->pBankedClips[nd]))
                continue;

            /*
             * It's faster to let the lower-level CopyArea do the clipping
             * within each bank.
             */
            nBox = REGION_NUM_RECTS(pScreenPriv->pBanks[nd]);
            pBox = REGION_RECTS(pScreenPriv->pBanks[nd]);

            for (;  nBox--;  pBox++)
            {
                cx1 = max(pBox->x1, dstx);
                cy1 = max(pBox->y1, dsty);
                cx2 = min(pBox->x2, dstx + w);
                cy2 = min(pBox->y2, dsty + h);

                if ((cx1 >= cx2) || (cy1 >= cy2))
                    continue;

                GCOP_UNWRAP;

                SET_SINGLE_BANK(pScreenPriv->pScreenPixmap, -1, -1, nd);

                if (SinglePlane)
                    (*pGC->ops->CopyPlane)(pSrc, pDst, pGC,
                        cx1 + dx - xorg, cy1 + dy - yorg,
                        cx2 - cx1, cy2 - cy1,
                        cx1 - xorg, cy1 - yorg, plane);
                else
                    (*pGC->ops->CopyArea)(pSrc, pDst, pGC,
                        cx1 + dx - xorg, cy1 + dy - yorg,
                        cx2 - cx1, cy2 - cy1,
                        cx1 - xorg, cy1 - yorg);

                GCOP_WRAP;
            }
        }

        CLIP_RESTORE;
    }
    else /* IS_BANKED(pSrc) && IS_BANKED(pDst) */
    {
        CLIP_SAVE;

        fExpose = pGC->fExpose;

        fastBox.x1 = srcx + pSrc->x;
        fastBox.y1 = srcy + pSrc->y;
        fastBox.x2 = fastBox.x1 + w;
        fastBox.y2 = fastBox.y1 + h;

        dx = dstx - fastBox.x1;
        dy = dsty - fastBox.y1;
        if (pGC->miTranslate)
        {
            xorg = pDst->x;
            yorg = pDst->y;
        }

        /*
         * Clip against the source.  Otherwise we will blit too much for SINGLE
         * and SHARED banked systems.
         */
        freeSrcClip = FALSE;
        fastClip    = FALSE;
        fastExpose  = FALSE;

        if (pGC->subWindowMode != IncludeInferiors)
            prgnSrcClip = &((WindowPtr)pSrc)->clipList;
        else if (!((WindowPtr)pSrc)->parent)
            fastClip = TRUE;
        else if ((pSrc == pDst) && (pGC->clientClipType == CT_NONE))
            prgnSrcClip = pGC->pCompositeClip;
        else
        {
            prgnSrcClip = NotClippedByChildren((WindowPtr)pSrc);
            freeSrcClip = TRUE;
        }

        if (fastClip)
        {
            fastExpose = TRUE;

            /*
             * Clip the source.  If regions extend beyond the source size, make
             * sure exposure events get sent.
             */
            if (fastBox.x1 < pSrc->x)
            {
                fastBox.x1 = pSrc->x;
                fastExpose = FALSE;
            }
            if (fastBox.y1 < pSrc->y)
            {
                fastBox.y1 = pSrc->y;
                fastExpose = FALSE;
            }
            if (fastBox.x2 > pSrc->x + (int) pSrc->width)
            {
                fastBox.x2 = pSrc->x + (int) pSrc->width;
                fastExpose = FALSE;
            }
            if (fastBox.y2 > pSrc->y + (int) pSrc->height)
            {
                fastBox.y2 = pSrc->y + (int) pSrc->height;
                fastExpose = FALSE;
            }

            nBox = 1;
            pBox = &fastBox;
        }
        else
        {
            REGION_INIT(pScreen, &rgnDst, &fastBox, 1);
            REGION_INTERSECT(pScreen, &rgnDst, &rgnDst, prgnSrcClip);
            pBox = REGION_RECTS(&rgnDst);
            nBox = REGION_NUM_RECTS(&rgnDst);
        }

        /*
         * fastBlit can only be TRUE if we don't need to worry about attempts
         * to read partial pixels through the destination bank.
         */
        if (SinglePlane)
            fastBlit = pGCPriv->fastPlane;
        else
            fastBlit = pGCPriv->fastCopy;

        nQueue = nBox * pScreenPriv->maxRects * 2;
        pQueue = Queue = xalloc_ARRAY(miBankQueue, nQueue);

        if (Queue)
        {
            for (;  nBox--;  pBox++)
            {
                ns = FirstBankOf(pBox->x1, pBox->y1);
                nse = LastBankOf(pBox->x2, pBox->y2);
                for (;  ns <= nse;  ns++)
                {
                    if (!pScreenPriv->pBanks[ns])
                        continue;

                    nBoxClipSrc = REGION_NUM_RECTS(pScreenPriv->pBanks[ns]);
                    pBoxClipSrc = REGION_RECTS(pScreenPriv->pBanks[ns]);

                    for (;  nBoxClipSrc--;  pBoxClipSrc++)
                    {
                        cx1 = max(pBox->x1, pBoxClipSrc->x1);
                        cy1 = max(pBox->y1, pBoxClipSrc->y1);
                        cx2 = min(pBox->x2, pBoxClipSrc->x2);
                        cy2 = min(pBox->y2, pBoxClipSrc->y2);

                        /* Check to see if the region is empty */
                        if ((cx1 >= cx2) || (cy1 >= cy2))
                            continue;

                        /* Translate c[xy]* to destination coordinates */
                        cx1 += dx + xorg;
                        cy1 += dy + yorg;
                        cx2 += dx + xorg;
                        cy2 += dy + yorg;

                        nd = FirstBankOf(cx1, cy1);
                        nde = LastBankOf(cx2, cy2);
                        for (;  nd <= nde;  nd++)
                        {
                            if (!pGCPriv->pBankedClips[nd])
                                continue;

                            /*
                             * Clients can send quite large clip descriptions,
                             * so use the bank clips here instead.
                             */
                            nBoxClipDst =
                                REGION_NUM_RECTS(pScreenPriv->pBanks[nd]);
                            pBoxClipDst =
                                REGION_RECTS(pScreenPriv->pBanks[nd]);

                            for (;  nBoxClipDst--;  pBoxClipDst++)
                            {
                                ccBox.x1 = max(cx1, pBoxClipDst->x1);
                                ccBox.y1 = max(cy1, pBoxClipDst->y1);
                                ccBox.x2 = min(cx2, pBoxClipDst->x2);
                                ccBox.y2 = min(cy2, pBoxClipDst->y2);

                                /* Check to see if the region is empty */
                                if ((ccBox.x1 >= ccBox.x2) ||
                                    (ccBox.y1 >= ccBox.y2))
                                    continue;

                                pQueue->srcBankNo = ns;
                                pQueue->dstBankNo = nd;
                                pQueue->x         = ccBox.x1 - xorg;
                                pQueue->y         = ccBox.y1 - yorg;
                                pQueue->w         = ccBox.x2 - ccBox.x1;
                                pQueue->h         = ccBox.y2 - ccBox.y1;

                                if (maxWidth < pQueue->w)
                                    maxWidth = pQueue->w;
                                if (maxHeight < pQueue->h)
                                    maxHeight = pQueue->h;

                                /*
                                 * When shared banking is used and the source
                                 * and destination banks differ, prevent
                                 * attempts to fetch partial scanline pad units
                                 * through the destination bank.
                                 */
                                pQueue->fastBlit = fastBlit;
                                if (fastBlit &&
                                    (pScreenPriv->type == BANK_SHARED) &&
                                    (ns != nd) &&
                                    ((ccBox.x1 %
                                      pScreenPriv->nPixelsPerScanlinePadUnit) ||
                                     (ccBox.x2 %
                                      pScreenPriv->nPixelsPerScanlinePadUnit) ||
                                     (RECT_IN_REGION(pScreen,
                                       pGCPriv->pBankedClips[nd], &ccBox) !=
                                      rgnIN)))
                                    pQueue->fastBlit = FALSE;
                                pQueue++;
                            }
                        }
                    }
                }
            }
        }

        if (!fastClip)
        {
            REGION_UNINIT(pScreen, &rgnDst);
            if (freeSrcClip)
                REGION_DESTROY(pScreen, prgnSrcClip);
        }

        pQueueNew = pQueue;
        nQueue = pQueue - Queue;

        if (nQueue > 0)
        {
            BANK_SAVE;

            pQueue = Queue;

            if ((nQueue > 1) &&
                ((pSrc == pDst) || (pGC->subWindowMode == IncludeInferiors)))
            {
                if ((srcy + pSrc->y) < (dsty + yorg))
                {
                    /* Sort from bottom to top */
                    pQueueBase = pQueueNext = pQueue + nQueue - 1;

                    while (pQueueBase >= pQueue)
                    {
                        while ((pQueueNext >= pQueue) &&
                               (pQueueBase->y == pQueueNext->y))
                            pQueueNext--;

                        pQueueTmp = pQueueNext + 1;
                        while (pQueueTmp <= pQueueBase)
                            *pQueueNew++ = *pQueueTmp++;

                        pQueueBase = pQueueNext;
                    }

                    pQueueNew -= nQueue;
                    pQueue = pQueueNew;
                    pQueueNew = Queue;
                }

                if ((srcx + pSrc->x) < (dstx + xorg))
                {
                    /* Sort from right to left */
                    pQueueBase = pQueueNext = pQueue;

                    while (pQueueBase < pQueue + nQueue)
                    {
                        while ((pQueueNext < pQueue + nQueue) &&
                               (pQueueNext->y == pQueueBase->y))
                            pQueueNext++;

                        pQueueTmp = pQueueNext;
                        while (pQueueTmp != pQueueBase)
                            *pQueueNew++ = *--pQueueTmp;

                        pQueueBase = pQueueNext;
                    }

                    pQueueNew -= nQueue;
                    pQueue = pQueueNew;
                }
            }

            paddedWidth = PixmapBytePad(maxWidth,
                pScreenPriv->pScreenPixmap->drawable.depth);
            pImage = (char *)xalloc(paddedWidth * maxHeight);

            pGC->fExpose = FALSE;

            while (nQueue--)
            {
                pGC->pCompositeClip = pGCPriv->pBankedClips[pQueue->dstBankNo];

                GCOP_UNWRAP;

                if (pQueue->srcBankNo == pQueue->dstBankNo)
                {
                    SET_SINGLE_BANK(pScreenPriv->pScreenPixmap,
                        -1, -1, pQueue->srcBankNo);

                    if (SinglePlane)
                        (*pGC->ops->CopyPlane)(pSrc, pDst, pGC,
                            pQueue->x - dx - pSrc->x, pQueue->y - dy - pSrc->y,
                            pQueue->w, pQueue->h, pQueue->x, pQueue->y, plane);
                    else
                        (*pGC->ops->CopyArea)(pSrc, pDst, pGC,
                            pQueue->x - dx - pSrc->x, pQueue->y - dy - pSrc->y,
                            pQueue->w, pQueue->h, pQueue->x, pQueue->y);
                }
                else if (pQueue->fastBlit)
                {
                    SET_SOURCE_BANK     (pScreenPriv->pBankPixmap,
                        pScreenPriv->pScreenPixmap->drawable.width,
                        pScreenPriv->pScreenPixmap->devKind,
                        pQueue->srcBankNo);
                    SET_DESTINATION_BANK(pScreenPriv->pScreenPixmap,
                        -1, -1, pQueue->dstBankNo);

                    if (SinglePlane)
                        (*pGC->ops->CopyPlane)(
                            (DrawablePtr)pScreenPriv->pBankPixmap, pDst, pGC,
                            pQueue->x - dx, pQueue->y - dy,
                            pQueue->w, pQueue->h, pQueue->x, pQueue->y, plane);
                    else
                        (*pGC->ops->CopyArea)(
                            (DrawablePtr)pScreenPriv->pBankPixmap, pDst, pGC,
                            pQueue->x - dx, pQueue->y - dy,
                            pQueue->w, pQueue->h, pQueue->x, pQueue->y);
                }
                else if (pImage)
                {
                    ModifyPixmap(pScreenPriv->pBankPixmap,
                        maxWidth, paddedWidth, pImage);

                    SET_SINGLE_BANK(pScreenPriv->pScreenPixmap,
                        -1, -1, pQueue->srcBankNo);

                    (*pScreenPriv->pBankGC->ops->CopyArea)(
                        pSrc, (DrawablePtr)pScreenPriv->pBankPixmap,
                        pScreenPriv->pBankGC,
                        pQueue->x - dx - pSrc->x, pQueue->y - dy - pSrc->y,
                        pQueue->w, pQueue->h, 0, 0);

                    SET_SINGLE_BANK(pScreenPriv->pScreenPixmap,
                        -1, -1, pQueue->dstBankNo);

                    if (SinglePlane)
                        (*pGC->ops->CopyPlane)(
                            (DrawablePtr)pScreenPriv->pBankPixmap,
                            pDst, pGC, 0, 0, pQueue->w, pQueue->h,
                            pQueue->x, pQueue->y, plane);
                    else
                        (*pGC->ops->CopyArea)(
                            (DrawablePtr)pScreenPriv->pBankPixmap,
                            pDst, pGC, 0, 0, pQueue->w, pQueue->h,
                            pQueue->x, pQueue->y);
                }

                GCOP_WRAP;

                pQueue++;
            }

            xfree(pImage);

            BANK_RESTORE;
        }

        CLIP_RESTORE;

        pGC->fExpose = fExpose;

        xfree(Queue);
    }

    SCREEN_RESTORE;

    if (!fExpose || fastExpose)
        return ret;

    return miHandleExposures(pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty, 0);
}

static RegionPtr
miBankCopyArea(
    DrawablePtr pSrc,
    DrawablePtr pDst,
    GCPtr       pGC,
    int         srcx,
    int         srcy,
    int         w,
    int         h,
    int         dstx,
    int         dsty
)
{
    return miBankCopy(pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty, 0, FALSE);
}

static RegionPtr
miBankCopyPlane(
    DrawablePtr   pSrc,
    DrawablePtr   pDst,
    GCPtr         pGC,
    int           srcx,
    int           srcy,
    int           w,
    int           h,
    int           dstx,
    int           dsty,
    unsigned long plane
)
{
    return
        miBankCopy(pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty, plane, TRUE);
}

static void
miBankPolyPoint(
    DrawablePtr pDrawable,
    GCPtr       pGC,
    int         mode,
    int         nArray,
    xPoint      *pArray
)
{
#   define GCOP_ARGS GCOP_0D_ARGS
    GCOP_COMPLEX(PolyPoint, xPoint);
#   undef GCOP_ARGS
}

static void
miBankPolylines(
    DrawablePtr pDrawable,
    GCPtr       pGC,
    int         mode,
    int         nArray,
    DDXPointPtr pArray
)
{
#   define GCOP_ARGS GCOP_0D_ARGS
    GCOP_COMPLEX(Polylines, DDXPointRec);
#   undef GCOP_ARGS
}

static void
miBankPolySegment(
    DrawablePtr pDrawable,
    GCPtr       pGC,
    int         nArray,
    xSegment    *pArray
)
{
#   define GCOP_ARGS GCOP_1D_ARGS
    GCOP_COMPLEX(PolySegment, xSegment);
#   undef GCOP_ARGS
}

static void
miBankPolyRectangle(
    DrawablePtr pDrawable,
    GCPtr       pGC,
    int         nArray,
    xRectangle  *pArray
)
{
#   define GCOP_ARGS GCOP_1D_ARGS
    GCOP_COMPLEX(PolyRectangle, xRectangle);
#   undef GCOP_ARGS
}

static void
miBankPolyArc(
    DrawablePtr pDrawable,
    GCPtr       pGC,
    int         nArray,
    xArc        *pArray
)
{
#   define GCOP_ARGS GCOP_1D_ARGS
    GCOP_COMPLEX(PolyArc, xArc);
#   undef GCOP_ARGS
}

static void
miBankFillPolygon(
    DrawablePtr pDrawable,
    GCPtr       pGC,
    int         shape,
    int         mode,
    int         nArray,
    DDXPointRec *pArray
)
{
#   define GCOP_ARGS GCOP_2D_ARGS
    GCOP_COMPLEX(FillPolygon, DDXPointRec);
#   undef GCOP_ARGS
}

static void
miBankPolyFillRect(
    DrawablePtr pDrawable,
    GCPtr       pGC,
    int         nArray,
    xRectangle  *pArray
)
{
#   define GCOP_ARGS GCOP_1D_ARGS
    GCOP_COMPLEX(PolyFillRect, xRectangle);
#   undef GCOP_ARGS
}

static void
miBankPolyFillArc(
    DrawablePtr pDrawable,
    GCPtr       pGC,
    int         nArray,
    xArc        *pArray
)
{
#   define GCOP_ARGS GCOP_1D_ARGS
    GCOP_COMPLEX(PolyFillArc, xArc);
#   undef GCOP_ARGS
}

static int
miBankPolyText8(
    DrawablePtr pDrawable,
    GCPtr       pGC,
    int         x,
    int         y,
    int         nArray,
    char        *pchar
)
{
    int retval = x;

    GCOP_SIMPLE(retval =
        (*pGC->ops->PolyText8)(pDrawable, pGC, x, y, nArray, pchar));

    return retval;
}

static int
miBankPolyText16(
    DrawablePtr    pDrawable,
    GCPtr          pGC,
    int            x,
    int            y,
    int            nArray,
    unsigned short *pchar
)
{
    int retval = x;

    GCOP_SIMPLE(retval =
        (*pGC->ops->PolyText16)(pDrawable, pGC, x, y, nArray, pchar));

    return retval;
}

static void
miBankImageText8(
    DrawablePtr pDrawable,
    GCPtr       pGC,
    int         x,
    int         y,
    int         nArray,
    char        *pchar
)
{
    GCOP_SIMPLE((*pGC->ops->ImageText8)(pDrawable, pGC, x, y, nArray, pchar));
}

static void
miBankImageText16(
    DrawablePtr    pDrawable,
    GCPtr          pGC,
    int            x,
    int            y,
    int            nArray,
    unsigned short *pchar
)
{
    GCOP_SIMPLE((*pGC->ops->ImageText16)(pDrawable, pGC, x, y, nArray, pchar));
}

static void
miBankImageGlyphBlt(
    DrawablePtr  pDrawable,
    GCPtr        pGC,
    int          x,
    int          y,
    unsigned int nArray,
    CharInfoPtr  *ppci,
    pointer      pglyphBase
)
{
    GCOP_SIMPLE((*pGC->ops->ImageGlyphBlt)(pDrawable, pGC,
        x, y, nArray, ppci, pglyphBase));
}

static void
miBankPolyGlyphBlt(
    DrawablePtr  pDrawable,
    GCPtr        pGC,
    int          x,
    int          y,
    unsigned int nArray,
    CharInfoPtr  *ppci,
    pointer      pglyphBase
)
{
    GCOP_SIMPLE((*pGC->ops->PolyGlyphBlt)(pDrawable, pGC,
        x, y, nArray, ppci, pglyphBase));
}

static void
miBankPushPixels(
    GCPtr       pGC,
    PixmapPtr   pBitmap,
    DrawablePtr pDrawable,
    int         w,
    int         h,
    int         x,
    int         y
)
{
    if ((w > 0) && (h > 0))
    {
        GCOP_INIT;
        SCREEN_SAVE;

        if (!IS_BANKED(pDrawable))
        {
            GCOP_UNWRAP;

            (*pGC->ops->PushPixels)(pGC, pBitmap, pDrawable, w, h, x, y);

            GCOP_WRAP;
        }
        else
        {
            int i, j;

            CLIP_SAVE;

            i = FirstBankOf(x,     y);
            j =  LastBankOf(x + w, y + h);
            for (;  i <= j;  i++)
            {
                if (!(pGC->pCompositeClip = pGCPriv->pBankedClips[i]))
                    continue;

                GCOP_UNWRAP;

                SET_SINGLE_BANK(pScreenPriv->pScreenPixmap, -1, -1, i);

                (*pGC->ops->PushPixels)(pGC, pBitmap, pDrawable, w, h, x, y);

                GCOP_WRAP;
            }

            CLIP_RESTORE;
        }

        SCREEN_RESTORE;
    }
}

static GCOps miBankGCOps =
{
    miBankFillSpans,
    miBankSetSpans,
    miBankPutImage,
    miBankCopyArea,
    miBankCopyPlane,
    miBankPolyPoint,
    miBankPolylines,
    miBankPolySegment,
    miBankPolyRectangle,
    miBankPolyArc,
    miBankFillPolygon,
    miBankPolyFillRect,
    miBankPolyFillArc,
    miBankPolyText8,
    miBankPolyText16,
    miBankImageText8,
    miBankImageText16,
    miBankImageGlyphBlt,
    miBankPolyGlyphBlt,
    miBankPushPixels,
    {NULL}              /* devPrivate */
};

/********************
 * GCFuncs wrappers *
 ********************/

static void
miBankValidateGC(
    GCPtr         pGC,
    unsigned long changes,
    DrawablePtr   pDrawable
)
{
    GC_INIT(pGC);
    GC_UNWRAP(pGC);

    (*pGC->funcs->ValidateGC)(pGC, changes, pDrawable);

    if ((changes & (GCClipXOrigin|GCClipYOrigin|GCClipMask|GCSubwindowMode)) ||
        (pDrawable->serialNumber != (pGC->serialNumber & DRAWABLE_SERIAL_BITS)))
    {
        ScreenPtr     pScreen = pGC->pScreen;
        RegionPtr     prgnClip;
        unsigned long planemask;
        int           i;

        SCREEN_INIT;
        SCREEN_SAVE;

        if (IS_BANKED(pDrawable))
        {
            for (i = 0;  i < pScreenPriv->nBanks;  i++)
            {
                if (!pScreenPriv->pBanks[i])
                    continue;

                if (!(prgnClip = pGCPriv->pBankedClips[i]))
                    prgnClip = REGION_CREATE(pScreen, NULL, 1);

                REGION_INTERSECT(pScreen, prgnClip,
                    pScreenPriv->pBanks[i], pGC->pCompositeClip);

                if ((REGION_NUM_RECTS(prgnClip) <= 1) &&
                    ((prgnClip->extents.x1 == prgnClip->extents.x2) ||
                     (prgnClip->extents.y1 == prgnClip->extents.y2)))
                {
                    REGION_DESTROY(pScreen, prgnClip);
                    pGCPriv->pBankedClips[i] = NULL;
                }
                else
                    pGCPriv->pBankedClips[i] = prgnClip;
            }

            /*
             * fastCopy and fastPlane can only be TRUE if we don't need to
             * worry about attempts to read partial pixels through the
             * destination bank.
             */
            switch (pScreenPriv->type)
            {
                case BANK_SHARED:
                    pGCPriv->fastCopy = pGCPriv->fastPlane = FALSE;

                    if ((pGC->alu != GXclear) && (pGC->alu != GXcopy) &&
                        (pGC->alu != GXcopyInverted) && (pGC->alu != GXset))
                        break;

                    if (pScreen->rootDepth == 1)
                        pGCPriv->fastPlane = TRUE;

                    /* This is probably paranoia */
                    if ((pDrawable->depth != pScreen->rootDepth) ||
                        (pDrawable->depth != pGC->depth))
                        break;

                    planemask = (1 << pGC->depth) - 1;
                    if ((pGC->planemask & planemask) == planemask)
                        pGCPriv->fastCopy = TRUE;

                    break;

                case BANK_DOUBLE:
                    pGCPriv->fastCopy = pGCPriv->fastPlane = TRUE;
                    break;

                default:
                    pGCPriv->fastCopy = pGCPriv->fastPlane = FALSE;
                    break;
            }
        }
        else
        {
            /*
             * Here we are on a pixmap and don't need all that special clipping
             * stuff, hence free it.
             */
            for (i = 0;  i < pScreenPriv->nBanks;  i++)
            {
                if (!pGCPriv->pBankedClips[i])
                    continue;

                REGION_DESTROY(pScreen, pGCPriv->pBankedClips[i]);
                pGCPriv->pBankedClips[i] = NULL;
            }
        }

        SCREEN_RESTORE;
    }

    GC_WRAP(pGC);
}

static void
miBankChangeGC(
    GCPtr         pGC,
    unsigned long mask
)
{
    GC_INIT(pGC);
    GC_UNWRAP(pGC);

    (*pGC->funcs->ChangeGC)(pGC, mask);

    GC_WRAP(pGC);
}

static void
miBankCopyGC(
    GCPtr         pGCSrc,
    unsigned long mask,
    GCPtr         pGCDst
)
{
    GC_INIT(pGCDst);
    GC_UNWRAP(pGCDst);

    (*pGCDst->funcs->CopyGC)(pGCSrc, mask, pGCDst);

    GC_WRAP(pGCDst);
}

static void
miBankDestroyGC(
    GCPtr pGC
)
{
    ScreenPtr pScreen = pGC->pScreen;
    int       i;

    SCREEN_INIT;
    GC_INIT(pGC);
    GC_UNWRAP(pGC);

    (*pGC->funcs->DestroyGC)(pGC);

    for (i = 0;  i < pScreenPriv->nBanks;  i++)
    {
        if (!pGCPriv->pBankedClips[i])
            continue;

        REGION_DESTROY(pScreen, pGCPriv->pBankedClips[i]);
        pGCPriv->pBankedClips[i] = NULL;
    }

    GC_WRAP(pGC);
}

static void
miBankChangeClip(
    GCPtr   pGC,
    int     type,
    pointer pvalue,
    int     nrects
)
{
    GC_INIT(pGC);
    GC_UNWRAP(pGC);

    (*pGC->funcs->ChangeClip)(pGC, type, pvalue, nrects);

    GC_WRAP(pGC);
}

static void
miBankDestroyClip(
    GCPtr pGC
)
{
    GC_INIT(pGC);
    GC_UNWRAP(pGC);

    (*pGC->funcs->DestroyClip)(pGC);

    GC_WRAP(pGC);
}

static void
miBankCopyClip(
    GCPtr pGCDst,
    GCPtr pGCSrc
)
{
    GC_INIT(pGCDst);
    GC_UNWRAP(pGCDst);

    (*pGCDst->funcs->CopyClip)(pGCDst, pGCSrc);

    GC_WRAP(pGCDst);
}

static GCFuncs miBankGCFuncs =
{
    miBankValidateGC,
    miBankChangeGC,
    miBankCopyGC,
    miBankDestroyGC,
    miBankChangeClip,
    miBankDestroyClip,
    miBankCopyClip
};

/*******************
 * Screen Wrappers *
 *******************/

static Bool
miBankCreateScreenResources(
    ScreenPtr pScreen
)
{
    Bool retval;

    SCREEN_INIT;
    SCREEN_UNWRAP(CreateScreenResources);

    if ((retval = (*pScreen->CreateScreenResources)(pScreen)))
    {
        /* Set screen buffer address to something recognizable */
        pScreenPriv->pScreenPixmap = (*pScreen->GetScreenPixmap)(pScreen);
        pScreenPriv->pbits = pScreenPriv->pScreenPixmap->devPrivate.ptr;
        pScreenPriv->pScreenPixmap->devPrivate.ptr = (pointer)pScreenPriv;

        /* Get shadow pixmap;  width & height of 0 means no pixmap data */
        pScreenPriv->pBankPixmap = (*pScreen->CreatePixmap)(pScreen, 0, 0,
            pScreenPriv->pScreenPixmap->drawable.depth, 0);
        if (!pScreenPriv->pBankPixmap)
            retval = FALSE;
    }

    /* Shadow the screen */
    if (retval)
        retval = (*pScreen->ModifyPixmapHeader)(pScreenPriv->pBankPixmap,
            pScreenPriv->pScreenPixmap->drawable.width,
            pScreenPriv->pScreenPixmap->drawable.height,
            pScreenPriv->pScreenPixmap->drawable.depth,
            pScreenPriv->pScreenPixmap->drawable.bitsPerPixel,
            pScreenPriv->pScreenPixmap->devKind, NULL);

    /* Create shadow GC */
    if (retval)
    {
        pScreenPriv->pBankGC = CreateScratchGC(pScreen,
            pScreenPriv->pBankPixmap->drawable.depth);
        if (!pScreenPriv->pBankGC)
            retval = FALSE;
    }

    /* Validate shadow GC */
    if (retval)
    {
        pScreenPriv->pBankGC->graphicsExposures = FALSE;
        pScreenPriv->pBankGC->subWindowMode = IncludeInferiors;
        ValidateGC((DrawablePtr)pScreenPriv->pBankPixmap,
            pScreenPriv->pBankGC);
    }

    SCREEN_WRAP(CreateScreenResources, miBankCreateScreenResources);

    return retval;
}

static Bool
miBankModifyPixmapHeader(
    PixmapPtr pPixmap,
    int       width,
    int       height,
    int       depth,
    int       bitsPerPixel,
    int       devKind,
    pointer   pPixData
)
{
    Bool retval = FALSE;

    if (pPixmap)
    {
        ScreenPtr pScreen = pPixmap->drawable.pScreen;

        SCREEN_INIT;
        PIXMAP_SAVE(pPixmap);
        SCREEN_UNWRAP(ModifyPixmapHeader);

        retval = (*pScreen->ModifyPixmapHeader)(pPixmap, width, height,
            depth, bitsPerPixel, devKind, pPixData);

        SCREEN_WRAP(ModifyPixmapHeader, miBankModifyPixmapHeader);

        if (pbits == (pointer)pScreenPriv)
        {
            pScreenPriv->pbits = pPixmap->devPrivate.ptr;
            pPixmap->devPrivate.ptr = pbits;
        }
    }

    return retval;
}

static Bool
miBankCloseScreen(
    int       nIndex,
    ScreenPtr pScreen
)
{
    int i;

    SCREEN_INIT;

    /* Free shadow GC */
    FreeScratchGC(pScreenPriv->pBankGC);

    /* Free shadow pixmap */
    (*pScreen->DestroyPixmap)(pScreenPriv->pBankPixmap);

    /* Restore screen pixmap devPrivate pointer */
    pScreenPriv->pScreenPixmap->devPrivate.ptr = pScreenPriv->pbits;

    /* Delete bank clips */
    for (i = 0;  i < pScreenPriv->nBanks;  i++)
        if (pScreenPriv->pBanks[i])
            REGION_DESTROY(pScreen, pScreenPriv->pBanks[i]);

    Xfree(pScreenPriv->pBanks);

    SCREEN_UNWRAP(CreateScreenResources);
    SCREEN_UNWRAP(ModifyPixmapHeader);
    SCREEN_UNWRAP(CloseScreen);
    SCREEN_UNWRAP(GetImage);
    SCREEN_UNWRAP(GetSpans);
    SCREEN_UNWRAP(CreateGC);
    SCREEN_UNWRAP(CopyWindow);

    Xfree(pScreenPriv);
    return (*pScreen->CloseScreen)(nIndex, pScreen);
}

static void
miBankGetImage(
    DrawablePtr   pDrawable,
    int           sx,
    int           sy,
    int           w,
    int           h,
    unsigned int  format,
    unsigned long planemask,
    char          *pImage
)
{
    if ((w > 0) && (h > 0))
    {
        ScreenPtr pScreen = pDrawable->pScreen;

        SCREEN_INIT;
        SCREEN_STATUS;
        SCREEN_UNWRAP(GetImage);

        if (!IS_BANKED(pDrawable))
        {
            (*pScreen->GetImage)(pDrawable, sx, sy, w, h,
                format, planemask, pImage);
        }
        else
        {
            int  paddedWidth;
            char *pBankImage;

            paddedWidth = PixmapBytePad(w,
                pScreenPriv->pScreenPixmap->drawable.depth);
            pBankImage = (char *)xalloc(paddedWidth * h);

            if (pBankImage)
            {
                BANK_SAVE;

                ModifyPixmap(pScreenPriv->pBankPixmap, w, paddedWidth,
                    pBankImage);

                (*pScreenPriv->pBankGC->ops->CopyArea)(
                    (DrawablePtr)WindowTable[pScreen->myNum],
                    (DrawablePtr)pScreenPriv->pBankPixmap,
                    pScreenPriv->pBankGC,
                    sx + pDrawable->x, sy + pDrawable->y, w, h, 0, 0);

                (*pScreen->GetImage)((DrawablePtr)pScreenPriv->pBankPixmap,
                    0, 0, w, h, format, planemask, pImage);

                BANK_RESTORE;

                xfree(pBankImage);
            }
        }

        SCREEN_WRAP(GetImage, miBankGetImage);
    }
}

static void
miBankGetSpans(
    DrawablePtr pDrawable,
    int         wMax,
    DDXPointPtr ppt,
    int         *pwidth,
    int         nspans,
    char        *pImage
)
{
    if (nspans > 0)
    {
        ScreenPtr pScreen = pDrawable->pScreen;

        SCREEN_INIT;
        SCREEN_STATUS;
        SCREEN_UNWRAP(GetSpans);

        if (!IS_BANKED(pDrawable))
        {
            (*pScreen->GetSpans)(pDrawable, wMax, ppt, pwidth, nspans, pImage);
        }
        else
        {
            char        *pBankImage;
            int         paddedWidth;
            DDXPointRec pt;

            pt.x = pt.y = 0;

            paddedWidth =
                PixmapBytePad(pScreenPriv->pScreenPixmap->drawable.width,
                    pScreenPriv->pScreenPixmap->drawable.depth);
            pBankImage = (char *)xalloc(paddedWidth);

            if (pBankImage)
            {
                BANK_SAVE;

                ModifyPixmap(pScreenPriv->pBankPixmap,
                    pScreenPriv->pScreenPixmap->drawable.width,
                    paddedWidth, pBankImage);

                for (;  nspans--;  ppt++, pwidth++)
                {
                    if (*pwidth <= 0)
                        continue;

                    (*pScreenPriv->pBankGC->ops->CopyArea)(
                        (DrawablePtr)WindowTable[pScreen->myNum],
                        (DrawablePtr)pScreenPriv->pBankPixmap,
                        pScreenPriv->pBankGC,
                        ppt->x, ppt->y, *pwidth, 1, 0, 0);

                    (*pScreen->GetSpans)((DrawablePtr)pScreenPriv->pBankPixmap,
                        wMax, &pt, pwidth, 1, pImage);

                    pImage = pImage + PixmapBytePad(*pwidth, pDrawable->depth);
                }

                BANK_RESTORE;

                xfree(pBankImage);
            }
        }

        SCREEN_WRAP(GetSpans, miBankGetSpans);
    }
}

static Bool
miBankCreateGC(
    GCPtr pGC
)
{
    ScreenPtr   pScreen = pGC->pScreen;
    miBankGCPtr pGCPriv = BANK_GCPRIVATE(pGC);
    Bool        ret;

    SCREEN_INIT;
    SCREEN_UNWRAP(CreateGC);

    if ((ret = (*pScreen->CreateGC)(pGC)))
    {
        pGCPriv->unwrappedOps = &miBankGCOps;
        pGCPriv->unwrappedFuncs = &miBankGCFuncs;
        GC_WRAP(pGC);

        memset(&pGCPriv->pBankedClips, 0,
            pScreenPriv->nBanks * sizeof(pGCPriv->pBankedClips));
    }

    SCREEN_WRAP(CreateGC, miBankCreateGC);

    return ret;
}

static void
miBankCopyWindow(
    WindowPtr   pWindow,
    DDXPointRec ptOldOrg,
    RegionPtr   pRgnSrc
)
{
    ScreenPtr   pScreen = pWindow->drawable.pScreen;
    GCPtr       pGC;
    int         dx, dy, nBox;
    DrawablePtr pDrawable = (DrawablePtr)WindowTable[pScreen->myNum];
    RegionPtr   pRgnDst;
    BoxPtr      pBox, pBoxTmp, pBoxNext, pBoxBase, pBoxNew1, pBoxNew2;
    XID         subWindowMode = IncludeInferiors;

    pGC = GetScratchGC(pDrawable->depth, pScreen);

    ChangeGC(pGC, GCSubwindowMode, &subWindowMode);
    ValidateGC(pDrawable, pGC);

    pRgnDst = REGION_CREATE(pScreen, NULL, 1);

    dx = ptOldOrg.x - pWindow->drawable.x;
    dy = ptOldOrg.y - pWindow->drawable.y;
    REGION_TRANSLATE(pScreen, pRgnSrc, -dx, -dy);
    REGION_INTERSECT(pScreen, pRgnDst, &pWindow->borderClip, pRgnSrc);

    pBox = REGION_RECTS(pRgnDst);
    nBox = REGION_NUM_RECTS(pRgnDst);

    pBoxNew1 = NULL;
    pBoxNew2 = NULL;

    if (nBox > 1)
    {
        if (dy < 0)
        {
            /* Sort boxes from bottom to top */
            pBoxNew1 = xalloc_ARRAY(BoxRec, nBox);

            if (pBoxNew1)
            {
                pBoxBase = pBoxNext = pBox + nBox - 1;

                while (pBoxBase >= pBox)
                {
                    while ((pBoxNext >= pBox) &&
                           (pBoxBase->y1 == pBoxNext->y1))
                        pBoxNext--;

                    pBoxTmp = pBoxNext + 1;

                    while (pBoxTmp <= pBoxBase)
                        *pBoxNew1++ = *pBoxTmp++;

                    pBoxBase = pBoxNext;
                }

                pBoxNew1 -= nBox;
                pBox = pBoxNew1;
            }
        }

        if (dx < 0)
        {
            /* Sort boxes from right to left */
            pBoxNew2 = xalloc_ARRAY(BoxRec, nBox);

            if (pBoxNew2)
            {
                pBoxBase = pBoxNext = pBox;

                while (pBoxBase < pBox + nBox)
                {
                    while ((pBoxNext < pBox + nBox) &&
                           (pBoxNext->y1 == pBoxBase->y1))
                        pBoxNext++;

                    pBoxTmp = pBoxNext;

                    while (pBoxTmp != pBoxBase)
                        *pBoxNew2++ = *--pBoxTmp;

                    pBoxBase = pBoxNext;
                }

                pBoxNew2 -= nBox;
                pBox = pBoxNew2;
            }
        }
    }

    while (nBox--)
    {
        (*pGC->ops->CopyArea)(pDrawable, pDrawable, pGC,
            pBox->x1 + dx, pBox->y1 + dy,
            pBox->x2 - pBox->x1, pBox->y2 - pBox->y1,
            pBox->x1, pBox->y1);

        pBox++;
    }

    FreeScratchGC(pGC);

    REGION_DESTROY(pScreen, pRgnDst);

    xfree(pBoxNew2);
    xfree(pBoxNew1);
}

_X_EXPORT Bool
miInitializeBanking(
    ScreenPtr     pScreen,
    unsigned int  xsize,
    unsigned int  ysize,
    unsigned int  width,
    miBankInfoPtr pBankInfo
)
{
    miBankScreenPtr pScreenPriv;
    unsigned long   nBitsPerBank, nBitsPerScanline, nPixelsPerScanlinePadUnit;
    unsigned long   BankBase, ServerPad;
    unsigned int    type, iBank, nBanks, maxRects, we, nBankBPP;
    int             i;

    if (!pBankInfo || !pBankInfo->BankSize)
        return TRUE;            /* No banking required */

    /* Sanity checks */

    if (!pScreen || !xsize || !ysize || (xsize > width) ||
        !pBankInfo->SetSourceBank || !pBankInfo->SetDestinationBank ||
        !pBankInfo->SetSourceAndDestinationBanks ||
        !pBankInfo->pBankA || !pBankInfo->pBankB ||
        !pBankInfo->nBankDepth)
        return FALSE;

    /*
     * DDX *must* have registered a pixmap format whose depth is
     * pBankInfo->nBankDepth.  This is not necessarily the rootDepth
     * pixmap format.
     */
    i = 0;
    while (screenInfo.formats[i].depth != pBankInfo->nBankDepth)
        if (++i >= screenInfo.numPixmapFormats)
            return FALSE;
    nBankBPP = screenInfo.formats[i].bitsPerPixel;

    i = 0;
    while (screenInfo.formats[i].depth != pScreen->rootDepth)
        if (++i >= screenInfo.numPixmapFormats)
            return FALSE;

    if (nBankBPP > screenInfo.formats[i].bitsPerPixel)
        return FALSE;

    /* Determine banking type */
    if ((type = miBankDeriveType(pScreen, pBankInfo)) == BANK_NOBANK)
        return FALSE;

    /* Internal data */

    nBitsPerBank = pBankInfo->BankSize * 8;
    ServerPad = PixmapBytePad(1, pBankInfo->nBankDepth) * 8;
    if (nBitsPerBank % ServerPad)
        return FALSE;
    nBitsPerScanline = PixmapBytePad(width, pBankInfo->nBankDepth) * 8;
    nBanks = ((nBitsPerScanline * (ysize - 1)) +
              (nBankBPP * xsize) + nBitsPerBank - 1) / nBitsPerBank;
    nPixelsPerScanlinePadUnit = miLCM(ServerPad, nBankBPP) / nBankBPP;

    /* Private areas */

    if (miBankGeneration != serverGeneration)
        miBankGeneration = serverGeneration;

    if (!dixRequestPrivate(miBankGCKey,
        (nBanks * sizeof(RegionPtr)) +
            (sizeof(miBankGCRec) - sizeof(RegionPtr))))
        return FALSE;

    if (!(pScreenPriv = (miBankScreenPtr)Xcalloc(sizeof(miBankScreenRec))))
        return FALSE;

    if (!(pScreenPriv->pBanks =                 /* Allocate and clear */
        (RegionPtr *)Xcalloc(nBanks * sizeof(RegionPtr))))
    {
        Xfree(pScreenPriv);
        return FALSE;
    }

    /*
     * Translate banks into clipping regions which are themselves clipped
     * against the screen.  This also ensures that pixels with imbedded bank
     * boundaries are off-screen.
     */

    BankBase = 0;
    maxRects = 0;
    we = 0;
    for (iBank = 0;  iBank < nBanks;  iBank++)
    {
        xRectangle   pRects[3], *pRect = pRects;
        unsigned int xb, yb, xe, ye;

        xb = ((BankBase + nBankBPP - 1) % nBitsPerScanline) / nBankBPP;
        yb =  (BankBase + nBankBPP - 1) / nBitsPerScanline;
        if (xb >= xsize)
        {
            xb = we = 0;
            yb++;
        }
        if (yb >= ysize)
        {
            we = 0;
            break;
        }

        if (we)
            break;

        BankBase += nBitsPerBank;

        we = (BankBase % nBitsPerScanline) % nBankBPP;
        xe = (BankBase % nBitsPerScanline) / nBankBPP;
        ye =  BankBase / nBitsPerScanline;
        if (xe >= xsize)
        {
            we = xe = 0;
            ye++;
        }
        if (ye >= ysize)
        {
            we = xe = 0;
            ye = ysize;
        }

        if (yb == ye)
        {
            if (xb >= xe)
                continue;

            pRect->x      = xb;
            pRect->y      = yb;
            pRect->width  = xe - xb;
            pRect->height = 1;
            maxRects += 2;
            pRect++;
        }
        else
        {
            if (xb)
            {
                pRect->x      = xb;
                pRect->y      = yb++;
                pRect->width  = xsize - xb;
                pRect->height = 1;
                maxRects += 2;
                pRect++;
            }

            if (yb < ye)
            {
                pRect->x      = 0;
                pRect->y      = yb;
                pRect->width  = xsize;
                pRect->height = ye - yb;
                maxRects += min(pRect->height, 3) + 1;
                pRect++;
            }

            if (xe)
            {
                pRect->x      = 0;
                pRect->y      = ye;
                pRect->width  = xe;
                pRect->height = 1;
                maxRects += 2;
                pRect++;
            }
        }

        pScreenPriv->pBanks[iBank] =
            RECTS_TO_REGION(pScreen, pRect - pRects, pRects, 0);
        if (!pScreenPriv->pBanks[iBank] ||
            REGION_NAR(pScreenPriv->pBanks[iBank]))
        {
            we = 1;
            break;
        }
    }

    if (we && (iBank < nBanks))
    {
        for (i = iBank;  i >= 0;  i--)
            if (pScreenPriv->pBanks[i])
                REGION_DESTROY(pScreen, pScreenPriv->pBanks[i]);

        Xfree(pScreenPriv->pBanks);
        Xfree(pScreenPriv);

        return FALSE;
    }

    /* Open for business */

    pScreenPriv->type = type;
    pScreenPriv->nBanks = nBanks;
    pScreenPriv->maxRects = maxRects;
    pScreenPriv->nBankBPP = nBankBPP;
    pScreenPriv->BankInfo = *pBankInfo;
    pScreenPriv->nBitsPerBank = nBitsPerBank;
    pScreenPriv->nBitsPerScanline = nBitsPerScanline;
    pScreenPriv->nPixelsPerScanlinePadUnit = nPixelsPerScanlinePadUnit;

    SCREEN_WRAP(CreateScreenResources, miBankCreateScreenResources);
    SCREEN_WRAP(ModifyPixmapHeader,    miBankModifyPixmapHeader);
    SCREEN_WRAP(CloseScreen,           miBankCloseScreen);
    SCREEN_WRAP(GetImage,              miBankGetImage);
    SCREEN_WRAP(GetSpans,              miBankGetSpans);
    SCREEN_WRAP(CreateGC,              miBankCreateGC);
    SCREEN_WRAP(CopyWindow,            miBankCopyWindow);

    dixSetPrivate(&pScreen->devPrivates, miBankScreenKey, pScreenPriv);

    return TRUE;
}

/* This is used to force GC revalidation when the banking type is changed */
/*ARGSUSED*/
static int
miBankNewSerialNumber(
    WindowPtr pWin,
    pointer   unused
)
{
    pWin->drawable.serialNumber = NEXT_SERIAL_NUMBER;
    return WT_WALKCHILDREN;
}

/* This entry modifies the banking interface */
_X_EXPORT Bool
miModifyBanking(
    ScreenPtr     pScreen,
    miBankInfoPtr pBankInfo
)
{
    unsigned int type;

    if (!pScreen)
        return FALSE;

    if (miBankGeneration == serverGeneration)
    {
        SCREEN_INIT;

        if (pScreenPriv)
        {
            if (!pBankInfo || !pBankInfo->BankSize ||
                !pBankInfo->pBankA || !pBankInfo->pBankB ||
                !pBankInfo->SetSourceBank || !pBankInfo->SetDestinationBank ||
                !pBankInfo->SetSourceAndDestinationBanks)
                return FALSE;

            /* BankSize and nBankDepth cannot, as yet, be changed */
            if ((pScreenPriv->BankInfo.BankSize != pBankInfo->BankSize) ||
                (pScreenPriv->BankInfo.nBankDepth != pBankInfo->nBankDepth))
                return FALSE;

            if ((type = miBankDeriveType(pScreen, pBankInfo)) == BANK_NOBANK)
                return FALSE;

            /* Reset banking info */
            pScreenPriv->BankInfo = *pBankInfo;
            if (type != pScreenPriv->type)
            {
                /*
                 * Banking type is changing.  Revalidate all window GC's.
                 */
                pScreenPriv->type = type;
                WalkTree(pScreen, miBankNewSerialNumber, 0);
            }

            return TRUE;
        }
    }

    if (!pBankInfo || !pBankInfo->BankSize)
        return TRUE;                            /* No change requested */

    return FALSE;
}

/*
 * Given various screen attributes, determine the minimum scanline width such
 * that each scanline is server and DDX padded and any pixels with imbedded
 * bank boundaries are off-screen.  This function returns -1 if such a width
 * cannot exist.  This function exists because the DDX needs to be able to
 * determine this width before initializing a frame buffer.
 */
int
miScanLineWidth(
    unsigned int     xsize,         /* pixels */
    unsigned int     ysize,         /* pixels */
    unsigned int     width,         /* pixels */
    unsigned long    BankSize,      /* char's */
    PixmapFormatRec *pBankFormat,
    unsigned int     nWidthUnit     /* bits */
)
{
    unsigned long nBitsPerBank, nBitsPerScanline, nBitsPerScanlinePadUnit;
    unsigned long minBitsPerScanline, maxBitsPerScanline;

    /* Sanity checks */

    if (!nWidthUnit || !pBankFormat)
        return -1;

    nBitsPerBank = BankSize * 8;
    if (nBitsPerBank % pBankFormat->scanlinePad)
        return -1;

    if (xsize > width)
        width = xsize;
    nBitsPerScanlinePadUnit = miLCM(pBankFormat->scanlinePad, nWidthUnit);
    nBitsPerScanline =
        (((width * pBankFormat->bitsPerPixel) + nBitsPerScanlinePadUnit - 1) /
         nBitsPerScanlinePadUnit) * nBitsPerScanlinePadUnit;
    width = nBitsPerScanline / pBankFormat->bitsPerPixel;

    if (!xsize || !(nBitsPerBank % pBankFormat->bitsPerPixel))
        return (int)width;

    /*
     * Scanlines will be server-pad aligned at this point.  They will also be
     * a multiple of nWidthUnit bits long.  Ensure that pixels with imbedded
     * bank boundaries are off-screen.
     *
     * It seems reasonable to limit total frame buffer size to 1/16 of the
     * theoretical maximum address space size.  On a machine with 32-bit
     * addresses (to 8-bit quantities) this turns out to be 256MB.  Not only
     * does this provide a simple limiting condition for the loops below, but
     * it also prevents unsigned long wraparounds.
     */
    if (!ysize)
        return -1;

    minBitsPerScanline = xsize * pBankFormat->bitsPerPixel;
    if (minBitsPerScanline > nBitsPerBank)
        return -1;

    if (ysize == 1)
        return (int)width;

    maxBitsPerScanline =
        (((unsigned long)(-1) >> 1) - minBitsPerScanline) / (ysize - 1);
    while (nBitsPerScanline <= maxBitsPerScanline)
    {
        unsigned long BankBase, BankUnit;

        BankUnit = ((nBitsPerBank + nBitsPerScanline - 1) / nBitsPerBank) *
            nBitsPerBank;
        if (!(BankUnit % nBitsPerScanline))
            return (int)width;

        for (BankBase = BankUnit;  ;  BankBase += nBitsPerBank)
        {
            unsigned long x, y;

            y = BankBase / nBitsPerScanline;
            if (y >= ysize)
                return (int)width;

            x = BankBase % nBitsPerScanline;
            if (!(x % pBankFormat->bitsPerPixel))
                continue;

            if (x < minBitsPerScanline)
            {
                /*
                 * Skip ahead certain widths by dividing the excess scanline
                 * amongst the y's.
                 */
                y *= nBitsPerScanlinePadUnit;
                nBitsPerScanline +=
                    ((x + y - 1) / y) * nBitsPerScanlinePadUnit;
                width = nBitsPerScanline / pBankFormat->bitsPerPixel;
                break;
            }

            if (BankBase != BankUnit)
                continue;

            if (!(nBitsPerScanline % x))
                return (int)width;

            BankBase = ((nBitsPerScanline - minBitsPerScanline) /
                (nBitsPerScanline - x)) * BankUnit;
        }
    }

    return -1;
}