blob: 27f102853efce761d9c9bfc32f6a50b961ab075f [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),
Constantin Kaplinsky2c019832008-05-30 11:02:04 +000054 m_image(buffer->getImage()),
55 m_bytesPerPixel(buffer->getPF().bpp / 8),
Constantin Kaplinskyec45c482008-01-18 14:13:16 +000056 m_offsetLeft(offsetLeft),
57 m_offsetTop(offsetTop),
Constantin Kaplinsky2c019832008-05-30 11:02:04 +000058 m_width(buffer->width()),
59 m_height(buffer->height()),
60 m_widthTiles((buffer->width() + 31) / 32),
61 m_heightTiles((buffer->height() + 31) / 32),
62 m_numTiles(((buffer->width() + 31) / 32) *
63 ((buffer->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.
69 m_rowImage = factory->newImage(m_dpy, m_width, 1);
Constantin Kaplinskyed3cf5d2007-12-28 17:59:10 +000070 m_columnImage = factory->newImage(m_dpy, 1, m_height);
71 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
Constantin Kaplinskyd0b15c62007-10-09 08:15:25 +0000126 // Perform polling and try update clients if changes were detected.
Constantin Kaplinsky54cfef32008-06-03 07:04:17 +0000127 if (pollScreen(server))
128 server->tryUpdate();
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000129
130#ifdef DEBUG
131 debugAfterPoll();
132#endif
133}
134
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000135#ifdef DEBUG_REPORT_CHANGED_TILES
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000136#define DBG_REPORT_CHANGES(title) printChanges((title))
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000137#else
138#define DBG_REPORT_CHANGES(title)
139#endif
140
Constantin Kaplinsky54cfef32008-06-03 07:04:17 +0000141bool PollingManager::pollScreen(VNCServer *server)
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000142{
Constantin Kaplinsky54cfef32008-06-03 07:04:17 +0000143 if (!server)
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000144 return false;
145
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +0000146 // Clear the m_changeFlags[] array, indicating that no changes have
147 // been detected yet.
148 memset(m_changeFlags, 0, m_numTiles * sizeof(bool));
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000149
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000150 // First pass over the framebuffer. Here we scan 1/32 part of the
151 // framebuffer -- that is, one line in each (32 * m_width) stripe.
152 // We compare the pixels of that line with previous framebuffer
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +0000153 // contents and raise corresponding elements of m_changeFlags[].
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000154 int scanOffset = m_pollingOrder[m_pollingStep++ % 32];
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000155 int nTilesChanged = 0;
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000156 for (int y = scanOffset; y < m_height; y += 32) {
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000157 nTilesChanged += checkRow(0, y, m_width);
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000158 }
159
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000160 DBG_REPORT_CHANGES("After 1st pass");
161
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000162 // If some changes have been detected:
Constantin Kaplinsky48792632007-12-28 18:21:42 +0000163 if (nTilesChanged) {
Constantin Kaplinsky82f7b012008-05-30 10:03:30 +0000164 // Try to find more changes around.
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000165 checkNeighbors();
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000166 DBG_REPORT_CHANGES("After checking neighbors");
Constantin Kaplinsky82f7b012008-05-30 10:03:30 +0000167 // Inform the server about the changes.
Constantin Kaplinsky54cfef32008-06-03 07:04:17 +0000168 nTilesChanged = sendChanges(server);
Constantin Kaplinsky48792632007-12-28 18:21:42 +0000169 }
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000170
Constantin Kaplinsky52f29d32007-12-28 18:30:54 +0000171#ifdef DEBUG_PRINT_NUM_CHANGED_TILES
172 printf("%3d ", nTilesChanged);
173 if (m_pollingStep % 32 == 0) {
174 printf("\n");
175 }
176#endif
177
Constantin Kaplinskyc1984e02007-10-08 14:54:18 +0000178#ifdef DEBUG
179 if (nTilesChanged != 0) {
180 fprintf(stderr, "#%d# ", nTilesChanged);
181 }
182#endif
183
Constantin Kaplinsky82f7b012008-05-30 10:03:30 +0000184 return (nTilesChanged != 0);
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000185}
186
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000187int PollingManager::checkRow(int x, int y, int w)
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000188{
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000189 // If necessary, expand the row to the left, to the tile border.
190 // In other words, x must be a multiple of 32.
191 if (x % 32 != 0) {
192 int correction = x % 32;
193 x -= correction;
194 w += correction;
195 }
196
Constantin Kaplinsky41de2422008-01-19 09:03:47 +0000197 // Read a row from the screen into m_rowImage.
198 getRow(x, y, w);
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000199
Constantin Kaplinsky04aa5202008-01-18 15:37:15 +0000200 // Compute a pointer to the initial element of m_changeFlags.
201 bool *pChangeFlags = &m_changeFlags[getTileIndex(x, y)];
202
203 // Compute pointers to image data to be compared.
Constantin Kaplinsky801123d2008-01-18 15:23:11 +0000204 char *ptr_old = m_image->locatePixel(x, y);
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000205 char *ptr_new = m_rowImage->xim->data;
206
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000207 // Compare pixels, raise corresponding elements of m_changeFlags[].
Constantin Kaplinsky86506882008-01-21 10:24:25 +0000208 // First, handle full-size (32 pixels wide) tiles.
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000209 int nTilesChanged = 0;
Constantin Kaplinsky86506882008-01-21 10:24:25 +0000210 int nBytesPerTile = 32 * m_bytesPerPixel;
211 for (int i = 0; i < w / 32; i++) {
212 if (memcmp(ptr_old, ptr_new, nBytesPerTile)) {
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000213 *pChangeFlags = true;
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000214 nTilesChanged++;
215 }
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000216 pChangeFlags++;
Constantin Kaplinsky86506882008-01-21 10:24:25 +0000217 ptr_old += nBytesPerTile;
218 ptr_new += nBytesPerTile;
219 }
220
221 // Handle the rightmost pixels, if the width is not a multiple of 32.
222 int nBytesLeft = (w % 32) * m_bytesPerPixel;
223 if (nBytesLeft != 0) {
224 if (memcmp(ptr_old, ptr_new, nBytesLeft)) {
225 *pChangeFlags = true;
226 nTilesChanged++;
227 }
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000228 }
229
230 return nTilesChanged;
231}
232
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000233int PollingManager::checkColumn(int x, int y, int h, bool *pChangeFlags)
234{
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000235 getColumn(x, y, h);
236
237 int nTilesChanged = 0;
238 for (int nTile = 0; nTile < (h + 31) / 32; nTile++) {
239 if (!*pChangeFlags) {
240 int tile_h = (h - nTile * 32 >= 32) ? 32 : h - nTile * 32;
241 for (int i = 0; i < tile_h; i++) {
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000242 // FIXME: Do not compute these pointers in the inner cycle.
Constantin Kaplinsky801123d2008-01-18 15:23:11 +0000243 char *ptr_old = m_image->locatePixel(x, y + nTile * 32 + i);
244 char *ptr_new = m_columnImage->locatePixel(0, nTile * 32 + i);
Constantin Kaplinskyec45c482008-01-18 14:13:16 +0000245 if (memcmp(ptr_old, ptr_new, m_bytesPerPixel)) {
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000246 *pChangeFlags = true;
247 nTilesChanged++;
248 break;
249 }
250 }
251 }
252 pChangeFlags += m_widthTiles;
253 }
254
255 return nTilesChanged;
256}
257
Constantin Kaplinsky54cfef32008-06-03 07:04:17 +0000258int PollingManager::sendChanges(VNCServer *server)
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000259{
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000260 const bool *pChangeFlags = m_changeFlags;
Constantin Kaplinsky1a032112008-01-09 10:22:42 +0000261 int nTilesChanged = 0;
262
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000263 Rect rect;
264 for (int y = 0; y < m_heightTiles; y++) {
265 for (int x = 0; x < m_widthTiles; x++) {
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000266 if (*pChangeFlags++) {
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000267 // Count successive tiles marked as changed.
268 int count = 1;
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000269 while (x + count < m_widthTiles && *pChangeFlags++) {
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000270 count++;
271 }
Constantin Kaplinsky1a032112008-01-09 10:22:42 +0000272 nTilesChanged += count;
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000273 // Compute the coordinates and the size of this band.
274 rect.setXYWH(x * 32, y * 32, count * 32, 32);
275 if (rect.br.x > m_width)
276 rect.br.x = m_width;
277 if (rect.br.y > m_height)
278 rect.br.y = m_height;
279 // Add to the changed region maintained by the server.
Constantin Kaplinsky54cfef32008-06-03 07:04:17 +0000280 server->add_changed(rect);
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000281 // Skip processed tiles.
282 x += count;
283 }
284 }
285 }
Constantin Kaplinsky1a032112008-01-09 10:22:42 +0000286 return nTilesChanged;
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000287}
288
Constantin Kaplinsky56649982007-09-04 09:15:35 +0000289void
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000290PollingManager::checkNeighbors()
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000291{
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000292 int x, y;
293
294 // Check neighboring pixels above and below changed tiles.
295 // FIXME: Fast skip to the first changed tile (and to the last, too).
296 // FIXME: Check the full-width line above the first changed tile?
297 for (y = 0; y < m_heightTiles; y++) {
298 bool doneAbove = false;
299 bool doneBelow = false;
300 for (x = 0; x < m_widthTiles; x++) {
301 if (!doneAbove && y > 0 &&
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000302 m_changeFlags[y * m_widthTiles + x] &&
303 !m_changeFlags[(y - 1) * m_widthTiles + x]) {
304 // FIXME: Check m_changeFlags[] to decrease height of the row.
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000305 checkRow(x * 32, y * 32 - 1, m_width - x * 32);
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000306 doneAbove = true;
307 }
308 if (!doneBelow && y < m_heightTiles - 1 &&
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000309 m_changeFlags[y * m_widthTiles + x] &&
310 !m_changeFlags[(y + 1) * m_widthTiles + x]) {
311 // FIXME: Check m_changeFlags[] to decrease height of the row.
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000312 checkRow(x * 32, (y + 1) * 32, m_width - x * 32);
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000313 doneBelow = true;
314 }
315 if (doneBelow && doneAbove)
316 break;
317 }
318 }
319
320 // Check neighboring pixels at the right side of changed tiles.
321 for (x = 0; x < m_widthTiles - 1; x++) {
322 for (y = 0; y < m_heightTiles; y++) {
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000323 if (m_changeFlags[y * m_widthTiles + x] &&
324 !m_changeFlags[y * m_widthTiles + x + 1]) {
325 // FIXME: Check m_changeFlags[] to decrease height of the column.
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000326 checkColumn((x + 1) * 32, y * 32, m_height - y * 32,
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000327 &m_changeFlags[y * m_widthTiles + x + 1]);
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000328 break;
329 }
330 }
331 }
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000332
333 // Check neighboring pixels at the left side of changed tiles.
334 for (x = m_widthTiles - 1; x > 0; x--) {
335 for (y = 0; y < m_heightTiles; y++) {
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000336 if (m_changeFlags[y * m_widthTiles + x] &&
337 !m_changeFlags[y * m_widthTiles + x - 1]) {
338 // FIXME: Check m_changeFlags[] to decrease height of the column.
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000339 checkColumn(x * 32 - 1, y * 32, m_height - y * 32,
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000340 &m_changeFlags[y * m_widthTiles + x - 1]);
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000341 break;
342 }
343 }
344 }
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000345}
346
347void
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000348PollingManager::printChanges(const char *header) const
Constantin Kaplinskyf50bd7f2008-01-10 15:27:42 +0000349{
350 fprintf(stderr, "%s:", header);
351
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000352 const bool *pChangeFlags = m_changeFlags;
353
Constantin Kaplinskyf50bd7f2008-01-10 15:27:42 +0000354 for (int y = 0; y < m_heightTiles; y++) {
355 for (int x = 0; x < m_widthTiles; x++) {
356 if (*pChangeFlags++) {
357 // Count successive tiles marked as changed.
358 int count = 1;
359 while (x + count < m_widthTiles && *pChangeFlags++) {
360 count++;
361 }
362 // Print.
363 fprintf(stderr, " (%d,%d)*%d", x, y, count);
364 // Skip processed tiles.
365 x += count;
366 }
367 }
368 }
369
370 fprintf(stderr, "\n");
371}
372