Edit

IABSD.fr/xenocara/xserver/miext/rootless/rootlessScreen.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2026-05-08 11:12:08
    Hash : f0e0a48c
    Message : Update to xserver 21.1.22. The security fixes were already committed.

  • xserver/miext/rootless/rootlessScreen.c
  • /*
     * 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 <dix-config.h>
    #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 "mipict.h"
    #include "colormapst.h"
    
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <string.h>
    
    #include "rootlessCommon.h"
    #include "rootlessWindow.h"
    
    /*
     * Render operations use PictFormat to describe pixel layout.  Depth-24
     * windows use PICT_x8r8g8b8, where 'x' tells pixman the high byte is
     * padding it may freely zero.  The compositor needs this byte to be 0xFF
     * (opaque).  Temporarily upgrading the destination format from 'x' to 'a'
     * prevents pixman from optimizing away the alpha channel, paralleling how
     * ROOTLESS_PROTECT_ALPHA prevents fb from doing the same for GC ops.
     */
    
    #if ROOTLESS_PROTECT_ALPHA
    #define RL_RENDER_SAVE_FORMAT(pict)                              \
        CARD32 _saved_format = (pict)->format;                       \
        if ((pict)->pDrawable->type == DRAWABLE_WINDOW &&            \
            (pict)->format == PICT_x8r8g8b8)                         \
            (pict)->format = PICT_a8r8g8b8
    
    #define RL_RENDER_RESTORE_FORMAT(pict) \
        (pict)->format = _saved_format
    
    #else
    #define RL_RENDER_SAVE_FORMAT(pict)
    #define RL_RENDER_RESTORE_FORMAT(pict)
    #endif
    
    extern int RootlessMiValidateTree(WindowPtr pRoot, WindowPtr pChild,
                                      VTKind kind);
    extern Bool RootlessCreateGC(GCPtr pGC);
    
    // Initialize globals
    DevPrivateKeyRec rootlessGCPrivateKeyRec;
    DevPrivateKeyRec rootlessScreenPrivateKeyRec;
    DevPrivateKeyRec rootlessWindowPrivateKeyRec;
    DevPrivateKeyRec rootlessWindowOldPixmapPrivateKeyRec;
    
    /*
     * 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) {
            free(s->pixmap_data);
    
            s->pixmap_data_size = rowbytes;
            s->pixmap_data = malloc(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(ScreenPtr pScreen)
    {
        RootlessScreenRec *s;
    
        s = SCREENREC(pScreen);
    
        // fixme unwrap everything that was wrapped?
        pScreen->CloseScreen = s->CloseScreen;
    
        if (s->pixmap_data != NULL) {
            free(s->pixmap_data);
            s->pixmap_data = NULL;
            s->pixmap_data_size = 0;
        }
    
        free(s);
        return pScreen->CloseScreen(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,
                           unsigned int subWindowMode)
    {
        SCREEN_UNWRAP(pDrawable->pScreen, SourceValidate);
        if (pDrawable->type == DRAWABLE_WINDOW) {
            WindowPtr pWin = (WindowPtr) pDrawable;
    
            RootlessStartDrawing(pWin);
        }
        pDrawable->pScreen->SourceValidate(pDrawable, x, y, w, h,
                                           subWindowMode);
        SCREEN_WRAP(pDrawable->pScreen, SourceValidate);
    }
    
    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 &&
                       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);
    
        RL_RENDER_SAVE_FORMAT(pDst);
        ps->Composite(op, pSrc, pMask, pDst,
                      xSrc, ySrc, xMask, yMask, xDst, yDst, width, height);
        RL_RENDER_RESTORE_FORMAT(pDst);
    
        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;
        RL_RENDER_SAVE_FORMAT(pDst);
        ps->Glyphs(op, pSrc, pDst, maskFormat, xSrc, ySrc, nlist, list, glyphs);
        RL_RENDER_RESTORE_FORMAT(pDst);
        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.y1 + 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 = min(box.x1, x1);
                        box.y1 = min(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++;
            }
        }
    }
    
    static void
    RootlessTrapezoids(CARD8 op, PicturePtr pSrc, PicturePtr pDst,
                       PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc,
                       int ntrap, xTrapezoid *traps)
    {
        ScreenPtr pScreen = pDst->pDrawable->pScreen;
        PictureScreenPtr ps = GetPictureScreen(pScreen);
        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;
    
        ps->Trapezoids = SCREENREC(pScreen)->Trapezoids;
    
        if (srcWin && IsFramedWindow(srcWin))
            RootlessStartDrawing(srcWin);
        if (dstWin && IsFramedWindow(dstWin))
            RootlessStartDrawing(dstWin);
    
        RL_RENDER_SAVE_FORMAT(pDst);
        ps->Trapezoids(op, pSrc, pDst, maskFormat, xSrc, ySrc, ntrap, traps);
        RL_RENDER_RESTORE_FORMAT(pDst);
    
        if (dstWin && IsFramedWindow(dstWin) && ntrap > 0) {
            BoxRec box;
    
            miTrapezoidBounds(ntrap, traps, &box);
    
            if (box.x1 < box.x2 && box.y1 < box.y2) {
                box.x1 += dstWin->drawable.x;
                box.y1 += dstWin->drawable.y;
                box.x2 += dstWin->drawable.x;
                box.y2 += dstWin->drawable.y;
                RootlessDamageBox(dstWin, &box);
            }
        }
    
        ps->Trapezoids = RootlessTrapezoids;
    }
    
    static void
    RootlessTriangles(CARD8 op, PicturePtr pSrc, PicturePtr pDst,
                      PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc,
                      int ntri, xTriangle *tris)
    {
        ScreenPtr pScreen = pDst->pDrawable->pScreen;
        PictureScreenPtr ps = GetPictureScreen(pScreen);
        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;
    
        ps->Triangles = SCREENREC(pScreen)->Triangles;
    
        if (srcWin && IsFramedWindow(srcWin))
            RootlessStartDrawing(srcWin);
        if (dstWin && IsFramedWindow(dstWin))
            RootlessStartDrawing(dstWin);
    
        RL_RENDER_SAVE_FORMAT(pDst);
        ps->Triangles(op, pSrc, pDst, maskFormat, xSrc, ySrc, ntri, tris);
        RL_RENDER_RESTORE_FORMAT(pDst);
    
        if (dstWin && IsFramedWindow(dstWin) && ntri > 0) {
            BoxRec box;
    
            miTriangleBounds(ntri, tris, &box);
    
            if (box.x1 < box.x2 && box.y1 < box.y2) {
                box.x1 += dstWin->drawable.x;
                box.y1 += dstWin->drawable.y;
                box.x2 += dstWin->drawable.x;
                box.y2 += dstWin->drawable.y;
                RootlessDamageBox(dstWin, &box);
            }
        }
    
        ps->Triangles = RootlessTriangles;
    }
    
    static void
    RootlessCompositeRects(CARD8 op, PicturePtr pDst, xRenderColor *color,
                           int nRect, xRectangle *rects)
    {
        ScreenPtr pScreen = pDst->pDrawable->pScreen;
        PictureScreenPtr ps = GetPictureScreen(pScreen);
        WindowPtr dstWin;
    
        dstWin = (pDst->pDrawable->type == DRAWABLE_WINDOW) ?
            (WindowPtr) pDst->pDrawable : NULL;
    
        ps->CompositeRects = SCREENREC(pScreen)->CompositeRects;
    
        if (dstWin && IsFramedWindow(dstWin))
            RootlessStartDrawing(dstWin);
    
        RL_RENDER_SAVE_FORMAT(pDst);
        ps->CompositeRects(op, pDst, color, nRect, rects);
        RL_RENDER_RESTORE_FORMAT(pDst);
    
        if (dstWin && IsFramedWindow(dstWin) && nRect > 0) {
            int i;
            BoxRec box;
    
            box.x1 = rects[0].x;
            box.y1 = rects[0].y;
            box.x2 = rects[0].x + rects[0].width;
            box.y2 = rects[0].y + rects[0].height;
    
            for (i = 1; i < nRect; i++) {
                short x1 = rects[i].x;
                short y1 = rects[i].y;
                short x2 = x1 + rects[i].width;
                short y2 = y1 + rects[i].height;
    
                if (x1 < box.x1) box.x1 = x1;
                if (y1 < box.y1) box.y1 = y1;
                if (x2 > box.x2) box.x2 = x2;
                if (y2 > box.y2) box.y2 = y2;
            }
    
            if (box.x1 < box.x2 && box.y1 < box.y2) {
                RootlessDamageRect(dstWin,
                                   box.x1, box.y1,
                                   box.x2 - box.x1, box.y2 - box.y1);
            }
        }
    
        ps->CompositeRects = RootlessCompositeRects;
    }
    
    /*
     * 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;
            MarkWindowProcPtr 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 (RegionBroken(&pChild->winSize))
                            SetWinSize(pChild);
                        if (RegionBroken(&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;
            }
            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;
    
        pWin->drawable.pScreen->PaintWindow(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(pScreen->root);
    }
    
    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(void *pbdata, void *ptimeout)
    {
        ScreenPtr pScreen = pbdata;
        RootlessScreenRec *screenRec = SCREENREC(pScreen);
    
        if (screenRec->redisplay_expired) {
            screenRec->redisplay_expired = FALSE;
    
            RootlessRedisplayScreen(pScreen);
        }
    }
    
    static void
    RootlessWakeupHandler(void *data, int result)
    {
        // nothing here
    }
    
    static Bool
    RootlessAllocatePrivates(ScreenPtr pScreen)
    {
        RootlessScreenRec *s;
    
        if (!dixRegisterPrivateKey
            (&rootlessGCPrivateKeyRec, PRIVATE_GC, sizeof(RootlessGCRec)))
            return FALSE;
        if (!dixRegisterPrivateKey(&rootlessScreenPrivateKeyRec, PRIVATE_SCREEN, 0))
            return FALSE;
        if (!dixRegisterPrivateKey(&rootlessWindowPrivateKeyRec, PRIVATE_WINDOW, 0))
            return FALSE;
        if (!dixRegisterPrivateKey
            (&rootlessWindowOldPixmapPrivateKeyRec, PRIVATE_WINDOW, 0))
            return FALSE;
    
        s = malloc(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(PaintWindow);
        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);
    
        {
            // PictureScreen procs don't use normal screen wrapping
            PictureScreenPtr ps = GetPictureScreen(pScreen);
    
            s->Composite = ps->Composite;
            ps->Composite = RootlessComposite;
            s->Glyphs = ps->Glyphs;
            ps->Glyphs = RootlessGlyphs;
            s->Trapezoids = ps->Trapezoids;
            ps->Trapezoids = RootlessTrapezoids;
            s->Triangles = ps->Triangles;
            ps->Triangles = RootlessTriangles;
            s->CompositeRects = ps->CompositeRects;
            ps->CompositeRects = RootlessCompositeRects;
        }
    
        // 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,
                                            (void *) 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]);
        }
    }