xenocara/xserver/Xext/mbufpx.c

649 lines
17 KiB
C

/************************************************************
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.
********************************************************/
#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 = 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 = 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 = 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, &region, &box, 1);
if (pMBBuffer->number == pMBBuffer->pMBWindow->displayedMultibuffer)
pDrawable = (DrawablePtr) pWin;
else
pDrawable = pMBBuffer->pDrawable;
MultibufferPaintBackgroundRegion(pWin, pDrawable, &region);
if (exposures)
MultibufferExpose(pMBBuffer, &region);
REGION_UNINIT(pScreen, &region);
}
static void
pixDeleteBufferDrawable(pDrawable)
DrawablePtr pDrawable;
{
(* pDrawable->pScreen->DestroyPixmap)((PixmapPtr) pDrawable);
}