Try to update an entire FramebufferUpdate in one go, avoiding tears.


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@3717 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/unix/vncviewer/DesktopWindow.cxx b/unix/vncviewer/DesktopWindow.cxx
index 8919859..36f96fc 100644
--- a/unix/vncviewer/DesktopWindow.cxx
+++ b/unix/vncviewer/DesktopWindow.cxx
@@ -65,7 +65,7 @@
 DesktopWindow::DesktopWindow(Display* dpy, int w, int h,
                              const rfb::PixelFormat& serverPF,
                              CConn* cc_, TXWindow* parent)
-  : TXWindow(dpy, w, h, parent), cc(cc_), im(0),
+  : TXWindow(dpy, w, h, parent), cc(cc_), im(0), updateTimer(this),
     cursorVisible(false), cursorAvailable(false), currentSelectionTime(0),
     newSelection(0), gettingInitialSelectionTime(true),
     newServerCutText(false), serverCutText_(0),
@@ -216,7 +216,6 @@
   if (cursorVisible) {
     cursorVisible = false;
     im->imageRect(cursorBackingRect, cursorBacking.data);
-    im->put(win(), gc, cursorBackingRect);
   }
 }
 
@@ -238,7 +237,6 @@
     im->getImage(cursorBacking.data, cursorBackingRect);
 
     im->maskRect(cursorRect, cursor.data, cursor.mask.buf);
-    im->put(win(), gc, cursorBackingRect);
   }
 }
 
@@ -266,19 +264,11 @@
 }
 
 
-// Call XSync() at the end of an update.  We do this because we'd like to
-// ensure that the current update has actually been drawn by the X server
-// before the next update arrives - this is necessary for copyRect to
-// behave correctly.  In particular, if part of the source of a copyRect is
-// not actually displayed in the window, then XCopyArea results in
-// GraphicsExpose events, which require us to draw from the off-screen
-// image.  By the time XSync returns, the GraphicsExpose events will be in
-// Xlib's queue, so hopefully will be processed before the next update.
-// Possibly we should process the GraphicsExpose events here explicitly?
+// Update the actual window with the changed parts of the framebuffer.
 
 void DesktopWindow::framebufferUpdateEnd()
 {
-  XSync(dpy, False);
+  updateWindow();
 }
 
 
@@ -297,7 +287,7 @@
       }
     }
   }
-  im->put(win(), gc, r);
+  damageRect(r);
 }
 
 
@@ -311,6 +301,22 @@
 }
 
 
+// Copy the areas of the framebuffer that have been changed (damaged)
+// to the displayed window.
+
+void DesktopWindow::updateWindow()
+{
+  Rect r;
+
+  updateTimer.stop();
+
+  r = damage.get_bounding_rect();
+  damage.clear();
+
+  im->put(win(), gc, r);
+}
+
+
 bool DesktopWindow::handleTimeout(rfb::Timer* timer)
 {
   if (timer == &setColourMapEntriesTimer) {
@@ -320,6 +326,8 @@
     if (!viewOnly) {
       cc->writer()->pointerEvent(lastPointerPos, lastButtonMask);
     }
+  } else if (timer == &updateTimer) {
+    updateWindow();
   }
   return false;
 }
diff --git a/unix/vncviewer/DesktopWindow.h b/unix/vncviewer/DesktopWindow.h
index fe5ef82..433ac33 100644
--- a/unix/vncviewer/DesktopWindow.h
+++ b/unix/vncviewer/DesktopWindow.h
@@ -24,6 +24,7 @@
 
 #include <rfb/Cursor.h>
 #include <rfb/Rect.h>
+#include <rfb/Region.h>
 #include <rfb/Timer.h>
 #include "TXWindow.h"
 #include "TXViewport.h"
@@ -61,13 +62,13 @@
   void fillRect(const rfb::Rect& r, rfb::Pixel pix) {
     if (r.overlaps(cursorBackingRect)) hideLocalCursor();
     im->fillRect(r, pix);
-    im->put(win(), gc, r);
+    damageRect(r);
     showLocalCursor();
   }
   void imageRect(const rfb::Rect& r, void* pixels) {
     if (r.overlaps(cursorBackingRect)) hideLocalCursor();
     im->imageRect(r, pixels);
-    im->put(win(), gc, r);
+    damageRect(r);
     showLocalCursor();
   }
   void copyRect(const rfb::Rect& r, int srcX, int srcY) {
@@ -75,11 +76,8 @@
         cursorBackingRect.overlaps(rfb::Rect(srcX, srcY,
                                              srcX+r.width(), srcY+r.height())))
       hideLocalCursor();
-    if (im->usingShm())
-      XSync(dpy, False);
     im->copyRect(r, rfb::Point(r.tl.x-srcX, r.tl.y-srcY));
-    XCopyArea(dpy, win(), win(), gc, srcX, srcY,
-              r.width(), r.height(), r.tl.x, r.tl.y);
+    damageRect(r);
     showLocalCursor();
   }
   void invertRect(const rfb::Rect& r);
@@ -97,12 +95,20 @@
   void createXCursors();
   void hideLocalCursor();
   void showLocalCursor();
+  void damageRect(const rfb::Rect& r) {
+    damage.assign_union(rfb::Region(r));
+    if (!updateTimer.isStarted())
+      updateTimer.start(100);
+  };
+  void updateWindow();
   bool handleTimeout(rfb::Timer* timer);
   void handlePointerEvent(const rfb::Point& pos, int buttonMask);
 
   CConn* cc;
   TXImage* im;
   GC gc;
+  rfb::Region damage;
+  rfb::Timer updateTimer;
   ::Cursor dotCursor, noCursor, localXCursor;
 
   rfb::Cursor cursor;