Polling screen area around current pointer position with higher
priority. Also, there is a number of code improvements, and some new
"FIXME" comments for potential future improvements.


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@477 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/x0vncserver/PollingManager.cxx b/x0vncserver/PollingManager.cxx
index 9491d76..00580e0 100644
--- a/x0vncserver/PollingManager.cxx
+++ b/x0vncserver/PollingManager.cxx
@@ -19,17 +19,30 @@
 // PollingManager.cxx
 //
 
+// FIXME: Don't compare pixels already marked as changed.
+// FIXME: Use Image::copyPixels() instead of Image::updateRect()?
+//        In that case, note the fact that arguments are not checked.
+
 #include <stdio.h>
 #include <string.h>
 #include <sys/time.h>
 #include <X11/Xlib.h>
 #include <rfb/VNCServer.h>
 #include <rfb/Configuration.h>
+#include <rfb/ServerCore.h>
 
 #include <x0vncserver/Image.h>
 #include <x0vncserver/PollingManager.h>
 
-IntParameter pollingType("PollingType", "Polling algorithm to use (0..3)", 3);
+BoolParameter PollingManager::pollPointer
+("PollPointer",
+ "Poll area under the pointer with higher priority",
+ true);
+
+IntParameter PollingManager::pollingType
+("PollingType",
+ "Polling algorithm to use (0..3)",
+ 3);
 
 const int PollingManager::m_pollingOrder[32] = {
    0, 16,  8, 24,  4, 20, 12, 28,
@@ -47,7 +60,8 @@
 
 PollingManager::PollingManager(Display *dpy, Image *image,
                                ImageFactory *factory)
-  : m_dpy(dpy), m_server(0), m_image(image), m_pollingStep(0)
+  : m_dpy(dpy), m_server(0), m_image(image), m_pointerPosKnown(false),
+    m_pollingStep(0)
 {
   // Save width and height of the screen (and the image).
   m_width = m_image->xim->width;
@@ -57,10 +71,11 @@
   m_widthTiles = (m_width + 31) / 32;
   m_heightTiles = (m_height + 31) / 32;
 
-  // Create two additional images used in the polling algorithm.
+  // Create additional images used in the polling algorithm.
   // FIXME: verify that these images use the same pixel format as in m_image.
   m_rowImage = factory->newImage(m_dpy, m_width, 1);
   m_tileImage = factory->newImage(m_dpy, 32, 32);
+  m_areaImage = factory->newImage(m_dpy, 128, 128);
 
   // FIXME: Extend the comment.
   // Create a matrix with one byte per each 32x32 tile. It will be
@@ -89,6 +104,10 @@
   delete[] m_rateMatrix;
 
   delete[] m_statusMatrix;
+
+  delete m_areaImage;
+  delete m_tileImage;
+  delete m_rowImage;
 }
 
 //
@@ -101,6 +120,28 @@
 }
 
 //
+// Update current pointer position which may be used as a hint for
+// polling algorithms.
+//
+
+void PollingManager::setPointerPos(const Point &pos)
+{
+  m_pointerPos = pos;
+  m_pointerPosKnown = true;
+}
+
+//
+// Indicate that current pointer position is unknown.
+// FIXME: Perhaps this should be done automatically after a number of
+//        polling cycles if the cursor position have not been changed?
+//
+
+void PollingManager::unsetPointerPos()
+{
+  m_pointerPosKnown = false;
+}
+
+//
 // DEBUG: a version of poll() measuring time spent in the function.
 //
 
@@ -160,7 +201,7 @@
 
   bool grandStep = (m_pollingStep % GRAND_STEP_DIVISOR == 0);
 
-  // FIXME: Save shortcuts in member variables.
+  // FIXME: Save shortcuts in member variables?
   int scanLine = m_pollingOrder[m_pollingStep++ % 32];
   int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
   int bytesPerLine = m_image->xim->bytes_per_line;
@@ -217,6 +258,11 @@
   if (grandStep)
     adjustVideoArea();
 
+  // FIXME: Exclude area near the pointer from the comparisons above.
+  // FIXME: Code duplication.
+  if (pollPointer && m_pointerPosKnown && pollPointerArea())
+    nTilesChanged++;
+
   return (nTilesChanged != 0);
 }
 
@@ -281,6 +327,11 @@
     }
   }
 
+  // FIXME: Exclude area near the pointer from the comparisons above.
+  // FIXME: Code duplication.
+  if (pollPointer && m_pointerPosKnown && pollPointerArea())
+    nTilesChanged++;
+
   return (nTilesChanged != 0);
 }
 
