]> git.sesse.net Git - rdpsrv/blobdiff - Xserver/programs/Xserver/hw/vnc/draw.c
Import X server from vnc-3.3.7.
[rdpsrv] / Xserver / programs / Xserver / hw / vnc / draw.c
diff --git a/Xserver/programs/Xserver/hw/vnc/draw.c b/Xserver/programs/Xserver/hw/vnc/draw.c
new file mode 100644 (file)
index 0000000..599e313
--- /dev/null
@@ -0,0 +1,1998 @@
+/*
+ * draw.c - drawing routines for the RFB X server.  This is a set of
+ * wrappers around the standard MI/MFB/CFB drawing routines which work out
+ * to a fair approximation the region of the screen being modified by the
+ * drawing.  If the RFB client is ready then the modified region of the screen
+ * is sent to the client, otherwise the modified region will simply grow with
+ * each drawing request until the client is ready.
+ */
+
+/*
+ *  Copyright (C) 2002-2003 RealVNC Ltd.
+ *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+ *
+ *  This is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This software is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this software; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ *  USA.
+ */
+
+/*
+
+Copyright (c) 1989  X Consortium
+
+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
+X CONSORTIUM 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 of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+*/
+
+#include <stdio.h>
+#include "scrnintstr.h"
+#include "gcstruct.h"
+#include "windowstr.h"
+#include "regionstr.h"
+#include "dixfontstr.h"
+#include "rfb.h"
+#include "mfb.h"
+
+extern WindowPtr *WindowTable; /* Why isn't this in a header file? */
+
+int rfbDeferUpdateTime = 40; /* ms */
+
+/* MAX_RECTS_PER_OP is the maximum number of rectangles we generate from
+   operations like Polylines and PolySegment.  If the operation is more complex
+   than this, we simply use the bounding box.  Ideally it would be a
+   command-line option, but that would involve an extra malloc each time, so we
+   fix it here. */
+#define MAX_RECTS_PER_OP 5
+
+
+/****************************************************************************/
+/*
+ * Macro definitions
+ */
+/****************************************************************************/
+
+/* SLIGHTLY DIRTY HACK - use Composite Clip region calculated by mfb */
+
+#define WINDOW_CLIP_REGION(_w, _gc) \
+  (((mfbPrivGCPtr)((_gc)->devPrivates[mfbGCPrivateIndex].ptr))->pCompositeClip)
+
+#define TRC(x) if (rfbTrace) rfbLog x
+
+/* ADD_TO_MODIFIED_REGION adds the given region to the modified region for each
+   client */
+
+#define ADD_TO_MODIFIED_REGION(pScreen,reg)                                   \
+  {                                                                           \
+      rfbClientPtr cl;                                                        \
+      for (cl = rfbClientHead; cl; cl = cl->next) {                           \
+          if (REGION_NUM_RECTS(&cl->modifiedRegion) > rfbMaxRects) {          \
+              BoxRec boundingBox = *(REGION_EXTENTS((pScreen),                \
+                                                    &cl->modifiedRegion));    \
+              REGION_RESET((pScreen), &cl->modifiedRegion, &boundingBox);     \
+          }                                                                   \
+                                                                              \
+         REGION_UNION((pScreen),&cl->modifiedRegion,&cl->modifiedRegion,reg);\
+      }                                                                       \
+  }
+
+/* SCHEDULE_FB_UPDATE is used at the end of each drawing routine to schedule an
+   update to be sent to each client if there is one pending and the client is
+   ready for it.  */
+
+#define SCHEDULE_FB_UPDATE(pScreen,prfb)                               \
+  if (!prfb->dontSendFramebufferUpdate) {                              \
+      rfbClientPtr cl, nextCl;                                         \
+      for (cl = rfbClientHead; cl; cl = nextCl) {                      \
+         nextCl = cl->next;                                            \
+         if (!cl->deferredUpdateScheduled && FB_UPDATE_PENDING(cl) &&  \
+             REGION_NOTEMPTY(pScreen,&cl->requestedRegion))            \
+         {                                                             \
+             rfbScheduleDeferredUpdate(cl);                            \
+         }                                                             \
+      }                                                                        \
+  }
+
+/* function prototypes */
+
+static void rfbCopyRegion(ScreenPtr pScreen, rfbClientPtr cl,
+                         RegionPtr src, RegionPtr dst, int dx, int dy);
+
+/* GC funcs */
+
+static void rfbValidateGC(GCPtr, unsigned long /*changes*/, DrawablePtr);
+static void rfbChangeGC(GCPtr, unsigned long /*mask*/);
+static void rfbCopyGC(GCPtr /*src*/, unsigned long /*mask*/, GCPtr /*dst*/);
+static void rfbDestroyGC(GCPtr);
+static void rfbChangeClip(GCPtr, int /*type*/, pointer /*pValue*/,
+                         int /*nrects*/);
+static void rfbDestroyClip(GCPtr);
+static void rfbCopyClip(GCPtr /*dst*/, GCPtr /*src*/);
+
+/* GC ops */
+
+static void rfbFillSpans();
+static void rfbSetSpans();
+static void rfbPutImage();
+static RegionPtr rfbCopyArea();
+static RegionPtr rfbCopyPlane();
+static void rfbPolyPoint();
+static void rfbPolylines();
+static void rfbPolySegment();
+static void rfbPolyRectangle();
+static void rfbPolyArc();
+static void rfbFillPolygon();
+static void rfbPolyFillRect();
+static void rfbPolyFillArc();
+static int rfbPolyText8();
+static int rfbPolyText16();
+static void rfbImageText8();
+static void rfbImageText16();
+static void rfbImageGlyphBlt();
+static void rfbPolyGlyphBlt();
+static void rfbPushPixels();
+
+
+static GCFuncs rfbGCFuncs = {
+    rfbValidateGC,
+    rfbChangeGC,
+    rfbCopyGC,
+    rfbDestroyGC,
+    rfbChangeClip,
+    rfbDestroyClip,
+    rfbCopyClip,
+};
+
+
+static GCOps rfbGCOps = {
+    rfbFillSpans,      rfbSetSpans,    rfbPutImage,    
+    rfbCopyArea,       rfbCopyPlane,   rfbPolyPoint,
+    rfbPolylines,      rfbPolySegment, rfbPolyRectangle,
+    rfbPolyArc,                rfbFillPolygon, rfbPolyFillRect,
+    rfbPolyFillArc,    rfbPolyText8,   rfbPolyText16,
+    rfbImageText8,     rfbImageText16, rfbImageGlyphBlt,
+    rfbPolyGlyphBlt,   rfbPushPixels
+};
+
+
+
+/****************************************************************************/
+/*
+ * Screen functions wrapper stuff
+ */
+/****************************************************************************/
+
+#define SCREEN_PROLOGUE(scrn, field)           \
+    ScreenPtr pScreen = scrn;                  \
+    rfbScreenInfoPtr prfb = &rfbScreen;                \
+    pScreen->field = prfb->field;
+
+#define SCREEN_EPILOGUE(field, wrapper) \
+    pScreen->field = wrapper;
+
+
+/*
+ * CloseScreen wrapper -- unwrap everything, free the private data
+ * and call the wrapped CloseScreen function.
+ */
+
+Bool
+rfbCloseScreen (i, pScreen)
+    int i;
+    ScreenPtr  pScreen;
+{
+    rfbScreenInfoPtr prfb = &rfbScreen;
+
+    pScreen->CloseScreen = prfb->CloseScreen;
+    pScreen->CreateGC = prfb->CreateGC;
+    pScreen->PaintWindowBackground = prfb->PaintWindowBackground;
+    pScreen->PaintWindowBorder = prfb->PaintWindowBorder;
+    pScreen->CopyWindow = prfb->CopyWindow;
+    pScreen->ClearToBackground = prfb->ClearToBackground;
+    pScreen->RestoreAreas = prfb->RestoreAreas;
+
+    TRC(("Unwrapped screen functions\n"));
+
+    return (*pScreen->CloseScreen) (i, pScreen);
+}
+
+/*
+ * CreateGC - wrap the GC funcs (the GC ops will be wrapped when the GC
+ * func "ValidateGC" is called).
+ */
+
+Bool
+rfbCreateGC (pGC)
+    GCPtr   pGC;
+{
+    Bool ret;
+    rfbGCPtr pGCPriv;
+
+    SCREEN_PROLOGUE(pGC->pScreen,CreateGC);
+    
+    pGCPriv = (rfbGCPtr)pGC->devPrivates[rfbGCIndex].ptr;
+
+    ret = (*pScreen->CreateGC) (pGC);
+
+    TRC(("rfbCreateGC called\n"));
+
+    pGCPriv->wrapOps = NULL;
+    pGCPriv->wrapFuncs = pGC->funcs;
+    pGC->funcs = &rfbGCFuncs;
+
+    SCREEN_EPILOGUE(CreateGC,rfbCreateGC);
+
+    return ret;
+}
+
+/*
+ * PaintWindowBackground - the region being modified is just the given region.
+ */
+
+void
+rfbPaintWindowBackground (pWin, pRegion, what)
+    WindowPtr  pWin;
+    RegionPtr  pRegion;
+    int                what;
+{
+    SCREEN_PROLOGUE(pWin->drawable.pScreen,PaintWindowBackground);
+
+    TRC(("rfbPaintWindowBackground called\n"));
+
+    ADD_TO_MODIFIED_REGION(pScreen,pRegion);
+
+    (*pScreen->PaintWindowBackground) (pWin, pRegion, what);
+
+    SCHEDULE_FB_UPDATE(pScreen, prfb);
+
+    SCREEN_EPILOGUE(PaintWindowBackground,rfbPaintWindowBackground);
+}
+
+/*
+ * PaintWindowBorder - the region being modified is just the given region.
+ */
+
+void
+rfbPaintWindowBorder (pWin, pRegion, what)
+    WindowPtr  pWin;
+    RegionPtr  pRegion;
+    int                what;
+{
+    SCREEN_PROLOGUE(pWin->drawable.pScreen,PaintWindowBorder);
+
+    TRC(("rfbPaintWindowBorder called\n"));
+
+    ADD_TO_MODIFIED_REGION(pScreen,pRegion);
+
+    (*pScreen->PaintWindowBorder) (pWin, pRegion, what);
+
+    SCHEDULE_FB_UPDATE(pScreen, prfb);
+
+    SCREEN_EPILOGUE(PaintWindowBorder,rfbPaintWindowBorder);
+}
+
+/*
+ * CopyWindow - the region being modified is the translation of the old
+ * region, clipped to the border clip region of the window.  Note that any
+ * parts of the window which have become newly-visible will not be affected by
+ * this call - a separate PaintWindowBackground/Border will be called to do
+ * that.  If the client will accept CopyRect messages then use rfbCopyRegion to
+ * optimise the pending screen changes into a single "copy region" plus the
+ * ordinary modified region.
+ */
+
+void
+rfbCopyWindow (pWin, ptOldOrg, pOldRegion)
+    WindowPtr  pWin;
+    DDXPointRec        ptOldOrg;
+    RegionPtr  pOldRegion;
+{
+    rfbClientPtr cl;
+    RegionRec srcRegion, dstRegion;
+    SCREEN_PROLOGUE(pWin->drawable.pScreen,CopyWindow);
+
+    TRC(("rfbCopyWindow called\n"));
+
+    REGION_INIT(pScreen,&dstRegion,NullBox,0);
+    REGION_COPY(pScreen,&dstRegion,pOldRegion);
+    REGION_TRANSLATE(pWin->drawable.pScreen, &dstRegion,
+                    pWin->drawable.x - ptOldOrg.x,
+                    pWin->drawable.y - ptOldOrg.y);
+    REGION_INTERSECT(pWin->drawable.pScreen, &dstRegion, &dstRegion,
+                    &pWin->borderClip);
+
+    for (cl = rfbClientHead; cl; cl = cl->next) {
+       if (cl->useCopyRect) {
+           REGION_INIT(pScreen,&srcRegion,NullBox,0);
+           REGION_COPY(pScreen,&srcRegion,pOldRegion);
+
+           rfbCopyRegion(pScreen, cl, &srcRegion, &dstRegion,
+                         pWin->drawable.x - ptOldOrg.x,
+                         pWin->drawable.y - ptOldOrg.y);
+
+           REGION_UNINIT(pSrc->pScreen, &srcRegion);
+
+       } else {
+
+           REGION_UNION(pScreen, &cl->modifiedRegion, &cl->modifiedRegion,
+                        &dstRegion);
+       }
+    }
+
+    REGION_UNINIT(pSrc->pScreen, &dstRegion);
+
+    (*pScreen->CopyWindow) (pWin, ptOldOrg, pOldRegion);
+
+    SCHEDULE_FB_UPDATE(pScreen, prfb);
+
+    SCREEN_EPILOGUE(CopyWindow,rfbCopyWindow);
+}
+
+/*
+ * ClearToBackground - when generateExposures is false, the region being
+ * modified is the given rectangle (clipped to the "window clip region").
+ */
+
+void
+rfbClearToBackground (pWin, x, y, w, h, generateExposures)
+    WindowPtr pWin;
+    int x,y,w,h;
+    Bool generateExposures;
+{
+    RegionRec tmpRegion;
+    BoxRec box;
+    SCREEN_PROLOGUE(pWin->drawable.pScreen,ClearToBackground);
+
+    TRC(("rfbClearToBackground called\n"));
+
+    if (!generateExposures) {
+       box.x1 = x + pWin->drawable.x;
+       box.y1 = y + pWin->drawable.y;
+       box.x2 = w ? (box.x1 + w) : (pWin->drawable.x + pWin->drawable.width);
+       box.y2 = h ? (box.y1 + h) : (pWin->drawable.y + pWin->drawable.height);
+
+       SAFE_REGION_INIT(pScreen, &tmpRegion, &box, 0);
+
+       REGION_INTERSECT(pScreen, &tmpRegion, &tmpRegion, &pWin->clipList);
+
+       ADD_TO_MODIFIED_REGION(pScreen, &tmpRegion);
+
+       REGION_UNINIT(pScreen, &tmpRegion);
+    }
+
+    (*pScreen->ClearToBackground) (pWin, x, y, w, h, generateExposures);
+
+    if (!generateExposures) {
+       SCHEDULE_FB_UPDATE(pScreen, prfb);
+    }
+
+    SCREEN_EPILOGUE(ClearToBackground,rfbClearToBackground);
+}
+
+/*
+ * RestoreAreas - just be safe here - the region being modified is the whole
+ * exposed region.
+ */
+
+RegionPtr
+rfbRestoreAreas (pWin, prgnExposed)
+    WindowPtr  pWin;
+    RegionPtr  prgnExposed;
+{
+    RegionPtr result;
+    SCREEN_PROLOGUE(pWin->drawable.pScreen,RestoreAreas);
+
+    TRC(("rfbRestoreAreas called\n"));
+
+    ADD_TO_MODIFIED_REGION(pScreen, prgnExposed);
+
+    result = (*pScreen->RestoreAreas) (pWin, prgnExposed);
+
+    SCHEDULE_FB_UPDATE(pScreen, prfb);
+
+    SCREEN_EPILOGUE(RestoreAreas,rfbRestoreAreas);
+
+    return result;
+}
+
+
+
+/****************************************************************************/
+/*
+ * GC funcs wrapper stuff
+ *
+ * We only really want to wrap the GC ops, but to do this we need to wrap
+ * ValidateGC and so all the other GC funcs must be wrapped as well.
+ */
+/****************************************************************************/
+
+#define GC_FUNC_PROLOGUE(pGC)                                          \
+    rfbGCPtr pGCPriv = (rfbGCPtr) (pGC)->devPrivates[rfbGCIndex].ptr;  \
+    (pGC)->funcs = pGCPriv->wrapFuncs;                                 \
+    if (pGCPriv->wrapOps)                                              \
+       (pGC)->ops = pGCPriv->wrapOps;
+
+#define GC_FUNC_EPILOGUE(pGC)          \
+    pGCPriv->wrapFuncs = (pGC)->funcs; \
+    (pGC)->funcs = &rfbGCFuncs;                \
+    if (pGCPriv->wrapOps) {            \
+       pGCPriv->wrapOps = (pGC)->ops;  \
+       (pGC)->ops = &rfbGCOps;         \
+    }
+
+
+/*
+ * ValidateGC - call the wrapped ValidateGC, then wrap the resulting GC ops if
+ * the drawing will be to a viewable window.
+ */
+
+static void
+rfbValidateGC (pGC, changes, pDrawable)
+    GCPtr      pGC;
+    unsigned long changes;
+    DrawablePtr        pDrawable;
+{
+    GC_FUNC_PROLOGUE(pGC);
+
+    TRC(("rfbValidateGC called\n"));
+
+    (*pGC->funcs->ValidateGC) (pGC, changes, pDrawable);
+    
+    pGCPriv->wrapOps = NULL;
+    if (pDrawable->type == DRAWABLE_WINDOW && ((WindowPtr)pDrawable)->viewable)
+    {
+       WindowPtr   pWin = (WindowPtr) pDrawable;
+       RegionPtr   pRegion = &pWin->clipList;
+
+       if (pGC->subWindowMode == IncludeInferiors)
+           pRegion = &pWin->borderClip;
+       if (REGION_NOTEMPTY(pDrawable->pScreen, pRegion)) {
+           pGCPriv->wrapOps = pGC->ops;
+           TRC(("rfbValidateGC: wrapped GC ops\n"));
+       }
+    }
+
+    GC_FUNC_EPILOGUE(pGC);
+}
+
+/*
+ * All other GC funcs simply unwrap the GC funcs and ops, call the wrapped
+ * function and then rewrap the funcs and ops.
+ */
+
+static void
+rfbChangeGC (pGC, mask)
+    GCPtr          pGC;
+    unsigned long   mask;
+{
+    GC_FUNC_PROLOGUE(pGC);
+    (*pGC->funcs->ChangeGC) (pGC, mask);
+    GC_FUNC_EPILOGUE(pGC);
+}
+
+static void
+rfbCopyGC (pGCSrc, mask, pGCDst)
+    GCPtr          pGCSrc, pGCDst;
+    unsigned long   mask;
+{
+    GC_FUNC_PROLOGUE(pGCDst);
+    (*pGCDst->funcs->CopyGC) (pGCSrc, mask, pGCDst);
+    GC_FUNC_EPILOGUE(pGCDst);
+}
+
+static void
+rfbDestroyGC (pGC)
+    GCPtr   pGC;
+{
+    GC_FUNC_PROLOGUE(pGC);
+    (*pGC->funcs->DestroyGC) (pGC);
+    GC_FUNC_EPILOGUE(pGC);
+}
+
+static void
+rfbChangeClip (pGC, type, pvalue, nrects)
+    GCPtr   pGC;
+    int                type;
+    pointer    pvalue;
+    int                nrects;
+{
+    GC_FUNC_PROLOGUE(pGC);
+    (*pGC->funcs->ChangeClip) (pGC, type, pvalue, nrects);
+    GC_FUNC_EPILOGUE(pGC);
+}
+
+static void
+rfbDestroyClip(pGC)
+    GCPtr      pGC;
+{
+    GC_FUNC_PROLOGUE(pGC);
+    (* pGC->funcs->DestroyClip)(pGC);
+    GC_FUNC_EPILOGUE(pGC);
+}
+
+static void
+rfbCopyClip(pgcDst, pgcSrc)
+    GCPtr pgcDst, pgcSrc;
+{
+    GC_FUNC_PROLOGUE(pgcDst);
+    (* pgcDst->funcs->CopyClip)(pgcDst, pgcSrc);
+    GC_FUNC_EPILOGUE(pgcDst);
+}
+
+
+/****************************************************************************/
+/*
+ * GC ops wrapper stuff
+ *
+ * Note that these routines will only have been wrapped for drawing to
+ * viewable windows so we don't need to check each time that the drawable
+ * is a viewable window.
+ */
+/****************************************************************************/
+
+#define GC_OP_PROLOGUE(pDrawable,pGC) \
+    rfbScreenInfoPtr prfb = &rfbScreen; \
+    rfbGCPtr pGCPrivate = (rfbGCPtr) (pGC)->devPrivates[rfbGCIndex].ptr; \
+    GCFuncs *oldFuncs = pGC->funcs; \
+    (pGC)->funcs = pGCPrivate->wrapFuncs; \
+    (pGC)->ops = pGCPrivate->wrapOps;
+
+#define GC_OP_EPILOGUE(pGC) \
+    pGCPrivate->wrapOps = (pGC)->ops; \
+    (pGC)->funcs = oldFuncs; \
+    (pGC)->ops = &rfbGCOps;
+
+
+/*
+ * FillSpans - being very safe - the region being modified is the border clip
+ * region of the window.
+ */
+
+static void
+rfbFillSpans(pDrawable, pGC, nInit, pptInit, pwidthInit, fSorted)
+    DrawablePtr pDrawable;
+    GCPtr      pGC;
+    int                nInit;                  /* number of spans to fill */
+    DDXPointPtr pptInit;               /* pointer to list of start points */
+    int                *pwidthInit;            /* pointer to list of n widths */
+    int        fSorted;
+{
+    GC_OP_PROLOGUE(pDrawable,pGC);
+
+    TRC(("rfbFillSpans called\n"));
+
+    ADD_TO_MODIFIED_REGION(pDrawable->pScreen,
+                          &((WindowPtr)pDrawable)->borderClip);
+
+    (*pGC->ops->FillSpans) (pDrawable, pGC, nInit, pptInit,pwidthInit,fSorted);
+
+    SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
+
+    GC_OP_EPILOGUE(pGC);
+}
+
+/*
+ * SetSpans - being very safe - the region being modified is the border clip
+ * region of the window.
+ */
+
+static void
+rfbSetSpans(pDrawable, pGC, psrc, ppt, pwidth, nspans, fSorted)
+    DrawablePtr                pDrawable;
+    GCPtr              pGC;
+    char               *psrc;
+    register DDXPointPtr ppt;
+    int                        *pwidth;
+    int                        nspans;
+    int                        fSorted;
+{
+    GC_OP_PROLOGUE(pDrawable,pGC);
+
+    TRC(("rfbSetSpans called\n"));
+
+    ADD_TO_MODIFIED_REGION(pDrawable->pScreen,
+                          &((WindowPtr)pDrawable)->borderClip);
+
+    (*pGC->ops->SetSpans) (pDrawable, pGC, psrc, ppt, pwidth, nspans, fSorted);
+
+    SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
+
+    GC_OP_EPILOGUE(pGC);
+}
+
+/*
+ * PutImage - the region being modified is the rectangle of the
+ * PutImage (clipped to the window clip region).
+ */
+
+static void
+rfbPutImage(pDrawable, pGC, depth, x, y, w, h, leftPad, format, pBits)
+    DrawablePtr          pDrawable;
+    GCPtr        pGC;
+    int                  depth;
+    int                  x;
+    int                  y;
+    int                  w;
+    int                  h;
+    int                  leftPad;
+    int                  format;
+    char         *pBits;
+{
+    RegionRec tmpRegion;
+    BoxRec box;
+    GC_OP_PROLOGUE(pDrawable, pGC);
+
+    TRC(("rfbPutImage called\n"));
+
+    box.x1 = x + pDrawable->x;
+    box.y1 = y + pDrawable->y;
+    box.x2 = box.x1 + w;
+    box.y2 = box.y1 + h;
+
+    SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0);
+
+    REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion,
+                    WINDOW_CLIP_REGION((WindowPtr)pDrawable,pGC));
+
+    ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion);
+
+    REGION_UNINIT(pDrawable->pScreen, &tmpRegion);
+
+    (*pGC->ops->PutImage) (pDrawable, pGC, depth, x, y, w, h,
+                          leftPad, format, pBits);
+
+    SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
+
+    GC_OP_EPILOGUE(pGC);
+}
+
+/*
+ * CopyArea - the region being modified is the destination rectangle (clipped
+ * to the window clip region).
+ * If the client will accept CopyRect messages then use rfbCopyRegion
+ * to optimise the pending screen changes into a single "copy region" plus
+ * the ordinary modified region.
+ */
+
+static RegionPtr
+rfbCopyArea (pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty)
+    DrawablePtr          pSrc;
+    DrawablePtr          pDst;
+    GCPtr        pGC;
+    int                  srcx;
+    int                  srcy;
+    int                  w;
+    int                  h;
+    int                  dstx;
+    int                  dsty;
+{
+    rfbClientPtr cl;
+    RegionPtr rgn;
+    RegionRec srcRegion, dstRegion;
+    BoxRec box;
+    GC_OP_PROLOGUE(pDst, pGC);
+
+    TRC(("rfbCopyArea called\n"));
+
+    box.x1 = dstx + pDst->x;
+    box.y1 = dsty + pDst->y;
+    box.x2 = box.x1 + w;
+    box.y2 = box.y1 + h;
+
+    SAFE_REGION_INIT(pDst->pScreen, &dstRegion, &box, 0);
+    REGION_INTERSECT(pDst->pScreen, &dstRegion, &dstRegion,
+                    WINDOW_CLIP_REGION((WindowPtr)pDst,pGC));
+
+    if ((pSrc->type == DRAWABLE_WINDOW) && (pSrc->pScreen == pDst->pScreen)) {
+       box.x1 = srcx + pSrc->x;
+       box.y1 = srcy + pSrc->y;
+       box.x2 = box.x1 + w;
+       box.y2 = box.y1 + h;
+
+       for (cl = rfbClientHead; cl; cl = cl->next) {
+           if (cl->useCopyRect) {
+               SAFE_REGION_INIT(pSrc->pScreen, &srcRegion, &box, 0);
+               REGION_INTERSECT(pSrc->pScreen, &srcRegion, &srcRegion,
+                                &((WindowPtr)pSrc)->clipList);
+
+               rfbCopyRegion(pSrc->pScreen, cl, &srcRegion, &dstRegion,
+                             dstx + pDst->x - srcx - pSrc->x,
+                             dsty + pDst->y - srcy - pSrc->y);
+
+               REGION_UNINIT(pSrc->pScreen, &srcRegion);
+
+           } else {
+
+               REGION_UNION(pScreen, &cl->modifiedRegion, &cl->modifiedRegion,
+                            &dstRegion);
+           }
+       }
+
+    } else {
+
+       ADD_TO_MODIFIED_REGION(pDst->pScreen, &dstRegion);
+    }
+
+    REGION_UNINIT(pDst->pScreen, &dstRegion);
+
+    rgn = (*pGC->ops->CopyArea) (pSrc, pDst, pGC, srcx, srcy, w, h,
+                                dstx, dsty);
+
+    SCHEDULE_FB_UPDATE(pDst->pScreen, prfb);
+
+    GC_OP_EPILOGUE(pGC);
+
+    return rgn;
+}
+
+
+/*
+ * CopyPlane - the region being modified is the destination rectangle (clipped
+ * to the window clip region).
+ */
+
+static RegionPtr
+rfbCopyPlane (pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty, plane)
+    DrawablePtr          pSrc;
+    DrawablePtr          pDst;
+    register GCPtr pGC;
+    int          srcx,
+                 srcy;
+    int          w,
+                 h;
+    int          dstx,
+                 dsty;
+    unsigned long  plane;
+{
+    RegionPtr rgn;
+    RegionRec tmpRegion;
+    BoxRec box;
+    GC_OP_PROLOGUE(pDst, pGC);
+
+    TRC(("rfbCopyPlane called\n"));
+
+    box.x1 = dstx + pDst->x;
+    box.y1 = dsty + pDst->y;
+    box.x2 = box.x1 + w;
+    box.y2 = box.y1 + h;
+
+    SAFE_REGION_INIT(pDst->pScreen, &tmpRegion, &box, 0);
+
+    REGION_INTERSECT(pDst->pScreen, &tmpRegion, &tmpRegion,
+                    WINDOW_CLIP_REGION((WindowPtr)pDst,pGC));
+
+    ADD_TO_MODIFIED_REGION(pDst->pScreen, &tmpRegion);
+
+    REGION_UNINIT(pDst->pScreen, &tmpRegion);
+
+    rgn = (*pGC->ops->CopyPlane) (pSrc, pDst, pGC, srcx, srcy, w, h,
+                                 dstx, dsty, plane);
+
+    SCHEDULE_FB_UPDATE(pDst->pScreen, prfb);
+
+    GC_OP_EPILOGUE(pGC);
+
+    return rgn;
+}
+
+/*
+ * PolyPoint - find the smallest rectangle which encloses the points drawn
+ * (and clip).
+ */
+
+static void
+rfbPolyPoint (pDrawable, pGC, mode, npt, pts)
+    DrawablePtr pDrawable;
+    GCPtr      pGC;
+    int                mode;           /* Origin or Previous */
+    int                npt;
+    xPoint     *pts;
+{
+    int i;
+    RegionRec tmpRegion;
+    BoxRec box;
+    GC_OP_PROLOGUE(pDrawable, pGC);
+
+    TRC(("rfbPolyPoint called: %d points\n",npt));
+
+    if (npt) {
+       int minX = pts[0].x, maxX = pts[0].x;
+       int minY = pts[0].y, maxY = pts[0].y;
+
+       if (mode == CoordModePrevious)
+       {
+           int x = pts[0].x, y = pts[0].y;
+
+           for (i = 1; i < npt; i++) {
+               x += pts[i].x;
+               y += pts[i].y;
+               if (x < minX) minX = x;
+               if (x > maxX) maxX = x;
+               if (y < minY) minY = y;
+               if (y > maxY) maxY = y;
+           }
+       }
+       else
+       {
+           for (i = 1; i < npt; i++) {
+               if (pts[i].x < minX) minX = pts[i].x;
+               if (pts[i].x > maxX) maxX = pts[i].x;
+               if (pts[i].y < minY) minY = pts[i].y;
+               if (pts[i].y > maxY) maxY = pts[i].y;
+           }
+       }
+
+       box.x1 = minX + pDrawable->x;
+       box.y1 = minY + pDrawable->y;
+       box.x2 = maxX + 1 + pDrawable->x;
+       box.y2 = maxY + 1 + pDrawable->y;
+
+       SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0);
+
+       REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion,
+                        WINDOW_CLIP_REGION(((WindowPtr)pDrawable),pGC));
+
+       ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion);
+
+       REGION_UNINIT(pDrawable->pScreen, &tmpRegion);
+    }
+
+    (*pGC->ops->PolyPoint) (pDrawable, pGC, mode, npt, pts);
+
+    if (npt) {
+       SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
+    }
+
+    GC_OP_EPILOGUE(pGC);
+}
+
+/*
+ * PolyLines - take the union of bounding boxes around each line (and clip).
+ */
+
+static void
+rfbPolylines (pDrawable, pGC, mode, npt, ppts)
+    DrawablePtr          pDrawable;
+    GCPtr        pGC;
+    int                  mode;
+    int                  npt;
+    DDXPointPtr          ppts;
+{
+    RegionPtr tmpRegion;
+    xRectangle regRects[MAX_RECTS_PER_OP];
+    int i, extra, nregRects, lw;
+    int prevX, prevY, curX, curY;
+    int rectX1, rectY1, rectX2, rectY2;
+    int minX, minY, maxX, maxY;
+    GC_OP_PROLOGUE(pDrawable, pGC);
+
+    TRC(("rfbPolylines called: %d points\n",npt));
+
+    if (npt) {
+       lw = pGC->lineWidth;
+       if (lw == 0)
+           lw = 1;
+
+       if (npt == 1)
+       {
+           nregRects = 1;
+           regRects[0].x = ppts[0].x - lw + pDrawable->x; /* being safe */
+           regRects[0].y = ppts[0].y - lw + pDrawable->y;
+           regRects[0].width = 2*lw;
+           regRects[0].height = 2*lw;
+       }
+       else
+       {
+           nregRects = npt - 1;
+
+           /*
+            * mitered joins can project quite a way from
+            * the line end; the 11 degree miter limit limits
+            * this extension to lw / (2 * tan(11/2)), rounded up
+            * and converted to int yields 6 * lw
+            */
+
+           if (pGC->joinStyle == JoinMiter) {
+               extra = 6 * lw;
+           } else {
+               extra = lw / 2;
+           }
+
+           prevX = ppts[0].x + pDrawable->x;
+           prevY = ppts[0].y + pDrawable->y;
+            minX = maxX = prevX;
+            minY = maxY = prevY;
+
+           for (i = 0; i < nregRects; i++) {
+               if (mode == CoordModeOrigin) {
+                   curX = pDrawable->x + ppts[i+1].x;
+                   curY = pDrawable->y + ppts[i+1].y;
+               } else {
+                   curX = prevX + ppts[i+1].x;
+                   curY = prevY + ppts[i+1].y;
+               }
+
+               if (prevX > curX) {
+                    rectX1 = curX - extra;
+                    rectX2 = prevX + extra + 1;
+               } else {
+                    rectX1 = prevX - extra;
+                    rectX2 = curX + extra + 1;
+               }
+
+               if (prevY > curY) {
+                    rectY1 = curY - extra;
+                    rectY2 = prevY + extra + 1;
+               } else {
+                    rectY1 = prevY - extra;
+                    rectY2 = curY + extra + 1;
+               }
+
+                if (nregRects <= MAX_RECTS_PER_OP) {
+                   regRects[i].x = rectX1;
+                   regRects[i].y = rectY1;
+                   regRects[i].width = rectX2 - rectX1;
+                   regRects[i].height = rectY2 - rectY1;
+                } else {
+                    if (rectX1 < minX) minX = rectX1;
+                    if (rectY1 < minY) minY = rectY1;
+                    if (rectX2 > maxX) maxX = rectX2;
+                    if (rectY2 > maxY) maxY = rectY2;
+                }
+
+               prevX = curX;
+               prevY = curY;
+           }
+
+            if (nregRects > MAX_RECTS_PER_OP) {
+                regRects[0].x = minX;
+                regRects[0].y = minY;
+                regRects[0].width = maxX - minX;
+                regRects[0].height = maxY - minY;
+                nregRects = 1;
+            }
+       }
+
+       tmpRegion = RECTS_TO_REGION(pDrawable->pScreen, nregRects, regRects,
+                                    CT_NONE);
+       REGION_INTERSECT(pDrawable->pScreen, tmpRegion, tmpRegion,
+                        WINDOW_CLIP_REGION((WindowPtr)pDrawable,pGC));
+
+       ADD_TO_MODIFIED_REGION(pDrawable->pScreen, tmpRegion);
+
+       REGION_DESTROY(pDrawable->pScreen, tmpRegion);
+    }
+
+    (*pGC->ops->Polylines) (pDrawable, pGC, mode, npt, ppts);
+
+    if (npt) {
+       SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
+    }
+
+    GC_OP_EPILOGUE(pGC);
+}
+
+/*
+ * PolySegment - take the union of bounding boxes around each segment (and
+ * clip).
+ */
+
+static void
+rfbPolySegment(pDrawable, pGC, nseg, segs)
+    DrawablePtr pDrawable;
+    GCPtr      pGC;
+    int                nseg;
+    xSegment   *segs;
+{
+    RegionPtr tmpRegion;
+    xRectangle regRects[MAX_RECTS_PER_OP];
+    int i, extra, lw, nregRects;
+    int rectX1, rectY1, rectX2, rectY2;
+    int minX, minY, maxX, maxY;
+
+    GC_OP_PROLOGUE(pDrawable, pGC);
+
+    TRC(("rfbPolySegment called: %d segments\n",nseg));
+
+    if (nseg) {
+        nregRects = nseg;
+       lw = pGC->lineWidth;
+       extra = lw / 2;
+
+        minX = maxX = segs[0].x1;
+        minY = maxY = segs[0].y1;
+
+       for (i = 0; i < nseg; i++)
+       {
+           if (segs[i].x1 > segs[i].x2) {
+                rectX1 = pDrawable->x + segs[i].x2 - extra;
+                rectX2 = pDrawable->x + segs[i].x1 + extra + 1;
+           } else {
+                rectX1 = pDrawable->x + segs[i].x1 - extra;
+                rectX2 = pDrawable->x + segs[i].x2 + extra + 1;
+           }
+
+           if (segs[i].y1 > segs[i].y2) {
+                rectY1 = pDrawable->y + segs[i].y2 - extra;
+                rectY2 = pDrawable->y + segs[i].y1 + extra + 1;
+           } else {
+                rectY1 = pDrawable->y + segs[i].y1 - extra;
+                rectY2 = pDrawable->y + segs[i].y2 + extra + 1;
+           }
+
+            if (nseg <= MAX_RECTS_PER_OP) {
+                regRects[i].x = rectX1;
+                regRects[i].y = rectY1;
+                regRects[i].width = rectX2 - rectX1;
+                regRects[i].height = rectY2 - rectY1;
+            } else {
+                if (rectX1 < minX) minX = rectX1;
+                if (rectY1 < minY) minY = rectY1;
+                if (rectX2 > maxX) maxX = rectX2;
+                if (rectY2 > maxY) maxY = rectY2;
+            }
+       }
+
+        if (nseg > MAX_RECTS_PER_OP) {
+            regRects[0].x = minX;
+            regRects[0].y = minY;
+            regRects[0].width = maxX - minX;
+            regRects[0].height = maxY - minY;
+            nregRects = 1;
+        }
+
+       tmpRegion = RECTS_TO_REGION(pDrawable->pScreen, nregRects, regRects,
+                                    CT_NONE);
+       REGION_INTERSECT(pDrawable->pScreen, tmpRegion, tmpRegion,
+                        WINDOW_CLIP_REGION((WindowPtr)pDrawable,pGC));
+
+       ADD_TO_MODIFIED_REGION(pDrawable->pScreen, tmpRegion);
+
+       REGION_DESTROY(pDrawable->pScreen, tmpRegion);
+    }
+
+    (*pGC->ops->PolySegment) (pDrawable, pGC, nseg, segs);
+
+    if (nseg) {
+       SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
+    }
+
+    GC_OP_EPILOGUE(pGC);
+}
+
+/*
+ * PolyRectangle (rectangle outlines) - take the union of bounding boxes
+ * around each line (and clip).
+ */
+
+static void
+rfbPolyRectangle(pDrawable, pGC, nrects, rects)
+    DrawablePtr        pDrawable;
+    GCPtr      pGC;
+    int                nrects;
+    xRectangle *rects;
+{
+    int i, extra, lw, nregRects;
+    int rectX1, rectY1, rectX2, rectY2;
+    int minX, minY, maxX, maxY;
+    RegionPtr tmpRegion;
+    xRectangle regRects[MAX_RECTS_PER_OP*4];
+    GC_OP_PROLOGUE(pDrawable, pGC);
+
+    TRC(("rfbPolyRectangle called: %d rects\n",nrects));
+
+    if (nrects) {
+        nregRects = nrects * 4;
+       lw = pGC->lineWidth;
+       extra = lw / 2;
+
+        minX = maxX = rects[0].x;
+        minY = maxY = rects[0].y;
+
+       for (i = 0; i < nrects; i++)
+       {
+            if (nrects <= MAX_RECTS_PER_OP) {
+                regRects[i*4].x = rects[i].x - extra + pDrawable->x;
+                regRects[i*4].y = rects[i].y - extra + pDrawable->y;
+                regRects[i*4].width = rects[i].width + 1 + 2 * extra;
+                regRects[i*4].height = 1 + 2 * extra;
+
+                regRects[i*4+1].x = rects[i].x - extra + pDrawable->x;
+                regRects[i*4+1].y = rects[i].y - extra + pDrawable->y;
+                regRects[i*4+1].width = 1 + 2 * extra;
+                regRects[i*4+1].height = rects[i].height + 1 + 2 * extra;
+
+                regRects[i*4+2].x
+                    = rects[i].x + rects[i].width - extra + pDrawable->x;
+                regRects[i*4+2].y = rects[i].y - extra + pDrawable->y;
+                regRects[i*4+2].width = 1 + 2 * extra;
+                regRects[i*4+2].height = rects[i].height + 1 + 2 * extra;
+
+                regRects[i*4+3].x = rects[i].x - extra + pDrawable->x;
+                regRects[i*4+3].y
+                    = rects[i].y + rects[i].height - extra + pDrawable->y;
+                regRects[i*4+3].width = rects[i].width + 1 + 2 * extra;
+                regRects[i*4+3].height = 1 + 2 * extra;
+            } else {
+                rectX1 = pDrawable->x + rects[i].x - extra;
+                rectY1 = pDrawable->y + rects[i].y - extra;
+                rectX2 = pDrawable->x + rects[i].x + rects[i].width + extra+1;
+                rectY2 = pDrawable->y + rects[i].y + rects[i].height + extra+1;
+                if (rectX1 < minX) minX = rectX1;
+                if (rectY1 < minY) minY = rectY1;
+                if (rectX2 > maxX) maxX = rectX2;
+                if (rectY2 > maxY) maxY = rectY2;
+            }
+       }
+
+        if (nrects > MAX_RECTS_PER_OP) {
+            regRects[0].x = minX;
+            regRects[0].y = minY;
+            regRects[0].width = maxX - minX;
+            regRects[0].height = maxY - minY;
+            nregRects = 1;
+        }
+
+       tmpRegion = RECTS_TO_REGION(pDrawable->pScreen, nregRects,
+                                   regRects, CT_NONE);
+       REGION_INTERSECT(pDrawable->pScreen, tmpRegion, tmpRegion,
+                        WINDOW_CLIP_REGION((WindowPtr)pDrawable,pGC));
+
+       ADD_TO_MODIFIED_REGION(pDrawable->pScreen, tmpRegion);
+
+       REGION_DESTROY(pDrawable->pScreen, tmpRegion);
+    }
+
+    (*pGC->ops->PolyRectangle) (pDrawable, pGC, nrects, rects);
+
+    if (nrects) {
+       SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
+    }
+
+    GC_OP_EPILOGUE(pGC);
+}
+
+/*
+ * PolyArc - take the union of bounding boxes around each arc (and clip).
+ * Bounding boxes assume each is a full circle / ellipse.
+ */
+
+static void
+rfbPolyArc(pDrawable, pGC, narcs, arcs)
+    DrawablePtr        pDrawable;
+    register GCPtr     pGC;
+    int                narcs;
+    xArc       *arcs;
+{
+    int i, extra, lw, nregRects;
+    int rectX1, rectY1, rectX2, rectY2;
+    int minX, minY, maxX, maxY;
+    RegionPtr tmpRegion;
+    xRectangle regRects[MAX_RECTS_PER_OP];
+    GC_OP_PROLOGUE(pDrawable, pGC);
+
+    TRC(("rfbPolyArc called: %d arcs\n",narcs));
+
+    if (narcs) {
+        nregRects = narcs;
+       lw = pGC->lineWidth;
+       if (lw == 0)
+           lw = 1;
+       extra = lw / 2;
+
+        minX = maxX = arcs[0].x;
+        minY = maxY = arcs[0].y;
+
+       for (i = 0; i < narcs; i++)
+       {
+            if (narcs <= MAX_RECTS_PER_OP) {
+                regRects[i].x = arcs[i].x - extra + pDrawable->x;
+                regRects[i].y = arcs[i].y - extra + pDrawable->y;
+                regRects[i].width = arcs[i].width + lw;
+                regRects[i].height = arcs[i].height + lw;
+            } else {
+                rectX1 = pDrawable->x + arcs[i].x - extra;
+                rectY1 = pDrawable->y + arcs[i].y - extra;
+                rectX2 = pDrawable->x + arcs[i].x + arcs[i].width + lw;
+                rectY2 = pDrawable->y + arcs[i].y + arcs[i].height + lw;
+                if (rectX1 < minX) minX = rectX1;
+                if (rectY1 < minY) minY = rectY1;
+                if (rectX2 > maxX) maxX = rectX2;
+                if (rectY2 > maxY) maxY = rectY2;
+            }
+       }
+
+        if (narcs > MAX_RECTS_PER_OP) {
+            regRects[0].x = minX;
+            regRects[0].y = minY;
+            regRects[0].width = maxX - minX;
+            regRects[0].height = maxY - minY;
+            nregRects = 1;
+        }
+
+       tmpRegion = RECTS_TO_REGION(pDrawable->pScreen, nregRects, regRects,
+                                    CT_NONE);
+       REGION_INTERSECT(pDrawable->pScreen, tmpRegion, tmpRegion,
+                        WINDOW_CLIP_REGION((WindowPtr)pDrawable,pGC));
+
+       ADD_TO_MODIFIED_REGION(pDrawable->pScreen, tmpRegion);
+
+       REGION_DESTROY(pDrawable->pScreen, tmpRegion);
+    }
+
+    (*pGC->ops->PolyArc) (pDrawable, pGC, narcs, arcs);
+
+    if (narcs) {
+       SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
+    }
+
+    GC_OP_EPILOGUE(pGC);
+}
+
+/*
+ * FillPolygon - take bounding box around polygon (and clip).
+ */
+
+static void
+rfbFillPolygon(pDrawable, pGC, shape, mode, count, pts)
+    register DrawablePtr pDrawable;
+    register GCPtr     pGC;
+    int                        shape, mode;
+    int                        count;
+    DDXPointPtr                pts;
+{
+    int i;
+    RegionRec tmpRegion;
+    BoxRec box;
+    GC_OP_PROLOGUE(pDrawable, pGC);
+
+    TRC(("rfbFillPolygon called\n"));
+
+    if (count) {
+       int minX = pts[0].x, maxX = pts[0].x;
+       int minY = pts[0].y, maxY = pts[0].y;
+
+       if (mode == CoordModePrevious)
+       {
+           int x = pts[0].x, y = pts[0].y;
+
+           for (i = 1; i < count; i++) {
+               x += pts[i].x;
+               y += pts[i].y;
+               if (x < minX) minX = x;
+               if (x > maxX) maxX = x;
+               if (y < minY) minY = y;
+               if (y > maxY) maxY = y;
+           }
+       }
+       else
+       {
+           for (i = 1; i < count; i++) {
+               if (pts[i].x < minX) minX = pts[i].x;
+               if (pts[i].x > maxX) maxX = pts[i].x;
+               if (pts[i].y < minY) minY = pts[i].y;
+               if (pts[i].y > maxY) maxY = pts[i].y;
+           }
+       }
+
+       box.x1 = minX + pDrawable->x;
+       box.y1 = minY + pDrawable->y;
+       box.x2 = maxX + 1 + pDrawable->x;
+       box.y2 = maxY + 1 + pDrawable->y;
+
+       SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0);
+
+       REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion,
+                        WINDOW_CLIP_REGION(((WindowPtr)pDrawable),pGC));
+
+       ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion);
+
+       REGION_UNINIT(pDrawable->pScreen, &tmpRegion);
+    }
+
+    (*pGC->ops->FillPolygon) (pDrawable, pGC, shape, mode, count, pts);
+
+    if (count) {
+       SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
+    }
+
+    GC_OP_EPILOGUE(pGC);
+}
+
+/*
+ * PolyFillRect - take the union of the given rectangles (and clip).
+ */
+
+static void
+rfbPolyFillRect(pDrawable, pGC, nrects, rects)
+    DrawablePtr pDrawable;
+    GCPtr      pGC;
+    int                nrects;
+    xRectangle *rects;
+{
+    RegionPtr tmpRegion;
+    xRectangle regRects[MAX_RECTS_PER_OP];
+    int i, nregRects;
+    int rectX1, rectY1, rectX2, rectY2;
+    int minX, minY, maxX, maxY;
+    GC_OP_PROLOGUE(pDrawable, pGC);
+
+    TRC(("rfbPolyFillRect called: %d rects\n",nrects));
+
+    if (nrects) {
+        nregRects = nrects;
+
+        minX = maxX = rects[0].x;
+        minY = maxY = rects[0].y;
+
+       for (i = 0; i < nrects; i++) {
+            if (nrects <= MAX_RECTS_PER_OP) {
+                regRects[i].x = rects[i].x + pDrawable->x;
+                regRects[i].y = rects[i].y + pDrawable->y;
+                regRects[i].width = rects[i].width;
+                regRects[i].height = rects[i].height;
+            } else {
+                rectX1 = pDrawable->x + rects[i].x;
+                rectY1 = pDrawable->y + rects[i].y;
+                rectX2 = pDrawable->x + rects[i].x + rects[i].width;
+                rectY2 = pDrawable->y + rects[i].y + rects[i].height;
+                if (rectX1 < minX) minX = rectX1;
+                if (rectY1 < minY) minY = rectY1;
+                if (rectX2 > maxX) maxX = rectX2;
+                if (rectY2 > maxY) maxY = rectY2;
+            }
+       }
+
+        if (nrects > MAX_RECTS_PER_OP) {
+            regRects[0].x = minX;
+            regRects[0].y = minY;
+            regRects[0].width = maxX - minX;
+            regRects[0].height = maxY - minY;
+            nregRects = 1;
+        }
+
+       tmpRegion = RECTS_TO_REGION(pDrawable->pScreen, nregRects, regRects,
+                                   CT_NONE);
+       REGION_INTERSECT(pDrawable->pScreen, tmpRegion, tmpRegion,
+                        WINDOW_CLIP_REGION((WindowPtr)pDrawable,pGC));
+
+       ADD_TO_MODIFIED_REGION(pDrawable->pScreen, tmpRegion);
+
+       REGION_DESTROY(pDrawable->pScreen, tmpRegion);
+    }
+
+    (*pGC->ops->PolyFillRect) (pDrawable, pGC, nrects, rects);
+
+    if (nrects) {
+       SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
+    }
+
+    GC_OP_EPILOGUE(pGC);
+}
+
+/*
+ * PolyFillArc - take the union of bounding boxes around each arc (and clip).
+ * Bounding boxes assume each is a full circle / ellipse.
+ */
+
+static void
+rfbPolyFillArc(pDrawable, pGC, narcs, arcs)
+    DrawablePtr        pDrawable;
+    GCPtr      pGC;
+    int                narcs;
+    xArc       *arcs;
+{
+    int i, extra, lw, nregRects;
+    int rectX1, rectY1, rectX2, rectY2;
+    int minX, minY, maxX, maxY;
+    RegionPtr tmpRegion;
+    xRectangle regRects[MAX_RECTS_PER_OP];
+    GC_OP_PROLOGUE(pDrawable, pGC);
+
+    TRC(("rfbPolyFillArc called: %d arcs\n",narcs));
+
+    if (narcs) {
+        nregRects = narcs;
+       lw = pGC->lineWidth;
+       if (lw == 0)
+           lw = 1;
+       extra = lw / 2;
+
+        minX = maxX = arcs[0].x;
+        minY = maxY = arcs[0].y;
+
+       for (i = 0; i < narcs; i++)
+       {
+            if (narcs <= MAX_RECTS_PER_OP) {
+                regRects[i].x = arcs[i].x - extra + pDrawable->x;
+                regRects[i].y = arcs[i].y - extra + pDrawable->y;
+                regRects[i].width = arcs[i].width + lw;
+                regRects[i].height = arcs[i].height + lw;
+            } else {
+                rectX1 = pDrawable->x + arcs[i].x - extra;
+                rectY1 = pDrawable->y + arcs[i].y - extra;
+                rectX2 = pDrawable->x + arcs[i].x + arcs[i].width + lw;
+                rectY2 = pDrawable->y + arcs[i].y + arcs[i].height + lw;
+                if (rectX1 < minX) minX = rectX1;
+                if (rectY1 < minY) minY = rectY1;
+                if (rectX2 > maxX) maxX = rectX2;
+                if (rectY2 > maxY) maxY = rectY2;
+            }
+       }
+
+        if (narcs > MAX_RECTS_PER_OP) {
+            regRects[0].x = minX;
+            regRects[0].y = minY;
+            regRects[0].width = maxX - minX;
+            regRects[0].height = maxY - minY;
+            nregRects = 1;
+        }
+
+       tmpRegion = RECTS_TO_REGION(pDrawable->pScreen, nregRects, regRects,
+                                    CT_NONE);
+       REGION_INTERSECT(pDrawable->pScreen, tmpRegion, tmpRegion,
+                        WINDOW_CLIP_REGION((WindowPtr)pDrawable,pGC));
+
+       ADD_TO_MODIFIED_REGION(pDrawable->pScreen, tmpRegion);
+
+       REGION_DESTROY(pDrawable->pScreen, tmpRegion);
+    }
+
+    (*pGC->ops->PolyFillArc) (pDrawable, pGC, narcs, arcs);
+
+    if (narcs) {
+       SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
+    }
+
+    GC_OP_EPILOGUE(pGC);
+}
+
+/*
+ * Get a rough bounding box around n characters of the given font.
+ */
+
+static void GetTextBoundingBox(pDrawable, font, x, y, n, pbox)
+    DrawablePtr pDrawable;
+    FontPtr font;
+    int x, y, n;
+    BoxPtr pbox;
+{
+    int maxAscent, maxDescent, maxCharWidth;
+
+    if (FONTASCENT(font) > FONTMAXBOUNDS(font,ascent))
+       maxAscent = FONTASCENT(font);
+    else
+       maxAscent = FONTMAXBOUNDS(font,ascent);
+
+    if (FONTDESCENT(font) > FONTMAXBOUNDS(font,descent))
+       maxDescent = FONTDESCENT(font);
+    else
+       maxDescent = FONTMAXBOUNDS(font,descent);
+
+    if (FONTMAXBOUNDS(font,rightSideBearing) > FONTMAXBOUNDS(font,characterWidth))
+       maxCharWidth = FONTMAXBOUNDS(font,rightSideBearing);
+    else
+       maxCharWidth = FONTMAXBOUNDS(font,characterWidth);
+
+    pbox->x1 = pDrawable->x + x;
+    pbox->y1 = pDrawable->y + y - maxAscent;
+    pbox->x2 = pbox->x1 + maxCharWidth * n;
+    pbox->y2 = pbox->y1 + maxAscent + maxDescent;
+
+    if (FONTMINBOUNDS(font,leftSideBearing) < 0) {
+       pbox->x1 += FONTMINBOUNDS(font,leftSideBearing);
+    }
+}
+
+
+/*
+ * PolyText8 - use rough bounding box.
+ */
+
+static int
+rfbPolyText8(pDrawable, pGC, x, y, count, chars)
+    DrawablePtr pDrawable;
+    GCPtr      pGC;
+    int                x, y;
+    int        count;
+    char       *chars;
+{
+    int        ret;
+    RegionRec tmpRegion;
+    BoxRec box;
+    GC_OP_PROLOGUE(pDrawable, pGC);
+
+    TRC(("rfbPolyText8 called '%.*s'\n",count,chars));
+
+    if (count) {
+       GetTextBoundingBox(pDrawable, pGC->font, x, y, count, &box);
+
+       SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0);
+
+       REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion,
+                        WINDOW_CLIP_REGION(((WindowPtr)pDrawable),pGC));
+
+       ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion);
+
+       REGION_UNINIT(pDrawable->pScreen, &tmpRegion);
+    }
+
+    ret = (*pGC->ops->PolyText8) (pDrawable, pGC, x, y, count, chars);
+
+    if (count) {
+       SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
+    }
+
+    GC_OP_EPILOGUE(pGC);
+    return ret;
+}
+
+/*
+ * PolyText16 - use rough bounding box.
+ */
+
+static int
+rfbPolyText16(pDrawable, pGC, x, y, count, chars)
+    DrawablePtr pDrawable;
+    GCPtr      pGC;
+    int                x, y;
+    int                count;
+    unsigned short *chars;
+{
+    int        ret;
+    RegionRec tmpRegion;
+    BoxRec box;
+    GC_OP_PROLOGUE(pDrawable, pGC);
+
+    TRC(("rfbPolyText16 called\n"));
+
+    if (count) {
+       GetTextBoundingBox(pDrawable, pGC->font, x, y, count, &box);
+
+       SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0);
+
+       REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion,
+                        WINDOW_CLIP_REGION(((WindowPtr)pDrawable),pGC));
+
+       ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion);
+
+       REGION_UNINIT(pDrawable->pScreen, &tmpRegion);
+    }
+
+    ret = (*pGC->ops->PolyText16) (pDrawable, pGC, x, y, count, chars);
+
+    if (count) {
+       SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
+    }
+
+    GC_OP_EPILOGUE(pGC);
+    return ret;
+}
+
+/*
+ * ImageText8 - use rough bounding box.
+ */
+
+static void
+rfbImageText8(pDrawable, pGC, x, y, count, chars)
+    DrawablePtr pDrawable;
+    GCPtr      pGC;
+    int                x, y;
+    int                count;
+    char       *chars;
+{
+    RegionRec tmpRegion;
+    BoxRec box;
+    GC_OP_PROLOGUE(pDrawable, pGC);
+
+    TRC(("rfbImageText8 called '%.*s'\n",count,chars));
+
+    if (count) {
+       GetTextBoundingBox(pDrawable, pGC->font, x, y, count, &box);
+
+       SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0);
+
+       REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion,
+                        WINDOW_CLIP_REGION(((WindowPtr)pDrawable),pGC));
+
+       ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion);
+
+       REGION_UNINIT(pDrawable->pScreen, &tmpRegion);
+    }
+
+    (*pGC->ops->ImageText8) (pDrawable, pGC, x, y, count, chars);
+
+    if (count) {
+       SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
+    }
+
+    GC_OP_EPILOGUE(pGC);
+}
+
+/*
+ * ImageText16 - use rough bounding box.
+ */
+
+static void
+rfbImageText16(pDrawable, pGC, x, y, count, chars)
+    DrawablePtr pDrawable;
+    GCPtr      pGC;
+    int                x, y;
+    int                count;
+    unsigned short *chars;
+{
+    RegionRec tmpRegion;
+    BoxRec box;
+    GC_OP_PROLOGUE(pDrawable, pGC);
+
+    TRC(("rfbImageText16 called\n"));
+
+    if (count) {
+       GetTextBoundingBox(pDrawable, pGC->font, x, y, count, &box);
+
+       SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0);
+
+       REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion,
+                        WINDOW_CLIP_REGION(((WindowPtr)pDrawable),pGC));
+
+       ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion);
+
+       REGION_UNINIT(pDrawable->pScreen, &tmpRegion);
+    }
+
+    (*pGC->ops->ImageText16) (pDrawable, pGC, x, y, count, chars);
+
+    if (count) {
+       SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
+    }
+
+    GC_OP_EPILOGUE(pGC);
+}
+
+/*
+ * ImageGlyphBlt - use rough bounding box.
+ */
+
+static void
+rfbImageGlyphBlt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase)
+    DrawablePtr pDrawable;
+    GCPtr      pGC;
+    int        x, y;
+    unsigned int nglyph;
+    CharInfoPtr *ppci;         /* array of character info */
+    pointer    pglyphBase;     /* start of array of glyphs */
+{
+    RegionRec tmpRegion;
+    BoxRec box;
+    GC_OP_PROLOGUE(pDrawable, pGC);
+
+    TRC(("rfbImageGlyphBlt called\n"));
+
+    if (nglyph) {
+       GetTextBoundingBox(pDrawable, pGC->font, x, y, nglyph, &box);
+
+       SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0);
+
+       REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion,
+                        WINDOW_CLIP_REGION(((WindowPtr)pDrawable),pGC));
+
+       ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion);
+
+       REGION_UNINIT(pDrawable->pScreen, &tmpRegion);
+    }
+
+    (*pGC->ops->ImageGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci,pglyphBase);
+
+    if (nglyph) {
+       SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
+    }
+
+    GC_OP_EPILOGUE(pGC);
+}
+
+/*
+ * PolyGlyphBlt - use rough bounding box.
+ */
+
+static void
+rfbPolyGlyphBlt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase)
+    DrawablePtr pDrawable;
+    GCPtr      pGC;
+    int        x, y;
+    unsigned int nglyph;
+    CharInfoPtr *ppci;         /* array of character info */
+    pointer    pglyphBase;     /* start of array of glyphs */
+{
+    RegionRec tmpRegion;
+    BoxRec box;
+    GC_OP_PROLOGUE(pDrawable, pGC);
+
+    TRC(("rfbPolyGlyphBlt called\n"));
+
+    if (nglyph) {
+       GetTextBoundingBox(pDrawable, pGC->font, x, y, nglyph, &box);
+
+       SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0);
+
+       REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion,
+                        WINDOW_CLIP_REGION(((WindowPtr)pDrawable),pGC));
+
+       ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion);
+
+       REGION_UNINIT(pDrawable->pScreen, &tmpRegion);
+    }
+
+    (*pGC->ops->PolyGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci, pglyphBase);
+
+    if (nglyph) {
+       SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
+    }
+
+    GC_OP_EPILOGUE(pGC);
+}
+
+/*
+ * PushPixels - be fairly safe - region modified is intersection of the given
+ * rectangle with the window clip region.
+ */
+
+static void
+rfbPushPixels(pGC, pBitMap, pDrawable, w, h, x, y)
+    GCPtr      pGC;
+    PixmapPtr  pBitMap;
+    DrawablePtr pDrawable;
+    int                w, h, x, y;
+{
+    RegionRec tmpRegion;
+    BoxRec box;
+    GC_OP_PROLOGUE(pDrawable, pGC);
+
+    TRC(("rfbPushPixels called\n"));
+
+    box.x1 = x + pDrawable->x;
+    box.y1 = y + pDrawable->y;
+    box.x2 = box.x1 + w;
+    box.y2 = box.y1 + h;
+
+    SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0);
+
+    REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion,
+                    WINDOW_CLIP_REGION(((WindowPtr)pDrawable),pGC));
+
+    ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion);
+
+    REGION_UNINIT(pDrawable->pScreen, &tmpRegion);
+
+    (*pGC->ops->PushPixels) (pGC, pBitMap, pDrawable, w, h, x, y);
+
+    SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
+
+    GC_OP_EPILOGUE(pGC);
+}
+
+
+
+/****************************************************************************/
+/*
+ * Other functions
+ */
+/****************************************************************************/
+
+/*
+ * rfbCopyRegion.  Args are src and dst regions plus a translation (dx,dy).
+ * Takes these args together with the existing modified region and possibly an
+ * existing copy region and translation.  Produces a combined modified region
+ * plus copy region and translation.  Note that the copy region is the
+ * destination of the copy.
+ *
+ * First we trim parts of src which are invalid (ie in the modified region).
+ * Then we see if there is any overlap between the src and the existing copy
+ * region.  If not then the two copies cannot be combined, so we choose
+ * whichever is bigger to form the basis of a new copy, while the other copy is
+ * just done the hard way by being added to the modified region.  So if the
+ * existing copy is bigger then we simply add the destination of the new copy
+ * to the modified region and we're done.  If the new copy is bigger, we add
+ * the old copy region to the modified region and behave as though there is no
+ * existing copy region.
+ * 
+ * At this stage we now know that either the two copies can be combined, or
+ * that there is no existing copy.  We temporarily add both the existing copy
+ * region and dst to the modified region (this is the entire area of the screen
+ * affected in any way).  Finally we calculate the new copy region, and remove
+ * it from the modified region.
+ *
+ * Note:
+ *   1. The src region is modified by this routine.
+ *   2. When the copy region is empty, copyDX and copyDY MUST be set to zero.
+ */
+
+static void
+rfbCopyRegion(pScreen, cl, src, dst, dx, dy)
+    ScreenPtr pScreen;
+    rfbClientPtr cl;
+    RegionPtr src;
+    RegionPtr dst;
+    int dx, dy;
+{
+    RegionRec tmp;
+
+    /* src = src - modifiedRegion */
+
+    REGION_SUBTRACT(pScreen, src, src, &cl->modifiedRegion);
+
+    if (REGION_NOTEMPTY(pScreen, &cl->copyRegion)) {
+
+       REGION_INIT(pScreen, &tmp, NullBox, 0);
+       REGION_INTERSECT(pScreen, &tmp, src, &cl->copyRegion);
+
+       if (REGION_NOTEMPTY(pScreen, &tmp)) {
+
+           /* if src and copyRegion overlap:
+                src = src intersect copyRegion */
+
+           REGION_COPY(pScreen, src, &tmp);
+
+       } else {
+
+           /* if no overlap, find bigger region */
+
+           int newArea = (((REGION_EXTENTS(pScreen,src))->x2
+                           - (REGION_EXTENTS(pScreen,src))->x1)
+                          * ((REGION_EXTENTS(pScreen,src))->y2
+                             - (REGION_EXTENTS(pScreen,src))->y1));
+
+           int oldArea = (((REGION_EXTENTS(pScreen,&cl->copyRegion))->x2
+                           - (REGION_EXTENTS(pScreen,&cl->copyRegion))->x1)
+                          * ((REGION_EXTENTS(pScreen,&cl->copyRegion))->y2
+                            - (REGION_EXTENTS(pScreen,&cl->copyRegion))->y1));
+
+           if (oldArea > newArea) {
+
+               /* existing copy is bigger:
+                    modifiedRegion = modifiedRegion union dst
+                    copyRegion = copyRegion - dst
+                    return */
+
+               REGION_UNION(pScreen, &cl->modifiedRegion, &cl->modifiedRegion,
+                            dst);
+               REGION_SUBTRACT(pScreen, &cl->copyRegion, &cl->copyRegion,
+                               dst);
+               if (!REGION_NOTEMPTY(pScreen, &cl->copyRegion)) {
+                   cl->copyDX = 0;
+                   cl->copyDY = 0;
+               }
+               return;
+           }
+
+           /* new copy is bigger:
+                modifiedRegion = modifiedRegion union copyRegion
+                copyRegion = empty */
+
+           REGION_UNION(pScreen, &cl->modifiedRegion, &cl->modifiedRegion,
+                        &cl->copyRegion);
+           REGION_EMPTY(pScreen, &cl->copyRegion);
+           cl->copyDX = cl->copyDY = 0;
+       }
+    }
+
+
+    /* modifiedRegion = modifiedRegion union dst union copyRegion */
+
+    REGION_UNION(pScreen, &cl->modifiedRegion, &cl->modifiedRegion, dst);
+    REGION_UNION(pScreen, &cl->modifiedRegion, &cl->modifiedRegion,
+                &cl->copyRegion);
+
+    /* copyRegion = T(src) intersect dst */
+
+    REGION_TRANSLATE(pScreen, src, dx, dy);
+    REGION_INTERSECT(pScreen, &cl->copyRegion, src, dst);
+
+    /* modifiedRegion = modifiedRegion - copyRegion */
+
+    REGION_SUBTRACT(pScreen, &cl->modifiedRegion, &cl->modifiedRegion,
+                   &cl->copyRegion);
+
+    /* combine new translation T with existing translation */
+
+    if (REGION_NOTEMPTY(pScreen, &cl->copyRegion)) {
+       cl->copyDX += dx;
+       cl->copyDY += dy;
+    } else {
+       cl->copyDX = 0;
+       cl->copyDY = 0;
+    }
+}
+
+
+/*
+ * rfbDeferredUpdateCallback() is called when a client's deferredUpdateTimer
+ * goes off.
+ */
+
+static CARD32
+rfbDeferredUpdateCallback(OsTimerPtr timer, CARD32 now, pointer arg)
+{
+  rfbClientPtr cl = (rfbClientPtr)arg;
+
+  rfbSendFramebufferUpdate(cl);
+
+  cl->deferredUpdateScheduled = FALSE;
+  return 0;
+}
+
+
+/*
+ * rfbScheduleDeferredUpdate() is called from the SCHEDULE_FB_UPDATE macro
+ * to schedule an update.
+ */
+
+void
+rfbScheduleDeferredUpdate(rfbClientPtr cl)
+{
+    if (rfbDeferUpdateTime != 0) {
+       cl->deferredUpdateTimer = TimerSet(cl->deferredUpdateTimer, 0,
+                                          rfbDeferUpdateTime,
+                                          rfbDeferredUpdateCallback, cl);
+       cl->deferredUpdateScheduled = TRUE;
+    } else {
+       rfbSendFramebufferUpdate(cl);
+    }
+}
+
+
+/*
+ * PrintRegion is useful for debugging.
+ */
+
+#if 0
+static void
+PrintRegion(pScreen,reg)
+    ScreenPtr pScreen;
+    RegionPtr reg;
+{
+    int nrects = REGION_NUM_RECTS(reg);
+    int i;
+
+    rfbLog("Region num rects %d extents %d,%d %d,%d\n",nrects,
+          (REGION_EXTENTS(pScreen,reg))->x1,
+          (REGION_EXTENTS(pScreen,reg))->y1,
+          (REGION_EXTENTS(pScreen,reg))->x2,
+          (REGION_EXTENTS(pScreen,reg))->y2);
+
+    for (i = 0; i < nrects; i++) {
+       rfbLog("    rect %d,%d %dx%d\n",
+              REGION_RECTS(reg)[i].x1,
+              REGION_RECTS(reg)[i].y1,
+              REGION_RECTS(reg)[i].x2-REGION_RECTS(reg)[i].x1,
+              REGION_RECTS(reg)[i].y2-REGION_RECTS(reg)[i].y1);
+    }
+}
+#endif