blob: 71b5d660c8578e44c30f29b68308dc3fa930339d [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//
Constantin Kaplinsky2c019832008-05-30 11:02:04 +000046// Note that dpy and buffer should remain valid during the object
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000047// lifetime, while factory is used only in the constructor itself.
48//
49
Constantin Kaplinsky2c019832008-05-30 11:02:04 +000050PollingManager::PollingManager(Display *dpy, XPixelBuffer *buffer,
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000051 ImageFactory *factory,
52 int offsetLeft, int offsetTop)
Constantin Kaplinskyec45c482008-01-18 14:13:16 +000053 : m_dpy(dpy),
54 m_server(0),
Constantin Kaplinsky2c019832008-05-30 11:02:04 +000055 m_image(buffer->getImage()),
56 m_bytesPerPixel(buffer->getPF().bpp / 8),
Constantin Kaplinskyec45c482008-01-18 14:13:16 +000057 m_offsetLeft(offsetLeft),
58 m_offsetTop(offsetTop),
Constantin Kaplinsky2c019832008-05-30 11:02:04 +000059 m_width(buffer->width()),
60 m_height(buffer->height()),
61 m_widthTiles((buffer->width() + 31) / 32),
62 m_heightTiles((buffer->height() + 31) / 32),
63 m_numTiles(((buffer->width() + 31) / 32) *
64 ((buffer->height() + 31) / 32)),
Constantin Kaplinskyd0b15c62007-10-09 08:15:25 +000065 m_pollingStep(0)
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000066{
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000067 // Get initial screen image.
68 m_image->get(DefaultRootWindow(m_dpy), m_offsetLeft, m_offsetTop);
69
Constantin Kaplinsky9ee8dc62007-10-09 07:46:32 +000070 // Create additional images used in polling algorithm, warn if
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000071 // underlying class names are different from the class name of the
72 // primary image.
73 m_rowImage = factory->newImage(m_dpy, m_width, 1);
Constantin Kaplinskyed3cf5d2007-12-28 17:59:10 +000074 m_columnImage = factory->newImage(m_dpy, 1, m_height);
75 const char *primaryImgClass = m_image->className();
76 const char *rowImgClass = m_rowImage->className();
77 const char *columnImgClass = m_columnImage->className();
78 if (strcmp(rowImgClass, primaryImgClass) != 0 ||
79 strcmp(columnImgClass, primaryImgClass) != 0) {
80 vlog.error("Image types do not match (%s, %s, %s)",
81 primaryImgClass, rowImgClass, columnImgClass);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000082 }
83
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +000084 m_changeFlags = new bool[m_numTiles];
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +000085 memset(m_changeFlags, 0, m_numTiles * sizeof(bool));
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000086}
87
88PollingManager::~PollingManager()
89{
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +000090 delete[] m_changeFlags;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000091
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000092 delete m_rowImage;
Constantin Kaplinskyed3cf5d2007-12-28 17:59:10 +000093 delete m_columnImage;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000094}
95
96//
97// Register VNCServer object.
98//
99
100void PollingManager::setVNCServer(VNCServer *s)
101{
102 m_server = s;
103}
104
105//
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000106// DEBUG: Measuring time spent in the poll() function,
107// as well as time intervals between poll() calls.
108//
109
110#ifdef DEBUG
111void PollingManager::debugBeforePoll()
112{
113 TimeMillis timeNow;
114 int diff = timeNow.diffFrom(m_timeSaved);
115 fprintf(stderr, "[wait%4dms]\t[step %2d]\t", diff, m_pollingStep % 32);
116 m_timeSaved = timeNow;
117}
118
119void PollingManager::debugAfterPoll()
120{
121 TimeMillis timeNow;
122 int diff = timeNow.diffFrom(m_timeSaved);
123 fprintf(stderr, "[poll%4dms]\n", diff);
124 m_timeSaved = timeNow;
125}
126
127#endif
128
129//
130// Search for changed rectangles on the screen.
131//
132
133void PollingManager::poll()
134{
135#ifdef DEBUG
136 debugBeforePoll();
137#endif
138
Constantin Kaplinskyd0b15c62007-10-09 08:15:25 +0000139 // Perform polling and try update clients if changes were detected.
140 if (pollScreen())
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000141 m_server->tryUpdate();
142
143#ifdef DEBUG
144 debugAfterPoll();
145#endif
146}
147
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000148#ifdef DEBUG_REPORT_CHANGED_TILES
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000149#define DBG_REPORT_CHANGES(title) printChanges((title))
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000150#else
151#define DBG_REPORT_CHANGES(title)
152#endif
153
Constantin Kaplinsky9ee8dc62007-10-09 07:46:32 +0000154bool PollingManager::pollScreen()
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000155{
156 if (!m_server)
157 return false;
158
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +0000159 // Clear the m_changeFlags[] array, indicating that no changes have
160 // been detected yet.
161 memset(m_changeFlags, 0, m_numTiles * sizeof(bool));
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000162
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000163 // First pass over the framebuffer. Here we scan 1/32 part of the
164 // framebuffer -- that is, one line in each (32 * m_width) stripe.
165 // We compare the pixels of that line with previous framebuffer
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +0000166 // contents and raise corresponding elements of m_changeFlags[].
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000167 int scanOffset = m_pollingOrder[m_pollingStep++ % 32];
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000168 int nTilesChanged = 0;
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000169 for (int y = scanOffset; y < m_height; y += 32) {
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000170 nTilesChanged += checkRow(0, y, m_width);
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000171 }
172
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000173 DBG_REPORT_CHANGES("After 1st pass");
174
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000175 // If some changes have been detected:
Constantin Kaplinsky48792632007-12-28 18:21:42 +0000176 if (nTilesChanged) {
Constantin Kaplinsky82f7b012008-05-30 10:03:30 +0000177 // Try to find more changes around.
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000178 checkNeighbors();
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000179 DBG_REPORT_CHANGES("After checking neighbors");
Constantin Kaplinsky82f7b012008-05-30 10:03:30 +0000180 // Inform the server about the changes.
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000181 nTilesChanged = sendChanges();
Constantin Kaplinsky48792632007-12-28 18:21:42 +0000182 }
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000183
Constantin Kaplinsky52f29d32007-12-28 18:30:54 +0000184#ifdef DEBUG_PRINT_NUM_CHANGED_TILES
185 printf("%3d ", nTilesChanged);
186 if (m_pollingStep % 32 == 0) {
187 printf("\n");
188 }
189#endif
190
Constantin Kaplinskyc1984e02007-10-08 14:54:18 +0000191#ifdef DEBUG
192 if (nTilesChanged != 0) {
193 fprintf(stderr, "#%d# ", nTilesChanged);
194 }
195#endif
196
Constantin Kaplinsky82f7b012008-05-30 10:03:30 +0000197 return (nTilesChanged != 0);
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000198}
199
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000200int PollingManager::checkRow(int x, int y, int w)
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000201{
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000202 // If necessary, expand the row to the left, to the tile border.
203 // In other words, x must be a multiple of 32.
204 if (x % 32 != 0) {
205 int correction = x % 32;
206 x -= correction;
207 w += correction;
208 }
209
Constantin Kaplinsky41de2422008-01-19 09:03:47 +0000210 // Read a row from the screen into m_rowImage.
211 getRow(x, y, w);
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000212
Constantin Kaplinsky04aa5202008-01-18 15:37:15 +0000213 // Compute a pointer to the initial element of m_changeFlags.
214 bool *pChangeFlags = &m_changeFlags[getTileIndex(x, y)];
215
216 // Compute pointers to image data to be compared.
Constantin Kaplinsky801123d2008-01-18 15:23:11 +0000217 char *ptr_old = m_image->locatePixel(x, y);
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000218 char *ptr_new = m_rowImage->xim->data;
219
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000220 // Compare pixels, raise corresponding elements of m_changeFlags[].
Constantin Kaplinsky86506882008-01-21 10:24:25 +0000221 // First, handle full-size (32 pixels wide) tiles.
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000222 int nTilesChanged = 0;
Constantin Kaplinsky86506882008-01-21 10:24:25 +0000223 int nBytesPerTile = 32 * m_bytesPerPixel;
224 for (int i = 0; i < w / 32; i++) {
225 if (memcmp(ptr_old, ptr_new, nBytesPerTile)) {
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000226 *pChangeFlags = true;
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000227 nTilesChanged++;
228 }
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000229 pChangeFlags++;
Constantin Kaplinsky86506882008-01-21 10:24:25 +0000230 ptr_old += nBytesPerTile;
231 ptr_new += nBytesPerTile;
232 }
233
234 // Handle the rightmost pixels, if the width is not a multiple of 32.
235 int nBytesLeft = (w % 32) * m_bytesPerPixel;
236 if (nBytesLeft != 0) {
237 if (memcmp(ptr_old, ptr_new, nBytesLeft)) {
238 *pChangeFlags = true;
239 nTilesChanged++;
240 }
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000241 }
242
243 return nTilesChanged;
244}
245
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000246int PollingManager::checkColumn(int x, int y, int h, bool *pChangeFlags)
247{
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000248 getColumn(x, y, h);
249
250 int nTilesChanged = 0;
251 for (int nTile = 0; nTile < (h + 31) / 32; nTile++) {
252 if (!*pChangeFlags) {
253 int tile_h = (h - nTile * 32 >= 32) ? 32 : h - nTile * 32;
254 for (int i = 0; i < tile_h; i++) {
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000255 // FIXME: Do not compute these pointers in the inner cycle.
Constantin Kaplinsky801123d2008-01-18 15:23:11 +0000256 char *ptr_old = m_image->locatePixel(x, y + nTile * 32 + i);
257 char *ptr_new = m_columnImage->locatePixel(0, nTile * 32 + i);
Constantin Kaplinskyec45c482008-01-18 14:13:16 +0000258 if (memcmp(ptr_old, ptr_new, m_bytesPerPixel)) {
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000259 *pChangeFlags = true;
260 nTilesChanged++;
261 break;
262 }
263 }
264 }
265 pChangeFlags += m_widthTiles;
266 }
267
268 return nTilesChanged;
269}
270
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000271int PollingManager::sendChanges()
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000272{
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000273 const bool *pChangeFlags = m_changeFlags;
Constantin Kaplinsky1a032112008-01-09 10:22:42 +0000274 int nTilesChanged = 0;
275
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000276 Rect rect;
277 for (int y = 0; y < m_heightTiles; y++) {
278 for (int x = 0; x < m_widthTiles; x++) {
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000279 if (*pChangeFlags++) {
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000280 // Count successive tiles marked as changed.
281 int count = 1;
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000282 while (x + count < m_widthTiles && *pChangeFlags++) {
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000283 count++;
284 }
Constantin Kaplinsky1a032112008-01-09 10:22:42 +0000285 nTilesChanged += count;
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000286 // Compute the coordinates and the size of this band.
287 rect.setXYWH(x * 32, y * 32, count * 32, 32);
288 if (rect.br.x > m_width)
289 rect.br.x = m_width;
290 if (rect.br.y > m_height)
291 rect.br.y = m_height;
292 // Add to the changed region maintained by the server.
293 getScreenRect(rect);
294 m_server->add_changed(rect);
295 // Skip processed tiles.
296 x += count;
297 }
298 }
299 }
Constantin Kaplinsky1a032112008-01-09 10:22:42 +0000300 return nTilesChanged;
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000301}
302
Constantin Kaplinsky56649982007-09-04 09:15:35 +0000303void
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000304PollingManager::checkNeighbors()
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000305{
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000306 int x, y;
307
308 // Check neighboring pixels above and below changed tiles.
309 // FIXME: Fast skip to the first changed tile (and to the last, too).
310 // FIXME: Check the full-width line above the first changed tile?
311 for (y = 0; y < m_heightTiles; y++) {
312 bool doneAbove = false;
313 bool doneBelow = false;
314 for (x = 0; x < m_widthTiles; x++) {
315 if (!doneAbove && y > 0 &&
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000316 m_changeFlags[y * m_widthTiles + x] &&
317 !m_changeFlags[(y - 1) * m_widthTiles + x]) {
318 // FIXME: Check m_changeFlags[] to decrease height of the row.
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000319 checkRow(x * 32, y * 32 - 1, m_width - x * 32);
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000320 doneAbove = true;
321 }
322 if (!doneBelow && y < m_heightTiles - 1 &&
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000323 m_changeFlags[y * m_widthTiles + x] &&
324 !m_changeFlags[(y + 1) * m_widthTiles + x]) {
325 // FIXME: Check m_changeFlags[] to decrease height of the row.
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000326 checkRow(x * 32, (y + 1) * 32, m_width - x * 32);
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000327 doneBelow = true;
328 }
329 if (doneBelow && doneAbove)
330 break;
331 }
332 }
333
334 // Check neighboring pixels at the right side of changed tiles.
335 for (x = 0; x < m_widthTiles - 1; x++) {
336 for (y = 0; y < m_heightTiles; y++) {
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000337 if (m_changeFlags[y * m_widthTiles + x] &&
338 !m_changeFlags[y * m_widthTiles + x + 1]) {
339 // FIXME: Check m_changeFlags[] to decrease height of the column.
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000340 checkColumn((x + 1) * 32, y * 32, m_height - y * 32,
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000341 &m_changeFlags[y * m_widthTiles + x + 1]);
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000342 break;
343 }
344 }
345 }
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000346
347 // Check neighboring pixels at the left side of changed tiles.
348 for (x = m_widthTiles - 1; x > 0; x--) {
349 for (y = 0; y < m_heightTiles; y++) {
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000350 if (m_changeFlags[y * m_widthTiles + x] &&
351 !m_changeFlags[y * m_widthTiles + x - 1]) {
352 // FIXME: Check m_changeFlags[] to decrease height of the column.
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000353 checkColumn(x * 32 - 1, y * 32, m_height - y * 32,
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000354 &m_changeFlags[y * m_widthTiles + x - 1]);
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000355 break;
356 }
357 }
358 }
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000359}
360
361void
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000362PollingManager::printChanges(const char *header) const
Constantin Kaplinskyf50bd7f2008-01-10 15:27:42 +0000363{
364 fprintf(stderr, "%s:", header);
365
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000366 const bool *pChangeFlags = m_changeFlags;
367
Constantin Kaplinskyf50bd7f2008-01-10 15:27:42 +0000368 for (int y = 0; y < m_heightTiles; y++) {
369 for (int x = 0; x < m_widthTiles; x++) {
370 if (*pChangeFlags++) {
371 // Count successive tiles marked as changed.
372 int count = 1;
373 while (x + count < m_widthTiles && *pChangeFlags++) {
374 count++;
375 }
376 // Print.
377 fprintf(stderr, " (%d,%d)*%d", x, y, count);
378 // Skip processed tiles.
379 x += count;
380 }
381 }
382 }
383
384 fprintf(stderr, "\n");
385}
386