blob: 93dce132ccfb7d0ee6bb40c51fed60364f294d37 [file] [log] [blame]
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +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// FIXME: Don't compare pixels already marked as changed.
23// FIXME: Use Image::copyPixels() instead of Image::updateRect()?
24// In that case, note the fact that arguments are not checked.
25
26#include <stdio.h>
27#include <string.h>
28#include <time.h>
29#include <X11/Xlib.h>
30#include <rfb/LogWriter.h>
31#include <rfb/VNCServer.h>
32#include <rfb/Configuration.h>
33#include <rfb/ServerCore.h>
34
35#include <x0vncserver/PollingManager.h>
36
37static LogWriter vlog("PollingMgr");
38
39BoolParameter PollingManager::pollPointer
40("PollPointer",
41 "DEBUG: Poll area under the pointer with higher priority",
Constantin Kaplinsky344c3fe2007-07-24 08:35:43 +000042 false);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000043
44IntParameter PollingManager::pollingType
45("PollingType",
Constantin Kaplinskya119b482007-10-04 06:02:02 +000046 "DEBUG: Select particular polling algorithm (0..4)",
47 4);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000048
49const int PollingManager::m_pollingOrder[32] = {
50 0, 16, 8, 24, 4, 20, 12, 28,
51 10, 26, 18, 2, 22, 6, 30, 14,
52 1, 17, 9, 25, 7, 23, 15, 31,
53 19, 3, 27, 11, 29, 13, 5, 21
54};
55
56//
57// Constructor.
58//
59// Note that dpy and image should remain valid during the object
60// lifetime, while factory is used only in the constructor itself.
61//
62
63PollingManager::PollingManager(Display *dpy, Image *image,
64 ImageFactory *factory,
65 int offsetLeft, int offsetTop)
66 : m_dpy(dpy), m_server(0), m_image(image),
67 m_offsetLeft(offsetLeft), m_offsetTop(offsetTop),
68 m_pointerPosKnown(false), m_pollingStep(0)
69{
70 // Save width and height of the screen (and the image).
71 m_width = m_image->xim->width;
72 m_height = m_image->xim->height;
73
74 // Compute width and height in 32x32 tiles.
75 m_widthTiles = (m_width + 31) / 32;
76 m_heightTiles = (m_height + 31) / 32;
77
78 // Get initial screen image.
79 m_image->get(DefaultRootWindow(m_dpy), m_offsetLeft, m_offsetTop);
80
81 // Create additional images used in polling algorithms, warn if
82 // underlying class names are different from the class name of the
83 // primary image.
84 m_rowImage = factory->newImage(m_dpy, m_width, 1);
85 m_tileImage = factory->newImage(m_dpy, 32, 32);
86 m_areaImage = factory->newImage(m_dpy, 128, 128);
87 if (strcmp(m_image->className(), m_rowImage->className()) != 0 ||
88 strcmp(m_image->className(), m_tileImage->className()) != 0 ||
89 strcmp(m_image->className(), m_areaImage->className()) != 0) {
90 vlog.error("Image types do not match (%s, %s, %s)",
91 m_rowImage->className(),
92 m_tileImage->className(),
93 m_areaImage->className());
94 }
95
96 // FIXME: Extend the comment.
97 // Create a matrix with one byte per each 32x32 tile. It will be
98 // used to limit the rate of updates on continuously-changed screen
99 // areas (like video).
100 int numTiles = m_widthTiles * m_heightTiles;
101 m_statusMatrix = new char[numTiles];
102 memset(m_statusMatrix, 0, numTiles);
103
104 // FIXME: Extend the comment.
105 // Create a matrix with one byte per each 32x32 tile. It will be
106 // used to limit the rate of updates on continuously-changed screen
107 // areas (like video).
108 m_rateMatrix = new char[numTiles];
109 m_videoFlags = new char[numTiles];
110 m_changedFlags = new char[numTiles];
111 memset(m_rateMatrix, 0, numTiles);
112 memset(m_videoFlags, 0, numTiles);
113 memset(m_changedFlags, 0, numTiles);
114}
115
116PollingManager::~PollingManager()
117{
118 delete[] m_changedFlags;
119 delete[] m_videoFlags;
120 delete[] m_rateMatrix;
121
122 delete[] m_statusMatrix;
123
124 delete m_areaImage;
125 delete m_tileImage;
126 delete m_rowImage;
127}
128
129//
130// Register VNCServer object.
131//
132
133void PollingManager::setVNCServer(VNCServer *s)
134{
135 m_server = s;
136}
137
138//
139// Update current pointer position which may be used as a hint for
140// polling algorithms.
141//
142
143void PollingManager::setPointerPos(const Point &pos)
144{
145 m_pointerPosTime = time(NULL);
146 m_pointerPos = pos;
147 m_pointerPosKnown = true;
148}
149
150//
151// Indicate that current pointer position is unknown.
152//
153
154void PollingManager::unsetPointerPos()
155{
156 m_pointerPosKnown = false;
157}
158
159//
160// DEBUG: Measuring time spent in the poll() function,
161// as well as time intervals between poll() calls.
162//
163
164#ifdef DEBUG
165void PollingManager::debugBeforePoll()
166{
167 TimeMillis timeNow;
168 int diff = timeNow.diffFrom(m_timeSaved);
169 fprintf(stderr, "[wait%4dms]\t[step %2d]\t", diff, m_pollingStep % 32);
170 m_timeSaved = timeNow;
171}
172
173void PollingManager::debugAfterPoll()
174{
175 TimeMillis timeNow;
176 int diff = timeNow.diffFrom(m_timeSaved);
177 fprintf(stderr, "[poll%4dms]\n", diff);
178 m_timeSaved = timeNow;
179}
180
181#endif
182
183//
184// Search for changed rectangles on the screen.
185//
186
187void PollingManager::poll()
188{
189#ifdef DEBUG
190 debugBeforePoll();
191#endif
192
193 // First step: full-screen polling.
194
195 bool changes1 = false;
196
197 switch((int)pollingType) {
198 case 0:
199 changes1 = poll_Dumb();
200 break;
201 case 1:
202 changes1 = poll_Traditional();
203 break;
204 case 2:
205 changes1 = poll_SkipCycles();
206 break;
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000207 case 3:
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000208 changes1 = poll_DetectVideo();
209 break;
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000210//case 4:
211 default:
212 changes1 = poll_New();
213 break;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000214 }
215
216 // Second step: optional thorough polling of the area around the pointer.
217 // We do that only if the pointer position is known and was set recently.
218
219 bool changes2 = false;
220 if (pollPointer) {
221 if (m_pointerPosKnown && time(NULL) - m_pointerPosTime >= 5) {
222 unsetPointerPos();
223 }
224 if (m_pointerPosKnown) {
225 changes2 = pollPointerArea();
226 }
227 }
228
229 // Update if needed.
230
231 if (changes1 || changes2)
232 m_server->tryUpdate();
233
234#ifdef DEBUG
235 debugAfterPoll();
236#endif
237}
238
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000239bool PollingManager::poll_New()
240{
241 if (!m_server)
242 return false;
243
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000244 // mxChanged[] array will hold boolean values corresponding to each
245 // 32x32 tile. If a value is true, then we've detected a change in
Constantin Kaplinsky5e2f69f2007-10-07 13:08:18 +0000246 // that tile. Initially, we fill in the array with zero values.
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000247 bool *mxChanged = new bool[m_widthTiles * m_heightTiles];
Constantin Kaplinskyf25307a2007-10-08 13:49:44 +0000248 memset(mxChanged, 0, m_widthTiles * m_heightTiles * sizeof(bool));
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000249
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000250 // First pass over the framebuffer. Here we scan 1/32 part of the
251 // framebuffer -- that is, one line in each (32 * m_width) stripe.
252 // We compare the pixels of that line with previous framebuffer
253 // contents and raise corresponding member values of mxChanged[].
254 int scanOffset = m_pollingOrder[m_pollingStep++ % 32];
255 bool *pmxChanged = mxChanged;
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000256 int nTilesChanged = 0;
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000257 for (int y = scanOffset; y < m_height; y += 32) {
258 nTilesChanged += checkRow(0, y, m_width, pmxChanged);
259 pmxChanged += m_widthTiles;
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000260 }
261
Constantin Kaplinskyc1984e02007-10-08 14:54:18 +0000262 // Do the work related to video area detection.
263 bool videoDetected = detectVideo(mxChanged);
264
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000265 // Inform the server about the changes.
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000266 if (nTilesChanged)
267 sendChanges(mxChanged);
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000268
269 // Cleanup.
270 delete[] mxChanged;
271
Constantin Kaplinskyc1984e02007-10-08 14:54:18 +0000272#ifdef DEBUG
273 if (nTilesChanged != 0) {
274 fprintf(stderr, "#%d# ", nTilesChanged);
275 }
276#endif
277
278 return (nTilesChanged != 0 || videoDetected);
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000279}
280
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000281int PollingManager::checkRow(int x, int y, int w, bool *pmxChanged)
282{
283 int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
284 int bytesPerLine = m_image->xim->bytes_per_line;
285
Constantin Kaplinsky29d32052007-10-08 14:16:02 +0000286 if (x == 0 && w == m_width) {
287 getFullRow(y); // use more efficient method if possible
288 } else {
289 getRow(x, y, w);
290 }
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000291
292 char *ptr_old = m_image->xim->data + y * bytesPerLine + x * bytesPerPixel;
293 char *ptr_new = m_rowImage->xim->data;
294
295 int nTilesChanged = 0;
296 for (int i = 0; i < (w + 31) / 32; i++) {
297 int tile_w = (w - i * 32 >= 32) ? 32 : w - i * 32;
298 int nBytes = tile_w * bytesPerPixel;
299 if (memcmp(ptr_old, ptr_new, nBytes)) {
300 *pmxChanged = true;
301 nTilesChanged++;
302 }
303 pmxChanged++;
304 ptr_old += nBytes;
305 ptr_new += nBytes;
306 }
307
308 return nTilesChanged;
309}
310
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000311void PollingManager::sendChanges(bool *pmxChanged)
312{
313 Rect rect;
314 for (int y = 0; y < m_heightTiles; y++) {
315 for (int x = 0; x < m_widthTiles; x++) {
316 if (*pmxChanged++) {
317 // Count successive tiles marked as changed.
318 int count = 1;
319 while (x + count < m_widthTiles && *pmxChanged++) {
320 count++;
321 }
322 // Compute the coordinates and the size of this band.
323 rect.setXYWH(x * 32, y * 32, count * 32, 32);
324 if (rect.br.x > m_width)
325 rect.br.x = m_width;
326 if (rect.br.y > m_height)
327 rect.br.y = m_height;
328 // Add to the changed region maintained by the server.
329 getScreenRect(rect);
330 m_server->add_changed(rect);
331 // Skip processed tiles.
332 x += count;
333 }
334 }
335 }
336}
337
Constantin Kaplinskyc1984e02007-10-08 14:54:18 +0000338bool PollingManager::detectVideo(bool *pmxChanged)
339{
340 // Configurable parameters.
341 const int VIDEO_THRESHOLD_0 = 3;
342 const int VIDEO_THRESHOLD_1 = 5;
343
344 // Each call: update counters in m_rateMatrix.
345 int numTiles = m_heightTiles * m_widthTiles;
346 for (int i = 0; i < numTiles; i++)
347 m_rateMatrix[i] += (pmxChanged[i] != false);
348
349 // Once per eight calls: detect video region. In other words, mark a
350 // region that consists of continuously changing pixels. Save the
351 // result in m_videoFlags[] and reset counters in m_rateMatrix[].
352 bool isGrandStep = (m_pollingStep % 8 == 0);
353 if (isGrandStep) {
354 for (int i = 0; i < numTiles; i++) {
355 if (m_rateMatrix[i] <= VIDEO_THRESHOLD_0) {
356 m_videoFlags[i] = 0;
357 } else if (m_rateMatrix[i] >= VIDEO_THRESHOLD_1) {
358 m_videoFlags[i] = 1;
359 }
360 m_rateMatrix[i] = 0;
361 }
362 }
363
364 // Choose the biggest rectangle from the region defined by
365 // m_videoFlags[].
366 Rect r;
367 getVideoAreaRect(&r);
368
369 // Exclude video rectangle from pmxChanged[].
370 for (int y = r.tl.y / 32; y < r.br.y / 32; y++) {
371 for (int x = r.tl.x / 32; x < r.br.x / 32; x++) {
372 pmxChanged[y * m_widthTiles + x] = false;
373 }
374 }
375
376 // Inform the server...
377 m_server->set_video_area(r);
378 if (!r.is_empty())
379 getScreenRect(r);
380
381 return (!r.is_empty());
382}
383
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000384bool PollingManager::poll_DetectVideo()
385{
386 if (!m_server)
387 return false;
388
389 const int GRAND_STEP_DIVISOR = 8;
390 const int VIDEO_THRESHOLD_0 = 3;
391 const int VIDEO_THRESHOLD_1 = 5;
392
393 bool grandStep = (m_pollingStep % GRAND_STEP_DIVISOR == 0);
394
395 // FIXME: Save shortcuts in member variables?
396 int scanLine = m_pollingOrder[m_pollingStep++ % 32];
397 int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
398 int bytesPerLine = m_image->xim->bytes_per_line;
399
400 Rect rect;
401 int nTilesChanged = 0;
402 int idx = 0;
403
404 for (int y = 0; y * 32 < m_height; y++) {
405 int tile_h = (m_height - y * 32 >= 32) ? 32 : m_height - y * 32;
406 if (scanLine >= tile_h)
407 break;
408 int scan_y = y * 32 + scanLine;
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000409 getFullRow(scan_y);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000410 char *ptr_old = m_image->xim->data + scan_y * bytesPerLine;
411 char *ptr_new = m_rowImage->xim->data;
412 for (int x = 0; x * 32 < m_width; x++) {
413 int tile_w = (m_width - x * 32 >= 32) ? 32 : m_width - x * 32;
414 int nBytes = tile_w * bytesPerPixel;
415
416 char wasChanged = (memcmp(ptr_old, ptr_new, nBytes) != 0);
417 m_rateMatrix[idx] += wasChanged;
418
419 if (grandStep) {
420 if (m_rateMatrix[idx] <= VIDEO_THRESHOLD_0) {
421 m_videoFlags[idx] = 0;
422 } else if (m_rateMatrix[idx] >= VIDEO_THRESHOLD_1) {
423 m_videoFlags[idx] = 1;
424 }
425 m_rateMatrix[idx] = 0;
426 }
427
428 m_changedFlags[idx] |= wasChanged;
429 if ( m_changedFlags[idx] && (!m_videoFlags[idx] || grandStep) ) {
430 getTile32(x, y, tile_w, tile_h);
431 m_image->updateRect(m_tileImage, x * 32, y * 32);
432 rect.setXYWH(x * 32, y * 32, tile_w, tile_h);
433 m_server->add_changed(rect);
434 nTilesChanged++;
435 m_changedFlags[idx] = 0;
436 }
437
438 ptr_old += nBytes;
439 ptr_new += nBytes;
440 idx++;
441 }
442 }
443
444 if (grandStep)
445 adjustVideoArea();
446
Constantin Kaplinsky56649982007-09-04 09:15:35 +0000447 bool videoDetected = false;
448 Rect r;
449 getVideoAreaRect(&r);
450 m_server->set_video_area(r);
451 videoDetected = !r.is_empty();
452 if (videoDetected) {
453 m_image->get(DefaultRootWindow(m_dpy),
454 m_offsetLeft + r.tl.x, m_offsetTop + r.tl.y,
455 r.width(), r.height(), r.tl.x, r.tl.y);
456 }
457
Constantin Kaplinsky344c3fe2007-07-24 08:35:43 +0000458#ifdef DEBUG
459 if (nTilesChanged != 0) {
460 fprintf(stderr, "#%d# ", nTilesChanged);
461 }
462#endif
463
Constantin Kaplinsky56649982007-09-04 09:15:35 +0000464 return (nTilesChanged != 0 || videoDetected);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000465}
466
467bool PollingManager::poll_SkipCycles()
468{
469 if (!m_server)
470 return false;
471
472 enum {
473 NOT_CHANGED, CHANGED_ONCE, CHANGED_AGAIN
474 };
475
476 bool grandStep = (m_pollingStep % 8 == 0);
477
478 int nTilesChanged = 0;
479 int scanLine = m_pollingOrder[m_pollingStep++ % 32];
480 int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
481 int bytesPerLine = m_image->xim->bytes_per_line;
482 char *pstatus = m_statusMatrix;
483 bool wasChanged;
484 Rect rect;
485
486 for (int y = 0; y * 32 < m_height; y++) {
487 int tile_h = (m_height - y * 32 >= 32) ? 32 : m_height - y * 32;
488 if (scanLine >= tile_h)
489 scanLine %= tile_h;
490 int scan_y = y * 32 + scanLine;
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000491 getFullRow(scan_y);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000492 char *ptr_old = m_image->xim->data + scan_y * bytesPerLine;
493 char *ptr_new = m_rowImage->xim->data;
494 for (int x = 0; x * 32 < m_width; x++) {
495 int tile_w = (m_width - x * 32 >= 32) ? 32 : m_width - x * 32;
496 int nBytes = tile_w * bytesPerPixel;
497
498 if (grandStep || *pstatus != CHANGED_AGAIN) {
499 wasChanged = (*pstatus == CHANGED_AGAIN) ?
500 true : (memcmp(ptr_old, ptr_new, nBytes) != 0);
501 if (wasChanged) {
502 if (grandStep || *pstatus == NOT_CHANGED) {
503 getTile32(x, y, tile_w, tile_h);
504 m_image->updateRect(m_tileImage, x * 32, y * 32);
505 rect.setXYWH(x * 32, y * 32, tile_w, tile_h);
506 m_server->add_changed(rect);
507 nTilesChanged++;
508 *pstatus = CHANGED_ONCE;
509 } else {
510 *pstatus = CHANGED_AGAIN;
511 }
512 } else if (grandStep) {
513 *pstatus = NOT_CHANGED;
514 }
515 }
516
517 ptr_old += nBytes;
518 ptr_new += nBytes;
519 pstatus++;
520 }
521 }
522
523 return (nTilesChanged != 0);
524}
525
526bool PollingManager::poll_Traditional()
527{
528 if (!m_server)
529 return false;
530
531 int nTilesChanged = 0;
532 int scanLine = m_pollingOrder[m_pollingStep++ % 32];
533 int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
534 int bytesPerLine = m_image->xim->bytes_per_line;
535 Rect rect;
536
537 for (int y = 0; y * 32 < m_height; y++) {
538 int tile_h = (m_height - y * 32 >= 32) ? 32 : m_height - y * 32;
539 if (scanLine >= tile_h)
540 break;
541 int scan_y = y * 32 + scanLine;
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000542 getFullRow(scan_y);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000543 char *ptr_old = m_image->xim->data + scan_y * bytesPerLine;
544 char *ptr_new = m_rowImage->xim->data;
545 for (int x = 0; x * 32 < m_width; x++) {
546 int tile_w = (m_width - x * 32 >= 32) ? 32 : m_width - x * 32;
547 int nBytes = tile_w * bytesPerPixel;
548 if (memcmp(ptr_old, ptr_new, nBytes)) {
549 getTile32(x, y, tile_w, tile_h);
550 m_image->updateRect(m_tileImage, x * 32, y * 32);
551 rect.setXYWH(x * 32, y * 32, tile_w, tile_h);
552 m_server->add_changed(rect);
553 nTilesChanged++;
554 }
555 ptr_old += nBytes;
556 ptr_new += nBytes;
557 }
558 }
559
560 return (nTilesChanged != 0);
561}
562
563//
564// Simplest polling method, from the original x0vncserver of VNC4.
565//
566
567bool PollingManager::poll_Dumb()
568{
569 if (!m_server)
570 return false;
571
572 getScreen();
573 Rect rect(0, 0, m_width, m_height);
574 m_server->add_changed(rect);
575
576 // Report that some changes have been detected.
577 return true;
578}
579
580//
581// Compute coordinates of the rectangle around the pointer.
582//
583// ASSUMES: (m_pointerPosKnown != false)
584//
585
586void PollingManager::computePointerArea(Rect *r)
587{
588 int x = m_pointerPos.x - 64;
589 int y = m_pointerPos.y - 64;
590 int w = 128;
591 int h = 128;
592 if (x < 0) {
593 w += x; x = 0;
594 }
595 if (x + w > m_width) {
596 w = m_width - x;
597 }
598 if (y < 0) {
599 h += y; y = 0;
600 }
601 if (y + h > m_height) {
602 h = m_height - y;
603 }
604
605 r->setXYWH(x, y, w, h);
606}
607
608//
609// Poll the area under current pointer position. Each pixel of the
610// area should be compared. Using such polling option gives higher
611// priority to screen area under the pointer.
612//
613// ASSUMES: (m_server != NULL && m_pointerPosKnown != false)
614//
615
616bool PollingManager::pollPointerArea()
617{
618 Rect r;
619 computePointerArea(&r);
620
621 // Shortcuts for coordinates.
622 int x = r.tl.x, y = r.tl.y;
623 int w = r.width(), h = r.height();
624
625 // Get new pixels.
626 getArea128(x, y, w, h);
627
628 // Now, try to minimize the rectangle by cutting out unchanged
629 // borders (at top and bottom).
630 //
631 // FIXME: Perhaps we should work on 32x32 tiles (properly aligned)
632 // to produce a region instead of a rectangle. If there would
633 // be just one universal polling algorithm, it could be
634 // better to integrate pointer area polling into that
635 // algorithm, instead of a separate pollPointerArea()
636 // function.
637
638 // Shortcuts.
639 int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
640 int oldBytesPerLine = m_image->xim->bytes_per_line;
641 int newBytesPerLine = m_areaImage->xim->bytes_per_line;
642 char *oldPtr = m_image->xim->data + y * oldBytesPerLine + x * bytesPerPixel;
643 char *newPtr = m_areaImage->xim->data;
644
645 // Check and cut out unchanged rows at the top.
646 int ty;
647 for (ty = 0; ty < h; ty++) {
648 if (memcmp(oldPtr, newPtr, w * bytesPerPixel) != 0)
649 break;
650 oldPtr += oldBytesPerLine;
651 newPtr += newBytesPerLine;
652 }
653 if (ty == h) {
654 return false; // no changes at all
655 }
656 y += ty; h -= ty;
657
658 // Check and cut out unchanged rows at the bottom.
659 oldPtr = m_image->xim->data + (y+h-1) * oldBytesPerLine + x * bytesPerPixel;
660 newPtr = m_areaImage->xim->data + (ty+h-1) * newBytesPerLine;
661 int by;
662 for (by = 0; by < h - 1; by++) {
663 if (memcmp(oldPtr, newPtr, w * bytesPerPixel) != 0)
664 break;
665 oldPtr -= oldBytesPerLine;
666 newPtr -= newBytesPerLine;
667 }
668 h -= by;
669
670 // Copy pixels.
671 m_image->updateRect(m_areaImage, x, y, 0, ty, w, h);
672
673 // Report updates to the server.
674 Rect rect(x, y, x+w, y+h);
675 m_server->add_changed(rect);
676 return true;
677}
678
679//
680// Make video area pattern more regular.
681//
682// FIXME: Replace the above with a normal comment.
683// FIXME: Is the function efficient enough?
684//
685
686void PollingManager::adjustVideoArea()
687{
688 char newFlags[m_widthTiles * m_heightTiles];
689 char *ptr = newFlags;
690 int x, y;
691
692 // DEBUG:
693 // int nVideoTiles = 0;
694
695 for (y = 0; y < m_heightTiles; y++) {
696 for (x = 0; x < m_widthTiles; x++) {
697
698 // DEBUG:
699 // nVideoTiles += m_videoFlags[y * m_widthTiles + x];
700
701 int weightedSum = 0, n;
702 if (y > 0 && x > 0) {
703 n = (m_videoFlags[ y * m_widthTiles + (x-1)] +
704 m_videoFlags[(y-1) * m_widthTiles + (x-1)] +
705 m_videoFlags[(y-1) * m_widthTiles + x ]);
706 if (n == 3) {
707 *ptr++ = 1;
708 continue;
709 }
710 weightedSum += n;
711 }
712 if (y > 0 && x < m_widthTiles - 1) {
713 n = (m_videoFlags[ y * m_widthTiles + (x+1)] +
714 m_videoFlags[(y-1) * m_widthTiles + (x+1)] +
715 m_videoFlags[(y-1) * m_widthTiles + x ]);
716 if (n == 3) {
717 *ptr++ = 1;
718 continue;
719 }
720 weightedSum += n;
721 }
722 if (y < m_heightTiles - 1 && x > 0) {
723 n = (m_videoFlags[ y * m_widthTiles + (x-1)] +
724 m_videoFlags[(y+1) * m_widthTiles + (x-1)] +
725 m_videoFlags[(y+1) * m_widthTiles + x ]);
726 if (n == 3) {
727 *ptr++ = 1;
728 continue;
729 }
730 weightedSum += n;
731 }
732 if (y < m_heightTiles - 1 && x < m_widthTiles - 1) {
733 n = (m_videoFlags[ y * m_widthTiles + (x+1)] +
734 m_videoFlags[(y+1) * m_widthTiles + (x+1)] +
735 m_videoFlags[(y+1) * m_widthTiles + x ]);
736 if (n == 3) {
737 *ptr++ = 1;
738 continue;
739 }
740 weightedSum += n;
741 }
742 *ptr++ = (weightedSum <= 3) ? 0 : m_videoFlags[y * m_widthTiles + x];
743 }
744 }
745
746 /*
747 /// DEBUG: ------------------------------------------------------
748 if (nVideoTiles) {
749 for (y = 0; y < m_heightTiles; y++) {
750 for (x = 0; x < m_widthTiles; x++) {
751 printf("%c", m_videoFlags[y * m_widthTiles + x] ? '@' : ':');
752 }
753 printf(" ");
754 for (x = 0; x < m_widthTiles; x++) {
755 printf("%c", newFlags[y * m_widthTiles + x] ? '@' : ':');
756 }
757 printf("\n");
758 }
759 printf("\n");
760 }
761 /// -------------------------------------------------------------
762 */
763
764 memcpy(m_videoFlags, newFlags, m_widthTiles * m_heightTiles);
765}
Constantin Kaplinsky56649982007-09-04 09:15:35 +0000766
767void
768PollingManager::getVideoAreaRect(Rect *result)
769{
Constantin Kaplinsky0fc9f172007-09-29 04:00:02 +0000770 int *mx_hlen, *mx_vlen;
771 constructLengthMatrices(&mx_hlen, &mx_vlen);
Constantin Kaplinsky56649982007-09-04 09:15:35 +0000772
Constantin Kaplinsky0fc9f172007-09-29 04:00:02 +0000773 int full_h = m_heightTiles;
774 int full_w = m_widthTiles;
775 int x, y;
776 Rect max_rect(0, 0, 0, 0);
777 Rect local_rect;
778
779 for (y = 0; y < full_h; y++) {
780 for (x = 0; x < full_w; x++) {
781 int max_w = mx_hlen[y * full_w + x];
782 int max_h = mx_vlen[y * full_w + x];
783 if (max_w > 2 && max_h > 1 && max_h * max_w > (int)max_rect.area()) {
784 local_rect.tl.x = x;
785 local_rect.tl.y = y;
786 findMaxLocalRect(&local_rect, mx_hlen, mx_vlen);
787 if (local_rect.area() > max_rect.area()) {
788 max_rect = local_rect;
789 }
790 }
Constantin Kaplinsky56649982007-09-04 09:15:35 +0000791 }
792 }
793
Constantin Kaplinsky0fc9f172007-09-29 04:00:02 +0000794 destroyLengthMatrices(mx_hlen, mx_vlen);
Constantin Kaplinsky56649982007-09-04 09:15:35 +0000795
Constantin Kaplinsky0fc9f172007-09-29 04:00:02 +0000796 max_rect.tl.x *= 32;
797 max_rect.tl.y *= 32;
798 max_rect.br.x *= 32;
799 max_rect.br.y *= 32;
800 if (max_rect.br.x > m_width)
801 max_rect.br.x = m_width;
802 if (max_rect.br.y > m_height)
803 max_rect.br.y = m_height;
804 *result = max_rect;
Constantin Kaplinsky56649982007-09-04 09:15:35 +0000805
Constantin Kaplinsky0fc9f172007-09-29 04:00:02 +0000806 if (!result->is_empty()) {
Constantin Kaplinsky56649982007-09-04 09:15:35 +0000807 fprintf(stderr, "Video rect %dx%d\tat(%d,%d)\n",
808 result->width(), result->height(), result->tl.x, result->tl.y);
809 }
810}
Constantin Kaplinsky0fc9f172007-09-29 04:00:02 +0000811
812void
813PollingManager::constructLengthMatrices(int **pmx_h, int **pmx_v)
814{
815 // Handy shortcuts.
816 int h = m_heightTiles;
817 int w = m_widthTiles;
818
819 // Allocate memory.
820 int *mx_h = new int[h * w];
821 memset(mx_h, 0, h * w * sizeof(int));
822 int *mx_v = new int[h * w];
823 memset(mx_v, 0, h * w * sizeof(int));
824
825 int x, y, len, i;
826
827 // Fill in horizontal length matrix.
828 for (y = 0; y < h; y++) {
829 for (x = 0; x < w; x++) {
830 len = 0;
831 while (x + len < w && m_videoFlags[y * w + x + len]) {
832 len++;
833 }
834 for (i = 0; i < len; i++) {
835 mx_h[y * w + x + i] = len - i;
836 }
837 x += len;
838 }
839 }
840
841 // Fill in vertical length matrix.
842 for (x = 0; x < w; x++) {
843 for (y = 0; y < h; y++) {
844 len = 0;
845 while (y + len < h && m_videoFlags[(y + len) * w + x]) {
846 len++;
847 }
848 for (i = 0; i < len; i++) {
849 mx_v[(y + i) * w + x] = len - i;
850 }
851 y += len;
852 }
853 }
854
855 *pmx_h = mx_h;
856 *pmx_v = mx_v;
857}
858
859void
860PollingManager::destroyLengthMatrices(int *mx_h, int *mx_v)
861{
862 delete[] mx_h;
863 delete[] mx_v;
864}
865
866// NOTE: This function assumes that current tile has non-zero in mx_h[],
867// otherwise we get division by zero.
868void
869PollingManager::findMaxLocalRect(Rect *r, int mx_h[], int mx_v[])
870{
871 int idx = r->tl.y * m_widthTiles + r->tl.x;
872
873 // NOTE: Rectangle's maximum width and height are 25 and 18
874 // (in tiles, where each tile is usually 32x32 pixels).
875 int max_w = mx_h[idx];
876 if (max_w > 25)
877 max_w = 25;
878 int cur_h = 18;
879
880 int best_w = max_w;
881 int best_area = 1 * best_w;
882
883 for (int i = 0; i < max_w; i++) {
884 int h = mx_v[idx + i];
885 if (h < cur_h) {
886 cur_h = h;
887 if (cur_h * max_w <= best_area)
888 break;
889 }
890 if (cur_h * (i + 1) > best_area) {
891 best_w = i + 1;
892 best_area = cur_h * best_w;
893 }
894 }
895
896 r->br.x = r->tl.x + best_w;
897 r->br.y = r->tl.y + best_area / best_w;
898}
899