/************************************************************ Copyright 1989, 1998 The Open Group 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. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of The Open Group shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from The Open Group. ********************************************************/ #define NEED_REPLIES #define NEED_EVENTS #ifdef HAVE_DIX_CONFIG_H #include #endif #include #include #include #include "misc.h" #include "os.h" #include "windowstr.h" #include "scrnintstr.h" #include "pixmapstr.h" #include "extnsionst.h" #include "dixstruct.h" #include "resource.h" #include "opaque.h" #include "regionstr.h" #include "gcstruct.h" #include "inputstr.h" #include #define _MULTIBUF_SERVER_ /* don't want Xlib structures */ #define _MULTIBUF_PIXMAP_ #include static Bool NoopDDA_True() { return TRUE; } static Bool pixPositionWindow(); static int pixCreateImageBuffers(); static void pixDisplayImageBuffers(); static void pixClearImageBufferArea(); static void pixDeleteBufferDrawable(); static void pixWrapScreenFuncs(); static void pixResetProc(); Bool pixMultibufferInit(pScreen, pMBScreen) ScreenPtr pScreen; mbufScreenPtr pMBScreen; { int i, j, k; xMbufBufferInfo *pInfo; int nInfo; DepthPtr pDepth; mbufPixmapPrivPtr pMBPriv; pMBScreen->CreateImageBuffers = pixCreateImageBuffers; pMBScreen->DestroyImageBuffers = (void (*)())NoopDDA; pMBScreen->DisplayImageBuffers = pixDisplayImageBuffers; pMBScreen->ClearImageBufferArea = pixClearImageBufferArea; pMBScreen->ChangeMBufferAttributes = NoopDDA_True; pMBScreen->ChangeBufferAttributes = NoopDDA_True; pMBScreen->DeleteBufferDrawable = pixDeleteBufferDrawable; pMBScreen->WrapScreenFuncs = pixWrapScreenFuncs; pMBScreen->ResetProc = pixResetProc; /* Support every depth and visual combination that the screen does */ nInfo = 0; for (i = 0; i < pScreen->numDepths; i++) { pDepth = &pScreen->allowedDepths[i]; nInfo += pDepth->numVids; } pInfo = (xMbufBufferInfo *) xalloc (nInfo * sizeof (xMbufBufferInfo)); if (!pInfo) return FALSE; k = 0; for (i = 0; i < pScreen->numDepths; i++) { pDepth = &pScreen->allowedDepths[i]; for (j = 0; j < pDepth->numVids; j++) { pInfo[k].visualID = pDepth->vids[j]; pInfo[k].maxBuffers = 0; pInfo[k].depth = pDepth->depth; k++; } } pMBScreen->nInfo = nInfo; pMBScreen->pInfo = pInfo; /* * Setup the devPrivate to mbufScreenRec */ pMBPriv = (mbufPixmapPrivPtr) xalloc(sizeof(* pMBPriv)); if (!pMBPriv) { xfree(pInfo); return (FALSE); } pMBScreen->devPrivate.ptr = (pointer) pMBPriv; pMBPriv->PositionWindow = NULL; pMBPriv->funcsWrapped = 0; return TRUE; } /*ARGSUSED*/ static int pixCreateImageBuffers (pWin, nbuf, ids, action, hint) WindowPtr pWin; int nbuf; XID *ids; int action; int hint; { mbufWindowPtr pMBWindow; mbufBufferPtr pMBBuffer; ScreenPtr pScreen; int width, height, depth; int i; pMBWindow = MB_WINDOW_PRIV(pWin); width = pWin->drawable.width; height = pWin->drawable.height; depth = pWin->drawable.depth; pScreen = pWin->drawable.pScreen; for (i = 0; i < nbuf; i++) { pMBBuffer = &pMBWindow->buffers[i]; pMBBuffer->pDrawable = (DrawablePtr) (*pScreen->CreatePixmap) (pScreen, width, height, depth); if (!pMBBuffer->pDrawable) break; if (!AddResource (ids[i], MultibufferDrawableResType, (pointer) pMBBuffer->pDrawable)) { (*pScreen->DestroyPixmap) ((PixmapPtr) pMBBuffer->pDrawable); break; } pMBBuffer->pDrawable->id = ids[i]; /* * In the description of the CreateImageBuffers request: * "If the window is mapped, or if these image buffers have * backing store, their contents will be tiled with the window * background, and zero or more expose events will be generated * for each of these buffers." */ (* MB_SCREEN_PRIV(pScreen)->ClearImageBufferArea) (pMBBuffer, 0,0, 0,0, TRUE); } return i; } /* * set up the gc to clear the pixmaps; */ static Bool SetupBackgroundPainter (pWin, pGC) WindowPtr pWin; GCPtr pGC; { XID gcvalues[4]; int ts_x_origin, ts_y_origin; PixUnion background; int backgroundState; Mask gcmask; /* * First take care of any ParentRelative stuff by altering the * tile/stipple origin to match the coordinates of the upper-left * corner of the first ancestor without a ParentRelative background. * This coordinate is, of course, negative. */ ts_x_origin = ts_y_origin = 0; while (pWin->backgroundState == ParentRelative) { ts_x_origin -= pWin->origin.x; ts_y_origin -= pWin->origin.y; pWin = pWin->parent; } backgroundState = pWin->backgroundState; background = pWin->background; switch (backgroundState) { case BackgroundPixel: gcvalues[0] = (XID) background.pixel; gcvalues[1] = FillSolid; gcmask = GCForeground|GCFillStyle; break; case BackgroundPixmap: gcvalues[0] = FillTiled; gcvalues[1] = (XID) background.pixmap; gcvalues[2] = ts_x_origin; gcvalues[3] = ts_y_origin; gcmask = GCFillStyle|GCTile|GCTileStipXOrigin|GCTileStipYOrigin; break; default: return FALSE; } DoChangeGC(pGC, gcmask, gcvalues, TRUE); return TRUE; } static void MultibufferPaintBackgroundRectangles(pWin, pDrawable, nrects, pRects) WindowPtr pWin; DrawablePtr pDrawable; int nrects; xRectangle *pRects; { GCPtr pGC; pGC = GetScratchGC (pWin->drawable.depth, pWin->drawable.pScreen); if (SetupBackgroundPainter(pWin, pGC)) { ValidateGC(pDrawable, pGC); (*pGC->ops->PolyFillRect) (pDrawable, pGC, nrects, pRects); } FreeScratchGC(pGC); } static void MultibufferPaintBackgroundRegion(pWin, pDrawable, pRegion) WindowPtr pWin; DrawablePtr pDrawable; RegionPtr pRegion; { xRectangle *pRects; int nrects = REGION_NUM_RECTS(pRegion); BoxPtr pbox = REGION_RECTS(pRegion); pRects = (xRectangle *)ALLOCATE_LOCAL(nrects * sizeof(xRectangle)); if (pRects) { int i; for (i = 0; i < nrects; i++) { pRects[i].x = pbox->x1; pRects[i].y = pbox->y1; pRects[i].width = pbox->x2 - pbox->x1; pRects[i].height = pbox->y2 - pbox->y1; } MultibufferPaintBackgroundRectangles(pWin, pDrawable, nrects, pRects); DEALLOCATE_LOCAL(pRects); } } static void pixDisplayImageBuffers(pScreen, ppMBWindow, ppMBBuffer, nbuf) mbufBufferPtr *ppMBBuffer; mbufWindowPtr *ppMBWindow; int nbuf; { GCPtr pGC = NULL; PixmapPtr pPrevPixmap, pNewPixmap; WindowPtr pWin; RegionPtr pExposed; int i; mbufBufferPtr pPrevMBBuffer; XID bool; xRectangle r; UpdateCurrentTime (); for (i = 0; i < nbuf; i++) { pWin = ppMBWindow[i]->pWindow; /* Time to get a different scratch GC? */ if (!pGC || pGC->depth != pWin->drawable.depth || pGC->pScreen != pWin->drawable.pScreen) { if (pGC) FreeScratchGC(pGC); pGC = GetScratchGC (pWin->drawable.depth, pWin->drawable.pScreen); } pPrevMBBuffer = MB_DISPLAYED_BUFFER(ppMBWindow[i]); pPrevPixmap = (PixmapPtr) pPrevMBBuffer->pDrawable; pNewPixmap = (PixmapPtr) ppMBBuffer[i]->pDrawable; if (pPrevPixmap == pNewPixmap) { /* "If a specified buffer is already displayed, any delays and * update action will still be performed for that buffer." * * We special-case this because applications do occasionally * request a redundant DisplayImageBuffers, and we can save * strokes by recognizing that the only update action that will * change the buffer contents in this case is Background. */ if (ppMBWindow[i]->updateAction == MultibufferUpdateActionBackground) { r.x = r.y = 0; r.width = pWin->drawable.width; r.height = pWin->drawable.height; MultibufferPaintBackgroundRectangles(pWin, (DrawablePtr)pWin, 1, &r); } } else /* different buffer is being displayed */ { /* perform update action */ switch (ppMBWindow[i]->updateAction) { case MultibufferUpdateActionUndefined: break; case MultibufferUpdateActionBackground: r.x = r.y = 0; r.width = pPrevPixmap->drawable.width; r.height = pPrevPixmap->drawable.height; MultibufferPaintBackgroundRectangles(pWin, (DrawablePtr)pPrevPixmap, 1, &r); break; case MultibufferUpdateActionUntouched: /* copy the window to the pixmap that represents the * currently displayed buffer */ if (pPrevMBBuffer->eventMask & ExposureMask) { bool = TRUE; DoChangeGC (pGC, GCGraphicsExposures, &bool, FALSE); } ValidateGC ((DrawablePtr)pPrevPixmap, pGC); pExposed = (*pGC->ops->CopyArea)((DrawablePtr) pWin, (DrawablePtr) pPrevPixmap, pGC, 0, 0, pWin->drawable.width, pWin->drawable.height, 0, 0); /* if we couldn't copy the whole window to the buffer, * send expose events (if any client wants them) */ if (pPrevMBBuffer->eventMask & ExposureMask) { /* some client wants expose events */ if (pExposed) { RegionPtr pWinSize; extern RegionPtr CreateUnclippedWinSize(); ScreenPtr pScreen = pWin->drawable.pScreen; pWinSize = CreateUnclippedWinSize (pWin); /* * pExposed is window-relative, but at this point * pWinSize is screen-relative. Make pWinSize be * window-relative so that region ops involving * pExposed and pWinSize behave sensibly. */ REGION_TRANSLATE(pScreen, pWinSize, -pWin->drawable.x, -pWin->drawable.y); REGION_INTERSECT(pScreen, pExposed, pExposed, pWinSize); REGION_DESTROY(pScreen, pWinSize); MultibufferExpose (pPrevMBBuffer, pExposed); REGION_DESTROY(pScreen, pExposed); } bool = FALSE; DoChangeGC (pGC, GCGraphicsExposures, &bool, FALSE); } /* end some client wants expose events */ break; /* end case MultibufferUpdateActionUntouched */ case MultibufferUpdateActionCopied: ValidateGC ((DrawablePtr)pPrevPixmap, pGC); (*pGC->ops->CopyArea) ((DrawablePtr)pNewPixmap, (DrawablePtr)pPrevPixmap, pGC, 0, 0, pWin->drawable.width, pWin->drawable.height, 0, 0); break; } /* end switch on update action */ /* display the new buffer */ ValidateGC ((DrawablePtr)pWin, pGC); (*pGC->ops->CopyArea) ((DrawablePtr)pNewPixmap, (DrawablePtr)pWin, pGC, 0, 0, pWin->drawable.width, pWin->drawable.height, 0, 0); } ppMBWindow[i]->lastUpdate = currentTime; } if (pGC) FreeScratchGC (pGC); return; } /* * resize the buffers when the window is resized */ static Bool pixPositionWindow (pWin, x, y) WindowPtr pWin; int x, y; { ScreenPtr pScreen; mbufPixmapPrivPtr pMBPriv; mbufWindowPtr pMBWindow; mbufBufferPtr pMBBuffer; int width, height; int i; int dx, dy, dw, dh; int sourcex, sourcey; int destx, desty; PixmapPtr pPixmap; GCPtr pGC; int savewidth, saveheight; Bool clear; RegionRec exposedRegion; Bool ret; pScreen = pWin->drawable.pScreen; pMBPriv = MB_SCREEN_PRIV_PIXMAP(pScreen); UNWRAP_SCREEN_FUNC(pScreen, pMBPriv, Bool, PositionWindow); ret = (* pScreen->PositionWindow) (pWin, x, y); REWRAP_SCREEN_FUNC(pScreen, pMBPriv, Bool, PositionWindow); if (!(pMBWindow = MB_WINDOW_PRIV(pWin))) return ret; /* if new size is same as old, we're done */ if (pMBWindow->width == pWin->drawable.width && pMBWindow->height == pWin->drawable.height) return ret; width = pWin->drawable.width; height = pWin->drawable.height; dx = pWin->drawable.x - pMBWindow->x; dy = pWin->drawable.x - pMBWindow->y; dw = width - pMBWindow->width; dh = height - pMBWindow->height; GravityTranslate (0, 0, -dx, -dy, dw, dh, pWin->bitGravity, &destx, &desty); /* if the window grew, remember to paint the window background, * and maybe send expose events, for the new areas of the buffers */ clear = pMBWindow->width < width || pMBWindow->height < height || pWin->bitGravity == ForgetGravity; sourcex = 0; sourcey = 0; savewidth = pMBWindow->width; saveheight = pMBWindow->height; /* clip rectangle to source and destination */ if (destx < 0) { savewidth += destx; sourcex -= destx; destx = 0; } if (destx + savewidth > width) savewidth = width - destx; if (desty < 0) { saveheight += desty; sourcey -= desty; desty = 0; } if (desty + saveheight > height) saveheight = height - desty; pMBWindow->width = width; pMBWindow->height = height; pMBWindow->x = pWin->drawable.x; pMBWindow->y = pWin->drawable.y; if (clear) { BoxRec box; box.x1 = box.y1 = 0; box.x2 = width; box.y2 = height; REGION_INIT(pScreen, &exposedRegion, &box, 1); if (pWin->bitGravity != ForgetGravity) { RegionRec preservedRegion; box.x1 = destx; box.y1 = desty; box.x2 = destx + savewidth; box.y2 = desty + saveheight; REGION_INIT(pScreen, &preservedRegion, &box, 1); REGION_SUBTRACT(pScreen, &exposedRegion, &exposedRegion, &preservedRegion); REGION_UNINIT(pScreen, &preservedRegion); } } /* end if (clear) */ pGC = GetScratchGC (pWin->drawable.depth, pScreen); /* create buffers with new window size */ for (i = 0; i < pMBWindow->numMultibuffer; i++) { pMBBuffer = &pMBWindow->buffers[i]; pPixmap = (*pScreen->CreatePixmap) (pScreen, width, height, pWin->drawable.depth); if (!pPixmap) { (* MB_SCREEN_PRIV(pScreen)->DestroyImageBuffers)(pWin); break; } if (clear) { MultibufferPaintBackgroundRegion(pWin, (DrawablePtr)pPixmap, &exposedRegion); MultibufferExpose(pMBBuffer, &exposedRegion); } if (pWin->bitGravity != ForgetGravity) { ValidateGC ((DrawablePtr)pPixmap, pGC); (*pGC->ops->CopyArea) (pMBBuffer->pDrawable, (DrawablePtr)pPixmap, pGC, sourcex, sourcey, savewidth, saveheight, destx, desty); } pPixmap->drawable.id = pMBBuffer->pDrawable->id; (*pScreen->DestroyPixmap) ((PixmapPtr) pMBBuffer->pDrawable); pMBBuffer->pDrawable = (DrawablePtr) pPixmap; if (i != pMBWindow->displayedMultibuffer) { ChangeResourceValue (pPixmap->drawable.id, MultibufferDrawableResType, (pointer) pPixmap); } } FreeScratchGC (pGC); if (clear) REGION_UNINIT(pScreen, &exposedRegion); return TRUE; } static void pixWrapScreenFuncs(pScreen) ScreenPtr pScreen; { mbufPixmapPrivPtr pMBPriv = MB_SCREEN_PRIV_PIXMAP(pScreen); WRAP_SCREEN_FUNC(pScreen, pMBPriv, PositionWindow, pixPositionWindow); } static void pixResetProc(pScreen) ScreenPtr pScreen; { mbufScreenPtr pMBScreen = MB_SCREEN_PRIV(pScreen); mbufPixmapPrivPtr pMBPriv = MB_SCREEN_PRIV_PIXMAP(pScreen); xfree(pMBScreen->pInfo); xfree(pMBPriv); } static void pixClearImageBufferArea(pMBBuffer, x,y, width,height, exposures) mbufBufferPtr pMBBuffer; short x, y; unsigned short width, height; Bool exposures; { WindowPtr pWin; ScreenPtr pScreen; BoxRec box; RegionRec region; int w_width, w_height; DrawablePtr pDrawable; pWin = pMBBuffer->pMBWindow->pWindow; pScreen = pWin->drawable.pScreen; w_width = pWin->drawable.width; w_height = pWin->drawable.height; box.x1 = x; box.y1 = y; box.x2 = width ? (box.x1 + width) : w_width; box.y2 = height ? (box.y1 + height) : w_height; if (box.x1 < 0) box.x1 = 0; if (box.y1 < 0) box.y1 = 0; if (box.x2 > w_width) box.x2 = w_width; if (box.y2 > w_height) box.y2 = w_height; REGION_INIT(pScreen, ®ion, &box, 1); if (pMBBuffer->number == pMBBuffer->pMBWindow->displayedMultibuffer) pDrawable = (DrawablePtr) pWin; else pDrawable = pMBBuffer->pDrawable; MultibufferPaintBackgroundRegion(pWin, pDrawable, ®ion); if (exposures) MultibufferExpose(pMBBuffer, ®ion); REGION_UNINIT(pScreen, ®ion); } static void pixDeleteBufferDrawable(pDrawable) DrawablePtr pDrawable; { (* pDrawable->pScreen->DestroyPixmap)((PixmapPtr) pDrawable); }