blob: 3c6aab9268a6e60bcb8ced03dbd5cff0b9ac5d1c [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 Kaplinsky14cd5472005-09-29 17:12:11 +0000130 switch((int)pollingType) {
131 case 0:
132 poll_Dumb();
133 break;
134 case 1:
135 poll_Traditional();
136 break;
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +0000137 case 2:
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000138 poll_SkipCycles();
139 break;
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +0000140//case 3:
141 default:
142 poll_DetectVideo();
143 break;
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000144 }
145}
146
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +0000147void PollingManager::poll_DetectVideo()
148{
149 if (!m_server)
150 return;
151
152 const int GRAND_STEP_DIVISOR = 8;
153 const int VIDEO_THRESHOLD_0 = 3;
154 const int VIDEO_THRESHOLD_1 = 5;
155
156 bool grandStep = (m_pollingStep % GRAND_STEP_DIVISOR == 0);
157
158 // FIXME: Save shortcuts in member variables.
159 int scanLine = m_pollingOrder[m_pollingStep++ % 32];
160 int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
161 int bytesPerLine = m_image->xim->bytes_per_line;
162
163 Rect rect;
164 int nTilesChanged = 0;
165 int idx = 0;
166
167 for (int y = 0; y * 32 < m_height; y++) {
168 int tile_h = (m_height - y * 32 >= 32) ? 32 : m_height - y * 32;
169 if (scanLine >= tile_h)
170 break;
171 int scan_y = y * 32 + scanLine;
172 m_rowImage->get(DefaultRootWindow(m_dpy), 0, scan_y);
173 char *ptr_old = m_image->xim->data + scan_y * bytesPerLine;
174 char *ptr_new = m_rowImage->xim->data;
175 for (int x = 0; x * 32 < m_width; x++) {
176 int tile_w = (m_width - x * 32 >= 32) ? 32 : m_width - x * 32;
177 int nBytes = tile_w * bytesPerPixel;
178
179 char wasChanged = (memcmp(ptr_old, ptr_new, nBytes) != 0);
180 m_rateMatrix[idx] += wasChanged;
181
182 if (grandStep) {
183 if (m_rateMatrix[idx] <= VIDEO_THRESHOLD_0) {
184 m_videoFlags[idx] = 0;
185 } else if (m_rateMatrix[idx] >= VIDEO_THRESHOLD_1) {
186 m_videoFlags[idx] = 1;
187 }
188 m_rateMatrix[idx] = 0;
189 }
190
191 m_changedFlags[idx] |= wasChanged;
192 if ( m_changedFlags[idx] && (!m_videoFlags[idx] || grandStep) ) {
193 if (tile_w == 32 && tile_h == 32) {
194 m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32);
195 } else {
196 m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32,
197 tile_w, tile_h);
198 }
199 m_image->updateRect(m_tileImage, x * 32, y * 32);
200 rect.setXYWH(x * 32, y * 32, tile_w, tile_h);
201 m_server->add_changed(rect);
202 nTilesChanged++;
203 m_changedFlags[idx] = 0;
204 }
205
206 if (wasChanged) {
207 }
208
209 ptr_old += nBytes;
210 ptr_new += nBytes;
211 idx++;
212 }
213 }
214
215 if (nTilesChanged)
216 m_server->tryUpdate();
217}
218
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000219void PollingManager::poll_SkipCycles()
220{
221 if (!m_server)
222 return;
223
224 enum {
225 NOT_CHANGED, CHANGED_ONCE, CHANGED_AGAIN
226 };
227
228 bool grandStep = (m_pollingStep % 8 == 0);
229
230 int nTilesChanged = 0;
231 int scanLine = m_pollingOrder[m_pollingStep++ % 32];
232 int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
233 int bytesPerLine = m_image->xim->bytes_per_line;
234 char *pstatus = m_statusMatrix;
235 bool wasChanged;
236 Rect rect;
237
238 for (int y = 0; y * 32 < m_height; y++) {
239 int tile_h = (m_height - y * 32 >= 32) ? 32 : m_height - y * 32;
240 if (scanLine >= tile_h)
241 scanLine %= tile_h;
242 int scan_y = y * 32 + scanLine;
243 m_rowImage->get(DefaultRootWindow(m_dpy), 0, scan_y);
244 char *ptr_old = m_image->xim->data + scan_y * bytesPerLine;
245 char *ptr_new = m_rowImage->xim->data;
246 for (int x = 0; x * 32 < m_width; x++) {
247 int tile_w = (m_width - x * 32 >= 32) ? 32 : m_width - x * 32;
248 int nBytes = tile_w * bytesPerPixel;
249
250 if (grandStep || *pstatus != CHANGED_AGAIN) {
251 wasChanged = (*pstatus == CHANGED_AGAIN) ?
252 true : (memcmp(ptr_old, ptr_new, nBytes) != 0);
253 if (wasChanged) {
254 if (grandStep || *pstatus == NOT_CHANGED) {
255 if (tile_w == 32 && tile_h == 32) {
256 m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32);
257 } else {
258 m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32,
259 tile_w, tile_h);
260 }
261 m_image->updateRect(m_tileImage, x * 32, y * 32);
262 rect.setXYWH(x * 32, y * 32, tile_w, tile_h);
263 m_server->add_changed(rect);
264 nTilesChanged++;
265 *pstatus = CHANGED_ONCE;
266 } else {
267 *pstatus = CHANGED_AGAIN;
268 }
269 } else if (grandStep) {
270 *pstatus = NOT_CHANGED;
271 }
272 }
273
274 ptr_old += nBytes;
275 ptr_new += nBytes;
276 pstatus++;
277 }
278 }
279
280 if (nTilesChanged)
281 m_server->tryUpdate();
282}
283
284void PollingManager::poll_Traditional()
285{
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +0000286 if (!m_server)
287 return;
288
289 int nTilesChanged = 0;
290 int scanLine = m_pollingOrder[m_pollingStep++ % 32];
291 int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
292 int bytesPerLine = m_image->xim->bytes_per_line;
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +0000293 Rect rect;
294
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000295 for (int y = 0; y * 32 < m_height; y++) {
296 int tile_h = (m_height - y * 32 >= 32) ? 32 : m_height - y * 32;
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +0000297 if (scanLine >= tile_h)
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000298 break;
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +0000299 int scan_y = y * 32 + scanLine;
300 m_rowImage->get(DefaultRootWindow(m_dpy), 0, scan_y);
301 char *ptr_old = m_image->xim->data + scan_y * bytesPerLine;
302 char *ptr_new = m_rowImage->xim->data;
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000303 for (int x = 0; x * 32 < m_width; x++) {
304 int tile_w = (m_width - x * 32 >= 32) ? 32 : m_width - x * 32;
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +0000305 int nBytes = tile_w * bytesPerPixel;
306 if (memcmp(ptr_old, ptr_new, nBytes)) {
307 if (tile_w == 32 && tile_h == 32) {
308 m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32);
309 } else {
310 m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32,
311 tile_w, tile_h);
312 }
313 m_image->updateRect(m_tileImage, x * 32, y * 32);
314 rect.setXYWH(x * 32, y * 32, tile_w, tile_h);
315 m_server->add_changed(rect);
316 nTilesChanged++;
317 }
318 ptr_old += nBytes;
319 ptr_new += nBytes;
320 }
321 }
322
323 if (nTilesChanged)
324 m_server->tryUpdate();
325}
326
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000327void PollingManager::poll_Dumb()
328{
329 if (m_server) {
330 m_image->get(DefaultRootWindow(m_dpy));
331 Rect rect(0, 0, m_width, m_height);
332 m_server->add_changed(rect);
333 m_server->tryUpdate();
334 }
335}