blob: 9a9f28aff9c4297e944b37fbd3aa29fb129ff642 [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 Kaplinsky9ee8dc62007-10-09 07:46:32 +000067 // Create additional images used in polling algorithm, warn if
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000068 // underlying class names are different from the class name of the
69 // primary image.
70 m_rowImage = factory->newImage(m_dpy, m_width, 1);
Constantin Kaplinskyed3cf5d2007-12-28 17:59:10 +000071 m_columnImage = factory->newImage(m_dpy, 1, m_height);
72 const char *primaryImgClass = m_image->className();
73 const char *rowImgClass = m_rowImage->className();
74 const char *columnImgClass = m_columnImage->className();
75 if (strcmp(rowImgClass, primaryImgClass) != 0 ||
76 strcmp(columnImgClass, primaryImgClass) != 0) {
77 vlog.error("Image types do not match (%s, %s, %s)",
78 primaryImgClass, rowImgClass, columnImgClass);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000079 }
80
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +000081 m_changeFlags = new bool[m_numTiles];
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +000082 memset(m_changeFlags, 0, m_numTiles * sizeof(bool));
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000083}
84
85PollingManager::~PollingManager()
86{
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +000087 delete[] m_changeFlags;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000088
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000089 delete m_rowImage;
Constantin Kaplinskyed3cf5d2007-12-28 17:59:10 +000090 delete m_columnImage;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000091}
92
93//
94// Register VNCServer object.
95//
96
97void PollingManager::setVNCServer(VNCServer *s)
98{
99 m_server = s;
100}
101
102//
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000103// DEBUG: Measuring time spent in the poll() function,
104// as well as time intervals between poll() calls.
105//
106
107#ifdef DEBUG
108void PollingManager::debugBeforePoll()
109{
110 TimeMillis timeNow;
111 int diff = timeNow.diffFrom(m_timeSaved);
112 fprintf(stderr, "[wait%4dms]\t[step %2d]\t", diff, m_pollingStep % 32);
113 m_timeSaved = timeNow;
114}
115
116void PollingManager::debugAfterPoll()
117{
118 TimeMillis timeNow;
119 int diff = timeNow.diffFrom(m_timeSaved);
120 fprintf(stderr, "[poll%4dms]\n", diff);
121 m_timeSaved = timeNow;
122}
123
124#endif
125
126//
127// Search for changed rectangles on the screen.
128//
129
130void PollingManager::poll()
131{
132#ifdef DEBUG
133 debugBeforePoll();
134#endif
135
Constantin Kaplinskyd0b15c62007-10-09 08:15:25 +0000136 // Perform polling and try update clients if changes were detected.
137 if (pollScreen())
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000138 m_server->tryUpdate();
139
140#ifdef DEBUG
141 debugAfterPoll();
142#endif
143}
144
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000145#ifdef DEBUG_REPORT_CHANGED_TILES
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000146#define DBG_REPORT_CHANGES(title) printChanges((title))
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000147#else
148#define DBG_REPORT_CHANGES(title)
149#endif
150
Constantin Kaplinsky9ee8dc62007-10-09 07:46:32 +0000151bool PollingManager::pollScreen()
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000152{
153 if (!m_server)
154 return false;
155
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +0000156 // Clear the m_changeFlags[] array, indicating that no changes have
157 // been detected yet.
158 memset(m_changeFlags, 0, m_numTiles * sizeof(bool));
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000159
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000160 // First pass over the framebuffer. Here we scan 1/32 part of the
161 // framebuffer -- that is, one line in each (32 * m_width) stripe.
162 // We compare the pixels of that line with previous framebuffer
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +0000163 // contents and raise corresponding elements of m_changeFlags[].
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000164 int scanOffset = m_pollingOrder[m_pollingStep++ % 32];
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000165 int nTilesChanged = 0;
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000166 for (int y = scanOffset; y < m_height; y += 32) {
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000167 nTilesChanged += checkRow(0, y, m_width);
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000168 }
169
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000170 DBG_REPORT_CHANGES("After 1st pass");
171
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000172 // If some changes have been detected:
Constantin Kaplinsky48792632007-12-28 18:21:42 +0000173 if (nTilesChanged) {
Constantin Kaplinsky82f7b012008-05-30 10:03:30 +0000174 // Try to find more changes around.
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000175 checkNeighbors();
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000176 DBG_REPORT_CHANGES("After checking neighbors");
Constantin Kaplinsky82f7b012008-05-30 10:03:30 +0000177 // Inform the server about the changes.
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000178 nTilesChanged = sendChanges();
Constantin Kaplinsky48792632007-12-28 18:21:42 +0000179 }
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000180
Constantin Kaplinsky52f29d32007-12-28 18:30:54 +0000181#ifdef DEBUG_PRINT_NUM_CHANGED_TILES
182 printf("%3d ", nTilesChanged);
183 if (m_pollingStep % 32 == 0) {
184 printf("\n");
185 }
186#endif
187
Constantin Kaplinskyc1984e02007-10-08 14:54:18 +0000188#ifdef DEBUG
189 if (nTilesChanged != 0) {
190 fprintf(stderr, "#%d# ", nTilesChanged);
191 }
192#endif
193
Constantin Kaplinsky82f7b012008-05-30 10:03:30 +0000194 return (nTilesChanged != 0);
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000195}
196
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000197int PollingManager::checkRow(int x, int y, int w)
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000198{
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000199 // If necessary, expand the row to the left, to the tile border.
200 // In other words, x must be a multiple of 32.
201 if (x % 32 != 0) {
202 int correction = x % 32;
203 x -= correction;
204 w += correction;
205 }
206
Constantin Kaplinsky41de2422008-01-19 09:03:47 +0000207 // Read a row from the screen into m_rowImage.
208 getRow(x, y, w);
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000209
Constantin Kaplinsky04aa5202008-01-18 15:37:15 +0000210 // Compute a pointer to the initial element of m_changeFlags.
211 bool *pChangeFlags = &m_changeFlags[getTileIndex(x, y)];
212
213 // Compute pointers to image data to be compared.
Constantin Kaplinsky801123d2008-01-18 15:23:11 +0000214 char *ptr_old = m_image->locatePixel(x, y);
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000215 char *ptr_new = m_rowImage->xim->data;
216
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000217 // Compare pixels, raise corresponding elements of m_changeFlags[].
Constantin Kaplinsky86506882008-01-21 10:24:25 +0000218 // First, handle full-size (32 pixels wide) tiles.
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000219 int nTilesChanged = 0;
Constantin Kaplinsky86506882008-01-21 10:24:25 +0000220 int nBytesPerTile = 32 * m_bytesPerPixel;
221 for (int i = 0; i < w / 32; i++) {
222 if (memcmp(ptr_old, ptr_new, nBytesPerTile)) {
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000223 *pChangeFlags = true;
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000224 nTilesChanged++;
225 }
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000226 pChangeFlags++;
Constantin Kaplinsky86506882008-01-21 10:24:25 +0000227 ptr_old += nBytesPerTile;
228 ptr_new += nBytesPerTile;
229 }
230
231 // Handle the rightmost pixels, if the width is not a multiple of 32.
232 int nBytesLeft = (w % 32) * m_bytesPerPixel;
233 if (nBytesLeft != 0) {
234 if (memcmp(ptr_old, ptr_new, nBytesLeft)) {
235 *pChangeFlags = true;
236 nTilesChanged++;
237 }
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000238 }
239
240 return nTilesChanged;
241}
242
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000243int PollingManager::checkColumn(int x, int y, int h, bool *pChangeFlags)
244{
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000245 getColumn(x, y, h);
246
247 int nTilesChanged = 0;
248 for (int nTile = 0; nTile < (h + 31) / 32; nTile++) {
249 if (!*pChangeFlags) {
250 int tile_h = (h - nTile * 32 >= 32) ? 32 : h - nTile * 32;
251 for (int i = 0; i < tile_h; i++) {
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000252 // FIXME: Do not compute these pointers in the inner cycle.
Constantin Kaplinsky801123d2008-01-18 15:23:11 +0000253 char *ptr_old = m_image->locatePixel(x, y + nTile * 32 + i);
254 char *ptr_new = m_columnImage->locatePixel(0, nTile * 32 + i);
Constantin Kaplinskyec45c482008-01-18 14:13:16 +0000255 if (memcmp(ptr_old, ptr_new, m_bytesPerPixel)) {
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000256 *pChangeFlags = true;
257 nTilesChanged++;
258 break;
259 }
260 }
261 }
262 pChangeFlags += m_widthTiles;
263 }
264
265 return nTilesChanged;
266}
267
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000268int PollingManager::sendChanges()
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000269{
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000270 const bool *pChangeFlags = m_changeFlags;
Constantin Kaplinsky1a032112008-01-09 10:22:42 +0000271 int nTilesChanged = 0;
272
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000273 Rect rect;
274 for (int y = 0; y < m_heightTiles; y++) {
275 for (int x = 0; x < m_widthTiles; x++) {
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000276 if (*pChangeFlags++) {
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000277 // Count successive tiles marked as changed.
278 int count = 1;
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000279 while (x + count < m_widthTiles && *pChangeFlags++) {
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000280 count++;
281 }
Constantin Kaplinsky1a032112008-01-09 10:22:42 +0000282 nTilesChanged += count;
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000283 // Compute the coordinates and the size of this band.
284 rect.setXYWH(x * 32, y * 32, count * 32, 32);
285 if (rect.br.x > m_width)
286 rect.br.x = m_width;
287 if (rect.br.y > m_height)
288 rect.br.y = m_height;
289 // Add to the changed region maintained by the server.
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000290 m_server->add_changed(rect);
291 // Skip processed tiles.
292 x += count;
293 }
294 }
295 }
Constantin Kaplinsky1a032112008-01-09 10:22:42 +0000296 return nTilesChanged;
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000297}
298
Constantin Kaplinsky56649982007-09-04 09:15:35 +0000299void
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000300PollingManager::checkNeighbors()
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000301{
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000302 int x, y;
303
304 // Check neighboring pixels above and below changed tiles.
305 // FIXME: Fast skip to the first changed tile (and to the last, too).
306 // FIXME: Check the full-width line above the first changed tile?
307 for (y = 0; y < m_heightTiles; y++) {
308 bool doneAbove = false;
309 bool doneBelow = false;
310 for (x = 0; x < m_widthTiles; x++) {
311 if (!doneAbove && y > 0 &&
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000312 m_changeFlags[y * m_widthTiles + x] &&
313 !m_changeFlags[(y - 1) * m_widthTiles + x]) {
314 // FIXME: Check m_changeFlags[] to decrease height of the row.
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000315 checkRow(x * 32, y * 32 - 1, m_width - x * 32);
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000316 doneAbove = true;
317 }
318 if (!doneBelow && y < m_heightTiles - 1 &&
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000319 m_changeFlags[y * m_widthTiles + x] &&
320 !m_changeFlags[(y + 1) * m_widthTiles + x]) {
321 // FIXME: Check m_changeFlags[] to decrease height of the row.
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000322 checkRow(x * 32, (y + 1) * 32, m_width - x * 32);
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000323 doneBelow = true;
324 }
325 if (doneBelow && doneAbove)
326 break;
327 }
328 }
329
330 // Check neighboring pixels at the right side of changed tiles.
331 for (x = 0; x < m_widthTiles - 1; x++) {
332 for (y = 0; y < m_heightTiles; y++) {
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000333 if (m_changeFlags[y * m_widthTiles + x] &&
334 !m_changeFlags[y * m_widthTiles + x + 1]) {
335 // FIXME: Check m_changeFlags[] to decrease height of the column.
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000336 checkColumn((x + 1) * 32, y * 32, m_height - y * 32,
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000337 &m_changeFlags[y * m_widthTiles + x + 1]);
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000338 break;
339 }
340 }
341 }
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000342
343 // Check neighboring pixels at the left side of changed tiles.
344 for (x = m_widthTiles - 1; x > 0; x--) {
345 for (y = 0; y < m_heightTiles; y++) {
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000346 if (m_changeFlags[y * m_widthTiles + x] &&
347 !m_changeFlags[y * m_widthTiles + x - 1]) {
348 // FIXME: Check m_changeFlags[] to decrease height of the column.
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000349 checkColumn(x * 32 - 1, y * 32, m_height - y * 32,
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000350 &m_changeFlags[y * m_widthTiles + x - 1]);
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000351 break;
352 }
353 }
354 }
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000355}
356
357void
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000358PollingManager::printChanges(const char *header) const
Constantin Kaplinskyf50bd7f2008-01-10 15:27:42 +0000359{
360 fprintf(stderr, "%s:", header);
361
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000362 const bool *pChangeFlags = m_changeFlags;
363
Constantin Kaplinskyf50bd7f2008-01-10 15:27:42 +0000364 for (int y = 0; y < m_heightTiles; y++) {
365 for (int x = 0; x < m_widthTiles; x++) {
366 if (*pChangeFlags++) {
367 // Count successive tiles marked as changed.
368 int count = 1;
369 while (x + count < m_widthTiles && *pChangeFlags++) {
370 count++;
371 }
372 // Print.
373 fprintf(stderr, " (%d,%d)*%d", x, y, count);
374 // Skip processed tiles.
375 x += count;
376 }
377 }
378 }
379
380 fprintf(stderr, "\n");
381}
382