From ce676c671ae9904723fc4cad90aca9749e99586f Mon Sep 17 00:00:00 2001 From: Constantin Kaplinsky Date: Wed, 8 Feb 2006 13:36:58 +0000 Subject: [PATCH] Polling screen area around current pointer position with higher priority. Also, there is a number of code improvements, and some new "FIXME" comments for potential future improvements. git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@477 3789f03b-4d11-0410-bbf8-ca57d06f2519 --- x0vncserver/Image.cxx | 91 ++++++++++++++--- x0vncserver/Image.h | 15 ++- x0vncserver/PollingManager.cxx | 173 +++++++++++++++++++++++++++++++-- x0vncserver/PollingManager.h | 22 ++++- x0vncserver/x0vncserver.cxx | 1 + 5 files changed, 278 insertions(+), 24 deletions(-) diff --git a/x0vncserver/Image.cxx b/x0vncserver/Image.cxx index b310619d..36b2de44 100644 --- a/x0vncserver/Image.cxx +++ b/x0vncserver/Image.cxx @@ -106,14 +106,38 @@ 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); } -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; @@ -121,16 +145,54 @@ void Image::updateRect(XImage *src, int dst_x, int dst_y) 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 @@ -433,6 +495,8 @@ void SolarisOverlayImage::get(Window wnd, int x, int y, int w, int h) // // 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) @@ -456,6 +520,7 @@ Image *ImageFactory::newImage(Display *d, int width, int height) Image *image = NULL; // First, try to create an image with overlay support. + // FIXME: Replace fprintf() with proper logging. #ifdef HAVE_OVERLAY_EXT if (mayUseOverlay) { diff --git a/x0vncserver/Image.h b/x0vncserver/Image.h index 032e24a8..e3de17d0 100644 --- a/x0vncserver/Image.h +++ b/x0vncserver/Image.h @@ -43,8 +43,15 @@ public: 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() @@ -55,6 +62,12 @@ 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; diff --git a/x0vncserver/PollingManager.cxx b/x0vncserver/PollingManager.cxx index 9491d766..00580e01 100644 --- a/x0vncserver/PollingManager.cxx +++ b/x0vncserver/PollingManager.cxx @@ -19,17 +19,30 @@ // PollingManager.cxx // +// FIXME: Don't compare pixels already marked as changed. +// FIXME: Use Image::copyPixels() instead of Image::updateRect()? +// In that case, note the fact that arguments are not checked. + #include #include #include #include #include #include +#include #include #include -IntParameter pollingType("PollingType", "Polling algorithm to use (0..3)", 3); +BoolParameter PollingManager::pollPointer +("PollPointer", + "Poll area under the pointer with higher priority", + true); + +IntParameter PollingManager::pollingType +("PollingType", + "Polling algorithm to use (0..3)", + 3); const int PollingManager::m_pollingOrder[32] = { 0, 16, 8, 24, 4, 20, 12, 28, @@ -47,7 +60,8 @@ const int PollingManager::m_pollingOrder[32] = { PollingManager::PollingManager(Display *dpy, Image *image, ImageFactory *factory) - : m_dpy(dpy), m_server(0), m_image(image), m_pollingStep(0) + : m_dpy(dpy), m_server(0), m_image(image), m_pointerPosKnown(false), + m_pollingStep(0) { // Save width and height of the screen (and the image). m_width = m_image->xim->width; @@ -57,10 +71,11 @@ PollingManager::PollingManager(Display *dpy, Image *image, m_widthTiles = (m_width + 31) / 32; m_heightTiles = (m_height + 31) / 32; - // Create two additional images used in the polling algorithm. + // Create additional images used in the polling algorithm. // FIXME: verify that these images use the same pixel format as in m_image. m_rowImage = factory->newImage(m_dpy, m_width, 1); m_tileImage = factory->newImage(m_dpy, 32, 32); + m_areaImage = factory->newImage(m_dpy, 128, 128); // FIXME: Extend the comment. // Create a matrix with one byte per each 32x32 tile. It will be @@ -89,6 +104,10 @@ PollingManager::~PollingManager() delete[] m_rateMatrix; delete[] m_statusMatrix; + + delete m_areaImage; + delete m_tileImage; + delete m_rowImage; } // @@ -100,6 +119,28 @@ 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_pointerPos = pos; + m_pointerPosKnown = true; +} + +// +// Indicate that current pointer position is unknown. +// FIXME: Perhaps this should be done automatically after a number of +// polling cycles if the cursor position have not been changed? +// + +void PollingManager::unsetPointerPos() +{ + m_pointerPosKnown = false; +} + // // DEBUG: a version of poll() measuring time spent in the function. // @@ -160,7 +201,7 @@ bool PollingManager::poll_DetectVideo() bool grandStep = (m_pollingStep % GRAND_STEP_DIVISOR == 0); - // FIXME: Save shortcuts in member variables. + // FIXME: Save shortcuts in member variables? int scanLine = m_pollingOrder[m_pollingStep++ % 32]; int bytesPerPixel = m_image->xim->bits_per_pixel / 8; int bytesPerLine = m_image->xim->bytes_per_line; @@ -217,6 +258,11 @@ bool PollingManager::poll_DetectVideo() if (grandStep) adjustVideoArea(); + // FIXME: Exclude area near the pointer from the comparisons above. + // FIXME: Code duplication. + if (pollPointer && m_pointerPosKnown && pollPointerArea()) + nTilesChanged++; + return (nTilesChanged != 0); } @@ -281,6 +327,11 @@ bool PollingManager::poll_SkipCycles() } } + // FIXME: Exclude area near the pointer from the comparisons above. + // FIXME: Code duplication. + if (pollPointer && m_pointerPosKnown && pollPointerArea()) + nTilesChanged++; + return (nTilesChanged != 0); } @@ -323,6 +374,11 @@ bool PollingManager::poll_Traditional() } } + // FIXME: Exclude area near the pointer from the comparisons above. + // FIXME: Code duplication. + if (pollPointer && m_pointerPosKnown && pollPointerArea()) + nTilesChanged++; + return (nTilesChanged != 0); } @@ -339,17 +395,118 @@ bool PollingManager::poll_Dumb() 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]; diff --git a/x0vncserver/PollingManager.h b/x0vncserver/PollingManager.h index 8f7f820a..a35fafee 100644 --- a/x0vncserver/PollingManager.h +++ b/x0vncserver/PollingManager.h @@ -38,9 +38,17 @@ public: 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: // @@ -52,6 +60,10 @@ 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; @@ -61,12 +73,18 @@ protected: 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; diff --git a/x0vncserver/x0vncserver.cxx b/x0vncserver/x0vncserver.cxx index f799de3a..f718164f 100644 --- a/x0vncserver/x0vncserver.cxx +++ b/x0vncserver/x0vncserver.cxx @@ -146,6 +146,7 @@ public: // -=- 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); -- 2.39.5