/* * Copyright © 2006 Sun Microsystems, Inc. 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 #endif #include "compint.h" #ifdef COMPOSITE_DEBUG static int compCheckWindow (WindowPtr pWin, pointer data) { ScreenPtr pScreen = pWin->drawable.pScreen; PixmapPtr pWinPixmap = (*pScreen->GetWindowPixmap) (pWin); PixmapPtr pParentPixmap = pWin->parent ? (*pScreen->GetWindowPixmap) (pWin->parent) : 0; PixmapPtr pScreenPixmap = (*pScreen->GetScreenPixmap) (pScreen); if (!pWin->parent) { assert (pWin->redirectDraw == RedirectDrawNone); assert (pWinPixmap == pScreenPixmap); } else if (pWin->redirectDraw != RedirectDrawNone) { assert (pWinPixmap != pParentPixmap); assert (pWinPixmap != pScreenPixmap); } else { assert (pWinPixmap == pParentPixmap); } assert (0 < pWinPixmap->refcnt && pWinPixmap->refcnt < 3); assert (0 < pScreenPixmap->refcnt && pScreenPixmap->refcnt < 3); if (pParentPixmap) assert (0 <= pParentPixmap->refcnt && pParentPixmap->refcnt < 3); return WT_WALKCHILDREN; } void compCheckTree (ScreenPtr pScreen) { WalkTree (pScreen, compCheckWindow, 0); } #endif typedef struct _compPixmapVisit { WindowPtr pWindow; PixmapPtr pPixmap; } CompPixmapVisitRec, *CompPixmapVisitPtr; static Bool compRepaintBorder (ClientPtr pClient, pointer closure) { WindowPtr pWindow; int rc = dixLookupWindow(&pWindow, (XID)(intptr_t)closure, pClient, DixWriteAccess); if (rc == Success) { RegionRec exposed; REGION_NULL(pScreen, &exposed); REGION_SUBTRACT(pScreen, &exposed, &pWindow->borderClip, &pWindow->winSize); miPaintWindow(pWindow, &exposed, PW_BORDER); REGION_UNINIT(pScreen, &exposed); } return TRUE; } static int compSetPixmapVisitWindow (WindowPtr pWindow, pointer data) { CompPixmapVisitPtr pVisit = (CompPixmapVisitPtr) data; ScreenPtr pScreen = pWindow->drawable.pScreen; if (pWindow != pVisit->pWindow && pWindow->redirectDraw != RedirectDrawNone) return WT_DONTWALKCHILDREN; (*pScreen->SetWindowPixmap) (pWindow, pVisit->pPixmap); /* * Recompute winSize and borderSize. This is duplicate effort * when resizing pixmaps, but necessary when changing redirection. * Might be nice to fix this. */ SetWinSize (pWindow); SetBorderSize (pWindow); if (HasBorder (pWindow)) QueueWorkProc (compRepaintBorder, serverClient, (pointer)(intptr_t) pWindow->drawable.id); return WT_WALKCHILDREN; } void compSetPixmap (WindowPtr pWindow, PixmapPtr pPixmap) { CompPixmapVisitRec visitRec; visitRec.pWindow = pWindow; visitRec.pPixmap = pPixmap; TraverseTree (pWindow, compSetPixmapVisitWindow, (pointer) &visitRec); compCheckTree (pWindow->drawable.pScreen); } Bool compCheckRedirect (WindowPtr pWin) { CompWindowPtr cw = GetCompWindow (pWin); CompScreenPtr cs = GetCompScreen(pWin->drawable.pScreen); Bool should; should = pWin->realized && (pWin->drawable.class != InputOnly) && (cw != NULL) && (pWin->parent != NULL); /* Never redirect the overlay window */ if (cs->pOverlayWin != NULL) { if (pWin == cs->pOverlayWin) { should = FALSE; } } if (should != (pWin->redirectDraw != RedirectDrawNone)) { if (should) return compAllocPixmap (pWin); else compFreePixmap (pWin); } return TRUE; } static int updateOverlayWindow(ScreenPtr pScreen) { CompScreenPtr cs; WindowPtr pWin; /* overlay window */ XID vlist[2]; cs = GetCompScreen(pScreen); if ((pWin = cs->pOverlayWin) != NULL) { if ((pWin->drawable.width == pScreen->width) && (pWin->drawable.height == pScreen->height)) return Success; /* Let's resize the overlay window. */ vlist[0] = pScreen->width; vlist[1] = pScreen->height; return ConfigureWindow(pWin, CWWidth | CWHeight, vlist, wClient(pWin)); } /* Let's be on the safe side and not assume an overlay window is always allocated. */ return Success; } Bool compPositionWindow (WindowPtr pWin, int x, int y) { ScreenPtr pScreen = pWin->drawable.pScreen; CompScreenPtr cs = GetCompScreen (pScreen); Bool ret = TRUE; pScreen->PositionWindow = cs->PositionWindow; /* * "Shouldn't need this as all possible places should be wrapped * compCheckRedirect (pWin); */ #ifdef COMPOSITE_DEBUG if ((pWin->redirectDraw != RedirectDrawNone) != (pWin->viewable && (GetCompWindow(pWin) != NULL))) OsAbort (); #endif if (pWin->redirectDraw != RedirectDrawNone) { PixmapPtr pPixmap = (*pScreen->GetWindowPixmap) (pWin); int bw = wBorderWidth (pWin); int nx = pWin->drawable.x - bw; int ny = pWin->drawable.y - bw; if (pPixmap->screen_x != nx || pPixmap->screen_y != ny) { pPixmap->screen_x = nx; pPixmap->screen_y = ny; pPixmap->drawable.serialNumber = NEXT_SERIAL_NUMBER; } } if (!(*pScreen->PositionWindow) (pWin, x, y)) ret = FALSE; cs->PositionWindow = pScreen->PositionWindow; pScreen->PositionWindow = compPositionWindow; compCheckTree (pWin->drawable.pScreen); if (updateOverlayWindow(pScreen) != Success) ret = FALSE; return ret; } Bool compRealizeWindow (WindowPtr pWin) { ScreenPtr pScreen = pWin->drawable.pScreen; CompScreenPtr cs = GetCompScreen (pScreen); Bool ret = TRUE; pScreen->RealizeWindow = cs->RealizeWindow; compCheckRedirect (pWin); if (!(*pScreen->RealizeWindow) (pWin)) ret = FALSE; cs->RealizeWindow = pScreen->RealizeWindow; pScreen->RealizeWindow = compRealizeWindow; compCheckTree (pWin->drawable.pScreen); return ret; } Bool compUnrealizeWindow (WindowPtr pWin) { ScreenPtr pScreen = pWin->drawable.pScreen; CompScreenPtr cs = GetCompScreen (pScreen); Bool ret = TRUE; pScreen->UnrealizeWindow = cs->UnrealizeWindow; compCheckRedirect (pWin); if (!(*pScreen->UnrealizeWindow) (pWin)) ret = FALSE; cs->UnrealizeWindow = pScreen->UnrealizeWindow; pScreen->UnrealizeWindow = compUnrealizeWindow; compCheckTree (pWin->drawable.pScreen); return ret; } /* * Called after the borderClip for the window has settled down * We use this to make sure our extra borderClip has the right origin */ void compClipNotify (WindowPtr pWin, int dx, int dy) { ScreenPtr pScreen = pWin->drawable.pScreen; CompScreenPtr cs = GetCompScreen (pScreen); CompWindowPtr cw = GetCompWindow (pWin); if (cw) { if (cw->borderClipX != pWin->drawable.x || cw->borderClipY != pWin->drawable.y) { REGION_TRANSLATE (pScreen, &cw->borderClip, pWin->drawable.x - cw->borderClipX, pWin->drawable.y - cw->borderClipY); cw->borderClipX = pWin->drawable.x; cw->borderClipY = pWin->drawable.y; } } if (cs->ClipNotify) { pScreen->ClipNotify = cs->ClipNotify; (*pScreen->ClipNotify) (pWin, dx, dy); cs->ClipNotify = pScreen->ClipNotify; pScreen->ClipNotify = compClipNotify; } } /* * Returns TRUE if the window needs server-provided automatic redirect, * which is true if the child and parent aren't both regular or ARGB visuals */ static Bool compIsAlternateVisual (ScreenPtr pScreen, XID visual) { CompScreenPtr cs = GetCompScreen (pScreen); int i; for (i = 0; i < cs->numAlternateVisuals; i++) if (cs->alternateVisuals[i] == visual) return TRUE; return FALSE; } static Bool compImplicitRedirect (WindowPtr pWin, WindowPtr pParent) { if (pParent) { ScreenPtr pScreen = pWin->drawable.pScreen; XID winVisual = wVisual (pWin); XID parentVisual = wVisual (pParent); if (winVisual != parentVisual && (compIsAlternateVisual (pScreen, winVisual) || compIsAlternateVisual (pScreen, parentVisual))) return TRUE; } return FALSE; } void compReparentWindow (WindowPtr pWin, WindowPtr pPriorParent) { ScreenPtr pScreen = pWin->drawable.pScreen; CompScreenPtr cs = GetCompScreen (pScreen); pScreen->ReparentWindow = cs->ReparentWindow; /* * Remove any implicit redirect due to synthesized visual */ if (compImplicitRedirect (pWin, pPriorParent)) compUnredirectWindow (serverClient, pWin, CompositeRedirectAutomatic); /* * Handle subwindows redirection */ compUnredirectOneSubwindow (pPriorParent, pWin); compRedirectOneSubwindow (pWin->parent, pWin); /* * Add any implict redirect due to synthesized visual */ if (compImplicitRedirect (pWin, pWin->parent)) compRedirectWindow (serverClient, pWin, CompositeRedirectAutomatic); /* * Allocate any necessary redirect pixmap * (this actually should never be true; pWin is always unmapped) */ compCheckRedirect (pWin); /* * Reset pixmap pointers as appropriate */ if (pWin->parent && pWin->redirectDraw == RedirectDrawNone) compSetPixmap (pWin, (*pScreen->GetWindowPixmap) (pWin->parent)); /* * Call down to next function */ if (pScreen->ReparentWindow) (*pScreen->ReparentWindow) (pWin, pPriorParent); cs->ReparentWindow = pScreen->ReparentWindow; pScreen->ReparentWindow = compReparentWindow; compCheckTree (pWin->drawable.pScreen); } void compCopyWindow (WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc) { ScreenPtr pScreen = pWin->drawable.pScreen; CompScreenPtr cs = GetCompScreen (pScreen); int dx = 0, dy = 0; if (pWin->redirectDraw != RedirectDrawNone) { PixmapPtr pPixmap = (*pScreen->GetWindowPixmap) (pWin); CompWindowPtr cw = GetCompWindow (pWin); assert (cw->oldx != COMP_ORIGIN_INVALID); assert (cw->oldy != COMP_ORIGIN_INVALID); if (cw->pOldPixmap) { /* * Ok, the old bits are available in pOldPixmap and * need to be copied to pNewPixmap. */ RegionRec rgnDst; PixmapPtr pPixmap = (*pScreen->GetWindowPixmap) (pWin); GCPtr pGC; dx = ptOldOrg.x - pWin->drawable.x; dy = ptOldOrg.y - pWin->drawable.y; REGION_TRANSLATE(pWin->drawable.pScreen, prgnSrc, -dx, -dy); REGION_NULL (pWin->drawable.pScreen, &rgnDst); REGION_INTERSECT(pWin->drawable.pScreen, &rgnDst, &pWin->borderClip, prgnSrc); REGION_TRANSLATE (pWin->drawable.pScreen, &rgnDst, -pPixmap->screen_x, -pPixmap->screen_y); dx = dx + pPixmap->screen_x - cw->oldx; dy = dy + pPixmap->screen_y - cw->oldy; pGC = GetScratchGC (pPixmap->drawable.depth, pScreen); if (pGC) { BoxPtr pBox = REGION_RECTS (&rgnDst); int nBox = REGION_NUM_RECTS (&rgnDst); ValidateGC(&pPixmap->drawable, pGC); while (nBox--) { (void) (*pGC->ops->CopyArea) (&cw->pOldPixmap->drawable, &pPixmap->drawable, pGC, pBox->x1 + dx, pBox->y1 + dy, pBox->x2 - pBox->x1, pBox->y2 - pBox->y1, pBox->x1, pBox->y1); pBox++; } FreeScratchGC (pGC); } return; } dx = pPixmap->screen_x - cw->oldx; dy = pPixmap->screen_y - cw->oldy; ptOldOrg.x += dx; ptOldOrg.y += dy; } pScreen->CopyWindow = cs->CopyWindow; if (ptOldOrg.x != pWin->drawable.x || ptOldOrg.y != pWin->drawable.y) { if (dx || dy) REGION_TRANSLATE (pScreen, prgnSrc, dx, dy); (*pScreen->CopyWindow) (pWin, ptOldOrg, prgnSrc); if (dx || dy) REGION_TRANSLATE (pScreen, prgnSrc, -dx, -dy); } else { ptOldOrg.x -= dx; ptOldOrg.y -= dy; REGION_TRANSLATE (prgnSrc, prgnSrc, pWin->drawable.x - ptOldOrg.x, pWin->drawable.y - ptOldOrg.y); DamageRegionAppend(&pWin->drawable, prgnSrc); } cs->CopyWindow = pScreen->CopyWindow; pScreen->CopyWindow = compCopyWindow; compCheckTree (pWin->drawable.pScreen); } Bool compCreateWindow (WindowPtr pWin) { ScreenPtr pScreen = pWin->drawable.pScreen; CompScreenPtr cs = GetCompScreen (pScreen); Bool ret; pScreen->CreateWindow = cs->CreateWindow; ret = (*pScreen->CreateWindow) (pWin); if (pWin->parent && ret) { CompSubwindowsPtr csw = GetCompSubwindows (pWin->parent); CompClientWindowPtr ccw; (*pScreen->SetWindowPixmap) (pWin, (*pScreen->GetWindowPixmap) (pWin->parent)); if (csw) for (ccw = csw->clients; ccw; ccw = ccw->next) compRedirectWindow (clients[CLIENT_ID(ccw->id)], pWin, ccw->update); if (compImplicitRedirect (pWin, pWin->parent)) compRedirectWindow (serverClient, pWin, CompositeRedirectAutomatic); } cs->CreateWindow = pScreen->CreateWindow; pScreen->CreateWindow = compCreateWindow; compCheckTree (pWin->drawable.pScreen); return ret; } Bool compDestroyWindow (WindowPtr pWin) { ScreenPtr pScreen = pWin->drawable.pScreen; CompScreenPtr cs = GetCompScreen (pScreen); CompWindowPtr cw; CompSubwindowsPtr csw; Bool ret; pScreen->DestroyWindow = cs->DestroyWindow; while ((cw = GetCompWindow (pWin))) FreeResource (cw->clients->id, RT_NONE); while ((csw = GetCompSubwindows (pWin))) FreeResource (csw->clients->id, RT_NONE); if (pWin->redirectDraw != RedirectDrawNone) compFreePixmap (pWin); ret = (*pScreen->DestroyWindow) (pWin); cs->DestroyWindow = pScreen->DestroyWindow; pScreen->DestroyWindow = compDestroyWindow; /* compCheckTree (pWin->drawable.pScreen); can't check -- tree isn't good*/ return ret; } void compSetRedirectBorderClip (WindowPtr pWin, RegionPtr pRegion) { CompWindowPtr cw = GetCompWindow (pWin); RegionRec damage; REGION_NULL (pScreen, &damage); /* * Align old border clip with new border clip */ REGION_TRANSLATE (pScreen, &cw->borderClip, pWin->drawable.x - cw->borderClipX, pWin->drawable.y - cw->borderClipY); /* * Compute newly visible portion of window for repaint */ REGION_SUBTRACT (pScreen, &damage, pRegion, &cw->borderClip); /* * Report that as damaged so it will be redrawn */ DamageRegionAppend(&pWin->drawable, &damage); REGION_UNINIT (pScreen, &damage); /* * Save the new border clip region */ REGION_COPY (pScreen, &cw->borderClip, pRegion); cw->borderClipX = pWin->drawable.x; cw->borderClipY = pWin->drawable.y; } RegionPtr compGetRedirectBorderClip (WindowPtr pWin) { CompWindowPtr cw = GetCompWindow (pWin); return &cw->borderClip; } static VisualPtr compGetWindowVisual (WindowPtr pWin) { ScreenPtr pScreen = pWin->drawable.pScreen; VisualID vid = wVisual (pWin); int i; for (i = 0; i < pScreen->numVisuals; i++) if (pScreen->visuals[i].vid == vid) return &pScreen->visuals[i]; return 0; } PictFormatPtr compWindowFormat (WindowPtr pWin) { ScreenPtr pScreen = pWin->drawable.pScreen; return PictureMatchVisual (pScreen, pWin->drawable.depth, compGetWindowVisual (pWin)); } static void compWindowUpdateAutomatic (WindowPtr pWin) { CompWindowPtr cw = GetCompWindow (pWin); ScreenPtr pScreen = pWin->drawable.pScreen; WindowPtr pParent = pWin->parent; PixmapPtr pSrcPixmap = (*pScreen->GetWindowPixmap) (pWin); PictFormatPtr pSrcFormat = compWindowFormat (pWin); PictFormatPtr pDstFormat = compWindowFormat (pWin->parent); int error; RegionPtr pRegion = DamageRegion (cw->damage); PicturePtr pSrcPicture = CreatePicture (0, &pSrcPixmap->drawable, pSrcFormat, 0, 0, serverClient, &error); XID subwindowMode = IncludeInferiors; PicturePtr pDstPicture = CreatePicture (0, &pParent->drawable, pDstFormat, CPSubwindowMode, &subwindowMode, serverClient, &error); /* * First move the region from window to screen coordinates */ REGION_TRANSLATE (pScreen, pRegion, pWin->drawable.x, pWin->drawable.y); /* * Clip against the "real" border clip */ REGION_INTERSECT (pScreen, pRegion, pRegion, &cw->borderClip); /* * Now translate from screen to dest coordinates */ REGION_TRANSLATE (pScreen, pRegion, -pParent->drawable.x, -pParent->drawable.y); /* * Clip the picture */ SetPictureClipRegion (pDstPicture, 0, 0, pRegion); /* * And paint */ CompositePicture (PictOpSrc, pSrcPicture, 0, pDstPicture, 0, 0, /* src_x, src_y */ 0, 0, /* msk_x, msk_y */ pSrcPixmap->screen_x - pParent->drawable.x, pSrcPixmap->screen_y - pParent->drawable.y, pSrcPixmap->drawable.width, pSrcPixmap->drawable.height); FreePicture (pSrcPicture, 0); FreePicture (pDstPicture, 0); /* * Empty the damage region. This has the nice effect of * rendering the translations above harmless */ DamageEmpty (cw->damage); } void compWindowUpdate (WindowPtr pWin) { WindowPtr pChild; for (pChild = pWin->lastChild; pChild; pChild = pChild->prevSib) compWindowUpdate (pChild); if (pWin->redirectDraw != RedirectDrawNone) { CompWindowPtr cw = GetCompWindow(pWin); if (cw->damaged) { compWindowUpdateAutomatic (pWin); cw->damaged = FALSE; } } } WindowPtr CompositeRealChildHead (WindowPtr pWin) { WindowPtr pChild, pChildBefore; CompScreenPtr cs; if (!pWin->parent && (screenIsSaved == SCREEN_SAVER_ON) && (HasSaverWindow (pWin->drawable.pScreen))) { /* First child is the screen saver; see if next child is the overlay */ pChildBefore = pWin->firstChild; pChild = pChildBefore->nextSib; } else { pChildBefore = NullWindow; pChild = pWin->firstChild; } if (!pChild) { return NullWindow; } cs = GetCompScreen(pWin->drawable.pScreen); if (pChild == cs->pOverlayWin) { return pChild; } else { return pChildBefore; } } int compConfigNotify(WindowPtr pWin, int x, int y, int w, int h, int bw, WindowPtr pSib) { ScreenPtr pScreen = pWin->drawable.pScreen; CompScreenPtr cs = GetCompScreen (pScreen); Bool ret = 0; WindowPtr pParent = pWin->parent; CompWindowPtr cw; int draw_x, draw_y; Bool alloc_ret; if (cs->ConfigNotify) { pScreen->ConfigNotify = cs->ConfigNotify; ret = (*pScreen->ConfigNotify)(pWin, x, y, w, h, bw, pSib); cs->ConfigNotify = pScreen->ConfigNotify; pScreen->ConfigNotify = compConfigNotify; if (ret) return ret; } if (pWin->redirectDraw == RedirectDrawNone) return Success; compCheckTree (pScreen); draw_x = pParent->drawable.x + x + bw; draw_y = pParent->drawable.y + y + bw; alloc_ret = compReallocPixmap (pWin, draw_x, draw_y, w, h, bw); cw = GetCompWindow (pWin); if (cw->pOldPixmap) { (*pScreen->DestroyPixmap) (cw->pOldPixmap); cw->pOldPixmap = NullPixmap; } compCheckTree (pScreen); if (alloc_ret == FALSE) return BadAlloc; return Success; }