123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376 |
- /* Copyright (C) 2004-2008 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
- //
-
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
-
- #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>
-
- using namespace rfb;
-
- static LogWriter vlog("PollingMgr");
-
- 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, const Image *image,
- ImageFactory &factory,
- int offsetLeft, int offsetTop)
- : m_dpy(dpy),
- m_image(image),
- m_bytesPerPixel(image->xim->bits_per_pixel / 8),
- m_offsetLeft(offsetLeft),
- m_offsetTop(offsetTop),
- m_width(image->xim->width),
- m_height(image->xim->height),
- m_widthTiles((image->xim->width + 31) / 32),
- m_heightTiles((image->xim->height + 31) / 32),
- m_numTiles(((image->xim->width + 31) / 32) *
- ((image->xim->height + 31) / 32)),
- m_pollingStep(0)
- {
- // Create additional images used in polling algorithm, 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_columnImage = factory.newImage(m_dpy, 1, m_height);
- const char *primaryImgClass = m_image->className();
- const char *rowImgClass = m_rowImage->className();
- const char *columnImgClass = m_columnImage->className();
- if (strcmp(rowImgClass, primaryImgClass) != 0 ||
- strcmp(columnImgClass, primaryImgClass) != 0) {
- vlog.error("Image types do not match (%s, %s, %s)",
- primaryImgClass, rowImgClass, columnImgClass);
- }
-
- m_changeFlags = new bool[m_numTiles];
- memset(m_changeFlags, 0, m_numTiles * sizeof(bool));
- }
-
- PollingManager::~PollingManager()
- {
- delete[] m_changeFlags;
-
- delete m_rowImage;
- delete m_columnImage;
- }
-
- //
- // 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(VNCServer *server)
- {
- #ifdef DEBUG
- debugBeforePoll();
- #endif
-
- pollScreen(server);
-
- #ifdef DEBUG
- debugAfterPoll();
- #endif
- }
-
- #ifdef DEBUG_REPORT_CHANGED_TILES
- #define DBG_REPORT_CHANGES(title) printChanges((title))
- #else
- #define DBG_REPORT_CHANGES(title)
- #endif
-
- bool PollingManager::pollScreen(VNCServer *server)
- {
- if (!server)
- return false;
-
- // Clear the m_changeFlags[] array, indicating that no changes have
- // been detected yet.
- memset(m_changeFlags, 0, m_numTiles * sizeof(bool));
-
- // First pass over the framebuffer. Here we scan 1/32 part of the
- // framebuffer -- that is, one line in each (32 * m_width) stripe.
- // We compare the pixels of that line with previous framebuffer
- // contents and raise corresponding elements of m_changeFlags[].
- int scanOffset = m_pollingOrder[m_pollingStep++ % 32];
- int nTilesChanged = 0;
- for (int y = scanOffset; y < m_height; y += 32) {
- nTilesChanged += checkRow(0, y, m_width);
- }
-
- DBG_REPORT_CHANGES("After 1st pass");
-
- // If some changes have been detected:
- if (nTilesChanged) {
- // Try to find more changes around.
- checkNeighbors();
- DBG_REPORT_CHANGES("After checking neighbors");
- // Inform the server about the changes.
- nTilesChanged = sendChanges(server);
- }
-
- #ifdef DEBUG_PRINT_NUM_CHANGED_TILES
- printf("%3d ", nTilesChanged);
- if (m_pollingStep % 32 == 0) {
- printf("\n");
- }
- #endif
-
- #ifdef DEBUG
- if (nTilesChanged != 0) {
- fprintf(stderr, "#%d# ", nTilesChanged);
- }
- #endif
-
- return (nTilesChanged != 0);
- }
-
- int PollingManager::checkRow(int x, int y, int w)
- {
- // If necessary, expand the row to the left, to the tile border.
- // In other words, x must be a multiple of 32.
- if (x % 32 != 0) {
- int correction = x % 32;
- x -= correction;
- w += correction;
- }
-
- // Read a row from the screen into m_rowImage.
- getRow(x, y, w);
-
- // Compute a pointer to the initial element of m_changeFlags.
- bool *pChangeFlags = &m_changeFlags[getTileIndex(x, y)];
-
- // Compute pointers to image data to be compared.
- char *ptr_old = m_image->locatePixel(x, y);
- char *ptr_new = m_rowImage->xim->data;
-
- // Compare pixels, raise corresponding elements of m_changeFlags[].
- // First, handle full-size (32 pixels wide) tiles.
- int nTilesChanged = 0;
- int nBytesPerTile = 32 * m_bytesPerPixel;
- for (int i = 0; i < w / 32; i++) {
- if (memcmp(ptr_old, ptr_new, nBytesPerTile)) {
- *pChangeFlags = true;
- nTilesChanged++;
- }
- pChangeFlags++;
- ptr_old += nBytesPerTile;
- ptr_new += nBytesPerTile;
- }
-
- // Handle the rightmost pixels, if the width is not a multiple of 32.
- int nBytesLeft = (w % 32) * m_bytesPerPixel;
- if (nBytesLeft != 0) {
- if (memcmp(ptr_old, ptr_new, nBytesLeft)) {
- *pChangeFlags = true;
- nTilesChanged++;
- }
- }
-
- return nTilesChanged;
- }
-
- int PollingManager::checkColumn(int x, int y, int h, bool *pChangeFlags)
- {
- getColumn(x, y, h);
-
- int nTilesChanged = 0;
- for (int nTile = 0; nTile < (h + 31) / 32; nTile++) {
- if (!*pChangeFlags) {
- int tile_h = (h - nTile * 32 >= 32) ? 32 : h - nTile * 32;
- for (int i = 0; i < tile_h; i++) {
- // FIXME: Do not compute these pointers in the inner cycle.
- char *ptr_old = m_image->locatePixel(x, y + nTile * 32 + i);
- char *ptr_new = m_columnImage->locatePixel(0, nTile * 32 + i);
- if (memcmp(ptr_old, ptr_new, m_bytesPerPixel)) {
- *pChangeFlags = true;
- nTilesChanged++;
- break;
- }
- }
- }
- pChangeFlags += m_widthTiles;
- }
-
- return nTilesChanged;
- }
-
- int PollingManager::sendChanges(VNCServer *server) const
- {
- const bool *pChangeFlags = m_changeFlags;
- int nTilesChanged = 0;
-
- Rect rect;
- for (int y = 0; y < m_heightTiles; y++) {
- for (int x = 0; x < m_widthTiles; x++) {
- if (*pChangeFlags++) {
- // Count successive tiles marked as changed.
- int count = 1;
- while (x + count < m_widthTiles && *pChangeFlags++) {
- count++;
- }
- nTilesChanged += count;
- // Compute the coordinates and the size of this band.
- rect.setXYWH(x * 32, y * 32, count * 32, 32);
- if (rect.br.x > m_width)
- rect.br.x = m_width;
- if (rect.br.y > m_height)
- rect.br.y = m_height;
- // Add to the changed region maintained by the server.
- server->add_changed(rect);
- // Skip processed tiles.
- x += count;
- }
- }
- }
- return nTilesChanged;
- }
-
- void
- PollingManager::checkNeighbors()
- {
- int x, y;
-
- // Check neighboring pixels above and below changed tiles.
- // FIXME: Fast skip to the first changed tile (and to the last, too).
- // FIXME: Check the full-width line above the first changed tile?
- for (y = 0; y < m_heightTiles; y++) {
- bool doneAbove = false;
- bool doneBelow = false;
- for (x = 0; x < m_widthTiles; x++) {
- if (!doneAbove && y > 0 &&
- m_changeFlags[y * m_widthTiles + x] &&
- !m_changeFlags[(y - 1) * m_widthTiles + x]) {
- // FIXME: Check m_changeFlags[] to decrease height of the row.
- checkRow(x * 32, y * 32 - 1, m_width - x * 32);
- doneAbove = true;
- }
- if (!doneBelow && y < m_heightTiles - 1 &&
- m_changeFlags[y * m_widthTiles + x] &&
- !m_changeFlags[(y + 1) * m_widthTiles + x]) {
- // FIXME: Check m_changeFlags[] to decrease height of the row.
- checkRow(x * 32, (y + 1) * 32, m_width - x * 32);
- doneBelow = true;
- }
- if (doneBelow && doneAbove)
- break;
- }
- }
-
- // Check neighboring pixels at the right side of changed tiles.
- for (x = 0; x < m_widthTiles - 1; x++) {
- for (y = 0; y < m_heightTiles; y++) {
- if (m_changeFlags[y * m_widthTiles + x] &&
- !m_changeFlags[y * m_widthTiles + x + 1]) {
- // FIXME: Check m_changeFlags[] to decrease height of the column.
- checkColumn((x + 1) * 32, y * 32, m_height - y * 32,
- &m_changeFlags[y * m_widthTiles + x + 1]);
- break;
- }
- }
- }
-
- // Check neighboring pixels at the left side of changed tiles.
- for (x = m_widthTiles - 1; x > 0; x--) {
- for (y = 0; y < m_heightTiles; y++) {
- if (m_changeFlags[y * m_widthTiles + x] &&
- !m_changeFlags[y * m_widthTiles + x - 1]) {
- // FIXME: Check m_changeFlags[] to decrease height of the column.
- checkColumn(x * 32 - 1, y * 32, m_height - y * 32,
- &m_changeFlags[y * m_widthTiles + x - 1]);
- break;
- }
- }
- }
- }
-
- void
- PollingManager::printChanges(const char *header) const
- {
- fprintf(stderr, "%s:", header);
-
- const bool *pChangeFlags = m_changeFlags;
-
- for (int y = 0; y < m_heightTiles; y++) {
- for (int x = 0; x < m_widthTiles; x++) {
- if (*pChangeFlags++) {
- // Count successive tiles marked as changed.
- int count = 1;
- while (x + count < m_widthTiles && *pChangeFlags++) {
- count++;
- }
- // Print.
- fprintf(stderr, " (%d,%d)*%d", x, y, count);
- // Skip processed tiles.
- x += count;
- }
- }
- }
-
- fprintf(stderr, "\n");
- }
|