blob: 9491d766dbc04b46cc19e61096b8edb57540031c [file] [log] [blame]
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +00001/* Copyright (C) 2004-2005 Constantin Kaplinsky. All Rights Reserved.
2 *
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 */
18//
19// PollingManager.cxx
20//
21
22#include <stdio.h>
23#include <string.h>
24#include <sys/time.h>
25#include <X11/Xlib.h>
26#include <rfb/VNCServer.h>
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +000027#include <rfb/Configuration.h>
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +000028
29#include <x0vncserver/Image.h>
30#include <x0vncserver/PollingManager.h>
31
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +000032IntParameter pollingType("PollingType", "Polling algorithm to use (0..3)", 3);
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +000033
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +000034const int PollingManager::m_pollingOrder[32] = {
35 0, 16, 8, 24, 4, 20, 12, 28,
36 10, 26, 18, 2, 22, 6, 30, 14,
37 1, 17, 9, 25, 7, 23, 15, 31,
38 19, 3, 27, 11, 29, 13, 5, 21
39};
40
41//
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +000042// Constructor.
43//
44// Note that dpy and image should remain valid during the object
45// lifetime, while factory is used only in the constructor itself.
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +000046//
47
48PollingManager::PollingManager(Display *dpy, Image *image,
49 ImageFactory *factory)
50 : m_dpy(dpy), m_server(0), m_image(image), m_pollingStep(0)
51{
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +000052 // Save width and height of the screen (and the image).
53 m_width = m_image->xim->width;
54 m_height = m_image->xim->height;
55
56 // Compute width and height in 32x32 tiles.
57 m_widthTiles = (m_width + 31) / 32;
58 m_heightTiles = (m_height + 31) / 32;
59
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +000060 // Create two additional images used in the polling algorithm.
61 // FIXME: verify that these images use the same pixel format as in m_image.
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +000062 m_rowImage = factory->newImage(m_dpy, m_width, 1);
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +000063 m_tileImage = factory->newImage(m_dpy, 32, 32);
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +000064
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +000065 // FIXME: Extend the comment.
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +000066 // Create a matrix with one byte per each 32x32 tile. It will be
67 // used to limit the rate of updates on continuously-changed screen
68 // areas (like video).
69 int numTiles = m_widthTiles * m_heightTiles;
70 m_statusMatrix = new char[numTiles];
71 memset(m_statusMatrix, 0, numTiles);
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +000072
73 // FIXME: Extend the comment.
74 // Create a matrix with one byte per each 32x32 tile. It will be
75 // used to limit the rate of updates on continuously-changed screen
76 // areas (like video).
77 m_rateMatrix = new char[numTiles];
78 m_videoFlags = new char[numTiles];
79 m_changedFlags = new char[numTiles];
80 memset(m_rateMatrix, 0, numTiles);
81 memset(m_videoFlags, 0, numTiles);
82 memset(m_changedFlags, 0, numTiles);
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +000083}
84
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +000085PollingManager::~PollingManager()
86{
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +000087 delete[] m_changedFlags;
88 delete[] m_videoFlags;
89 delete[] m_rateMatrix;
90
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +000091 delete[] m_statusMatrix;
92}
93
94//
95// Register VNCServer object.
96//
97
98void PollingManager::setVNCServer(VNCServer *s)
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +000099{
100 m_server = s;
101}
102
103//
104// DEBUG: a version of poll() measuring time spent in the function.
105//
106
107void PollingManager::pollDebug()
108{
109 struct timeval timeSaved, timeNow;
110 struct timezone tz;
111 timeSaved.tv_sec = 0;
112 timeSaved.tv_usec = 0;
113 gettimeofday(&timeSaved, &tz);
114
115 poll();
116
117 gettimeofday(&timeNow, &tz);
118 int diff = (int)((timeNow.tv_usec - timeSaved.tv_usec + 500) / 1000 +
119 (timeNow.tv_sec - timeSaved.tv_sec) * 1000);
120 if (diff != 0)
121 fprintf(stderr, "DEBUG: poll(): %4d ms\n", diff);
122}
123
124//
125// Search for changed rectangles on the screen.
126//
127
128void PollingManager::poll()
129{
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000130 bool someChanges = false;
131
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000132 switch((int)pollingType) {
133 case 0:
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000134 someChanges = poll_Dumb();
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000135 break;
136 case 1:
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000137 someChanges = poll_Traditional();
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000138 break;
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +0000139 case 2:
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000140 someChanges = poll_SkipCycles();
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000141 break;
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +0000142//case 3:
143 default:
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000144 someChanges = poll_DetectVideo();
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +0000145 break;
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000146 }
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000147
148 if (someChanges)
149 m_server->tryUpdate();
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000150}
151
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000152bool PollingManager::poll_DetectVideo()
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +0000153{
154 if (!m_server)
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000155 return false;
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +0000156
157 const int GRAND_STEP_DIVISOR = 8;
158 const int VIDEO_THRESHOLD_0 = 3;
159 const int VIDEO_THRESHOLD_1 = 5;
160
161 bool grandStep = (m_pollingStep % GRAND_STEP_DIVISOR == 0);
162
163 // FIXME: Save shortcuts in member variables.
164 int scanLine = m_pollingOrder[m_pollingStep++ % 32];
165 int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
166 int bytesPerLine = m_image->xim->bytes_per_line;
167
168 Rect rect;
169 int nTilesChanged = 0;
170 int idx = 0;
171
172 for (int y = 0; y * 32 < m_height; y++) {
173 int tile_h = (m_height - y * 32 >= 32) ? 32 : m_height - y * 32;
174 if (scanLine >= tile_h)
175 break;
176 int scan_y = y * 32 + scanLine;
177 m_rowImage->get(DefaultRootWindow(m_dpy), 0, scan_y);
178 char *ptr_old = m_image->xim->data + scan_y * bytesPerLine;
179 char *ptr_new = m_rowImage->xim->data;
180 for (int x = 0; x * 32 < m_width; x++) {
181 int tile_w = (m_width - x * 32 >= 32) ? 32 : m_width - x * 32;
182 int nBytes = tile_w * bytesPerPixel;
183
184 char wasChanged = (memcmp(ptr_old, ptr_new, nBytes) != 0);
185 m_rateMatrix[idx] += wasChanged;
186
187 if (grandStep) {
188 if (m_rateMatrix[idx] <= VIDEO_THRESHOLD_0) {
189 m_videoFlags[idx] = 0;
190 } else if (m_rateMatrix[idx] >= VIDEO_THRESHOLD_1) {
191 m_videoFlags[idx] = 1;
192 }
193 m_rateMatrix[idx] = 0;
194 }
195
196 m_changedFlags[idx] |= wasChanged;
197 if ( m_changedFlags[idx] && (!m_videoFlags[idx] || grandStep) ) {
198 if (tile_w == 32 && tile_h == 32) {
199 m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32);
200 } else {
201 m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32,
202 tile_w, tile_h);
203 }
204 m_image->updateRect(m_tileImage, x * 32, y * 32);
205 rect.setXYWH(x * 32, y * 32, tile_w, tile_h);
206 m_server->add_changed(rect);
207 nTilesChanged++;
208 m_changedFlags[idx] = 0;
209 }
210
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +0000211 ptr_old += nBytes;
212 ptr_new += nBytes;
213 idx++;
214 }
215 }
216
Constantin Kaplinsky6df69902006-02-03 11:42:00 +0000217 if (grandStep)
218 adjustVideoArea();
219
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000220 return (nTilesChanged != 0);
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +0000221}
222
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000223bool PollingManager::poll_SkipCycles()
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000224{
225 if (!m_server)
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000226 return false;
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000227
228 enum {
229 NOT_CHANGED, CHANGED_ONCE, CHANGED_AGAIN
230 };
231
232 bool grandStep = (m_pollingStep % 8 == 0);
233
234 int nTilesChanged = 0;
235 int scanLine = m_pollingOrder[m_pollingStep++ % 32];
236 int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
237 int bytesPerLine = m_image->xim->bytes_per_line;
238 char *pstatus = m_statusMatrix;
239 bool wasChanged;
240 Rect rect;
241
242 for (int y = 0; y * 32 < m_height; y++) {
243 int tile_h = (m_height - y * 32 >= 32) ? 32 : m_height - y * 32;
244 if (scanLine >= tile_h)
245 scanLine %= tile_h;
246 int scan_y = y * 32 + scanLine;
247 m_rowImage->get(DefaultRootWindow(m_dpy), 0, scan_y);
248 char *ptr_old = m_image->xim->data + scan_y * bytesPerLine;
249 char *ptr_new = m_rowImage->xim->data;
250 for (int x = 0; x * 32 < m_width; x++) {
251 int tile_w = (m_width - x * 32 >= 32) ? 32 : m_width - x * 32;
252 int nBytes = tile_w * bytesPerPixel;
253
254 if (grandStep || *pstatus != CHANGED_AGAIN) {
255 wasChanged = (*pstatus == CHANGED_AGAIN) ?
256 true : (memcmp(ptr_old, ptr_new, nBytes) != 0);
257 if (wasChanged) {
258 if (grandStep || *pstatus == NOT_CHANGED) {
259 if (tile_w == 32 && tile_h == 32) {
260 m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32);
261 } else {
262 m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32,
263 tile_w, tile_h);
264 }
265 m_image->updateRect(m_tileImage, x * 32, y * 32);
266 rect.setXYWH(x * 32, y * 32, tile_w, tile_h);
267 m_server->add_changed(rect);
268 nTilesChanged++;
269 *pstatus = CHANGED_ONCE;
270 } else {
271 *pstatus = CHANGED_AGAIN;
272 }
273 } else if (grandStep) {
274 *pstatus = NOT_CHANGED;
275 }
276 }
277
278 ptr_old += nBytes;
279 ptr_new += nBytes;
280 pstatus++;
281 }
282 }
283
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000284 return (nTilesChanged != 0);
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000285}
286
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000287bool PollingManager::poll_Traditional()
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000288{
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +0000289 if (!m_server)
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000290 return false;
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +0000291
292 int nTilesChanged = 0;
293 int scanLine = m_pollingOrder[m_pollingStep++ % 32];
294 int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
295 int bytesPerLine = m_image->xim->bytes_per_line;
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +0000296 Rect rect;
297
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000298 for (int y = 0; y * 32 < m_height; y++) {
299 int tile_h = (m_height - y * 32 >= 32) ? 32 : m_height - y * 32;
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +0000300 if (scanLine >= tile_h)
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000301 break;
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +0000302 int scan_y = y * 32 + scanLine;
303 m_rowImage->get(DefaultRootWindow(m_dpy), 0, scan_y);
304 char *ptr_old = m_image->xim->data + scan_y * bytesPerLine;
305 char *ptr_new = m_rowImage->xim->data;
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000306 for (int x = 0; x * 32 < m_width; x++) {
307 int tile_w = (m_width - x * 32 >= 32) ? 32 : m_width - x * 32;
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +0000308 int nBytes = tile_w * bytesPerPixel;
309 if (memcmp(ptr_old, ptr_new, nBytes)) {
310 if (tile_w == 32 && tile_h == 32) {
311 m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32);
312 } else {
313 m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32,
314 tile_w, tile_h);
315 }
316 m_image->updateRect(m_tileImage, x * 32, y * 32);
317 rect.setXYWH(x * 32, y * 32, tile_w, tile_h);
318 m_server->add_changed(rect);
319 nTilesChanged++;
320 }
321 ptr_old += nBytes;
322 ptr_new += nBytes;
323 }
324 }
325
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000326 return (nTilesChanged != 0);
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +0000327}
328
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000329//
330// Simplest polling method, from the original x0vncserver of VNC4.
331//
332
333bool PollingManager::poll_Dumb()
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000334{
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000335 if (!m_server)
336 return false;
337
338 m_image->get(DefaultRootWindow(m_dpy));
339 Rect rect(0, 0, m_width, m_height);
340 m_server->add_changed(rect);
341
342 // As we have no idea if any pixels were changed,
343 // always report that some changes have been detected.
344 return true;
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000345}
Constantin Kaplinsky6df69902006-02-03 11:42:00 +0000346
347//
348// FIXME: Replace with a normal comment.
349// Make video area pattern more regular.
350//
351
352// FIXME: Is it efficient enough?
353void PollingManager::adjustVideoArea()
354{
355 char newFlags[m_widthTiles * m_heightTiles];
356 char *ptr = newFlags;
357 int x, y;
358
359 // DEBUG:
360 // int nVideoTiles = 0;
361
362 for (y = 0; y < m_heightTiles; y++) {
363 for (x = 0; x < m_widthTiles; x++) {
364
365 // DEBUG:
366 // nVideoTiles += m_videoFlags[y * m_widthTiles + x];
367
368 int weightedSum = 0, n;
369 if (y > 0 && x > 0) {
370 n = (m_videoFlags[ y * m_widthTiles + (x-1)] +
371 m_videoFlags[(y-1) * m_widthTiles + (x-1)] +
372 m_videoFlags[(y-1) * m_widthTiles + x ]);
373 if (n == 3) {
374 *ptr++ = 1;
375 continue;
376 }
377 weightedSum += n;
378 }
379 if (y > 0 && x < m_widthTiles - 1) {
380 n = (m_videoFlags[ y * m_widthTiles + (x+1)] +
381 m_videoFlags[(y-1) * m_widthTiles + (x+1)] +
382 m_videoFlags[(y-1) * m_widthTiles + x ]);
383 if (n == 3) {
384 *ptr++ = 1;
385 continue;
386 }
387 weightedSum += n;
388 }
389 if (y < m_heightTiles - 1 && x > 0) {
390 n = (m_videoFlags[ y * m_widthTiles + (x-1)] +
391 m_videoFlags[(y+1) * m_widthTiles + (x-1)] +
392 m_videoFlags[(y+1) * m_widthTiles + x ]);
393 if (n == 3) {
394 *ptr++ = 1;
395 continue;
396 }
397 weightedSum += n;
398 }
399 if (y < m_heightTiles - 1 && x < m_widthTiles - 1) {
400 n = (m_videoFlags[ y * m_widthTiles + (x+1)] +
401 m_videoFlags[(y+1) * m_widthTiles + (x+1)] +
402 m_videoFlags[(y+1) * m_widthTiles + x ]);
403 if (n == 3) {
404 *ptr++ = 1;
405 continue;
406 }
407 weightedSum += n;
408 }
409 *ptr++ = (weightedSum <= 3) ? 0 : m_videoFlags[y * m_widthTiles + x];
410 }
411 }
412
413 /*
414 /// DEBUG: ------------------------------------------------------
415 if (nVideoTiles) {
416 for (y = 0; y < m_heightTiles; y++) {
417 for (x = 0; x < m_widthTiles; x++) {
418 printf("%c", m_videoFlags[y * m_widthTiles + x] ? '@' : ':');
419 }
420 printf(" ");
421 for (x = 0; x < m_widthTiles; x++) {
422 printf("%c", newFlags[y * m_widthTiles + x] ? '@' : ':');
423 }
424 printf("\n");
425 }
426 printf("\n");
427 }
428 /// -------------------------------------------------------------
429 */
430
431 memcpy(m_videoFlags, newFlags, m_widthTiles * m_heightTiles);
432}