Merge branch 'fps' of https://github.com/CendioOssman/tigervnc
diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx
index 81eed37..ec5e962 100644
--- a/common/rfb/VNCServerST.cxx
+++ b/common/rfb/VNCServerST.cxx
@@ -66,12 +66,6 @@
 static LogWriter slog("VNCServerST");
 LogWriter VNCServerST::connectionsLog("Connections");
 
-rfb::IntParameter deferUpdateTime("DeferUpdate",
-                                  "Time in milliseconds to defer updates",1);
-
-rfb::BoolParameter alwaysSetDeferUpdateTimer("AlwaysSetDeferUpdateTimer",
-                  "Always reset the defer update timer on every change",false);
-
 //
 // -=- VNCServerST Implementation
 //
@@ -86,7 +80,7 @@
     renderedCursorInvalid(false),
     queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance),
     lastConnectionTime(0), disableclients(false),
-    deferTimer(this), deferPending(false)
+    frameTimer(this)
 {
   lastUserInputTime = lastDisconnectTime = time(0);
   slog.debug("creating single-threaded server %s", name.buf);
@@ -99,6 +93,9 @@
   // Close any active clients, with appropriate logging & cleanup
   closeClients("Server shutdown");
 
+  // Stop trying to render things
+  stopFrameClock();
+
   // Delete all the clients, and their sockets, and any closing sockets
   //   NB: Deleting a client implicitly removes it from the clients list
   while (!clients.empty()) {
@@ -286,6 +283,8 @@
 void VNCServerST::blockUpdates()
 {
   blockCounter++;
+
+  stopFrameClock();
 }
 
 void VNCServerST::unblockUpdates()
@@ -294,9 +293,11 @@
 
   blockCounter--;
 
-  // Flush out any updates we might have blocked
-  if (blockCounter == 0)
-    tryUpdate();
+  // Restart the frame clock if we have updates
+  if (blockCounter == 0) {
+    if (!comparer->is_empty())
+      startFrameClock();
+  }
 }
 
 void VNCServerST::setPixelBuffer(PixelBuffer* pb_, const ScreenSet& layout)
@@ -417,8 +418,7 @@
     return;
 
   comparer->add_changed(region);
-  startDefer();
-  tryUpdate();
+  startFrameClock();
 }
 
 void VNCServerST::add_copied(const Region& dest, const Point& delta)
@@ -427,8 +427,7 @@
     return;
 
   comparer->add_copied(dest, delta);
-  startDefer();
-  tryUpdate();
+  startFrameClock();
 }
 
 void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
@@ -507,10 +506,14 @@
 
 bool VNCServerST::handleTimeout(Timer* t)
 {
-  if (t != &deferTimer)
-    return false;
+  if (t == &frameTimer) {
+    // We keep running until we go a full interval without any updates
+    if (comparer->is_empty())
+      return false;
 
-  tryUpdate();
+    writeUpdate();
+    return true;
+  }
 
   return false;
 }
@@ -546,87 +549,47 @@
   return false;
 }
 
-inline void VNCServerST::startDefer()
+void VNCServerST::startFrameClock()
 {
-  if (deferUpdateTime == 0)
+  if (frameTimer.isStarted())
     return;
-
-  if (deferPending && !alwaysSetDeferUpdateTimer)
-    return;
-
-  gettimeofday(&deferStart, NULL);
-  deferTimer.start(deferUpdateTime);
-
-  deferPending = true;
-}
-
-inline bool VNCServerST::checkDefer()
-{
-  if (!deferPending)
-    return true;
-
-  if (msSince(&deferStart) >= (unsigned)deferUpdateTime)
-    return true;
-
-  return false;
-}
-
-void VNCServerST::tryUpdate()
-{
-  std::list<VNCSConnectionST*>::iterator ci, ci_next;
-
   if (blockCounter > 0)
     return;
 
-  if (!checkDefer())
-    return;
-
-  for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
-    ci_next = ci; ci_next++;
-    (*ci)->writeFramebufferUpdateOrClose();
-  }
+  frameTimer.start(1000/rfb::Server::frameRate);
 }
 
-// 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
-// areas of the screen which haven't actually changed.  It also checks the
-// state of the (server-side) rendered cursor, if necessary rendering it again
-// with the correct background.
+void VNCServerST::stopFrameClock()
+{
+  frameTimer.stop();
+}
 
-bool VNCServerST::checkUpdate()
+// writeUpdate() is called on a regular interval in order 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 areas of the screen which haven't actually changed. It
+// also checks the state of the (server-side) rendered cursor, if
+// necessary rendering it again with the correct background.
+
+void VNCServerST::writeUpdate()
 {
   UpdateInfo ui;
+  Region toCheck;
+
+  std::list<VNCSConnectionST*>::iterator ci, ci_next;
+
+  assert(blockCounter == 0);
+
   comparer->getUpdateInfo(&ui, pb->getRect());
+  toCheck = ui.changed.union_(ui.copied);
 
-  bool renderCursor = needRenderedCursor();
-
-  if (ui.is_empty() && !(renderCursor && renderedCursorInvalid))
-    return true;
-
-  // Block clients as the frame buffer cannot be safely accessed
-  if (blockCounter > 0)
-    return false;
-
-  // Block client from updating if we are currently deferring updates
-  if (!checkDefer())
-    return false;
-
-  deferPending = false;
-
-  Region toCheck = ui.changed.union_(ui.copied);
-
-  if (renderCursor) {
+  if (needRenderedCursor()) {
     Rect clippedCursorRect = Rect(0, 0, cursor->width(), cursor->height())
                              .translate(cursorPos.subtract(cursor->hotspot()))
                              .intersect(pb->getRect());
 
-    if (!renderedCursorInvalid && (toCheck.intersect(clippedCursorRect)
-                                   .is_empty())) {
-      renderCursor = false;
-    } else {
-      toCheck.assign_union(clippedCursorRect);
-    }
+    if (!toCheck.intersect(clippedCursorRect).is_empty())
+      renderedCursorInvalid = true;
   }
 
   pb->grabRegion(toCheck);
@@ -639,23 +602,42 @@
   if (comparer->compare())
     comparer->getUpdateInfo(&ui, pb->getRect());
 
-  if (renderCursor) {
-    renderedCursor.update(pb, cursor, cursorPos);
-    renderedCursorInvalid = false;
-  }
+  comparer->clear();
 
-  std::list<VNCSConnectionST*>::iterator ci, ci_next;
   for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
     ci_next = ci; ci_next++;
     (*ci)->add_copied(ui.copied, ui.copy_delta);
     (*ci)->add_changed(ui.changed);
+    (*ci)->writeFramebufferUpdateOrClose();
   }
+}
 
-  comparer->clear();
+// checkUpdate() is called by clients to see if it is safe to read from
+// the framebuffer at this time.
+
+bool VNCServerST::checkUpdate()
+{
+  // Block clients as the frame buffer cannot be safely accessed
+  if (blockCounter > 0)
+    return false;
+
+  // Block client from updating if there are pending updates
+  if (!comparer->is_empty())
+    return false;
 
   return true;
 }
 
+const RenderedCursor* VNCServerST::getRenderedCursor()
+{
+  if (renderedCursorInvalid) {
+    renderedCursor.update(pb, cursor, cursorPos);
+    renderedCursorInvalid = false;
+  }
+
+  return &renderedCursor;
+}
+
 void VNCServerST::getConnInfo(ListConnInfo * listConn)
 {
   listConn->Clear();