@@ -323,6 +374,11 @@
     }
   }
 
+  // FIXME: Exclude area near the pointer from the comparisons above.
+  // FIXME: Code duplication.
+  if (pollPointer && m_pointerPosKnown && pollPointerArea())
+    nTilesChanged++;
+
   return (nTilesChanged != 0);
 }
 
@@ -339,17 +395,118 @@
   Rect rect(0, 0, m_width, m_height);
   m_server->add_changed(rect);
 
-  // As we have no idea if any pixels were changed,
-  // always report that some changes have been detected.
+  // Report that some changes have been detected.
   return true;
 }
 
 //
-// FIXME: Replace with a normal comment.
-// Make video area pattern more regular.
+// Compute coordinates of the rectangle around the pointer.
 //
 
-// FIXME: Is it efficient enough?
+void PollingManager::computePointerArea(Rect *r)
+{
+  int x = m_pointerPos.x - 64;
+  int y = m_pointerPos.y - 64;
+  int w = 128;
+  int h = 128;
+  if (x < 0) {
+    w += x; x = 0;
+  }
+  if (x + w > m_width) {
+    w = m_width - x;
+  }
+  if (y < 0) {
+    h += y; y = 0;
+  }
+  if (y + h > m_height) {
+    h = m_height - y;
+  }
+
+  r->setXYWH(x, y, w, h);
+}
+
+//
+// Poll the area under current pointer position. Each pixel of the
+// area should be compared. Using such polling option gives higher
+// priority to screen area under the pointer.
+//
+// ASSUMES: (m_server != NULL && m_pointerPosKnown != false)
+//
+
+bool PollingManager::pollPointerArea()
+{
+  Rect r;
+  computePointerArea(&r);
+
+  // Shortcuts for coordinates.
+  int x = r.tl.x, y = r.tl.y;
+  int w = r.width(), h = r.height();
+
+  // Get new pixels.
+  if (w == 128 && h == 128) {
+    m_areaImage->get(DefaultRootWindow(m_dpy), x, y);
+  } else {
+    m_areaImage->get(DefaultRootWindow(m_dpy), x, y, w, h);
+  }
+
+  // Now, try to minimize the rectangle by cutting out unchanged
+  // borders (at top and bottom).
+  //
+  // FIXME: Perhaps we should work on 32x32 tiles (properly aligned)
+  //        to produce a region instead of a rectangle. If there would
+  //        be just one universal polling algorithm, it could be
+  //        better to integrate pointer area polling into that
+  //        algorithm, instead of a separate pollPointerArea()
+  //        function.
+
+  // Shortcuts.
+  int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
+  int oldBytesPerLine = m_image->xim->bytes_per_line;
+  int newBytesPerLine = m_areaImage->xim->bytes_per_line;
+  char *oldPtr = m_image->xim->data + y * oldBytesPerLine + x * bytesPerPixel;
+  char *newPtr = m_areaImage->xim->data;
+
+  // Check and cut out unchanged rows at the top.
+  int ty;
+  for (ty = 0; ty < h; ty++) {
+    if (memcmp(oldPtr, newPtr, w * bytesPerPixel) != 0)
+      break;
+    oldPtr += oldBytesPerLine;
+    newPtr += newBytesPerLine;
+  }
+  if (ty == h) {
+    return false;               // no changes at all
+  }
+  y += ty; h -= ty;
+
+  // Check and cut out unchanged rows at the bottom.
+  oldPtr = m_image->xim->data + (y+h-1) * oldBytesPerLine + x * bytesPerPixel;
+  newPtr = m_areaImage->xim->data + (ty+h-1) * newBytesPerLine;
+  int by;
+  for (by = 0; by < h - 1; by++) {
+    if (memcmp(oldPtr, newPtr, w * bytesPerPixel) != 0)
+      break;
+    oldPtr -= oldBytesPerLine;
+    newPtr -= newBytesPerLine;
+  }
+  h -= by;
+
+  // Copy pixels.
+  m_image->updateRect(m_areaImage, x, y, 0, ty, w, h);
+
+  // Report updates to the server.
+  Rect rect(x, y, x+w, y+h);
+  m_server->add_changed(rect);
+  return true;
+}
+
+//
+// Make video area pattern more regular.
+//
+// FIXME: Replace the above with a normal comment.
+// FIXME: Is the function efficient enough?
+//
+
 void PollingManager::adjustVideoArea()
 {
   char newFlags[m_widthTiles * m_heightTiles];