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/Geometry.cxx b/unix/x0vncserver/Geometry.cxx
new file mode 100644
index 0000000..ccb4e69
--- /dev/null
+++ b/unix/x0vncserver/Geometry.cxx
@@ -0,0 +1,71 @@
+/* Copyright (C) 2006 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.
+ */
+
+//
+// Geometry.cxx
+//
+
+#include <rfb/Rect.h>
+#include <rfb/LogWriter.h>
+#include <x0vncserver/Geometry.h>
+
+static LogWriter vlog("Geometry");
+
+StringParameter Geometry::m_geometryParam("Geometry",
+  "Screen area shown to VNC clients. "
+  "Format is <width>x<height>+<offset_x>+<offset_y>, "
+  "more information in man X, section GEOMETRY SPECIFICATIONS. "
+  "If the argument is empty, full screen is shown to VNC clients.",
+  "");
+
+Geometry::Geometry(int fullWidth, int fullHeight)
+  : m_width(fullWidth), m_height(fullHeight),
+    m_offsetLeft(0), m_offsetTop(0)
+{
+  const char *param = m_geometryParam.getData();
+  if (strlen(param) != 0) {
+    int w, h;
+    int x = 0, y = 0;
+    char sign_x[2] = "+";
+    char sign_y[2] = "+";
+    int n = sscanf(param, "%dx%d%1[+-]%d%1[+-]%d",
+                   &w, &h, sign_x, &x, sign_y, &y);
+    if ((n == 2 || n == 6) && w > 0 && h > 0 && x >= 0 && y >= 0) {
+      if (sign_x[0] == '-')
+        x = fullWidth - w - x;
+      if (sign_y[0] == '-')
+        y = fullHeight - h - y;
+      Rect fullRect(0, 0, fullWidth, fullHeight);
+      Rect partRect(x, y, x + w, y + h);
+      Rect r = partRect.intersect(fullRect);
+      if (r.area() > 0) {
+        m_width = r.width();
+        m_height = r.height();
+        m_offsetLeft = r.tl.x;
+        m_offsetTop = r.tl.y;
+      } else {
+        vlog.error("Requested area is out of the desktop boundaries");
+      }
+    } else {
+      vlog.error("Wrong argument format");
+    }
+  }
+  vlog.info("Desktop geometry is %dx%d+%d+%d",
+            m_width, m_height, m_offsetLeft, m_offsetTop);
+}
+
diff --git a/unix/x0vncserver/Geometry.h b/unix/x0vncserver/Geometry.h
new file mode 100644
index 0000000..95059e7
--- /dev/null
+++ b/unix/x0vncserver/Geometry.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2006 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.
+ */
+
+//
+// Geometry.h
+//
+
+#ifndef __GEOMETRY_H__
+#define __GEOMETRY_H__
+
+#include <rfb/Configuration.h>
+
+using namespace rfb;
+
+class Geometry
+{
+public:
+  Geometry(int fullWidth, int fullHeight);
+
+  int width() const { return m_width; }
+  int height() const { return m_height; }
+  int offsetLeft() const { return m_offsetLeft; }
+  int offsetTop() const { return m_offsetTop; }
+
+protected:
+  static StringParameter m_geometryParam;
+
+  int m_width;
+  int m_height;
+  int m_offsetLeft;
+  int m_offsetTop;
+};
+
+#endif // __GEOMETRY_H__
+
diff --git a/unix/x0vncserver/Image.cxx b/unix/x0vncserver/Image.cxx
new file mode 100644
index 0000000..fc66c5a
--- /dev/null
+++ b/unix/x0vncserver/Image.cxx
@@ -0,0 +1,549 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ * 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.
+ */
+//
+// Image.cxx
+//
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#ifdef HAVE_MITSHM
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#endif
+
+#include <rfb/LogWriter.h>
+#include <x0vncserver/Image.h>
+
+//
+// ImageCleanup is used to delete Image instances automatically on
+// program shutdown. This is important for shared memory images.
+//
+
+#include <list>
+
+class ImageCleanup {
+public:
+  std::list<Image *> images;
+
+  ~ImageCleanup()
+  {
+    while (!images.empty()) {
+      delete images.front();
+    }
+  }
+};
+
+ImageCleanup imageCleanup;
+
+//
+// Image class implementation.
+//
+
+static rfb::LogWriter vlog("Image");
+
+Image::Image(Display *d)
+  : xim(NULL), dpy(d), trueColor(true)
+{
+  imageCleanup.images.push_back(this);
+}
+
+Image::Image(Display *d, int width, int height)
+  : xim(NULL), dpy(d), trueColor(true)
+{
+  imageCleanup.images.push_back(this);
+  Init(width, height);
+}
+
+void Image::Init(int width, int height)
+{
+  Visual* vis = DefaultVisual(dpy, DefaultScreen(dpy));
+  trueColor = (vis->c_class == TrueColor);
+
+  xim = XCreateImage(dpy, vis, DefaultDepth(dpy, DefaultScreen(dpy)),
+                     ZPixmap, 0, 0, width, height, BitmapPad(dpy), 0);
+
+  xim->data = (char *)malloc(xim->bytes_per_line * xim->height);
+  if (xim->data == NULL) {
+    vlog.error("malloc() failed");
+    exit(1);
+  }
+}
+
+Image::~Image()
+{
+  imageCleanup.images.remove(this);
+
+  // XDestroyImage will free xim->data if necessary
+  if (xim != NULL)
+    XDestroyImage(xim);
+}
+
+void Image::get(Window wnd, int x, int y)
+{
+  get(wnd, x, y, xim->width, xim->height);
+}
+
+void Image::get(Window wnd, int x, int y, int w, int h)
+{
+  XGetSubImage(dpy, wnd, x, y, w, h, AllPlanes, ZPixmap, xim, 0, 0);
+}
+
+//
+// Copying pixels from one image to another.
+//
+// FIXME: Use Point and Rect structures?
+// FIXME: Too many similar methods?
+//
+
+inline
+void Image::copyPixels(XImage *src,
+                       int dst_x, int dst_y,
+                       int src_x, int src_y,
+                       int w, int h)
+{
+  const char *srcOffset =
+    src->data + (src_y * src->bytes_per_line +
+                 src_x * (src->bits_per_pixel / 8));
+  char *dstOffset =
+    xim->data + (dst_y * xim->bytes_per_line +
+                 dst_x * (xim->bits_per_pixel / 8));
+
+  int rowLength = w * (xim->bits_per_pixel / 8);
+
+  for (int i = 0; i < h ; i++) {
+    memcpy(dstOffset, srcOffset, rowLength);
+    srcOffset += src->bytes_per_line;
+    dstOffset += xim->bytes_per_line;
+  }
+}
+
+void Image::updateRect(XImage *src, int dst_x, int dst_y)
+{
+  // Limit width and height at destination image size.
+  int w = src->width;
+  if (dst_x + w > xim->width)
+    w = xim->width - dst_x;
+  int h = src->height;
+  if (dst_y + h > xim->height)
+    h = xim->height - dst_y;
+
+  copyPixels(src, dst_x, dst_y, 0, 0, w, h);
+}
+
+void Image::updateRect(Image *src, int dst_x, int dst_y)
+{
+  updateRect(src->xim, dst_x, dst_y);
+}
+
+void Image::updateRect(XImage *src, int dst_x, int dst_y, int w, int h)
+{
+  // Correct width and height if necessary.
+  if (w > src->width)
+    w = src->width;
+  if (dst_x + w > xim->width)
+    w = xim->width - dst_x;
+  if (h > src->height)
+    h = src->height;
+  if (dst_y + h > xim->height)
+    h = xim->height - dst_y;
+
+  copyPixels(src, dst_x, dst_y, 0, 0, w, h);
+}
+
+void Image::updateRect(Image *src, int dst_x, int dst_y, int w, int h)
+{
+  updateRect(src->xim, dst_x, dst_y, w, h);
+}
+
+void Image::updateRect(XImage *src, int dst_x, int dst_y,
+                       int src_x, int src_y, int w, int h)
+{
+  // Correct width and height if necessary.
+  if (src_x + w > src->width)
+    w = src->width - src_x;
+  if (dst_x + w > xim->width)
+    w = xim->width - dst_x;
+  if (src_y + h > src->height)
+    h = src->height - src_y;
+  if (dst_y + h > xim->height)
+    h = xim->height - dst_y;
+
+  copyPixels(src, dst_x, dst_y, src_x, src_y, w, h);
+}
+
+void Image::updateRect(Image *src, int dst_x, int dst_y,
+                       int src_x, int src_y, int w, int h)
+{
+  updateRect(src->xim, dst_x, dst_y, src_x, src_y, w, h);
+}
+
+#ifdef HAVE_MITSHM
+
+//
+// ShmImage class implementation.
+//
+
+static bool caughtShmError = false;
+
+static int ShmCreationXErrorHandler(Display *dpy, XErrorEvent *error)
+{
+  caughtShmError = true;
+  return 0;
+}
+
+ShmImage::ShmImage(Display *d)
+  : Image(d), shminfo(NULL)
+{
+}
+
+ShmImage::ShmImage(Display *d, int width, int height)
+  : Image(d), shminfo(NULL)
+{
+  Init(width, height);
+}
+
+// FIXME: Remove duplication of cleanup operations.
+void ShmImage::Init(int width, int height, const XVisualInfo *vinfo)
+{
+  int major, minor;
+  Bool pixmaps;
+
+  if (!XShmQueryVersion(dpy, &major, &minor, &pixmaps)) {
+    vlog.error("XShmQueryVersion() failed");
+    return;
+  }
+
+  Visual *visual;
+  int depth;
+
+  if (vinfo == NULL) {
+    visual = DefaultVisual(dpy, DefaultScreen(dpy));
+    depth = DefaultDepth(dpy, DefaultScreen(dpy));
+  } else {
+    visual = vinfo->visual;
+    depth = vinfo->depth;
+  }
+
+  trueColor = (visual->c_class == TrueColor);
+
+  shminfo = new XShmSegmentInfo;
+
+  xim = XShmCreateImage(dpy, visual, depth, ZPixmap, 0, shminfo,
+			width, height);
+  if (xim == NULL) {
+    vlog.error("XShmCreateImage() failed");
+    delete shminfo;
+    shminfo = NULL;
+    return;
+  }
+
+  shminfo->shmid = shmget(IPC_PRIVATE,
+                          xim->bytes_per_line * xim->height,
+                          IPC_CREAT|0777);
+  if (shminfo->shmid == -1) {
+    perror("shmget");
+    vlog.error("shmget() failed (%d bytes requested)",
+               int(xim->bytes_per_line * xim->height));
+    XDestroyImage(xim);
+    xim = NULL;
+    delete shminfo;
+    shminfo = NULL;
+    return;
+  }
+
+  shminfo->shmaddr = xim->data = (char *)shmat(shminfo->shmid, 0, 0);
+  if (shminfo->shmaddr == (char *)-1) {
+    perror("shmat");
+    vlog.error("shmat() failed (%d bytes requested)",
+               int(xim->bytes_per_line * xim->height));
+    shmctl(shminfo->shmid, IPC_RMID, 0);
+    XDestroyImage(xim);
+    xim = NULL;
+    delete shminfo;
+    shminfo = NULL;
+    return;
+  }
+
+  shminfo->readOnly = False;
+
+  XErrorHandler oldHdlr = XSetErrorHandler(ShmCreationXErrorHandler);
+  XShmAttach(dpy, shminfo);
+  XSync(dpy, False);
+  XSetErrorHandler(oldHdlr);
+  if (caughtShmError) {
+    vlog.error("XShmAttach() failed");
+    shmdt(shminfo->shmaddr);
+    shmctl(shminfo->shmid, IPC_RMID, 0);
+    XDestroyImage(xim);
+    xim = NULL;
+    delete shminfo;
+    shminfo = NULL;
+    return;
+  }
+}
+
+ShmImage::~ShmImage()
+{
+  // FIXME: Destroy image as described in MIT-SHM documentation.
+  if (shminfo != NULL) {
+    shmdt(shminfo->shmaddr);
+    shmctl(shminfo->shmid, IPC_RMID, 0);
+    delete shminfo;
+  }
+}
+
+void ShmImage::get(Window wnd, int x, int y)
+{
+  XShmGetImage(dpy, wnd, xim, x, y, AllPlanes);
+}
+
+void ShmImage::get(Window wnd, int x, int y, int w, int h)
+{
+  // FIXME: Use SHM for this as well?
+  XGetSubImage(dpy, wnd, x, y, w, h, AllPlanes, ZPixmap, xim, 0, 0);
+}
+
+#ifdef HAVE_READDISPLAY
+
+//
+// IrixOverlayShmImage class implementation.
+//
+
+IrixOverlayShmImage::IrixOverlayShmImage(Display *d)
+  : ShmImage(d), readDisplayBuf(NULL)
+{
+}
+
+IrixOverlayShmImage::IrixOverlayShmImage(Display *d, int width, int height)
+  : ShmImage(d), readDisplayBuf(NULL)
+{
+  Init(width, height);
+}
+
+void IrixOverlayShmImage::Init(int width, int height)
+{
+  // First determine the pixel format used by XReadDisplay.
+  XVisualInfo vinfo;
+  if (!getOverlayVisualInfo(&vinfo))
+    return;
+
+  // Create an SHM image of the same format.
+  ShmImage::Init(width, height, &vinfo);
+  if (xim == NULL)
+    return;
+
+  // FIXME: Check if the extension is available at run time.
+  readDisplayBuf = XShmCreateReadDisplayBuf(dpy, NULL, shminfo, width, height);
+}
+
+bool IrixOverlayShmImage::getOverlayVisualInfo(XVisualInfo *vinfo_ret)
+{
+  // First, get an image in the format returned by XReadDisplay.
+  unsigned long hints = 0, hints_ret;
+  XImage *testImage = XReadDisplay(dpy, DefaultRootWindow(dpy),
+				   0, 0, 8, 8, hints, &hints_ret);
+  if (testImage == NULL)
+    return false;
+
+  // Fill in a template for matching visuals.
+  XVisualInfo tmpl;
+  tmpl.c_class = TrueColor;
+  tmpl.depth = 24;
+  tmpl.red_mask = testImage->red_mask;
+  tmpl.green_mask = testImage->green_mask;
+  tmpl.blue_mask = testImage->blue_mask;
+
+  // List fields in template that make sense.
+  long mask = (VisualClassMask |
+	       VisualRedMaskMask |
+	       VisualGreenMaskMask |
+	       VisualBlueMaskMask);
+
+  // We don't need that image any more.
+  XDestroyImage(testImage);
+
+  // Now, get a list of matching visuals available.
+  int nVisuals;
+  XVisualInfo *vinfo = XGetVisualInfo(dpy, mask, &tmpl, &nVisuals);
+  if (vinfo == NULL || nVisuals <= 0) {
+    if (vinfo != NULL) {
+      XFree(vinfo);
+    }
+    return false;
+  }
+
+  // Use first visual from the list.
+  *vinfo_ret = vinfo[0];
+
+  XFree(vinfo);
+
+  return true;
+}
+
+IrixOverlayShmImage::~IrixOverlayShmImage()
+{
+  if (readDisplayBuf != NULL)
+    XShmDestroyReadDisplayBuf(readDisplayBuf);
+}
+
+void IrixOverlayShmImage::get(Window wnd, int x, int y)
+{
+  get(wnd, x, y, xim->width, xim->height);
+}
+
+void IrixOverlayShmImage::get(Window wnd, int x, int y, int w, int h)
+{
+  XRectangle rect;
+  unsigned long hints = XRD_TRANSPARENT | XRD_READ_POINTER;
+
+  rect.x = x;
+  rect.y = y;
+  rect.width = w;
+  rect.height = h;
+
+  XShmReadDisplayRects(dpy, wnd,
+                       &rect, 1, readDisplayBuf, -x, -y,
+                       hints, &hints);
+}
+
+#endif // HAVE_READDISPLAY
+#endif // HAVE_MITSHM
+
+#ifdef HAVE_SUN_OVL
+
+//
+// SolarisOverlayImage class implementation
+//
+
+SolarisOverlayImage::SolarisOverlayImage(Display *d)
+  : Image(d)
+{
+}
+
+SolarisOverlayImage::SolarisOverlayImage(Display *d, int width, int height)
+  : Image(d)
+{
+  Init(width, height);
+}
+
+void SolarisOverlayImage::Init(int width, int height)
+{
+  // FIXME: Check if the extension is available at run time.
+  // FIXME: Maybe just read a small (e.g. 8x8) screen area then
+  //        reallocate xim->data[] and correct width and height?
+  xim = XReadScreen(dpy, DefaultRootWindow(dpy), 0, 0, width, height, True);
+  if (xim == NULL) {
+    vlog.error("XReadScreen() failed");
+    return;
+  }
+}
+
+SolarisOverlayImage::~SolarisOverlayImage()
+{
+}
+
+void SolarisOverlayImage::get(Window wnd, int x, int y)
+{
+  get(wnd, x, y, xim->width, xim->height);
+}
+
+void SolarisOverlayImage::get(Window wnd, int x, int y, int w, int h)
+{
+  XImage *tmp_xim = XReadScreen(dpy, wnd, x, y, w, h, True);
+  if (tmp_xim == NULL)
+    return;
+
+  updateRect(tmp_xim, 0, 0);
+
+  XDestroyImage(tmp_xim);
+}
+
+#endif // HAVE_SUN_OVL
+
+//
+// ImageFactory class implementation
+//
+// FIXME: Make ImageFactory always create images of the same class?
+//
+
+// Prepare useful shortcuts for compile-time options.
+#if defined(HAVE_READDISPLAY) && defined(HAVE_MITSHM)
+#define HAVE_SHM_READDISPLAY
+#endif
+#if defined(HAVE_SHM_READDISPLAY) || defined(HAVE_SUN_OVL)
+#define HAVE_OVERLAY_EXT
+#endif
+
+ImageFactory::ImageFactory(bool allowShm, bool allowOverlay)
+  : mayUseShm(allowShm), mayUseOverlay(allowOverlay)
+{
+}
+
+ImageFactory::~ImageFactory()
+{
+}
+
+Image *ImageFactory::newImage(Display *d, int width, int height)
+{
+  Image *image = NULL;
+
+  // First, try to create an image with overlay support.
+
+#ifdef HAVE_OVERLAY_EXT
+  if (mayUseOverlay) {
+#if defined(HAVE_SHM_READDISPLAY)
+    if (mayUseShm) {
+      image = new IrixOverlayShmImage(d, width, height);
+      if (image->xim != NULL) {
+        return image;
+      }
+    }
+#elif defined(HAVE_SUN_OVL)
+    image = new SolarisOverlayImage(d, width, height);
+    if (image->xim != NULL) {
+      return image;
+    }
+#endif
+    if (image != NULL) {
+      delete image;
+      vlog.error("Failed to create overlay image, trying other options");
+    }
+  }
+#endif // HAVE_OVERLAY_EXT
+
+  // Now, try to use shared memory image.
+
+#ifdef HAVE_MITSHM
+  if (mayUseShm) {
+    image = new ShmImage(d, width, height);
+    if (image->xim != NULL) {
+      return image;
+    }
+
+    delete image;
+    vlog.error("Failed to create SHM image, falling back to Xlib image");
+  }
+#endif // HAVE_MITSHM
+
+  // Fall back to Xlib image.
+
+  image = new Image(d, width, height);
+  return image;
+}
diff --git a/unix/x0vncserver/Image.h b/unix/x0vncserver/Image.h
new file mode 100644
index 0000000..535cee6
--- /dev/null
+++ b/unix/x0vncserver/Image.h
@@ -0,0 +1,223 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ * 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.
+ */
+//
+// Image.h
+//
+
+#ifndef __IMAGE_H__
+#define __IMAGE_H__
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+//
+// Image class is an Xlib-based implementation of screen image storage.
+//
+
+class Image {
+
+public:
+
+  Image(Display *d);
+  Image(Display *d, int width, int height);
+  virtual ~Image();
+
+  bool isTrueColor() const { return trueColor; }
+
+  virtual const char *className() const {
+    return "Image";
+  }
+  virtual const char *classDesc() const {
+    return "basic Xlib image";
+  }
+
+  virtual void get(Window wnd, int x = 0, int y = 0);
+  virtual void get(Window wnd, int x, int y, int w, int h);
+
+// Copying pixels from one image to another.
+  virtual void updateRect(XImage *src, int dst_x = 0, int dst_y = 0);
+  virtual void updateRect(Image *src, int dst_x = 0, int dst_y = 0);
+  virtual void updateRect(XImage *src, int dst_x, int dst_y, int w, int h);
+  virtual void updateRect(Image *src, int dst_x, int dst_y, int w, int h);
+  virtual void updateRect(XImage *src, int dst_x, int dst_y,
+                          int src_x, int src_y, int w, int h);
+  virtual void updateRect(Image *src, int dst_x, int dst_y,
+                          int src_x, int src_y, int w, int h);
+
+  // Pointer to corresponding XImage, made public for efficiency.
+  // NOTE: if this field is NULL, then no methods other than Init()
+  //       may be called.
+  XImage* xim;
+
+protected:
+
+  void Init(int width, int height);
+
+  // Like updateRect(), but does not check arguments.
+  void copyPixels(XImage *src,
+                  int dst_x, int dst_y,
+                  int src_x, int src_y,
+                  int w, int h);
+
+  Display *dpy;
+  bool trueColor;
+
+};
+
+//
+// ShmImage uses MIT-SHM extension of an X server to get image data.
+//
+
+#ifdef HAVE_MITSHM
+
+#include <X11/extensions/XShm.h>
+
+class ShmImage : public Image {
+
+public:
+
+  ShmImage(Display *d);
+  ShmImage(Display *d, int width, int height);
+  virtual ~ShmImage();
+
+  virtual const char *className() const {
+    return "ShmImage";
+  }
+  virtual const char *classDesc() const {
+    return "shared memory image";
+  }
+
+  virtual void get(Window wnd, int x = 0, int y = 0);
+  virtual void get(Window wnd, int x, int y, int w, int h);
+
+protected:
+
+  void Init(int width, int height, const XVisualInfo *vinfo = NULL);
+
+  XShmSegmentInfo *shminfo;
+
+};
+
+//
+// IrixOverlayShmImage uses ReadDisplay extension of an X server to
+// get truecolor image data, regardless of the default X visual type. 
+// This method is available on Irix only.
+//
+
+#ifdef HAVE_READDISPLAY
+
+#include <X11/extensions/readdisplay.h>
+
+class IrixOverlayShmImage : public ShmImage {
+
+public:
+
+  IrixOverlayShmImage(Display *d);
+  IrixOverlayShmImage(Display *d, int width, int height);
+  virtual ~IrixOverlayShmImage();
+
+  virtual const char *className() const {
+    return "IrixOverlayShmImage";
+  }
+  virtual const char *classDesc() const {
+    return "IRIX-specific SHM-aware overlay image";
+  }
+
+  virtual void get(Window wnd, int x = 0, int y = 0);
+  virtual void get(Window wnd, int x, int y, int w, int h);
+
+protected:
+
+  void Init(int width, int height);
+
+  // This method searches available X visuals for one that matches
+  // actual pixel format returned by XReadDisplay(). Returns true on
+  // success, false if there is no matching visual. On success, visual
+  // information is placed into the structure pointed by vinfo_ret.
+  bool getOverlayVisualInfo(XVisualInfo *vinfo_ret);
+
+  ShmReadDisplayBuf *readDisplayBuf;
+
+};
+
+#endif // HAVE_READDISPLAY
+#endif // HAVE_MITSHM
+
+//
+// SolarisOverlayImage uses SUN_OVL extension of an X server to get
+// truecolor image data, regardless of the default X visual type. This
+// method is available on Solaris only.
+//
+
+#ifdef HAVE_SUN_OVL
+
+#include <X11/extensions/transovl.h>
+
+class SolarisOverlayImage : public Image {
+
+public:
+
+  SolarisOverlayImage(Display *d);
+  SolarisOverlayImage(Display *d, int width, int height);
+  virtual ~SolarisOverlayImage();
+
+  virtual const char *className() const {
+    return "SolarisOverlayImage";
+  }
+  virtual const char *classDesc() const {
+    return "Solaris-specific non-SHM overlay image";
+  }
+
+  virtual void get(Window wnd, int x = 0, int y = 0);
+  virtual void get(Window wnd, int x, int y, int w, int h);
+
+protected:
+
+  void Init(int width, int height);
+
+};
+
+#endif // HAVE_SUN_OVL
+
+//
+// ImageFactory class is used to produce instances of Image-derived
+// objects that are most appropriate for current X server and user
+// settings.
+//
+
+class ImageFactory {
+
+public:
+
+  ImageFactory(bool allowShm, bool allowOverlay);
+  virtual ~ImageFactory();
+
+  bool isShmAllowed()     { return mayUseShm; }
+  bool isOverlayAllowed() { return mayUseOverlay; }
+
+  virtual Image *newImage(Display *d, int width, int height);
+
+protected:
+
+  bool mayUseShm;
+  bool mayUseOverlay;
+
+};
+
+#endif // __IMAGE_H__
diff --git a/unix/x0vncserver/Makefile.in b/unix/x0vncserver/Makefile.in
new file mode 100644
index 0000000..cbb9fed
--- /dev/null
+++ b/unix/x0vncserver/Makefile.in
@@ -0,0 +1,30 @@
+
+SRCS = Image.cxx TimeMillis.cxx PollingScheduler.cxx PollingManager.cxx \
+       Geometry.cxx \
+       x0vncserver.cxx ../vncconfig_unix/QueryConnectDialog.cxx
+
+OBJS = $(SRCS:.cxx=.o)
+
+program = x0vncserver
+
+DEP_LIBS = ../rfb/librfb.a \
+           ../network/libnetwork.a \
+           ../rdr/librdr.a \
+	   ../tx/libtx.a
+
+EXTRA_LIBS = @ZLIB_LIB@ @JPEG_LIB@ @INET_LIB@ @X_PRE_LIBS@ @X_LIBS@ \
+             @XTEST_LIB@ -lXext -lX11 @X_EXTRA_LIBS@
+
+# X_CFLAGS are really CPPFLAGS
+DIR_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/tx -I$(top_srcdir)/vncconfig_unix \
+  @XTEST_DEFINE@ @READDISPLAY_DEFINE@ @MITSHM_DEFINE@ @X_CFLAGS@
+
+all:: $(program)
+
+$(program): $(OBJS) buildtime.o $(DEP_LIBS)
+	rm -f $(program)
+	$(CXXLD) $(CXXFLAGS) $(LDFLAGS) -o $@ $(OBJS) buildtime.o $(DEP_LIBS) $(LIBS) $(EXTRA_LIBS)
+
+buildtime.o: $(OBJS) $(DEP_LIBS)
+
+# followed by boilerplate.mk
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);
+}
diff --git a/unix/x0vncserver/PollingManager.h b/unix/x0vncserver/PollingManager.h
new file mode 100644
index 0000000..b8eef50
--- /dev/null
+++ b/unix/x0vncserver/PollingManager.h
@@ -0,0 +1,148 @@
+/* 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.h
+//
+
+#ifndef __POLLINGMANAGER_H__
+#define __POLLINGMANAGER_H__
+
+#include <X11/Xlib.h>
+#include <rfb/VNCServer.h>
+
+#include <x0vncserver/Image.h>
+
+#ifdef DEBUG
+#include <x0vncserver/TimeMillis.h>
+#endif
+
+using namespace rfb;
+
+class PollingManager {
+
+public:
+
+  PollingManager(Display *dpy, Image *image, ImageFactory *factory,
+                 int offsetLeft = 0, int offsetTop = 0);
+  virtual ~PollingManager();
+
+  void setVNCServer(VNCServer *s);
+
+  void setPointerPos(const Point &pos);
+  void unsetPointerPos();
+
+  void poll();
+
+  // Configurable parameters.
+  static BoolParameter pollPointer;
+  static IntParameter pollingType;
+
+protected:
+
+  //
+  // Implementations of different polling algorithms.
+  // Return value of true reports that some changes were detected.
+  //
+  bool poll_DetectVideo();
+  bool poll_SkipCycles();
+  bool poll_Traditional();
+  bool poll_Dumb();
+
+  // Separate polling for the area around current pointer position.
+  void computePointerArea(Rect *r);
+  bool pollPointerArea();
+
+  Display *m_dpy;
+  VNCServer *m_server;
+
+  Image *m_image;
+  int m_offsetLeft;
+  int m_offsetTop;
+  int m_width;
+  int m_height;
+  int m_widthTiles;
+  int m_heightTiles;
+
+  // Tracking pointer position for polling improvements.
+  bool m_pointerPosKnown;
+  Point m_pointerPos;
+  time_t m_pointerPosTime;
+
+private:
+
+  inline void getScreen() {
+    m_image->get(DefaultRootWindow(m_dpy), m_offsetLeft, m_offsetTop);
+  }
+
+  inline void getRow(int y) {
+    m_rowImage->get(DefaultRootWindow(m_dpy), m_offsetLeft, m_offsetTop + y);
+  }
+
+  inline void getTile32(int tx, int ty, int w, int h) {
+    if (w == 32 && h == 32) {
+      // This version of get() may be better optimized.
+      m_tileImage->get(DefaultRootWindow(m_dpy),
+                       m_offsetLeft + tx * 32, m_offsetTop + ty * 32);
+    } else {
+      // Generic version of get() for arbitrary width and height.
+      m_tileImage->get(DefaultRootWindow(m_dpy),
+                       m_offsetLeft + tx * 32, m_offsetTop + ty * 32, w, h);
+    }
+  }
+
+  inline void getArea128(int x, int y, int w, int h) {
+    if (w == 128 && h == 128) {
+      // This version of get() may be better optimized.
+      m_areaImage->get(DefaultRootWindow(m_dpy),
+                       m_offsetLeft + x, m_offsetTop + y);
+    } else {
+      // Generic version of get() for arbitrary width and height.
+      m_areaImage->get(DefaultRootWindow(m_dpy),
+                       m_offsetLeft + x, m_offsetTop + y, w, h);
+    }
+  }
+
+  void adjustVideoArea();
+
+  // Additional images used in polling algorithms.
+  Image *m_rowImage;            // One row of the framebuffer
+  Image *m_tileImage;           // One tile (32x32 or less)
+  Image *m_areaImage;           // Area around the pointer (up to 128x128)
+
+  char *m_statusMatrix;
+
+  char *m_rateMatrix;
+  char *m_videoFlags;
+  char *m_changedFlags;
+
+  unsigned int m_pollingStep;
+  static const int m_pollingOrder[];
+
+#ifdef DEBUG
+private:
+
+  void debugBeforePoll();
+  void debugAfterPoll();
+
+  TimeMillis m_timeSaved;
+#endif
+
+};
+
+#endif // __POLLINGMANAGER_H__
diff --git a/unix/x0vncserver/PollingScheduler.cxx b/unix/x0vncserver/PollingScheduler.cxx
new file mode 100644
index 0000000..c9d8d60
--- /dev/null
+++ b/unix/x0vncserver/PollingScheduler.cxx
@@ -0,0 +1,211 @@
+/* Copyright (C) 2006 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.
+ */
+
+//
+// PollingScheduler class implementation.
+//
+
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+#include <x0vncserver/PollingScheduler.h>
+
+PollingScheduler::PollingScheduler(int interval, int maxload)
+{
+  setParameters(interval, maxload);
+  reset();
+}
+
+void PollingScheduler::setParameters(int interval, int maxload)
+{
+  m_interval = interval;
+  m_maxload = maxload;
+
+  if (m_interval < 0) {
+    m_interval = 0;
+  }
+  if (m_maxload < 1) {
+    m_maxload = 1;
+  } else if (m_maxload > 100) {
+    m_maxload = 100;
+  }
+}
+
+void PollingScheduler::reset()
+{
+  m_initialState = true;
+}
+
+void PollingScheduler::newPass()
+{
+  TimeMillis timeNow;
+
+  if (m_initialState) {
+
+    // First polling pass: initialize statistics.
+    m_initialState = false;
+    m_ratedDuration = 0;
+    m_sleeping = 0;
+    memset(m_errors, 0, sizeof(m_errors));
+    m_errorSum = 0;
+    m_errorAbsSum = 0;
+    memset(m_durations, 0, sizeof(m_durations));
+    m_durationSum = 0;
+    memset(m_slept, 0, sizeof(m_slept));
+    m_sleptSum = 0;
+    m_idx = 0;
+    m_count = 0;
+
+  } else {
+
+    // Stop sleeping if not yet.
+    if (m_sleeping)
+      sleepFinished();
+
+    // Update statistics on sleeping time and total pass duration.
+    int duration = timeNow.diffFrom(m_passStarted);
+
+    int oldest = m_durations[m_idx];
+    m_durations[m_idx] = duration;
+    m_durationSum = m_durationSum - oldest + duration;
+
+    oldest = m_slept[m_idx];
+    m_slept[m_idx] = m_sleptThisPass;
+    m_sleptSum = m_sleptSum - oldest + m_sleptThisPass;
+
+    // Compute and save the difference between actual and planned time.
+    int newError = duration - m_interval;
+    oldest = m_errors[m_idx];
+    m_errors[m_idx] = newError;
+    m_errorSum = m_errorSum - oldest + newError;
+    m_errorAbsSum = m_errorAbsSum - abs(oldest) + abs(newError);
+
+    //
+    // Below is the most important part.
+    // Compute desired duration of the upcoming polling pass.
+    //
+
+    // Estimation based on keeping up constant interval.
+    m_ratedDuration = m_interval - m_errorSum / 2;
+
+    // Estimations based on keeping up desired CPU load.
+    int optimalLoadDuration1 = 0;
+    int optimalLoadDuration8 = 0;
+    int optimalLoadDuration = 0;
+
+    if (m_count > 4) {
+      // Estimation 1 (use previous pass statistics).
+      optimalLoadDuration1 =
+        ((duration - m_sleptThisPass) * 100 + m_maxload/2) / m_maxload;
+
+      if (m_count > 16) {
+        // Estimation 2 (use history of 8 previous passes).
+        optimalLoadDuration8 =
+          ((m_durationSum - m_sleptSum) * 900 + m_maxload*4) / (m_maxload*8)
+          - m_durationSum;
+        // Mix the above two giving more priority to the first.
+        optimalLoadDuration =
+          (2 * optimalLoadDuration1 + optimalLoadDuration8) / 3;
+      } else {
+        optimalLoadDuration = optimalLoadDuration1;
+      }
+    }
+
+#ifdef DEBUG
+    fprintf(stderr, "<est %3d,%3d,%d>\t",
+            m_ratedDuration, optimalLoadDuration1, optimalLoadDuration8);
+#endif
+
+    // Choose final estimation.
+    if (m_ratedDuration < optimalLoadDuration) {
+      m_ratedDuration = optimalLoadDuration;
+    }
+    if (m_ratedDuration < 0) {
+      m_ratedDuration = 0;
+    } else if (m_ratedDuration > 500 && m_interval <= 100) {
+      m_ratedDuration = 500;
+    } else if (m_ratedDuration > 1000) {
+      m_ratedDuration = 1000;
+    }
+
+#ifdef DEBUG
+    fprintf(stderr, "<final est %3d>\t", m_ratedDuration);
+#endif
+
+    // Update ring buffer indexer (8 elements per each arrays).
+    m_idx = (m_idx + 1) & 7;
+
+    // Update pass counter.
+    m_count++;
+
+  }
+
+  m_passStarted = timeNow;
+  m_sleptThisPass = 0;
+}
+
+void PollingScheduler::sleepStarted()
+{
+  if (m_initialState || m_sleeping)
+    return;
+
+  m_sleepStarted.update();
+
+  m_sleeping = true;
+}
+
+void PollingScheduler::sleepFinished()
+{
+  if (m_initialState || !m_sleeping)
+    return;
+
+  TimeMillis timeNow;
+  m_sleptThisPass += timeNow.diffFrom(m_sleepStarted);
+
+  m_sleeping = false;
+}
+
+int PollingScheduler::millisRemaining() const
+{
+  if (m_initialState)
+    return 0;
+
+  TimeMillis timeNow;
+  int elapsed = timeNow.diffFrom(m_passStarted);
+
+  if (elapsed > m_ratedDuration)
+    return 0;
+
+  return (m_ratedDuration - elapsed);
+}
+
+bool PollingScheduler::goodTimeToPoll() const
+{
+  if (m_initialState)
+    return true;
+
+  // Average error (per 8 elements in the ring buffer).
+  int errorAvg = (m_errorAbsSum + 4) / 8;
+
+  // It's ok to poll earlier if new error is no more than half-average.
+  return (millisRemaining() <= errorAvg / 2);
+}
diff --git a/unix/x0vncserver/PollingScheduler.h b/unix/x0vncserver/PollingScheduler.h
new file mode 100644
index 0000000..2e3e5be
--- /dev/null
+++ b/unix/x0vncserver/PollingScheduler.h
@@ -0,0 +1,98 @@
+/* Copyright (C) 2006 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.
+ */
+
+//
+// PollingScheduler class. It is used for deciding when to start new
+// polling pass, and how much time it is ok to sleep before starting. 
+// PollingScheduler is given a desired polling interval, but it can
+// add time between polling passes if needed for satisfying processor
+// usage limitation.
+//
+
+#ifndef __POLLINGSCHEDULER_H__
+#define __POLLINGSCHEDULER_H__
+
+#include <x0vncserver/TimeMillis.h>
+
+class PollingScheduler {
+
+public:
+
+  PollingScheduler(int interval, int maxload = 50);
+
+  // Set polling parameters.
+  void setParameters(int interval, int maxload = 50);
+
+  // Reset the object into the initial state (no polling performed).
+  void reset();
+
+  // Tell the scheduler that new polling pass is just being started.
+  void newPass();
+
+  // Inform the scheduler about times when we sleep.
+  void sleepStarted();
+  void sleepFinished();
+
+  // This function estimates time remaining before new polling pass.
+  int millisRemaining() const;
+
+  // This function tells if it's ok to start polling pass right now.
+  bool goodTimeToPoll() const;
+
+protected:
+
+  // Parameters.
+  int m_interval;
+  int m_maxload;
+
+  // This boolean flag is true when we do not poll the screen.
+  bool m_initialState;
+
+  // Time stamp saved on starting current polling pass.
+  TimeMillis m_passStarted;
+
+  // Desired duration of current polling pass.
+  int m_ratedDuration;
+
+  // These are for measuring sleep time in current pass.
+  TimeMillis m_sleepStarted;
+  bool m_sleeping;
+  int m_sleptThisPass;
+
+  // Ring buffer for tracking past timing errors.
+  int m_errors[8];
+  int m_errorSum;
+  int m_errorAbsSum;
+
+  // Ring buffer for tracking total pass durations (work + sleep).
+  int m_durations[8];
+  int m_durationSum;
+
+  // Ring buffer for tracking past sleep times.
+  int m_slept[8];
+  int m_sleptSum;
+
+  // Indexer for all ring buffers.
+  int m_idx;
+
+  // Pass counter.
+  int m_count;
+};
+
+#endif // __POLLINGSCHEDULER_H__
+
diff --git a/unix/x0vncserver/TimeMillis.cxx b/unix/x0vncserver/TimeMillis.cxx
new file mode 100644
index 0000000..059c043
--- /dev/null
+++ b/unix/x0vncserver/TimeMillis.cxx
@@ -0,0 +1,49 @@
+/* Copyright (C) 2006 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.
+ */
+
+//
+// TimeMillis.cxx
+//
+
+#include <x0vncserver/TimeMillis.h>
+
+// XXX Lynx/OS 2.3: get proto for gettimeofday()
+#ifdef Lynx
+#include <sys/proto.h>
+#endif
+
+TimeMillis::TimeMillis()
+{
+  update();
+}
+
+bool TimeMillis::update()
+{
+  struct timezone tz;
+  return (gettimeofday(&m_timeval, &tz) == 0);
+}
+
+int TimeMillis::diffFrom(const TimeMillis &older) const
+{
+  int diff = (int)
+    ((m_timeval.tv_usec - older.m_timeval.tv_usec + 500) / 1000 +
+     (m_timeval.tv_sec - older.m_timeval.tv_sec) * 1000);
+
+  return diff;
+}
+
diff --git a/unix/x0vncserver/TimeMillis.h b/unix/x0vncserver/TimeMillis.h
new file mode 100644
index 0000000..e79db12
--- /dev/null
+++ b/unix/x0vncserver/TimeMillis.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 2006 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.
+ */
+
+//
+// TimeMillis.h
+//
+
+#ifndef __TIMEMILLIS_H__
+#define __TIMEMILLIS_H__
+
+#include <sys/time.h>
+
+class TimeMillis {
+
+public:
+
+  TimeMillis();
+
+  // Set this object to current time, returns true on sucess.
+  bool update();
+
+  // Return difference in milliseconds between two time points.
+  int diffFrom(const TimeMillis &older) const;
+
+protected:
+
+  struct timeval m_timeval;
+
+};
+
+#endif // __TIMEMILLIS_H__
+
diff --git a/unix/x0vncserver/buildtime.c b/unix/x0vncserver/buildtime.c
new file mode 100644
index 0000000..a96031c
--- /dev/null
+++ b/unix/x0vncserver/buildtime.c
@@ -0,0 +1,18 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  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.
+ */
+char buildtime[] = __DATE__ " " __TIME__;
diff --git a/unix/x0vncserver/x0vncserver.cxx b/unix/x0vncserver/x0vncserver.cxx
new file mode 100644
index 0000000..80483bf
--- /dev/null
+++ b/unix/x0vncserver/x0vncserver.cxx
@@ -0,0 +1,580 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright (C) 2004-2006 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.
+ */
+
+// FIXME: Check cases when screen width/height is not a multiply of 32.
+//        e.g. 800x600.
+
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <rfb/Logger_stdio.h>
+#include <rfb/LogWriter.h>
+#include <rfb/VNCServerST.h>
+#include <rfb/Configuration.h>
+#include <rfb/SSecurityFactoryStandard.h>
+#include <rfb/Timer.h>
+#include <network/TcpSocket.h>
+#include <tx/TXWindow.h>
+
+#include <vncconfig_unix/QueryConnectDialog.h>
+
+#include <signal.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#ifdef HAVE_XTEST
+#include <X11/extensions/XTest.h>
+#endif
+
+#include <x0vncserver/Geometry.h>
+#include <x0vncserver/Image.h>
+#include <x0vncserver/PollingManager.h>
+#include <x0vncserver/PollingScheduler.h>
+
+// XXX Lynx/OS 2.3: protos for select(), bzero()
+#ifdef Lynx
+#include <sys/proto.h>
+#endif
+
+using namespace rfb;
+using namespace network;
+
+static LogWriter vlog("Main");
+
+IntParameter pollingCycle("PollingCycle", "Milliseconds per one polling "
+                          "cycle; actual interval may be dynamically "
+                          "adjusted to satisfy MaxProcessorUsage setting", 30);
+IntParameter maxProcessorUsage("MaxProcessorUsage", "Maximum percentage of "
+                               "CPU time to be consumed", 35);
+BoolParameter useShm("UseSHM", "Use MIT-SHM extension if available", true);
+BoolParameter useOverlay("OverlayMode", "Use overlay mode under "
+                         "IRIX or Solaris", true);
+StringParameter displayname("display", "The X display", "");
+IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",5900);
+IntParameter queryConnectTimeout("QueryConnectTimeout",
+                                 "Number of seconds to show the Accept Connection dialog before "
+                                 "rejecting the connection",
+                                 10);
+StringParameter hostsFile("HostsFile", "File with IP access control rules", "");
+
+//
+// Allow the main loop terminate itself gracefully on receiving a signal.
+//
+
+static bool caughtSignal = false;
+
+static void CleanupSignalHandler(int sig)
+{
+  caughtSignal = true;
+}
+
+
+class QueryConnHandler : public VNCServerST::QueryConnectionHandler,
+                         public QueryResultCallback {
+public:
+  QueryConnHandler(Display* dpy, VNCServerST* vs)
+    : display(dpy), server(vs), queryConnectDialog(0), queryConnectSock(0) {}
+  ~QueryConnHandler() { delete queryConnectDialog; }
+
+  // -=- VNCServerST::QueryConnectionHandler interface
+  virtual VNCServerST::queryResult queryConnection(network::Socket* sock,
+                                                   const char* userName,
+                                                   char** reason) {
+    if (queryConnectSock) {
+      *reason = strDup("Another connection is currently being queried.");
+      return VNCServerST::REJECT;
+    }
+    if (!userName) userName = "(anonymous)";
+    queryConnectSock = sock;
+    CharArray address(sock->getPeerAddress());
+    delete queryConnectDialog;
+    queryConnectDialog = new QueryConnectDialog(display, address.buf,
+                                                userName, queryConnectTimeout,
+                                                this);
+    queryConnectDialog->map();
+    return VNCServerST::PENDING;
+  }
+
+  // -=- QueryResultCallback interface
+  virtual void queryApproved() {
+    server->approveConnection(queryConnectSock, true, 0);
+    queryConnectSock = 0;
+  }
+  virtual void queryRejected() {
+    server->approveConnection(queryConnectSock, false,
+                              "Connection rejected by local user");
+    queryConnectSock = 0;
+  }
+private:
+  Display* display;
+  VNCServerST* server;
+  QueryConnectDialog* queryConnectDialog;
+  network::Socket* queryConnectSock;
+};
+
+
+//
+// XPixelBuffer is a modification of FullFramePixelBuffer that does
+// not always return buffer width in getStride().
+//
+
+class XPixelBuffer : public FullFramePixelBuffer
+{
+public:
+  XPixelBuffer(const PixelFormat& pf, int width, int height,
+               rdr::U8* data_, ColourMap* cm, int stride_) :
+    FullFramePixelBuffer(pf, width, height, data_, cm), stride(stride_)
+  {
+  }
+
+  virtual int getStride() const { return stride; }
+
+protected:
+  int stride;
+};
+
+
+class XDesktop : public SDesktop, public ColourMap
+{
+public:
+  XDesktop(Display* dpy_, Geometry *geometry_)
+    : dpy(dpy_), geometry(geometry_), pb(0), server(0), image(0), pollmgr(0),
+      oldButtonMask(0), haveXtest(false), maxButtons(0), running(false)
+  {
+#ifdef HAVE_XTEST
+    int xtestEventBase;
+    int xtestErrorBase;
+    int major, minor;
+
+    if (XTestQueryExtension(dpy, &xtestEventBase,
+                            &xtestErrorBase, &major, &minor)) {
+      XTestGrabControl(dpy, True);
+      vlog.info("XTest extension present - version %d.%d",major,minor);
+      haveXtest = true;
+    } else {
+#endif
+      vlog.info("XTest extension not present");
+      vlog.info("Unable to inject events or display while server is grabbed");
+#ifdef HAVE_XTEST
+    }
+#endif
+
+  }
+  virtual ~XDesktop() {
+    stop();
+  }
+
+  // -=- SDesktop interface
+
+  virtual void start(VNCServer* vs) {
+
+    // Determine actual number of buttons of the X pointer device.
+    unsigned char btnMap[8];
+    int numButtons = XGetPointerMapping(dpy, btnMap, 8);
+    maxButtons = (numButtons > 8) ? 8 : numButtons;
+    vlog.info("Enabling %d button%s of X pointer device",
+              maxButtons, (maxButtons != 1) ? "s" : "");
+
+    // Create an image for maintaining framebuffer data.
+    ImageFactory factory((bool)useShm, (bool)useOverlay);
+    image = factory.newImage(dpy, geometry->width(), geometry->height());
+    vlog.info("Allocated %s", image->classDesc());
+
+    // Create polling manager object. It will track screen changes and
+    // keep pixels of the `image' object up to date.
+    pollmgr = new PollingManager(dpy, image, &factory,
+                                 geometry->offsetLeft(),
+                                 geometry->offsetTop());
+    pollmgr->setVNCServer(vs);
+
+    pf.bpp = image->xim->bits_per_pixel;
+    pf.depth = image->xim->depth;
+    pf.bigEndian = (image->xim->byte_order == MSBFirst);
+    pf.trueColour = image->isTrueColor();
+    pf.redShift   = ffs(image->xim->red_mask) - 1;
+    pf.greenShift = ffs(image->xim->green_mask) - 1;
+    pf.blueShift  = ffs(image->xim->blue_mask) - 1;
+    pf.redMax     = image->xim->red_mask   >> pf.redShift;
+    pf.greenMax   = image->xim->green_mask >> pf.greenShift;
+    pf.blueMax    = image->xim->blue_mask  >> pf.blueShift;
+
+    // Calculate the number of pixels in a row, with padding included.
+    int stride = image->xim->bytes_per_line * 8 / image->xim->bits_per_pixel;
+
+    // Provide pixel buffer to the server object.
+    pb = new XPixelBuffer(pf, geometry->width(), geometry->height(),
+                          (rdr::U8*)image->xim->data, this, stride);
+    server = vs;
+    server->setPixelBuffer(pb);
+
+    running = true;
+  }
+
+  virtual void stop() {
+    running = false;
+
+    delete pb;
+    delete pollmgr;
+    delete image;
+
+    pb = 0;
+    pollmgr = 0;
+    image = 0;
+  }
+
+  inline bool isRunning() {
+    return running;
+  }
+
+  inline void poll() {
+    if (pollmgr)
+      pollmgr->poll();
+  }
+
+  virtual void pointerEvent(const Point& pos, int buttonMask) {
+    pollmgr->setPointerPos(pos);
+#ifdef HAVE_XTEST
+    if (!haveXtest) return;
+    XTestFakeMotionEvent(dpy, DefaultScreen(dpy),
+                         geometry->offsetLeft() + pos.x,
+                         geometry->offsetTop() + pos.y,
+                         CurrentTime);
+    if (buttonMask != oldButtonMask) {
+      for (int i = 0; i < maxButtons; i++) {
+	if ((buttonMask ^ oldButtonMask) & (1<<i)) {
+          if (buttonMask & (1<<i)) {
+            XTestFakeButtonEvent(dpy, i+1, True, CurrentTime);
+          } else {
+            XTestFakeButtonEvent(dpy, i+1, False, CurrentTime);
+          }
+        }
+      }
+    }
+    oldButtonMask = buttonMask;
+#endif
+  }
+
+  virtual void keyEvent(rdr::U32 key, bool down) {
+#ifdef HAVE_XTEST
+    if (!haveXtest) return;
+    int keycode = XKeysymToKeycode(dpy, key);
+    if (keycode)
+      XTestFakeKeyEvent(dpy, keycode, down, CurrentTime);
+#endif
+  }
+
+  virtual void clientCutText(const char* str, int len) {
+  }
+
+  virtual Point getFbSize() {
+    return Point(pb->width(), pb->height());
+  }
+
+  // -=- ColourMap callbacks
+  virtual void lookup(int index, int* r, int* g, int* b) {
+    XColor xc;
+    xc.pixel = index;
+    if (index < DisplayCells(dpy,DefaultScreen(dpy))) {
+      XQueryColor(dpy, DefaultColormap(dpy,DefaultScreen(dpy)), &xc);
+    } else {
+      xc.red = xc.green = xc.blue = 0;
+    }
+    *r = xc.red;
+    *g = xc.green;
+    *b = xc.blue;
+  }
+
+protected:
+  Display* dpy;
+  Geometry* geometry;
+  PixelFormat pf;
+  PixelBuffer* pb;
+  VNCServer* server;
+  Image* image;
+  PollingManager* pollmgr;
+  int oldButtonMask;
+  bool haveXtest;
+  int maxButtons;
+  bool running;
+};
+
+
+class FileTcpFilter : public TcpFilter
+{
+
+public:
+
+  FileTcpFilter(const char *fname)
+    : TcpFilter("-"), fileName(NULL), lastModTime(0)
+  {
+    if (fname != NULL)
+      fileName = strdup((char *)fname);
+  }
+
+  virtual ~FileTcpFilter()
+  {
+    if (fileName != NULL)
+      free(fileName);
+  }
+
+  virtual bool verifyConnection(Socket* s)
+  {
+    if (!reloadRules()) {
+      vlog.error("Could not read IP filtering rules: rejecting all clients");
+      filter.clear();
+      filter.push_back(parsePattern("-"));
+      return false;
+    }
+
+    return TcpFilter::verifyConnection(s);
+  }
+
+protected:
+
+  bool reloadRules()
+  {
+    if (fileName == NULL)
+      return true;
+
+    struct stat st;
+    if (stat(fileName, &st) != 0)
+      return false;
+
+    if (st.st_mtime != lastModTime) {
+      // Actually reload only if the file was modified
+      FILE *fp = fopen(fileName, "r");
+      if (fp == NULL)
+        return false;
+
+      // Remove all the rules from the parent class
+      filter.clear();
+
+      // Parse the file contents adding rules to the parent class
+      char buf[32];
+      while (readLine(buf, 32, fp)) {
+        if (buf[0] && strchr("+-?", buf[0])) {
+          filter.push_back(parsePattern(buf));
+        }
+      }
+
+      fclose(fp);
+      lastModTime = st.st_mtime;
+    }
+    return true;
+  }
+
+protected:
+
+  char *fileName;
+  time_t lastModTime;
+
+private:
+
+  //
+  // NOTE: we silently truncate long lines in this function.
+  //
+
+  bool readLine(char *buf, int bufSize, FILE *fp)
+  {
+    if (fp == NULL || buf == NULL || bufSize == 0)
+      return false;
+
+    if (fgets(buf, bufSize, fp) == NULL)
+      return false;
+
+    char *ptr = strchr(buf, '\n');
+    if (ptr != NULL) {
+      *ptr = '\0';              // remove newline at the end
+    } else {
+      if (!feof(fp)) {
+        int c;
+        do {                    // skip the rest of a long line
+          c = getc(fp);
+        } while (c != '\n' && c != EOF);
+      }
+    }
+    return true;
+  }
+
+};
+
+char* programName;
+
+static void usage()
+{
+  fprintf(stderr, "\nusage: %s [<parameters>]\n", programName);
+  fprintf(stderr,"\n"
+          "Parameters can be turned on with -<param> or off with -<param>=0\n"
+          "Parameters which take a value can be specified as "
+          "-<param> <value>\n"
+          "Other valid forms are <param>=<value> -<param>=<value> "
+          "--<param>=<value>\n"
+          "Parameter names are case-insensitive.  The parameters are:\n\n");
+  Configuration::listParams(79, 14);
+  exit(1);
+}
+
+int main(int argc, char** argv)
+{
+  initStdIOLoggers();
+  LogWriter::setLogParams("*:stderr:30");
+
+  programName = argv[0];
+  Display* dpy;
+
+  for (int i = 1; i < argc; i++) {
+    if (Configuration::setParam(argv[i]))
+      continue;
+
+    if (argv[i][0] == '-') {
+      if (i+1 < argc) {
+        if (Configuration::setParam(&argv[i][1], argv[i+1])) {
+          i++;
+          continue;
+        }
+      }
+      usage();
+    }
+
+    usage();
+  }
+
+  CharArray dpyStr(displayname.getData());
+  if (!(dpy = XOpenDisplay(dpyStr.buf[0] ? dpyStr.buf : 0))) {
+    fprintf(stderr,"%s: unable to open display \"%s\"\r\n",
+            programName, XDisplayName(displayname.getData()));
+    exit(1);
+  }
+
+  signal(SIGHUP, CleanupSignalHandler);
+  signal(SIGINT, CleanupSignalHandler);
+  signal(SIGTERM, CleanupSignalHandler);
+
+  try {
+    TXWindow::init(dpy,"x0vncserver");
+    Geometry geo(DisplayWidth(dpy, DefaultScreen(dpy)),
+                 DisplayHeight(dpy, DefaultScreen(dpy)));
+    XDesktop desktop(dpy, &geo);
+    VNCServerST server("x0vncserver", &desktop);
+    QueryConnHandler qcHandler(dpy, &server);
+    server.setQueryConnectionHandler(&qcHandler);
+
+    TcpListener listener((int)rfbport);
+    vlog.info("Listening on port %d", (int)rfbport);
+
+    FileTcpFilter fileTcpFilter(hostsFile.getData());
+    if (strlen(hostsFile.getData()) != 0)
+      listener.setFilter(&fileTcpFilter);
+
+    PollingScheduler sched((int)pollingCycle, (int)maxProcessorUsage);
+
+    while (!caughtSignal) {
+      struct timeval tv;
+      fd_set rfds;
+      std::list<Socket*> sockets;
+      std::list<Socket*>::iterator i;
+
+      // Process any incoming X events
+      TXWindow::handleXEvents(dpy);
+
+      FD_ZERO(&rfds);
+      FD_SET(listener.getFd(), &rfds);
+      server.getSockets(&sockets);
+      int clients_connected = 0;
+      for (i = sockets.begin(); i != sockets.end(); i++) {
+        if ((*i)->isShutdown()) {
+          server.removeSocket(*i);
+          delete (*i);
+        } else {
+          FD_SET((*i)->getFd(), &rfds);
+          clients_connected++;
+        }
+      }
+
+      if (clients_connected) {
+        int wait_ms = sched.millisRemaining();
+        if (wait_ms > 500) {
+          wait_ms = 500;
+        }
+        tv.tv_usec = wait_ms * 1000;
+#ifdef DEBUG
+        // fprintf(stderr, "[%d]\t", wait_ms);
+#endif
+      } else {
+        sched.reset();
+        tv.tv_usec = 100000;
+      }
+      tv.tv_sec = 0;
+
+      // Do the wait...
+      sched.sleepStarted();
+      int n = select(FD_SETSIZE, &rfds, 0, 0, &tv);
+      sched.sleepFinished();
+
+      if (n < 0) {
+        if (errno == EINTR) {
+          vlog.debug("Interrupted select() system call");
+          continue;
+        } else {
+          throw rdr::SystemException("select", errno);
+        }
+      }
+
+      // Accept new VNC connections
+      if (FD_ISSET(listener.getFd(), &rfds)) {
+        Socket* sock = listener.accept();
+        if (sock) {
+          server.addSocket(sock);
+        } else {
+          vlog.status("Client connection rejected");
+        }
+      }
+
+      Timer::checkTimeouts();
+      server.checkTimeouts();
+
+      // Client list could have been changed.
+      server.getSockets(&sockets);
+
+      // Nothing more to do if there are no client connections.
+      if (sockets.empty())
+        continue;
+
+      // Process events on existing VNC connections
+      for (i = sockets.begin(); i != sockets.end(); i++) {
+        if (FD_ISSET((*i)->getFd(), &rfds))
+          server.processSocketEvent(*i);
+      }
+
+      if (desktop.isRunning() && sched.goodTimeToPoll()) {
+        sched.newPass();
+        desktop.poll();
+      }
+    }
+
+  } catch (rdr::Exception &e) {
+    vlog.error(e.str());
+    return 1;
+  }
+
+  vlog.info("Terminated");
+  return 0;
+}
diff --git a/unix/x0vncserver/x0vncserver.man b/unix/x0vncserver/x0vncserver.man
new file mode 100644
index 0000000..da9ba94
--- /dev/null
+++ b/unix/x0vncserver/x0vncserver.man
@@ -0,0 +1,33 @@
+.TH x0vncserver 1 "17 Apr 2006" "TightVNC" "Virtual Network Computing"
+.SH NAME
+x0vncserver \- VNC server which continuously polls an X display
+.SH SYNOPSIS
+.B x0vncserver
+[\fIparameters\fP]
+.SH DESCRIPTION
+.B x0vncserver
+is a VNC server which continuously polls any X display, allowing it to be
+controlled via VNC.  How usable it will be depends a lot on the machine it's
+running on, and what you're expecting.  It won't be as fast as Xvnc or a native
+X server with VNC support compiled in, but in many cases it is the best option
+since it is just an ordinary X application requiring no special installation.
+
+It has many of the same parameters as Xvnc.  Running \fBx0vncserver -h\fP will
+give a list of parameters with descriptions.  Note that you need to explicitly
+specify an appropriate password file using the PasswordFile parameter.
+
+.SH SEE ALSO
+.BR Xvnc (1)
+.BR vncpasswd (1),
+.BR vncviewer (1),
+.BR vncserver (1),
+.br
+http://www.tightvnc.com
+
+.SH AUTHOR
+Tristan Richardson, RealVNC Ltd.
+
+VNC was originally developed by the RealVNC team while at Olivetti
+Research Ltd / AT&T Laboratories Cambridge.  TightVNC additions was
+implemented by Constantin Kaplinsky. Many other people participated in
+development, testing and support.