diff --git a/glx/glxext.c b/glx/glxext.c index b7d6d1196..2fd6fb8c8 100644 --- a/glx/glxext.c +++ b/glx/glxext.c @@ -281,24 +281,6 @@ GlxPushProvider(__GLXprovider * provider) __glXProviderStack = provider; } -static Bool -checkScreenVisuals(void) -{ - int i, j; - - for (i = 0; i < screenInfo.numScreens; i++) { - ScreenPtr screen = screenInfo.screens[i]; - for (j = 0; j < screen->numVisuals; j++) { - if ((screen->visuals[j].class == TrueColor || - screen->visuals[j].class == DirectColor) && - screen->visuals[j].nplanes > 12) - return TRUE; - } - } - - return FALSE; -} - static void GetGLXDrawableBytes(void *value, XID id, ResourceSizePtr size) { @@ -472,10 +454,6 @@ static Bool xorgGlxServerPreInit(const ExtensionEntry *extEntry) { if (glxGeneration != serverGeneration) { - /* Mesa requires at least one True/DirectColor visual */ - if (!checkScreenVisuals()) - return FALSE; - __glXContextRes = CreateNewResourceType((DeleteType) ContextGone, "GLXContext"); __glXDrawableRes = CreateNewResourceType((DeleteType) DrawableGone, diff --git a/hw/xquartz/xpr/xprFrame.c b/hw/xquartz/xpr/xprFrame.c index 57d16d42f..cff001137 100644 --- a/hw/xquartz/xpr/xprFrame.c +++ b/hw/xquartz/xpr/xprFrame.c @@ -106,11 +106,18 @@ xprCopyWindow(RootlessFrameID wid, int dstNrects, const BoxRec *dstRects, int dx, int dy); -static inline xp_error +static inline void xprConfigureWindow(xp_window_id id, unsigned int mask, const xp_window_changes *values) { - return xp_configure_window(id, mask, values); + xp_error err = xp_configure_window(id, mask, values); + + /* Log rather than drop a reconfigure silently: the implementation rejects every mask on a frame that is locked for + * drawing, which would otherwise leave a window on screen that the X server believes it has already moved, resized + * or unmapped. + */ + if (err != Success) + ErrorF("Could not configure window %d, mask 0x%x (%d).\n", (int)id, mask, (int)err); } static void @@ -197,9 +204,9 @@ xprCreateFrame(RootlessWindowPtr pFrame, ScreenPtr pScreen, return FALSE; } - dispatch_async(window_hash_serial_q, ^ { - x_hash_table_insert(window_hash, pFrame->wid, pFrame); - }); + dispatch_sync(window_hash_serial_q, ^ { + x_hash_table_insert(window_hash, pFrame->wid, pFrame); + }); xprSetNativeProperty(pFrame); @@ -214,15 +221,15 @@ xprDestroyFrame(RootlessFrameID wid) { xp_error err; - dispatch_async(window_hash_serial_q, ^ { - x_hash_table_remove(window_hash, wid); - }); + dispatch_sync(window_hash_serial_q, ^ { + x_hash_table_remove(window_hash, wid); + }); err = xp_destroy_window(x_cvt_vptr_to_uint(wid)); + /* Not a FatalError: leaking the window costs us far less than terminating every client connected to the server. + */ if (err != Success) - FatalError("Could not destroy window %d (%d).", - (int)x_cvt_vptr_to_uint( - wid), (int)err); + ErrorF("Could not destroy window %d (%d).\n", (int)x_cvt_vptr_to_uint(wid), (int)err); } /* @@ -284,6 +291,9 @@ xprRestackFrame(RootlessFrameID wid, RootlessFrameID nextWid) wc.sibling = x_cvt_vptr_to_uint(nextWid); } + /* Unlike xprGetXWindow(), reading the record after the critical section is safe here because this only ever runs on + * the server thread, which is also the only thread that frees it. + */ dispatch_sync(window_hash_serial_q, ^ { winRec = x_hash_table_lookup(window_hash, wid, NULL); }); @@ -355,6 +365,10 @@ xprStartDrawing(RootlessFrameID wid, char **pixelData, int *bytesPerRow) err = xp_lock_window(x_cvt_vptr_to_uint( wid), NULL, NULL, data, rowbytes, NULL); + /* Deliberately still fatal, unlike the failures either side of this: the implementation leaves data[] untouched on + * failure and RootlessStartDrawing() cannot refuse, so carrying on would hand fb an uninitialized pointer to draw + * through. Demoting this needs a way to report the failure upwards first. + */ if (err != Success) FatalError("Could not lock window %d for drawing (%d).", (int)x_cvt_vptr_to_uint( @@ -386,7 +400,7 @@ xprStopDrawing(RootlessFrameID wid, Bool flush) * FatalError after http://xquartz.macosforge.org/trac/ticket/482 is fixed. */ if (err != Success) - ErrorF("Could not unlock window %d after drawing (%d).", + ErrorF("Could not unlock window %d after drawing (%d).\n", (int)x_cvt_vptr_to_uint( wid), (int)err); } @@ -476,10 +490,14 @@ xprInit(ScreenPtr pScreen) rootless_CopyBytes_threshold = xp_copy_bytes_threshold; rootless_CopyWindow_threshold = xp_scroll_area_threshold; - assert((window_hash = x_hash_table_new(NULL, NULL, NULL, NULL))); - assert((window_hash_serial_q = - dispatch_queue_create(BUNDLE_ID_PREFIX ".X11.xpr_window_hash", - NULL))); + window_hash = x_hash_table_new(NULL, NULL, NULL, NULL); + if (window_hash == NULL) + FatalError("Could not allocate window hash."); + + window_hash_serial_q = + dispatch_queue_create(BUNDLE_ID_PREFIX ".X11.xpr_window_hash", NULL); + if (window_hash_serial_q == NULL) + FatalError("Could not create window hash queue."); return TRUE; } @@ -491,14 +509,21 @@ xprInit(ScreenPtr pScreen) WindowPtr xprGetXWindow(xp_window_id wid) { - RootlessWindowRec *winRec __block; + WindowPtr pWin __block = NULL; + + /* Load the window inside the critical section. Letting the frame record escape it would defeat the point, since + * the server thread frees the record as soon as the frame is destroyed. + */ dispatch_sync(window_hash_serial_q, ^ { - winRec = + RootlessWindowRec *winRec = x_hash_table_lookup(window_hash, x_cvt_uint_to_vptr(wid), NULL); + + if (winRec != NULL) + pWin = winRec->win; }); - return winRec != NULL ? winRec->win : NULL; + return pWin; } /* @@ -566,14 +591,16 @@ xprHideWindows(Bool hide) Bool no_configure_window; -static inline int +/* As xprConfigureWindow(), but honoring no_configure_window so that responding to a native window change does not echo + * straight back to the implementation. Only the colormap and hide paths below go through this; the move, resize, + * restack, reshape and unmap hooks call xprConfigureWindow() directly and so are not suppressed. + */ +static inline void configure_window(xp_window_id id, unsigned int mask, const xp_window_changes *values) { if (!no_configure_window) - return xp_configure_window(id, mask, values); - else - return XP_Success; + xprConfigureWindow(id, mask, values); } static diff --git a/miext/rootless/README.txt b/miext/rootless/README.txt index f5ebc9080..b2f935d9d 100644 --- a/miext/rootless/README.txt +++ b/miext/rootless/README.txt @@ -173,7 +173,8 @@ typedef Bool (*RootlessCreateFrameProc) /* * Destroy a frame. - * Drawing is stopped and all updates are flushed before this is called. + * Drawing is stopped before this is called. + * The frame record stays valid for the duration of the call and is freed immediately afterwards. * * wid Frame id */ @@ -182,7 +183,7 @@ typedef void (*RootlessDestroyFrameProc) /* * Move a frame on screen. - * Drawing is stopped and all updates are flushed before this is called. + * Drawing is stopped before this is called. * * wid Frame id * pScreen Screen to move the new frame to @@ -232,6 +233,7 @@ typedef void (*RootlessReshapeFrameProc) /* * Unmap a frame. + * Drawing is stopped before this is called. * * wid Frame id */ diff --git a/miext/rootless/rootless.h b/miext/rootless/rootless.h index a843e077b..3538c8d9f 100644 --- a/miext/rootless/rootless.h +++ b/miext/rootless/rootless.h @@ -114,7 +114,8 @@ typedef Bool (*RootlessCreateFrameProc) /* * Destroy a frame. - * Drawing is stopped and all updates are flushed before this is called. + * Drawing is stopped before this is called. + * The frame record stays valid for the duration of the call and is freed immediately afterwards. * * wid Frame id */ @@ -123,7 +124,7 @@ typedef void (*RootlessDestroyFrameProc) /* * Move a frame on screen. - * Drawing is stopped and all updates are flushed before this is called. + * Drawing is stopped before this is called. * * wid Frame id * pScreen Screen to move the new frame to @@ -172,6 +173,7 @@ typedef void (*RootlessReshapeFrameProc) /* * Unmap a frame. + * Drawing is stopped before this is called. * * wid Frame id */ diff --git a/miext/rootless/rootlessCommon.c b/miext/rootless/rootlessCommon.c index e80655e68..7c4186416 100644 --- a/miext/rootless/rootlessCommon.c +++ b/miext/rootless/rootlessCommon.c @@ -96,6 +96,79 @@ IsFramedWindow(WindowPtr pWin) return (top && WINREC(top)); } +/* + * RootlessWindowIsScreenBacked + * Returns TRUE if pWin draws into the screen pixmap. Framed and redirected + * windows have buffers of their own; anything else still points at the screen + * pixmap fbCreateWindow installed. Note that comparing pixmaps would not + * answer this: framed windows hold the screen pixmap while not mid-draw. + */ +Bool +RootlessWindowIsScreenBacked(WindowPtr pWin) +{ + WindowPtr top; + +#ifdef COMPOSITE + if (pWin->redirectDraw != RedirectDrawNone) + return FALSE; +#endif + + if (!dixPrivateKeyRegistered(&rootlessWindowPrivateKeyRec)) + return FALSE; + + /* Not IsFramedWindow(): an unrealized window still has its frame. */ + top = TopLevelParent(pWin); + + return (top == NULL || WINREC(top) == NULL); +} + +/* + * RootlessGetScreenPixmapBox + * Store the screen pixmap's extent in pBox, in the coordinates fb resolves + * against. Returns FALSE if there is no screen pixmap, or it is empty. + */ +Bool +RootlessGetScreenPixmapBox(ScreenPtr pScreen, BoxPtr pBox) +{ + PixmapPtr pPix = (*pScreen->GetScreenPixmap) (pScreen); + + if (pPix == NULL) + return FALSE; + + pBox->x1 = pPix->screen_x; + pBox->y1 = pPix->screen_y; + pBox->x2 = pPix->screen_x + pPix->drawable.width; + pBox->y2 = pPix->screen_y + pPix->drawable.height; + + return (pBox->x2 > pBox->x1 && pBox->y2 > pBox->y1); +} + +/* + * RootlessBoundRegionToScreenPixmap + * Restrict pRegion to what a window drawing into the screen pixmap may reach. + * That pixmap is one scanline, and fb bounds its addresses by the clip alone, + * so an unbounded clip indexes outside the allocation in either direction. + */ +void +RootlessBoundRegionToScreenPixmap(WindowPtr pWin, RegionPtr pRegion) +{ + RegionRec bound; + BoxRec box; + + if (!RootlessWindowIsScreenBacked(pWin)) + return; + + /* Nothing to bound against, so draw nothing. */ + if (!RootlessGetScreenPixmapBox(pWin->drawable.pScreen, &box)) { + RegionEmpty(pRegion); + return; + } + + RegionInit(&bound, &box, 1); + RegionIntersect(pRegion, pRegion, &bound); + RegionUninit(&bound); +} + Bool RootlessResolveColormap(ScreenPtr pScreen, int first_color, int n_colors, uint32_t * colors) @@ -232,11 +305,6 @@ RootlessStartDrawing(WindowPtr pWindow) } } -/* - * RootlessStopDrawing - * Stop drawing to a window's backing buffer. If flush is true, - * damaged regions are flushed to the screen. - */ static int RestorePreDrawingPixmapVisitor(WindowPtr pWindow, void *data) { @@ -268,24 +336,28 @@ RestorePreDrawingPixmapVisitor(WindowPtr pWindow, void *data) return WT_WALKCHILDREN; } +/* + * RootlessStopDrawingFrame + * Stop drawing to the given frame's backing buffer. If flush is true, damaged regions are flushed to the screen. + * winRec must not be NULL. + * + * Unlike RootlessStopDrawing(), the frame is named directly instead of being found by walking up from a window. + * Callers need that once a framed window has been reparented below another framed window, because TopLevelParent() + * then resolves either to a different frame or to no frame at all rather than to the one this record describes. + * + * This deliberately does not process is_reorder_pending; see RootlessStopDrawing(). + */ void -RootlessStopDrawing(WindowPtr pWindow, Bool flush) +RootlessStopDrawingFrame(RootlessWindowPtr winRec, Bool flush) { - ScreenPtr pScreen = pWindow->drawable.pScreen; - WindowPtr top = TopLevelParent(pWindow); - RootlessWindowRec *winRec; - - if (top == NULL) - return; - winRec = WINREC(top); - if (winRec == NULL) - return; + WindowPtr pWin = winRec->win; + ScreenPtr pScreen = pWin->drawable.pScreen; if (winRec->is_drawing) { SCREENREC(pScreen)->imp->StopDrawing(winRec->wid, flush); FreeScratchPixmapHeader(winRec->pixmap); - TraverseTree(top, RestorePreDrawingPixmapVisitor, (void *) winRec); + TraverseTree(pWin, RestorePreDrawingPixmapVisitor, (void *) winRec); winRec->pixmap = NULL; winRec->is_drawing = FALSE; @@ -293,7 +365,31 @@ RootlessStopDrawing(WindowPtr pWindow, Bool flush) else if (flush) { SCREENREC(pScreen)->imp->UpdateRegion(winRec->wid, NULL); } +} + +/* + * RootlessStopDrawing + * Stop drawing to a window's backing buffer. If flush is true, + * damaged regions are flushed to the screen. + */ +void +RootlessStopDrawing(WindowPtr pWindow, Bool flush) +{ + WindowPtr top = TopLevelParent(pWindow); + RootlessWindowRec *winRec; + + if (top == NULL) + return; + winRec = WINREC(top); + if (winRec == NULL) + return; + + RootlessStopDrawingFrame(winRec, flush); + /* Reorder the window the caller named rather than the frame's owner. Clearing the flag consumes it either way, + * but RootlessReorderWindow() itself does nothing unless pWindow is the window that owns the frame. This is + * long-standing behavior; every caller that passes flush reorders a frame-owning window. + */ if (flush && winRec->is_reorder_pending) { winRec->is_reorder_pending = FALSE; RootlessReorderWindow(pWindow); diff --git a/miext/rootless/rootlessCommon.h b/miext/rootless/rootlessCommon.h index 4b9010840..ce016fc50 100644 --- a/miext/rootless/rootlessCommon.h +++ b/miext/rootless/rootlessCommon.h @@ -238,6 +238,15 @@ extern RegionRec rootlessHugeRoot; // (e.g. it is visible and has a top-level or root parent) Bool IsFramedWindow(WindowPtr pWin); +// TRUE if this window draws into the screen pixmap rather than into a frame. +Bool RootlessWindowIsScreenBacked(WindowPtr pWin); + +// Store the screen pixmap's extent. FALSE if there is none, or it is empty. +Bool RootlessGetScreenPixmapBox(ScreenPtr pScreen, BoxPtr pBox); + +// Restrict a region to what a screen-pixmap-backed window may reach. +void RootlessBoundRegionToScreenPixmap(WindowPtr pWin, RegionPtr pRegion); + // Routines that cause regions to get redrawn. // DamageRegion and DamageRect are in global coordinates. // DamageBox is in window-local coordinates. @@ -247,6 +256,10 @@ void RootlessDamageBox(WindowPtr pWindow, BoxPtr pBox); void RootlessRedisplay(WindowPtr pWindow); void RootlessRedisplayScreen(ScreenPtr pScreen); +// As RootlessRedisplay/RootlessStopDrawing, but for a frame named directly +// rather than one found by walking up from a window. winRec must not be NULL. +void RootlessStopDrawingFrame(RootlessWindowPtr winRec, Bool flush); + void RootlessQueueRedisplay(ScreenPtr pScreen); /* Return the colormap currently installed on the given screen. */ diff --git a/miext/rootless/rootlessGC.c b/miext/rootless/rootlessGC.c index 5af18a4a0..40ce95741 100644 --- a/miext/rootless/rootlessGC.c +++ b/miext/rootless/rootlessGC.c @@ -315,6 +315,94 @@ RootlessCreateGC(GCPtr pGC) (pGC)->ops = &rootlessGCOps; \ } +/* + * Empty clip for the paths that must draw nothing and cannot allocate. It is + * installed with freeCompClip FALSE, so mi replaces it rather than writing + * through it or freeing it. + */ +static RegionRec rootlessEmptyClip = { {0, 0, 0, 0}, &RegionEmptyData }; + +/* + * RootlessInstallCompositeClip + * Give pGC a composite clip that it owns. The old one must never be modified + * in place: with no client clip it is the window's own clipList. + */ +static void +RootlessInstallCompositeClip(GCPtr pGC, RegionPtr pNewClip) +{ + if (pGC->freeCompClip) + RegionDestroy(pGC->pCompositeClip); + + pGC->pCompositeClip = pNewClip; + pGC->freeCompClip = TRUE; +} + +/* + * RootlessCloseCompositeClip + * Reduce pGC's composite clip to nothing, without allocating. + */ +static void +RootlessCloseCompositeClip(GCPtr pGC) +{ + if (pGC->freeCompClip) + RegionDestroy(pGC->pCompositeClip); + + pGC->pCompositeClip = &rootlessEmptyClip; + pGC->freeCompClip = FALSE; +} + +/* + * RootlessClampCompositeClip + * Bound the composite clip of a window drawing into the screen pixmap. + * + * RootlessMiValidateTree bounds these clips as it recomputes them, but a + * screen resize republishes the pixmap without recomputing any clip but the + * root's. After a shrink, a window nothing else revalidated is left bounded + * to the larger screen, so clamp here as well. + */ +static void +RootlessClampCompositeClip(GCPtr pGC, WindowPtr pWin) +{ + RegionPtr pClip = pGC->pCompositeClip; + RegionPtr pNewClip; + BoxPtr pExtents; + BoxRec box; + + if (pClip == NULL || !RootlessWindowIsScreenBacked(pWin)) + return; + + /* A nil region's extents say nothing about what it covers. */ + if (RegionNil(pClip)) + return; + + if (!RootlessGetScreenPixmapBox(pWin->drawable.pScreen, &box)) { + /* Nothing to bound against, so draw nothing. */ + RootlessCloseCompositeClip(pGC); + return; + } + + pExtents = RegionExtents(pClip); + if (pExtents->x1 >= box.x1 && pExtents->y1 >= box.y1 && + pExtents->x2 <= box.x2 && pExtents->y2 <= box.y2) + return; + + RL_DEBUG_MSG("clip of window %p (%d,%d %d,%d) escapes the screen pixmap " + "(%d,%d %d,%d); clamping\n", pWin, + pExtents->x1, pExtents->y1, pExtents->x2, pExtents->y2, + box.x1, box.y1, box.x2, box.y2); + + /* RegionCreate returns a shared static on failure, not NULL. */ + pNewClip = RegionCreate(&box, 1); + if (RegionNar(pNewClip)) { + RegionDestroy(pNewClip); + RootlessCloseCompositeClip(pGC); + return; + } + + RegionIntersect(pNewClip, pNewClip, pClip); + RootlessInstallCompositeClip(pGC, pNewClip); +} + static void RootlessValidateGC(GCPtr pGC, unsigned long changes, DrawablePtr pDrawable) { @@ -335,6 +423,7 @@ RootlessValidateGC(GCPtr pGC, unsigned long changes, DrawablePtr pDrawable) #else VALIDATE_GC(pGC, changes, pDrawable); #endif + RootlessClampCompositeClip(pGC, (WindowPtr) pDrawable); } else { pGC->funcs->ValidateGC(pGC, changes, pDrawable); diff --git a/miext/rootless/rootlessScreen.c b/miext/rootless/rootlessScreen.c index 0cd1995c0..af911c2b3 100644 --- a/miext/rootless/rootlessScreen.c +++ b/miext/rootless/rootlessScreen.c @@ -348,8 +348,8 @@ RootlessGlyphs(CARD8 op, PicturePtr pSrc, PicturePtr pDst, //SCREEN_WRAP(ps, Glyphs); if (dstWin && IsFramedWindow(dstWin)) { - x = xSrc; - y = ySrc; + x = 0; + y = 0; while (nlist--) { x += list->xOff; @@ -392,6 +392,11 @@ RootlessGlyphs(CARD8 op, PicturePtr pSrc, PicturePtr pDst, y += glyph->info.yOff; } + /* RootlessDamageBox expects global (screen) coordinates */ + box.x1 += dstWin->drawable.x; + box.y1 += dstWin->drawable.y; + box.x2 += dstWin->drawable.x; + box.y2 += dstWin->drawable.y; RootlessDamageBox(dstWin, &box); } list++; diff --git a/miext/rootless/rootlessValTree.c b/miext/rootless/rootlessValTree.c index f2146a63b..fac3e137f 100644 --- a/miext/rootless/rootlessValTree.c +++ b/miext/rootless/rootlessValTree.c @@ -104,6 +104,8 @@ Equipment Corporation. #include "globals.h" +#include "rootlessCommon.h" + int RootlessMiValidateTree(WindowPtr pRoot, WindowPtr pChild, VTKind kind); #define HasParentRelativeBorder(w) (!(w)->borderIsPixel && \ @@ -146,6 +148,10 @@ RootlessComputeClips(WindowPtr pParent, ScreenPtr pScreen, * will be completely inside the universe (the universe will cover it * completely). If the window is completely obscured, none of the * universe will cover the rectangle. + * + * A window bounded to the screen pixmap is the exception: it reports itself + * obscured where it extends past the screen. That also stops the VTMove + * case below from translating an unbounded clip into the stored regions. */ borderSize.x1 = pParent->drawable.x - wBorderWidth(pParent); borderSize.y1 = pParent->drawable.y - wBorderWidth(pParent); @@ -501,6 +507,7 @@ RootlessMiValidateTree(WindowPtr pRoot, /* Parent to validate */ if (pWin->viewable) { if (pWin->valdata) { RegionCopy(&childClip, &pWin->borderSize); + RootlessBoundRegionToScreenPixmap(pWin, &childClip); RootlessComputeClips(pWin, pScreen, &childClip, kind, &exposed); } else if (pWin->visibility == VisibilityNotViewable) { diff --git a/miext/rootless/rootlessWindow.c b/miext/rootless/rootlessWindow.c index 3cda2bbf9..771f7f599 100644 --- a/miext/rootless/rootlessWindow.c +++ b/miext/rootless/rootlessWindow.c @@ -105,7 +105,12 @@ RootlessNativeWindowMoved(WindowPtr pWin) ClientPtr pClient; RootlessWindowRec *winRec; + if (pWin == NULL) + return; + winRec = WINREC(pWin); + if (winRec == NULL) + return; if (xp_get_window_bounds(MAKE_WINDOW_ID(winRec->wid), &bounds) != Success) return; @@ -182,6 +187,13 @@ RootlessCreateWindow(WindowPtr pWin) static void RootlessDestroyFrame(WindowPtr pWin, RootlessWindowPtr winRec) { + /* The implementation cannot destroy a frame that is still locked for drawing, and the rootless layer + * intentionally holds that lock across requests (see RootlessSourceValidate() and StartFrameResize()) and drops it + * lazily from the block handler. A frame reconfigured after its window was unrealized queues no damage to wake + * that handler, so it can still be locked here. + */ + RootlessStopDrawingFrame(winRec, FALSE); + SCREENREC(pWin->drawable.pScreen)->imp->DestroyFrame(winRec->wid); free(winRec); SETWINREC(pWin, NULL); @@ -449,8 +461,18 @@ RootlessRealizeWindow(WindowPtr pWin) RootlessWindowRec *winRec; winRec = RootlessEnsureFrame(pWin); - if (winRec == NULL) + if (winRec == NULL) { + /* + * RealizeTree marks the window viewable and ignores our return + * value, so it stays mapped with no buffer of its own. That is + * safe: it draws into the screen pixmap, whose clips are bounded. + * Clearing viewable here would only leave viewable children under + * an unviewable parent, since RealizeTree marks those too. + */ + ErrorF("rootless: no frame for window %p; it will not be drawn\n", + pWin); return FALSE; + } winRec->is_reorder_pending = TRUE; @@ -646,6 +668,27 @@ RootlessNoCopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc) RegionTranslate(prgnSrc, -dx, -dy); } +/* + * RootlessCanAccelerateCopy + * Returns TRUE if the implementation may be asked to copy this window's + * contents around. + * + * 8bit frames are excluded: libXplugin keeps those in a software backing + * store of its own and _xp_backing_scroll() computes the destination + * address of the copy with a 32bit (bytes_per_row * dy) multiply which it + * then zero-extends. Any copy moving content upwards is therefore done + * ~4GB past the backing store and takes the server down with SIGBUS. + * Nothing is lost by copying such windows ourselves, libXplugin would + * only memcpy them as well. + */ +static Bool +RootlessCanAccelerateCopy(WindowPtr pWin) +{ + WindowPtr top = TopLevelParent(pWin); + + return top != NULL && top->drawable.depth != 8; +} + /* * RootlessCopyWindow * Update *new* location of window. Old location is redrawn with @@ -679,6 +722,7 @@ RootlessCopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc) /* If the area exceeds threshold, use the implementation's accelerated version. */ if (area > rootless_CopyWindow_threshold && + RootlessCanAccelerateCopy(pWin) && SCREENREC(pScreen)->imp->CopyWindow) { RootlessWindowRec *winRec; WindowPtr top; @@ -717,6 +761,11 @@ RootlessCopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc) miCopyRegion(pDrawable, pDrawable, 0, &rgnDst, dx, dy, fbCopyWindowProc, 0, 0); + /* RootlessDamageRegion() wants global coordinates */ + if (pPixmap->screen_x || pPixmap->screen_y) { + RegionTranslate(&rgnDst, pPixmap->screen_x, pPixmap->screen_y); + } + RootlessDamageRegion(pWin, &rgnDst); } @@ -985,11 +1034,24 @@ RootlessResizeWindow(WindowPtr pWin, int x, int y, /* Special case for resizing the root window */ BoxRec box; + /* Release the frame before reconfiguring it, as everywhere else: the implementation rejects the resize below on + * a frame that is locked for drawing. Flush first, because the pending damage is relative to the geometry the + * resize is about to replace. + */ + if (winRec) + RootlessStopDrawingFrame(winRec, TRUE); + pWin->drawable.x = x; pWin->drawable.y = y; pWin->drawable.width = w; pWin->drawable.height = h; + /* Keep the frame record in step with the window. Nothing else updates it on this path, and callers such as + * RootlessRepositionWindow() and RootlessDamageRegion() position the frame from it. + */ + if (winRec) + RootlessInitializeFrame(pWin, winRec); + box.x1 = x; box.y1 = y; box.x2 = x + w; @@ -1076,11 +1138,21 @@ RootlessReparentWindow(WindowPtr pWin, WindowPtr pPriorParent) RootlessDestroyFrame(pWin, winRec); } else { + /* Flush while the frame's damage still matches winRec's geometry, because RootlessInitializeFrame() below is + * about to change it, and release the lock before UnmapFrame() and ResizeFrame(), which the implementation + * rejects on a locked frame. + */ + RootlessStopDrawingFrame(winRec, TRUE); + if (!pTopWin->realized && pWin->realized) { SCREENREC(pScreen)->imp->UnmapFrame(winRec->wid); } - /* Switch the frame record from one to the other. */ + /* Switch the frame record from one to the other. Nothing in here may resolve a frame through + * TopLevelParent(): until the second SETWINREC() the record is unreachable from any window, and from there + * until RootlessInitializeFrame() it is reachable from pTopWin while winRec->win still names pWin, so a lookup + * would find it and then walk the wrong subtree. + */ SETWINREC(pWin, NULL); SETWINREC(pTopWin, winRec); diff --git a/os/WaitFor.c b/os/WaitFor.c index b0747faee..f335b6667 100644 --- a/os/WaitFor.c +++ b/os/WaitFor.c @@ -192,11 +192,12 @@ WaitForSomething(Bool are_ready) /* deal with any blocked jobs */ ProcessWorkQueue(); - timeout = check_timers(); are_ready = clients_are_ready(); if (are_ready) timeout = 0; + else + timeout = check_timers(); BlockHandler(&timeout); if (NewOutputPending) diff --git a/randr/randr.c b/randr/randr.c index 2994bcd6c..cc85d27bd 100644 --- a/randr/randr.c +++ b/randr/randr.c @@ -414,9 +414,6 @@ RRExtensionInit(void) { ExtensionEntry *extEntry; - if (RRNScreens == 0) - return; - if (!dixRegisterPrivateKey(&RRClientPrivateKeyRec, PRIVATE_CLIENT, sizeof(RRClientRec) + screenInfo.numScreens * sizeof(RRTimesRec)))