xenocara/xserver/composite/compalloc.c
2011-11-05 13:32:40 +00:00

732 lines
19 KiB
C

/*
* Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) 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 AUTHORS OR COPYRIGHT HOLDERS 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.
*
* Copyright © 2003 Keith Packard
*
* 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 Keith Packard not be used in
* advertising or publicity pertaining to distribution of the software without
* specific, written prior permission. Keith Packard makes no
* representations about the suitability of this software for any purpose. It
* is provided "as is" without express or implied warranty.
*
* KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL KEITH PACKARD 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 "compint.h"
static void
compScreenUpdate (ScreenPtr pScreen)
{
compCheckTree (pScreen);
compPaintChildrenToWindow (pScreen->root);
}
static void
compBlockHandler (int i,
pointer blockData,
pointer pTimeout,
pointer pReadmask)
{
ScreenPtr pScreen = screenInfo.screens[i];
CompScreenPtr cs = GetCompScreen (pScreen);
pScreen->BlockHandler = cs->BlockHandler;
compScreenUpdate (pScreen);
(*pScreen->BlockHandler) (i, blockData, pTimeout, pReadmask);
/* Next damage will restore the block handler */
cs->BlockHandler = NULL;
}
static void
compReportDamage (DamagePtr pDamage, RegionPtr pRegion, void *closure)
{
WindowPtr pWin = (WindowPtr) closure;
ScreenPtr pScreen = pWin->drawable.pScreen;
CompScreenPtr cs = GetCompScreen (pScreen);
CompWindowPtr cw = GetCompWindow (pWin);
if (!cs->BlockHandler) {
cs->BlockHandler = pScreen->BlockHandler;
pScreen->BlockHandler = compBlockHandler;
}
cw->damaged = TRUE;
/* Mark the ancestors */
pWin = pWin->parent;
while (pWin) {
if (pWin->damagedDescendants)
break;
pWin->damagedDescendants = TRUE;
pWin = pWin->parent;
}
}
static void
compDestroyDamage (DamagePtr pDamage, void *closure)
{
WindowPtr pWin = (WindowPtr) closure;
CompWindowPtr cw = GetCompWindow (pWin);
cw->damage = 0;
}
static Bool
compMarkWindows(WindowPtr pWin,
WindowPtr *ppLayerWin)
{
ScreenPtr pScreen = pWin->drawable.pScreen;
WindowPtr pLayerWin = pWin;
if (!pWin->viewable)
return FALSE;
(*pScreen->MarkOverlappedWindows)(pWin, pWin, &pLayerWin);
(*pScreen->MarkWindow)(pLayerWin->parent);
*ppLayerWin = pLayerWin;
return TRUE;
}
static void
compHandleMarkedWindows(WindowPtr pWin, WindowPtr pLayerWin)
{
ScreenPtr pScreen = pWin->drawable.pScreen;
(*pScreen->ValidateTree)(pLayerWin->parent, pLayerWin, VTOther);
(*pScreen->HandleExposures)(pLayerWin->parent);
if (pScreen->PostValidateTree)
(*pScreen->PostValidateTree)(pLayerWin->parent, pLayerWin, VTOther);
}
/*
* Redirect one window for one client
*/
int
compRedirectWindow (ClientPtr pClient, WindowPtr pWin, int update)
{
CompWindowPtr cw = GetCompWindow (pWin);
CompClientWindowPtr ccw;
CompScreenPtr cs = GetCompScreen(pWin->drawable.pScreen);
WindowPtr pLayerWin;
Bool anyMarked = FALSE;
if (pWin == cs->pOverlayWin) {
return Success;
}
if (!pWin->parent)
return BadMatch;
/*
* Only one Manual update is allowed
*/
if (cw && update == CompositeRedirectManual)
for (ccw = cw->clients; ccw; ccw = ccw->next)
if (ccw->update == CompositeRedirectManual)
return BadAccess;
/*
* Allocate per-client per-window structure
* The client *could* allocate multiple, but while supported,
* it is not expected to be common
*/
ccw = malloc(sizeof (CompClientWindowRec));
if (!ccw)
return BadAlloc;
ccw->id = FakeClientID (pClient->index);
ccw->update = update;
/*
* Now make sure there's a per-window structure to hang this from
*/
if (!cw)
{
cw = malloc(sizeof (CompWindowRec));
if (!cw)
{
free(ccw);
return BadAlloc;
}
cw->damage = DamageCreate (compReportDamage,
compDestroyDamage,
DamageReportNonEmpty,
FALSE,
pWin->drawable.pScreen,
pWin);
if (!cw->damage)
{
free(ccw);
free(cw);
return BadAlloc;
}
anyMarked = compMarkWindows (pWin, &pLayerWin);
RegionNull(&cw->borderClip);
cw->update = CompositeRedirectAutomatic;
cw->clients = 0;
cw->oldx = COMP_ORIGIN_INVALID;
cw->oldy = COMP_ORIGIN_INVALID;
cw->damageRegistered = FALSE;
cw->damaged = FALSE;
cw->pOldPixmap = NullPixmap;
dixSetPrivate(&pWin->devPrivates, CompWindowPrivateKey, cw);
}
ccw->next = cw->clients;
cw->clients = ccw;
if (!AddResource (ccw->id, CompositeClientWindowType, pWin))
return BadAlloc;
if (ccw->update == CompositeRedirectManual)
{
if (!anyMarked)
anyMarked = compMarkWindows (pWin, &pLayerWin);
if (cw->damageRegistered)
{
DamageUnregister (&pWin->drawable, cw->damage);
cw->damageRegistered = FALSE;
}
cw->update = CompositeRedirectManual;
}
else if (cw->update == CompositeRedirectAutomatic && !cw->damageRegistered) {
if (!anyMarked)
anyMarked = compMarkWindows (pWin, &pLayerWin);
}
if (!compCheckRedirect (pWin))
{
FreeResource (ccw->id, RT_NONE);
return BadAlloc;
}
if (anyMarked)
compHandleMarkedWindows (pWin, pLayerWin);
return Success;
}
void
compRestoreWindow (WindowPtr pWin, PixmapPtr pPixmap)
{
ScreenPtr pScreen = pWin->drawable.pScreen;
WindowPtr pParent = pWin->parent;
if (pParent->drawable.depth == pWin->drawable.depth) {
GCPtr pGC = GetScratchGC (pWin->drawable.depth, pScreen);
int bw = (int) pWin->borderWidth;
int x = bw;
int y = bw;
int w = pWin->drawable.width;
int h = pWin->drawable.height;
if (pGC) {
ChangeGCVal val;
val.val = IncludeInferiors;
ChangeGC (NullClient, pGC, GCSubwindowMode, &val);
ValidateGC(&pWin->drawable, pGC);
(*pGC->ops->CopyArea) (&pPixmap->drawable,
&pWin->drawable,
pGC,
x, y, w, h, 0, 0);
FreeScratchGC (pGC);
}
}
}
/*
* Free one of the per-client per-window resources, clearing
* redirect and the per-window pointer as appropriate
*/
void
compFreeClientWindow (WindowPtr pWin, XID id)
{
ScreenPtr pScreen = pWin->drawable.pScreen;
CompWindowPtr cw = GetCompWindow (pWin);
CompClientWindowPtr ccw, *prev;
Bool anyMarked = FALSE;
WindowPtr pLayerWin;
PixmapPtr pPixmap = NULL;
if (!cw)
return;
for (prev = &cw->clients; (ccw = *prev); prev = &ccw->next)
{
if (ccw->id == id)
{
*prev = ccw->next;
if (ccw->update == CompositeRedirectManual)
cw->update = CompositeRedirectAutomatic;
free(ccw);
break;
}
}
if (!cw->clients)
{
anyMarked = compMarkWindows (pWin, &pLayerWin);
if (pWin->redirectDraw != RedirectDrawNone) {
pPixmap = (*pScreen->GetWindowPixmap) (pWin);
compSetParentPixmap (pWin);
}
if (cw->damage)
DamageDestroy (cw->damage);
RegionUninit(&cw->borderClip);
dixSetPrivate(&pWin->devPrivates, CompWindowPrivateKey, NULL);
free(cw);
}
else if (cw->update == CompositeRedirectAutomatic &&
!cw->damageRegistered && pWin->redirectDraw != RedirectDrawNone)
{
anyMarked = compMarkWindows (pWin, &pLayerWin);
DamageRegister (&pWin->drawable, cw->damage);
cw->damageRegistered = TRUE;
pWin->redirectDraw = RedirectDrawAutomatic;
DamageDamageRegion(&pWin->drawable, &pWin->borderSize);
}
if (anyMarked)
compHandleMarkedWindows (pWin, pLayerWin);
if (pPixmap) {
compRestoreWindow (pWin, pPixmap);
(*pScreen->DestroyPixmap) (pPixmap);
}
}
/*
* This is easy, just free the appropriate resource.
*/
int
compUnredirectWindow (ClientPtr pClient, WindowPtr pWin, int update)
{
CompWindowPtr cw = GetCompWindow (pWin);
CompClientWindowPtr ccw;
if (!cw)
return BadValue;
for (ccw = cw->clients; ccw; ccw = ccw->next)
if (ccw->update == update && CLIENT_ID(ccw->id) == pClient->index)
{
FreeResource (ccw->id, RT_NONE);
return Success;
}
return BadValue;
}
/*
* Redirect all subwindows for one client
*/
int
compRedirectSubwindows (ClientPtr pClient, WindowPtr pWin, int update)
{
CompSubwindowsPtr csw = GetCompSubwindows (pWin);
CompClientWindowPtr ccw;
WindowPtr pChild;
/*
* Only one Manual update is allowed
*/
if (csw && update == CompositeRedirectManual)
for (ccw = csw->clients; ccw; ccw = ccw->next)
if (ccw->update == CompositeRedirectManual)
return BadAccess;
/*
* Allocate per-client per-window structure
* The client *could* allocate multiple, but while supported,
* it is not expected to be common
*/
ccw = malloc(sizeof (CompClientWindowRec));
if (!ccw)
return BadAlloc;
ccw->id = FakeClientID (pClient->index);
ccw->update = update;
/*
* Now make sure there's a per-window structure to hang this from
*/
if (!csw)
{
csw = malloc(sizeof (CompSubwindowsRec));
if (!csw)
{
free(ccw);
return BadAlloc;
}
csw->update = CompositeRedirectAutomatic;
csw->clients = 0;
dixSetPrivate(&pWin->devPrivates, CompSubwindowsPrivateKey, csw);
}
/*
* Redirect all existing windows
*/
for (pChild = pWin->lastChild; pChild; pChild = pChild->prevSib)
{
int ret = compRedirectWindow (pClient, pChild, update);
if (ret != Success)
{
for (pChild = pChild->nextSib; pChild; pChild = pChild->nextSib)
(void) compUnredirectWindow (pClient, pChild, update);
if (!csw->clients)
{
free(csw);
dixSetPrivate(&pWin->devPrivates, CompSubwindowsPrivateKey, 0);
}
free(ccw);
return ret;
}
}
/*
* Hook into subwindows list
*/
ccw->next = csw->clients;
csw->clients = ccw;
if (!AddResource (ccw->id, CompositeClientSubwindowsType, pWin))
return BadAlloc;
if (ccw->update == CompositeRedirectManual)
{
csw->update = CompositeRedirectManual;
/*
* tell damage extension that damage events for this client are
* critical output
*/
DamageExtSetCritical (pClient, TRUE);
pWin->inhibitBGPaint = TRUE;
}
return Success;
}
/*
* Free one of the per-client per-subwindows resources,
* which frees one redirect per subwindow
*/
void
compFreeClientSubwindows (WindowPtr pWin, XID id)
{
CompSubwindowsPtr csw = GetCompSubwindows (pWin);
CompClientWindowPtr ccw, *prev;
WindowPtr pChild;
if (!csw)
return;
for (prev = &csw->clients; (ccw = *prev); prev = &ccw->next)
{
if (ccw->id == id)
{
ClientPtr pClient = clients[CLIENT_ID(id)];
*prev = ccw->next;
if (ccw->update == CompositeRedirectManual)
{
/*
* tell damage extension that damage events for this client are
* critical output
*/
DamageExtSetCritical (pClient, FALSE);
csw->update = CompositeRedirectAutomatic;
pWin->inhibitBGPaint = FALSE;
if (pWin->mapped)
(*pWin->drawable.pScreen->ClearToBackground)(pWin, 0, 0, 0, 0, TRUE);
}
/*
* Unredirect all existing subwindows
*/
for (pChild = pWin->lastChild; pChild; pChild = pChild->prevSib)
(void) compUnredirectWindow (pClient, pChild, ccw->update);
free(ccw);
break;
}
}
/*
* Check if all of the per-client records are gone
*/
if (!csw->clients)
{
dixSetPrivate(&pWin->devPrivates, CompSubwindowsPrivateKey, NULL);
free(csw);
}
}
/*
* This is easy, just free the appropriate resource.
*/
int
compUnredirectSubwindows (ClientPtr pClient, WindowPtr pWin, int update)
{
CompSubwindowsPtr csw = GetCompSubwindows (pWin);
CompClientWindowPtr ccw;
if (!csw)
return BadValue;
for (ccw = csw->clients; ccw; ccw = ccw->next)
if (ccw->update == update && CLIENT_ID(ccw->id) == pClient->index)
{
FreeResource (ccw->id, RT_NONE);
return Success;
}
return BadValue;
}
/*
* Add redirection information for one subwindow (during reparent)
*/
int
compRedirectOneSubwindow (WindowPtr pParent, WindowPtr pWin)
{
CompSubwindowsPtr csw = GetCompSubwindows (pParent);
CompClientWindowPtr ccw;
if (!csw)
return Success;
for (ccw = csw->clients; ccw; ccw = ccw->next)
{
int ret = compRedirectWindow (clients[CLIENT_ID(ccw->id)],
pWin, ccw->update);
if (ret != Success)
return ret;
}
return Success;
}
/*
* Remove redirection information for one subwindow (during reparent)
*/
int
compUnredirectOneSubwindow (WindowPtr pParent, WindowPtr pWin)
{
CompSubwindowsPtr csw = GetCompSubwindows (pParent);
CompClientWindowPtr ccw;
if (!csw)
return Success;
for (ccw = csw->clients; ccw; ccw = ccw->next)
{
int ret = compUnredirectWindow (clients[CLIENT_ID(ccw->id)],
pWin, ccw->update);
if (ret != Success)
return ret;
}
return Success;
}
static PixmapPtr
compNewPixmap (WindowPtr pWin, int x, int y, int w, int h)
{
ScreenPtr pScreen = pWin->drawable.pScreen;
WindowPtr pParent = pWin->parent;
PixmapPtr pPixmap;
pPixmap = (*pScreen->CreatePixmap) (pScreen, w, h, pWin->drawable.depth,
CREATE_PIXMAP_USAGE_BACKING_PIXMAP);
if (!pPixmap)
return 0;
pPixmap->screen_x = x;
pPixmap->screen_y = y;
if (pParent->drawable.depth == pWin->drawable.depth)
{
GCPtr pGC = GetScratchGC (pWin->drawable.depth, pScreen);
if (pGC)
{
ChangeGCVal val;
val.val = IncludeInferiors;
ChangeGC (NullClient, pGC, GCSubwindowMode, &val);
ValidateGC(&pPixmap->drawable, pGC);
(*pGC->ops->CopyArea) (&pParent->drawable,
&pPixmap->drawable,
pGC,
x - pParent->drawable.x,
y - pParent->drawable.y,
w, h, 0, 0);
FreeScratchGC (pGC);
}
}
else
{
PictFormatPtr pSrcFormat = compWindowFormat (pParent);
PictFormatPtr pDstFormat = compWindowFormat (pWin);
XID inferiors = IncludeInferiors;
int error;
PicturePtr pSrcPicture = CreatePicture (None,
&pParent->drawable,
pSrcFormat,
CPSubwindowMode,
&inferiors,
serverClient, &error);
PicturePtr pDstPicture = CreatePicture (None,
&pPixmap->drawable,
pDstFormat,
0, 0,
serverClient, &error);
if (pSrcPicture && pDstPicture)
{
CompositePicture (PictOpSrc,
pSrcPicture,
NULL,
pDstPicture,
x - pParent->drawable.x,
y - pParent->drawable.y,
0, 0, 0, 0, w, h);
}
if (pSrcPicture)
FreePicture (pSrcPicture, 0);
if (pDstPicture)
FreePicture (pDstPicture, 0);
}
return pPixmap;
}
Bool
compAllocPixmap (WindowPtr pWin)
{
int bw = (int) pWin->borderWidth;
int x = pWin->drawable.x - bw;
int y = pWin->drawable.y - bw;
int w = pWin->drawable.width + (bw << 1);
int h = pWin->drawable.height + (bw << 1);
PixmapPtr pPixmap = compNewPixmap (pWin, x, y, w, h);
CompWindowPtr cw = GetCompWindow (pWin);
if (!pPixmap)
return FALSE;
if (cw->update == CompositeRedirectAutomatic)
pWin->redirectDraw = RedirectDrawAutomatic;
else
pWin->redirectDraw = RedirectDrawManual;
compSetPixmap (pWin, pPixmap);
cw->oldx = COMP_ORIGIN_INVALID;
cw->oldy = COMP_ORIGIN_INVALID;
cw->damageRegistered = FALSE;
if (cw->update == CompositeRedirectAutomatic)
{
DamageRegister (&pWin->drawable, cw->damage);
cw->damageRegistered = TRUE;
}
/* Make sure our borderClip is up to date */
RegionUninit(&cw->borderClip);
RegionCopy(&cw->borderClip, &pWin->borderClip);
cw->borderClipX = pWin->drawable.x;
cw->borderClipY = pWin->drawable.y;
return TRUE;
}
void
compSetParentPixmap (WindowPtr pWin)
{
ScreenPtr pScreen = pWin->drawable.pScreen;
PixmapPtr pParentPixmap;
CompWindowPtr cw = GetCompWindow (pWin);
if (cw->damageRegistered)
{
DamageUnregister (&pWin->drawable, cw->damage);
cw->damageRegistered = FALSE;
DamageEmpty (cw->damage);
}
/*
* Move the parent-constrained border clip region back into
* the window so that ValidateTree will handle the unmap
* case correctly. Unmap adds the window borderClip to the
* parent exposed area; regions beyond the parent cause crashes
*/
RegionCopy(&pWin->borderClip, &cw->borderClip);
pParentPixmap = (*pScreen->GetWindowPixmap) (pWin->parent);
pWin->redirectDraw = RedirectDrawNone;
compSetPixmap (pWin, pParentPixmap);
}
/*
* Make sure the pixmap is the right size and offset. Allocate a new
* pixmap to change size, adjust origin to change offset, leaving the
* old pixmap in cw->pOldPixmap so bits can be recovered
*/
Bool
compReallocPixmap (WindowPtr pWin, int draw_x, int draw_y,
unsigned int w, unsigned int h, int bw)
{
ScreenPtr pScreen = pWin->drawable.pScreen;
PixmapPtr pOld = (*pScreen->GetWindowPixmap) (pWin);
PixmapPtr pNew;
CompWindowPtr cw = GetCompWindow (pWin);
int pix_x, pix_y;
int pix_w, pix_h;
assert (cw && pWin->redirectDraw != RedirectDrawNone);
cw->oldx = pOld->screen_x;
cw->oldy = pOld->screen_y;
pix_x = draw_x - bw;
pix_y = draw_y - bw;
pix_w = w + (bw << 1);
pix_h = h + (bw << 1);
if (pix_w != pOld->drawable.width || pix_h != pOld->drawable.height)
{
pNew = compNewPixmap (pWin, pix_x, pix_y, pix_w, pix_h);
if (!pNew)
return FALSE;
cw->pOldPixmap = pOld;
compSetPixmap (pWin, pNew);
}
else
{
pNew = pOld;
cw->pOldPixmap = 0;
}
pNew->screen_x = pix_x;
pNew->screen_y = pix_y;
return TRUE;
}