diff options
author | marha <marha@users.sourceforge.net> | 2009-06-28 22:07:26 +0000 |
---|---|---|
committer | marha <marha@users.sourceforge.net> | 2009-06-28 22:07:26 +0000 |
commit | 3562e78743202e43aec8727005182a2558117eca (patch) | |
tree | 8f9113a77d12470c5c851a2a8e4cb02e89df7d43 /xorg-server/mi/mibank.c | |
download | vcxsrv-3562e78743202e43aec8727005182a2558117eca.tar.gz vcxsrv-3562e78743202e43aec8727005182a2558117eca.tar.bz2 vcxsrv-3562e78743202e43aec8727005182a2558117eca.zip |
Checked in the following released items:
xkeyboard-config-1.4.tar.gz
ttf-bitstream-vera-1.10.tar.gz
font-alias-1.0.1.tar.gz
font-sun-misc-1.0.0.tar.gz
font-sun-misc-1.0.0.tar.gz
font-sony-misc-1.0.0.tar.gz
font-schumacher-misc-1.0.0.tar.gz
font-mutt-misc-1.0.0.tar.gz
font-misc-misc-1.0.0.tar.gz
font-misc-meltho-1.0.0.tar.gz
font-micro-misc-1.0.0.tar.gz
font-jis-misc-1.0.0.tar.gz
font-isas-misc-1.0.0.tar.gz
font-dec-misc-1.0.0.tar.gz
font-daewoo-misc-1.0.0.tar.gz
font-cursor-misc-1.0.0.tar.gz
font-arabic-misc-1.0.0.tar.gz
font-winitzki-cyrillic-1.0.0.tar.gz
font-misc-cyrillic-1.0.0.tar.gz
font-cronyx-cyrillic-1.0.0.tar.gz
font-screen-cyrillic-1.0.1.tar.gz
font-xfree86-type1-1.0.1.tar.gz
font-adobe-utopia-type1-1.0.1.tar.gz
font-ibm-type1-1.0.0.tar.gz
font-bitstream-type1-1.0.0.tar.gz
font-bitstream-speedo-1.0.0.tar.gz
font-bh-ttf-1.0.0.tar.gz
font-bh-type1-1.0.0.tar.gz
font-bitstream-100dpi-1.0.0.tar.gz
font-bh-lucidatypewriter-100dpi-1.0.0.tar.gz
font-bh-100dpi-1.0.0.tar.gz
font-adobe-utopia-100dpi-1.0.1.tar.gz
font-adobe-100dpi-1.0.0.tar.gz
font-util-1.0.1.tar.gz
font-bitstream-75dpi-1.0.0.tar.gz
font-bh-lucidatypewriter-75dpi-1.0.0.tar.gz
font-adobe-utopia-75dpi-1.0.1.tar.gz
font-bh-75dpi-1.0.0.tar.gz
bdftopcf-1.0.1.tar.gz
font-adobe-75dpi-1.0.0.tar.gz
mkfontscale-1.0.6.tar.gz
openssl-0.9.8k.tar.gz
bigreqsproto-1.0.2.tar.gz
xtrans-1.2.2.tar.gz
resourceproto-1.0.2.tar.gz
inputproto-1.4.4.tar.gz
compositeproto-0.4.tar.gz
damageproto-1.1.0.tar.gz
zlib-1.2.3.tar.gz
xkbcomp-1.0.5.tar.gz
freetype-2.3.9.tar.gz
pthreads-w32-2-8-0-release.tar.gz
pixman-0.12.0.tar.gz
kbproto-1.0.3.tar.gz
evieext-1.0.2.tar.gz
fixesproto-4.0.tar.gz
recordproto-1.13.2.tar.gz
randrproto-1.2.2.tar.gz
scrnsaverproto-1.1.0.tar.gz
renderproto-0.9.3.tar.gz
xcmiscproto-1.1.2.tar.gz
fontsproto-2.0.2.tar.gz
xextproto-7.0.3.tar.gz
xproto-7.0.14.tar.gz
libXdmcp-1.0.2.tar.gz
libxkbfile-1.0.5.tar.gz
libfontenc-1.0.4.tar.gz
libXfont-1.3.4.tar.gz
libX11-1.1.5.tar.gz
libXau-1.0.4.tar.gz
libxcb-1.1.tar.gz
xorg-server-1.5.3.tar.gz
Diffstat (limited to 'xorg-server/mi/mibank.c')
-rw-r--r-- | xorg-server/mi/mibank.c | 2378 |
1 files changed, 2378 insertions, 0 deletions
diff --git a/xorg-server/mi/mibank.c b/xorg-server/mi/mibank.c new file mode 100644 index 000000000..ea79e9d36 --- /dev/null +++ b/xorg-server/mi/mibank.c @@ -0,0 +1,2378 @@ +/* + * 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; +} |