Reimplement the deferred update handling, this time in a more robust and
well-behaved manner.


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4784 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx
index bcc7f23..30d9b6a 100644
--- a/common/rfb/VNCSConnectionST.cxx
+++ b/common/rfb/VNCSConnectionST.cxx
@@ -668,7 +668,11 @@
 
   updates.enable_copyrect(cp.useCopyRect);
 
-  server->checkUpdate();
+  // Fetch updates from server object, and see if we are allowed to send
+  // anything right now (the framebuffer might have changed in ways we
+  // haven't yet been informed of).
+  if (!server->checkUpdate())
+    return;
 
   // Get the lists of updates. Prior to exporting the data to the `ui' object,
   // getUpdateInfo() will normalize the `updates' object such way that its
diff --git a/common/rfb/VNCServer.h b/common/rfb/VNCServer.h
index 1c8d38e..4e55479 100644
--- a/common/rfb/VNCServer.h
+++ b/common/rfb/VNCServer.h
@@ -62,10 +62,6 @@
     //   their close() method with the supplied reason.
     virtual void closeClients(const char* reason) = 0;
 
-    // tryUpdate() causes the server to attempt to send updates to any waiting
-    // clients.
-    virtual void tryUpdate() = 0;
-
     // setCursor() tells the server that the cursor has changed.  The
     // cursorData argument contains width*height pixel values in the pixel
     // buffer's format.  The mask argument is a bitmask with a 1-bit meaning
diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx
index eea6565..5c13596 100644
--- a/common/rfb/VNCServerST.cxx
+++ b/common/rfb/VNCServerST.cxx
@@ -64,6 +64,12 @@
 static LogWriter slog("VNCServerST");
 LogWriter VNCServerST::connectionsLog("Connections");
 
+rfb::IntParameter deferUpdateTime("DeferUpdate",
+                                  "Time in milliseconds to defer updates",10);
+
+rfb::BoolParameter alwaysSetDeferUpdateTimer("AlwaysSetDeferUpdateTimer",
+                  "Always reset the defer update timer on every change",false);
+
 //
 // -=- VNCServerST Implementation
 //
@@ -76,7 +82,8 @@
     renderedCursorInvalid(false),
     queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance),
     useEconomicTranslate(false),
-    lastConnectionTime(0), disableclients(false)
+    lastConnectionTime(0), disableclients(false),
+    deferTimer(this), deferPending(false)
 {
   lastUserInputTime = lastDisconnectTime = time(0);
   slog.debug("creating single-threaded server %s", name.buf);
@@ -373,25 +380,22 @@
 
 void VNCServerST::add_changed(const Region& region)
 {
-  if (comparer != 0) {
-    comparer->add_changed(region);
-  }
+  if (comparer == NULL)
+    return;
+
+  comparer->add_changed(region);
+  startDefer();
+  tryUpdate();
 }
 
 void VNCServerST::add_copied(const Region& dest, const Point& delta)
 {
-  if (comparer != 0) {
-    comparer->add_copied(dest, delta);
-  }
-}
+  if (comparer == NULL)
+    return;
 
-void VNCServerST::tryUpdate()
-{
-  std::list<VNCSConnectionST*>::iterator ci, ci_next;
-  for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
-    ci_next = ci; ci_next++;
-    (*ci)->writeFramebufferUpdateOrClose();
-  }
+  comparer->add_copied(dest, delta);
+  startDefer();
+  tryUpdate();
 }
 
 void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
@@ -471,6 +475,15 @@
   return 0;
 }
 
+bool VNCServerST::handleTimeout(Timer* t)
+{
+  if (t != &deferTimer)
+    return false;
+
+  tryUpdate();
+
+  return false;
+}
 
 // -=- Internal methods
 
@@ -503,6 +516,44 @@
   return false;
 }
 
