/* * Screen routines for generic rootless X server */ /* * Copyright (c) 2001 Greg Parker. All Rights Reserved. * Copyright (c) 2002-2003 Torrey T. Lyons. All Rights Reserved. * Copyright (c) 2002 Apple Computer, 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 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 ABOVE LISTED COPYRIGHT HOLDER(S) 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(s) of the above copyright * holders shall not be used in advertising or otherwise to promote the sale, * use or other dealings in this Software without prior written authorization. */ #ifdef HAVE_DIX_CONFIG_H #include #endif #include "mi.h" #include "scrnintstr.h" #include "gcstruct.h" #include "pixmapstr.h" #include "windowstr.h" #include "propertyst.h" #include "mivalidate.h" #include "picturestr.h" #include "colormapst.h" #include #include #include #include #include "rootlessCommon.h" #include "rootlessWindow.h" /* In milliseconds */ #ifndef ROOTLESS_REDISPLAY_DELAY #define ROOTLESS_REDISPLAY_DELAY 10 #endif extern int RootlessMiValidateTree(WindowPtr pRoot, WindowPtr pChild, VTKind kind); extern Bool RootlessCreateGC(GCPtr pGC); // Initialize globals static int rootlessGCPrivateKeyIndex; DevPrivateKey rootlessGCPrivateKey = &rootlessGCPrivateKeyIndex; static int rootlessScreenPrivateKeyIndex; DevPrivateKey rootlessScreenPrivateKey = &rootlessScreenPrivateKeyIndex; static int rootlessWindowPrivateKeyIndex; DevPrivateKey rootlessWindowPrivateKey = &rootlessWindowPrivateKeyIndex; static int rootlessWindowOldPixmapPrivateKeyIndex; DevPrivateKey rootlessWindowOldPixmapPrivateKey = &rootlessWindowOldPixmapPrivateKeyIndex; /* * RootlessUpdateScreenPixmap * miCreateScreenResources does not like a null framebuffer pointer, * it leaves the screen pixmap with an uninitialized data pointer. * Thus, rootless implementations typically set the framebuffer width * to zero so that miCreateScreenResources does not allocate a screen * pixmap for us. We allocate our own screen pixmap here since we need * the screen pixmap to be valid (e.g. CopyArea from the root window). */ void RootlessUpdateScreenPixmap(ScreenPtr pScreen) { RootlessScreenRec *s = SCREENREC(pScreen); PixmapPtr pPix; unsigned int rowbytes; pPix = (*pScreen->GetScreenPixmap)(pScreen); if (pPix == NULL) { pPix = (*pScreen->CreatePixmap)(pScreen, 0, 0, pScreen->rootDepth, 0); (*pScreen->SetScreenPixmap)(pPix); } rowbytes = PixmapBytePad(pScreen->width, pScreen->rootDepth); if (s->pixmap_data_size < rowbytes) { if (s->pixmap_data != NULL) xfree(s->pixmap_data); s->pixmap_data_size = rowbytes; s->pixmap_data = xalloc(s->pixmap_data_size); if (s->pixmap_data == NULL) return; memset(s->pixmap_data, 0xFF, s->pixmap_data_size); pScreen->ModifyPixmapHeader(pPix, pScreen->width, pScreen->height, pScreen->rootDepth, BitsPerPixel(pScreen->rootDepth), 0, s->pixmap_data); /* ModifyPixmapHeader ignores zero arguments, so install rowbytes by hand. */ pPix->devKind = 0; } } /* * RootlessCreateScreenResources * Rootless implementations typically set a null framebuffer pointer, which * causes problems with miCreateScreenResources. We fix things up here. */ static Bool RootlessCreateScreenResources(ScreenPtr pScreen) { Bool ret = TRUE; SCREEN_UNWRAP(pScreen, CreateScreenResources); if (pScreen->CreateScreenResources != NULL) ret = (*pScreen->CreateScreenResources)(pScreen); SCREEN_WRAP(pScreen, CreateScreenResources); if (!ret) return ret; /* Make sure we have a valid screen pixmap. */ RootlessUpdateScreenPixmap(pScreen); return ret; } static Bool RootlessCloseScreen(int i, ScreenPtr pScreen) { RootlessScreenRec *s; s = SCREENREC(pScreen); // fixme unwrap everything that was wrapped? pScreen->CloseScreen = s->CloseScreen; if (s->pixmap_data != NULL) { xfree (s->pixmap_data); s->pixmap_data = NULL; s->pixmap_data_size = 0; } xfree(s); return pScreen->CloseScreen(i, pScreen); } static void RootlessGetImage(DrawablePtr pDrawable, int sx, int sy, int w, int h, unsigned int format, unsigned long planeMask, char *pdstLine) { ScreenPtr pScreen = pDrawable->pScreen; SCREEN_UNWRAP(pScreen, GetImage); if (pDrawable->type == DRAWABLE_WINDOW) { int x0, y0, x1, y1; RootlessWindowRec *winRec; // Many apps use GetImage to sync with the visible frame buffer // FIXME: entire screen or just window or all screens? RootlessRedisplayScreen(pScreen); // RedisplayScreen stops drawing, so we need to start it again RootlessStartDrawing((WindowPtr)pDrawable); /* Check that we have some place to read from. */ winRec = WINREC(TopLevelParent((WindowPtr) pDrawable)); if (winRec == NULL) goto out; /* Clip to top-level window bounds. */ /* FIXME: fbGetImage uses the width parameter to calculate the stride of the destination pixmap. If w is clipped, the data returned will be garbage, although we will not crash. */ x0 = pDrawable->x + sx; y0 = pDrawable->y + sy; x1 = x0 + w; y1 = y0 + h; x0 = max (x0, winRec->x); y0 = max (y0, winRec->y); x1 = min (x1, winRec->x + winRec->width); y1 = min (y1, winRec->y + winRec->height); sx = x0 - pDrawable->x; sy = y0 - pDrawable->y; w = x1 - x0; h = y1 - y0; if (w <= 0 || h <= 0) goto out; } pScreen->GetImage(pDrawable, sx, sy, w, h, format, planeMask, pdstLine); out: SCREEN_WRAP(pScreen, GetImage); } /* * RootlessSourceValidate * CopyArea and CopyPlane use a GC tied to the destination drawable. * StartDrawing/StopDrawing wrappers won't be called if source is * a visible window but the destination isn't. So, we call StartDrawing * here and leave StopDrawing for the block handler. */ static void RootlessSourceValidate(DrawablePtr pDrawable, int x, int y, int w, int h) { SCREEN_UNWRAP(pDrawable->pScreen, SourceValidate); if (pDrawable->type == DRAWABLE_WINDOW) { WindowPtr pWin = (WindowPtr)pDrawable; RootlessStartDrawing(pWin); } if (pDrawable->pScreen->SourceValidate) { pDrawable->pScreen->SourceValidate(pDrawable, x, y, w, h); } SCREEN_WRAP(pDrawable->pScreen, SourceValidate); } #ifdef RENDER static void RootlessComposite(CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst, INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask, INT16 xDst, INT16 yDst, CARD16 width, CARD16 height) { ScreenPtr pScreen = pDst->pDrawable->pScreen; PictureScreenPtr ps = GetPictureScreen(pScreen); WindowPtr srcWin, dstWin, maskWin = NULL; if (pMask) { // pMask can be NULL maskWin = (pMask->pDrawable->type == DRAWABLE_WINDOW) ? (WindowPtr)pMask->pDrawable : NULL; } srcWin = (pSrc->pDrawable && pSrc->pDrawable->type == DRAWABLE_WINDOW) ? (WindowPtr)pSrc->pDrawable : NULL; dstWin = (pDst->pDrawable->type == DRAWABLE_WINDOW) ? (WindowPtr)pDst->pDrawable : NULL; // SCREEN_UNWRAP(ps, Composite); ps->Composite = SCREENREC(pScreen)->Composite; if (srcWin && IsFramedWindow(srcWin)) RootlessStartDrawing(srcWin); if (maskWin && IsFramedWindow(maskWin)) RootlessStartDrawing(maskWin); if (dstWin && IsFramedWindow(dstWin)) RootlessStartDrawing(dstWin); ps->Composite(op, pSrc, pMask, pDst, xSrc, ySrc, xMask, yMask, xDst, yDst, width, height); if (dstWin && IsFramedWindow(dstWin)) { RootlessDamageRect(dstWin, xDst, yDst, width, height); } ps->Composite = RootlessComposite; // SCREEN_WRAP(ps, Composite); } static void RootlessGlyphs(CARD8 op, PicturePtr pSrc, PicturePtr pDst, PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int nlist, GlyphListPtr list, GlyphPtr *glyphs) { ScreenPtr pScreen = pDst->pDrawable->pScreen; PictureScreenPtr ps = GetPictureScreen(pScreen); int x, y; int n; GlyphPtr glyph; WindowPtr srcWin, dstWin; srcWin = (pSrc->pDrawable && pSrc->pDrawable->type == DRAWABLE_WINDOW) ? (WindowPtr)pSrc->pDrawable : NULL; dstWin = (pDst->pDrawable->type == DRAWABLE_WINDOW) ? (WindowPtr)pDst->pDrawable : NULL; if (srcWin && IsFramedWindow(srcWin)) RootlessStartDrawing(srcWin); if (dstWin && IsFramedWindow(dstWin)) RootlessStartDrawing(dstWin); //SCREEN_UNWRAP(ps, Glyphs); ps->Glyphs = SCREENREC(pScreen)->Glyphs; ps->Glyphs(op, pSrc, pDst, maskFormat, xSrc, ySrc, nlist, list, glyphs); ps->Glyphs = RootlessGlyphs; //SCREEN_WRAP(ps, Glyphs); if (dstWin && IsFramedWindow(dstWin)) { x = xSrc; y = ySrc; while (nlist--) { x += list->xOff; y += list->yOff; n = list->len; /* Calling DamageRect for the bounding box of each glyph is inefficient. So compute the union of all glyphs in a list and damage that. */ if (n > 0) { BoxRec box; glyph = *glyphs++; box.x1 = x - glyph->info.x; box.y1 = y - glyph->info.y; box.x2 = box.x1 + glyph->info.width; box.y2 = box.y2 + glyph->info.height; x += glyph->info.xOff; y += glyph->info.yOff; while (--n > 0) { short x1, y1, x2, y2; glyph = *glyphs++; x1 = x - glyph->info.x; y1 = y - glyph->info.y; x2 = x1 + glyph->info.width; y2 = y1 + glyph->info.height; box.x1 = max (box.x1, x1); box.y1 = max (box.y1, y1); box.x2 = max (box.x2, x2); box.y2 = max (box.y2, y2); x += glyph->info.xOff; y += glyph->info.yOff; } RootlessDamageBox(dstWin, &box); } list++; } } } #endif // RENDER /* * RootlessValidateTree * ValidateTree is modified in two ways: * - top-level windows don't clip each other * - windows aren't clipped against root. * These only matter when validating from the root. */ static int RootlessValidateTree(WindowPtr pParent, WindowPtr pChild, VTKind kind) { int result; RegionRec saveRoot; ScreenPtr pScreen = pParent->drawable.pScreen; SCREEN_UNWRAP(pScreen, ValidateTree); RL_DEBUG_MSG("VALIDATETREE start "); // Use our custom version to validate from root if (IsRoot(pParent)) { RL_DEBUG_MSG("custom "); result = RootlessMiValidateTree(pParent, pChild, kind); } else { HUGE_ROOT(pParent); result = pScreen->ValidateTree(pParent, pChild, kind); NORMAL_ROOT(pParent); } SCREEN_WRAP(pScreen, ValidateTree); RL_DEBUG_MSG("VALIDATETREE end\n"); return result; } /* * RootlessMarkOverlappedWindows * MarkOverlappedWindows is modified to ignore overlapping * top-level windows. */ static Bool RootlessMarkOverlappedWindows(WindowPtr pWin, WindowPtr pFirst, WindowPtr *ppLayerWin) { RegionRec saveRoot; Bool result; ScreenPtr pScreen = pWin->drawable.pScreen; SCREEN_UNWRAP(pScreen, MarkOverlappedWindows); RL_DEBUG_MSG("MARKOVERLAPPEDWINDOWS start "); HUGE_ROOT(pWin); if (IsRoot(pWin)) { // root - mark nothing RL_DEBUG_MSG("is root not marking "); result = FALSE; } else if (! IsTopLevel(pWin)) { // not top-level window - mark normally result = pScreen->MarkOverlappedWindows(pWin, pFirst, ppLayerWin); } else { //top-level window - mark children ONLY - NO overlaps with sibs (?) // This code copied from miMarkOverlappedWindows() register WindowPtr pChild; Bool anyMarked = FALSE; void (* MarkWindow)() = pScreen->MarkWindow; RL_DEBUG_MSG("is top level! "); /* single layered systems are easy */ if (ppLayerWin) *ppLayerWin = pWin; if (pWin == pFirst) { /* Blindly mark pWin and all of its inferiors. This is a slight * overkill if there are mapped windows that outside pWin's border, * but it's better than wasting time on RectIn checks. */ pChild = pWin; while (1) { if (pChild->viewable) { if (REGION_BROKEN (pScreen, &pChild->winSize)) SetWinSize (pChild); if (REGION_BROKEN (pScreen, &pChild->borderSize)) SetBorderSize (pChild); (* MarkWindow)(pChild); if (pChild->firstChild) { pChild = pChild->firstChild; continue; } } while (!pChild->nextSib && (pChild != pWin)) pChild = pChild->parent; if (pChild == pWin) break; pChild = pChild->nextSib; } anyMarked = TRUE; pFirst = pFirst->nextSib; } if (anyMarked) (* MarkWindow)(pWin->parent); result = anyMarked; } NORMAL_ROOT(pWin); SCREEN_WRAP(pScreen, MarkOverlappedWindows); RL_DEBUG_MSG("MARKOVERLAPPEDWINDOWS end\n"); return result; } static void expose_1 (WindowPtr pWin) { WindowPtr pChild; if (!pWin->realized) return; miPaintWindow(pWin, &pWin->borderClip, PW_BACKGROUND); /* FIXME: comments in windowstr.h indicate that borderClip doesn't include subwindow visibility. But I'm not so sure.. so we may be exposing too much.. */ miSendExposures (pWin, &pWin->borderClip, pWin->drawable.x, pWin->drawable.y); for (pChild = pWin->firstChild; pChild != NULL; pChild = pChild->nextSib) expose_1 (pChild); } void RootlessScreenExpose (ScreenPtr pScreen) { expose_1 (WindowTable[pScreen->myNum]); } ColormapPtr RootlessGetColormap (ScreenPtr pScreen) { RootlessScreenRec *s = SCREENREC (pScreen); return s->colormap; } static void RootlessInstallColormap (ColormapPtr pMap) { ScreenPtr pScreen = pMap->pScreen; RootlessScreenRec *s = SCREENREC (pScreen); SCREEN_UNWRAP(pScreen, InstallColormap); if (s->colormap != pMap) { s->colormap = pMap; s->colormap_changed = TRUE; RootlessQueueRedisplay (pScreen); } pScreen->InstallColormap (pMap); SCREEN_WRAP (pScreen, InstallColormap); } static void RootlessUninstallColormap (ColormapPtr pMap) { ScreenPtr pScreen = pMap->pScreen; RootlessScreenRec *s = SCREENREC (pScreen); SCREEN_UNWRAP(pScreen, UninstallColormap); if (s->colormap == pMap) s->colormap = NULL; pScreen->UninstallColormap (pMap); SCREEN_WRAP(pScreen, UninstallColormap); } static void RootlessStoreColors (ColormapPtr pMap, int ndef, xColorItem *pdef) { ScreenPtr pScreen = pMap->pScreen; RootlessScreenRec *s = SCREENREC (pScreen); SCREEN_UNWRAP(pScreen, StoreColors); if (s->colormap == pMap && ndef > 0) { s->colormap_changed = TRUE; RootlessQueueRedisplay (pScreen); } pScreen->StoreColors (pMap, ndef, pdef); SCREEN_WRAP(pScreen, StoreColors); } static CARD32 RootlessRedisplayCallback(OsTimerPtr timer, CARD32 time, void *arg) { RootlessScreenRec *screenRec = arg; if (!screenRec->redisplay_queued) { /* No update needed. Stop the timer. */ screenRec->redisplay_timer_set = FALSE; return 0; } screenRec->redisplay_queued = FALSE; /* Mark that we should redisplay before waiting for I/O next time */ screenRec->redisplay_expired = TRUE; /* Reinstall the timer immediately, so we get as close to our redisplay interval as possible. */ return ROOTLESS_REDISPLAY_DELAY; } /* * RootlessQueueRedisplay * Queue a redisplay after a timer delay to ensure we do not redisplay * too frequently. */ void RootlessQueueRedisplay(ScreenPtr pScreen) { RootlessScreenRec *screenRec = SCREENREC(pScreen); screenRec->redisplay_queued = TRUE; if (screenRec->redisplay_timer_set) return; screenRec->redisplay_timer = TimerSet(screenRec->redisplay_timer, 0, ROOTLESS_REDISPLAY_DELAY, RootlessRedisplayCallback, screenRec); screenRec->redisplay_timer_set = TRUE; } /* * RootlessBlockHandler * If the redisplay timer has expired, flush drawing before blocking * on select(). */ static void RootlessBlockHandler(pointer pbdata, OSTimePtr pTimeout, pointer pReadmask) { ScreenPtr pScreen = pbdata; RootlessScreenRec *screenRec = SCREENREC(pScreen); if (screenRec->redisplay_expired) { screenRec->redisplay_expired = FALSE; RootlessRedisplayScreen(pScreen); } } static void RootlessWakeupHandler(pointer data, int i, pointer LastSelectMask) { // nothing here } static Bool RootlessAllocatePrivates(ScreenPtr pScreen) { RootlessScreenRec *s; // no allocation needed for screen privates if (!dixRequestPrivate(rootlessGCPrivateKey, sizeof(RootlessGCRec))) return FALSE; s = xalloc(sizeof(RootlessScreenRec)); if (! s) return FALSE; SETSCREENREC(pScreen, s); s->pixmap_data = NULL; s->pixmap_data_size = 0; s->redisplay_timer = NULL; s->redisplay_timer_set = FALSE; return TRUE; } static void RootlessWrap(ScreenPtr pScreen) { RootlessScreenRec *s = SCREENREC(pScreen); #define WRAP(a) \ if (pScreen->a) { \ s->a = pScreen->a; \ } else { \ RL_DEBUG_MSG("null screen fn " #a "\n"); \ s->a = NULL; \ } \ pScreen->a = Rootless##a WRAP(CreateScreenResources); WRAP(CloseScreen); WRAP(CreateGC); WRAP(CopyWindow); WRAP(GetImage); WRAP(SourceValidate); WRAP(CreateWindow); WRAP(DestroyWindow); WRAP(RealizeWindow); WRAP(UnrealizeWindow); WRAP(MoveWindow); WRAP(PositionWindow); WRAP(ResizeWindow); WRAP(RestackWindow); WRAP(ReparentWindow); WRAP(ChangeBorderWidth); WRAP(MarkOverlappedWindows); WRAP(ValidateTree); WRAP(ChangeWindowAttributes); WRAP(InstallColormap); WRAP(UninstallColormap); WRAP(StoreColors); WRAP(SetShape); #ifdef RENDER { // Composite and Glyphs don't use normal screen wrapping PictureScreenPtr ps = GetPictureScreen(pScreen); s->Composite = ps->Composite; ps->Composite = RootlessComposite; s->Glyphs = ps->Glyphs; ps->Glyphs = RootlessGlyphs; } #endif // WRAP(ClearToBackground); fixme put this back? useful for shaped wins? #undef WRAP } /* * RootlessInit * Called by the rootless implementation to initialize the rootless layer. * Rootless wraps lots of stuff and needs a bunch of devPrivates. */ Bool RootlessInit(ScreenPtr pScreen, RootlessFrameProcsPtr procs) { RootlessScreenRec *s; if (!RootlessAllocatePrivates(pScreen)) return FALSE; s = SCREENREC(pScreen); s->imp = procs; s->colormap = NULL; s->redisplay_expired = FALSE; RootlessWrap(pScreen); if (!RegisterBlockAndWakeupHandlers(RootlessBlockHandler, RootlessWakeupHandler, (pointer) pScreen)) { return FALSE; } return TRUE; } void RootlessUpdateRooted (Bool state) { int i; if (!state) { for (i = 0; i < screenInfo.numScreens; i++) RootlessDisableRoot (screenInfo.screens[i]); } else { for (i = 0; i < screenInfo.numScreens; i++) RootlessEnableRoot (screenInfo.screens[i]); } }