blob: bcc9547c02987051184d58726bc3bec13fda7e63 [file] [log] [blame]
Constantin Kaplinsky82f7b012008-05-30 10:03:30 +00001/* Copyright (C) 2004-2008 Constantin Kaplinsky. All Rights Reserved.
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +00002 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
Constantin Kaplinsky82f7b012008-05-30 10:03:30 +000018
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000019//
20// PollingManager.cxx
21//
22
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000023#include <stdio.h>
24#include <string.h>
25#include <time.h>
26#include <X11/Xlib.h>
27#include <rfb/LogWriter.h>
28#include <rfb/VNCServer.h>
29#include <rfb/Configuration.h>
30#include <rfb/ServerCore.h>
31
32#include <x0vncserver/PollingManager.h>
33
34static LogWriter vlog("PollingMgr");
35
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000036const int PollingManager::m_pollingOrder[32] = {
37 0, 16, 8, 24, 4, 20, 12, 28,
38 10, 26, 18, 2, 22, 6, 30, 14,
39 1, 17, 9, 25, 7, 23, 15, 31,
40 19, 3, 27, 11, 29, 13, 5, 21
41};
42
43//
44// Constructor.
45//
46// Note that dpy and image should remain valid during the object
47// lifetime, while factory is used only in the constructor itself.
48//
Constantin Kaplinsky936c3692007-12-27 08:42:53 +000049// FIXME: Pass XPixelBuffer* instead of Image*.
50//
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000051
52PollingManager::PollingManager(Display *dpy, Image *image,
53 ImageFactory *factory,
54 int offsetLeft, int offsetTop)
Constantin Kaplinskyec45c482008-01-18 14:13:16 +000055 : m_dpy(dpy),
56 m_server(0),
57 m_image(image),
58 m_bytesPerPixel(image->xim->bits_per_pixel / 8),
59 m_offsetLeft(offsetLeft),
60 m_offsetTop(offsetTop),
61 m_width(image->xim->width),
Constantin Kaplinskyadebffb2008-01-18 14:33:05 +000062 m_height(image->xim->height),
63 m_widthTiles((image->xim->width + 31) / 32),
64 m_heightTiles((image->xim->height + 31) / 32),
65 m_numTiles(((image->xim->width + 31) / 32) *
66 ((image->xim->height + 31) / 32)),
Constantin Kaplinskyd0b15c62007-10-09 08:15:25 +000067 m_pollingStep(0)
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000068{
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000069 // Get initial screen image.
70 m_image->get(DefaultRootWindow(m_dpy), m_offsetLeft, m_offsetTop);
71
Constantin Kaplinsky9ee8dc62007-10-09 07:46:32 +000072 // Create additional images used in polling algorithm, warn if
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000073 // underlying class names are different from the class name of the
74 // primary image.
75 m_rowImage = factory->newImage(m_dpy, m_width, 1);
Constantin Kaplinskyed3cf5d2007-12-28 17:59:10 +000076 m_columnImage = factory->newImage(m_dpy, 1, m_height);
77 const char *primaryImgClass = m_image->className();
78 const char *rowImgClass = m_rowImage->className();
79 const char *columnImgClass = m_columnImage->className();
80 if (strcmp(rowImgClass, primaryImgClass) != 0 ||
81 strcmp(columnImgClass, primaryImgClass) != 0) {
82 vlog.error("Image types do not match (%s, %s, %s)",
83 primaryImgClass, rowImgClass, columnImgClass);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000084 }
85
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +000086 m_changeFlags = new bool[m_numTiles];
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +000087 memset(m_changeFlags, 0, m_numTiles * sizeof(bool));
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000088}
89
90PollingManager::~PollingManager()
91{
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +000092 delete[] m_changeFlags;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000093
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000094 delete m_rowImage;
Constantin Kaplinskyed3cf5d2007-12-28 17:59:10 +000095 delete m_columnImage;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000096}
97
98//
99// Register VNCServer object.
100//
101
102void PollingManager::setVNCServer(VNCServer *s)
103{
104 m_server = s;
105}
106
107//
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000108// DEBUG: Measuring time spent in the poll() function,
109// as well as time intervals between poll() calls.
110//
111
112#ifdef DEBUG
113void PollingManager::debugBeforePoll()
114{
115 TimeMillis timeNow;
116 int diff = timeNow.diffFrom(m_timeSaved);
117 fprintf(stderr, "[wait%4dms]\t[step %2d]\t", diff, m_pollingStep % 32);
118 m_timeSaved = timeNow;
119}
120
121void PollingManager::debugAfterPoll()
122{
123 TimeMillis timeNow;
124 int diff = timeNow.diffFrom(m_timeSaved);
125 fprintf(stderr, "[poll%4dms]\n", diff);
126 m_timeSaved = timeNow;
127}
128
129#endif
130
131//
132// Search for changed rectangles on the screen.
133//
134
135void PollingManager::poll()
136{
137#ifdef DEBUG
138 debugBeforePoll();
139#endif
140
Constantin Kaplinskyd0b15c62007-10-09 08:15:25 +0000141 // Perform polling and try update clients if changes were detected.
142 if (pollScreen())
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000143 m_server->tryUpdate();
144
145#ifdef DEBUG
146 debugAfterPoll();
147#endif
148}
149
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000150#ifdef DEBUG_REPORT_CHANGED_TILES
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000151#define DBG_REPORT_CHANGES(title) printChanges((title))
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000152#else
153#define DBG_REPORT_CHANGES(title)
154#endif
155
Constantin Kaplinsky9ee8dc62007-10-09 07:46:32 +0000156bool PollingManager::pollScreen()
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000157{
158 if (!m_server)
159 return false;
160
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +0000161 // Clear the m_changeFlags[] array, indicating that no changes have
162 // been detected yet.
163 memset(m_changeFlags, 0, m_numTiles * sizeof(bool));
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000164
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000165 // First pass over the framebuffer. Here we scan 1/32 part of the
166 // framebuffer -- that is, one line in each (32 * m_width) stripe.
167 // We compare the pixels of that line with previous framebuffer
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +0000168 // contents and raise corresponding elements of m_changeFlags[].
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000169 int scanOffset = m_pollingOrder[m_pollingStep++ % 32];
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000170 int nTilesChanged = 0;
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000171 for (int y = scanOffset; y < m_height; y += 32) {
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000172 nTilesChanged += checkRow(0, y, m_width);
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000173 }
174
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000175 DBG_REPORT_CHANGES("After 1st pass");
176
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000177 // If some changes have been detected:
Constantin Kaplinsky48792632007-12-28 18:21:42 +0000178 if (nTilesChanged) {
Constantin Kaplinsky82f7b012008-05-30 10:03:30 +0000179 // Try to find more changes around.
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000180 checkNeighbors();
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000181 DBG_REPORT_CHANGES("After checking neighbors");
Constantin Kaplinsky82f7b012008-05-30 10:03:30 +0000182 // Inform the server about the changes.
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000183 nTilesChanged = sendChanges();
Constantin Kaplinsky48792632007-12-28 18:21:42 +0000184 }
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000185
Constantin Kaplinsky52f29d32007-12-28 18:30:54 +0000186#ifdef DEBUG_PRINT_NUM_CHANGED_TILES
187 printf("%3d ", nTilesChanged);
188 if (m_pollingStep % 32 == 0) {
189 printf("\n");
190 }
191#endif
192
Constantin Kaplinskyc1984e02007-10-08 14:54:18 +0000193#ifdef DEBUG
194 if (nTilesChanged != 0) {
195 fprintf(stderr, "#%d# ", nTilesChanged);
196 }
197#endif
198
Constantin Kaplinsky82f7b012008-05-30 10:03:30 +0000199 return (nTilesChanged != 0);
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000200}
201
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000202int PollingManager::checkRow(int x, int y, int w)
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000203{
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000204 // If necessary, expand the row to the left, to the tile border.
205 // In other words, x must be a multiple of 32.
206 if (x % 32 != 0) {
207 int correction = x % 32;
208 x -= correction;
209 w += correction;
210 }
211
Constantin Kaplinsky41de2422008-01-19 09:03:47 +0000212 // Read a row from the screen into m_rowImage.
213 getRow(x, y, w);
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000214
Constantin Kaplinsky04aa5202008-01-18 15:37:15 +0000215 // Compute a pointer to the initial element of m_changeFlags.
216 bool *pChangeFlags = &m_changeFlags[getTileIndex(x, y)];
217
218 // Compute pointers to image data to be compared.
Constantin Kaplinsky801123d2008-01-18 15:23:11 +0000219 char *ptr_old = m_image->locatePixel(x, y);
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000220 char *ptr_new = m_rowImage->xim->data;
221
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000222 // Compare pixels, raise corresponding elements of m_changeFlags[].
Constantin Kaplinsky86506882008-01-21 10:24:25 +0000223 // First, handle full-size (32 pixels wide) tiles.
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000224 int nTilesChanged = 0;
Constantin Kaplinsky86506882008-01-21 10:24:25 +0000225 int nBytesPerTile = 32 * m_bytesPerPixel;
226 for (int i = 0; i < w / 32; i++) {
227 if (memcmp(ptr_old, ptr_new, nBytesPerTile)) {
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000228 *pChangeFlags = true;
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000229 nTilesChanged++;
230 }
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000231 pChangeFlags++;
Constantin Kaplinsky86506882008-01-21 10:24:25 +0000232 ptr_old += nBytesPerTile;
233 ptr_new += nBytesPerTile;
234 }
235
236 // Handle the rightmost pixels, if the width is not a multiple of 32.
237 int nBytesLeft = (w % 32) * m_bytesPerPixel;
238 if (nBytesLeft != 0) {
239 if (memcmp(ptr_old, ptr_new, nBytesLeft)) {
240 *pChangeFlags = true;
241 nTilesChanged++;
242 }
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000243 }
244
245 return nTilesChanged;
246}
247
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000248int PollingManager::checkColumn(int x, int y, int h, bool *pChangeFlags)
249{
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000250 getColumn(x, y, h);
251
252 int nTilesChanged = 0;
253 for (int nTile = 0; nTile < (h + 31) / 32; nTile++) {
254 if (!*pChangeFlags) {
255 int tile_h = (h - nTile * 32 >= 32) ? 32 : h - nTile * 32;
256 for (int i = 0; i < tile_h; i++) {
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000257 // FIXME: Do not compute these pointers in the inner cycle.
Constantin Kaplinsky801123d2008-01-18 15:23:11 +0000258 char *ptr_old = m_image->locatePixel(x, y + nTile * 32 + i);
259 char *ptr_new = m_columnImage->locatePixel(0, nTile * 32 + i);
Constantin Kaplinskyec45c482008-01-18 14:13:16 +0000260 if (memcmp(ptr_old, ptr_new, m_bytesPerPixel)) {
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000261 *pChangeFlags = true;
262 nTilesChanged++;
263 break;
264 }
265 }
266 }
267 pChangeFlags += m_widthTiles;
268 }
269
270 return nTilesChanged;
271}
272
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000273int PollingManager::sendChanges()
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000274{
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000275 const bool *pChangeFlags = m_changeFlags;
Constantin Kaplinsky1a032112008-01-09 10:22:42 +0000276 int nTilesChanged = 0;
277
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000278 Rect rect;
279 for (int y = 0; y < m_heightTiles; y++) {
280 for (int x = 0; x < m_widthTiles; x++) {
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000281 if (*pChangeFlags++) {
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000282 // Count successive tiles marked as changed.
283 int count = 1;
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000284 while (x + count < m_widthTiles && *pChangeFlags++) {
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000285 count++;
286 }
Constantin Kaplinsky1a032112008-01-09 10:22:42 +0000287 nTilesChanged += count;
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000288 // Compute the coordinates and the size of this band.
289 rect.setXYWH(x * 32, y * 32, count * 32, 32);
290 if (rect.br.x > m_width)
291 rect.br.x = m_width;
292 if (rect.br.y > m_height)
293 rect.br.y = m_height;
294 // Add to the changed region maintained by the server.
295 getScreenRect(rect);
296 m_server->add_changed(rect);
297 // Skip processed tiles.
298 x += count;
299 }
300 }
301 }
Constantin Kaplinsky1a032112008-01-09 10:22:42 +0000302 return nTilesChanged;
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000303}
304
Constantin Kaplinsky56649982007-09-04 09:15:35 +0000305void
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000306PollingManager::checkNeighbors()
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000307{
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000308 int x, y;
309
310 // Check neighboring pixels above and below changed tiles.
311 // FIXME: Fast skip to the first changed tile (and to the last, too).
312 // FIXME: Check the full-width line above the first changed tile?
313 for (y = 0; y < m_heightTiles; y++) {
314 bool doneAbove = false;
315 bool doneBelow = false;
316 for (x = 0; x < m_widthTiles; x++) {
317 if (!doneAbove && y > 0 &&
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000318 m_changeFlags[y * m_widthTiles + x] &&
319 !m_changeFlags[(y - 1) * m_widthTiles + x]) {
320 // FIXME: Check m_changeFlags[] to decrease height of the row.
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000321 checkRow(x * 32, y * 32 - 1, m_width - x * 32);
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000322 doneAbove = true;
323 }
324 if (!doneBelow && y < m_heightTiles - 1 &&
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000325 m_changeFlags[y * m_widthTiles + x] &&
326 !m_changeFlags[(y + 1) * m_widthTiles + x]) {
327 // FIXME: Check m_changeFlags[] to decrease height of the row.
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000328 checkRow(x * 32, (y + 1) * 32, m_width - x * 32);
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000329 doneBelow = true;
330 }
331 if (doneBelow && doneAbove)
332 break;
333 }
334 }
335
336 // Check neighboring pixels at the right side of changed tiles.
337 for (x = 0; x < m_widthTiles - 1; x++) {
338 for (y = 0; y < m_heightTiles; y++) {
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000339 if (m_changeFlags[y * m_widthTiles + x] &&
340 !m_changeFlags[y * m_widthTiles + x + 1]) {
341 // FIXME: Check m_changeFlags[] to decrease height of the column.
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000342 checkColumn((x + 1) * 32, y * 32, m_height - y * 32,
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000343 &m_changeFlags[y * m_widthTiles + x + 1]);
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000344 break;
345 }
346 }
347 }
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000348
349 // Check neighboring pixels at the left side of changed tiles.
350 for (x = m_widthTiles - 1; x > 0; x--) {
351 for (y = 0; y < m_heightTiles; y++) {
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000352 if (m_changeFlags[y * m_widthTiles + x] &&
353 !m_changeFlags[y * m_widthTiles + x - 1]) {
354 // FIXME: Check m_changeFlags[] to decrease height of the column.
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000355 checkColumn(x * 32 - 1, y * 32, m_height - y * 32,
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000356 &m_changeFlags[y * m_widthTiles + x - 1]);
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000357 break;
358 }
359 }
360 }
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000361}
362
363void
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000364PollingManager::printChanges(const char *header) const
Constantin Kaplinskyf50bd7f2008-01-10 15:27:42 +0000365{
366 fprintf(stderr, "%s:", header);
367
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000368 const bool *pChangeFlags = m_changeFlags;
369
Constantin Kaplinskyf50bd7f2008-01-10 15:27:42 +0000370 for (int y = 0; y < m_heightTiles; y++) {
371 for (int x = 0; x < m_widthTiles; x++) {
372 if (*pChangeFlags++) {
373 // Count successive tiles marked as changed.
374 int count = 1;
375 while (x + count < m_widthTiles && *pChangeFlags++) {
376 count++;
377 }
378 // Print.
379 fprintf(stderr, " (%d,%d)*%d", x, y, count);
380 // Skip processed tiles.
381 x += count;
382 }
383 }
384 }
385
386 fprintf(stderr, "\n");
387}
388