+inline void VNCServerST::startDefer()
+{
+  if (deferUpdateTime == 0)
+    return;
+
+  if (deferPending && !alwaysSetDeferUpdateTimer)
+    return;
+
+  gettimeofday(&deferStart, NULL);
+  deferTimer.start(deferUpdateTime);
+
+  deferPending = true;
+}
+
+inline bool VNCServerST::checkDefer()
+{
+  if (!deferPending)
+    return true;
+
+  if (msSince(&deferStart) >= deferUpdateTime)
+    return true;
+
+  return false;
+}
+
+void VNCServerST::tryUpdate()
+{
+  std::list<VNCSConnectionST*>::iterator ci, ci_next;
+
+  if (!checkDefer())
+    return;
+
+  for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+    ci_next = ci; ci_next++;
+    (*ci)->writeFramebufferUpdateOrClose();
+  }
+}
+
 // checkUpdate() is called just before sending an update.  It checks to see
 // what updates are pending and propagates them to the update tracker for each
 // client.  It uses the ComparingUpdateTracker's compare() method to filter out
@@ -510,7 +561,7 @@
 // state of the (server-side) rendered cursor, if necessary rendering it again
 // with the correct background.
 
-void VNCServerST::checkUpdate()
+bool VNCServerST::checkUpdate()
 {
   UpdateInfo ui;
   comparer->getUpdateInfo(&ui, pb->getRect());
@@ -518,7 +569,13 @@
   bool renderCursor = needRenderedCursor();
 
   if (ui.is_empty() && !(renderCursor && renderedCursorInvalid))
-    return;
+    return true;
+
+  // Block client from updating if we are currently deferring updates
+  if (!checkDefer())
+    return false;
+
+  deferPending = false;
 
   Region toCheck = ui.changed.union_(ui.copied);
 
@@ -561,6 +618,8 @@
   }
 
   comparer->clear();
+
+  return true;
 }
 
 void VNCServerST::getConnInfo(ListConnInfo * listConn)
diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h
index c74be90..47a4801 100644
--- a/common/rfb/VNCServerST.h
+++ b/common/rfb/VNCServerST.h
@@ -23,6 +23,8 @@
 #ifndef __RFB_VNCSERVERST_H__
 #define __RFB_VNCSERVERST_H__
 
+#include <sys/time.h>
+
 #include <list>
 
 #include <rfb/SDesktop.h>
@@ -31,6 +33,7 @@
 #include <rfb/LogWriter.h>
 #include <rfb/Blacklist.h>
 #include <rfb/Cursor.h>
+#include <rfb/Timer.h>
 #include <network/Socket.h>
 #include <rfb/ListConnInfo.h>
 #include <rfb/ScreenSet.h>
@@ -42,7 +45,9 @@
   class PixelBuffer;
   class KeyRemapper;
 
-  class VNCServerST : public VNCServer, public network::SocketServer {
+  class VNCServerST : public VNCServer,
+                      public Timer::Callback,
+                      public network::SocketServer {
   public:
     // -=- Constructors
 
@@ -85,7 +90,6 @@
     virtual void serverCutText(const char* str, int len);
     virtual void add_changed(const Region &region);
     virtual void add_copied(const Region &dest, const Point &delta);
-    virtual void tryUpdate();
     virtual void setCursor(int width, int height, const Point& hotspot,
                            void* cursorData, void* mask);
     virtual void setCursorPos(const Point& p);
@@ -192,6 +196,11 @@
 
     friend class VNCSConnectionST;
 
+    // Timer callbacks
+    virtual bool handleTimeout(Timer* t);
+
+    // - Internal methods
+
     void startDesktop();
 
     static LogWriter connectionsLog;
@@ -222,7 +231,10 @@
     int authClientCount();
 
     bool needRenderedCursor();
-    void checkUpdate();
+    void startDefer();
+    bool checkDefer();
+    void tryUpdate();
+    bool checkUpdate();
 
     void notifyScreenLayoutChange(VNCSConnectionST *requester);
 
@@ -235,6 +247,10 @@
     time_t lastConnectionTime;
 
     bool disableclients;
+
+    Timer deferTimer;
+    bool deferPending;
+    struct timeval deferStart;
   };
 
 };