XGetSubImage(dpy, wnd, x, y, w, h, AllPlanes, ZPixmap, xim, 0, 0);
}
-void Image::updateRect(Image *src, int dst_x, int dst_y)
+//
+// 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)
{
- updateRect(src->xim, dst_x, dst_y);
+ 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
+ // Limit width and height at destination image size.
int w = src->width;
if (dst_x + w > xim->width)
w = xim->width - dst_x;
if (dst_y + h > xim->height)
h = xim->height - dst_y;
- // Copy pixels
- const char *srcOffset = src->data;
- char *dstOffset = xim->data + (dst_y * xim->bytes_per_line +
- dst_x * (xim->bits_per_pixel / 8));
- int lineLength = w * (xim->bits_per_pixel / 8);
- for (int i = 0; i < h ; i++) {
- memcpy(dstOffset, srcOffset, lineLength);
- srcOffset += src->bytes_per_line;
- dstOffset += xim->bytes_per_line;
- }
+ 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
//
// 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)
Image *image = NULL;
// First, try to create an image with overlay support.
+ // FIXME: Replace fprintf() with proper logging.
#ifdef HAVE_OVERLAY_EXT
if (mayUseOverlay) {
virtual void get(Window wnd, int x = 0, int y = 0);
virtual void get(Window wnd, int x, int y, int w, int h);
- virtual void updateRect(Image *src, int dst_x = 0, int dst_y = 0);
+// 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()
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;
// PollingManager.cxx
//
+// FIXME: Don't compare pixels already marked as changed.
+// FIXME: Use Image::copyPixels() instead of Image::updateRect()?
+// In that case, note the fact that arguments are not checked.
+
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#include <rfb/VNCServer.h>
#include <rfb/Configuration.h>
+#include <rfb/ServerCore.h>
#include <x0vncserver/Image.h>
#include <x0vncserver/PollingManager.h>
-IntParameter pollingType("PollingType", "Polling algorithm to use (0..3)", 3);
+BoolParameter PollingManager::pollPointer
+("PollPointer",
+ "Poll area under the pointer with higher priority",
+ true);
+
+IntParameter PollingManager::pollingType
+("PollingType",
+ "Polling algorithm to use (0..3)",
+ 3);
const int PollingManager::m_pollingOrder[32] = {
0, 16, 8, 24, 4, 20, 12, 28,
PollingManager::PollingManager(Display *dpy, Image *image,
ImageFactory *factory)
- : m_dpy(dpy), m_server(0), m_image(image), m_pollingStep(0)
+ : m_dpy(dpy), m_server(0), m_image(image), m_pointerPosKnown(false),
+ m_pollingStep(0)
{
// Save width and height of the screen (and the image).
m_width = m_image->xim->width;
m_widthTiles = (m_width + 31) / 32;
m_heightTiles = (m_height + 31) / 32;
- // Create two additional images used in the polling algorithm.
+ // Create additional images used in the polling algorithm.
// FIXME: verify that these images use the same pixel format as in m_image.
m_rowImage = factory->newImage(m_dpy, m_width, 1);
m_tileImage = factory->newImage(m_dpy, 32, 32);
+ m_areaImage = factory->newImage(m_dpy, 128, 128);
// FIXME: Extend the comment.
// Create a matrix with one byte per each 32x32 tile. It will be
delete[] m_rateMatrix;
delete[] m_statusMatrix;
+
+ delete m_areaImage;
+ delete m_tileImage;
+ delete m_rowImage;
}
//
m_server = s;
}
+//
+// Update current pointer position which may be used as a hint for
+// polling algorithms.
+//
+
+void PollingManager::setPointerPos(const Point &pos)
+{
+ m_pointerPos = pos;
+ m_pointerPosKnown = true;
+}
+
+//
+// Indicate that current pointer position is unknown.
+// FIXME: Perhaps this should be done automatically after a number of
+// polling cycles if the cursor position have not been changed?
+//
+
+void PollingManager::unsetPointerPos()
+{
+ m_pointerPosKnown = false;
+}
+
//
// DEBUG: a version of poll() measuring time spent in the function.
//
bool grandStep = (m_pollingStep % GRAND_STEP_DIVISOR == 0);
- // FIXME: Save shortcuts in member variables.
+ // FIXME: Save shortcuts in member variables?
int scanLine = m_pollingOrder[m_pollingStep++ % 32];
int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
int bytesPerLine = m_image->xim->bytes_per_line;
if (grandStep)
adjustVideoArea();
+ // FIXME: Exclude area near the pointer from the comparisons above.
+ // FIXME: Code duplication.
+ if (pollPointer && m_pointerPosKnown && pollPointerArea())
+ nTilesChanged++;
+
return (nTilesChanged != 0);
}
}
}
+ // FIXME: Exclude area near the pointer from the comparisons above.
+ // FIXME: Code duplication.
+ if (pollPointer && m_pointerPosKnown && pollPointerArea())
+ nTilesChanged++;
+
return (nTilesChanged != 0);
}
}
}
+ // FIXME: Exclude area near the pointer from the comparisons above.
+ // FIXME: Code duplication.
+ if (pollPointer && m_pointerPosKnown && pollPointerArea())
+ nTilesChanged++;
+
return (nTilesChanged != 0);
}
Rect rect(0, 0, m_width, m_height);
m_server->add_changed(rect);
- // As we have no idea if any pixels were changed,
- // always report that some changes have been detected.
+ // Report that some changes have been detected.
+ return true;
+}
+
+//
+// Compute coordinates of the rectangle around the pointer.
+//
+
+void PollingManager::computePointerArea(Rect *r)
+{
+ int x = m_pointerPos.x - 64;
+ int y = m_pointerPos.y - 64;
+ int w = 128;
+ int h = 128;
+ if (x < 0) {
+ w += x; x = 0;
+ }
+ if (x + w > m_width) {
+ w = m_width - x;
+ }
+ if (y < 0) {
+ h += y; y = 0;
+ }
+ if (y + h > m_height) {
+ h = m_height - y;
+ }
+
+ r->setXYWH(x, y, w, h);
+}
+
+//
+// Poll the area under current pointer position. Each pixel of the
+// area should be compared. Using such polling option gives higher
+// priority to screen area under the pointer.
+//
+// ASSUMES: (m_server != NULL && m_pointerPosKnown != false)
+//
+
+bool PollingManager::pollPointerArea()
+{
+ Rect r;
+ computePointerArea(&r);
+
+ // Shortcuts for coordinates.
+ int x = r.tl.x, y = r.tl.y;
+ int w = r.width(), h = r.height();
+
+ // Get new pixels.
+ if (w == 128 && h == 128) {
+ m_areaImage->get(DefaultRootWindow(m_dpy), x, y);
+ } else {
+ m_areaImage->get(DefaultRootWindow(m_dpy), x, y, w, h);
+ }
+
+ // Now, try to minimize the rectangle by cutting out unchanged
+ // borders (at top and bottom).
+ //
+ // FIXME: Perhaps we should work on 32x32 tiles (properly aligned)
+ // to produce a region instead of a rectangle. If there would
+ // be just one universal polling algorithm, it could be
+ // better to integrate pointer area polling into that
+ // algorithm, instead of a separate pollPointerArea()
+ // function.
+
+ // Shortcuts.
+ int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
+ int oldBytesPerLine = m_image->xim->bytes_per_line;
+ int newBytesPerLine = m_areaImage->xim->bytes_per_line;
+ char *oldPtr = m_image->xim->data + y * oldBytesPerLine + x * bytesPerPixel;
+ char *newPtr = m_areaImage->xim->data;
+
+ // Check and cut out unchanged rows at the top.
+ int ty;
+ for (ty = 0; ty < h; ty++) {
+ if (memcmp(oldPtr, newPtr, w * bytesPerPixel) != 0)
+ break;
+ oldPtr += oldBytesPerLine;
+ newPtr += newBytesPerLine;
+ }
+ if (ty == h) {
+ return false; // no changes at all
+ }
+ y += ty; h -= ty;
+
+ // Check and cut out unchanged rows at the bottom.
+ oldPtr = m_image->xim->data + (y+h-1) * oldBytesPerLine + x * bytesPerPixel;
+ newPtr = m_areaImage->xim->data + (ty+h-1) * newBytesPerLine;
+ int by;
+ for (by = 0; by < h - 1; by++) {
+ if (memcmp(oldPtr, newPtr, w * bytesPerPixel) != 0)
+ break;
+ oldPtr -= oldBytesPerLine;
+ newPtr -= newBytesPerLine;
+ }
+ h -= by;
+
+ // Copy pixels.
+ m_image->updateRect(m_areaImage, x, y, 0, ty, w, h);
+
+ // Report updates to the server.
+ Rect rect(x, y, x+w, y+h);
+ m_server->add_changed(rect);
return true;
}
//
-// FIXME: Replace with a normal comment.
// Make video area pattern more regular.
//
+// FIXME: Replace the above with a normal comment.
+// FIXME: Is the function efficient enough?
+//
-// FIXME: Is it efficient enough?
void PollingManager::adjustVideoArea()
{
char newFlags[m_widthTiles * m_heightTiles];
virtual ~PollingManager();
void setVNCServer(VNCServer *s);
+
+ void setPointerPos(const Point &pos);
+ void unsetPointerPos();
+
void pollDebug();
void poll();
+ // Configurable parameters.
+ static BoolParameter pollPointer;
+ static IntParameter pollingType;
+
protected:
//
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;
int m_widthTiles;
int m_heightTiles;
+ // Tracking pointer position for polling improvements.
+ bool m_pointerPosKnown;
+ Point m_pointerPos;
+
private:
void adjustVideoArea();
- Image *m_rowImage;
- Image *m_tileImage;
+ // 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;
// -=- SDesktop interface
virtual void pointerEvent(const Point& pos, rdr::U8 buttonMask) {
+ pollmgr->setPointerPos(pos);
#ifdef HAVE_XTEST
if (!haveXtest) return;
XTestFakeMotionEvent(dpy, DefaultScreen(dpy), pos.x, pos.y, CurrentTime);