summaryrefslogtreecommitdiffstats
path: root/unix/x0vncserver
diff options
context:
space:
mode:
authorConstantin Kaplinsky <const@tightvnc.com>2006-05-25 05:04:46 +0000
committerConstantin Kaplinsky <const@tightvnc.com>2006-05-25 05:04:46 +0000
commitb30ae7facbdf8273f34f5d67d3d2e9c81db75576 (patch)
tree5091b0a7b991672b19c17b86b263e5ff4f173a3e /unix/x0vncserver
parenta2adc8d4cfdf7336ce9192414c5e775224742a97 (diff)
downloadtigervnc-b30ae7facbdf8273f34f5d67d3d2e9c81db75576.tar.gz
tigervnc-b30ae7facbdf8273f34f5d67d3d2e9c81db75576.zip
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
Diffstat (limited to 'unix/x0vncserver')
-rw-r--r--unix/x0vncserver/Geometry.cxx71
-rw-r--r--unix/x0vncserver/Geometry.h50
-rw-r--r--unix/x0vncserver/Image.cxx549
-rw-r--r--unix/x0vncserver/Image.h223
-rw-r--r--unix/x0vncserver/Makefile.in30
-rw-r--r--unix/x0vncserver/PollingManager.cxx600
-rw-r--r--unix/x0vncserver/PollingManager.h148
-rw-r--r--unix/x0vncserver/PollingScheduler.cxx211
-rw-r--r--unix/x0vncserver/PollingScheduler.h98
-rw-r--r--unix/x0vncserver/TimeMillis.cxx49
-rw-r--r--unix/x0vncserver/TimeMillis.h47
-rw-r--r--unix/x0vncserver/buildtime.c18
-rw-r--r--unix/x0vncserver/x0vncserver.cxx580
-rw-r--r--unix/x0vncserver/x0vncserver.man33
14 files changed, 2707 insertions, 0 deletions
diff --git a/unix/x0vncserver/Geometry.cxx b/unix/x0vncserver/Geometry.cxx
new file mode 100644
index 00000000..ccb4e699
--- /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 00000000..95059e7d
--- /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 00000000..fc66c5a9
--- /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 00000000..535cee6a
--- /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 00000000..cbb9fed6
--- /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 00000000..a823ed42
--- /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 00000000..b8eef509
--- /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 00000000..c9d8d602
--- /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 00000000..2e3e5be7
--- /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 00000000..059c043b
--- /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 00000000..e79db12c
--- /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 00000000..a96031cc
--- /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 00000000..80483bf8
--- /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 00000000..da9ba944
--- /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.