Throttle overlapping screen updates

We need to make sure the display server has finished reading our
previous update before we overwrite the buffer with the next update.
diff --git a/vncviewer/X11PixelBuffer.cxx b/vncviewer/X11PixelBuffer.cxx
index 9196fdc..046676e 100644
--- a/vncviewer/X11PixelBuffer.cxx
+++ b/vncviewer/X11PixelBuffer.cxx
@@ -21,8 +21,10 @@
 #include <config.h>
 #endif
 
+#include <assert.h>
 #include <stdlib.h>
 
+#include <FL/Fl.H>
 #include <FL/x.H>
 
 #include <rfb/LogWriter.h>
@@ -35,6 +37,8 @@
 
 static rfb::LogWriter vlog("X11PixelBuffer");
 
+std::list<X11PixelBuffer*> X11PixelBuffer::shmList;
+
 static PixelFormat display_pf()
 {
   int i;
@@ -97,7 +101,7 @@
 
 X11PixelBuffer::X11PixelBuffer(int width, int height) :
   PlatformPixelBuffer(display_pf(), width, height, NULL, 0),
-  shminfo(NULL), xim(NULL)
+  shminfo(NULL), xim(NULL), pendingPutImage(0)
 {
   // Might not be open at this point
   fl_open_display();
@@ -122,6 +126,8 @@
 {
   if (shminfo) {
     vlog.debug("Freeing shared memory XImage");
+    shmList.remove(this);
+    Fl::remove_system_handler(handleSystemEvent);
     shmdt(shminfo->shmaddr);
     shmctl(shminfo->shmid, IPC_RMID, 0);
     delete shminfo;
@@ -137,12 +143,18 @@
 
 void X11PixelBuffer::draw(int src_x, int src_y, int x, int y, int w, int h)
 {
-  if (shminfo)
-    XShmPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h, False);
-  else
+  if (shminfo) {
+    XShmPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h, True);
+    pendingPutImage++;
+  } else {
     XPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h);
+  }
 }
 
+bool X11PixelBuffer::isRendering(void)
+{
+  return pendingPutImage > 0;
+}
 
 static bool caughtError;
 
@@ -202,6 +214,11 @@
   if (caughtError)
     goto free_shmaddr;
 
+  // FLTK is a bit stupid and unreliable if you register the same
+  // callback with different data values.
+  Fl::add_system_handler(handleSystemEvent, NULL);
+  shmList.push_back(this);
+
   vlog.debug("Using shared memory XImage");
 
   return 1;
@@ -222,3 +239,31 @@
 
   return 0;
 }
+
+int X11PixelBuffer::handleSystemEvent(void* event, void* data)
+{
+  XEvent* xevent;
+  XShmCompletionEvent* shmevent;
+
+  std::list<X11PixelBuffer*>::iterator iter;
+
+  xevent = (XEvent*)event;
+  assert(xevent);
+
+  if (xevent->type != XShmGetEventBase(fl_display))
+    return 0;
+
+  shmevent = (XShmCompletionEvent*)event;
+
+  for (iter = shmList.begin();iter != shmList.end();++iter) {
+    if (shmevent->shmseg != (*iter)->shminfo->shmseg)
+      continue;
+
+    (*iter)->pendingPutImage--;
+    assert((*iter)->pendingPutImage >= 0);
+
+    return 1;
+  }
+
+  return 0;
+}