]> git.sesse.net Git - rdpsrv/blob - Xserver/programs/Xserver/hw/vnc/draw.c
Import X server from vnc-3.3.7.
[rdpsrv] / Xserver / programs / Xserver / hw / vnc / draw.c
1 /*
2  * draw.c - drawing routines for the RFB X server.  This is a set of
3  * wrappers around the standard MI/MFB/CFB drawing routines which work out
4  * to a fair approximation the region of the screen being modified by the
5  * drawing.  If the RFB client is ready then the modified region of the screen
6  * is sent to the client, otherwise the modified region will simply grow with
7  * each drawing request until the client is ready.
8  */
9
10 /*
11  *  Copyright (C) 2002-2003 RealVNC Ltd.
12  *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
13  *
14  *  This is free software; you can redistribute it and/or modify
15  *  it under the terms of the GNU General Public License as published by
16  *  the Free Software Foundation; either version 2 of the License, or
17  *  (at your option) any later version.
18  *
19  *  This software is distributed in the hope that it will be useful,
20  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  *  GNU General Public License for more details.
23  *
24  *  You should have received a copy of the GNU General Public License
25  *  along with this software; if not, write to the Free Software
26  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
27  *  USA.
28  */
29
30 /*
31
32 Copyright (c) 1989  X Consortium
33
34 Permission is hereby granted, free of charge, to any person obtaining a copy
35 of this software and associated documentation files (the "Software"), to deal
36 in the Software without restriction, including without limitation the rights
37 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
38 copies of the Software, and to permit persons to whom the Software is
39 furnished to do so, subject to the following conditions:
40
41 The above copyright notice and this permission notice shall be included in
42 all copies or substantial portions of the Software.
43
44 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
45 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
46 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
47 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
48 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
49 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
50
51 Except as contained in this notice, the name of the X Consortium shall not be
52 used in advertising or otherwise to promote the sale, use or other dealings
53 in this Software without prior written authorization from the X Consortium.
54 */
55
56 #include <stdio.h>
57 #include "scrnintstr.h"
58 #include "gcstruct.h"
59 #include "windowstr.h"
60 #include "regionstr.h"
61 #include "dixfontstr.h"
62 #include "rfb.h"
63 #include "mfb.h"
64
65 extern WindowPtr *WindowTable; /* Why isn't this in a header file? */
66
67 int rfbDeferUpdateTime = 40; /* ms */
68
69 /* MAX_RECTS_PER_OP is the maximum number of rectangles we generate from
70    operations like Polylines and PolySegment.  If the operation is more complex
71    than this, we simply use the bounding box.  Ideally it would be a
72    command-line option, but that would involve an extra malloc each time, so we
73    fix it here. */
74 #define MAX_RECTS_PER_OP 5
75
76
77 /****************************************************************************/
78 /*
79  * Macro definitions
80  */
81 /****************************************************************************/
82
83 /* SLIGHTLY DIRTY HACK - use Composite Clip region calculated by mfb */
84
85 #define WINDOW_CLIP_REGION(_w, _gc) \
86   (((mfbPrivGCPtr)((_gc)->devPrivates[mfbGCPrivateIndex].ptr))->pCompositeClip)
87
88 #define TRC(x) if (rfbTrace) rfbLog x
89
90 /* ADD_TO_MODIFIED_REGION adds the given region to the modified region for each
91    client */
92
93 #define ADD_TO_MODIFIED_REGION(pScreen,reg)                                   \
94   {                                                                           \
95       rfbClientPtr cl;                                                        \
96       for (cl = rfbClientHead; cl; cl = cl->next) {                           \
97           if (REGION_NUM_RECTS(&cl->modifiedRegion) > rfbMaxRects) {          \
98               BoxRec boundingBox = *(REGION_EXTENTS((pScreen),                \
99                                                     &cl->modifiedRegion));    \
100               REGION_RESET((pScreen), &cl->modifiedRegion, &boundingBox);     \
101           }                                                                   \
102                                                                               \
103           REGION_UNION((pScreen),&cl->modifiedRegion,&cl->modifiedRegion,reg);\
104       }                                                                       \
105   }
106
107 /* SCHEDULE_FB_UPDATE is used at the end of each drawing routine to schedule an
108    update to be sent to each client if there is one pending and the client is
109    ready for it.  */
110
111 #define SCHEDULE_FB_UPDATE(pScreen,prfb)                                \
112   if (!prfb->dontSendFramebufferUpdate) {                               \
113       rfbClientPtr cl, nextCl;                                          \
114       for (cl = rfbClientHead; cl; cl = nextCl) {                       \
115           nextCl = cl->next;                                            \
116           if (!cl->deferredUpdateScheduled && FB_UPDATE_PENDING(cl) &&  \
117               REGION_NOTEMPTY(pScreen,&cl->requestedRegion))            \
118           {                                                             \
119               rfbScheduleDeferredUpdate(cl);                            \
120           }                                                             \
121       }                                                                 \
122   }
123
124 /* function prototypes */
125
126 static void rfbCopyRegion(ScreenPtr pScreen, rfbClientPtr cl,
127                           RegionPtr src, RegionPtr dst, int dx, int dy);
128
129 /* GC funcs */
130
131 static void rfbValidateGC(GCPtr, unsigned long /*changes*/, DrawablePtr);
132 static void rfbChangeGC(GCPtr, unsigned long /*mask*/);
133 static void rfbCopyGC(GCPtr /*src*/, unsigned long /*mask*/, GCPtr /*dst*/);
134 static void rfbDestroyGC(GCPtr);
135 static void rfbChangeClip(GCPtr, int /*type*/, pointer /*pValue*/,
136                           int /*nrects*/);
137 static void rfbDestroyClip(GCPtr);
138 static void rfbCopyClip(GCPtr /*dst*/, GCPtr /*src*/);
139
140 /* GC ops */
141
142 static void rfbFillSpans();
143 static void rfbSetSpans();
144 static void rfbPutImage();
145 static RegionPtr rfbCopyArea();
146 static RegionPtr rfbCopyPlane();
147 static void rfbPolyPoint();
148 static void rfbPolylines();
149 static void rfbPolySegment();
150 static void rfbPolyRectangle();
151 static void rfbPolyArc();
152 static void rfbFillPolygon();
153 static void rfbPolyFillRect();
154 static void rfbPolyFillArc();
155 static int rfbPolyText8();
156 static int rfbPolyText16();
157 static void rfbImageText8();
158 static void rfbImageText16();
159 static void rfbImageGlyphBlt();
160 static void rfbPolyGlyphBlt();
161 static void rfbPushPixels();
162
163
164 static GCFuncs rfbGCFuncs = {
165     rfbValidateGC,
166     rfbChangeGC,
167     rfbCopyGC,
168     rfbDestroyGC,
169     rfbChangeClip,
170     rfbDestroyClip,
171     rfbCopyClip,
172 };
173
174
175 static GCOps rfbGCOps = {
176     rfbFillSpans,       rfbSetSpans,    rfbPutImage,    
177     rfbCopyArea,        rfbCopyPlane,   rfbPolyPoint,
178     rfbPolylines,       rfbPolySegment, rfbPolyRectangle,
179     rfbPolyArc,         rfbFillPolygon, rfbPolyFillRect,
180     rfbPolyFillArc,     rfbPolyText8,   rfbPolyText16,
181     rfbImageText8,      rfbImageText16, rfbImageGlyphBlt,
182     rfbPolyGlyphBlt,    rfbPushPixels
183 };
184
185
186
187 /****************************************************************************/
188 /*
189  * Screen functions wrapper stuff
190  */
191 /****************************************************************************/
192
193 #define SCREEN_PROLOGUE(scrn, field)            \
194     ScreenPtr pScreen = scrn;                   \
195     rfbScreenInfoPtr prfb = &rfbScreen;         \
196     pScreen->field = prfb->field;
197
198 #define SCREEN_EPILOGUE(field, wrapper) \
199     pScreen->field = wrapper;
200
201
202 /*
203  * CloseScreen wrapper -- unwrap everything, free the private data
204  * and call the wrapped CloseScreen function.
205  */
206
207 Bool
208 rfbCloseScreen (i, pScreen)
209     int i;
210     ScreenPtr   pScreen;
211 {
212     rfbScreenInfoPtr prfb = &rfbScreen;
213
214     pScreen->CloseScreen = prfb->CloseScreen;
215     pScreen->CreateGC = prfb->CreateGC;
216     pScreen->PaintWindowBackground = prfb->PaintWindowBackground;
217     pScreen->PaintWindowBorder = prfb->PaintWindowBorder;
218     pScreen->CopyWindow = prfb->CopyWindow;
219     pScreen->ClearToBackground = prfb->ClearToBackground;
220     pScreen->RestoreAreas = prfb->RestoreAreas;
221
222     TRC(("Unwrapped screen functions\n"));
223
224     return (*pScreen->CloseScreen) (i, pScreen);
225 }
226
227 /*
228  * CreateGC - wrap the GC funcs (the GC ops will be wrapped when the GC
229  * func "ValidateGC" is called).
230  */
231
232 Bool
233 rfbCreateGC (pGC)
234     GCPtr   pGC;
235 {
236     Bool ret;
237     rfbGCPtr pGCPriv;
238
239     SCREEN_PROLOGUE(pGC->pScreen,CreateGC);
240     
241     pGCPriv = (rfbGCPtr)pGC->devPrivates[rfbGCIndex].ptr;
242
243     ret = (*pScreen->CreateGC) (pGC);
244
245     TRC(("rfbCreateGC called\n"));
246
247     pGCPriv->wrapOps = NULL;
248     pGCPriv->wrapFuncs = pGC->funcs;
249     pGC->funcs = &rfbGCFuncs;
250
251     SCREEN_EPILOGUE(CreateGC,rfbCreateGC);
252
253     return ret;
254 }
255
256 /*
257  * PaintWindowBackground - the region being modified is just the given region.
258  */
259
260 void
261 rfbPaintWindowBackground (pWin, pRegion, what)
262     WindowPtr   pWin;
263     RegionPtr   pRegion;
264     int         what;
265 {
266     SCREEN_PROLOGUE(pWin->drawable.pScreen,PaintWindowBackground);
267
268     TRC(("rfbPaintWindowBackground called\n"));
269
270     ADD_TO_MODIFIED_REGION(pScreen,pRegion);
271
272     (*pScreen->PaintWindowBackground) (pWin, pRegion, what);
273
274     SCHEDULE_FB_UPDATE(pScreen, prfb);
275
276     SCREEN_EPILOGUE(PaintWindowBackground,rfbPaintWindowBackground);
277 }
278
279 /*
280  * PaintWindowBorder - the region being modified is just the given region.
281  */
282
283 void
284 rfbPaintWindowBorder (pWin, pRegion, what)
285     WindowPtr   pWin;
286     RegionPtr   pRegion;
287     int         what;
288 {
289     SCREEN_PROLOGUE(pWin->drawable.pScreen,PaintWindowBorder);
290
291     TRC(("rfbPaintWindowBorder called\n"));
292
293     ADD_TO_MODIFIED_REGION(pScreen,pRegion);
294
295     (*pScreen->PaintWindowBorder) (pWin, pRegion, what);
296
297     SCHEDULE_FB_UPDATE(pScreen, prfb);
298
299     SCREEN_EPILOGUE(PaintWindowBorder,rfbPaintWindowBorder);
300 }
301
302 /*
303  * CopyWindow - the region being modified is the translation of the old
304  * region, clipped to the border clip region of the window.  Note that any
305  * parts of the window which have become newly-visible will not be affected by
306  * this call - a separate PaintWindowBackground/Border will be called to do
307  * that.  If the client will accept CopyRect messages then use rfbCopyRegion to
308  * optimise the pending screen changes into a single "copy region" plus the
309  * ordinary modified region.
310  */
311
312 void
313 rfbCopyWindow (pWin, ptOldOrg, pOldRegion)
314     WindowPtr   pWin;
315     DDXPointRec ptOldOrg;
316     RegionPtr   pOldRegion;
317 {
318     rfbClientPtr cl;
319     RegionRec srcRegion, dstRegion;
320     SCREEN_PROLOGUE(pWin->drawable.pScreen,CopyWindow);
321
322     TRC(("rfbCopyWindow called\n"));
323
324     REGION_INIT(pScreen,&dstRegion,NullBox,0);
325     REGION_COPY(pScreen,&dstRegion,pOldRegion);
326     REGION_TRANSLATE(pWin->drawable.pScreen, &dstRegion,
327                      pWin->drawable.x - ptOldOrg.x,
328                      pWin->drawable.y - ptOldOrg.y);
329     REGION_INTERSECT(pWin->drawable.pScreen, &dstRegion, &dstRegion,
330                      &pWin->borderClip);
331
332     for (cl = rfbClientHead; cl; cl = cl->next) {
333         if (cl->useCopyRect) {
334             REGION_INIT(pScreen,&srcRegion,NullBox,0);
335             REGION_COPY(pScreen,&srcRegion,pOldRegion);
336
337             rfbCopyRegion(pScreen, cl, &srcRegion, &dstRegion,
338                           pWin->drawable.x - ptOldOrg.x,
339                           pWin->drawable.y - ptOldOrg.y);
340
341             REGION_UNINIT(pSrc->pScreen, &srcRegion);
342
343         } else {
344
345             REGION_UNION(pScreen, &cl->modifiedRegion, &cl->modifiedRegion,
346                          &dstRegion);
347         }
348     }
349
350     REGION_UNINIT(pSrc->pScreen, &dstRegion);
351
352     (*pScreen->CopyWindow) (pWin, ptOldOrg, pOldRegion);
353
354     SCHEDULE_FB_UPDATE(pScreen, prfb);
355
356     SCREEN_EPILOGUE(CopyWindow,rfbCopyWindow);
357 }
358
359 /*
360  * ClearToBackground - when generateExposures is false, the region being
361  * modified is the given rectangle (clipped to the "window clip region").
362  */
363
364 void
365 rfbClearToBackground (pWin, x, y, w, h, generateExposures)
366     WindowPtr pWin;
367     int x,y,w,h;
368     Bool generateExposures;
369 {
370     RegionRec tmpRegion;
371     BoxRec box;
372     SCREEN_PROLOGUE(pWin->drawable.pScreen,ClearToBackground);
373
374     TRC(("rfbClearToBackground called\n"));
375
376     if (!generateExposures) {
377         box.x1 = x + pWin->drawable.x;
378         box.y1 = y + pWin->drawable.y;
379         box.x2 = w ? (box.x1 + w) : (pWin->drawable.x + pWin->drawable.width);
380         box.y2 = h ? (box.y1 + h) : (pWin->drawable.y + pWin->drawable.height);
381
382         SAFE_REGION_INIT(pScreen, &tmpRegion, &box, 0);
383
384         REGION_INTERSECT(pScreen, &tmpRegion, &tmpRegion, &pWin->clipList);
385
386         ADD_TO_MODIFIED_REGION(pScreen, &tmpRegion);
387
388         REGION_UNINIT(pScreen, &tmpRegion);
389     }
390
391     (*pScreen->ClearToBackground) (pWin, x, y, w, h, generateExposures);
392
393     if (!generateExposures) {
394         SCHEDULE_FB_UPDATE(pScreen, prfb);
395     }
396
397     SCREEN_EPILOGUE(ClearToBackground,rfbClearToBackground);
398 }
399
400 /*
401  * RestoreAreas - just be safe here - the region being modified is the whole
402  * exposed region.
403  */
404
405 RegionPtr
406 rfbRestoreAreas (pWin, prgnExposed)
407     WindowPtr   pWin;
408     RegionPtr   prgnExposed;
409 {
410     RegionPtr result;
411     SCREEN_PROLOGUE(pWin->drawable.pScreen,RestoreAreas);
412
413     TRC(("rfbRestoreAreas called\n"));
414
415     ADD_TO_MODIFIED_REGION(pScreen, prgnExposed);
416
417     result = (*pScreen->RestoreAreas) (pWin, prgnExposed);
418
419     SCHEDULE_FB_UPDATE(pScreen, prfb);
420
421     SCREEN_EPILOGUE(RestoreAreas,rfbRestoreAreas);
422
423     return result;
424 }
425
426
427
428 /****************************************************************************/
429 /*
430  * GC funcs wrapper stuff
431  *
432  * We only really want to wrap the GC ops, but to do this we need to wrap
433  * ValidateGC and so all the other GC funcs must be wrapped as well.
434  */
435 /****************************************************************************/
436
437 #define GC_FUNC_PROLOGUE(pGC)                                           \
438     rfbGCPtr pGCPriv = (rfbGCPtr) (pGC)->devPrivates[rfbGCIndex].ptr;   \
439     (pGC)->funcs = pGCPriv->wrapFuncs;                                  \
440     if (pGCPriv->wrapOps)                                               \
441         (pGC)->ops = pGCPriv->wrapOps;
442
443 #define GC_FUNC_EPILOGUE(pGC)           \
444     pGCPriv->wrapFuncs = (pGC)->funcs;  \
445     (pGC)->funcs = &rfbGCFuncs;         \
446     if (pGCPriv->wrapOps) {             \
447         pGCPriv->wrapOps = (pGC)->ops;  \
448         (pGC)->ops = &rfbGCOps;         \
449     }
450
451
452 /*
453  * ValidateGC - call the wrapped ValidateGC, then wrap the resulting GC ops if
454  * the drawing will be to a viewable window.
455  */
456
457 static void
458 rfbValidateGC (pGC, changes, pDrawable)
459     GCPtr       pGC;
460     unsigned long changes;
461     DrawablePtr pDrawable;
462 {
463     GC_FUNC_PROLOGUE(pGC);
464
465     TRC(("rfbValidateGC called\n"));
466
467     (*pGC->funcs->ValidateGC) (pGC, changes, pDrawable);
468     
469     pGCPriv->wrapOps = NULL;
470     if (pDrawable->type == DRAWABLE_WINDOW && ((WindowPtr)pDrawable)->viewable)
471     {
472         WindowPtr   pWin = (WindowPtr) pDrawable;
473         RegionPtr   pRegion = &pWin->clipList;
474
475         if (pGC->subWindowMode == IncludeInferiors)
476             pRegion = &pWin->borderClip;
477         if (REGION_NOTEMPTY(pDrawable->pScreen, pRegion)) {
478             pGCPriv->wrapOps = pGC->ops;
479             TRC(("rfbValidateGC: wrapped GC ops\n"));
480         }
481     }
482
483     GC_FUNC_EPILOGUE(pGC);
484 }
485
486 /*
487  * All other GC funcs simply unwrap the GC funcs and ops, call the wrapped
488  * function and then rewrap the funcs and ops.
489  */
490
491 static void
492 rfbChangeGC (pGC, mask)
493     GCPtr           pGC;
494     unsigned long   mask;
495 {
496     GC_FUNC_PROLOGUE(pGC);
497     (*pGC->funcs->ChangeGC) (pGC, mask);
498     GC_FUNC_EPILOGUE(pGC);
499 }
500
501 static void
502 rfbCopyGC (pGCSrc, mask, pGCDst)
503     GCPtr           pGCSrc, pGCDst;
504     unsigned long   mask;
505 {
506     GC_FUNC_PROLOGUE(pGCDst);
507     (*pGCDst->funcs->CopyGC) (pGCSrc, mask, pGCDst);
508     GC_FUNC_EPILOGUE(pGCDst);
509 }
510
511 static void
512 rfbDestroyGC (pGC)
513     GCPtr   pGC;
514 {
515     GC_FUNC_PROLOGUE(pGC);
516     (*pGC->funcs->DestroyGC) (pGC);
517     GC_FUNC_EPILOGUE(pGC);
518 }
519
520 static void
521 rfbChangeClip (pGC, type, pvalue, nrects)
522     GCPtr   pGC;
523     int         type;
524     pointer     pvalue;
525     int         nrects;
526 {
527     GC_FUNC_PROLOGUE(pGC);
528     (*pGC->funcs->ChangeClip) (pGC, type, pvalue, nrects);
529     GC_FUNC_EPILOGUE(pGC);
530 }
531
532 static void
533 rfbDestroyClip(pGC)
534     GCPtr       pGC;
535 {
536     GC_FUNC_PROLOGUE(pGC);
537     (* pGC->funcs->DestroyClip)(pGC);
538     GC_FUNC_EPILOGUE(pGC);
539 }
540
541 static void
542 rfbCopyClip(pgcDst, pgcSrc)
543     GCPtr pgcDst, pgcSrc;
544 {
545     GC_FUNC_PROLOGUE(pgcDst);
546     (* pgcDst->funcs->CopyClip)(pgcDst, pgcSrc);
547     GC_FUNC_EPILOGUE(pgcDst);
548 }
549
550
551 /****************************************************************************/
552 /*
553  * GC ops wrapper stuff
554  *
555  * Note that these routines will only have been wrapped for drawing to
556  * viewable windows so we don't need to check each time that the drawable
557  * is a viewable window.
558  */
559 /****************************************************************************/
560
561 #define GC_OP_PROLOGUE(pDrawable,pGC) \
562     rfbScreenInfoPtr prfb = &rfbScreen; \
563     rfbGCPtr pGCPrivate = (rfbGCPtr) (pGC)->devPrivates[rfbGCIndex].ptr; \
564     GCFuncs *oldFuncs = pGC->funcs; \
565     (pGC)->funcs = pGCPrivate->wrapFuncs; \
566     (pGC)->ops = pGCPrivate->wrapOps;
567
568 #define GC_OP_EPILOGUE(pGC) \
569     pGCPrivate->wrapOps = (pGC)->ops; \
570     (pGC)->funcs = oldFuncs; \
571     (pGC)->ops = &rfbGCOps;
572
573
574 /*
575  * FillSpans - being very safe - the region being modified is the border clip
576  * region of the window.
577  */
578
579 static void
580 rfbFillSpans(pDrawable, pGC, nInit, pptInit, pwidthInit, fSorted)
581     DrawablePtr pDrawable;
582     GCPtr       pGC;
583     int         nInit;                  /* number of spans to fill */
584     DDXPointPtr pptInit;                /* pointer to list of start points */
585     int         *pwidthInit;            /* pointer to list of n widths */
586     int         fSorted;
587 {
588     GC_OP_PROLOGUE(pDrawable,pGC);
589
590     TRC(("rfbFillSpans called\n"));
591
592     ADD_TO_MODIFIED_REGION(pDrawable->pScreen,
593                            &((WindowPtr)pDrawable)->borderClip);
594
595     (*pGC->ops->FillSpans) (pDrawable, pGC, nInit, pptInit,pwidthInit,fSorted);
596
597     SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
598
599     GC_OP_EPILOGUE(pGC);
600 }
601
602 /*
603  * SetSpans - being very safe - the region being modified is the border clip
604  * region of the window.
605  */
606
607 static void
608 rfbSetSpans(pDrawable, pGC, psrc, ppt, pwidth, nspans, fSorted)
609     DrawablePtr         pDrawable;
610     GCPtr               pGC;
611     char                *psrc;
612     register DDXPointPtr ppt;
613     int                 *pwidth;
614     int                 nspans;
615     int                 fSorted;
616 {
617     GC_OP_PROLOGUE(pDrawable,pGC);
618
619     TRC(("rfbSetSpans called\n"));
620
621     ADD_TO_MODIFIED_REGION(pDrawable->pScreen,
622                            &((WindowPtr)pDrawable)->borderClip);
623
624     (*pGC->ops->SetSpans) (pDrawable, pGC, psrc, ppt, pwidth, nspans, fSorted);
625
626     SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
627
628     GC_OP_EPILOGUE(pGC);
629 }
630
631 /*
632  * PutImage - the region being modified is the rectangle of the
633  * PutImage (clipped to the window clip region).
634  */
635
636 static void
637 rfbPutImage(pDrawable, pGC, depth, x, y, w, h, leftPad, format, pBits)
638     DrawablePtr   pDrawable;
639     GCPtr         pGC;
640     int           depth;
641     int           x;
642     int           y;
643     int           w;
644     int           h;
645     int           leftPad;
646     int           format;
647     char          *pBits;
648 {
649     RegionRec tmpRegion;
650     BoxRec box;
651     GC_OP_PROLOGUE(pDrawable, pGC);
652
653     TRC(("rfbPutImage called\n"));
654
655     box.x1 = x + pDrawable->x;
656     box.y1 = y + pDrawable->y;
657     box.x2 = box.x1 + w;
658     box.y2 = box.y1 + h;
659
660     SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0);
661
662     REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion,
663                      WINDOW_CLIP_REGION((WindowPtr)pDrawable,pGC));
664
665     ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion);
666
667     REGION_UNINIT(pDrawable->pScreen, &tmpRegion);
668
669     (*pGC->ops->PutImage) (pDrawable, pGC, depth, x, y, w, h,
670                            leftPad, format, pBits);
671
672     SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
673
674     GC_OP_EPILOGUE(pGC);
675 }
676
677 /*
678  * CopyArea - the region being modified is the destination rectangle (clipped
679  * to the window clip region).
680  * If the client will accept CopyRect messages then use rfbCopyRegion
681  * to optimise the pending screen changes into a single "copy region" plus
682  * the ordinary modified region.
683  */
684
685 static RegionPtr
686 rfbCopyArea (pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty)
687     DrawablePtr   pSrc;
688     DrawablePtr   pDst;
689     GCPtr         pGC;
690     int           srcx;
691     int           srcy;
692     int           w;
693     int           h;
694     int           dstx;
695     int           dsty;
696 {
697     rfbClientPtr cl;
698     RegionPtr rgn;
699     RegionRec srcRegion, dstRegion;
700     BoxRec box;
701     GC_OP_PROLOGUE(pDst, pGC);
702
703     TRC(("rfbCopyArea called\n"));
704
705     box.x1 = dstx + pDst->x;
706     box.y1 = dsty + pDst->y;
707     box.x2 = box.x1 + w;
708     box.y2 = box.y1 + h;
709
710     SAFE_REGION_INIT(pDst->pScreen, &dstRegion, &box, 0);
711     REGION_INTERSECT(pDst->pScreen, &dstRegion, &dstRegion,
712                      WINDOW_CLIP_REGION((WindowPtr)pDst,pGC));
713
714     if ((pSrc->type == DRAWABLE_WINDOW) && (pSrc->pScreen == pDst->pScreen)) {
715         box.x1 = srcx + pSrc->x;
716         box.y1 = srcy + pSrc->y;
717         box.x2 = box.x1 + w;
718         box.y2 = box.y1 + h;
719
720         for (cl = rfbClientHead; cl; cl = cl->next) {
721             if (cl->useCopyRect) {
722                 SAFE_REGION_INIT(pSrc->pScreen, &srcRegion, &box, 0);
723                 REGION_INTERSECT(pSrc->pScreen, &srcRegion, &srcRegion,
724                                  &((WindowPtr)pSrc)->clipList);
725
726                 rfbCopyRegion(pSrc->pScreen, cl, &srcRegion, &dstRegion,
727                               dstx + pDst->x - srcx - pSrc->x,
728                               dsty + pDst->y - srcy - pSrc->y);
729
730                 REGION_UNINIT(pSrc->pScreen, &srcRegion);
731
732             } else {
733
734                 REGION_UNION(pScreen, &cl->modifiedRegion, &cl->modifiedRegion,
735                              &dstRegion);
736             }
737         }
738
739     } else {
740
741         ADD_TO_MODIFIED_REGION(pDst->pScreen, &dstRegion);
742     }
743
744     REGION_UNINIT(pDst->pScreen, &dstRegion);
745
746     rgn = (*pGC->ops->CopyArea) (pSrc, pDst, pGC, srcx, srcy, w, h,
747                                  dstx, dsty);
748
749     SCHEDULE_FB_UPDATE(pDst->pScreen, prfb);
750
751     GC_OP_EPILOGUE(pGC);
752
753     return rgn;
754 }
755
756
757 /*
758  * CopyPlane - the region being modified is the destination rectangle (clipped
759  * to the window clip region).
760  */
761
762 static RegionPtr
763 rfbCopyPlane (pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty, plane)
764     DrawablePtr   pSrc;
765     DrawablePtr   pDst;
766     register GCPtr pGC;
767     int           srcx,
768                   srcy;
769     int           w,
770                   h;
771     int           dstx,
772                   dsty;
773     unsigned long  plane;
774 {
775     RegionPtr rgn;
776     RegionRec tmpRegion;
777     BoxRec box;
778     GC_OP_PROLOGUE(pDst, pGC);
779
780     TRC(("rfbCopyPlane called\n"));
781
782     box.x1 = dstx + pDst->x;
783     box.y1 = dsty + pDst->y;
784     box.x2 = box.x1 + w;
785     box.y2 = box.y1 + h;
786
787     SAFE_REGION_INIT(pDst->pScreen, &tmpRegion, &box, 0);
788
789     REGION_INTERSECT(pDst->pScreen, &tmpRegion, &tmpRegion,
790                      WINDOW_CLIP_REGION((WindowPtr)pDst,pGC));
791
792     ADD_TO_MODIFIED_REGION(pDst->pScreen, &tmpRegion);
793
794     REGION_UNINIT(pDst->pScreen, &tmpRegion);
795
796     rgn = (*pGC->ops->CopyPlane) (pSrc, pDst, pGC, srcx, srcy, w, h,
797                                   dstx, dsty, plane);
798
799     SCHEDULE_FB_UPDATE(pDst->pScreen, prfb);
800
801     GC_OP_EPILOGUE(pGC);
802
803     return rgn;
804 }
805
806 /*
807  * PolyPoint - find the smallest rectangle which encloses the points drawn
808  * (and clip).
809  */
810
811 static void
812 rfbPolyPoint (pDrawable, pGC, mode, npt, pts)
813     DrawablePtr pDrawable;
814     GCPtr       pGC;
815     int         mode;           /* Origin or Previous */
816     int         npt;
817     xPoint      *pts;
818 {
819     int i;
820     RegionRec tmpRegion;
821     BoxRec box;
822     GC_OP_PROLOGUE(pDrawable, pGC);
823
824     TRC(("rfbPolyPoint called: %d points\n",npt));
825
826     if (npt) {
827         int minX = pts[0].x, maxX = pts[0].x;
828         int minY = pts[0].y, maxY = pts[0].y;
829
830         if (mode == CoordModePrevious)
831         {
832             int x = pts[0].x, y = pts[0].y;
833
834             for (i = 1; i < npt; i++) {
835                 x += pts[i].x;
836                 y += pts[i].y;
837                 if (x < minX) minX = x;
838                 if (x > maxX) maxX = x;
839                 if (y < minY) minY = y;
840                 if (y > maxY) maxY = y;
841             }
842         }
843         else
844         {
845             for (i = 1; i < npt; i++) {
846                 if (pts[i].x < minX) minX = pts[i].x;
847                 if (pts[i].x > maxX) maxX = pts[i].x;
848                 if (pts[i].y < minY) minY = pts[i].y;
849                 if (pts[i].y > maxY) maxY = pts[i].y;
850             }
851         }
852
853         box.x1 = minX + pDrawable->x;
854         box.y1 = minY + pDrawable->y;
855         box.x2 = maxX + 1 + pDrawable->x;
856         box.y2 = maxY + 1 + pDrawable->y;
857
858         SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0);
859
860         REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion,
861                          WINDOW_CLIP_REGION(((WindowPtr)pDrawable),pGC));
862
863         ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion);
864
865         REGION_UNINIT(pDrawable->pScreen, &tmpRegion);
866     }
867
868     (*pGC->ops->PolyPoint) (pDrawable, pGC, mode, npt, pts);
869
870     if (npt) {
871         SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
872     }
873
874     GC_OP_EPILOGUE(pGC);
875 }
876
877 /*
878  * PolyLines - take the union of bounding boxes around each line (and clip).
879  */
880
881 static void
882 rfbPolylines (pDrawable, pGC, mode, npt, ppts)
883     DrawablePtr   pDrawable;
884     GCPtr         pGC;
885     int           mode;
886     int           npt;
887     DDXPointPtr   ppts;
888 {
889     RegionPtr tmpRegion;
890     xRectangle regRects[MAX_RECTS_PER_OP];
891     int i, extra, nregRects, lw;
892     int prevX, prevY, curX, curY;
893     int rectX1, rectY1, rectX2, rectY2;
894     int minX, minY, maxX, maxY;
895     GC_OP_PROLOGUE(pDrawable, pGC);
896
897     TRC(("rfbPolylines called: %d points\n",npt));
898
899     if (npt) {
900         lw = pGC->lineWidth;
901         if (lw == 0)
902             lw = 1;
903
904         if (npt == 1)
905         {
906             nregRects = 1;
907             regRects[0].x = ppts[0].x - lw + pDrawable->x; /* being safe */
908             regRects[0].y = ppts[0].y - lw + pDrawable->y;
909             regRects[0].width = 2*lw;
910             regRects[0].height = 2*lw;
911         }
912         else
913         {
914             nregRects = npt - 1;
915
916             /*
917              * mitered joins can project quite a way from
918              * the line end; the 11 degree miter limit limits
919              * this extension to lw / (2 * tan(11/2)), rounded up
920              * and converted to int yields 6 * lw
921              */
922
923             if (pGC->joinStyle == JoinMiter) {
924                 extra = 6 * lw;
925             } else {
926                 extra = lw / 2;
927             }
928
929             prevX = ppts[0].x + pDrawable->x;
930             prevY = ppts[0].y + pDrawable->y;
931             minX = maxX = prevX;
932             minY = maxY = prevY;
933
934             for (i = 0; i < nregRects; i++) {
935                 if (mode == CoordModeOrigin) {
936                     curX = pDrawable->x + ppts[i+1].x;
937                     curY = pDrawable->y + ppts[i+1].y;
938                 } else {
939                     curX = prevX + ppts[i+1].x;
940                     curY = prevY + ppts[i+1].y;
941                 }
942
943                 if (prevX > curX) {
944                     rectX1 = curX - extra;
945                     rectX2 = prevX + extra + 1;
946                 } else {
947                     rectX1 = prevX - extra;
948                     rectX2 = curX + extra + 1;
949                 }
950
951                 if (prevY > curY) {
952                     rectY1 = curY - extra;
953                     rectY2 = prevY + extra + 1;
954                 } else {
955                     rectY1 = prevY - extra;
956                     rectY2 = curY + extra + 1;
957                 }
958
959                 if (nregRects <= MAX_RECTS_PER_OP) {
960                     regRects[i].x = rectX1;
961                     regRects[i].y = rectY1;
962                     regRects[i].width = rectX2 - rectX1;
963                     regRects[i].height = rectY2 - rectY1;
964                 } else {
965                     if (rectX1 < minX) minX = rectX1;
966                     if (rectY1 < minY) minY = rectY1;
967                     if (rectX2 > maxX) maxX = rectX2;
968                     if (rectY2 > maxY) maxY = rectY2;
969                 }
970
971                 prevX = curX;
972                 prevY = curY;
973             }
974
975             if (nregRects > MAX_RECTS_PER_OP) {
976                 regRects[0].x = minX;
977                 regRects[0].y = minY;
978                 regRects[0].width = maxX - minX;
979                 regRects[0].height = maxY - minY;
980                 nregRects = 1;
981             }
982         }
983
984         tmpRegion = RECTS_TO_REGION(pDrawable->pScreen, nregRects, regRects,
985                                     CT_NONE);
986         REGION_INTERSECT(pDrawable->pScreen, tmpRegion, tmpRegion,
987                          WINDOW_CLIP_REGION((WindowPtr)pDrawable,pGC));
988
989         ADD_TO_MODIFIED_REGION(pDrawable->pScreen, tmpRegion);
990
991         REGION_DESTROY(pDrawable->pScreen, tmpRegion);
992     }
993
994     (*pGC->ops->Polylines) (pDrawable, pGC, mode, npt, ppts);
995
996     if (npt) {
997         SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
998     }
999
1000     GC_OP_EPILOGUE(pGC);
1001 }
1002
1003 /*
1004  * PolySegment - take the union of bounding boxes around each segment (and
1005  * clip).
1006  */
1007
1008 static void
1009 rfbPolySegment(pDrawable, pGC, nseg, segs)
1010     DrawablePtr pDrawable;
1011     GCPtr       pGC;
1012     int         nseg;
1013     xSegment    *segs;
1014 {
1015     RegionPtr tmpRegion;
1016     xRectangle regRects[MAX_RECTS_PER_OP];
1017     int i, extra, lw, nregRects;
1018     int rectX1, rectY1, rectX2, rectY2;
1019     int minX, minY, maxX, maxY;
1020
1021     GC_OP_PROLOGUE(pDrawable, pGC);
1022
1023     TRC(("rfbPolySegment called: %d segments\n",nseg));
1024
1025     if (nseg) {
1026         nregRects = nseg;
1027         lw = pGC->lineWidth;
1028         extra = lw / 2;
1029
1030         minX = maxX = segs[0].x1;
1031         minY = maxY = segs[0].y1;
1032
1033         for (i = 0; i < nseg; i++)
1034         {
1035             if (segs[i].x1 > segs[i].x2) {
1036                 rectX1 = pDrawable->x + segs[i].x2 - extra;
1037                 rectX2 = pDrawable->x + segs[i].x1 + extra + 1;
1038             } else {
1039                 rectX1 = pDrawable->x + segs[i].x1 - extra;
1040                 rectX2 = pDrawable->x + segs[i].x2 + extra + 1;
1041             }
1042
1043             if (segs[i].y1 > segs[i].y2) {
1044                 rectY1 = pDrawable->y + segs[i].y2 - extra;
1045                 rectY2 = pDrawable->y + segs[i].y1 + extra + 1;
1046             } else {
1047                 rectY1 = pDrawable->y + segs[i].y1 - extra;
1048                 rectY2 = pDrawable->y + segs[i].y2 + extra + 1;
1049             }
1050
1051             if (nseg <= MAX_RECTS_PER_OP) {
1052                 regRects[i].x = rectX1;
1053                 regRects[i].y = rectY1;
1054                 regRects[i].width = rectX2 - rectX1;
1055                 regRects[i].height = rectY2 - rectY1;
1056             } else {
1057                 if (rectX1 < minX) minX = rectX1;
1058                 if (rectY1 < minY) minY = rectY1;
1059                 if (rectX2 > maxX) maxX = rectX2;
1060                 if (rectY2 > maxY) maxY = rectY2;
1061             }
1062         }
1063
1064         if (nseg > MAX_RECTS_PER_OP) {
1065             regRects[0].x = minX;
1066             regRects[0].y = minY;
1067             regRects[0].width = maxX - minX;
1068             regRects[0].height = maxY - minY;
1069             nregRects = 1;
1070         }
1071
1072         tmpRegion = RECTS_TO_REGION(pDrawable->pScreen, nregRects, regRects,
1073                                     CT_NONE);
1074         REGION_INTERSECT(pDrawable->pScreen, tmpRegion, tmpRegion,
1075                          WINDOW_CLIP_REGION((WindowPtr)pDrawable,pGC));
1076
1077         ADD_TO_MODIFIED_REGION(pDrawable->pScreen, tmpRegion);
1078
1079         REGION_DESTROY(pDrawable->pScreen, tmpRegion);
1080     }
1081
1082     (*pGC->ops->PolySegment) (pDrawable, pGC, nseg, segs);
1083
1084     if (nseg) {
1085         SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
1086     }
1087
1088     GC_OP_EPILOGUE(pGC);
1089 }
1090
1091 /*
1092  * PolyRectangle (rectangle outlines) - take the union of bounding boxes
1093  * around each line (and clip).
1094  */
1095
1096 static void
1097 rfbPolyRectangle(pDrawable, pGC, nrects, rects)
1098     DrawablePtr pDrawable;
1099     GCPtr       pGC;
1100     int         nrects;
1101     xRectangle  *rects;
1102 {
1103     int i, extra, lw, nregRects;
1104     int rectX1, rectY1, rectX2, rectY2;
1105     int minX, minY, maxX, maxY;
1106     RegionPtr tmpRegion;
1107     xRectangle regRects[MAX_RECTS_PER_OP*4];
1108     GC_OP_PROLOGUE(pDrawable, pGC);
1109
1110     TRC(("rfbPolyRectangle called: %d rects\n",nrects));
1111
1112     if (nrects) {
1113         nregRects = nrects * 4;
1114         lw = pGC->lineWidth;
1115         extra = lw / 2;
1116
1117         minX = maxX = rects[0].x;
1118         minY = maxY = rects[0].y;
1119
1120         for (i = 0; i < nrects; i++)
1121         {
1122             if (nrects <= MAX_RECTS_PER_OP) {
1123                 regRects[i*4].x = rects[i].x - extra + pDrawable->x;
1124                 regRects[i*4].y = rects[i].y - extra + pDrawable->y;
1125                 regRects[i*4].width = rects[i].width + 1 + 2 * extra;
1126                 regRects[i*4].height = 1 + 2 * extra;
1127
1128                 regRects[i*4+1].x = rects[i].x - extra + pDrawable->x;
1129                 regRects[i*4+1].y = rects[i].y - extra + pDrawable->y;
1130                 regRects[i*4+1].width = 1 + 2 * extra;
1131                 regRects[i*4+1].height = rects[i].height + 1 + 2 * extra;
1132
1133                 regRects[i*4+2].x
1134                     = rects[i].x + rects[i].width - extra + pDrawable->x;
1135                 regRects[i*4+2].y = rects[i].y - extra + pDrawable->y;
1136                 regRects[i*4+2].width = 1 + 2 * extra;
1137                 regRects[i*4+2].height = rects[i].height + 1 + 2 * extra;
1138
1139                 regRects[i*4+3].x = rects[i].x - extra + pDrawable->x;
1140                 regRects[i*4+3].y
1141                     = rects[i].y + rects[i].height - extra + pDrawable->y;
1142                 regRects[i*4+3].width = rects[i].width + 1 + 2 * extra;
1143                 regRects[i*4+3].height = 1 + 2 * extra;
1144             } else {
1145                 rectX1 = pDrawable->x + rects[i].x - extra;
1146                 rectY1 = pDrawable->y + rects[i].y - extra;
1147                 rectX2 = pDrawable->x + rects[i].x + rects[i].width + extra+1;
1148                 rectY2 = pDrawable->y + rects[i].y + rects[i].height + extra+1;
1149                 if (rectX1 < minX) minX = rectX1;
1150                 if (rectY1 < minY) minY = rectY1;
1151                 if (rectX2 > maxX) maxX = rectX2;
1152                 if (rectY2 > maxY) maxY = rectY2;
1153             }
1154         }
1155
1156         if (nrects > MAX_RECTS_PER_OP) {
1157             regRects[0].x = minX;
1158             regRects[0].y = minY;
1159             regRects[0].width = maxX - minX;
1160             regRects[0].height = maxY - minY;
1161             nregRects = 1;
1162         }
1163
1164         tmpRegion = RECTS_TO_REGION(pDrawable->pScreen, nregRects,
1165                                     regRects, CT_NONE);
1166         REGION_INTERSECT(pDrawable->pScreen, tmpRegion, tmpRegion,
1167                          WINDOW_CLIP_REGION((WindowPtr)pDrawable,pGC));
1168
1169         ADD_TO_MODIFIED_REGION(pDrawable->pScreen, tmpRegion);
1170
1171         REGION_DESTROY(pDrawable->pScreen, tmpRegion);
1172     }
1173
1174     (*pGC->ops->PolyRectangle) (pDrawable, pGC, nrects, rects);
1175
1176     if (nrects) {
1177         SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
1178     }
1179
1180     GC_OP_EPILOGUE(pGC);
1181 }
1182
1183 /*
1184  * PolyArc - take the union of bounding boxes around each arc (and clip).
1185  * Bounding boxes assume each is a full circle / ellipse.
1186  */
1187
1188 static void
1189 rfbPolyArc(pDrawable, pGC, narcs, arcs)
1190     DrawablePtr pDrawable;
1191     register GCPtr      pGC;
1192     int         narcs;
1193     xArc        *arcs;
1194 {
1195     int i, extra, lw, nregRects;
1196     int rectX1, rectY1, rectX2, rectY2;
1197     int minX, minY, maxX, maxY;
1198     RegionPtr tmpRegion;
1199     xRectangle regRects[MAX_RECTS_PER_OP];
1200     GC_OP_PROLOGUE(pDrawable, pGC);
1201
1202     TRC(("rfbPolyArc called: %d arcs\n",narcs));
1203
1204     if (narcs) {
1205         nregRects = narcs;
1206         lw = pGC->lineWidth;
1207         if (lw == 0)
1208             lw = 1;
1209         extra = lw / 2;
1210
1211         minX = maxX = arcs[0].x;
1212         minY = maxY = arcs[0].y;
1213
1214         for (i = 0; i < narcs; i++)
1215         {
1216             if (narcs <= MAX_RECTS_PER_OP) {
1217                 regRects[i].x = arcs[i].x - extra + pDrawable->x;
1218                 regRects[i].y = arcs[i].y - extra + pDrawable->y;
1219                 regRects[i].width = arcs[i].width + lw;
1220                 regRects[i].height = arcs[i].height + lw;
1221             } else {
1222                 rectX1 = pDrawable->x + arcs[i].x - extra;
1223                 rectY1 = pDrawable->y + arcs[i].y - extra;
1224                 rectX2 = pDrawable->x + arcs[i].x + arcs[i].width + lw;
1225                 rectY2 = pDrawable->y + arcs[i].y + arcs[i].height + lw;
1226                 if (rectX1 < minX) minX = rectX1;
1227                 if (rectY1 < minY) minY = rectY1;
1228                 if (rectX2 > maxX) maxX = rectX2;
1229                 if (rectY2 > maxY) maxY = rectY2;
1230             }
1231         }
1232
1233         if (narcs > MAX_RECTS_PER_OP) {
1234             regRects[0].x = minX;
1235             regRects[0].y = minY;
1236             regRects[0].width = maxX - minX;
1237             regRects[0].height = maxY - minY;
1238             nregRects = 1;
1239         }
1240
1241         tmpRegion = RECTS_TO_REGION(pDrawable->pScreen, nregRects, regRects,
1242                                     CT_NONE);
1243         REGION_INTERSECT(pDrawable->pScreen, tmpRegion, tmpRegion,
1244                          WINDOW_CLIP_REGION((WindowPtr)pDrawable,pGC));
1245
1246         ADD_TO_MODIFIED_REGION(pDrawable->pScreen, tmpRegion);
1247
1248         REGION_DESTROY(pDrawable->pScreen, tmpRegion);
1249     }
1250
1251     (*pGC->ops->PolyArc) (pDrawable, pGC, narcs, arcs);
1252
1253     if (narcs) {
1254         SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
1255     }
1256
1257     GC_OP_EPILOGUE(pGC);
1258 }
1259
1260 /*
1261  * FillPolygon - take bounding box around polygon (and clip).
1262  */
1263
1264 static void
1265 rfbFillPolygon(pDrawable, pGC, shape, mode, count, pts)
1266     register DrawablePtr pDrawable;
1267     register GCPtr      pGC;
1268     int                 shape, mode;
1269     int                 count;
1270     DDXPointPtr         pts;
1271 {
1272     int i;
1273     RegionRec tmpRegion;
1274     BoxRec box;
1275     GC_OP_PROLOGUE(pDrawable, pGC);
1276
1277     TRC(("rfbFillPolygon called\n"));
1278
1279     if (count) {
1280         int minX = pts[0].x, maxX = pts[0].x;
1281         int minY = pts[0].y, maxY = pts[0].y;
1282
1283         if (mode == CoordModePrevious)
1284         {
1285             int x = pts[0].x, y = pts[0].y;
1286
1287             for (i = 1; i < count; i++) {
1288                 x += pts[i].x;
1289                 y += pts[i].y;
1290                 if (x < minX) minX = x;
1291                 if (x > maxX) maxX = x;
1292                 if (y < minY) minY = y;
1293                 if (y > maxY) maxY = y;
1294             }
1295         }
1296         else
1297         {
1298             for (i = 1; i < count; i++) {
1299                 if (pts[i].x < minX) minX = pts[i].x;
1300                 if (pts[i].x > maxX) maxX = pts[i].x;
1301                 if (pts[i].y < minY) minY = pts[i].y;
1302                 if (pts[i].y > maxY) maxY = pts[i].y;
1303             }
1304         }
1305
1306         box.x1 = minX + pDrawable->x;
1307         box.y1 = minY + pDrawable->y;
1308         box.x2 = maxX + 1 + pDrawable->x;
1309         box.y2 = maxY + 1 + pDrawable->y;
1310
1311         SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0);
1312
1313         REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion,
1314                          WINDOW_CLIP_REGION(((WindowPtr)pDrawable),pGC));
1315
1316         ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion);
1317
1318         REGION_UNINIT(pDrawable->pScreen, &tmpRegion);
1319     }
1320
1321     (*pGC->ops->FillPolygon) (pDrawable, pGC, shape, mode, count, pts);
1322
1323     if (count) {
1324         SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
1325     }
1326
1327     GC_OP_EPILOGUE(pGC);
1328 }
1329
1330 /*
1331  * PolyFillRect - take the union of the given rectangles (and clip).
1332  */
1333
1334 static void
1335 rfbPolyFillRect(pDrawable, pGC, nrects, rects)
1336     DrawablePtr pDrawable;
1337     GCPtr       pGC;
1338     int         nrects;
1339     xRectangle  *rects;
1340 {
1341     RegionPtr tmpRegion;
1342     xRectangle regRects[MAX_RECTS_PER_OP];
1343     int i, nregRects;
1344     int rectX1, rectY1, rectX2, rectY2;
1345     int minX, minY, maxX, maxY;
1346     GC_OP_PROLOGUE(pDrawable, pGC);
1347
1348     TRC(("rfbPolyFillRect called: %d rects\n",nrects));
1349
1350     if (nrects) {
1351         nregRects = nrects;
1352
1353         minX = maxX = rects[0].x;
1354         minY = maxY = rects[0].y;
1355
1356         for (i = 0; i < nrects; i++) {
1357             if (nrects <= MAX_RECTS_PER_OP) {
1358                 regRects[i].x = rects[i].x + pDrawable->x;
1359                 regRects[i].y = rects[i].y + pDrawable->y;
1360                 regRects[i].width = rects[i].width;
1361                 regRects[i].height = rects[i].height;
1362             } else {
1363                 rectX1 = pDrawable->x + rects[i].x;
1364                 rectY1 = pDrawable->y + rects[i].y;
1365                 rectX2 = pDrawable->x + rects[i].x + rects[i].width;
1366                 rectY2 = pDrawable->y + rects[i].y + rects[i].height;
1367                 if (rectX1 < minX) minX = rectX1;
1368                 if (rectY1 < minY) minY = rectY1;
1369                 if (rectX2 > maxX) maxX = rectX2;
1370                 if (rectY2 > maxY) maxY = rectY2;
1371             }
1372         }
1373
1374         if (nrects > MAX_RECTS_PER_OP) {
1375             regRects[0].x = minX;
1376             regRects[0].y = minY;
1377             regRects[0].width = maxX - minX;
1378             regRects[0].height = maxY - minY;
1379             nregRects = 1;
1380         }
1381
1382         tmpRegion = RECTS_TO_REGION(pDrawable->pScreen, nregRects, regRects,
1383                                     CT_NONE);
1384         REGION_INTERSECT(pDrawable->pScreen, tmpRegion, tmpRegion,
1385                          WINDOW_CLIP_REGION((WindowPtr)pDrawable,pGC));
1386
1387         ADD_TO_MODIFIED_REGION(pDrawable->pScreen, tmpRegion);
1388
1389         REGION_DESTROY(pDrawable->pScreen, tmpRegion);
1390     }
1391
1392     (*pGC->ops->PolyFillRect) (pDrawable, pGC, nrects, rects);
1393
1394     if (nrects) {
1395         SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
1396     }
1397
1398     GC_OP_EPILOGUE(pGC);
1399 }
1400
1401 /*
1402  * PolyFillArc - take the union of bounding boxes around each arc (and clip).
1403  * Bounding boxes assume each is a full circle / ellipse.
1404  */
1405
1406 static void
1407 rfbPolyFillArc(pDrawable, pGC, narcs, arcs)
1408     DrawablePtr pDrawable;
1409     GCPtr       pGC;
1410     int         narcs;
1411     xArc        *arcs;
1412 {
1413     int i, extra, lw, nregRects;
1414     int rectX1, rectY1, rectX2, rectY2;
1415     int minX, minY, maxX, maxY;
1416     RegionPtr tmpRegion;
1417     xRectangle regRects[MAX_RECTS_PER_OP];
1418     GC_OP_PROLOGUE(pDrawable, pGC);
1419
1420     TRC(("rfbPolyFillArc called: %d arcs\n",narcs));
1421
1422     if (narcs) {
1423         nregRects = narcs;
1424         lw = pGC->lineWidth;
1425         if (lw == 0)
1426             lw = 1;
1427         extra = lw / 2;
1428
1429         minX = maxX = arcs[0].x;
1430         minY = maxY = arcs[0].y;
1431
1432         for (i = 0; i < narcs; i++)
1433         {
1434             if (narcs <= MAX_RECTS_PER_OP) {
1435                 regRects[i].x = arcs[i].x - extra + pDrawable->x;
1436                 regRects[i].y = arcs[i].y - extra + pDrawable->y;
1437                 regRects[i].width = arcs[i].width + lw;
1438                 regRects[i].height = arcs[i].height + lw;
1439             } else {
1440                 rectX1 = pDrawable->x + arcs[i].x - extra;
1441                 rectY1 = pDrawable->y + arcs[i].y - extra;
1442                 rectX2 = pDrawable->x + arcs[i].x + arcs[i].width + lw;
1443                 rectY2 = pDrawable->y + arcs[i].y + arcs[i].height + lw;
1444                 if (rectX1 < minX) minX = rectX1;
1445                 if (rectY1 < minY) minY = rectY1;
1446                 if (rectX2 > maxX) maxX = rectX2;
1447                 if (rectY2 > maxY) maxY = rectY2;
1448             }
1449         }
1450
1451         if (narcs > MAX_RECTS_PER_OP) {
1452             regRects[0].x = minX;
1453             regRects[0].y = minY;
1454             regRects[0].width = maxX - minX;
1455             regRects[0].height = maxY - minY;
1456             nregRects = 1;
1457         }
1458
1459         tmpRegion = RECTS_TO_REGION(pDrawable->pScreen, nregRects, regRects,
1460                                     CT_NONE);
1461         REGION_INTERSECT(pDrawable->pScreen, tmpRegion, tmpRegion,
1462                          WINDOW_CLIP_REGION((WindowPtr)pDrawable,pGC));
1463
1464         ADD_TO_MODIFIED_REGION(pDrawable->pScreen, tmpRegion);
1465
1466         REGION_DESTROY(pDrawable->pScreen, tmpRegion);
1467     }
1468
1469     (*pGC->ops->PolyFillArc) (pDrawable, pGC, narcs, arcs);
1470
1471     if (narcs) {
1472         SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
1473     }
1474
1475     GC_OP_EPILOGUE(pGC);
1476 }
1477
1478 /*
1479  * Get a rough bounding box around n characters of the given font.
1480  */
1481
1482 static void GetTextBoundingBox(pDrawable, font, x, y, n, pbox)
1483     DrawablePtr pDrawable;
1484     FontPtr font;
1485     int x, y, n;
1486     BoxPtr pbox;
1487 {
1488     int maxAscent, maxDescent, maxCharWidth;
1489
1490     if (FONTASCENT(font) > FONTMAXBOUNDS(font,ascent))
1491         maxAscent = FONTASCENT(font);
1492     else
1493         maxAscent = FONTMAXBOUNDS(font,ascent);
1494
1495     if (FONTDESCENT(font) > FONTMAXBOUNDS(font,descent))
1496         maxDescent = FONTDESCENT(font);
1497     else
1498         maxDescent = FONTMAXBOUNDS(font,descent);
1499
1500     if (FONTMAXBOUNDS(font,rightSideBearing) > FONTMAXBOUNDS(font,characterWidth))
1501         maxCharWidth = FONTMAXBOUNDS(font,rightSideBearing);
1502     else
1503         maxCharWidth = FONTMAXBOUNDS(font,characterWidth);
1504
1505     pbox->x1 = pDrawable->x + x;
1506     pbox->y1 = pDrawable->y + y - maxAscent;
1507     pbox->x2 = pbox->x1 + maxCharWidth * n;
1508     pbox->y2 = pbox->y1 + maxAscent + maxDescent;
1509
1510     if (FONTMINBOUNDS(font,leftSideBearing) < 0) {
1511         pbox->x1 += FONTMINBOUNDS(font,leftSideBearing);
1512     }
1513 }
1514
1515
1516 /*
1517  * PolyText8 - use rough bounding box.
1518  */
1519
1520 static int
1521 rfbPolyText8(pDrawable, pGC, x, y, count, chars)
1522     DrawablePtr pDrawable;
1523     GCPtr       pGC;
1524     int         x, y;
1525     int         count;
1526     char        *chars;
1527 {
1528     int ret;
1529     RegionRec tmpRegion;
1530     BoxRec box;
1531     GC_OP_PROLOGUE(pDrawable, pGC);
1532
1533     TRC(("rfbPolyText8 called '%.*s'\n",count,chars));
1534
1535     if (count) {
1536         GetTextBoundingBox(pDrawable, pGC->font, x, y, count, &box);
1537
1538         SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0);
1539
1540         REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion,
1541                          WINDOW_CLIP_REGION(((WindowPtr)pDrawable),pGC));
1542
1543         ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion);
1544
1545         REGION_UNINIT(pDrawable->pScreen, &tmpRegion);
1546     }
1547
1548     ret = (*pGC->ops->PolyText8) (pDrawable, pGC, x, y, count, chars);
1549
1550     if (count) {
1551         SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
1552     }
1553
1554     GC_OP_EPILOGUE(pGC);
1555     return ret;
1556 }
1557
1558 /*
1559  * PolyText16 - use rough bounding box.
1560  */
1561
1562 static int
1563 rfbPolyText16(pDrawable, pGC, x, y, count, chars)
1564     DrawablePtr pDrawable;
1565     GCPtr       pGC;
1566     int         x, y;
1567     int         count;
1568     unsigned short *chars;
1569 {
1570     int ret;
1571     RegionRec tmpRegion;
1572     BoxRec box;
1573     GC_OP_PROLOGUE(pDrawable, pGC);
1574
1575     TRC(("rfbPolyText16 called\n"));
1576
1577     if (count) {
1578         GetTextBoundingBox(pDrawable, pGC->font, x, y, count, &box);
1579
1580         SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0);
1581
1582         REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion,
1583                          WINDOW_CLIP_REGION(((WindowPtr)pDrawable),pGC));
1584
1585         ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion);
1586
1587         REGION_UNINIT(pDrawable->pScreen, &tmpRegion);
1588     }
1589
1590     ret = (*pGC->ops->PolyText16) (pDrawable, pGC, x, y, count, chars);
1591
1592     if (count) {
1593         SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
1594     }
1595
1596     GC_OP_EPILOGUE(pGC);
1597     return ret;
1598 }
1599
1600 /*
1601  * ImageText8 - use rough bounding box.
1602  */
1603
1604 static void
1605 rfbImageText8(pDrawable, pGC, x, y, count, chars)
1606     DrawablePtr pDrawable;
1607     GCPtr       pGC;
1608     int         x, y;
1609     int         count;
1610     char        *chars;
1611 {
1612     RegionRec tmpRegion;
1613     BoxRec box;
1614     GC_OP_PROLOGUE(pDrawable, pGC);
1615
1616     TRC(("rfbImageText8 called '%.*s'\n",count,chars));
1617
1618     if (count) {
1619         GetTextBoundingBox(pDrawable, pGC->font, x, y, count, &box);
1620
1621         SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0);
1622
1623         REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion,
1624                          WINDOW_CLIP_REGION(((WindowPtr)pDrawable),pGC));
1625
1626         ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion);
1627
1628         REGION_UNINIT(pDrawable->pScreen, &tmpRegion);
1629     }
1630
1631     (*pGC->ops->ImageText8) (pDrawable, pGC, x, y, count, chars);
1632
1633     if (count) {
1634         SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
1635     }
1636
1637     GC_OP_EPILOGUE(pGC);
1638 }
1639
1640 /*
1641  * ImageText16 - use rough bounding box.
1642  */
1643
1644 static void
1645 rfbImageText16(pDrawable, pGC, x, y, count, chars)
1646     DrawablePtr pDrawable;
1647     GCPtr       pGC;
1648     int         x, y;
1649     int         count;
1650     unsigned short *chars;
1651 {
1652     RegionRec tmpRegion;
1653     BoxRec box;
1654     GC_OP_PROLOGUE(pDrawable, pGC);
1655
1656     TRC(("rfbImageText16 called\n"));
1657
1658     if (count) {
1659         GetTextBoundingBox(pDrawable, pGC->font, x, y, count, &box);
1660
1661         SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0);
1662
1663         REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion,
1664                          WINDOW_CLIP_REGION(((WindowPtr)pDrawable),pGC));
1665
1666         ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion);
1667
1668         REGION_UNINIT(pDrawable->pScreen, &tmpRegion);
1669     }
1670
1671     (*pGC->ops->ImageText16) (pDrawable, pGC, x, y, count, chars);
1672
1673     if (count) {
1674         SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
1675     }
1676
1677     GC_OP_EPILOGUE(pGC);
1678 }
1679
1680 /*
1681  * ImageGlyphBlt - use rough bounding box.
1682  */
1683
1684 static void
1685 rfbImageGlyphBlt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase)
1686     DrawablePtr pDrawable;
1687     GCPtr       pGC;
1688     int         x, y;
1689     unsigned int nglyph;
1690     CharInfoPtr *ppci;          /* array of character info */
1691     pointer     pglyphBase;     /* start of array of glyphs */
1692 {
1693     RegionRec tmpRegion;
1694     BoxRec box;
1695     GC_OP_PROLOGUE(pDrawable, pGC);
1696
1697     TRC(("rfbImageGlyphBlt called\n"));
1698
1699     if (nglyph) {
1700         GetTextBoundingBox(pDrawable, pGC->font, x, y, nglyph, &box);
1701
1702         SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0);
1703
1704         REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion,
1705                          WINDOW_CLIP_REGION(((WindowPtr)pDrawable),pGC));
1706
1707         ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion);
1708
1709         REGION_UNINIT(pDrawable->pScreen, &tmpRegion);
1710     }
1711
1712     (*pGC->ops->ImageGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci,pglyphBase);
1713
1714     if (nglyph) {
1715         SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
1716     }
1717
1718     GC_OP_EPILOGUE(pGC);
1719 }
1720
1721 /*
1722  * PolyGlyphBlt - use rough bounding box.
1723  */
1724
1725 static void
1726 rfbPolyGlyphBlt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase)
1727     DrawablePtr pDrawable;
1728     GCPtr       pGC;
1729     int         x, y;
1730     unsigned int nglyph;
1731     CharInfoPtr *ppci;          /* array of character info */
1732     pointer     pglyphBase;     /* start of array of glyphs */
1733 {
1734     RegionRec tmpRegion;
1735     BoxRec box;
1736     GC_OP_PROLOGUE(pDrawable, pGC);
1737
1738     TRC(("rfbPolyGlyphBlt called\n"));
1739
1740     if (nglyph) {
1741         GetTextBoundingBox(pDrawable, pGC->font, x, y, nglyph, &box);
1742
1743         SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0);
1744
1745         REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion,
1746                          WINDOW_CLIP_REGION(((WindowPtr)pDrawable),pGC));
1747
1748         ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion);
1749
1750         REGION_UNINIT(pDrawable->pScreen, &tmpRegion);
1751     }
1752
1753     (*pGC->ops->PolyGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci, pglyphBase);
1754
1755     if (nglyph) {
1756         SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
1757     }
1758
1759     GC_OP_EPILOGUE(pGC);
1760 }
1761
1762 /*
1763  * PushPixels - be fairly safe - region modified is intersection of the given
1764  * rectangle with the window clip region.
1765  */
1766
1767 static void
1768 rfbPushPixels(pGC, pBitMap, pDrawable, w, h, x, y)
1769     GCPtr       pGC;
1770     PixmapPtr   pBitMap;
1771     DrawablePtr pDrawable;
1772     int         w, h, x, y;
1773 {
1774     RegionRec tmpRegion;
1775     BoxRec box;
1776     GC_OP_PROLOGUE(pDrawable, pGC);
1777
1778     TRC(("rfbPushPixels called\n"));
1779
1780     box.x1 = x + pDrawable->x;
1781     box.y1 = y + pDrawable->y;
1782     box.x2 = box.x1 + w;
1783     box.y2 = box.y1 + h;
1784
1785     SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0);
1786
1787     REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion,
1788                      WINDOW_CLIP_REGION(((WindowPtr)pDrawable),pGC));
1789
1790     ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion);
1791
1792     REGION_UNINIT(pDrawable->pScreen, &tmpRegion);
1793
1794     (*pGC->ops->PushPixels) (pGC, pBitMap, pDrawable, w, h, x, y);
1795
1796     SCHEDULE_FB_UPDATE(pDrawable->pScreen, prfb);
1797
1798     GC_OP_EPILOGUE(pGC);
1799 }
1800
1801
1802
1803 /****************************************************************************/
1804 /*
1805  * Other functions
1806  */
1807 /****************************************************************************/
1808
1809 /*
1810  * rfbCopyRegion.  Args are src and dst regions plus a translation (dx,dy).
1811  * Takes these args together with the existing modified region and possibly an
1812  * existing copy region and translation.  Produces a combined modified region
1813  * plus copy region and translation.  Note that the copy region is the
1814  * destination of the copy.
1815  *
1816  * First we trim parts of src which are invalid (ie in the modified region).
1817  * Then we see if there is any overlap between the src and the existing copy
1818  * region.  If not then the two copies cannot be combined, so we choose
1819  * whichever is bigger to form the basis of a new copy, while the other copy is
1820  * just done the hard way by being added to the modified region.  So if the
1821  * existing copy is bigger then we simply add the destination of the new copy
1822  * to the modified region and we're done.  If the new copy is bigger, we add
1823  * the old copy region to the modified region and behave as though there is no
1824  * existing copy region.
1825  * 
1826  * At this stage we now know that either the two copies can be combined, or
1827  * that there is no existing copy.  We temporarily add both the existing copy
1828  * region and dst to the modified region (this is the entire area of the screen
1829  * affected in any way).  Finally we calculate the new copy region, and remove
1830  * it from the modified region.
1831  *
1832  * Note:
1833  *   1. The src region is modified by this routine.
1834  *   2. When the copy region is empty, copyDX and copyDY MUST be set to zero.
1835  */
1836
1837 static void
1838 rfbCopyRegion(pScreen, cl, src, dst, dx, dy)
1839     ScreenPtr pScreen;
1840     rfbClientPtr cl;
1841     RegionPtr src;
1842     RegionPtr dst;
1843     int dx, dy;
1844 {
1845     RegionRec tmp;
1846
1847     /* src = src - modifiedRegion */
1848
1849     REGION_SUBTRACT(pScreen, src, src, &cl->modifiedRegion);
1850
1851     if (REGION_NOTEMPTY(pScreen, &cl->copyRegion)) {
1852
1853         REGION_INIT(pScreen, &tmp, NullBox, 0);
1854         REGION_INTERSECT(pScreen, &tmp, src, &cl->copyRegion);
1855
1856         if (REGION_NOTEMPTY(pScreen, &tmp)) {
1857
1858             /* if src and copyRegion overlap:
1859                  src = src intersect copyRegion */
1860
1861             REGION_COPY(pScreen, src, &tmp);
1862
1863         } else {
1864
1865             /* if no overlap, find bigger region */
1866
1867             int newArea = (((REGION_EXTENTS(pScreen,src))->x2
1868                             - (REGION_EXTENTS(pScreen,src))->x1)
1869                            * ((REGION_EXTENTS(pScreen,src))->y2
1870                               - (REGION_EXTENTS(pScreen,src))->y1));
1871
1872             int oldArea = (((REGION_EXTENTS(pScreen,&cl->copyRegion))->x2
1873                             - (REGION_EXTENTS(pScreen,&cl->copyRegion))->x1)
1874                            * ((REGION_EXTENTS(pScreen,&cl->copyRegion))->y2
1875                              - (REGION_EXTENTS(pScreen,&cl->copyRegion))->y1));
1876
1877             if (oldArea > newArea) {
1878
1879                 /* existing copy is bigger:
1880                      modifiedRegion = modifiedRegion union dst
1881                      copyRegion = copyRegion - dst
1882                      return */
1883
1884                 REGION_UNION(pScreen, &cl->modifiedRegion, &cl->modifiedRegion,
1885                              dst);
1886                 REGION_SUBTRACT(pScreen, &cl->copyRegion, &cl->copyRegion,
1887                                 dst);
1888                 if (!REGION_NOTEMPTY(pScreen, &cl->copyRegion)) {
1889                     cl->copyDX = 0;
1890                     cl->copyDY = 0;
1891                 }
1892                 return;
1893             }
1894
1895             /* new copy is bigger:
1896                  modifiedRegion = modifiedRegion union copyRegion
1897                  copyRegion = empty */
1898
1899             REGION_UNION(pScreen, &cl->modifiedRegion, &cl->modifiedRegion,
1900                          &cl->copyRegion);
1901             REGION_EMPTY(pScreen, &cl->copyRegion);
1902             cl->copyDX = cl->copyDY = 0;
1903         }
1904     }
1905
1906
1907     /* modifiedRegion = modifiedRegion union dst union copyRegion */
1908
1909     REGION_UNION(pScreen, &cl->modifiedRegion, &cl->modifiedRegion, dst);
1910     REGION_UNION(pScreen, &cl->modifiedRegion, &cl->modifiedRegion,
1911                  &cl->copyRegion);
1912
1913     /* copyRegion = T(src) intersect dst */
1914
1915     REGION_TRANSLATE(pScreen, src, dx, dy);
1916     REGION_INTERSECT(pScreen, &cl->copyRegion, src, dst);
1917
1918     /* modifiedRegion = modifiedRegion - copyRegion */
1919
1920     REGION_SUBTRACT(pScreen, &cl->modifiedRegion, &cl->modifiedRegion,
1921                     &cl->copyRegion);
1922
1923     /* combine new translation T with existing translation */
1924
1925     if (REGION_NOTEMPTY(pScreen, &cl->copyRegion)) {
1926         cl->copyDX += dx;
1927         cl->copyDY += dy;
1928     } else {
1929         cl->copyDX = 0;
1930         cl->copyDY = 0;
1931     }
1932 }
1933
1934
1935 /*
1936  * rfbDeferredUpdateCallback() is called when a client's deferredUpdateTimer
1937  * goes off.
1938  */
1939
1940 static CARD32
1941 rfbDeferredUpdateCallback(OsTimerPtr timer, CARD32 now, pointer arg)
1942 {
1943   rfbClientPtr cl = (rfbClientPtr)arg;
1944
1945   rfbSendFramebufferUpdate(cl);
1946
1947   cl->deferredUpdateScheduled = FALSE;
1948   return 0;
1949 }
1950
1951
1952 /*
1953  * rfbScheduleDeferredUpdate() is called from the SCHEDULE_FB_UPDATE macro
1954  * to schedule an update.
1955  */
1956
1957 void
1958 rfbScheduleDeferredUpdate(rfbClientPtr cl)
1959 {
1960     if (rfbDeferUpdateTime != 0) {
1961         cl->deferredUpdateTimer = TimerSet(cl->deferredUpdateTimer, 0,
1962                                            rfbDeferUpdateTime,
1963                                            rfbDeferredUpdateCallback, cl);
1964         cl->deferredUpdateScheduled = TRUE;
1965     } else {
1966         rfbSendFramebufferUpdate(cl);
1967     }
1968 }
1969
1970
1971 /*
1972  * PrintRegion is useful for debugging.
1973  */
1974
1975 #if 0
1976 static void
1977 PrintRegion(pScreen,reg)
1978     ScreenPtr pScreen;
1979     RegionPtr reg;
1980 {
1981     int nrects = REGION_NUM_RECTS(reg);
1982     int i;
1983
1984     rfbLog("Region num rects %d extents %d,%d %d,%d\n",nrects,
1985            (REGION_EXTENTS(pScreen,reg))->x1,
1986            (REGION_EXTENTS(pScreen,reg))->y1,
1987            (REGION_EXTENTS(pScreen,reg))->x2,
1988            (REGION_EXTENTS(pScreen,reg))->y2);
1989
1990     for (i = 0; i < nrects; i++) {
1991         rfbLog("    rect %d,%d %dx%d\n",
1992                REGION_RECTS(reg)[i].x1,
1993                REGION_RECTS(reg)[i].y1,
1994                REGION_RECTS(reg)[i].x2-REGION_RECTS(reg)[i].x1,
1995                REGION_RECTS(reg)[i].y2-REGION_RECTS(reg)[i].y1);
1996     }
1997 }
1998 #endif