/* * $XFree86: xc/programs/Xserver/miext/layer/layerwin.c,v 1.4 2001/07/20 19:25:01 keithp Exp $ * * Copyright © 2001 Keith Packard, member of The XFree86 Project, Inc. * * 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. */ #include "layerstr.h" static LayerListPtr NewLayerList (ScreenPtr pScreen, LayerPtr pLayer) { LayerListPtr pLayList; pLayList = (LayerListPtr) xalloc (sizeof (LayerListRec)); if (!pLayList) return 0; pLayList->pNext = 0; pLayList->pLayer = pLayer; pLayList->inheritClip = TRUE; REGION_INIT (pScreen, &pLayList->clipList, NullBox, 0); REGION_INIT (pScreen, &pLayList->borderClip, NullBox, 0); return pLayList; } static void FreeLayerList (ScreenPtr pScreen, LayerListPtr pLayList) { REGION_UNINIT (&pScreen, &pLayList->clipList); REGION_UNINIT (&pScreen, &pLayList->borderClip); xfree (pLayList); } /* * Create pixmap for a layer */ Bool LayerCreatePixmap (ScreenPtr pScreen, LayerPtr pLayer) { /* XXX create full-screen sized layers all around */ pLayer->pPixmap = (*pScreen->CreatePixmap) (pScreen, pScreen->width, pScreen->height, pLayer->depth); if (!pLayer->pPixmap) return FALSE; if (pLayer->pKind->kind == LAYER_SHADOW) { if (!shadowAdd (pScreen, pLayer->pPixmap, pLayer->update, pLayer->window, pLayer->rotate, pLayer->closure)) return FALSE; } return TRUE; } /* * Destroy pixmap for a layer */ void LayerDestroyPixmap (ScreenPtr pScreen, LayerPtr pLayer) { if (pLayer->pPixmap) { if (pLayer->pKind->kind == LAYER_SHADOW) shadowRemove (pScreen, pLayer->pPixmap); if (pLayer->freePixmap) (*pScreen->DestroyPixmap) (pLayer->pPixmap); } pLayer->pPixmap = 0; } /* * Add a window to a layer */ Bool LayerWindowAdd (ScreenPtr pScreen, LayerPtr pLayer, WindowPtr pWin) { layerWinPriv(pWin); if (pLayer->pPixmap == LAYER_SCREEN_PIXMAP) pLayer->pPixmap = (*pScreen->GetScreenPixmap) (pScreen); else if (!pLayer->pPixmap && !LayerCreatePixmap (pScreen, pLayer)) return FALSE; /* * Add a new layer list if needed */ if (pLayWin->isList || pLayWin->u.pLayer) { LayerListPtr pPrev; LayerListPtr pLayList; if (!pLayWin->isList) { pPrev = NewLayerList (pScreen, pLayWin->u.pLayer); if (!pPrev) return FALSE; } else { for (pPrev = pLayWin->u.pLayList; pPrev->pNext; pPrev = pPrev->pNext) ; } pLayList = NewLayerList (pScreen, pLayer); if (!pLayList) { if (!pLayWin->isList) FreeLayerList (pScreen, pPrev); return FALSE; } pPrev->pNext = pLayList; if (!pLayWin->isList) { pLayWin->isList = TRUE; pLayWin->u.pLayList = pPrev; } } else pLayWin->u.pLayer = pLayer; /* * XXX only one layer supported for drawing, last one wins */ (*pScreen->SetWindowPixmap) (pWin, pLayer->pPixmap); pWin->drawable.serialNumber = NEXT_SERIAL_NUMBER; pLayer->refcnt++; pLayer->windows++; return TRUE; } /* * Remove a window from a layer */ void LayerWindowRemove (ScreenPtr pScreen, LayerPtr pLayer, WindowPtr pWin) { layerWinPriv(pWin); if (pLayWin->isList) { LayerListPtr *pPrev; LayerListPtr pLayList; for (pPrev = &pLayWin->u.pLayList; pLayList = *pPrev; pPrev = &pLayList->pNext) { if (pLayList->pLayer == pLayer) { *pPrev = pLayList->pNext; FreeLayerList (pScreen, pLayList); --pLayer->windows; if (pLayer->windows <= 0) LayerDestroyPixmap (pScreen, pLayer); LayerDestroy (pScreen, pLayer); break; } } pLayList = pLayWin->u.pLayList; if (!pLayList) { /* * List is empty, set isList back to false */ pLayWin->isList = FALSE; pLayWin->u.pLayer = 0; } else if (!pLayList->pNext && pLayList->inheritClip) { /* * List contains a single element using the * window clip, free the list structure and * host the layer back to the window private */ pLayer = pLayList->pLayer; FreeLayerList (pScreen, pLayList); pLayWin->isList = FALSE; pLayWin->u.pLayer = pLayer; } } else { if (pLayWin->u.pLayer == pLayer) { --pLayer->windows; if (pLayer->windows <= 0) LayerDestroyPixmap (pScreen, pLayer); LayerDestroy (pScreen, pLayer); pLayWin->u.pLayer = 0; } } pWin->drawable.serialNumber = NEXT_SERIAL_NUMBER; } /* * Looping primitives for window layering. Usage: * * for (pLayer = LayerWindowFirst (pWin, &loop); * pLayer; * pLayer = LayerWindowNext (&loop)) * { * ... * } * LayerWindowDone (pWin, &loop); */ LayerPtr LayerWindowFirst (WindowPtr pWin, LayerWinLoopPtr pLoop) { layerWinPriv (pWin); LayerListPtr pLayList; LayerPtr pLayer; pLoop->pLayWin = pLayWin; if (!pLayWin->isList) return pLayWin->u.pLayer; /* * Preserve original state */ pLoop->clipList = pWin->clipList; pLoop->borderClip = pWin->borderClip; pLoop->pPixmap = (*pWin->drawable.pScreen->GetWindowPixmap) (pWin); /* * Set initial list element */ pLoop->pLayList = pLayWin->u.pLayList; /* * Return first layer */ return LayerWindowNext (pWin, pLoop); } LayerPtr LayerWindowNext (WindowPtr pWin, LayerWinLoopPtr pLoop) { LayerPtr pLayer; LayerWinPtr pLayWin = pLoop->pLayWin; LayerListPtr pLayList; if (!pLayWin->isList) return 0; pLayList = pLoop->pLayList; pLayer = pLayList->pLayer; /* * Configure window for this layer */ (*pWin->drawable.pScreen->SetWindowPixmap) (pWin, pLayer->pPixmap); if (!pLayList->inheritClip) { pWin->clipList = pLayList->clipList; pWin->borderClip = pLayList->borderClip; } /* * Step to next layer list */ pLoop->pLayList = pLayList->pNext; /* * Return layer */ return pLayer; } void LayerWindowDone (WindowPtr pWin, LayerWinLoopPtr pLoop) { LayerWinPtr pLayWin = pLoop->pLayWin; if (!pLayWin->isList) return; /* * clean up after the loop */ pWin->clipList = pLoop->clipList; pWin->borderClip = pLoop->clipList; (*pWin->drawable.pScreen->SetWindowPixmap) (pWin, pLoop->pPixmap); } Bool layerCreateWindow (WindowPtr pWin) { ScreenPtr pScreen = pWin->drawable.pScreen; layerWinPriv(pWin); layerScrPriv(pScreen); LayerPtr pLayer; Bool ret; pLayWin->isList = FALSE; pLayWin->u.pLayer = 0; /* * input only windows don't live in any layer */ if (pWin->drawable.type == UNDRAWABLE_WINDOW) return TRUE; /* * Use a reasonable default layer -- the first * layer matching the windows depth. Subsystems needing * alternative layering semantics can override this by * replacing this function. Perhaps a new screen function * could be used to select the correct initial window * layer instead. */ for (pLayer = pLayScr->pLayers; pLayer; pLayer = pLayer->pNext) if (pLayer->depth == pWin->drawable.depth) break; ret = TRUE; if (pLayer) { pScreen->CreateWindow = pLayer->pKind->CreateWindow; ret = (*pScreen->CreateWindow) (pWin); pLayer->pKind->CreateWindow = pScreen->CreateWindow; pScreen->CreateWindow = layerCreateWindow; LayerWindowAdd (pScreen, pLayer, pWin); } return ret; } Bool layerDestroyWindow (WindowPtr pWin) { ScreenPtr pScreen = pWin->drawable.pScreen; layerWinPriv(pWin); LayerPtr pLayer; Bool ret = TRUE; while (pLayer = layerWinLayer (pLayWin)) { LayerUnwrap (pScreen, pLayer->pKind, DestroyWindow); ret = (*pScreen->DestroyWindow) (pWin); LayerWrap (pScreen, pLayer->pKind, DestroyWindow, layerDestroyWindow); LayerWindowRemove (pWin->drawable.pScreen, pLayer, pWin); } return ret; } Bool layerChangeWindowAttributes (WindowPtr pWin, unsigned long mask) { ScreenPtr pScreen = pWin->drawable.pScreen; layerWinPriv (pWin); LayerPtr pLay; LayerWinLoopRec loop; Bool ret = TRUE; for (pLay = LayerWindowFirst (pWin, &loop); pLay; pLay = LayerWindowNext (pWin, &loop)) { LayerUnwrap(pScreen,pLay->pKind,ChangeWindowAttributes); if (!(*pScreen->ChangeWindowAttributes) (pWin, mask)) ret = FALSE; LayerWrap(pScreen,pLay->pKind,ChangeWindowAttributes,layerChangeWindowAttributes); } LayerWindowDone (pWin, &loop); return ret; } void layerPaintWindowBackground (WindowPtr pWin, RegionPtr pRegion, int what) { layerWinPriv (pWin); ScreenPtr pScreen = pWin->drawable.pScreen; LayerPtr pLay; LayerWinLoopRec loop; for (pLay = LayerWindowFirst (pWin, &loop); pLay; pLay = LayerWindowNext (pWin, &loop)) { LayerUnwrap(pScreen,pLay->pKind,PaintWindowBackground); (*pScreen->PaintWindowBackground) (pWin, pRegion, what); LayerWrap(pScreen,pLay->pKind,PaintWindowBackground,layerPaintWindowBackground); } LayerWindowDone (pWin, &loop); } void layerPaintWindowBorder (WindowPtr pWin, RegionPtr pRegion, int what) { layerWinPriv (pWin); ScreenPtr pScreen = pWin->drawable.pScreen; LayerPtr pLay; LayerWinLoopRec loop; for (pLay = LayerWindowFirst (pWin, &loop); pLay; pLay = LayerWindowNext (pWin, &loop)) { LayerUnwrap(pScreen,pLay->pKind,PaintWindowBorder); (*pScreen->PaintWindowBorder) (pWin, pRegion, what); LayerWrap(pScreen,pLay->pKind,PaintWindowBorder,layerPaintWindowBorder); } LayerWindowDone (pWin, &loop); } void layerCopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc) { layerWinPriv (pWin); ScreenPtr pScreen = pWin->drawable.pScreen; LayerPtr pLay; LayerWinLoopRec loop; int dx = 0, dy = 0; for (pLay = LayerWindowFirst (pWin, &loop); pLay; pLay = LayerWindowNext (pWin, &loop)) { LayerUnwrap(pScreen,pLay->pKind,CopyWindow); /* * Undo the translation done within the last CopyWindow proc (sigh) */ if (dx || dy) REGION_TRANSLATE(pWin->drawable.pScreen, prgnSrc, dx, dy); (*pScreen->CopyWindow) (pWin, ptOldOrg, prgnSrc); LayerWrap(pScreen,pLay->pKind,CopyWindow,layerCopyWindow); /* * Save offset to undo translation next time around */ dx = ptOldOrg.x - pWin->drawable.x; dy = ptOldOrg.y - pWin->drawable.y; } LayerWindowDone (pWin, &loop); }