xserver

xserver with xephyr scale patch
git clone https://git.neptards.moe/u3shit/xserver.git
Log | Files | Refs | README | LICENSE

rootlessGC.c (42785B)


      1 /*
      2  * Graphics Context support for generic rootless X server
      3  */
      4 /*
      5  * Copyright (c) 2001 Greg Parker. All Rights Reserved.
      6  * Copyright (c) 2002-2003 Torrey T. Lyons. All Rights Reserved.
      7  * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
      8  *
      9  * Permission is hereby granted, free of charge, to any person obtaining a
     10  * copy of this software and associated documentation files (the "Software"),
     11  * to deal in the Software without restriction, including without limitation
     12  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     13  * and/or sell copies of the Software, and to permit persons to whom the
     14  * Software is furnished to do so, subject to the following conditions:
     15  *
     16  * The above copyright notice and this permission notice shall be included in
     17  * all copies or substantial portions of the Software.
     18  *
     19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     22  * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
     23  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     24  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     25  * DEALINGS IN THE SOFTWARE.
     26  *
     27  * Except as contained in this notice, the name(s) of the above copyright
     28  * holders shall not be used in advertising or otherwise to promote the sale,
     29  * use or other dealings in this Software without prior written authorization.
     30  */
     31 
     32 #ifdef HAVE_DIX_CONFIG_H
     33 #include <dix-config.h>
     34 #endif
     35 
     36 #include <stddef.h>             /* For NULL */
     37 #include "mi.h"
     38 #include "scrnintstr.h"
     39 #include "gcstruct.h"
     40 #include "pixmapstr.h"
     41 #include "windowstr.h"
     42 #include "dixfontstr.h"
     43 #include "mivalidate.h"
     44 #include "fb.h"
     45 
     46 #include <sys/types.h>
     47 #include <sys/stat.h>
     48 #include <fcntl.h>
     49 
     50 #include "rootlessCommon.h"
     51 
     52 // GC functions
     53 static void RootlessValidateGC(GCPtr pGC, unsigned long changes,
     54                                DrawablePtr pDrawable);
     55 static void RootlessChangeGC(GCPtr pGC, unsigned long mask);
     56 static void RootlessCopyGC(GCPtr pGCSrc, unsigned long mask, GCPtr pGCDst);
     57 static void RootlessDestroyGC(GCPtr pGC);
     58 static void RootlessChangeClip(GCPtr pGC, int type, void *pvalue, int nrects);
     59 static void RootlessDestroyClip(GCPtr pGC);
     60 static void RootlessCopyClip(GCPtr pgcDst, GCPtr pgcSrc);
     61 
     62 Bool RootlessCreateGC(GCPtr pGC);
     63 
     64 GCFuncs rootlessGCFuncs = {
     65     RootlessValidateGC,
     66     RootlessChangeGC,
     67     RootlessCopyGC,
     68     RootlessDestroyGC,
     69     RootlessChangeClip,
     70     RootlessDestroyClip,
     71     RootlessCopyClip,
     72 };
     73 
     74 // GC operations
     75 static void RootlessFillSpans(DrawablePtr dst, GCPtr pGC, int nInit,
     76                               DDXPointPtr pptInit, int *pwidthInit, int sorted);
     77 static void RootlessSetSpans(DrawablePtr dst, GCPtr pGC, char *pSrc,
     78                              DDXPointPtr pptInit, int *pwidthInit,
     79                              int nspans, int sorted);
     80 static void RootlessPutImage(DrawablePtr dst, GCPtr pGC,
     81                              int depth, int x, int y, int w, int h,
     82                              int leftPad, int format, char *pBits);
     83 static RegionPtr RootlessCopyArea(DrawablePtr pSrc, DrawablePtr dst, GCPtr pGC,
     84                                   int srcx, int srcy, int w, int h,
     85                                   int dstx, int dsty);
     86 static RegionPtr RootlessCopyPlane(DrawablePtr pSrc, DrawablePtr dst,
     87                                    GCPtr pGC, int srcx, int srcy,
     88                                    int w, int h, int dstx, int dsty,
     89                                    unsigned long plane);
     90 static void RootlessPolyPoint(DrawablePtr dst, GCPtr pGC,
     91                               int mode, int npt, DDXPointPtr pptInit);
     92 static void RootlessPolylines(DrawablePtr dst, GCPtr pGC,
     93                               int mode, int npt, DDXPointPtr pptInit);
     94 static void RootlessPolySegment(DrawablePtr dst, GCPtr pGC,
     95                                 int nseg, xSegment * pSeg);
     96 static void RootlessPolyRectangle(DrawablePtr dst, GCPtr pGC,
     97                                   int nRects, xRectangle *pRects);
     98 static void RootlessPolyArc(DrawablePtr dst, GCPtr pGC, int narcs,
     99                             xArc * parcs);
    100 static void RootlessFillPolygon(DrawablePtr dst, GCPtr pGC, int shape, int mode,
    101                                 int count, DDXPointPtr pptInit);
    102 static void RootlessPolyFillRect(DrawablePtr dst, GCPtr pGC, int nRectsInit,
    103                                  xRectangle *pRectsInit);
    104 static void RootlessPolyFillArc(DrawablePtr dst, GCPtr pGC, int narcsInit,
    105                                 xArc * parcsInit);
    106 static int RootlessPolyText8(DrawablePtr dst, GCPtr pGC, int x, int y,
    107                              int count, char *chars);
    108 static int RootlessPolyText16(DrawablePtr dst, GCPtr pGC, int x, int y,
    109                               int count, unsigned short *chars);
    110 static void RootlessImageText8(DrawablePtr dst, GCPtr pGC, int x, int y,
    111                                int count, char *chars);
    112 static void RootlessImageText16(DrawablePtr dst, GCPtr pGC, int x, int y,
    113                                 int count, unsigned short *chars);
    114 static void RootlessImageGlyphBlt(DrawablePtr dst, GCPtr pGC, int x, int y,
    115                                   unsigned int nglyphInit,
    116                                   CharInfoPtr * ppciInit, void *unused);
    117 static void RootlessPolyGlyphBlt(DrawablePtr dst, GCPtr pGC, int x, int y,
    118                                  unsigned int nglyph, CharInfoPtr * ppci,
    119                                  void *pglyphBase);
    120 static void RootlessPushPixels(GCPtr pGC, PixmapPtr pBitMap, DrawablePtr dst,
    121                                int dx, int dy, int xOrg, int yOrg);
    122 
    123 static GCOps rootlessGCOps = {
    124     RootlessFillSpans,
    125     RootlessSetSpans,
    126     RootlessPutImage,
    127     RootlessCopyArea,
    128     RootlessCopyPlane,
    129     RootlessPolyPoint,
    130     RootlessPolylines,
    131     RootlessPolySegment,
    132     RootlessPolyRectangle,
    133     RootlessPolyArc,
    134     RootlessFillPolygon,
    135     RootlessPolyFillRect,
    136     RootlessPolyFillArc,
    137     RootlessPolyText8,
    138     RootlessPolyText16,
    139     RootlessImageText8,
    140     RootlessImageText16,
    141     RootlessImageGlyphBlt,
    142     RootlessPolyGlyphBlt,
    143     RootlessPushPixels
    144 };
    145 
    146 /*
    147    If ROOTLESS_PROTECT_ALPHA is set, we have to make sure that the alpha
    148    channel of the on screen windows is always opaque. fb makes this harder
    149    than it would otherwise be by noticing that a planemask of 0x00ffffff
    150    includes all bits when depth==24, and so it "optimizes" the planemask to
    151    0xffffffff. We work around this by temporarily setting depth=bpp while
    152    changing the GC.
    153 
    154    So the normal situation (in 32 bit mode) is that the planemask is
    155    0x00ffffff and thus fb leaves the alpha channel alone. The rootless
    156    implementation is responsible for setting the alpha channel opaque
    157    initially.
    158 
    159    Unfortunately drawing with a planemask that doesn't have all bits set
    160    normally causes fb to fall off its fastest paths when blitting and
    161    filling.  So we try to recognize when we can relax the planemask back to
    162    0xffffffff, and do that for the duration of the drawing operation,
    163    setting the alpha channel in fg/bg pixels to opaque at the same time. We
    164    can do this when drawing op is GXcopy. We can also do this when copying
    165    from another window since its alpha channel must also be opaque.
    166 
    167    The three macros below are used to implement this. Drawing ops that can
    168    potentially have their planemask relaxed look like:
    169 
    170    OP {
    171        GC_SAVE(gc);
    172        GCOP_UNWRAP(gc);
    173 
    174        ...
    175 
    176        if (canAccelxxx(..) && otherwise-suitable)
    177             GC_UNSET_PM(gc, dst);
    178 
    179        gc->funcs->OP(gc, ...);
    180 
    181        GC_RESTORE(gc, dst);
    182        GCOP_WRAP(gc);
    183    }
    184 
    185  */
    186 
    187 #define GC_SAVE(pGC) 				\
    188     unsigned long _save_fg = (pGC)->fgPixel;	\
    189     unsigned long _save_bg = (pGC)->bgPixel;	\
    190     unsigned long _save_pm = (pGC)->planemask;	\
    191     Bool _changed = FALSE
    192 
    193 #define GC_RESTORE(pGC, pDraw)					\
    194     do {							\
    195         if (_changed) {						\
    196             unsigned int depth = (pDraw)->depth;		\
    197             (pGC)->fgPixel = _save_fg;				\
    198             (pGC)->bgPixel = _save_bg;				\
    199             (pGC)->planemask = _save_pm;			\
    200             (pDraw)->depth = (pDraw)->bitsPerPixel;		\
    201             VALIDATE_GC(pGC, GCForeground | GCBackground |	\
    202                         GCPlaneMask, pDraw);			\
    203             (pDraw)->depth = depth;				\
    204         }							\
    205     } while (0)
    206 
    207 #define GC_UNSET_PM(pGC, pDraw)						\
    208     do {								\
    209         unsigned int mask = RootlessAlphaMask ((pDraw)->bitsPerPixel);	\
    210         if (((pGC)->planemask & mask) != mask) {			\
    211             unsigned int depth = (pDraw)->depth;			\
    212             (pGC)->fgPixel |= mask;					\
    213             (pGC)->bgPixel |= mask;					\
    214             (pGC)->planemask |= mask;					\
    215             (pDraw)->depth = (pDraw)->bitsPerPixel;			\
    216             VALIDATE_GC(pGC, GCForeground |				\
    217                         GCBackground | GCPlaneMask, pDraw);		\
    218             (pDraw)->depth = depth;					\
    219             _changed = TRUE;						\
    220         }								\
    221     } while (0)
    222 
    223 #define VALIDATE_GC(pGC, changes, pDrawable)				\
    224     do {								\
    225         pGC->funcs->ValidateGC(pGC, changes, pDrawable);		\
    226         if (((WindowPtr) pDrawable)->viewable) {			\
    227             gcrec->originalOps = pGC->ops;				\
    228         }								\
    229     } while(0)
    230 
    231 static RootlessWindowRec *
    232 canAccelBlit(DrawablePtr pDraw, GCPtr pGC)
    233 {
    234     WindowPtr pTop;
    235     RootlessWindowRec *winRec;
    236     unsigned int pm;
    237 
    238     if (pGC->alu != GXcopy)
    239         return NULL;
    240 
    241     if (pDraw->type != DRAWABLE_WINDOW)
    242         return NULL;
    243 
    244     pm = ~RootlessAlphaMask(pDraw->bitsPerPixel);
    245     if ((pGC->planemask & pm) != pm)
    246         return NULL;
    247 
    248     pTop = TopLevelParent((WindowPtr) pDraw);
    249     if (pTop == NULL)
    250         return NULL;
    251 
    252     winRec = WINREC(pTop);
    253     if (winRec == NULL)
    254         return NULL;
    255 
    256     return winRec;
    257 }
    258 
    259 static inline RootlessWindowRec *
    260 canAccelFill(DrawablePtr pDraw, GCPtr pGC)
    261 {
    262     if (pGC->fillStyle != FillSolid)
    263         return NULL;
    264 
    265     return canAccelBlit(pDraw, pGC);
    266 }
    267 
    268 /*
    269  * Screen function to create a graphics context
    270  */
    271 Bool
    272 RootlessCreateGC(GCPtr pGC)
    273 {
    274     RootlessGCRec *gcrec;
    275     RootlessScreenRec *s;
    276     Bool result;
    277 
    278     SCREEN_UNWRAP(pGC->pScreen, CreateGC);
    279     s = SCREENREC(pGC->pScreen);
    280     result = s->CreateGC(pGC);
    281 
    282     gcrec = (RootlessGCRec *)
    283         dixLookupPrivate(&pGC->devPrivates, rootlessGCPrivateKey);
    284     gcrec->originalOps = NULL;  // don't wrap ops yet
    285     gcrec->originalFuncs = pGC->funcs;
    286     pGC->funcs = &rootlessGCFuncs;
    287 
    288     SCREEN_WRAP(pGC->pScreen, CreateGC);
    289     return result;
    290 }
    291 
    292 /*
    293  * GC funcs
    294  *
    295  * These wrap lower level GC funcs.
    296  * ValidateGC wraps the GC ops iff dest is viewable.
    297  * All the others just unwrap and call.
    298  */
    299 
    300 // GCFUNC_UNRAP assumes funcs have been wrapped and
    301 // does not assume ops have been wrapped
    302 #define GCFUNC_UNWRAP(pGC) \
    303     RootlessGCRec *gcrec = (RootlessGCRec *) \
    304 	dixLookupPrivate(&(pGC)->devPrivates, rootlessGCPrivateKey); \
    305     (pGC)->funcs = gcrec->originalFuncs; \
    306     if (gcrec->originalOps) { \
    307         (pGC)->ops = gcrec->originalOps; \
    308 }
    309 
    310 #define GCFUNC_WRAP(pGC) \
    311     gcrec->originalFuncs = (pGC)->funcs; \
    312     (pGC)->funcs = &rootlessGCFuncs; \
    313     if (gcrec->originalOps) { \
    314         gcrec->originalOps = (pGC)->ops; \
    315         (pGC)->ops = &rootlessGCOps; \
    316 }
    317 
    318 static void
    319 RootlessValidateGC(GCPtr pGC, unsigned long changes, DrawablePtr pDrawable)
    320 {
    321     GCFUNC_UNWRAP(pGC);
    322 
    323     gcrec->originalOps = NULL;
    324 
    325     if (pDrawable->type == DRAWABLE_WINDOW) {
    326 #ifdef ROOTLESS_PROTECT_ALPHA
    327         unsigned int depth = pDrawable->depth;
    328 
    329         // We force a planemask so fb doesn't overwrite the alpha channel.
    330         // Left to its own devices, fb will optimize away the planemask.
    331         pDrawable->depth = pDrawable->bitsPerPixel;
    332         pGC->planemask &= ~RootlessAlphaMask(pDrawable->bitsPerPixel);
    333         VALIDATE_GC(pGC, changes | GCPlaneMask, pDrawable);
    334         pDrawable->depth = depth;
    335 #else
    336         VALIDATE_GC(pGC, changes, pDrawable);
    337 #endif
    338     }
    339     else {
    340         pGC->funcs->ValidateGC(pGC, changes, pDrawable);
    341     }
    342 
    343     GCFUNC_WRAP(pGC);
    344 }
    345 
    346 static void
    347 RootlessChangeGC(GCPtr pGC, unsigned long mask)
    348 {
    349     GCFUNC_UNWRAP(pGC);
    350     pGC->funcs->ChangeGC(pGC, mask);
    351     GCFUNC_WRAP(pGC);
    352 }
    353 
    354 static void
    355 RootlessCopyGC(GCPtr pGCSrc, unsigned long mask, GCPtr pGCDst)
    356 {
    357     GCFUNC_UNWRAP(pGCDst);
    358     pGCDst->funcs->CopyGC(pGCSrc, mask, pGCDst);
    359     GCFUNC_WRAP(pGCDst);
    360 }
    361 
    362 static void
    363 RootlessDestroyGC(GCPtr pGC)
    364 {
    365     GCFUNC_UNWRAP(pGC);
    366     pGC->funcs->DestroyGC(pGC);
    367     GCFUNC_WRAP(pGC);
    368 }
    369 
    370 static void
    371 RootlessChangeClip(GCPtr pGC, int type, void *pvalue, int nrects)
    372 {
    373     GCFUNC_UNWRAP(pGC);
    374     pGC->funcs->ChangeClip(pGC, type, pvalue, nrects);
    375     GCFUNC_WRAP(pGC);
    376 }
    377 
    378 static void
    379 RootlessDestroyClip(GCPtr pGC)
    380 {
    381     GCFUNC_UNWRAP(pGC);
    382     pGC->funcs->DestroyClip(pGC);
    383     GCFUNC_WRAP(pGC);
    384 }
    385 
    386 static void
    387 RootlessCopyClip(GCPtr pgcDst, GCPtr pgcSrc)
    388 {
    389     GCFUNC_UNWRAP(pgcDst);
    390     pgcDst->funcs->CopyClip(pgcDst, pgcSrc);
    391     GCFUNC_WRAP(pgcDst);
    392 }
    393 
    394 /*
    395  * GC ops
    396  *
    397  * We can't use shadowfb because shadowfb assumes one pixmap
    398  * and our root window is a special case.
    399  * However, much of this code is copied from shadowfb.
    400  */
    401 
    402 // assumes both funcs and ops are wrapped
    403 #define GCOP_UNWRAP(pGC) \
    404     RootlessGCRec *gcrec = (RootlessGCRec *) \
    405         dixLookupPrivate(&(pGC)->devPrivates, rootlessGCPrivateKey); \
    406     const GCFuncs *saveFuncs = pGC->funcs; \
    407     (pGC)->funcs = gcrec->originalFuncs; \
    408     (pGC)->ops = gcrec->originalOps;
    409 
    410 #define GCOP_WRAP(pGC) \
    411     gcrec->originalOps = (pGC)->ops; \
    412     (pGC)->funcs = saveFuncs; \
    413     (pGC)->ops = &rootlessGCOps;
    414 
    415 static void
    416 RootlessFillSpans(DrawablePtr dst, GCPtr pGC, int nInit,
    417                   DDXPointPtr pptInit, int *pwidthInit, int sorted)
    418 {
    419     GC_SAVE(pGC);
    420     GCOP_UNWRAP(pGC);
    421     RL_DEBUG_MSG("fill spans start ");
    422 
    423     if (nInit <= 0) {
    424         pGC->ops->FillSpans(dst, pGC, nInit, pptInit, pwidthInit, sorted);
    425     }
    426     else {
    427         DDXPointPtr ppt = pptInit;
    428         int *pwidth = pwidthInit;
    429         int i = nInit;
    430         BoxRec box;
    431 
    432         box.x1 = ppt->x;
    433         box.x2 = box.x1 + *pwidth;
    434         box.y2 = box.y1 = ppt->y;
    435 
    436         while (--i) {
    437             ppt++;
    438             pwidth++;
    439             if (box.x1 > ppt->x)
    440                 box.x1 = ppt->x;
    441             if (box.x2 < (ppt->x + *pwidth))
    442                 box.x2 = ppt->x + *pwidth;
    443             if (box.y1 > ppt->y)
    444                 box.y1 = ppt->y;
    445             else if (box.y2 < ppt->y)
    446                 box.y2 = ppt->y;
    447         }
    448 
    449         box.y2++;
    450 
    451         RootlessStartDrawing((WindowPtr) dst);
    452 
    453         if (canAccelFill(dst, pGC)) {
    454             GC_UNSET_PM(pGC, dst);
    455         }
    456 
    457         pGC->ops->FillSpans(dst, pGC, nInit, pptInit, pwidthInit, sorted);
    458 
    459         TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
    460         if (BOX_NOT_EMPTY(box))
    461             RootlessDamageBox((WindowPtr) dst, &box);
    462     }
    463 
    464     GC_RESTORE(pGC, dst);
    465     GCOP_WRAP(pGC);
    466     RL_DEBUG_MSG("fill spans end\n");
    467 }
    468 
    469 static void
    470 RootlessSetSpans(DrawablePtr dst, GCPtr pGC, char *pSrc,
    471                  DDXPointPtr pptInit, int *pwidthInit, int nspans, int sorted)
    472 {
    473     GCOP_UNWRAP(pGC);
    474     RL_DEBUG_MSG("set spans start ");
    475 
    476     if (nspans <= 0) {
    477         pGC->ops->SetSpans(dst, pGC, pSrc, pptInit, pwidthInit, nspans, sorted);
    478     }
    479     else {
    480         DDXPointPtr ppt = pptInit;
    481         int *pwidth = pwidthInit;
    482         int i = nspans;
    483         BoxRec box;
    484 
    485         box.x1 = ppt->x;
    486         box.x2 = box.x1 + *pwidth;
    487         box.y2 = box.y1 = ppt->y;
    488 
    489         while (--i) {
    490             ppt++;
    491             pwidth++;
    492             if (box.x1 > ppt->x)
    493                 box.x1 = ppt->x;
    494             if (box.x2 < (ppt->x + *pwidth))
    495                 box.x2 = ppt->x + *pwidth;
    496             if (box.y1 > ppt->y)
    497                 box.y1 = ppt->y;
    498             else if (box.y2 < ppt->y)
    499                 box.y2 = ppt->y;
    500         }
    501 
    502         box.y2++;
    503 
    504         RootlessStartDrawing((WindowPtr) dst);
    505         pGC->ops->SetSpans(dst, pGC, pSrc, pptInit, pwidthInit, nspans, sorted);
    506 
    507         TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
    508         if (BOX_NOT_EMPTY(box))
    509             RootlessDamageBox((WindowPtr) dst, &box);
    510     }
    511     GCOP_WRAP(pGC);
    512     RL_DEBUG_MSG("set spans end\n");
    513 }
    514 
    515 static void
    516 RootlessPutImage(DrawablePtr dst, GCPtr pGC,
    517                  int depth, int x, int y, int w, int h,
    518                  int leftPad, int format, char *pBits)
    519 {
    520     BoxRec box;
    521 
    522     GCOP_UNWRAP(pGC);
    523     RL_DEBUG_MSG("put image start ");
    524 
    525     RootlessStartDrawing((WindowPtr) dst);
    526     pGC->ops->PutImage(dst, pGC, depth, x, y, w, h, leftPad, format, pBits);
    527 
    528     box.x1 = x + dst->x;
    529     box.x2 = box.x1 + w;
    530     box.y1 = y + dst->y;
    531     box.y2 = box.y1 + h;
    532 
    533     TRIM_BOX(box, pGC);
    534     if (BOX_NOT_EMPTY(box))
    535         RootlessDamageBox((WindowPtr) dst, &box);
    536 
    537     GCOP_WRAP(pGC);
    538     RL_DEBUG_MSG("put image end\n");
    539 }
    540 
    541 /* changed area is *dest* rect */
    542 static RegionPtr
    543 RootlessCopyArea(DrawablePtr pSrc, DrawablePtr dst, GCPtr pGC,
    544                  int srcx, int srcy, int w, int h, int dstx, int dsty)
    545 {
    546     RegionPtr result;
    547     BoxRec box;
    548 
    549     GC_SAVE(pGC);
    550     GCOP_UNWRAP(pGC);
    551 
    552     RL_DEBUG_MSG("copy area start (src 0x%x, dst 0x%x)", pSrc, dst);
    553 
    554     if (pSrc->type == DRAWABLE_WINDOW && IsFramedWindow((WindowPtr) pSrc)) {
    555         /* If both source and dest are windows, and we're doing
    556            a simple copy operation, we can remove the alpha-protecting
    557            planemask (since source has opaque alpha as well) */
    558 
    559         if (canAccelBlit(pSrc, pGC)) {
    560             GC_UNSET_PM(pGC, dst);
    561         }
    562 
    563         RootlessStartDrawing((WindowPtr) pSrc);
    564     }
    565     RootlessStartDrawing((WindowPtr) dst);
    566     result = pGC->ops->CopyArea(pSrc, dst, pGC, srcx, srcy, w, h, dstx, dsty);
    567 
    568     box.x1 = dstx + dst->x;
    569     box.x2 = box.x1 + w;
    570     box.y1 = dsty + dst->y;
    571     box.y2 = box.y1 + h;
    572 
    573     TRIM_BOX(box, pGC);
    574     if (BOX_NOT_EMPTY(box))
    575         RootlessDamageBox((WindowPtr) dst, &box);
    576 
    577     GC_RESTORE(pGC, dst);
    578     GCOP_WRAP(pGC);
    579     RL_DEBUG_MSG("copy area end\n");
    580     return result;
    581 }
    582 
    583 /* changed area is *dest* rect */
    584 static RegionPtr
    585 RootlessCopyPlane(DrawablePtr pSrc, DrawablePtr dst,
    586                   GCPtr pGC, int srcx, int srcy,
    587                   int w, int h, int dstx, int dsty, unsigned long plane)
    588 {
    589     RegionPtr result;
    590     BoxRec box;
    591 
    592     GCOP_UNWRAP(pGC);
    593 
    594     RL_DEBUG_MSG("copy plane start ");
    595 
    596     if (pSrc->type == DRAWABLE_WINDOW && IsFramedWindow((WindowPtr) pSrc)) {
    597         RootlessStartDrawing((WindowPtr) pSrc);
    598     }
    599     RootlessStartDrawing((WindowPtr) dst);
    600     result = pGC->ops->CopyPlane(pSrc, dst, pGC, srcx, srcy, w, h,
    601                                  dstx, dsty, plane);
    602 
    603     box.x1 = dstx + dst->x;
    604     box.x2 = box.x1 + w;
    605     box.y1 = dsty + dst->y;
    606     box.y2 = box.y1 + h;
    607 
    608     TRIM_BOX(box, pGC);
    609     if (BOX_NOT_EMPTY(box))
    610         RootlessDamageBox((WindowPtr) dst, &box);
    611 
    612     GCOP_WRAP(pGC);
    613     RL_DEBUG_MSG("copy plane end\n");
    614     return result;
    615 }
    616 
    617 // Options for size of changed area:
    618 //  0 = box per point
    619 //  1 = big box around all points
    620 //  2 = accumulate point in 20 pixel radius
    621 #define ROOTLESS_CHANGED_AREA 1
    622 #define abs(a) ((a) > 0 ? (a) : -(a))
    623 
    624 /* changed area is box around all points */
    625 static void
    626 RootlessPolyPoint(DrawablePtr dst, GCPtr pGC,
    627                   int mode, int npt, DDXPointPtr pptInit)
    628 {
    629     GCOP_UNWRAP(pGC);
    630     RL_DEBUG_MSG("polypoint start ");
    631 
    632     RootlessStartDrawing((WindowPtr) dst);
    633     pGC->ops->PolyPoint(dst, pGC, mode, npt, pptInit);
    634 
    635     if (npt > 0) {
    636 #if ROOTLESS_CHANGED_AREA==0
    637         // box per point
    638         BoxRec box;
    639 
    640         while (npt) {
    641             box.x1 = pptInit->x;
    642             box.y1 = pptInit->y;
    643             box.x2 = box.x1 + 1;
    644             box.y2 = box.y1 + 1;
    645 
    646             TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
    647             if (BOX_NOT_EMPTY(box))
    648                 RootlessDamageBox((WindowPtr) dst, &box);
    649 
    650             npt--;
    651             pptInit++;
    652         }
    653 
    654 #elif ROOTLESS_CHANGED_AREA==1
    655         // one big box
    656         BoxRec box;
    657 
    658         box.x2 = box.x1 = pptInit->x;
    659         box.y2 = box.y1 = pptInit->y;
    660         while (--npt) {
    661             pptInit++;
    662             if (box.x1 > pptInit->x)
    663                 box.x1 = pptInit->x;
    664             else if (box.x2 < pptInit->x)
    665                 box.x2 = pptInit->x;
    666             if (box.y1 > pptInit->y)
    667                 box.y1 = pptInit->y;
    668             else if (box.y2 < pptInit->y)
    669                 box.y2 = pptInit->y;
    670         }
    671 
    672         box.x2++;
    673         box.y2++;
    674 
    675         TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
    676         if (BOX_NOT_EMPTY(box))
    677             RootlessDamageBox((WindowPtr) dst, &box);
    678 
    679 #elif ROOTLESS_CHANGED_AREA==2
    680         // clever(?) method: accumulate point in 20-pixel radius
    681         BoxRec box;
    682         int firstx, firsty;
    683 
    684         box.x2 = box.x1 = firstx = pptInit->x;
    685         box.y2 = box.y1 = firsty = pptInit->y;
    686         while (--npt) {
    687             pptInit++;
    688             if (abs(pptInit->x - firstx) > 20 || abs(pptInit->y - firsty) > 20) {
    689                 box.x2++;
    690                 box.y2++;
    691                 TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
    692                 if (BOX_NOT_EMPTY(box))
    693                     RootlessDamageBox((WindowPtr) dst, &box);
    694                 box.x2 = box.x1 = firstx = pptInit->x;
    695                 box.y2 = box.y1 = firsty = pptInit->y;
    696             }
    697             else {
    698                 if (box.x1 > pptInit->x)
    699                     box.x1 = pptInit->x;
    700                 else if (box.x2 < pptInit->x)
    701                     box.x2 = pptInit->x;
    702                 if (box.y1 > pptInit->y)
    703                     box.y1 = pptInit->y;
    704                 else if (box.y2 < pptInit->y)
    705                     box.y2 = pptInit->y;
    706             }
    707         }
    708         box.x2++;
    709         box.y2++;
    710         TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
    711         if (BOX_NOT_EMPTY(box))
    712             RootlessDamageBox((WindowPtr) dst, &box);
    713 #endif                          /* ROOTLESS_CHANGED_AREA */
    714     }
    715 
    716     GCOP_WRAP(pGC);
    717     RL_DEBUG_MSG("polypoint end\n");
    718 }
    719 
    720 #undef ROOTLESS_CHANGED_AREA
    721 
    722 /* changed area is box around each line */
    723 static void
    724 RootlessPolylines(DrawablePtr dst, GCPtr pGC,
    725                   int mode, int npt, DDXPointPtr pptInit)
    726 {
    727     GCOP_UNWRAP(pGC);
    728     RL_DEBUG_MSG("poly lines start ");
    729 
    730     RootlessStartDrawing((WindowPtr) dst);
    731     pGC->ops->Polylines(dst, pGC, mode, npt, pptInit);
    732 
    733     if (npt > 0) {
    734         BoxRec box;
    735         int extra = pGC->lineWidth >> 1;
    736 
    737         box.x2 = box.x1 = pptInit->x;
    738         box.y2 = box.y1 = pptInit->y;
    739 
    740         if (npt > 1) {
    741             if (pGC->joinStyle == JoinMiter)
    742                 extra = 6 * pGC->lineWidth;
    743             else if (pGC->capStyle == CapProjecting)
    744                 extra = pGC->lineWidth;
    745         }
    746 
    747         if (mode == CoordModePrevious) {
    748             int x = box.x1;
    749             int y = box.y1;
    750 
    751             while (--npt) {
    752                 pptInit++;
    753                 x += pptInit->x;
    754                 y += pptInit->y;
    755                 if (box.x1 > x)
    756                     box.x1 = x;
    757                 else if (box.x2 < x)
    758                     box.x2 = x;
    759                 if (box.y1 > y)
    760                     box.y1 = y;
    761                 else if (box.y2 < y)
    762                     box.y2 = y;
    763             }
    764         }
    765         else {
    766             while (--npt) {
    767                 pptInit++;
    768                 if (box.x1 > pptInit->x)
    769                     box.x1 = pptInit->x;
    770                 else if (box.x2 < pptInit->x)
    771                     box.x2 = pptInit->x;
    772                 if (box.y1 > pptInit->y)
    773                     box.y1 = pptInit->y;
    774                 else if (box.y2 < pptInit->y)
    775                     box.y2 = pptInit->y;
    776             }
    777         }
    778 
    779         box.x2++;
    780         box.y2++;
    781 
    782         if (extra) {
    783             box.x1 -= extra;
    784             box.x2 += extra;
    785             box.y1 -= extra;
    786             box.y2 += extra;
    787         }
    788 
    789         TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
    790         if (BOX_NOT_EMPTY(box))
    791             RootlessDamageBox((WindowPtr) dst, &box);
    792     }
    793 
    794     GCOP_WRAP(pGC);
    795     RL_DEBUG_MSG("poly lines end\n");
    796 }
    797 
    798 /* changed area is box around each line segment */
    799 static void
    800 RootlessPolySegment(DrawablePtr dst, GCPtr pGC, int nseg, xSegment * pSeg)
    801 {
    802     GCOP_UNWRAP(pGC);
    803     RL_DEBUG_MSG("poly segment start (win 0x%x)", dst);
    804 
    805     RootlessStartDrawing((WindowPtr) dst);
    806     pGC->ops->PolySegment(dst, pGC, nseg, pSeg);
    807 
    808     if (nseg > 0) {
    809         BoxRec box;
    810         int extra = pGC->lineWidth;
    811 
    812         if (pGC->capStyle != CapProjecting)
    813             extra >>= 1;
    814 
    815         if (pSeg->x2 > pSeg->x1) {
    816             box.x1 = pSeg->x1;
    817             box.x2 = pSeg->x2;
    818         }
    819         else {
    820             box.x2 = pSeg->x1;
    821             box.x1 = pSeg->x2;
    822         }
    823 
    824         if (pSeg->y2 > pSeg->y1) {
    825             box.y1 = pSeg->y1;
    826             box.y2 = pSeg->y2;
    827         }
    828         else {
    829             box.y2 = pSeg->y1;
    830             box.y1 = pSeg->y2;
    831         }
    832 
    833         while (--nseg) {
    834             pSeg++;
    835             if (pSeg->x2 > pSeg->x1) {
    836                 if (pSeg->x1 < box.x1)
    837                     box.x1 = pSeg->x1;
    838                 if (pSeg->x2 > box.x2)
    839                     box.x2 = pSeg->x2;
    840             }
    841             else {
    842                 if (pSeg->x2 < box.x1)
    843                     box.x1 = pSeg->x2;
    844                 if (pSeg->x1 > box.x2)
    845                     box.x2 = pSeg->x1;
    846             }
    847             if (pSeg->y2 > pSeg->y1) {
    848                 if (pSeg->y1 < box.y1)
    849                     box.y1 = pSeg->y1;
    850                 if (pSeg->y2 > box.y2)
    851                     box.y2 = pSeg->y2;
    852             }
    853             else {
    854                 if (pSeg->y2 < box.y1)
    855                     box.y1 = pSeg->y2;
    856                 if (pSeg->y1 > box.y2)
    857                     box.y2 = pSeg->y1;
    858             }
    859         }
    860 
    861         box.x2++;
    862         box.y2++;
    863 
    864         if (extra) {
    865             box.x1 -= extra;
    866             box.x2 += extra;
    867             box.y1 -= extra;
    868             box.y2 += extra;
    869         }
    870 
    871         TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
    872         if (BOX_NOT_EMPTY(box))
    873             RootlessDamageBox((WindowPtr) dst, &box);
    874     }
    875 
    876     GCOP_WRAP(pGC);
    877     RL_DEBUG_MSG("poly segment end\n");
    878 }
    879 
    880 /* changed area is box around each line (not entire rects) */
    881 static void
    882 RootlessPolyRectangle(DrawablePtr dst, GCPtr pGC,
    883                       int nRects, xRectangle *pRects)
    884 {
    885     GCOP_UNWRAP(pGC);
    886     RL_DEBUG_MSG("poly rectangle start ");
    887 
    888     RootlessStartDrawing((WindowPtr) dst);
    889     pGC->ops->PolyRectangle(dst, pGC, nRects, pRects);
    890 
    891     if (nRects > 0) {
    892         BoxRec box;
    893         int offset1, offset2, offset3;
    894 
    895         offset2 = pGC->lineWidth;
    896         if (!offset2)
    897             offset2 = 1;
    898         offset1 = offset2 >> 1;
    899         offset3 = offset2 - offset1;
    900 
    901         while (nRects--) {
    902             box.x1 = pRects->x - offset1;
    903             box.y1 = pRects->y - offset1;
    904             box.x2 = box.x1 + pRects->width + offset2;
    905             box.y2 = box.y1 + offset2;
    906             TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
    907             if (BOX_NOT_EMPTY(box))
    908                 RootlessDamageBox((WindowPtr) dst, &box);
    909 
    910             box.x1 = pRects->x - offset1;
    911             box.y1 = pRects->y + offset3;
    912             box.x2 = box.x1 + offset2;
    913             box.y2 = box.y1 + pRects->height - offset2;
    914             TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
    915             if (BOX_NOT_EMPTY(box))
    916                 RootlessDamageBox((WindowPtr) dst, &box);
    917 
    918             box.x1 = pRects->x + pRects->width - offset1;
    919             box.y1 = pRects->y + offset3;
    920             box.x2 = box.x1 + offset2;
    921             box.y2 = box.y1 + pRects->height - offset2;
    922             TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
    923             if (BOX_NOT_EMPTY(box))
    924                 RootlessDamageBox((WindowPtr) dst, &box);
    925 
    926             box.x1 = pRects->x - offset1;
    927             box.y1 = pRects->y + pRects->height - offset1;
    928             box.x2 = box.x1 + pRects->width + offset2;
    929             box.y2 = box.y1 + offset2;
    930             TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
    931             if (BOX_NOT_EMPTY(box))
    932                 RootlessDamageBox((WindowPtr) dst, &box);
    933 
    934             pRects++;
    935         }
    936     }
    937 
    938     GCOP_WRAP(pGC);
    939     RL_DEBUG_MSG("poly rectangle end\n");
    940 }
    941 
    942 /* changed area is box around each arc (assumes all arcs are 360 degrees) */
    943 static void
    944 RootlessPolyArc(DrawablePtr dst, GCPtr pGC, int narcs, xArc * parcs)
    945 {
    946     GCOP_UNWRAP(pGC);
    947     RL_DEBUG_MSG("poly arc start ");
    948 
    949     RootlessStartDrawing((WindowPtr) dst);
    950     pGC->ops->PolyArc(dst, pGC, narcs, parcs);
    951 
    952     if (narcs > 0) {
    953         int extra = pGC->lineWidth >> 1;
    954         BoxRec box;
    955 
    956         box.x1 = parcs->x;
    957         box.x2 = box.x1 + parcs->width;
    958         box.y1 = parcs->y;
    959         box.y2 = box.y1 + parcs->height;
    960 
    961         /* should I break these up instead ? */
    962 
    963         while (--narcs) {
    964             parcs++;
    965             if (box.x1 > parcs->x)
    966                 box.x1 = parcs->x;
    967             if (box.x2 < (parcs->x + parcs->width))
    968                 box.x2 = parcs->x + parcs->width;
    969             if (box.y1 > parcs->y)
    970                 box.y1 = parcs->y;
    971             if (box.y2 < (parcs->y + parcs->height))
    972                 box.y2 = parcs->y + parcs->height;
    973         }
    974 
    975         if (extra) {
    976             box.x1 -= extra;
    977             box.x2 += extra;
    978             box.y1 -= extra;
    979             box.y2 += extra;
    980         }
    981 
    982         box.x2++;
    983         box.y2++;
    984 
    985         TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
    986         if (BOX_NOT_EMPTY(box))
    987             RootlessDamageBox((WindowPtr) dst, &box);
    988     }
    989 
    990     GCOP_WRAP(pGC);
    991     RL_DEBUG_MSG("poly arc end\n");
    992 }
    993 
    994 /* changed area is box around each poly */
    995 static void
    996 RootlessFillPolygon(DrawablePtr dst, GCPtr pGC,
    997                     int shape, int mode, int count, DDXPointPtr pptInit)
    998 {
    999     GC_SAVE(pGC);
   1000     GCOP_UNWRAP(pGC);
   1001     RL_DEBUG_MSG("fill poly start (win 0x%x, fillStyle 0x%x)", dst,
   1002                  pGC->fillStyle);
   1003 
   1004     if (count <= 2) {
   1005         pGC->ops->FillPolygon(dst, pGC, shape, mode, count, pptInit);
   1006     }
   1007     else {
   1008         DDXPointPtr ppt = pptInit;
   1009         int i = count;
   1010         BoxRec box;
   1011 
   1012         box.x2 = box.x1 = ppt->x;
   1013         box.y2 = box.y1 = ppt->y;
   1014 
   1015         if (mode != CoordModeOrigin) {
   1016             int x = box.x1;
   1017             int y = box.y1;
   1018 
   1019             while (--i) {
   1020                 ppt++;
   1021                 x += ppt->x;
   1022                 y += ppt->y;
   1023                 if (box.x1 > x)
   1024                     box.x1 = x;
   1025                 else if (box.x2 < x)
   1026                     box.x2 = x;
   1027                 if (box.y1 > y)
   1028                     box.y1 = y;
   1029                 else if (box.y2 < y)
   1030                     box.y2 = y;
   1031             }
   1032         }
   1033         else {
   1034             while (--i) {
   1035                 ppt++;
   1036                 if (box.x1 > ppt->x)
   1037                     box.x1 = ppt->x;
   1038                 else if (box.x2 < ppt->x)
   1039                     box.x2 = ppt->x;
   1040                 if (box.y1 > ppt->y)
   1041                     box.y1 = ppt->y;
   1042                 else if (box.y2 < ppt->y)
   1043                     box.y2 = ppt->y;
   1044             }
   1045         }
   1046 
   1047         box.x2++;
   1048         box.y2++;
   1049 
   1050         RootlessStartDrawing((WindowPtr) dst);
   1051 
   1052         if (canAccelFill(dst, pGC)) {
   1053             GC_UNSET_PM(pGC, dst);
   1054         }
   1055 
   1056         pGC->ops->FillPolygon(dst, pGC, shape, mode, count, pptInit);
   1057 
   1058         TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
   1059         if (BOX_NOT_EMPTY(box))
   1060             RootlessDamageBox((WindowPtr) dst, &box);
   1061     }
   1062 
   1063     GC_RESTORE(pGC, dst);
   1064     GCOP_WRAP(pGC);
   1065     RL_DEBUG_MSG("fill poly end\n");
   1066 }
   1067 
   1068 /* changed area is the rects */
   1069 static void
   1070 RootlessPolyFillRect(DrawablePtr dst, GCPtr pGC,
   1071                      int nRectsInit, xRectangle *pRectsInit)
   1072 {
   1073     GC_SAVE(pGC);
   1074     GCOP_UNWRAP(pGC);
   1075     RL_DEBUG_MSG("fill rect start (win 0x%x, fillStyle 0x%x)", dst,
   1076                  pGC->fillStyle);
   1077 
   1078     if (nRectsInit <= 0) {
   1079         pGC->ops->PolyFillRect(dst, pGC, nRectsInit, pRectsInit);
   1080     }
   1081     else {
   1082         BoxRec box;
   1083         xRectangle *pRects = pRectsInit;
   1084         int nRects = nRectsInit;
   1085 
   1086         box.x1 = pRects->x;
   1087         box.x2 = box.x1 + pRects->width;
   1088         box.y1 = pRects->y;
   1089         box.y2 = box.y1 + pRects->height;
   1090 
   1091         while (--nRects) {
   1092             pRects++;
   1093             if (box.x1 > pRects->x)
   1094                 box.x1 = pRects->x;
   1095             if (box.x2 < (pRects->x + pRects->width))
   1096                 box.x2 = pRects->x + pRects->width;
   1097             if (box.y1 > pRects->y)
   1098                 box.y1 = pRects->y;
   1099             if (box.y2 < (pRects->y + pRects->height))
   1100                 box.y2 = pRects->y + pRects->height;
   1101         }
   1102 
   1103         RootlessStartDrawing((WindowPtr) dst);
   1104 
   1105         if (canAccelFill(dst, pGC)) {
   1106             GC_UNSET_PM(pGC, dst);
   1107         }
   1108 
   1109         pGC->ops->PolyFillRect(dst, pGC, nRectsInit, pRectsInit);
   1110 
   1111         TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
   1112         if (BOX_NOT_EMPTY(box))
   1113             RootlessDamageBox((WindowPtr) dst, &box);
   1114     }
   1115 
   1116     GC_RESTORE(pGC, dst);
   1117     GCOP_WRAP(pGC);
   1118     RL_DEBUG_MSG("fill rect end\n");
   1119 }
   1120 
   1121 /* changed area is box around each arc (assuming arcs are all 360 degrees) */
   1122 static void
   1123 RootlessPolyFillArc(DrawablePtr dst, GCPtr pGC, int narcsInit, xArc * parcsInit)
   1124 {
   1125     GC_SAVE(pGC);
   1126     GCOP_UNWRAP(pGC);
   1127     RL_DEBUG_MSG("fill arc start ");
   1128 
   1129     if (narcsInit > 0) {
   1130         BoxRec box;
   1131         int narcs = narcsInit;
   1132         xArc *parcs = parcsInit;
   1133 
   1134         box.x1 = parcs->x;
   1135         box.x2 = box.x1 + parcs->width;
   1136         box.y1 = parcs->y;
   1137         box.y2 = box.y1 + parcs->height;
   1138 
   1139         /* should I break these up instead ? */
   1140 
   1141         while (--narcs) {
   1142             parcs++;
   1143             if (box.x1 > parcs->x)
   1144                 box.x1 = parcs->x;
   1145             if (box.x2 < (parcs->x + parcs->width))
   1146                 box.x2 = parcs->x + parcs->width;
   1147             if (box.y1 > parcs->y)
   1148                 box.y1 = parcs->y;
   1149             if (box.y2 < (parcs->y + parcs->height))
   1150                 box.y2 = parcs->y + parcs->height;
   1151         }
   1152 
   1153         RootlessStartDrawing((WindowPtr) dst);
   1154 
   1155         if (canAccelFill(dst, pGC)) {
   1156             GC_UNSET_PM(pGC, dst);
   1157         }
   1158 
   1159         pGC->ops->PolyFillArc(dst, pGC, narcsInit, parcsInit);
   1160 
   1161         TRIM_AND_TRANSLATE_BOX(box, dst, pGC);
   1162         if (BOX_NOT_EMPTY(box))
   1163             RootlessDamageBox((WindowPtr) dst, &box);
   1164     }
   1165     else {
   1166         pGC->ops->PolyFillArc(dst, pGC, narcsInit, parcsInit);
   1167     }
   1168 
   1169     GC_RESTORE(pGC, dst);
   1170     GCOP_WRAP(pGC);
   1171     RL_DEBUG_MSG("fill arc end\n");
   1172 }
   1173 
   1174 static void
   1175 RootlessImageText8(DrawablePtr dst, GCPtr pGC,
   1176                    int x, int y, int count, char *chars)
   1177 {
   1178     GC_SAVE(pGC);
   1179     GCOP_UNWRAP(pGC);
   1180     RL_DEBUG_MSG("imagetext8 start ");
   1181 
   1182     if (count > 0) {
   1183         int top, bot, Min, Max;
   1184         BoxRec box;
   1185 
   1186         top = max(FONTMAXBOUNDS(pGC->font, ascent), FONTASCENT(pGC->font));
   1187         bot = max(FONTMAXBOUNDS(pGC->font, descent), FONTDESCENT(pGC->font));
   1188 
   1189         Min = count * FONTMINBOUNDS(pGC->font, characterWidth);
   1190         if (Min > 0)
   1191             Min = 0;
   1192         Max = count * FONTMAXBOUNDS(pGC->font, characterWidth);
   1193         if (Max < 0)
   1194             Max = 0;
   1195 
   1196         /* ugh */
   1197         box.x1 = dst->x + x + Min + FONTMINBOUNDS(pGC->font, leftSideBearing);
   1198         box.x2 = dst->x + x + Max + FONTMAXBOUNDS(pGC->font, rightSideBearing);
   1199 
   1200         box.y1 = dst->y + y - top;
   1201         box.y2 = dst->y + y + bot;
   1202 
   1203         RootlessStartDrawing((WindowPtr) dst);
   1204 
   1205         if (canAccelFill(dst, pGC)) {
   1206             GC_UNSET_PM(pGC, dst);
   1207         }
   1208 
   1209         pGC->ops->ImageText8(dst, pGC, x, y, count, chars);
   1210 
   1211         TRIM_BOX(box, pGC);
   1212         if (BOX_NOT_EMPTY(box))
   1213             RootlessDamageBox((WindowPtr) dst, &box);
   1214     }
   1215     else {
   1216         pGC->ops->ImageText8(dst, pGC, x, y, count, chars);
   1217     }
   1218 
   1219     GC_RESTORE(pGC, dst);
   1220     GCOP_WRAP(pGC);
   1221     RL_DEBUG_MSG("imagetext8 end\n");
   1222 }
   1223 
   1224 static int
   1225 RootlessPolyText8(DrawablePtr dst, GCPtr pGC,
   1226                   int x, int y, int count, char *chars)
   1227 {
   1228     int width;                  // the result, sorta
   1229 
   1230     GCOP_UNWRAP(pGC);
   1231 
   1232     RL_DEBUG_MSG("polytext8 start ");
   1233 
   1234     RootlessStartDrawing((WindowPtr) dst);
   1235     width = pGC->ops->PolyText8(dst, pGC, x, y, count, chars);
   1236     width -= x;
   1237 
   1238     if (width > 0) {
   1239         BoxRec box;
   1240 
   1241         /* ugh */
   1242         box.x1 = dst->x + x + FONTMINBOUNDS(pGC->font, leftSideBearing);
   1243         box.x2 = dst->x + x + FONTMAXBOUNDS(pGC->font, rightSideBearing);
   1244 
   1245         if (count > 1) {
   1246             box.x2 += width;
   1247         }
   1248 
   1249         box.y1 = dst->y + y - FONTMAXBOUNDS(pGC->font, ascent);
   1250         box.y2 = dst->y + y + FONTMAXBOUNDS(pGC->font, descent);
   1251 
   1252         TRIM_BOX(box, pGC);
   1253         if (BOX_NOT_EMPTY(box))
   1254             RootlessDamageBox((WindowPtr) dst, &box);
   1255     }
   1256 
   1257     GCOP_WRAP(pGC);
   1258     RL_DEBUG_MSG("polytext8 end\n");
   1259     return width + x;
   1260 }
   1261 
   1262 static void
   1263 RootlessImageText16(DrawablePtr dst, GCPtr pGC,
   1264                     int x, int y, int count, unsigned short *chars)
   1265 {
   1266     GC_SAVE(pGC);
   1267     GCOP_UNWRAP(pGC);
   1268     RL_DEBUG_MSG("imagetext16 start ");
   1269 
   1270     if (count > 0) {
   1271         int top, bot, Min, Max;
   1272         BoxRec box;
   1273 
   1274         top = max(FONTMAXBOUNDS(pGC->font, ascent), FONTASCENT(pGC->font));
   1275         bot = max(FONTMAXBOUNDS(pGC->font, descent), FONTDESCENT(pGC->font));
   1276 
   1277         Min = count * FONTMINBOUNDS(pGC->font, characterWidth);
   1278         if (Min > 0)
   1279             Min = 0;
   1280         Max = count * FONTMAXBOUNDS(pGC->font, characterWidth);
   1281         if (Max < 0)
   1282             Max = 0;
   1283 
   1284         /* ugh */
   1285         box.x1 = dst->x + x + Min + FONTMINBOUNDS(pGC->font, leftSideBearing);
   1286         box.x2 = dst->x + x + Max + FONTMAXBOUNDS(pGC->font, rightSideBearing);
   1287 
   1288         box.y1 = dst->y + y - top;
   1289         box.y2 = dst->y + y + bot;
   1290 
   1291         RootlessStartDrawing((WindowPtr) dst);
   1292 
   1293         if (canAccelFill(dst, pGC)) {
   1294             GC_UNSET_PM(pGC, dst);
   1295         }
   1296 
   1297         pGC->ops->ImageText16(dst, pGC, x, y, count, chars);
   1298 
   1299         TRIM_BOX(box, pGC);
   1300         if (BOX_NOT_EMPTY(box))
   1301             RootlessDamageBox((WindowPtr) dst, &box);
   1302     }
   1303     else {
   1304         pGC->ops->ImageText16(dst, pGC, x, y, count, chars);
   1305     }
   1306 
   1307     GC_RESTORE(pGC, dst);
   1308     GCOP_WRAP(pGC);
   1309     RL_DEBUG_MSG("imagetext16 end\n");
   1310 }
   1311 
   1312 static int
   1313 RootlessPolyText16(DrawablePtr dst, GCPtr pGC,
   1314                    int x, int y, int count, unsigned short *chars)
   1315 {
   1316     int width;                  // the result, sorta
   1317 
   1318     GCOP_UNWRAP(pGC);
   1319 
   1320     RL_DEBUG_MSG("polytext16 start ");
   1321 
   1322     RootlessStartDrawing((WindowPtr) dst);
   1323     width = pGC->ops->PolyText16(dst, pGC, x, y, count, chars);
   1324     width -= x;
   1325 
   1326     if (width > 0) {
   1327         BoxRec box;
   1328 
   1329         /* ugh */
   1330         box.x1 = dst->x + x + FONTMINBOUNDS(pGC->font, leftSideBearing);
   1331         box.x2 = dst->x + x + FONTMAXBOUNDS(pGC->font, rightSideBearing);
   1332 
   1333         if (count > 1) {
   1334             box.x2 += width;
   1335         }
   1336 
   1337         box.y1 = dst->y + y - FONTMAXBOUNDS(pGC->font, ascent);
   1338         box.y2 = dst->y + y + FONTMAXBOUNDS(pGC->font, descent);
   1339 
   1340         TRIM_BOX(box, pGC);
   1341         if (BOX_NOT_EMPTY(box))
   1342             RootlessDamageBox((WindowPtr) dst, &box);
   1343     }
   1344 
   1345     GCOP_WRAP(pGC);
   1346     RL_DEBUG_MSG("polytext16 end\n");
   1347     return width + x;
   1348 }
   1349 
   1350 static void
   1351 RootlessImageGlyphBlt(DrawablePtr dst, GCPtr pGC,
   1352                       int x, int y, unsigned int nglyphInit,
   1353                       CharInfoPtr * ppciInit, void *unused)
   1354 {
   1355     GC_SAVE(pGC);
   1356     GCOP_UNWRAP(pGC);
   1357     RL_DEBUG_MSG("imageglyph start ");
   1358 
   1359     if (nglyphInit > 0) {
   1360         int top, bot, width = 0;
   1361         BoxRec box;
   1362         unsigned int nglyph = nglyphInit;
   1363         CharInfoPtr *ppci = ppciInit;
   1364 
   1365         top = max(FONTMAXBOUNDS(pGC->font, ascent), FONTASCENT(pGC->font));
   1366         bot = max(FONTMAXBOUNDS(pGC->font, descent), FONTDESCENT(pGC->font));
   1367 
   1368         box.x1 = ppci[0]->metrics.leftSideBearing;
   1369         if (box.x1 > 0)
   1370             box.x1 = 0;
   1371         box.x2 = ppci[nglyph - 1]->metrics.rightSideBearing -
   1372             ppci[nglyph - 1]->metrics.characterWidth;
   1373         if (box.x2 < 0)
   1374             box.x2 = 0;
   1375 
   1376         box.x2 += dst->x + x;
   1377         box.x1 += dst->x + x;
   1378 
   1379         while (nglyph--) {
   1380             width += (*ppci)->metrics.characterWidth;
   1381             ppci++;
   1382         }
   1383 
   1384         if (width > 0)
   1385             box.x2 += width;
   1386         else
   1387             box.x1 += width;
   1388 
   1389         box.y1 = dst->y + y - top;
   1390         box.y2 = dst->y + y + bot;
   1391 
   1392         RootlessStartDrawing((WindowPtr) dst);
   1393 
   1394         if (canAccelFill(dst, pGC)) {
   1395             GC_UNSET_PM(pGC, dst);
   1396         }
   1397 
   1398         pGC->ops->ImageGlyphBlt(dst, pGC, x, y, nglyphInit, ppciInit, unused);
   1399 
   1400         TRIM_BOX(box, pGC);
   1401         if (BOX_NOT_EMPTY(box))
   1402             RootlessDamageBox((WindowPtr) dst, &box);
   1403     }
   1404     else {
   1405         pGC->ops->ImageGlyphBlt(dst, pGC, x, y, nglyphInit, ppciInit, unused);
   1406     }
   1407 
   1408     GC_RESTORE(pGC, dst);
   1409     GCOP_WRAP(pGC);
   1410     RL_DEBUG_MSG("imageglyph end\n");
   1411 }
   1412 
   1413 static void
   1414 RootlessPolyGlyphBlt(DrawablePtr dst, GCPtr pGC,
   1415                      int x, int y, unsigned int nglyph,
   1416                      CharInfoPtr * ppci, void *pglyphBase)
   1417 {
   1418     GCOP_UNWRAP(pGC);
   1419     RL_DEBUG_MSG("polyglyph start ");
   1420 
   1421     RootlessStartDrawing((WindowPtr) dst);
   1422     pGC->ops->PolyGlyphBlt(dst, pGC, x, y, nglyph, ppci, pglyphBase);
   1423 
   1424     if (nglyph > 0) {
   1425         BoxRec box;
   1426 
   1427         /* ugh */
   1428         box.x1 = dst->x + x + ppci[0]->metrics.leftSideBearing;
   1429         box.x2 = dst->x + x + ppci[nglyph - 1]->metrics.rightSideBearing;
   1430 
   1431         if (nglyph > 1) {
   1432             int width = 0;
   1433 
   1434             while (--nglyph) {
   1435                 width += (*ppci)->metrics.characterWidth;
   1436                 ppci++;
   1437             }
   1438 
   1439             if (width > 0)
   1440                 box.x2 += width;
   1441             else
   1442                 box.x1 += width;
   1443         }
   1444 
   1445         box.y1 = dst->y + y - FONTMAXBOUNDS(pGC->font, ascent);
   1446         box.y2 = dst->y + y + FONTMAXBOUNDS(pGC->font, descent);
   1447 
   1448         TRIM_BOX(box, pGC);
   1449         if (BOX_NOT_EMPTY(box))
   1450             RootlessDamageBox((WindowPtr) dst, &box);
   1451     }
   1452 
   1453     GCOP_WRAP(pGC);
   1454     RL_DEBUG_MSG("polyglyph end\n");
   1455 }
   1456 
   1457 /* changed area is in dest */
   1458 static void
   1459 RootlessPushPixels(GCPtr pGC, PixmapPtr pBitMap, DrawablePtr dst,
   1460                    int dx, int dy, int xOrg, int yOrg)
   1461 {
   1462     BoxRec box;
   1463 
   1464     GCOP_UNWRAP(pGC);
   1465     RL_DEBUG_MSG("push pixels start ");
   1466 
   1467     RootlessStartDrawing((WindowPtr) dst);
   1468     pGC->ops->PushPixels(pGC, pBitMap, dst, dx, dy, xOrg, yOrg);
   1469 
   1470     box.x1 = xOrg + dst->x;
   1471     box.x2 = box.x1 + dx;
   1472     box.y1 = yOrg + dst->y;
   1473     box.y2 = box.y1 + dy;
   1474 
   1475     TRIM_BOX(box, pGC);
   1476     if (BOX_NOT_EMPTY(box))
   1477         RootlessDamageBox((WindowPtr) dst, &box);
   1478 
   1479     GCOP_WRAP(pGC);
   1480     RL_DEBUG_MSG("push pixels end\n");
   1481 }