blob: 3a040e23e2760c36e852465268ed6fef7e94d6ad [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 Kaplinsky8a85c492008-06-04 05:36:40 +000046// Note that dpy and image 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 Kaplinsky8a85c492008-06-04 05:36:40 +000050PollingManager::PollingManager(Display *dpy, const Image *image,
Constantin Kaplinsky303433a2008-06-04 05:57:06 +000051 ImageFactory &factory,
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000052 int offsetLeft, int offsetTop)
Constantin Kaplinskyec45c482008-01-18 14:13:16 +000053 : m_dpy(dpy),
Constantin Kaplinsky8a85c492008-06-04 05:36:40 +000054 m_image(image),
55 m_bytesPerPixel(image->xim->bits_per_pixel / 8),
Constantin Kaplinskyec45c482008-01-18 14:13:16 +000056 m_offsetLeft(offsetLeft),
57 m_offsetTop(offsetTop),
Constantin Kaplinsky8a85c492008-06-04 05:36:40 +000058 m_width(image->xim->width),
59 m_height(image->xim->height),
60 m_widthTiles((image->xim->width + 31) / 32),
61 m_heightTiles((image->xim->height + 31) / 32),
62 m_numTiles(((image->xim->width + 31) / 32) *
63 ((image->xim->height + 31) / 32)),
Constantin Kaplinskyd0b15c62007-10-09 08:15:25 +000064 m_pollingStep(0)
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000065{
Constantin Kaplinsky9ee8dc62007-10-09 07:46:32 +000066 // Create additional images used in polling algorithm, warn if
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000067 // underlying class names are different from the class name of the
68 // primary image.
Constantin Kaplinsky303433a2008-06-04 05:57:06 +000069 m_rowImage = factory.newImage(m_dpy, m_width, 1);
70 m_columnImage = factory.newImage(m_dpy, 1, m_height);
Constantin Kaplinskyed3cf5d2007-12-28 17:59:10 +000071 const char *primaryImgClass = m_image->className();
72 const char *rowImgClass = m_rowImage->className();
73 const char *columnImgClass = m_columnImage->className();
74 if (strcmp(rowImgClass, primaryImgClass) != 0 ||
75 strcmp(columnImgClass, primaryImgClass) != 0) {
76 vlog.error("Image types do not match (%s, %s, %s)",
77 primaryImgClass, rowImgClass, columnImgClass);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000078 }
79
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +000080 m_changeFlags = new bool[m_numTiles];
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +000081 memset(m_changeFlags, 0, m_numTiles * sizeof(bool));
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000082}
83
84PollingManager::~PollingManager()
85{
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +000086 delete[] m_changeFlags;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000087
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000088 delete m_rowImage;
Constantin Kaplinskyed3cf5d2007-12-28 17:59:10 +000089 delete m_columnImage;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000090}
91
92//
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000093// DEBUG: Measuring time spent in the poll() function,
94// as well as time intervals between poll() calls.
95//
96
97#ifdef DEBUG
98void PollingManager::debugBeforePoll()
99{
100 TimeMillis timeNow;
101 int diff = timeNow.diffFrom(m_timeSaved);
102 fprintf(stderr, "[wait%4dms]\t[step %2d]\t", diff, m_pollingStep % 32);
103 m_timeSaved = timeNow;
104}
105
106void PollingManager::debugAfterPoll()
107{
108 TimeMillis timeNow;
109 int diff = timeNow.diffFrom(m_timeSaved);
110 fprintf(stderr, "[poll%4dms]\n", diff);
111 m_timeSaved = timeNow;
112}
113
114#endif
115
116//
117// Search for changed rectangles on the screen.
118//
119
Constantin Kaplinsky54cfef32008-06-03 07:04:17 +0000120void PollingManager::poll(VNCServer *server)
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000121{
122#ifdef DEBUG
123 debugBeforePoll();
124#endif
125
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000126 pollScreen(server);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000127
128#ifdef DEBUG
129 debugAfterPoll();
130#endif
131}
132
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000133#ifdef DEBUG_REPORT_CHANGED_TILES
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000134#define DBG_REPORT_CHANGES(title) printChanges((title))
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000135#else
136#define DBG_REPORT_CHANGES(title)
137#endif
138
Constantin Kaplinsky54cfef32008-06-03 07:04:17 +0000139bool PollingManager::pollScreen(VNCServer *server)
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000140{
Constantin Kaplinsky54cfef32008-06-03 07:04:17 +0000141 if (!server)
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000142 return false;
143
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +0000144 // Clear the m_changeFlags[] array, indicating that no changes have
145 // been detected yet.
146 memset(m_changeFlags, 0, m_numTiles * sizeof(bool));
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000147
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000148 // First pass over the framebuffer. Here we scan 1/32 part of the
149 // framebuffer -- that is, one line in each (32 * m_width) stripe.
150 // We compare the pixels of that line with previous framebuffer
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +0000151 // contents and raise corresponding elements of m_changeFlags[].
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000152 int scanOffset = m_pollingOrder[m_pollingStep++ % 32];
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000153 int nTilesChanged = 0;
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000154 for (int y = scanOffset; y < m_height; y += 32) {
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000155 nTilesChanged += checkRow(0, y, m_width);
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000156 }
157
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000158 DBG_REPORT_CHANGES("After 1st pass");
159
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000160 // If some changes have been detected:
Constantin Kaplinsky48792632007-12-28 18:21:42 +0000161 if (nTilesChanged) {
Constantin Kaplinsky82f7b012008-05-30 10:03:30 +0000162 // Try to find more changes around.
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000163 checkNeighbors();
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000164 DBG_REPORT_CHANGES("After checking neighbors");
Constantin Kaplinsky82f7b012008-05-30 10:03:30 +0000165 // Inform the server about the changes.
Constantin Kaplinsky54cfef32008-06-03 07:04:17 +0000166 nTilesChanged = sendChanges(server);
Constantin Kaplinsky48792632007-12-28 18:21:42 +0000167 }
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000168
Constantin Kaplinsky52f29d32007-12-28 18:30:54 +0000169#ifdef DEBUG_PRINT_NUM_CHANGED_TILES
170 printf("%3d ", nTilesChanged);
171 if (m_pollingStep % 32 == 0) {
172 printf("\n");
173 }
174#endif
175
Constantin Kaplinskyc1984e02007-10-08 14:54:18 +0000176#ifdef DEBUG
177 if (nTilesChanged != 0) {
178 fprintf(stderr, "#%d# ", nTilesChanged);
179 }
180#endif
181
Constantin Kaplinsky82f7b012008-05-30 10:03:30 +0000182 return (nTilesChanged != 0);
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000183}
184
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000185int PollingManager::checkRow(int x, int y, int w)
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000186{
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000187 // If necessary, expand the row to the left, to the tile border.
188 // In other words, x must be a multiple of 32.
189 if (x % 32 != 0) {
190 int correction = x % 32;
191 x -= correction;
192 w += correction;
193 }
194
Constantin Kaplinsky41de2422008-01-19 09:03:47 +0000195 // Read a row from the screen into m_rowImage.
196 getRow(x, y, w);
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000197
Constantin Kaplinsky04aa5202008-01-18 15:37:15 +0000198 // Compute a pointer to the initial element of m_changeFlags.
199 bool *pChangeFlags = &m_changeFlags[getTileIndex(x, y)];
200
201 // Compute pointers to image data to be compared.
Constantin Kaplinsky801123d2008-01-18 15:23:11 +0000202 char *ptr_old = m_image->locatePixel(x, y);
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000203 char *ptr_new = m_rowImage->xim->data;
204
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000205 // Compare pixels, raise corresponding elements of m_changeFlags[].
Constantin Kaplinsky86506882008-01-21 10:24:25 +0000206 // First, handle full-size (32 pixels wide) tiles.
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000207 int nTilesChanged = 0;
Constantin Kaplinsky86506882008-01-21 10:24:25 +0000208 int nBytesPerTile = 32 * m_bytesPerPixel;
209 for (int i = 0; i < w / 32; i++) {
210 if (memcmp(ptr_old, ptr_new, nBytesPerTile)) {
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000211 *pChangeFlags = true;
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000212 nTilesChanged++;
213 }
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000214 pChangeFlags++;
Constantin Kaplinsky86506882008-01-21 10:24:25 +0000215 ptr_old += nBytesPerTile;
216 ptr_new += nBytesPerTile;
217 }
218
219 // Handle the rightmost pixels, if the width is not a multiple of 32.
220 int nBytesLeft = (w % 32) * m_bytesPerPixel;
221 if (nBytesLeft != 0) {
222 if (memcmp(ptr_old, ptr_new, nBytesLeft)) {
223 *pChangeFlags = true;
224 nTilesChanged++;
225 }
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000226 }
227
228 return nTilesChanged;
229}
230
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000231int PollingManager::checkColumn(int x, int y, int h, bool *pChangeFlags)
232{
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000233 getColumn(x, y, h);
234
235 int nTilesChanged = 0;
236 for (int nTile = 0; nTile < (h + 31) / 32; nTile++) {
237 if (!*pChangeFlags) {
238 int tile_h = (h - nTile * 32 >= 32) ? 32 : h - nTile * 32;
239 for (int i = 0; i < tile_h; i++) {
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000240 // FIXME: Do not compute these pointers in the inner cycle.
Constantin Kaplinsky801123d2008-01-18 15:23:11 +0000241 char *ptr_old = m_image->locatePixel(x, y + nTile * 32 + i);
242 char *ptr_new = m_columnImage->locatePixel(0, nTile * 32 + i);
Constantin Kaplinskyec45c482008-01-18 14:13:16 +0000243 if (memcmp(ptr_old, ptr_new, m_bytesPerPixel)) {
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000244 *pChangeFlags = true;
245 nTilesChanged++;
246 break;
247 }
248 }
249 }
250 pChangeFlags += m_widthTiles;
251 }
252
253 return nTilesChanged;
254}
255
Constantin Kaplinsky93d40aa2008-06-04 11:20:09 +0000256int PollingManager::sendChanges(VNCServer *server) const
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000257{
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000258 const bool *pChangeFlags = m_changeFlags;
Constantin Kaplinsky1a032112008-01-09 10:22:42 +0000259 int nTilesChanged = 0;
260
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000261 Rect rect;
262 for (int y = 0; y < m_heightTiles; y++) {
263 for (int x = 0; x < m_widthTiles; x++) {
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000264 if (*pChangeFlags++) {
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000265 // Count successive tiles marked as changed.
266 int count = 1;
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000267 while (x + count < m_widthTiles && *pChangeFlags++) {
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000268 count++;
269 }
Constantin Kaplinsky1a032112008-01-09 10:22:42 +0000270 nTilesChanged += count;
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000271 // Compute the coordinates and the size of this band.
272 rect.setXYWH(x * 32, y * 32, count * 32, 32);
273 if (rect.br.x > m_width)
274 rect.br.x = m_width;
275 if (rect.br.y > m_height)
276 rect.br.y = m_height;
277 // Add to the changed region maintained by the server.
Constantin Kaplinsky54cfef32008-06-03 07:04:17 +0000278 server->add_changed(rect);
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000279 // Skip processed tiles.
280 x += count;
281 }
282 }
283 }
Constantin Kaplinsky1a032112008-01-09 10:22:42 +0000284 return nTilesChanged;
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000285}
286
Constantin Kaplinsky56649982007-09-04 09:15:35 +0000287void
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000288PollingManager::checkNeighbors()
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000289{
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000290 int x, y;
291
292 // Check neighboring pixels above and below changed tiles.
293 // FIXME: Fast skip to the first changed tile (and to the last, too).
294 // FIXME: Check the full-width line above the first changed tile?
295 for (y = 0; y < m_heightTiles; y++) {
296 bool doneAbove = false;
297 bool doneBelow = false;
298 for (x = 0; x < m_widthTiles; x++) {
299 if (!doneAbove && y > 0 &&
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000300 m_changeFlags[y * m_widthTiles + x] &&
301 !m_changeFlags[(y - 1) * m_widthTiles + x]) {
302 // FIXME: Check m_changeFlags[] to decrease height of the row.
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000303 checkRow(x * 32, y * 32 - 1, m_width - x * 32);
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000304 doneAbove = true;
305 }
306 if (!doneBelow && y < m_heightTiles - 1 &&
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000307 m_changeFlags[y * m_widthTiles + x] &&
308 !m_changeFlags[(y + 1) * m_widthTiles + x]) {
309 // FIXME: Check m_changeFlags[] to decrease height of the row.
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000310 checkRow(x * 32, (y + 1) * 32, m_width - x * 32);
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000311 doneBelow = true;
312 }
313 if (doneBelow && doneAbove)
314 break;
315 }
316 }
317
318 // Check neighboring pixels at the right side of changed tiles.
319 for (x = 0; x < m_widthTiles - 1; x++) {
320 for (y = 0; y < m_heightTiles; y++) {
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000321 if (m_changeFlags[y * m_widthTiles + x] &&
322 !m_changeFlags[y * m_widthTiles + x + 1]) {
323 // FIXME: Check m_changeFlags[] to decrease height of the column.
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000324 checkColumn((x + 1) * 32, y * 32, m_height - y * 32,
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000325 &m_changeFlags[y * m_widthTiles + x + 1]);
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000326 break;
327 }
328 }
329 }
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000330
331 // Check neighboring pixels at the left side of changed tiles.
332 for (x = m_widthTiles - 1; x > 0; x--) {
333 for (y = 0; y < m_heightTiles; y++) {
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000334 if (m_changeFlags[y * m_widthTiles + x] &&
335 !m_changeFlags[y * m_widthTiles + x - 1]) {
336 // FIXME: Check m_changeFlags[] to decrease height of the column.
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000337 checkColumn(x * 32 - 1, y * 32, m_height - y * 32,
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000338 &m_changeFlags[y * m_widthTiles + x - 1]);
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000339 break;
340 }
341 }
342 }
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000343}
344
345void
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000346PollingManager::printChanges(const char *header) const
Constantin Kaplinskyf50bd7f2008-01-10 15:27:42 +0000347{
348 fprintf(stderr, "%s:", header);
349
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000350 const bool *pChangeFlags = m_changeFlags;
351
Constantin Kaplinskyf50bd7f2008-01-10 15:27:42 +0000352 for (int y = 0; y < m_heightTiles; y++) {
353 for (int x = 0; x < m_widthTiles; x++) {
354 if (*pChangeFlags++) {
355 // Count successive tiles marked as changed.
356 int count = 1;
357 while (x + count < m_widthTiles && *pChangeFlags++) {
358 count++;
359 }
360 // Print.
361 fprintf(stderr, " (%d,%d)*%d", x, y, count);
362 // Skip processed tiles.
363 x += count;
364 }
365 }
366 }
367
368 fprintf(stderr, "\n");
369}
370