diff options
Diffstat (limited to 'xorg-server/Xext/mbufpx.c')
-rw-r--r-- | xorg-server/Xext/mbufpx.c | 650 |
1 files changed, 650 insertions, 0 deletions
diff --git a/xorg-server/Xext/mbufpx.c b/xorg-server/Xext/mbufpx.c new file mode 100644 index 000000000..21d525906 --- /dev/null +++ b/xorg-server/Xext/mbufpx.c @@ -0,0 +1,650 @@ +/************************************************************ + +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 <dix-config.h> +#endif + +#include <stdio.h> +#include <X11/X.h> +#include <X11/Xproto.h> +#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 <sys/time.h> + +#define _MULTIBUF_SERVER_ /* don't want Xlib structures */ +#define _MULTIBUF_PIXMAP_ +#include <X11/extensions/multibufst.h> + + +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, 0); + 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 *)xalloc(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); + xfree(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, + CREATE_PIXMAP_USAGE_SCRATCH); + 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); +} |