428261197a
Tested by ajacoutot@, krw@, shadchin@ and jasper@ on various configurations including multihead with both zaphod and xrandr.
529 lines
13 KiB
C
529 lines
13 KiB
C
/*
|
|
* Copyright © 2004 Eric Anholt
|
|
*
|
|
* 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 Eric Anholt not be used in
|
|
* advertising or publicity pertaining to distribution of the software without
|
|
* specific, written prior permission. Eric Anholt makes no
|
|
* representations about the suitability of this software for any purpose. It
|
|
* is provided "as is" without express or implied warranty.
|
|
*
|
|
* ERIC ANHOLT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
* EVENT SHALL ERIC ANHOLT 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.
|
|
*/
|
|
|
|
#ifdef HAVE_DIX_CONFIG_H
|
|
#include <dix-config.h>
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
#include "gcstruct.h"
|
|
#include "windowstr.h"
|
|
#include "cw.h"
|
|
|
|
#define CW_DEBUG 1
|
|
|
|
#if CW_DEBUG
|
|
#define CW_ASSERT(x) do { \
|
|
if (!(x)) { \
|
|
ErrorF("composite wrapper: assertion failed at %s:%d\n", __FUNC__, \
|
|
__LINE__); \
|
|
} \
|
|
} while (0)
|
|
#else
|
|
#define CW_ASSERT(x) do {} while (0)
|
|
#endif
|
|
|
|
DevPrivateKeyRec cwGCKeyRec;
|
|
DevPrivateKeyRec cwScreenKeyRec;
|
|
DevPrivateKeyRec cwWindowKeyRec;
|
|
DevPrivateKeyRec cwPictureKeyRec;
|
|
|
|
extern GCOps cwGCOps;
|
|
|
|
static Bool
|
|
cwCloseScreen (int i, ScreenPtr pScreen);
|
|
|
|
static void
|
|
cwValidateGC(GCPtr pGC, unsigned long stateChanges, DrawablePtr pDrawable);
|
|
static void
|
|
cwChangeGC(GCPtr pGC, unsigned long mask);
|
|
static void
|
|
cwCopyGC(GCPtr pGCSrc, unsigned long mask, GCPtr pGCDst);
|
|
static void
|
|
cwDestroyGC(GCPtr pGC);
|
|
static void
|
|
cwChangeClip(GCPtr pGC, int type, pointer pvalue, int nrects);
|
|
static void
|
|
cwCopyClip(GCPtr pgcDst, GCPtr pgcSrc);
|
|
static void
|
|
cwDestroyClip(GCPtr pGC);
|
|
|
|
GCFuncs cwGCFuncs = {
|
|
cwValidateGC,
|
|
cwChangeGC,
|
|
cwCopyGC,
|
|
cwDestroyGC,
|
|
cwChangeClip,
|
|
cwDestroyClip,
|
|
cwCopyClip,
|
|
};
|
|
|
|
/* Find the real drawable to draw to, and provide offsets that will translate
|
|
* window coordinates to backing pixmap coordinates.
|
|
*/
|
|
DrawablePtr
|
|
cwGetBackingDrawable(DrawablePtr pDrawable, int *x_off, int *y_off)
|
|
{
|
|
PixmapPtr pPixmap;
|
|
|
|
if (pDrawable->type == DRAWABLE_WINDOW &&
|
|
(pPixmap = getCwPixmap ((WindowPtr) pDrawable)))
|
|
{
|
|
*x_off = pDrawable->x - pPixmap->screen_x;
|
|
*y_off = pDrawable->y - pPixmap->screen_y;
|
|
return &pPixmap->drawable;
|
|
} else {
|
|
*x_off = *y_off = 0;
|
|
return pDrawable;
|
|
}
|
|
}
|
|
|
|
#define FUNC_PROLOGUE(pGC, pPriv) do { \
|
|
(pGC)->funcs = (pPriv)->wrapFuncs; \
|
|
(pGC)->ops = (pPriv)->wrapOps; \
|
|
} while (0)
|
|
|
|
#define FUNC_EPILOGUE(pGC, pPriv) do { \
|
|
(pPriv)->wrapFuncs = (pGC)->funcs; \
|
|
(pPriv)->wrapOps = (pGC)->ops; \
|
|
(pGC)->funcs = &cwGCFuncs; \
|
|
(pGC)->ops = &cwGCOps; \
|
|
} while (0)
|
|
|
|
|
|
static Bool
|
|
cwCreateBackingGC(GCPtr pGC, DrawablePtr pDrawable)
|
|
{
|
|
cwGCRec *pPriv = getCwGC(pGC);
|
|
int status, x_off, y_off;
|
|
XID noexpose = xFalse;
|
|
DrawablePtr pBackingDrawable;
|
|
|
|
pBackingDrawable = cwGetBackingDrawable(pDrawable, &x_off, &y_off);
|
|
pPriv->pBackingGC = CreateGC(pBackingDrawable, GCGraphicsExposures,
|
|
&noexpose, &status, (XID)0, serverClient);
|
|
if (status != Success)
|
|
return FALSE;
|
|
|
|
pPriv->serialNumber = 0;
|
|
pPriv->stateChanges = GCAllBits;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
cwDestroyBackingGC(GCPtr pGC)
|
|
{
|
|
cwGCPtr pPriv;
|
|
|
|
pPriv = (cwGCPtr) getCwGC (pGC);
|
|
|
|
if (pPriv->pBackingGC) {
|
|
FreeGC(pPriv->pBackingGC, (XID)0);
|
|
pPriv->pBackingGC = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
cwValidateGC(GCPtr pGC, unsigned long stateChanges, DrawablePtr pDrawable)
|
|
{
|
|
GCPtr pBackingGC;
|
|
cwGCPtr pPriv;
|
|
DrawablePtr pBackingDrawable;
|
|
int x_off, y_off;
|
|
|
|
pPriv = (cwGCPtr) getCwGC (pGC);
|
|
|
|
FUNC_PROLOGUE(pGC, pPriv);
|
|
|
|
/*
|
|
* Must call ValidateGC to ensure pGC->pCompositeClip is valid
|
|
*/
|
|
(*pGC->funcs->ValidateGC)(pGC, stateChanges, pDrawable);
|
|
|
|
if (!cwDrawableIsRedirWindow(pDrawable)) {
|
|
cwDestroyBackingGC(pGC);
|
|
FUNC_EPILOGUE(pGC, pPriv);
|
|
return;
|
|
} else {
|
|
if (!pPriv->pBackingGC && !cwCreateBackingGC(pGC, pDrawable)) {
|
|
FUNC_EPILOGUE(pGC, pPriv);
|
|
return;
|
|
}
|
|
}
|
|
|
|
pBackingGC = pPriv->pBackingGC;
|
|
pBackingDrawable = cwGetBackingDrawable(pDrawable, &x_off, &y_off);
|
|
|
|
pPriv->stateChanges |= stateChanges;
|
|
|
|
/*
|
|
* Copy the composite clip into the backing GC if either
|
|
* the drawable clip list has changed or the client has changed
|
|
* the client clip data
|
|
*/
|
|
if (pDrawable->serialNumber != pPriv->serialNumber ||
|
|
(pPriv->stateChanges & (GCClipXOrigin|GCClipYOrigin|GCClipMask)))
|
|
{
|
|
ChangeGCVal vals[2];
|
|
RegionPtr pCompositeClip;
|
|
|
|
pCompositeClip = RegionCreate(NULL, 0);
|
|
RegionCopy(pCompositeClip, pGC->pCompositeClip);
|
|
|
|
/* Either the drawable has changed, or the clip list in the drawable has
|
|
* changed. Copy the new clip list over and set the new translated
|
|
* offset for it.
|
|
*/
|
|
|
|
(*pBackingGC->funcs->ChangeClip) (pBackingGC, CT_REGION,
|
|
(pointer) pCompositeClip, 0);
|
|
|
|
vals[0].val = x_off - pDrawable->x;
|
|
vals[1].val = y_off - pDrawable->y;
|
|
ChangeGC(NullClient, pBackingGC,
|
|
(GCClipXOrigin | GCClipYOrigin), vals);
|
|
|
|
pPriv->serialNumber = pDrawable->serialNumber;
|
|
/*
|
|
* Mask off any client clip changes to make sure
|
|
* the clip list set above remains in effect
|
|
*/
|
|
pPriv->stateChanges &= ~(GCClipXOrigin|GCClipYOrigin|GCClipMask);
|
|
}
|
|
|
|
if (pPriv->stateChanges) {
|
|
CopyGC(pGC, pBackingGC, pPriv->stateChanges);
|
|
pPriv->stateChanges = 0;
|
|
}
|
|
|
|
if ((pGC->patOrg.x + x_off) != pBackingGC->patOrg.x ||
|
|
(pGC->patOrg.y + y_off) != pBackingGC->patOrg.y)
|
|
{
|
|
ChangeGCVal vals[2];
|
|
vals[0].val = pGC->patOrg.x + x_off;
|
|
vals[1].val = pGC->patOrg.y + y_off;
|
|
ChangeGC(NullClient, pBackingGC,
|
|
(GCTileStipXOrigin | GCTileStipYOrigin), vals);
|
|
}
|
|
|
|
ValidateGC(pBackingDrawable, pBackingGC);
|
|
|
|
FUNC_EPILOGUE(pGC, pPriv);
|
|
}
|
|
|
|
static void
|
|
cwChangeGC(GCPtr pGC, unsigned long mask)
|
|
{
|
|
cwGCPtr pPriv = (cwGCPtr)dixLookupPrivate(&pGC->devPrivates, cwGCKey);
|
|
|
|
FUNC_PROLOGUE(pGC, pPriv);
|
|
|
|
(*pGC->funcs->ChangeGC) (pGC, mask);
|
|
|
|
FUNC_EPILOGUE(pGC, pPriv);
|
|
}
|
|
|
|
static void
|
|
cwCopyGC(GCPtr pGCSrc, unsigned long mask, GCPtr pGCDst)
|
|
{
|
|
cwGCPtr pPriv = (cwGCPtr)dixLookupPrivate(&pGCDst->devPrivates, cwGCKey);
|
|
|
|
FUNC_PROLOGUE(pGCDst, pPriv);
|
|
|
|
(*pGCDst->funcs->CopyGC) (pGCSrc, mask, pGCDst);
|
|
|
|
FUNC_EPILOGUE(pGCDst, pPriv);
|
|
}
|
|
|
|
static void
|
|
cwDestroyGC(GCPtr pGC)
|
|
{
|
|
cwGCPtr pPriv = (cwGCPtr)dixLookupPrivate(&pGC->devPrivates, cwGCKey);
|
|
|
|
FUNC_PROLOGUE(pGC, pPriv);
|
|
|
|
cwDestroyBackingGC(pGC);
|
|
|
|
(*pGC->funcs->DestroyGC) (pGC);
|
|
|
|
/* leave it unwrapped */
|
|
}
|
|
|
|
static void
|
|
cwChangeClip(GCPtr pGC, int type, pointer pvalue, int nrects)
|
|
{
|
|
cwGCPtr pPriv = (cwGCPtr)dixLookupPrivate(&pGC->devPrivates, cwGCKey);
|
|
|
|
FUNC_PROLOGUE(pGC, pPriv);
|
|
|
|
(*pGC->funcs->ChangeClip)(pGC, type, pvalue, nrects);
|
|
|
|
FUNC_EPILOGUE(pGC, pPriv);
|
|
}
|
|
|
|
static void
|
|
cwCopyClip(GCPtr pgcDst, GCPtr pgcSrc)
|
|
{
|
|
cwGCPtr pPriv = (cwGCPtr)dixLookupPrivate(&pgcDst->devPrivates, cwGCKey);
|
|
|
|
FUNC_PROLOGUE(pgcDst, pPriv);
|
|
|
|
(*pgcDst->funcs->CopyClip)(pgcDst, pgcSrc);
|
|
|
|
FUNC_EPILOGUE(pgcDst, pPriv);
|
|
}
|
|
|
|
static void
|
|
cwDestroyClip(GCPtr pGC)
|
|
{
|
|
cwGCPtr pPriv = (cwGCPtr)dixLookupPrivate(&pGC->devPrivates, cwGCKey);
|
|
|
|
FUNC_PROLOGUE(pGC, pPriv);
|
|
|
|
(*pGC->funcs->DestroyClip)(pGC);
|
|
|
|
FUNC_EPILOGUE(pGC, pPriv);
|
|
}
|
|
|
|
/*
|
|
* Screen wrappers.
|
|
*/
|
|
|
|
#define SCREEN_PROLOGUE(pScreen, field) \
|
|
((pScreen)->field = getCwScreen(pScreen)->field)
|
|
|
|
#define SCREEN_EPILOGUE(pScreen, field, wrapper) do { \
|
|
getCwScreen(pScreen)->field = (pScreen)->field; \
|
|
(pScreen)->field = (wrapper); \
|
|
} while (0)
|
|
|
|
static Bool
|
|
cwCreateGC(GCPtr pGC)
|
|
{
|
|
cwGCPtr pPriv = getCwGC(pGC);
|
|
ScreenPtr pScreen = pGC->pScreen;
|
|
Bool ret;
|
|
|
|
memset(pPriv, 0, sizeof(cwGCRec));
|
|
SCREEN_PROLOGUE(pScreen, CreateGC);
|
|
|
|
if ( (ret = (*pScreen->CreateGC)(pGC)) )
|
|
FUNC_EPILOGUE(pGC, pPriv);
|
|
|
|
SCREEN_EPILOGUE(pScreen, CreateGC, cwCreateGC);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
cwGetImage(DrawablePtr pSrc, int x, int y, int w, int h, unsigned int format,
|
|
unsigned long planemask, char *pdstLine)
|
|
{
|
|
ScreenPtr pScreen = pSrc->pScreen;
|
|
DrawablePtr pBackingDrawable;
|
|
int src_off_x, src_off_y;
|
|
|
|
SCREEN_PROLOGUE(pScreen, GetImage);
|
|
|
|
pBackingDrawable = cwGetBackingDrawable(pSrc, &src_off_x, &src_off_y);
|
|
|
|
CW_OFFSET_XY_SRC(x, y);
|
|
|
|
(*pScreen->GetImage)(pBackingDrawable, x, y, w, h, format, planemask,
|
|
pdstLine);
|
|
|
|
SCREEN_EPILOGUE(pScreen, GetImage, cwGetImage);
|
|
}
|
|
|
|
static void
|
|
cwGetSpans(DrawablePtr pSrc, int wMax, DDXPointPtr ppt, int *pwidth,
|
|
int nspans, char *pdstStart)
|
|
{
|
|
ScreenPtr pScreen = pSrc->pScreen;
|
|
DrawablePtr pBackingDrawable;
|
|
int i;
|
|
int src_off_x, src_off_y;
|
|
|
|
SCREEN_PROLOGUE(pScreen, GetSpans);
|
|
|
|
pBackingDrawable = cwGetBackingDrawable(pSrc, &src_off_x, &src_off_y);
|
|
|
|
for (i = 0; i < nspans; i++)
|
|
CW_OFFSET_XY_SRC(ppt[i].x, ppt[i].y);
|
|
|
|
(*pScreen->GetSpans)(pBackingDrawable, wMax, ppt, pwidth, nspans,
|
|
pdstStart);
|
|
|
|
SCREEN_EPILOGUE(pScreen, GetSpans, cwGetSpans);
|
|
}
|
|
|
|
|
|
static void
|
|
cwCopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc)
|
|
{
|
|
ScreenPtr pScreen = pWin->drawable.pScreen;
|
|
|
|
SCREEN_PROLOGUE(pScreen, CopyWindow);
|
|
|
|
if (!cwDrawableIsRedirWindow((DrawablePtr)pWin)) {
|
|
(*pScreen->CopyWindow)(pWin, ptOldOrg, prgnSrc);
|
|
} else {
|
|
GCPtr pGC;
|
|
BoxPtr pExtents;
|
|
int x_off, y_off;
|
|
int dx, dy;
|
|
PixmapPtr pBackingPixmap;
|
|
RegionPtr pClip;
|
|
int src_x, src_y, dst_x, dst_y, w, h;
|
|
|
|
dx = ptOldOrg.x - pWin->drawable.x;
|
|
dy = ptOldOrg.y - pWin->drawable.y;
|
|
|
|
pExtents = RegionExtents(prgnSrc);
|
|
|
|
pBackingPixmap = (PixmapPtr) cwGetBackingDrawable((DrawablePtr)pWin,
|
|
&x_off, &y_off);
|
|
|
|
src_x = pExtents->x1 - pBackingPixmap->screen_x;
|
|
src_y = pExtents->y1 - pBackingPixmap->screen_y;
|
|
w = pExtents->x2 - pExtents->x1;
|
|
h = pExtents->y2 - pExtents->y1;
|
|
dst_x = src_x - dx;
|
|
dst_y = src_y - dy;
|
|
|
|
/* Translate region (as required by API) */
|
|
RegionTranslate(prgnSrc, -dx, -dy);
|
|
|
|
pGC = GetScratchGC(pBackingPixmap->drawable.depth, pScreen);
|
|
/*
|
|
* Copy region to GC as clip, aligning as dest clip
|
|
*/
|
|
pClip = RegionCreate(NULL, 0);
|
|
RegionIntersect(pClip, &pWin->borderClip, prgnSrc);
|
|
RegionTranslate(pClip,
|
|
-pBackingPixmap->screen_x,
|
|
-pBackingPixmap->screen_y);
|
|
|
|
(*pGC->funcs->ChangeClip) (pGC, CT_REGION, pClip, 0);
|
|
|
|
ValidateGC(&pBackingPixmap->drawable, pGC);
|
|
|
|
(*pGC->ops->CopyArea) (&pBackingPixmap->drawable,
|
|
&pBackingPixmap->drawable, pGC,
|
|
src_x, src_y, w, h, dst_x, dst_y);
|
|
|
|
(*pGC->funcs->DestroyClip) (pGC);
|
|
|
|
FreeScratchGC(pGC);
|
|
}
|
|
|
|
SCREEN_EPILOGUE(pScreen, CopyWindow, cwCopyWindow);
|
|
}
|
|
|
|
static PixmapPtr
|
|
cwGetWindowPixmap (WindowPtr pWin)
|
|
{
|
|
PixmapPtr pPixmap = getCwPixmap (pWin);
|
|
|
|
if (!pPixmap)
|
|
{
|
|
ScreenPtr pScreen = pWin->drawable.pScreen;
|
|
SCREEN_PROLOGUE(pScreen, GetWindowPixmap);
|
|
if (pScreen->GetWindowPixmap)
|
|
pPixmap = (*pScreen->GetWindowPixmap) (pWin);
|
|
SCREEN_EPILOGUE(pScreen, GetWindowPixmap, cwGetWindowPixmap);
|
|
}
|
|
return pPixmap;
|
|
}
|
|
|
|
static void
|
|
cwSetWindowPixmap (WindowPtr pWindow, PixmapPtr pPixmap)
|
|
{
|
|
ScreenPtr pScreen = pWindow->drawable.pScreen;
|
|
|
|
if (pPixmap == (*pScreen->GetScreenPixmap) (pScreen))
|
|
pPixmap = NULL;
|
|
setCwPixmap (pWindow, pPixmap);
|
|
}
|
|
|
|
/* Screen initialization/teardown */
|
|
void
|
|
miInitializeCompositeWrapper(ScreenPtr pScreen)
|
|
{
|
|
cwScreenPtr pScreenPriv;
|
|
Bool has_render = GetPictureScreenIfSet(pScreen) != NULL;
|
|
|
|
if (!dixRegisterPrivateKey(&cwScreenKeyRec, PRIVATE_SCREEN, 0))
|
|
return;
|
|
|
|
if (!dixRegisterPrivateKey(&cwGCKeyRec, PRIVATE_GC, sizeof(cwGCRec)))
|
|
return;
|
|
|
|
if (!dixRegisterPrivateKey(&cwWindowKeyRec, PRIVATE_WINDOW, 0))
|
|
return;
|
|
|
|
if (!dixRegisterPrivateKey(&cwPictureKeyRec, PRIVATE_PICTURE, 0))
|
|
return;
|
|
|
|
pScreenPriv = malloc(sizeof(cwScreenRec));
|
|
if (!pScreenPriv)
|
|
return;
|
|
|
|
dixSetPrivate(&pScreen->devPrivates, cwScreenKey, pScreenPriv);
|
|
|
|
SCREEN_EPILOGUE(pScreen, CloseScreen, cwCloseScreen);
|
|
SCREEN_EPILOGUE(pScreen, GetImage, cwGetImage);
|
|
SCREEN_EPILOGUE(pScreen, GetSpans, cwGetSpans);
|
|
SCREEN_EPILOGUE(pScreen, CreateGC, cwCreateGC);
|
|
SCREEN_EPILOGUE(pScreen, CopyWindow, cwCopyWindow);
|
|
|
|
SCREEN_EPILOGUE(pScreen, SetWindowPixmap, cwSetWindowPixmap);
|
|
SCREEN_EPILOGUE(pScreen, GetWindowPixmap, cwGetWindowPixmap);
|
|
|
|
if (has_render)
|
|
cwInitializeRender(pScreen);
|
|
}
|
|
|
|
static Bool
|
|
cwCloseScreen (int i, ScreenPtr pScreen)
|
|
{
|
|
cwScreenPtr pScreenPriv;
|
|
PictureScreenPtr ps = GetPictureScreenIfSet(pScreen);
|
|
|
|
pScreenPriv = (cwScreenPtr)dixLookupPrivate(&pScreen->devPrivates,
|
|
cwScreenKey);
|
|
pScreen->CloseScreen = pScreenPriv->CloseScreen;
|
|
pScreen->GetImage = pScreenPriv->GetImage;
|
|
pScreen->GetSpans = pScreenPriv->GetSpans;
|
|
pScreen->CreateGC = pScreenPriv->CreateGC;
|
|
pScreen->CopyWindow = pScreenPriv->CopyWindow;
|
|
|
|
if (ps)
|
|
cwFiniRender(pScreen);
|
|
|
|
free((pointer)pScreenPriv);
|
|
|
|
return (*pScreen->CloseScreen)(i, pScreen);
|
|
}
|