Migrating to new directory structure adopted from the RealVNC's source tree. More changes will follow.

git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@590 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/unix/x0vncserver/PollingManager.cxx b/unix/x0vncserver/PollingManager.cxx
new file mode 100644
index 0000000..a823ed4
--- /dev/null
+++ b/unix/x0vncserver/PollingManager.cxx
@@ -0,0 +1,600 @@
+/* Copyright (C) 2004-2005 Constantin Kaplinsky.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// 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 <time.h>
+#include <X11/Xlib.h>
+#include <rfb/LogWriter.h>
+#include <rfb/VNCServer.h>
+#include <rfb/Configuration.h>
+#include <rfb/ServerCore.h>
+
+#include <x0vncserver/PollingManager.h>
+
+static LogWriter vlog("PollingMgr");
+
+BoolParameter PollingManager::pollPointer
+("PollPointer",
+ "DEBUG: Poll area under the pointer with higher priority",
+ true);
+
+IntParameter PollingManager::pollingType
+("PollingType",
+ "DEBUG: Select particular polling algorithm (0..3)",
+ 3);
+
+const int PollingManager::m_pollingOrder[32] = {
+   0, 16,  8, 24,  4, 20, 12, 28,
+  10, 26, 18,  2, 22,  6, 30, 14,
+   1, 17,  9, 25,  7, 23, 15, 31,
+  19,  3, 27, 11, 29, 13,  5, 21
+};
+
+//
+// Constructor.
+//
+// Note that dpy and image should remain valid during the object
+// lifetime, while factory is used only in the constructor itself.
+//
+
+PollingManager::PollingManager(Display *dpy, Image *image,
+                               ImageFactory *factory,
+                               int offsetLeft, int offsetTop)
+  : m_dpy(dpy), m_server(0), m_image(image),
+    m_offsetLeft(offsetLeft), m_offsetTop(offsetTop),
+    m_pointerPosKnown(false), m_pollingStep(0)
+{
+  // Save width and height of the screen (and the image).
+  m_width = m_image->xim->width;
+  m_height = m_image->xim->height;
+
+  // Compute width and height in 32x32 tiles.
+  m_widthTiles = (m_width + 31) / 32;
+  m_heightTiles = (m_height + 31) / 32;
+
+  // Get initial screen image.
+  m_image->get(DefaultRootWindow(m_dpy), m_offsetLeft, m_offsetTop);
+
+  // Create additional images used in polling algorithms, warn if
+  // underlying class names are different from the class name of the
+  // primary 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);
+  if (strcmp(m_image->className(), m_rowImage->className()) != 0 ||
+      strcmp(m_image->className(), m_tileImage->className()) != 0 ||
+      strcmp(m_image->className(), m_areaImage->className()) != 0) {
+    vlog.error("Image types do not match (%s, %s, %s)",
+               m_rowImage->className(),
+               m_tileImage->className(),
+               m_areaImage->className());
+  }
+
+  // FIXME: Extend the comment.
+  // Create a matrix with one byte per each 32x32 tile. It will be
+  // used to limit the rate of updates on continuously-changed screen
+  // areas (like video).
+  int numTiles = m_widthTiles * m_heightTiles;
+  m_statusMatrix = new char[numTiles];
+  memset(m_statusMatrix, 0, numTiles);
+
+  // FIXME: Extend the comment.
+  // Create a matrix with one byte per each 32x32 tile. It will be
+  // used to limit the rate of updates on continuously-changed screen
+  // areas (like video).
+  m_rateMatrix = new char[numTiles];
+  m_videoFlags = new char[numTiles];
+  m_changedFlags = new char[numTiles];
+  memset(m_rateMatrix, 0, numTiles);
+  memset(m_videoFlags, 0, numTiles);
+  memset(m_changedFlags, 0, numTiles);
+}
+
+PollingManager::~PollingManager()
+{
+  delete[] m_changedFlags;
+  delete[] m_videoFlags;
+  delete[] m_rateMatrix;
+
+  delete[] m_statusMatrix;
+
+  delete m_areaImage;
+  delete m_tileImage;
+  delete m_rowImage;
+}
+
+//
+// Register VNCServer object.
+//
+
+void PollingManager::setVNCServer(VNCServer *s)
+{
+  m_server = s;
+}
+
+//
+// Update current pointer position which may be used as a hint for
+// polling algorithms.
+//
+
+void PollingManager::setPointerPos(const Point &pos)
+{
+  m_pointerPosTime = time(NULL);
+  m_pointerPos = pos;
+  m_pointerPosKnown = true;
+}
+
+//
+// Indicate that current pointer position is unknown.
+//
+
+void PollingManager::unsetPointerPos()
+{
+  m_pointerPosKnown = false;
+}
+
+//
+// DEBUG: Measuring time spent in the poll() function,
+//        as well as time intervals between poll() calls.
+//
+
+#ifdef DEBUG
+void PollingManager::debugBeforePoll()
+{
+  TimeMillis timeNow;
+  int diff = timeNow.diffFrom(m_timeSaved);
+  fprintf(stderr, "[wait%4dms]\t[step %2d]\t", diff, m_pollingStep % 32);
+  m_timeSaved = timeNow;
+}
+
+void PollingManager::debugAfterPoll()
+{
+  TimeMillis timeNow;
+  int diff = timeNow.diffFrom(m_timeSaved);
+  fprintf(stderr, "[poll%4dms]\n", diff);
+  m_timeSaved = timeNow;
+}
+
+#endif
+
+//
+// Search for changed rectangles on the screen.
+//
+
+void PollingManager::poll()
+{
+#ifdef DEBUG
+  debugBeforePoll();
+#endif
+
+  // First step: full-screen polling.
+
+  bool changes1 = false;
+
+  switch((int)pollingType) {
+  case 0:
+    changes1 = poll_Dumb();
+    break;
+  case 1:
+    changes1 = poll_Traditional();
+    break;
+  case 2:
+    changes1 = poll_SkipCycles();
+    break;
+//case 3:
+  default:
+    changes1 = poll_DetectVideo();
+    break;
+  }
+
+  // Second step: optional thorough polling of the area around the pointer.
+  // We do that only if the pointer position is known and was set recently.
+
+  bool changes2 = false;
+  if (pollPointer) {
+    if (m_pointerPosKnown && time(NULL) - m_pointerPosTime >= 5) {
+      unsetPointerPos();
+    }
+    if (m_pointerPosKnown) {
+      changes2 = pollPointerArea();
+    }
+  }
+
+  // Update if needed.
+
+  if (changes1 || changes2)
+    m_server->tryUpdate();
+
+#ifdef DEBUG
+  debugAfterPoll();
+#endif
+}
+
+bool PollingManager::poll_DetectVideo()
+{
+  if (!m_server)
+    return false;
+
+  const int GRAND_STEP_DIVISOR = 8;
+  const int VIDEO_THRESHOLD_0 = 3;
+  const int VIDEO_THRESHOLD_1 = 5;
+
+  bool grandStep = (m_pollingStep % GRAND_STEP_DIVISOR == 0);
+
+  // 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;
+
+  Rect rect;
+  int nTilesChanged = 0;
+  int idx = 0;
+
+  for (int y = 0; y * 32 < m_height; y++) {
+    int tile_h = (m_height - y * 32 >= 32) ? 32 : m_height - y * 32;
+    if (scanLine >= tile_h)
+      break;
+    int scan_y = y * 32 + scanLine;
+    getRow(scan_y);
+    char *ptr_old = m_image->xim->data + scan_y * bytesPerLine;
+    char *ptr_new = m_rowImage->xim->data;
+    for (int x = 0; x * 32 < m_width; x++) {
+      int tile_w = (m_width - x * 32 >= 32) ? 32 : m_width - x * 32;
+      int nBytes = tile_w * bytesPerPixel;
+
+      char wasChanged = (memcmp(ptr_old, ptr_new, nBytes) != 0);
+      m_rateMatrix[idx] += wasChanged;
+
+      if (grandStep) {
+        if (m_rateMatrix[idx] <= VIDEO_THRESHOLD_0) {
+          m_videoFlags[idx] = 0;
+        } else if (m_rateMatrix[idx] >= VIDEO_THRESHOLD_1) {
+          m_videoFlags[idx] = 1;
+        }
+        m_rateMatrix[idx] = 0;
+      }
+
+      m_changedFlags[idx] |= wasChanged;
+      if ( m_changedFlags[idx] && (!m_videoFlags[idx] || grandStep) ) {
+        getTile32(x, y, tile_w, tile_h);
+        m_image->updateRect(m_tileImage, x * 32, y * 32);
+        rect.setXYWH(x * 32, y * 32, tile_w, tile_h);
+        m_server->add_changed(rect);
+        nTilesChanged++;
+        m_changedFlags[idx] = 0;
+      }
+
+      ptr_old += nBytes;
+      ptr_new += nBytes;
+      idx++;
+    }
+  }
+
+  if (grandStep)
+    adjustVideoArea();
+
+  return (nTilesChanged != 0);
+}
+
+bool PollingManager::poll_SkipCycles()
+{
+  if (!m_server)
+    return false;
+
+  enum {
+    NOT_CHANGED, CHANGED_ONCE, CHANGED_AGAIN
+  };
+
+  bool grandStep = (m_pollingStep % 8 == 0);
+
+  int nTilesChanged = 0;
+  int scanLine = m_pollingOrder[m_pollingStep++ % 32];
+  int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
+  int bytesPerLine = m_image->xim->bytes_per_line;
+  char *pstatus = m_statusMatrix;
+  bool wasChanged;
+  Rect rect;
+
+  for (int y = 0; y * 32 < m_height; y++) {
+    int tile_h = (m_height - y * 32 >= 32) ? 32 : m_height - y * 32;
+    if (scanLine >= tile_h)
+      scanLine %= tile_h;
+    int scan_y = y * 32 + scanLine;
+    getRow(scan_y);
+    char *ptr_old = m_image->xim->data + scan_y * bytesPerLine;
+    char *ptr_new = m_rowImage->xim->data;
+    for (int x = 0; x * 32 < m_width; x++) {
+      int tile_w = (m_width - x * 32 >= 32) ? 32 : m_width - x * 32;
+      int nBytes = tile_w * bytesPerPixel;
+
+      if (grandStep || *pstatus != CHANGED_AGAIN) {
+        wasChanged = (*pstatus == CHANGED_AGAIN) ?
+          true : (memcmp(ptr_old, ptr_new, nBytes) != 0);
+        if (wasChanged) {
+          if (grandStep || *pstatus == NOT_CHANGED) {
+            getTile32(x, y, tile_w, tile_h);
+            m_image->updateRect(m_tileImage, x * 32, y * 32);
+            rect.setXYWH(x * 32, y * 32, tile_w, tile_h);
+            m_server->add_changed(rect);
+            nTilesChanged++;
+            *pstatus = CHANGED_ONCE;
+          } else {
+            *pstatus = CHANGED_AGAIN;
+          }
+        } else if (grandStep) {
+          *pstatus = NOT_CHANGED;
+        }
+      }
+
+      ptr_old += nBytes;
+      ptr_new += nBytes;
+      pstatus++;
+    }
+  }
+
+  return (nTilesChanged != 0);
+}
+
+bool PollingManager::poll_Traditional()
+{
+  if (!m_server)
+    return false;
+
+  int nTilesChanged = 0;
+  int scanLine = m_pollingOrder[m_pollingStep++ % 32];
+  int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
+  int bytesPerLine = m_image->xim->bytes_per_line;
+  Rect rect;
+
+  for (int y = 0; y * 32 < m_height; y++) {
+    int tile_h = (m_height - y * 32 >= 32) ? 32 : m_height - y * 32;
+    if (scanLine >= tile_h)
+      break;
+    int scan_y = y * 32 + scanLine;
+    getRow(scan_y);
+    char *ptr_old = m_image->xim->data + scan_y * bytesPerLine;
+    char *ptr_new = m_rowImage->xim->data;
+    for (int x = 0; x * 32 < m_width; x++) {
+      int tile_w = (m_width - x * 32 >= 32) ? 32 : m_width - x * 32;
+      int nBytes = tile_w * bytesPerPixel;
+      if (memcmp(ptr_old, ptr_new, nBytes)) {
+        getTile32(x, y, tile_w, tile_h);
+        m_image->updateRect(m_tileImage, x * 32, y * 32);
+        rect.setXYWH(x * 32, y * 32, tile_w, tile_h);
+        m_server->add_changed(rect);
+        nTilesChanged++;
+      }
+      ptr_old += nBytes;
+      ptr_new += nBytes;
+    }
+  }
+
+  return (nTilesChanged != 0);
+}
+
+//
+// Simplest polling method, from the original x0vncserver of VNC4.
+//
+
+bool PollingManager::poll_Dumb()
+{
+  if (!m_server)
+    return false;
+
+  getScreen();
+  Rect rect(0, 0, m_width, m_height);
+  m_server->add_changed(rect);
+
+  // Report that some changes have been detected.
+  return true;
+}
+
+//
+// Compute coordinates of the rectangle around the pointer.
+//
+// ASSUMES: (m_pointerPosKnown != false)
+//
+
+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.
+  getArea128(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];
+  char *ptr = newFlags;
+  int x, y;
+
+  // DEBUG:
+  // int nVideoTiles = 0;
+
+  for (y = 0; y < m_heightTiles; y++) {
+    for (x = 0; x < m_widthTiles; x++) {
+
+      // DEBUG:
+      // nVideoTiles += m_videoFlags[y * m_widthTiles + x];
+
+      int weightedSum = 0, n;
+      if (y > 0 && x > 0) {
+        n = (m_videoFlags[ y    * m_widthTiles + (x-1)] +
+             m_videoFlags[(y-1) * m_widthTiles + (x-1)] +
+             m_videoFlags[(y-1) * m_widthTiles +  x   ]);
+        if (n == 3) {
+          *ptr++ = 1;
+          continue;
+        }
+        weightedSum += n;
+      }
+      if (y > 0 && x < m_widthTiles - 1) {
+        n = (m_videoFlags[ y    * m_widthTiles + (x+1)] +
+             m_videoFlags[(y-1) * m_widthTiles + (x+1)] +
+             m_videoFlags[(y-1) * m_widthTiles +  x   ]);
+        if (n == 3) {
+          *ptr++ = 1;
+          continue;
+        }
+        weightedSum += n;
+      }
+      if (y < m_heightTiles - 1 && x > 0) {
+        n = (m_videoFlags[ y    * m_widthTiles + (x-1)] +
+             m_videoFlags[(y+1) * m_widthTiles + (x-1)] +
+             m_videoFlags[(y+1) * m_widthTiles +  x   ]);
+        if (n == 3) {
+          *ptr++ = 1;
+          continue;
+        }
+        weightedSum += n;
+      }
+      if (y < m_heightTiles - 1 && x < m_widthTiles - 1) {
+        n = (m_videoFlags[ y    * m_widthTiles + (x+1)] +
+             m_videoFlags[(y+1) * m_widthTiles + (x+1)] +
+             m_videoFlags[(y+1) * m_widthTiles +  x   ]);
+        if (n == 3) {
+          *ptr++ = 1;
+          continue;
+        }
+        weightedSum += n;
+      }
+      *ptr++ = (weightedSum <= 3) ? 0 : m_videoFlags[y * m_widthTiles + x];
+    }
+  }
+
+  /*
+  /// DEBUG: ------------------------------------------------------
+  if (nVideoTiles) {
+    for (y = 0; y < m_heightTiles; y++) {
+      for (x = 0; x < m_widthTiles; x++) {
+        printf("%c", m_videoFlags[y * m_widthTiles + x] ? '@' : ':');
+      }
+      printf("        ");
+      for (x = 0; x < m_widthTiles; x++) {
+        printf("%c", newFlags[y * m_widthTiles + x] ? '@' : ':');
+      }
+      printf("\n");
+    }
+    printf("\n");
+  }
+  /// -------------------------------------------------------------
+  */
+
+  memcpy(m_videoFlags, newFlags, m_widthTiles * m_heightTiles);
+}