1 /*
2 Copyright (C) 2002-2010 Karl J. Runge <runge@karlrunge.com>
3 All rights reserved.
4
5 This file is part of x11vnc.
6
7 x11vnc is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or (at
10 your option) any later version.
11
12 x11vnc is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with x11vnc; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
20 or see <http://www.gnu.org/licenses/>.
21
22 In addition, as a special exception, Karl J. Runge
23 gives permission to link the code of its release of x11vnc with the
24 OpenSSL project's "OpenSSL" library (or with modified versions of it
25 that use the same license as the "OpenSSL" library), and distribute
26 the linked executables. You must obey the GNU General Public License
27 in all respects for all of the code used other than "OpenSSL". If you
28 modify this file, you may extend this exception to your version of the
29 file, but you are not obligated to do so. If you do not wish to do
30 so, delete this exception statement from your version.
31 */
32
33 /* -- scan.c -- */
34
35 #include "x11vnc.h"
36 #include "xinerama.h"
37 #include "xwrappers.h"
38 #include "xdamage.h"
39 #include "xrandr.h"
40 #include "win_utils.h"
41 #include "8to24.h"
42 #include "screen.h"
43 #include "pointer.h"
44 #include "cleanup.h"
45 #include "unixpw.h"
46 #include "screen.h"
47 #include "macosx.h"
48 #include "userinput.h"
49
50 /*
51 * routines for scanning and reading the X11 display for changes, and
52 * for doing all the tile work (shm, etc).
53 */
54 void initialize_tiles(void);
55 void free_tiles(void);
56 void shm_delete(XShmSegmentInfo *shm);
57 void shm_clean(XShmSegmentInfo *shm, XImage *xim);
58 void initialize_polling_images(void);
59 void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
60 char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
61 int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark);
62 void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark);
63 void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force);
64 int copy_screen(void);
65 int copy_snap(void);
66 void nap_sleep(int ms, int split);
67 void set_offset(void);
68 int scan_for_updates(int count_only);
69 void rotate_curs(char *dst_0, char *src_0, int Dx, int Dy, int Bpp);
70 void rotate_coords(int x, int y, int *xo, int *yo, int dxi, int dyi);
71 void rotate_coords_inverse(int x, int y, int *xo, int *yo, int dxi, int dyi);
72
73 static void set_fs_factor(int max);
74 static char *flip_ximage_byte_order(XImage *xim);
75 static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
76 char *name);
77 static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint);
78 static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint);
79 static void save_hint(hint_t hint, int loc);
80 static void hint_updates(void);
81 static void mark_hint(hint_t hint);
82 static int copy_tiles(int tx, int ty, int nt);
83 static int copy_all_tiles(void);
84 static int copy_all_tile_runs(void);
85 static int copy_tiles_backward_pass(void);
86 static int copy_tiles_additional_pass(void);
87 static int gap_try(int x, int y, int *run, int *saw, int along_x);
88 static int fill_tile_gaps(void);
89 static int island_try(int x, int y, int u, int v, int *run);
90 static int grow_islands(void);
91 static void blackout_regions(void);
92 static void nap_set(int tile_cnt);
93 static void nap_check(int tile_cnt);
94 static void ping_clients(int tile_cnt);
95 static int blackout_line_skip(int n, int x, int y, int rescan,
96 int *tile_count);
97 static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src,
98 int w, int pixelsize);
99 static int scan_display(int ystart, int rescan);
100
101
102 /* array to hold the hints: */
103 static hint_t *hint_list;
104
105 /* nap state */
106 int nap_ok = 0;
107 static int nap_diff_count = 0;
108
109 static int scan_count = 0; /* indicates which scan pattern we are on */
110 static int scan_in_progress = 0;
111
112
113 typedef struct tile_change_region {
114 /* start and end lines, along y, of the changed area inside a tile. */
115 unsigned short first_line, last_line;
116 short first_x, last_x;
117 /* info about differences along edges. */
118 unsigned short left_diff, right_diff;
119 unsigned short top_diff, bot_diff;
120 } region_t;
121
122 /* array to hold the tiles region_t-s. */
123 static region_t *tile_region;
124
125
126
127
128 /*
129 * setup tile numbers and allocate the tile and hint arrays:
130 */
initialize_tiles(void)131 void initialize_tiles(void) {
132
133 ntiles_x = (dpy_x - 1)/tile_x + 1;
134 ntiles_y = (dpy_y - 1)/tile_y + 1;
135 ntiles = ntiles_x * ntiles_y;
136
137 tile_has_diff = (unsigned char *)
138 calloc((size_t) (ntiles * sizeof(unsigned char)), 1);
139 tile_has_xdamage_diff = (unsigned char *)
140 calloc((size_t) (ntiles * sizeof(unsigned char)), 1);
141 tile_row_has_xdamage_diff = (unsigned char *)
142 calloc((size_t) (ntiles_y * sizeof(unsigned char)), 1);
143 tile_tried = (unsigned char *)
144 calloc((size_t) (ntiles * sizeof(unsigned char)), 1);
145 tile_copied = (unsigned char *)
146 calloc((size_t) (ntiles * sizeof(unsigned char)), 1);
147 tile_blackout = (tile_blackout_t *)
148 calloc((size_t) (ntiles * sizeof(tile_blackout_t)), 1);
149 tile_region = (region_t *) calloc((size_t) (ntiles * sizeof(region_t)), 1);
150
151 tile_row = (XImage **)
152 calloc((size_t) ((ntiles_x + 1) * sizeof(XImage *)), 1);
153 tile_row_shm = (XShmSegmentInfo *)
154 calloc((size_t) ((ntiles_x + 1) * sizeof(XShmSegmentInfo)), 1);
155
156 /* there will never be more hints than tiles: */
157 hint_list = (hint_t *) calloc((size_t) (ntiles * sizeof(hint_t)), 1);
158 }
159
free_tiles(void)160 void free_tiles(void) {
161 if (tile_has_diff) {
162 free(tile_has_diff);
163 tile_has_diff = NULL;
164 }
165 if (tile_has_xdamage_diff) {
166 free(tile_has_xdamage_diff);
167 tile_has_xdamage_diff = NULL;
168 }
169 if (tile_row_has_xdamage_diff) {
170 free(tile_row_has_xdamage_diff);
171 tile_row_has_xdamage_diff = NULL;
172 }
173 if (tile_tried) {
174 free(tile_tried);
175 tile_tried = NULL;
176 }
177 if (tile_copied) {
178 free(tile_copied);
179 tile_copied = NULL;
180 }
181 if (tile_blackout) {
182 free(tile_blackout);
183 tile_blackout = NULL;
184 }
185 if (tile_region) {
186 free(tile_region);
187 tile_region = NULL;
188 }
189 if (tile_row) {
190 free(tile_row);
191 tile_row = NULL;
192 }
193 if (tile_row_shm) {
194 free(tile_row_shm);
195 tile_row_shm = NULL;
196 }
197 if (hint_list) {
198 free(hint_list);
199 hint_list = NULL;
200 }
201 }
202
203 /*
204 * silly function to factor dpy_y until fullscreen shm is not bigger than max.
205 * should always work unless dpy_y is a large prime or something... under
206 * failure fs_factor remains 0 and no fullscreen updates will be tried.
207 */
208 static int fs_factor = 0;
209
set_fs_factor(int max)210 static void set_fs_factor(int max) {
211 int f, fac = 1, n = dpy_y;
212
213 fs_factor = 0;
214 if ((bpp/8) * dpy_x * dpy_y <= max) {
215 fs_factor = 1;
216 return;
217 }
218 for (f=2; f <= 101; f++) {
219 while (n % f == 0) {
220 n = n / f;
221 fac = fac * f;
222 if ( (bpp/8) * dpy_x * (dpy_y/fac) <= max ) {
223 fs_factor = fac;
224 return;
225 }
226 }
227 }
228 }
229
flip_ximage_byte_order(XImage * xim)230 static char *flip_ximage_byte_order(XImage *xim) {
231 char *order;
232 if (xim->byte_order == LSBFirst) {
233 order = "MSBFirst";
234 xim->byte_order = MSBFirst;
235 xim->bitmap_bit_order = MSBFirst;
236 } else {
237 order = "LSBFirst";
238 xim->byte_order = LSBFirst;
239 xim->bitmap_bit_order = LSBFirst;
240 }
241 return order;
242 }
243
244 /*
245 * set up an XShm image, or if not using shm just create the XImage.
246 */
shm_create(XShmSegmentInfo * shm,XImage ** ximg_ptr,int w,int h,char * name)247 static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
248 char *name) {
249
250 XImage *xim;
251 static int reported_flip = 0;
252 int db = 0;
253
254 shm->shmid = -1;
255 shm->shmaddr = (char *) -1;
256 *ximg_ptr = NULL;
257
258 if (nofb) {
259 return 1;
260 }
261
262 X_LOCK;
263
264 if (! using_shm || xform24to32 || raw_fb) {
265 /* we only need the XImage created */
266 xim = XCreateImage_wr(dpy, default_visual, depth, ZPixmap,
267 0, NULL, w, h, raw_fb ? 32 : BitmapPad(dpy), 0);
268
269 X_UNLOCK;
270
271 if (xim == NULL) {
272 rfbErr("XCreateImage(%s) failed.\n", name);
273 if (quiet) {
274 fprintf(stderr, "XCreateImage(%s) failed.\n",
275 name);
276 }
277 return 0;
278 }
279 if (db) fprintf(stderr, "shm_create simple %d %d\t%p %s\n", w, h, (void *)xim, name);
280 xim->data = (char *) malloc(xim->bytes_per_line * xim->height);
281 if (xim->data == NULL) {
282 rfbErr("XCreateImage(%s) data malloc failed.\n", name);
283 if (quiet) {
284 fprintf(stderr, "XCreateImage(%s) data malloc"
285 " failed.\n", name);
286 }
287 return 0;
288 }
289 if (flip_byte_order) {
290 char *order = flip_ximage_byte_order(xim);
291 if (! reported_flip && ! quiet) {
292 rfbLog("Changing XImage byte order"
293 " to %s\n", order);
294 reported_flip = 1;
295 }
296 }
297
298 *ximg_ptr = xim;
299 return 1;
300 }
301
302 if (! dpy) {
303 X_UNLOCK;
304 return 0;
305 }
306
307 xim = XShmCreateImage_wr(dpy, default_visual, depth, ZPixmap, NULL,
308 shm, w, h);
309
310 if (xim == NULL) {
311 rfbErr("XShmCreateImage(%s) failed.\n", name);
312 if (quiet) {
313 fprintf(stderr, "XShmCreateImage(%s) failed.\n", name);
314 }
315 X_UNLOCK;
316 return 0;
317 }
318
319 *ximg_ptr = xim;
320
321 #if LIBVNCSERVER_HAVE_XSHM
322 shm->shmid = shmget(IPC_PRIVATE,
323 xim->bytes_per_line * xim->height, IPC_CREAT | 0777);
324
325 if (shm->shmid == -1) {
326 rfbErr("shmget(%s) failed.\n", name);
327 rfbLogPerror("shmget");
328
329 XDestroyImage(xim);
330 *ximg_ptr = NULL;
331
332 X_UNLOCK;
333 return 0;
334 }
335
336 shm->shmaddr = xim->data = (char *) shmat(shm->shmid, 0, 0);
337
338 if (shm->shmaddr == (char *)-1) {
339 rfbErr("shmat(%s) failed.\n", name);
340 rfbLogPerror("shmat");
341
342 XDestroyImage(xim);
343 *ximg_ptr = NULL;
344
345 shmctl(shm->shmid, IPC_RMID, 0);
346 shm->shmid = -1;
347
348 X_UNLOCK;
349 return 0;
350 }
351
352 shm->readOnly = False;
353
354 if (! XShmAttach_wr(dpy, shm)) {
355 rfbErr("XShmAttach(%s) failed.\n", name);
356 XDestroyImage(xim);
357 *ximg_ptr = NULL;
358
359 shmdt(shm->shmaddr);
360 shm->shmaddr = (char *) -1;
361
362 shmctl(shm->shmid, IPC_RMID, 0);
363 shm->shmid = -1;
364
365 X_UNLOCK;
366 return 0;
367 }
368 #endif
369
370 X_UNLOCK;
371 return 1;
372 }
373
shm_delete(XShmSegmentInfo * shm)374 void shm_delete(XShmSegmentInfo *shm) {
375 #if LIBVNCSERVER_HAVE_XSHM
376 if (getenv("X11VNC_SHM_DEBUG")) fprintf(stderr, "shm_delete: %p\n", (void *) shm);
377 if (shm != NULL && shm->shmaddr != (char *) -1) {
378 shmdt(shm->shmaddr);
379 }
380 if (shm != NULL && shm->shmid != -1) {
381 shmctl(shm->shmid, IPC_RMID, 0);
382 }
383 if (shm != NULL) {
384 shm->shmaddr = (char *) -1;
385 shm->shmid = -1;
386 }
387 #else
388 if (!shm) {}
389 #endif
390 }
391
shm_clean(XShmSegmentInfo * shm,XImage * xim)392 void shm_clean(XShmSegmentInfo *shm, XImage *xim) {
393 int db = 0;
394
395 if (db) fprintf(stderr, "shm_clean: called: %p\n", (void *)xim);
396 X_LOCK;
397 #if LIBVNCSERVER_HAVE_XSHM
398 if (shm != NULL && shm->shmid != -1 && dpy) {
399 if (db) fprintf(stderr, "shm_clean: XShmDetach_wr\n");
400 XShmDetach_wr(dpy, shm);
401 }
402 #endif
403 if (xim != NULL) {
404 if (! raw_fb_back_to_X) { /* raw_fb hack */
405 if (xim->bitmap_unit != -1) {
406 if (db) fprintf(stderr, "shm_clean: XDestroyImage %p\n", (void *)xim);
407 XDestroyImage(xim);
408 } else {
409 if (xim->data) {
410 if (db) fprintf(stderr, "shm_clean: free xim->data %p %p\n", (void *)xim, (void *)(xim->data));
411 free(xim->data);
412 xim->data = NULL;
413 }
414 }
415 }
416 xim = NULL;
417 }
418 X_UNLOCK;
419
420 shm_delete(shm);
421 }
422
initialize_polling_images(void)423 void initialize_polling_images(void) {
424 int i, MB = 1024 * 1024;
425
426 /* set all shm areas to "none" before trying to create any */
427 scanline_shm.shmid = -1;
428 scanline_shm.shmaddr = (char *) -1;
429 scanline = NULL;
430 fullscreen_shm.shmid = -1;
431 fullscreen_shm.shmaddr = (char *) -1;
432 fullscreen = NULL;
433 snaprect_shm.shmid = -1;
434 snaprect_shm.shmaddr = (char *) -1;
435 snaprect = NULL;
436 for (i=1; i<=ntiles_x; i++) {
437 tile_row_shm[i].shmid = -1;
438 tile_row_shm[i].shmaddr = (char *) -1;
439 tile_row[i] = NULL;
440 }
441
442 /* the scanline (e.g. 1280x1) shared memory area image: */
443
444 if (! shm_create(&scanline_shm, &scanline, dpy_x, 1, "scanline")) {
445 clean_up_exit(1);
446 }
447
448 /*
449 * the fullscreen (e.g. 1280x1024/fs_factor) shared memory area image:
450 * (we cut down the size of the shm area to try avoid and shm segment
451 * limits, e.g. the default 1MB on Solaris)
452 */
453 if (UT.sysname && strstr(UT.sysname, "Linux")) {
454 set_fs_factor(10 * MB);
455 } else {
456 set_fs_factor(1 * MB);
457 }
458 if (fs_frac >= 1.0) {
459 fs_frac = 1.1;
460 fs_factor = 0;
461 }
462 if (! fs_factor) {
463 rfbLog("warning: fullscreen updates are disabled.\n");
464 } else {
465 if (! shm_create(&fullscreen_shm, &fullscreen, dpy_x,
466 dpy_y/fs_factor, "fullscreen")) {
467 clean_up_exit(1);
468 }
469 }
470 if (use_snapfb) {
471 if (! fs_factor) {
472 rfbLog("warning: disabling -snapfb mode.\n");
473 use_snapfb = 0;
474 } else if (! shm_create(&snaprect_shm, &snaprect, dpy_x,
475 dpy_y/fs_factor, "snaprect")) {
476 clean_up_exit(1);
477 }
478 }
479
480 /*
481 * for copy_tiles we need a lot of shared memory areas, one for
482 * each possible run length of changed tiles. 32 for 1024x768
483 * and 40 for 1280x1024, etc.
484 */
485
486 tile_shm_count = 0;
487 for (i=1; i<=ntiles_x; i++) {
488 if (! shm_create(&tile_row_shm[i], &tile_row[i], tile_x * i,
489 tile_y, "tile_row")) {
490 if (i == 1) {
491 clean_up_exit(1);
492 }
493 rfbLog("shm: Error creating shared memory tile-row for"
494 " len=%d,\n", i);
495 rfbLog("shm: reverting to -onetile mode. If this"
496 " problem persists\n");
497 rfbLog("shm: try using the -onetile or -noshm options"
498 " to limit\n");
499 rfbLog("shm: shared memory usage, or run ipcrm(1)"
500 " to manually\n");
501 rfbLog("shm: delete unattached shm segments.\n");
502 single_copytile_count = i;
503 single_copytile = 1;
504 }
505 tile_shm_count++;
506 if (single_copytile && i >= 1) {
507 /* only need 1x1 tiles */
508 break;
509 }
510 }
511 if (verbose) {
512 if (using_shm && ! xform24to32) {
513 rfbLog("created %d tile_row shm polling images.\n",
514 tile_shm_count);
515 } else {
516 rfbLog("created %d tile_row polling images.\n",
517 tile_shm_count);
518 }
519 }
520 }
521
522 /*
523 * A hint is a rectangular region built from 1 or more adjacent tiles
524 * glued together. Ultimately, this information in a single hint is sent
525 * to libvncserver rather than sending each tile separately.
526 */
create_tile_hint(int x,int y,int tw,int th,hint_t * hint)527 static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint) {
528 int w = dpy_x - x;
529 int h = dpy_y - y;
530
531 if (w > tw) {
532 w = tw;
533 }
534 if (h > th) {
535 h = th;
536 }
537
538 hint->x = x;
539 hint->y = y;
540 hint->w = w;
541 hint->h = h;
542 }
543
extend_tile_hint(int x,int y,int tw,int th,hint_t * hint)544 static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint) {
545 int w = dpy_x - x;
546 int h = dpy_y - y;
547
548 if (w > tw) {
549 w = tw;
550 }
551 if (h > th) {
552 h = th;
553 }
554
555 if (hint->x > x) { /* extend to the left */
556 hint->w += hint->x - x;
557 hint->x = x;
558 }
559 if (hint->y > y) { /* extend upward */
560 hint->h += hint->y - y;
561 hint->y = y;
562 }
563
564 if (hint->x + hint->w < x + w) { /* extend to the right */
565 hint->w = x + w - hint->x;
566 }
567 if (hint->y + hint->h < y + h) { /* extend downward */
568 hint->h = y + h - hint->y;
569 }
570 }
571
save_hint(hint_t hint,int loc)572 static void save_hint(hint_t hint, int loc) {
573 /* simply copy it to the global array for later use. */
574 hint_list[loc].x = hint.x;
575 hint_list[loc].y = hint.y;
576 hint_list[loc].w = hint.w;
577 hint_list[loc].h = hint.h;
578 }
579
580 /*
581 * Glue together horizontal "runs" of adjacent changed tiles into one big
582 * rectangle change "hint" to be passed to the vnc machinery.
583 */
hint_updates(void)584 static void hint_updates(void) {
585 hint_t hint;
586 int x, y, i, n, ty, th, tx, tw;
587 int hint_count = 0, in_run = 0;
588
589 hint.x = hint.y = hint.w = hint.h = 0;
590
591 for (y=0; y < ntiles_y; y++) {
592 for (x=0; x < ntiles_x; x++) {
593 n = x + y * ntiles_x;
594
595 if (tile_has_diff[n]) {
596 ty = tile_region[n].first_line;
597 th = tile_region[n].last_line - ty + 1;
598
599 tx = tile_region[n].first_x;
600 tw = tile_region[n].last_x - tx + 1;
601 if (tx < 0) {
602 tx = 0;
603 tw = tile_x;
604 }
605
606 if (! in_run) {
607 create_tile_hint( x * tile_x + tx,
608 y * tile_y + ty, tw, th, &hint);
609 in_run = 1;
610 } else {
611 extend_tile_hint( x * tile_x + tx,
612 y * tile_y + ty, tw, th, &hint);
613 }
614 } else {
615 if (in_run) {
616 /* end of a row run of altered tiles: */
617 save_hint(hint, hint_count++);
618 in_run = 0;
619 }
620 }
621 }
622 if (in_run) { /* save the last row run */
623 save_hint(hint, hint_count++);
624 in_run = 0;
625 }
626 }
627
628 for (i=0; i < hint_count; i++) {
629 /* pass update info to vnc: */
630 mark_hint(hint_list[i]);
631 }
632 }
633
634 /*
635 * kludge, simple ceil+floor for non-negative doubles:
636 */
637 #define CEIL(x) ( (double) ((int) (x)) == (x) ? \
638 (double) ((int) (x)) : (double) ((int) (x) + 1) )
639 #define FLOOR(x) ( (double) ((int) (x)) )
640
641 /*
642 * Scaling:
643 *
644 * For shrinking, a destination (scaled) pixel will correspond to more
645 * than one source (i.e. main fb) pixel. Think of an x-y plane made with
646 * graph paper. Each unit square in the graph paper (i.e. collection of
647 * points (x,y) such that N < x < N+1 and M < y < M+1, N and M integers)
648 * corresponds to one pixel in the unscaled fb. There is a solid
649 * color filling the inside of such a square. A scaled pixel has width
650 * 1/scale_fac, e.g. for "-scale 3/4" the width of the scaled pixel
651 * is 1.333. The area of this scaled pixel is 1.333 * 1.333 (so it
652 * obviously overlaps more than one source pixel, each which have area 1).
653 *
654 * We take the weight an unscaled pixel (source) contributes to a
655 * scaled pixel (destination) as simply proportional to the overlap area
656 * between the two pixels. One can then think of the value of the scaled
657 * pixel as an integral over the portion of the graph paper it covers.
658 * The thing being integrated is the color value of the unscaled source.
659 * That color value is constant over a graph paper square (source pixel),
660 * and changes discontinuously from one unit square to the next.
661 *
662
663 Here is an example for -scale 3/4, the solid lines are the source pixels
664 (graph paper unit squares), while the dotted lines denote the scaled
665 pixels (destination pixels):
666
667 0 1 4/3 2 8/3 3 4=12/3
668 |---------|--.------|------.--|---------|.
669 | | . | . | |.
670 | A | . B | . | |.
671 | | . | . | |.
672 | | . | . | |.
673 1 |---------|--.------|------.--|---------|.
674 4/3|.........|.........|.........|.........|.
675 | | . | . | |.
676 | C | . D | . | |.
677 | | . | . | |.
678 2 |---------|--.------|------.--|---------|.
679 | | . | . | |.
680 | | . | . | |.
681 8/3|.........|.........|.........|.........|.
682 | | . | . | |.
683 3 |---------|--.------|------.--|---------|.
684
685 So we see the first scaled pixel (0 < x < 4/3 and 0 < y < 4/3) mostly
686 overlaps with unscaled source pixel "A". The integration (averaging)
687 weights for this scaled pixel are:
688
689 A 1
690 B 1/3
691 C 1/3
692 D 1/9
693
694 *
695 * The Red, Green, and Blue color values must be averaged over separately
696 * otherwise you can get a complete mess (except in solid regions),
697 * because high order bits are averaged differently from the low order bits.
698 *
699 * So the algorithm is roughly:
700 *
701 * - Given as input a rectangle in the unscaled source fb with changes,
702 * find the rectangle of pixels this affects in the scaled destination fb.
703 *
704 * - For each of the affected scaled (dest) pixels, determine all of the
705 * unscaled (source) pixels it overlaps with.
706 *
707 * - Average those unscaled source values together, weighted by the area
708 * overlap with the destination pixel. Average R, G, B separately.
709 *
710 * - Take this average value and convert to a valid pixel value if
711 * necessary (e.g. rounding, shifting), and then insert it into the
712 * destination framebuffer as the pixel value.
713 *
714 * - On to the next destination pixel...
715 *
716 * ========================================================================
717 *
718 * For expanding, e.g. -scale 1.1 (which we don't think people will do
719 * very often... or at least so we hope, the framebuffer can become huge)
720 * the situation is reversed and the destination pixel is smaller than a
721 * "graph paper" unit square (source pixel). Some destination pixels
722 * will be completely within a single unscaled source pixel.
723 *
724 * What we do here is a simple 4 point interpolation scheme:
725 *
726 * Let P00 be the source pixel closest to the destination pixel but with
727 * x and y values less than or equal to those of the destination pixel.
728 * (for simplicity, think of the upper left corner of a pixel defining the
729 * x,y location of the pixel, the center would work just as well). So it
730 * is the source pixel immediately to the upper left of the destination
731 * pixel. Let P10 be the source pixel one to the right of P00. Let P01
732 * be one down from P00. And let P11 be one down and one to the right
733 * of P00. They form a 2x2 square we will interpolate inside of.
734 *
735 * Let V00, V10, V01, and V11 be the color values of those 4 source
736 * pixels. Let dx be the displacement along x the destination pixel is
737 * from P00. Note: 0 <= dx < 1 by definition of P00. Similarly let
738 * dy be the displacement along y. The weighted average for the
739 * interpolation is:
740 *
741 * V_ave = V00 * (1 - dx) * (1 - dy)
742 * + V10 * dx * (1 - dy)
743 * + V01 * (1 - dx) * dy
744 * + V11 * dx * dy
745 *
746 * Note that the weights (1-dx)*(1-dy) + dx*(1-dy) + (1-dx)*dy + dx*dy
747 * automatically add up to 1. It is also nice that all the weights are
748 * positive (unsigned char stays unsigned char). The above formula can
749 * be motivated by doing two 1D interpolations along x:
750 *
751 * VA = V00 * (1 - dx) + V10 * dx
752 * VB = V01 * (1 - dx) + V11 * dx
753 *
754 * and then interpolating VA and VB along y:
755 *
756 * V_ave = VA * (1 - dy) + VB * dy
757 *
758 * VA
759 * v |<-dx->|
760 * -- V00 ------ V10
761 * dy | |
762 * -- | o...|... "o" denotes the position of the desired
763 * ^ | . | . destination pixel relative to the P00
764 * | . | . source pixel.
765 * V10 ----.- V11 .
766 * ........
767 * |
768 * VB
769 *
770 *
771 * Of course R, G, B averages are done separately as in the shrinking
772 * case. This gives reasonable results, and the implementation for
773 * shrinking can simply be used with different choices for weights for
774 * the loop over the 4 pixels.
775 */
776
scale_rect(double factor_x,double factor_y,int blend,int interpolate,int Bpp,char * src_fb,int src_bytes_per_line,char * dst_fb,int dst_bytes_per_line,int Nx,int Ny,int nx,int ny,int X1,int Y1,int X2,int Y2,int mark)777 void scale_rect(double factor_x, double factor_y, int blend, int interpolate, int Bpp,
778 char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line,
779 int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark) {
780 /*
781 * Notation:
782 * "i" an x pixel index in the destination (scaled) framebuffer
783 * "j" a y pixel index in the destination (scaled) framebuffer
784 * "I" an x pixel index in the source (un-scaled, i.e. main) framebuffer
785 * "J" a y pixel index in the source (un-scaled, i.e. main) framebuffer
786 *
787 * Similarly for nx, ny, Nx, Ny, etc. Lowercase: dest, Uppercase: source.
788 */
789 int i, j, i1, i2, j1, j2; /* indices for scaled fb (dest) */
790 int I, J, I1, I2, J1, J2; /* indices for main fb (source) */
791
792 double w, wx, wy, wtot; /* pixel weights */
793
794 double x1, y1, x2, y2; /* x-y coords for destination pixels edges */
795 double dx, dy; /* size of destination pixel */
796 double ddx=0, ddy=0; /* for interpolation expansion */
797
798 char *src, *dest; /* pointers to the two framebuffers */
799
800
801 unsigned short us = 0;
802 unsigned char uc = 0;
803 unsigned int ui = 0;
804
805 int use_noblend_shortcut = 1;
806 int shrink; /* whether shrinking or expanding */
807 static int constant_weights = -1, mag_int = -1;
808 static int last_Nx = -1, last_Ny = -1, cnt = 0;
809 static double last_factor = -1.0;
810 int b, k;
811 double pixave[4]; /* for averaging pixel values */
812
813 if (factor_x <= 1.0 && factor_y <= 1.0) {
814 shrink = 1;
815 } else {
816 shrink = 0;
817 }
818
819 /*
820 * N.B. width and height (real numbers) of a scaled pixel.
821 * both are > 1 (e.g. 1.333 for -scale 3/4)
822 * they should also be equal but we don't assume it.
823 *
824 * This new way is probably the best we can do, take the inverse
825 * of the scaling factor to double precision.
826 */
827 dx = 1.0/factor_x;
828 dy = 1.0/factor_y;
829
830 /*
831 * There is some speedup if the pixel weights are constant, so
832 * let's special case these.
833 *
834 * If scale = 1/n and n divides Nx and Ny, the pixel weights
835 * are constant (e.g. 1/2 => equal on 2x2 square).
836 */
837 if (factor_x != last_factor || Nx != last_Nx || Ny != last_Ny) {
838 constant_weights = -1;
839 mag_int = -1;
840 last_Nx = Nx;
841 last_Ny = Ny;
842 last_factor = factor_x;
843 }
844 if (constant_weights < 0 && factor_x != factor_y) {
845 constant_weights = 0;
846 mag_int = 0;
847
848 } else if (constant_weights < 0) {
849 int n = 0;
850
851 constant_weights = 0;
852 mag_int = 0;
853
854 for (i = 2; i<=128; i++) {
855 double test = ((double) 1)/ i;
856 double diff, eps = 1.0e-7;
857 diff = factor_x - test;
858 if (-eps < diff && diff < eps) {
859 n = i;
860 break;
861 }
862 }
863 if (! blend || ! shrink || interpolate) {
864 ;
865 } else if (n != 0) {
866 if (Nx % n == 0 && Ny % n == 0) {
867 static int didmsg = 0;
868 if (mark && ! didmsg) {
869 didmsg = 1;
870 rfbLog("scale_and_mark_rect: using "
871 "constant pixel weight speedup "
872 "for 1/%d\n", n);
873 }
874 constant_weights = 1;
875 }
876 }
877
878 n = 0;
879 for (i = 2; i<=32; i++) {
880 double test = (double) i;
881 double diff, eps = 1.0e-7;
882 diff = factor_x - test;
883 if (-eps < diff && diff < eps) {
884 n = i;
885 break;
886 }
887 }
888 if (! blend && factor_x > 1.0 && n) {
889 mag_int = n;
890 }
891 }
892
893 if (mark && factor_x > 1.0 && blend) {
894 /*
895 * kludge: correct for interpolating blurring leaking
896 * up or left 1 destination pixel.
897 */
898 if (X1 > 0) X1--;
899 if (Y1 > 0) Y1--;
900 }
901
902 /*
903 * find the extent of the change the input rectangle induces in
904 * the scaled framebuffer.
905 */
906
907 /* Left edges: find largest i such that i * dx <= X1 */
908 i1 = FLOOR(X1/dx);
909
910 /* Right edges: find smallest i such that (i+1) * dx >= X2+1 */
911 i2 = CEIL( (X2+1)/dx ) - 1;
912
913 /* To be safe, correct any overflows: */
914 i1 = nfix(i1, nx);
915 i2 = nfix(i2, nx) + 1; /* add 1 to make a rectangle upper boundary */
916
917 /* Repeat above for y direction: */
918 j1 = FLOOR(Y1/dy);
919 j2 = CEIL( (Y2+1)/dy ) - 1;
920
921 j1 = nfix(j1, ny);
922 j2 = nfix(j2, ny) + 1;
923
924 /*
925 * special case integer magnification with no blending.
926 * vision impaired magnification usage is interested in this case.
927 */
928 if (mark && ! blend && mag_int && Bpp != 3) {
929 int jmin, jmax, imin, imax;
930
931 /* outer loop over *source* pixels */
932 for (J=Y1; J < Y2; J++) {
933 jmin = J * mag_int;
934 jmax = jmin + mag_int;
935 for (I=X1; I < X2; I++) {
936 /* extract value */
937 src = src_fb + J*src_bytes_per_line + I*Bpp;
938 if (Bpp == 4) {
939 ui = *((unsigned int *)src);
940 } else if (Bpp == 2) {
941 us = *((unsigned short *)src);
942 } else if (Bpp == 1) {
943 uc = *((unsigned char *)src);
944 }
945 imin = I * mag_int;
946 imax = imin + mag_int;
947 /* inner loop over *dest* pixels */
948 for (j=jmin; j<jmax; j++) {
949 dest = dst_fb + j*dst_bytes_per_line + imin*Bpp;
950 for (i=imin; i<imax; i++) {
951 if (Bpp == 4) {
952 *((unsigned int *)dest) = ui;
953 } else if (Bpp == 2) {
954 *((unsigned short *)dest) = us;
955 } else if (Bpp == 1) {
956 *((unsigned char *)dest) = uc;
957 }
958 dest += Bpp;
959 }
960 }
961 }
962 }
963 goto markit;
964 }
965
966 /* set these all to 1.0 to begin with */
967 wx = 1.0;
968 wy = 1.0;
969 w = 1.0;
970
971 /*
972 * Loop over destination pixels in scaled fb:
973 */
974 for (j=j1; j<j2; j++) {
975 y1 = j * dy; /* top edge */
976 if (y1 > Ny - 1) {
977 /* can go over with dy = 1/scale_fac */
978 y1 = Ny - 1;
979 }
980 y2 = y1 + dy; /* bottom edge */
981
982 /* Find main fb indices covered by this dest pixel: */
983 J1 = (int) FLOOR(y1);
984 J1 = nfix(J1, Ny);
985
986 if (shrink && ! interpolate) {
987 J2 = (int) CEIL(y2) - 1;
988 J2 = nfix(J2, Ny);
989 } else {
990 J2 = J1 + 1; /* simple interpolation */
991 ddy = y1 - J1;
992 }
993
994 /* destination char* pointer: */
995 dest = dst_fb + j*dst_bytes_per_line + i1*Bpp;
996
997 for (i=i1; i<i2; i++) {
998
999 x1 = i * dx; /* left edge */
1000 if (x1 > Nx - 1) {
1001 /* can go over with dx = 1/scale_fac */
1002 x1 = Nx - 1;
1003 }
1004 x2 = x1 + dx; /* right edge */
1005
1006 cnt++;
1007
1008 /* Find main fb indices covered by this dest pixel: */
1009 I1 = (int) FLOOR(x1);
1010 if (I1 >= Nx) I1 = Nx - 1;
1011
1012 if (! blend && use_noblend_shortcut) {
1013 /*
1014 * The noblend case involves no weights,
1015 * and 1 pixel, so just copy the value
1016 * directly.
1017 */
1018 src = src_fb + J1*src_bytes_per_line + I1*Bpp;
1019 if (Bpp == 4) {
1020 *((unsigned int *)dest)
1021 = *((unsigned int *)src);
1022 } else if (Bpp == 2) {
1023 *((unsigned short *)dest)
1024 = *((unsigned short *)src);
1025 } else if (Bpp == 1) {
1026 *(dest) = *(src);
1027 } else if (Bpp == 3) {
1028 /* rare case */
1029 for (k=0; k<=2; k++) {
1030 *(dest+k) = *(src+k);
1031 }
1032 }
1033 dest += Bpp;
1034 continue;
1035 }
1036
1037 if (shrink && ! interpolate) {
1038 I2 = (int) CEIL(x2) - 1;
1039 if (I2 >= Nx) I2 = Nx - 1;
1040 } else {
1041 I2 = I1 + 1; /* simple interpolation */
1042 ddx = x1 - I1;
1043 }
1044
1045 /* Zero out accumulators for next pixel average: */
1046 for (b=0; b<4; b++) {
1047 pixave[b] = 0.0; /* for RGB weighted sums */
1048 }
1049
1050 /*
1051 * wtot is for accumulating the total weight.
1052 * It should always sum to 1/(scale_fac * scale_fac).
1053 */
1054 wtot = 0.0;
1055
1056 /*
1057 * Loop over source pixels covered by this dest pixel.
1058 *
1059 * These "extra" loops over "J" and "I" make
1060 * the cache/cacheline performance unclear.
1061 * For example, will the data brought in from
1062 * src for j, i, and J=0 still be in the cache
1063 * after the J > 0 data have been accessed and
1064 * we are at j, i+1, J=0? The stride in J is
1065 * main_bytes_per_line, and so ~4 KB.
1066 *
1067 * Typical case when shrinking are 2x2 loop, so
1068 * just two lines to worry about.
1069 */
1070 for (J=J1; J<=J2; J++) {
1071 /* see comments for I, x1, x2, etc. below */
1072 if (constant_weights) {
1073 ;
1074 } else if (! blend) {
1075 if (J != J1) {
1076 continue;
1077 }
1078 wy = 1.0;
1079
1080 /* interpolation scheme: */
1081 } else if (! shrink || interpolate) {
1082 if (J >= Ny) {
1083 continue;
1084 } else if (J == J1) {
1085 wy = 1.0 - ddy;
1086 } else if (J != J1) {
1087 wy = ddy;
1088 }
1089
1090 /* integration scheme: */
1091 } else if (J < y1) {
1092 wy = J+1 - y1;
1093 } else if (J+1 > y2) {
1094 wy = y2 - J;
1095 } else {
1096 wy = 1.0;
1097 }
1098
1099 src = src_fb + J*src_bytes_per_line + I1*Bpp;
1100
1101 for (I=I1; I<=I2; I++) {
1102
1103 /* Work out the weight: */
1104
1105 if (constant_weights) {
1106 ;
1107 } else if (! blend) {
1108 /*
1109 * Ugh, PseudoColor colormap is
1110 * bad news, to avoid random
1111 * colors just take the first
1112 * pixel. Or user may have
1113 * specified :nb to fraction.
1114 * The :fb will force blending
1115 * for this case.
1116 */
1117 if (I != I1) {
1118 continue;
1119 }
1120 wx = 1.0;
1121
1122 /* interpolation scheme: */
1123 } else if (! shrink || interpolate) {
1124 if (I >= Nx) {
1125 continue; /* off edge */
1126 } else if (I == I1) {
1127 wx = 1.0 - ddx;
1128 } else if (I != I1) {
1129 wx = ddx;
1130 }
1131
1132 /* integration scheme: */
1133 } else if (I < x1) {
1134 /*
1135 * source left edge (I) to the
1136 * left of dest left edge (x1):
1137 * fractional weight
1138 */
1139 wx = I+1 - x1;
1140 } else if (I+1 > x2) {
1141 /*
1142 * source right edge (I+1) to the
1143 * right of dest right edge (x2):
1144 * fractional weight
1145 */
1146 wx = x2 - I;
1147 } else {
1148 /*
1149 * source edges (I and I+1) completely
1150 * inside dest edges (x1 and x2):
1151 * full weight
1152 */
1153 wx = 1.0;
1154 }
1155
1156 w = wx * wy;
1157 wtot += w;
1158
1159 /*
1160 * We average the unsigned char value
1161 * instead of char value: otherwise
1162 * the minimum (char 0) is right next
1163 * to the maximum (char -1)! This way
1164 * they are spread between 0 and 255.
1165 */
1166 if (Bpp == 4) {
1167 /* unroll the loops, can give 20% */
1168 pixave[0] += w * ((unsigned char) *(src ));
1169 pixave[1] += w * ((unsigned char) *(src+1));
1170 pixave[2] += w * ((unsigned char) *(src+2));
1171 pixave[3] += w * ((unsigned char) *(src+3));
1172 } else if (Bpp == 2) {
1173 /*
1174 * 16bpp: trickier with green
1175 * split over two bytes, so we
1176 * use the masks:
1177 */
1178 us = *((unsigned short *) src);
1179 pixave[0] += w*(us & main_red_mask);
1180 pixave[1] += w*(us & main_green_mask);
1181 pixave[2] += w*(us & main_blue_mask);
1182 } else if (Bpp == 1) {
1183 pixave[0] += w *
1184 ((unsigned char) *(src));
1185 } else {
1186 for (b=0; b<Bpp; b++) {
1187 pixave[b] += w *
1188 ((unsigned char) *(src+b));
1189 }
1190 }
1191 src += Bpp;
1192 }
1193 }
1194
1195 if (wtot <= 0.0) {
1196 wtot = 1.0;
1197 }
1198 wtot = 1.0/wtot; /* normalization factor */
1199
1200 /* place weighted average pixel in the scaled fb: */
1201 if (Bpp == 4) {
1202 *(dest ) = (char) (wtot * pixave[0]);
1203 *(dest+1) = (char) (wtot * pixave[1]);
1204 *(dest+2) = (char) (wtot * pixave[2]);
1205 *(dest+3) = (char) (wtot * pixave[3]);
1206 } else if (Bpp == 2) {
1207 /* 16bpp / 565 case: */
1208 pixave[0] *= wtot;
1209 pixave[1] *= wtot;
1210 pixave[2] *= wtot;
1211 us = (main_red_mask & (int) pixave[0])
1212 | (main_green_mask & (int) pixave[1])
1213 | (main_blue_mask & (int) pixave[2]);
1214 *( (unsigned short *) dest ) = us;
1215 } else if (Bpp == 1) {
1216 *(dest) = (char) (wtot * pixave[0]);
1217 } else {
1218 for (b=0; b<Bpp; b++) {
1219 *(dest+b) = (char) (wtot * pixave[b]);
1220 }
1221 }
1222 dest += Bpp;
1223 }
1224 }
1225 markit:
1226 if (mark) {
1227 mark_rect_as_modified(i1, j1, i2, j2, 1);
1228 }
1229 }
1230
1231 /*
1232 Framebuffers data flow:
1233
1234 General case:
1235 -------- -------- -------- --------
1236 ----- |8to24_fb| |main_fb | |snap_fb | | X |
1237 |rfbfb| <== | | <== | | <== | | <== | Server |
1238 ----- -------- -------- -------- --------
1239 (to vnc) (optional) (usu = rfbfb) (optional) (read only)
1240
1241 8to24_fb mode will create side fbs: poll24_fb and poll8_fb for
1242 bookkeepping the different regions (merged into 8to24_fb).
1243
1244 Normal case:
1245 -------- --------
1246 |main_fb | | X |
1247 |= rfb_fb| <== | Server |
1248 -------- --------
1249
1250 Scaling case:
1251 -------- --------
1252 ----- |main_fb | | X |
1253 |rfbfb| <== | | <== | Server |
1254 ----- -------- --------
1255
1256 Webcam/video case:
1257 -------- -------- --------
1258 |main_fb | |snap_fb | | Video |
1259 | | <== | | <== | device |
1260 -------- -------- --------
1261
1262 If we ever do a -rr rotation/reflection tran, it probably should
1263 be done after any scaling (need a rr_fb for intermediate results)
1264
1265 -rr option: transformation:
1266
1267 none x -> x;
1268 y -> y;
1269
1270 x x -> w - x - 1;
1271 y -> y;
1272
1273 y x -> x;
1274 x -> h - y - 1;
1275
1276 xy x -> w - x - 1;
1277 y -> h - y - 1;
1278
1279 +90 x -> h - y - 1;
1280 y -> x;
1281
1282 +90x x -> y;
1283 y -> x;
1284
1285 +90y x -> h - y - 1;
1286 y -> w - x - 1;
1287
1288 -90 x -> y;
1289 y -> w - x - 1;
1290
1291 some aliases:
1292
1293 xy: yx, +180, -180, 180
1294 +90: 90
1295 +90x: 90x
1296 +90y: 90y
1297 -90: +270, 270
1298 */
1299
scale_and_mark_rect(int X1,int Y1,int X2,int Y2,int mark)1300 void scale_and_mark_rect(int X1, int Y1, int X2, int Y2, int mark) {
1301 char *dst_fb, *src_fb = main_fb;
1302 int dst_bpl, Bpp = bpp/8, fac = 1;
1303
1304 if (!screen || !rfb_fb || !main_fb) {
1305 return;
1306 }
1307 if (! screen->serverFormat.trueColour) {
1308 /*
1309 * PseudoColor colormap... blending leads to random colors.
1310 * User can override with ":fb"
1311 */
1312 if (scaling_blend == 1) {
1313 /* :fb option sets it to 2 */
1314 if (default_visual->class == StaticGray) {
1315 /*
1316 * StaticGray can be blended OK, otherwise
1317 * user can disable with :nb
1318 */
1319 ;
1320 } else {
1321 scaling_blend = 0;
1322 }
1323 }
1324 }
1325
1326 if (cmap8to24 && cmap8to24_fb) {
1327 src_fb = cmap8to24_fb;
1328 if (scaling) {
1329 if (depth <= 8) {
1330 fac = 4;
1331 } else if (depth <= 16) {
1332 fac = 2;
1333 }
1334 }
1335 }
1336 dst_fb = rfb_fb;
1337 dst_bpl = rfb_bytes_per_line;
1338
1339 scale_rect(scale_fac_x, scale_fac_y, scaling_blend, scaling_interpolate, fac * Bpp,
1340 src_fb, fac * main_bytes_per_line, dst_fb, dst_bpl, dpy_x, dpy_y,
1341 scaled_x, scaled_y, X1, Y1, X2, Y2, mark);
1342 }
1343
rotate_coords(int x,int y,int * xo,int * yo,int dxi,int dyi)1344 void rotate_coords(int x, int y, int *xo, int *yo, int dxi, int dyi) {
1345 int xi = x, yi = y;
1346 int Dx, Dy;
1347
1348 if (dxi >= 0) {
1349 Dx = dxi;
1350 Dy = dyi;
1351 } else if (scaling) {
1352 Dx = scaled_x;
1353 Dy = scaled_y;
1354 } else {
1355 Dx = dpy_x;
1356 Dy = dpy_y;
1357 }
1358
1359 /* ncache?? */
1360
1361 if (rotating == ROTATE_NONE) {
1362 *xo = xi;
1363 *yo = yi;
1364 } else if (rotating == ROTATE_X) {
1365 *xo = Dx - xi - 1;
1366 *yo = yi;
1367 } else if (rotating == ROTATE_Y) {
1368 *xo = xi;
1369 *yo = Dy - yi - 1;
1370 } else if (rotating == ROTATE_XY) {
1371 *xo = Dx - xi - 1;
1372 *yo = Dy - yi - 1;
1373 } else if (rotating == ROTATE_90) {
1374 *xo = Dy - yi - 1;
1375 *yo = xi;
1376 } else if (rotating == ROTATE_90X) {
1377 *xo = yi;
1378 *yo = xi;
1379 } else if (rotating == ROTATE_90Y) {
1380 *xo = Dy - yi - 1;
1381 *yo = Dx - xi - 1;
1382 } else if (rotating == ROTATE_270) {
1383 *xo = yi;
1384 *yo = Dx - xi - 1;
1385 }
1386 }
1387
rotate_coords_inverse(int x,int y,int * xo,int * yo,int dxi,int dyi)1388 void rotate_coords_inverse(int x, int y, int *xo, int *yo, int dxi, int dyi) {
1389 int xi = x, yi = y;
1390
1391 int Dx, Dy;
1392
1393 if (dxi >= 0) {
1394 Dx = dxi;
1395 Dy = dyi;
1396 } else if (scaling) {
1397 Dx = scaled_x;
1398 Dy = scaled_y;
1399 } else {
1400 Dx = dpy_x;
1401 Dy = dpy_y;
1402 }
1403 if (! rotating_same) {
1404 int t = Dx;
1405 Dx = Dy;
1406 Dy = t;
1407 }
1408
1409 if (rotating == ROTATE_NONE) {
1410 *xo = xi;
1411 *yo = yi;
1412 } else if (rotating == ROTATE_X) {
1413 *xo = Dx - xi - 1;
1414 *yo = yi;
1415 } else if (rotating == ROTATE_Y) {
1416 *xo = xi;
1417 *yo = Dy - yi - 1;
1418 } else if (rotating == ROTATE_XY) {
1419 *xo = Dx - xi - 1;
1420 *yo = Dy - yi - 1;
1421 } else if (rotating == ROTATE_90) {
1422 *xo = yi;
1423 *yo = Dx - xi - 1;
1424 } else if (rotating == ROTATE_90X) {
1425 *xo = yi;
1426 *yo = xi;
1427 } else if (rotating == ROTATE_90Y) {
1428 *xo = Dy - yi - 1;
1429 *yo = Dx - xi - 1;
1430 } else if (rotating == ROTATE_270) {
1431 *xo = Dy - yi - 1;
1432 *yo = xi;
1433 }
1434 }
1435
1436 /* unroll the Bpp loop to be used in each case: */
1437 #define ROT_COPY \
1438 src = src_0 + fbl*y + Bpp*x; \
1439 dst = dst_0 + rbl*yn + Bpp*xn; \
1440 if (Bpp == 1) { \
1441 *(dst) = *(src); \
1442 } else if (Bpp == 2) { \
1443 *(dst+0) = *(src+0); \
1444 *(dst+1) = *(src+1); \
1445 } else if (Bpp == 3) { \
1446 *(dst+0) = *(src+0); \
1447 *(dst+1) = *(src+1); \
1448 *(dst+2) = *(src+2); \
1449 } else if (Bpp == 4) { \
1450 *(dst+0) = *(src+0); \
1451 *(dst+1) = *(src+1); \
1452 *(dst+2) = *(src+2); \
1453 *(dst+3) = *(src+3); \
1454 }
1455
rotate_fb(int x1,int y1,int x2,int y2)1456 void rotate_fb(int x1, int y1, int x2, int y2) {
1457 int x, y, xn, yn, r_x1, r_y1, r_x2, r_y2, Bpp = bpp/8;
1458 int fbl = rfb_bytes_per_line;
1459 int rbl = rot_bytes_per_line;
1460 int Dx, Dy;
1461 char *src, *dst;
1462 char *src_0 = rfb_fb;
1463 char *dst_0 = rot_fb;
1464
1465 if (! rotating || ! rot_fb) {
1466 return;
1467 }
1468
1469 if (scaling) {
1470 Dx = scaled_x;
1471 Dy = scaled_y;
1472 } else {
1473 Dx = dpy_x;
1474 Dy = dpy_y;
1475 }
1476 rotate_coords(x1, y1, &r_x1, &r_y1, -1, -1);
1477 rotate_coords(x2, y2, &r_x2, &r_y2, -1, -1);
1478
1479 dst = rot_fb;
1480
1481 if (rotating == ROTATE_X) {
1482 for (y = y1; y < y2; y++) {
1483 for (x = x1; x < x2; x++) {
1484 xn = Dx - x - 1;
1485 yn = y;
1486 ROT_COPY
1487 }
1488 }
1489 } else if (rotating == ROTATE_Y) {
1490 for (y = y1; y < y2; y++) {
1491 for (x = x1; x < x2; x++) {
1492 xn = x;
1493 yn = Dy - y - 1;
1494 ROT_COPY
1495 }
1496 }
1497 } else if (rotating == ROTATE_XY) {
1498 for (y = y1; y < y2; y++) {
1499 for (x = x1; x < x2; x++) {
1500 xn = Dx - x - 1;
1501 yn = Dy - y - 1;
1502 ROT_COPY
1503 }
1504 }
1505 } else if (rotating == ROTATE_90) {
1506 for (y = y1; y < y2; y++) {
1507 for (x = x1; x < x2; x++) {
1508 xn = Dy - y - 1;
1509 yn = x;
1510 ROT_COPY
1511 }
1512 }
1513 } else if (rotating == ROTATE_90X) {
1514 for (y = y1; y < y2; y++) {
1515 for (x = x1; x < x2; x++) {
1516 xn = y;
1517 yn = x;
1518 ROT_COPY
1519 }
1520 }
1521 } else if (rotating == ROTATE_90Y) {
1522 for (y = y1; y < y2; y++) {
1523 for (x = x1; x < x2; x++) {
1524 xn = Dy - y - 1;
1525 yn = Dx - x - 1;
1526 ROT_COPY
1527 }
1528 }
1529 } else if (rotating == ROTATE_270) {
1530 for (y = y1; y < y2; y++) {
1531 for (x = x1; x < x2; x++) {
1532 xn = y;
1533 yn = Dx - x - 1;
1534 ROT_COPY
1535 }
1536 }
1537 }
1538 }
1539
rotate_curs(char * dst_0,char * src_0,int Dx,int Dy,int Bpp)1540 void rotate_curs(char *dst_0, char *src_0, int Dx, int Dy, int Bpp) {
1541 int x, y, xn, yn;
1542 char *src, *dst;
1543 int fbl, rbl;
1544
1545 if (! rotating) {
1546 return;
1547 }
1548
1549 fbl = Dx * Bpp;
1550 if (rotating_same) {
1551 rbl = Dx * Bpp;
1552 } else {
1553 rbl = Dy * Bpp;
1554 }
1555
1556 if (rotating == ROTATE_X) {
1557 for (y = 0; y < Dy; y++) {
1558 for (x = 0; x < Dx; x++) {
1559 xn = Dx - x - 1;
1560 yn = y;
1561 ROT_COPY
1562 if (0) fprintf(stderr, "rcurs: %d %d %d %d\n", x, y, xn, yn);
1563 }
1564 }
1565 } else if (rotating == ROTATE_Y) {
1566 for (y = 0; y < Dy; y++) {
1567 for (x = 0; x < Dx; x++) {
1568 xn = x;
1569 yn = Dy - y - 1;
1570 ROT_COPY
1571 }
1572 }
1573 } else if (rotating == ROTATE_XY) {
1574 for (y = 0; y < Dy; y++) {
1575 for (x = 0; x < Dx; x++) {
1576 xn = Dx - x - 1;
1577 yn = Dy - y - 1;
1578 ROT_COPY
1579 }
1580 }
1581 } else if (rotating == ROTATE_90) {
1582 for (y = 0; y < Dy; y++) {
1583 for (x = 0; x < Dx; x++) {
1584 xn = Dy - y - 1;
1585 yn = x;
1586 ROT_COPY
1587 }
1588 }
1589 } else if (rotating == ROTATE_90X) {
1590 for (y = 0; y < Dy; y++) {
1591 for (x = 0; x < Dx; x++) {
1592 xn = y;
1593 yn = x;
1594 ROT_COPY
1595 }
1596 }
1597 } else if (rotating == ROTATE_90Y) {
1598 for (y = 0; y < Dy; y++) {
1599 for (x = 0; x < Dx; x++) {
1600 xn = Dy - y - 1;
1601 yn = Dx - x - 1;
1602 ROT_COPY
1603 }
1604 }
1605 } else if (rotating == ROTATE_270) {
1606 for (y = 0; y < Dy; y++) {
1607 for (x = 0; x < Dx; x++) {
1608 xn = y;
1609 yn = Dx - x - 1;
1610 ROT_COPY
1611 }
1612 }
1613 }
1614 }
1615
mark_wrapper(int x1,int y1,int x2,int y2)1616 void mark_wrapper(int x1, int y1, int x2, int y2) {
1617 int t, r_x1 = x1, r_y1 = y1, r_x2 = x2, r_y2 = y2;
1618
1619 if (rotating) {
1620 /* well we hope rot_fb will always be the last one... */
1621 rotate_coords(x1, y1, &r_x1, &r_y1, -1, -1);
1622 rotate_coords(x2, y2, &r_x2, &r_y2, -1, -1);
1623 rotate_fb(x1, y1, x2, y2);
1624 if (r_x1 > r_x2) {
1625 t = r_x1;
1626 r_x1 = r_x2;
1627 r_x2 = t;
1628 }
1629 if (r_y1 > r_y2) {
1630 t = r_y1;
1631 r_y1 = r_y2;
1632 r_y2 = t;
1633 }
1634 /* painting errors */
1635 r_x1--;
1636 r_x2++;
1637 r_y1--;
1638 r_y2++;
1639 }
1640 rfbMarkRectAsModified(screen, r_x1, r_y1, r_x2, r_y2);
1641 }
1642
mark_rect_as_modified(int x1,int y1,int x2,int y2,int force)1643 void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force) {
1644
1645 if (damage_time != 0) {
1646 /*
1647 * This is not XDAMAGE, rather a hack for testing
1648 * where we allow the framebuffer to be corrupted for
1649 * damage_delay seconds.
1650 */
1651 int debug = 0;
1652 if (time(NULL) > damage_time + damage_delay) {
1653 if (! quiet) {
1654 rfbLog("damaging turned off.\n");
1655 }
1656 damage_time = 0;
1657 damage_delay = 0;
1658 } else {
1659 if (debug) {
1660 rfbLog("damaging viewer fb by not marking "
1661 "rect: %d,%d,%d,%d\n", x1, y1, x2, y2);
1662 }
1663 return;
1664 }
1665 }
1666
1667
1668 if (rfb_fb == main_fb || force) {
1669 mark_wrapper(x1, y1, x2, y2);
1670 return;
1671 }
1672
1673 if (cmap8to24) {
1674 bpp8to24(x1, y1, x2, y2);
1675 }
1676
1677 if (scaling) {
1678 scale_and_mark_rect(x1, y1, x2, y2, 1);
1679 } else {
1680 mark_wrapper(x1, y1, x2, y2);
1681 }
1682 }
1683
1684 /*
1685 * Notifies libvncserver of a changed hint rectangle.
1686 */
mark_hint(hint_t hint)1687 static void mark_hint(hint_t hint) {
1688 int x = hint.x;
1689 int y = hint.y;
1690 int w = hint.w;
1691 int h = hint.h;
1692
1693 mark_rect_as_modified(x, y, x + w, y + h, 0);
1694 }
1695
1696 /*
1697 * copy_tiles() gives a slight improvement over copy_tile() since
1698 * adjacent runs of tiles are done all at once there is some savings
1699 * due to contiguous memory access. Not a great speedup, but in some
1700 * cases it can be up to 2X. Even more on a SunRay or ShadowFB where
1701 * no graphics hardware is involved in the read. Generally, graphics
1702 * devices are optimized for write, not read, so we are limited by the
1703 * read bandwidth, sometimes only 5 MB/sec on otherwise fast hardware.
1704 */
1705 static int *first_line = NULL, *last_line = NULL;
1706 static unsigned short *left_diff = NULL, *right_diff = NULL;
1707
copy_tiles(int tx,int ty,int nt)1708 static int copy_tiles(int tx, int ty, int nt) {
1709 int x, y, line;
1710 int size_x, size_y, width1, width2;
1711 int off, len, n, dw, dx, t;
1712 int w1, w2, dx1, dx2; /* tmps for normal and short tiles */
1713 int pixelsize = bpp/8;
1714 int first_min, last_max;
1715 int first_x = -1, last_x = -1;
1716 static int prev_ntiles_x = -1;
1717
1718 char *src, *dst, *s_src, *s_dst, *m_src, *m_dst;
1719 char *h_src, *h_dst;
1720 if (unixpw_in_progress) return 0;
1721
1722 if (ntiles_x != prev_ntiles_x && first_line != NULL) {
1723 free(first_line); first_line = NULL;
1724 free(last_line); last_line = NULL;
1725 free(left_diff); left_diff = NULL;
1726 free(right_diff); right_diff = NULL;
1727 }
1728
1729 if (first_line == NULL) {
1730 /* allocate arrays first time in. */
1731 int n = ntiles_x + 1;
1732 rfbLog("copy_tiles: allocating first_line at size %d\n", n);
1733 first_line = (int *) malloc((size_t) (n * sizeof(int)));
1734 last_line = (int *) malloc((size_t) (n * sizeof(int)));
1735 left_diff = (unsigned short *)
1736 malloc((size_t) (n * sizeof(unsigned short)));
1737 right_diff = (unsigned short *)
1738 malloc((size_t) (n * sizeof(unsigned short)));
1739 }
1740 prev_ntiles_x = ntiles_x;
1741
1742 x = tx * tile_x;
1743 y = ty * tile_y;
1744
1745 size_x = dpy_x - x;
1746 if ( size_x > tile_x * nt ) {
1747 size_x = tile_x * nt;
1748 width1 = tile_x;
1749 width2 = tile_x;
1750 } else {
1751 /* short tile */
1752 width1 = tile_x; /* internal tile */
1753 width2 = size_x - (nt - 1) * tile_x; /* right hand tile */
1754 }
1755
1756 size_y = dpy_y - y;
1757 if ( size_y > tile_y ) {
1758 size_y = tile_y;
1759 }
1760
1761 n = tx + ty * ntiles_x; /* number of the first tile */
1762
1763 if (blackouts && tile_blackout[n].cover == 2) {
1764 /*
1765 * If there are blackouts and this tile is completely covered
1766 * no need to poll screen or do anything else..
1767 * n.b. we are in single copy_tile mode: nt=1
1768 */
1769 tile_has_diff[n] = 0;
1770 return(0);
1771 }
1772
1773 X_LOCK;
1774 XRANDR_SET_TRAP_RET(-1, "copy_tile-set");
1775 /* read in the whole tile run at once: */
1776 copy_image(tile_row[nt], x, y, size_x, size_y);
1777 XRANDR_CHK_TRAP_RET(-1, "copy_tile-chk");
1778
1779
1780 X_UNLOCK;
1781
1782 if (blackouts && tile_blackout[n].cover == 1) {
1783 /*
1784 * If there are blackouts and this tile is partially covered
1785 * we should re-black-out the portion.
1786 * n.b. we are in single copy_tile mode: nt=1
1787 */
1788 int x1, x2, y1, y2, b;
1789 int w, s, fill = 0;
1790
1791 for (b=0; b < tile_blackout[n].count; b++) {
1792 char *b_dst = tile_row[nt]->data;
1793
1794 x1 = tile_blackout[n].bo[b].x1 - x;
1795 y1 = tile_blackout[n].bo[b].y1 - y;
1796 x2 = tile_blackout[n].bo[b].x2 - x;
1797 y2 = tile_blackout[n].bo[b].y2 - y;
1798
1799 w = (x2 - x1) * pixelsize;
1800 s = x1 * pixelsize;
1801
1802 for (line = 0; line < size_y; line++) {
1803 if (y1 <= line && line < y2) {
1804 memset(b_dst + s, fill, (size_t) w);
1805 }
1806 b_dst += tile_row[nt]->bytes_per_line;
1807 }
1808 }
1809 }
1810
1811 src = tile_row[nt]->data;
1812 dst = main_fb + y * main_bytes_per_line + x * pixelsize;
1813
1814 s_src = src;
1815 s_dst = dst;
1816
1817 for (t=1; t <= nt; t++) {
1818 first_line[t] = -1;
1819 }
1820
1821 /* find the first line with difference: */
1822 w1 = width1 * pixelsize;
1823 w2 = width2 * pixelsize;
1824
1825 /* foreach line: */
1826 for (line = 0; line < size_y; line++) {
1827 /* foreach horizontal tile: */
1828 for (t=1; t <= nt; t++) {
1829 if (first_line[t] != -1) {
1830 continue;
1831 }
1832
1833 off = (t-1) * w1;
1834 if (t == nt) {
1835 len = w2; /* possible short tile */
1836 } else {
1837 len = w1;
1838 }
1839
1840 if (memcmp(s_dst + off, s_src + off, len)) {
1841 first_line[t] = line;
1842 }
1843 }
1844 s_src += tile_row[nt]->bytes_per_line;
1845 s_dst += main_bytes_per_line;
1846 }
1847
1848 /* see if there were any differences for any tile: */
1849 first_min = -1;
1850 for (t=1; t <= nt; t++) {
1851 tile_tried[n+(t-1)] = 1;
1852 if (first_line[t] != -1) {
1853 if (first_min == -1 || first_line[t] < first_min) {
1854 first_min = first_line[t];
1855 }
1856 }
1857 }
1858 if (first_min == -1) {
1859 /* no tile has a difference, note this and get out: */
1860 for (t=1; t <= nt; t++) {
1861 tile_has_diff[n+(t-1)] = 0;
1862 }
1863 return(0);
1864 } else {
1865 /*
1866 * at least one tile has a difference. make sure info
1867 * is recorded (e.g. sometimes we guess tiles and they
1868 * came in with tile_has_diff 0)
1869 */
1870 for (t=1; t <= nt; t++) {
1871 if (first_line[t] == -1) {
1872 tile_has_diff[n+(t-1)] = 0;
1873 } else {
1874 tile_has_diff[n+(t-1)] = 1;
1875 }
1876 }
1877 }
1878
1879 m_src = src + (tile_row[nt]->bytes_per_line * size_y);
1880 m_dst = dst + (main_bytes_per_line * size_y);
1881
1882 for (t=1; t <= nt; t++) {
1883 last_line[t] = first_line[t];
1884 }
1885
1886 /* find the last line with difference: */
1887 w1 = width1 * pixelsize;
1888 w2 = width2 * pixelsize;
1889
1890 /* foreach line: */
1891 for (line = size_y - 1; line > first_min; line--) {
1892
1893 m_src -= tile_row[nt]->bytes_per_line;
1894 m_dst -= main_bytes_per_line;
1895
1896 /* foreach tile: */
1897 for (t=1; t <= nt; t++) {
1898 if (first_line[t] == -1
1899 || last_line[t] != first_line[t]) {
1900 /* tile has no changes or already done */
1901 continue;
1902 }
1903
1904 off = (t-1) * w1;
1905 if (t == nt) {
1906 len = w2; /* possible short tile */
1907 } else {
1908 len = w1;
1909 }
1910 if (memcmp(m_dst + off, m_src + off, len)) {
1911 last_line[t] = line;
1912 }
1913 }
1914 }
1915
1916 /*
1917 * determine the farthest down last changed line
1918 * will be used below to limit our memcpy() to the framebuffer.
1919 */
1920 last_max = -1;
1921 for (t=1; t <= nt; t++) {
1922 if (first_line[t] == -1) {
1923 continue;
1924 }
1925 if (last_max == -1 || last_line[t] > last_max) {
1926 last_max = last_line[t];
1927 }
1928 }
1929
1930 /* look for differences on left and right hand edges: */
1931 for (t=1; t <= nt; t++) {
1932 left_diff[t] = 0;
1933 right_diff[t] = 0;
1934 }
1935
1936 h_src = src;
1937 h_dst = dst;
1938
1939 w1 = width1 * pixelsize;
1940 w2 = width2 * pixelsize;
1941
1942 dx1 = (width1 - tile_fuzz) * pixelsize;
1943 dx2 = (width2 - tile_fuzz) * pixelsize;
1944 dw = tile_fuzz * pixelsize;
1945
1946 /* foreach line: */
1947 for (line = 0; line < size_y; line++) {
1948 /* foreach tile: */
1949 for (t=1; t <= nt; t++) {
1950 if (first_line[t] == -1) {
1951 /* tile has no changes at all */
1952 continue;
1953 }
1954
1955 off = (t-1) * w1;
1956 if (t == nt) {
1957 dx = dx2; /* possible short tile */
1958 if (dx <= 0) {
1959 break;
1960 }
1961 } else {
1962 dx = dx1;
1963 }
1964
1965 if (! left_diff[t] && memcmp(h_dst + off,
1966 h_src + off, dw)) {
1967 left_diff[t] = 1;
1968 }
1969 if (! right_diff[t] && memcmp(h_dst + off + dx,
1970 h_src + off + dx, dw) ) {
1971 right_diff[t] = 1;
1972 }
1973 }
1974 h_src += tile_row[nt]->bytes_per_line;
1975 h_dst += main_bytes_per_line;
1976 }
1977
1978 /* now finally copy the difference to the rfb framebuffer: */
1979 s_src = src + tile_row[nt]->bytes_per_line * first_min;
1980 s_dst = dst + main_bytes_per_line * first_min;
1981
1982 for (line = first_min; line <= last_max; line++) {
1983 /* for I/O speed we do not do this tile by tile */
1984 memcpy(s_dst, s_src, size_x * pixelsize);
1985 if (nt == 1) {
1986 /*
1987 * optimization for tall skinny lines, e.g. wm
1988 * frame. try to find first_x and last_x to limit
1989 * the size of the hint. could help for a slow
1990 * link. Unfortunately we spent a lot of time
1991 * reading in the many tiles.
1992 *
1993 * BTW, we like to think the above memcpy leaves
1994 * the data we use below in the cache... (but
1995 * it could be two 128 byte segments at 32bpp)
1996 * so this inner loop is not as bad as it seems.
1997 */
1998 int k, kx;
1999 kx = pixelsize;
2000 for (k=0; k<size_x; k++) {
2001 if (memcmp(s_dst + k*kx, s_src + k*kx, kx)) {
2002 if (first_x == -1 || k < first_x) {
2003 first_x = k;
2004 }
2005 if (last_x == -1 || k > last_x) {
2006 last_x = k;
2007 }
2008 }
2009 }
2010 }
2011 s_src += tile_row[nt]->bytes_per_line;
2012 s_dst += main_bytes_per_line;
2013 }
2014
2015 /* record all the info in the region array for this tile: */
2016 for (t=1; t <= nt; t++) {
2017 int s = t - 1;
2018
2019 if (first_line[t] == -1) {
2020 /* tile unchanged */
2021 continue;
2022 }
2023 tile_region[n+s].first_line = first_line[t];
2024 tile_region[n+s].last_line = last_line[t];
2025
2026 tile_region[n+s].first_x = first_x;
2027 tile_region[n+s].last_x = last_x;
2028
2029 tile_region[n+s].top_diff = 0;
2030 tile_region[n+s].bot_diff = 0;
2031 if ( first_line[t] < tile_fuzz ) {
2032 tile_region[n+s].top_diff = 1;
2033 }
2034 if ( last_line[t] > (size_y - 1) - tile_fuzz ) {
2035 tile_region[n+s].bot_diff = 1;
2036 }
2037
2038 tile_region[n+s].left_diff = left_diff[t];
2039 tile_region[n+s].right_diff = right_diff[t];
2040
2041 tile_copied[n+s] = 1;
2042 }
2043
2044 return(1);
2045 }
2046
2047 /*
2048 * The copy_tile() call in the loop below copies the changed tile into
2049 * the rfb framebuffer. Note that copy_tile() sets the tile_region
2050 * struct to have info about the y-range of the changed region and also
2051 * whether the tile edges contain diffs (within distance tile_fuzz).
2052 *
2053 * We use this tile_region info to try to guess if the downward and right
2054 * tiles will have diffs. These tiles will be checked later in the loop
2055 * (since y+1 > y and x+1 > x).
2056 *
2057 * See copy_tiles_backward_pass() for analogous checking upward and
2058 * left tiles.
2059 */
copy_all_tiles(void)2060 static int copy_all_tiles(void) {
2061 int x, y, n, m;
2062 int diffs = 0, ct;
2063
2064 if (unixpw_in_progress) return 0;
2065
2066 for (y=0; y < ntiles_y; y++) {
2067 for (x=0; x < ntiles_x; x++) {
2068 n = x + y * ntiles_x;
2069
2070 if (tile_has_diff[n]) {
2071 ct = copy_tiles(x, y, 1);
2072 if (ct < 0) return ct; /* fatal */
2073 }
2074 if (! tile_has_diff[n]) {
2075 /*
2076 * n.b. copy_tiles() may have detected
2077 * no change and reset tile_has_diff to 0.
2078 */
2079 continue;
2080 }
2081 diffs++;
2082
2083 /* neighboring tile downward: */
2084 if ( (y+1) < ntiles_y && tile_region[n].bot_diff) {
2085 m = x + (y+1) * ntiles_x;
2086 if (! tile_has_diff[m]) {
2087 tile_has_diff[m] = 2;
2088 }
2089 }
2090 /* neighboring tile to right: */
2091 if ( (x+1) < ntiles_x && tile_region[n].right_diff) {
2092 m = (x+1) + y * ntiles_x;
2093 if (! tile_has_diff[m]) {
2094 tile_has_diff[m] = 2;
2095 }
2096 }
2097 }
2098 }
2099 return diffs;
2100 }
2101
2102 /*
2103 * Routine analogous to copy_all_tiles() above, but for horizontal runs
2104 * of adjacent changed tiles.
2105 */
copy_all_tile_runs(void)2106 static int copy_all_tile_runs(void) {
2107 int x, y, n, m, i;
2108 int diffs = 0, ct;
2109 int in_run = 0, run = 0;
2110 int ntave = 0, ntcnt = 0;
2111
2112 if (unixpw_in_progress) return 0;
2113
2114 for (y=0; y < ntiles_y; y++) {
2115 for (x=0; x < ntiles_x + 1; x++) {
2116 n = x + y * ntiles_x;
2117
2118 if (x != ntiles_x && tile_has_diff[n]) {
2119 in_run = 1;
2120 run++;
2121 } else {
2122 if (! in_run) {
2123 in_run = 0;
2124 run = 0;
2125 continue;
2126 }
2127 ct = copy_tiles(x - run, y, run);
2128 if (ct < 0) return ct; /* fatal */
2129
2130 ntcnt++;
2131 ntave += run;
2132 diffs += run;
2133
2134 /* neighboring tile downward: */
2135 for (i=1; i <= run; i++) {
2136 if ((y+1) < ntiles_y
2137 && tile_region[n-i].bot_diff) {
2138 m = (x-i) + (y+1) * ntiles_x;
2139 if (! tile_has_diff[m]) {
2140 tile_has_diff[m] = 2;
2141 }
2142 }
2143 }
2144
2145 /* neighboring tile to right: */
2146 if (((x-1)+1) < ntiles_x
2147 && tile_region[n-1].right_diff) {
2148 m = ((x-1)+1) + y * ntiles_x;
2149 if (! tile_has_diff[m]) {
2150 tile_has_diff[m] = 2;
2151 }
2152
2153 /* note that this starts a new run */
2154 in_run = 1;
2155 run = 1;
2156 } else {
2157 in_run = 0;
2158 run = 0;
2159 }
2160 }
2161 }
2162 /*
2163 * Could some activity go here, to emulate threaded
2164 * behavior by servicing some libvncserver tasks?
2165 */
2166 }
2167 return diffs;
2168 }
2169
2170 /*
2171 * Here starts a bunch of heuristics to guess/detect changed tiles.
2172 * They are:
2173 * copy_tiles_backward_pass, fill_tile_gaps/gap_try, grow_islands/island_try
2174 */
2175
2176 /*
2177 * Try to predict whether the upward and/or leftward tile has been modified.
2178 * copy_all_tiles() has already done downward and rightward tiles.
2179 */
copy_tiles_backward_pass(void)2180 static int copy_tiles_backward_pass(void) {
2181 int x, y, n, m;
2182 int diffs = 0, ct;
2183
2184 if (unixpw_in_progress) return 0;
2185
2186 for (y = ntiles_y - 1; y >= 0; y--) {
2187 for (x = ntiles_x - 1; x >= 0; x--) {
2188 n = x + y * ntiles_x; /* number of this tile */
2189
2190 if (! tile_has_diff[n]) {
2191 continue;
2192 }
2193
2194 m = x + (y-1) * ntiles_x; /* neighboring tile upward */
2195
2196 if (y >= 1 && ! tile_has_diff[m] && tile_region[n].top_diff) {
2197 if (! tile_tried[m]) {
2198 tile_has_diff[m] = 2;
2199 ct = copy_tiles(x, y-1, 1);
2200 if (ct < 0) return ct; /* fatal */
2201 }
2202 }
2203
2204 m = (x-1) + y * ntiles_x; /* neighboring tile to left */
2205
2206 if (x >= 1 && ! tile_has_diff[m] && tile_region[n].left_diff) {
2207 if (! tile_tried[m]) {
2208 tile_has_diff[m] = 2;
2209 ct = copy_tiles(x-1, y, 1);
2210 if (ct < 0) return ct; /* fatal */
2211 }
2212 }
2213 }
2214 }
2215 for (n=0; n < ntiles; n++) {
2216 if (tile_has_diff[n]) {
2217 diffs++;
2218 }
2219 }
2220 return diffs;
2221 }
2222
copy_tiles_additional_pass(void)2223 static int copy_tiles_additional_pass(void) {
2224 int x, y, n;
2225 int diffs = 0, ct;
2226
2227 if (unixpw_in_progress) return 0;
2228
2229 for (y=0; y < ntiles_y; y++) {
2230 for (x=0; x < ntiles_x; x++) {
2231 n = x + y * ntiles_x; /* number of this tile */
2232
2233 if (! tile_has_diff[n]) {
2234 continue;
2235 }
2236 if (tile_copied[n]) {
2237 continue;
2238 }
2239
2240 ct = copy_tiles(x, y, 1);
2241 if (ct < 0) return ct; /* fatal */
2242 }
2243 }
2244 for (n=0; n < ntiles; n++) {
2245 if (tile_has_diff[n]) {
2246 diffs++;
2247 }
2248 }
2249 return diffs;
2250 }
2251
gap_try(int x,int y,int * run,int * saw,int along_x)2252 static int gap_try(int x, int y, int *run, int *saw, int along_x) {
2253 int n, m, i, xt, yt, ct;
2254
2255 n = x + y * ntiles_x;
2256
2257 if (! tile_has_diff[n]) {
2258 if (*saw) {
2259 (*run)++; /* extend the gap run. */
2260 }
2261 return 0;
2262 }
2263 if (! *saw || *run == 0 || *run > gaps_fill) {
2264 *run = 0; /* unacceptable run. */
2265 *saw = 1;
2266 return 0;
2267 }
2268
2269 for (i=1; i <= *run; i++) { /* iterate thru the run. */
2270 if (along_x) {
2271 xt = x - i;
2272 yt = y;
2273 } else {
2274 xt = x;
2275 yt = y - i;
2276 }
2277
2278 m = xt + yt * ntiles_x;
2279 if (tile_tried[m]) { /* do not repeat tiles */
2280 continue;
2281 }
2282
2283 ct = copy_tiles(xt, yt, 1);
2284 if (ct < 0) return ct; /* fatal */
2285 }
2286 *run = 0;
2287 *saw = 1;
2288 return 1;
2289 }
2290
2291 /*
2292 * Look for small gaps of unchanged tiles that may actually contain changes.
2293 * E.g. when paging up and down in a web broswer or terminal there can
2294 * be a distracting delayed filling in of such gaps. gaps_fill is the
2295 * tweak parameter that sets the width of the gaps that are checked.
2296 *
2297 * BTW, grow_islands() is actually pretty successful at doing this too...
2298 */
fill_tile_gaps(void)2299 static int fill_tile_gaps(void) {
2300 int x, y, run, saw;
2301 int n, diffs = 0, ct;
2302
2303 /* horizontal: */
2304 for (y=0; y < ntiles_y; y++) {
2305 run = 0;
2306 saw = 0;
2307 for (x=0; x < ntiles_x; x++) {
2308 ct = gap_try(x, y, &run, &saw, 1);
2309 if (ct < 0) return ct; /* fatal */
2310 }
2311 }
2312
2313 /* vertical: */
2314 for (x=0; x < ntiles_x; x++) {
2315 run = 0;
2316 saw = 0;
2317 for (y=0; y < ntiles_y; y++) {
2318 ct = gap_try(x, y, &run, &saw, 0);
2319 if (ct < 0) return ct; /* fatal */
2320 }
2321 }
2322
2323 for (n=0; n < ntiles; n++) {
2324 if (tile_has_diff[n]) {
2325 diffs++;
2326 }
2327 }
2328 return diffs;
2329 }
2330
island_try(int x,int y,int u,int v,int * run)2331 static int island_try(int x, int y, int u, int v, int *run) {
2332 int n, m, ct;
2333
2334 n = x + y * ntiles_x;
2335 m = u + v * ntiles_x;
2336
2337 if (tile_has_diff[n]) {
2338 (*run)++;
2339 } else {
2340 *run = 0;
2341 }
2342
2343 if (tile_has_diff[n] && ! tile_has_diff[m]) {
2344 /* found a discontinuity */
2345
2346 if (tile_tried[m]) {
2347 return 0;
2348 } else if (*run < grow_fill) {
2349 return 0;
2350 }
2351
2352 ct = copy_tiles(u, v, 1);
2353 if (ct < 0) return ct; /* fatal */
2354 }
2355 return 1;
2356 }
2357
2358 /*
2359 * Scan looking for discontinuities in tile_has_diff[]. Try to extend
2360 * the boundary of the discontinuity (i.e. make the island larger).
2361 * Vertical scans are skipped since they do not seem to yield much...
2362 */
grow_islands(void)2363 static int grow_islands(void) {
2364 int x, y, n, run;
2365 int diffs = 0, ct;
2366
2367 /*
2368 * n.b. the way we scan here should keep an extension going,
2369 * and so also fill in gaps effectively...
2370 */
2371
2372 /* left to right: */
2373 for (y=0; y < ntiles_y; y++) {
2374 run = 0;
2375 for (x=0; x <= ntiles_x - 2; x++) {
2376 ct = island_try(x, y, x+1, y, &run);
2377 if (ct < 0) return ct; /* fatal */
2378 }
2379 }
2380 /* right to left: */
2381 for (y=0; y < ntiles_y; y++) {
2382 run = 0;
2383 for (x = ntiles_x - 1; x >= 1; x--) {
2384 ct = island_try(x, y, x-1, y, &run);
2385 if (ct < 0) return ct; /* fatal */
2386 }
2387 }
2388 for (n=0; n < ntiles; n++) {
2389 if (tile_has_diff[n]) {
2390 diffs++;
2391 }
2392 }
2393 return diffs;
2394 }
2395
2396 /*
2397 * Fill the framebuffer with zeros for each blackout region
2398 */
blackout_regions(void)2399 static void blackout_regions(void) {
2400 int i;
2401 for (i=0; i < blackouts; i++) {
2402 zero_fb(blackr[i].x1, blackr[i].y1, blackr[i].x2, blackr[i].y2);
2403 }
2404 }
2405
2406 /*
2407 * copy the whole X screen to the rfb framebuffer. For a large enough
2408 * number of changed tiles, this is faster than tiles scheme at retrieving
2409 * the info from the X server. Bandwidth to client and compression time
2410 * are other issues... use -fs 1.0 to disable.
2411 */
copy_screen(void)2412 int copy_screen(void) {
2413 char *fbp;
2414 int i, y, block_size;
2415
2416 if (! fs_factor) {
2417 return 0;
2418 }
2419 if (debug_tiles) fprintf(stderr, "copy_screen\n");
2420
2421 if (unixpw_in_progress) return 0;
2422
2423
2424 if (! main_fb) {
2425 return 0;
2426 }
2427
2428 block_size = ((dpy_y/fs_factor) * main_bytes_per_line);
2429
2430 fbp = main_fb;
2431 y = 0;
2432
2433 X_LOCK;
2434
2435 /* screen may be too big for 1 shm area, so broken into fs_factor */
2436 for (i=0; i < fs_factor; i++) {
2437 XRANDR_SET_TRAP_RET(-1, "copy_screen-set");
2438 copy_image(fullscreen, 0, y, 0, 0);
2439 XRANDR_CHK_TRAP_RET(-1, "copy_screen-chk");
2440
2441 memcpy(fbp, fullscreen->data, (size_t) block_size);
2442
2443 y += dpy_y / fs_factor;
2444 fbp += block_size;
2445 }
2446
2447 X_UNLOCK;
2448
2449 if (blackouts) {
2450 blackout_regions();
2451 }
2452
2453 mark_rect_as_modified(0, 0, dpy_x, dpy_y, 0);
2454 return 0;
2455 }
2456
2457 #include <rfb/default8x16.h>
2458
2459 /*
2460 * Color values from the vcsadump program.
2461 * void dumpcss(FILE *fp, char *attribs_used)
2462 * char *colormap[] = {
2463 * "#000000", "#0000AA", "#00AA00", "#00AAAA", "#AA0000", "#AA00AA", "#AA5500", "#AAAAAA",
2464 * "#555555", "#5555AA", "#55FF55", "#55FFFF", "#FF5555", "#FF55FF", "#FFFF00", "#FFFFFF" };
2465 */
2466
2467 static unsigned char console_cmap[16*3]={
2468 /* 0 */ 0x00, 0x00, 0x00,
2469 /* 1 */ 0x00, 0x00, 0xAA,
2470 /* 2 */ 0x00, 0xAA, 0x00,
2471 /* 3 */ 0x00, 0xAA, 0xAA,
2472 /* 4 */ 0xAA, 0x00, 0x00,
2473 /* 5 */ 0xAA, 0x00, 0xAA,
2474 /* 6 */ 0xAA, 0x55, 0x00,
2475 /* 7 */ 0xAA, 0xAA, 0xAA,
2476 /* 8 */ 0x55, 0x55, 0x55,
2477 /* 9 */ 0x55, 0x55, 0xAA,
2478 /* 10 */ 0x55, 0xFF, 0x55,
2479 /* 11 */ 0x55, 0xFF, 0xFF,
2480 /* 12 */ 0xFF, 0x55, 0x55,
2481 /* 13 */ 0xFF, 0x55, 0xFF,
2482 /* 14 */ 0xFF, 0xFF, 0x00,
2483 /* 15 */ 0xFF, 0xFF, 0xFF
2484 };
2485
snap_vcsa_rawfb(void)2486 static void snap_vcsa_rawfb(void) {
2487 int n;
2488 char *dst;
2489 char buf[32];
2490 int i, len, del;
2491 unsigned char rows, cols, xpos, ypos;
2492 static int prev_rows = -1, prev_cols = -1;
2493 static unsigned char prev_xpos = -1, prev_ypos = -1;
2494 static char *vcsabuf = NULL;
2495 static char *vcsabuf0 = NULL;
2496 static unsigned int color_tab[16];
2497 static int Cw = 8, Ch = 16;
2498 static int db = -1, first = 1;
2499 int created = 0;
2500 rfbScreenInfo s;
2501 rfbScreenInfoPtr fake_screen = &s;
2502 int Bpp = raw_fb_native_bpp / 8;
2503
2504 if (db < 0) {
2505 if (getenv("X11VNC_DEBUG_VCSA")) {
2506 db = atoi(getenv("X11VNC_DEBUG_VCSA"));
2507 } else {
2508 db = 0;
2509 }
2510 }
2511
2512 if (first) {
2513 unsigned int rm = raw_fb_native_red_mask;
2514 unsigned int gm = raw_fb_native_green_mask;
2515 unsigned int bm = raw_fb_native_blue_mask;
2516 unsigned int rs = raw_fb_native_red_shift;
2517 unsigned int gs = raw_fb_native_green_shift;
2518 unsigned int bs = raw_fb_native_blue_shift;
2519 unsigned int rx = raw_fb_native_red_max;
2520 unsigned int gx = raw_fb_native_green_max;
2521 unsigned int bx = raw_fb_native_blue_max;
2522
2523 for (i=0; i < 16; i++) {
2524 int r = console_cmap[3*i+0];
2525 int g = console_cmap[3*i+1];
2526 int b = console_cmap[3*i+2];
2527 r = rx * r / 255;
2528 g = gx * g / 255;
2529 b = bx * b / 255;
2530 color_tab[i] = (r << rs) | (g << gs) | (b << bs);
2531 if (db) fprintf(stderr, "cmap[%02d] 0x%08x %04d %04d %04d\n", i, color_tab[i], r, g, b);
2532 if (i != 0 && getenv("RAWFB_VCSA_BW")) {
2533 color_tab[i] = rm | gm | bm;
2534 }
2535 }
2536 }
2537 first = 0;
2538
2539 lseek(raw_fb_fd, 0, SEEK_SET);
2540 len = 4;
2541 del = 0;
2542 memset(buf, 0, sizeof(buf));
2543 while (len > 0) {
2544 n = read(raw_fb_fd, buf + del, len);
2545 if (n > 0) {
2546 del += n;
2547 len -= n;
2548 } else if (n == 0) {
2549 break;
2550 } else if (errno != EINTR && errno != EAGAIN) {
2551 break;
2552 }
2553 }
2554
2555 rows = (unsigned char) buf[0];
2556 cols = (unsigned char) buf[1];
2557 xpos = (unsigned char) buf[2];
2558 ypos = (unsigned char) buf[3];
2559
2560 if (db) fprintf(stderr, "rows=%d cols=%d xpos=%d ypos=%d Bpp=%d\n", rows, cols, xpos, ypos, Bpp);
2561 if (rows == 0 || cols == 0) {
2562 usleep(100 * 1000);
2563 return;
2564 }
2565
2566 if (vcsabuf == NULL || prev_rows != rows || prev_cols != cols) {
2567 if (vcsabuf) {
2568 free(vcsabuf);
2569 free(vcsabuf0);
2570 }
2571 vcsabuf = (char *) calloc(2 * rows * cols, 1);
2572 vcsabuf0 = (char *) calloc(2 * rows * cols, 1);
2573 created = 1;
2574
2575 if (prev_rows != -1 && prev_cols != -1) {
2576 do_new_fb(1);
2577 }
2578
2579 prev_rows = rows;
2580 prev_cols = cols;
2581 }
2582
2583 if (!rfbEndianTest) {
2584 unsigned char tc = rows;
2585 rows = cols;
2586 cols = tc;
2587
2588 tc = xpos;
2589 xpos = ypos;
2590 ypos = tc;
2591 }
2592
2593 len = 2 * rows * cols;
2594 del = 0;
2595 memset(vcsabuf, 0, len);
2596 while (len > 0) {
2597 n = read(raw_fb_fd, vcsabuf + del, len);
2598 if (n > 0) {
2599 del += n;
2600 len -= n;
2601 } else if (n == 0) {
2602 break;
2603 } else if (errno != EINTR && errno != EAGAIN) {
2604 break;
2605 }
2606 }
2607
2608 fake_screen->frameBuffer = snap->data;
2609 fake_screen->paddedWidthInBytes = snap->bytes_per_line;
2610 fake_screen->serverFormat.bitsPerPixel = raw_fb_native_bpp;
2611 fake_screen->width = snap->width;
2612 fake_screen->height = snap->height;
2613
2614 for (i=0; i < rows * cols; i++) {
2615 int ix, iy, x, y, w, h;
2616 unsigned char chr = 0;
2617 unsigned char attr;
2618 unsigned int fore, back;
2619 unsigned short *usp;
2620 unsigned int *uip;
2621 chr = (unsigned char) vcsabuf[2*i];
2622 attr = vcsabuf[2*i+1];
2623
2624 iy = i / cols;
2625 ix = i - iy * cols;
2626
2627 if (ix == prev_xpos && iy == prev_ypos) {
2628 ;
2629 } else if (ix == xpos && iy == ypos) {
2630 ;
2631 } else if (!created && chr == vcsabuf0[2*i] && attr == vcsabuf0[2*i+1]) {
2632 continue;
2633 }
2634
2635 if (!rfbEndianTest) {
2636 unsigned char tc = chr;
2637 chr = attr;
2638 attr = tc;
2639 }
2640
2641 y = iy * Ch;
2642 x = ix * Cw;
2643 dst = snap->data + y * snap->bytes_per_line + x * Bpp;
2644
2645 fore = color_tab[attr & 0xf];
2646 back = color_tab[(attr >> 4) & 0x7];
2647
2648 if (ix == xpos && iy == ypos) {
2649 unsigned int ti = fore;
2650 fore = back;
2651 back = ti;
2652 }
2653
2654 for (h = 0; h < Ch; h++) {
2655 if (Bpp == 1) {
2656 memset(dst, back, Cw);
2657 } else if (Bpp == 2) {
2658 for (w = 0; w < Cw; w++) {
2659 usp = (unsigned short *) (dst + w*Bpp);
2660 *usp = (unsigned short) back;
2661 }
2662 } else if (Bpp == 4) {
2663 for (w = 0; w < Cw; w++) {
2664 uip = (unsigned int *) (dst + w*Bpp);
2665 *uip = (unsigned int) back;
2666 }
2667 }
2668 dst += snap->bytes_per_line;
2669 }
2670 rfbDrawChar(fake_screen, &default8x16Font, x, y + Ch, chr, fore);
2671 }
2672 memcpy(vcsabuf0, vcsabuf, 2 * rows * cols);
2673 prev_xpos = xpos;
2674 prev_ypos = ypos;
2675 }
2676
snap_all_rawfb(void)2677 static void snap_all_rawfb(void) {
2678 int pixelsize = bpp/8;
2679 int n, sz;
2680 char *dst;
2681 static char *unclipped_dst = NULL;
2682 static int unclipped_len = 0;
2683
2684 dst = snap->data;
2685
2686 if (xform24to32 && bpp == 32) {
2687 pixelsize = 3;
2688 }
2689 sz = dpy_y * snap->bytes_per_line;
2690
2691 if (wdpy_x > dpy_x || wdpy_y > dpy_y) {
2692 sz = wdpy_x * wdpy_y * pixelsize;
2693 if (sz > unclipped_len || unclipped_dst == NULL) {
2694 if (unclipped_dst) {
2695 free(unclipped_dst);
2696 }
2697 unclipped_dst = (char *) malloc(sz+4);
2698 unclipped_len = sz;
2699 }
2700 dst = unclipped_dst;
2701 }
2702
2703 if (! raw_fb_seek) {
2704 memcpy(dst, raw_fb_addr + raw_fb_offset, sz);
2705
2706 } else {
2707 int len = sz, del = 0;
2708 off_t off = (off_t) raw_fb_offset;
2709
2710 lseek(raw_fb_fd, off, SEEK_SET);
2711 while (len > 0) {
2712 n = read(raw_fb_fd, dst + del, len);
2713 if (n > 0) {
2714 del += n;
2715 len -= n;
2716 } else if (n == 0) {
2717 break;
2718 } else if (errno != EINTR && errno != EAGAIN) {
2719 break;
2720 }
2721 }
2722 }
2723
2724 if (dst == unclipped_dst) {
2725 char *src;
2726 int h;
2727 int x = off_x + coff_x;
2728 int y = off_y + coff_y;
2729
2730 src = unclipped_dst + y * wdpy_x * pixelsize +
2731 x * pixelsize;
2732 dst = snap->data;
2733
2734 for (h = 0; h < dpy_y; h++) {
2735 memcpy(dst, src, dpy_x * pixelsize);
2736 src += wdpy_x * pixelsize;
2737 dst += snap->bytes_per_line;
2738 }
2739 }
2740 }
2741
copy_snap(void)2742 int copy_snap(void) {
2743 int db = 1;
2744 char *fbp;
2745 int i, y, block_size;
2746 double dt;
2747 static int first = 1, snapcnt = 0;
2748
2749 if (raw_fb_str) {
2750 int read_all_at_once = 1;
2751 double start = dnow();
2752 if (rawfb_reset < 0) {
2753 if (getenv("SNAPFB_RAWFB_RESET")) {
2754 rawfb_reset = 1;
2755 } else {
2756 rawfb_reset = 0;
2757 }
2758 }
2759 if (snap_fb == NULL || snap == NULL) {
2760 rfbLog("copy_snap: rawfb mode and null snap fb\n");
2761 clean_up_exit(1);
2762 }
2763 if (rawfb_reset) {
2764 initialize_raw_fb(1);
2765 }
2766 if (raw_fb_bytes_per_line != snap->bytes_per_line) {
2767 read_all_at_once = 0;
2768 }
2769 if (raw_fb_full_str && strstr(raw_fb_full_str, "/dev/vcsa")) {
2770 snap_vcsa_rawfb();
2771 } else if (read_all_at_once) {
2772 snap_all_rawfb();
2773 } else {
2774 /* this goes line by line, XXX not working for video */
2775 copy_raw_fb(snap, 0, 0, dpy_x, dpy_y);
2776 }
2777 if (db && snapcnt++ < 5) rfbLog("rawfb copy_snap took: %.5f secs\n", dnow() - start);
2778
2779 return 0;
2780 }
2781
2782 if (! fs_factor) {
2783 return 0;
2784 }
2785
2786
2787 if (! snap_fb || ! snap || ! snaprect) {
2788 return 0;
2789 }
2790 block_size = ((dpy_y/fs_factor) * snap->bytes_per_line);
2791
2792 fbp = snap_fb;
2793 y = 0;
2794
2795
2796 dtime0(&dt);
2797 X_LOCK;
2798
2799 /* screen may be too big for 1 shm area, so broken into fs_factor */
2800 for (i=0; i < fs_factor; i++) {
2801 XRANDR_SET_TRAP_RET(-1, "copy_snap-set");
2802 copy_image(snaprect, 0, y, 0, 0);
2803 XRANDR_CHK_TRAP_RET(-1, "copy_snap-chk");
2804
2805 memcpy(fbp, snaprect->data, (size_t) block_size);
2806
2807 y += dpy_y / fs_factor;
2808 fbp += block_size;
2809 }
2810
2811 X_UNLOCK;
2812
2813 dt = dtime(&dt);
2814 if (first) {
2815 rfbLog("copy_snap: time for -snapfb snapshot: %.3f sec\n", dt);
2816 first = 0;
2817 }
2818
2819 return 0;
2820 }
2821
2822
2823 /*
2824 * debugging: print out a picture of the tiles.
2825 */
print_tiles(void)2826 static void print_tiles(void) {
2827 /* hack for viewing tile diffs on the screen. */
2828 static char *prev = NULL;
2829 int n, x, y, ms = 1500;
2830
2831 ms = 1;
2832
2833 if (! prev) {
2834 prev = (char *) malloc((size_t) ntiles);
2835 for (n=0; n < ntiles; n++) {
2836 prev[n] = 0;
2837 }
2838 }
2839 fprintf(stderr, " ");
2840 for (x=0; x < ntiles_x; x++) {
2841 fprintf(stderr, "%1d", x % 10);
2842 }
2843 fprintf(stderr, "\n");
2844 n = 0;
2845 for (y=0; y < ntiles_y; y++) {
2846 fprintf(stderr, "%2d ", y);
2847 for (x=0; x < ntiles_x; x++) {
2848 if (tile_has_diff[n]) {
2849 fprintf(stderr, "X");
2850 } else if (prev[n]) {
2851 fprintf(stderr, "o");
2852 } else {
2853 fprintf(stderr, ".");
2854 }
2855 n++;
2856 }
2857 fprintf(stderr, "\n");
2858 }
2859 for (n=0; n < ntiles; n++) {
2860 prev[n] = tile_has_diff[n];
2861 }
2862 usleep(ms * 1000);
2863 }
2864
2865 /*
2866 * Utilities for managing the "naps" to cut down on amount of polling.
2867 */
nap_set(int tile_cnt)2868 static void nap_set(int tile_cnt) {
2869 int nap_in = nap_ok;
2870 time_t now = time(NULL);
2871
2872 if (scan_count == 0) {
2873 /* roll up check for all NSCAN scans */
2874 nap_ok = 0;
2875 if (naptile && nap_diff_count < 2 * NSCAN * naptile) {
2876 /* "2" is a fudge to permit a bit of bg drawing */
2877 nap_ok = 1;
2878 }
2879 nap_diff_count = 0;
2880 }
2881 if (nap_ok && ! nap_in && use_xdamage) {
2882 if (XD_skip > 0.8 * XD_tot) {
2883 /* X DAMAGE is keeping load low, so skip nap */
2884 nap_ok = 0;
2885 }
2886 }
2887 if (! nap_ok && client_count) {
2888 if(now > last_fb_bytes_sent + no_fbu_blank) {
2889 if (debug_tiles > 1) {
2890 fprintf(stderr, "nap_set: nap_ok=1: now: %d last: %d\n",
2891 (int) now, (int) last_fb_bytes_sent);
2892 }
2893 nap_ok = 1;
2894 }
2895 }
2896
2897 if (show_cursor) {
2898 /* kludge for the up to 4 tiles the mouse patch could occupy */
2899 if ( tile_cnt > 4) {
2900 last_event = now;
2901 }
2902 } else if (tile_cnt != 0) {
2903 last_event = now;
2904 }
2905 }
2906
2907 /*
2908 * split up a long nap to improve the wakeup time
2909 */
nap_sleep(int ms,int split)2910 void nap_sleep(int ms, int split) {
2911 int i, input = got_user_input;
2912 int gd = got_local_pointer_input;
2913
2914 for (i=0; i<split; i++) {
2915 usleep(ms * 1000 / split);
2916 if (! use_threads && i != split - 1) {
2917 rfbPE(-1);
2918 }
2919 if (input != got_user_input) {
2920 break;
2921 }
2922 if (gd != got_local_pointer_input) {
2923 break;
2924 }
2925 }
2926 }
2927
get_load(void)2928 static char *get_load(void) {
2929 static char tmp[64];
2930 static int count = 0;
2931
2932 if (count++ % 5 == 0) {
2933 struct stat sb;
2934 memset(tmp, 0, sizeof(tmp));
2935 if (stat("/proc/loadavg", &sb) == 0) {
2936 int d = open("/proc/loadavg", O_RDONLY);
2937 if (d >= 0) {
2938 read(d, tmp, 60);
2939 close(d);
2940 }
2941 }
2942 if (tmp[0] == '\0') {
2943 strcat(tmp, "unknown");
2944 }
2945 }
2946 return tmp;
2947 }
2948
2949 /*
2950 * see if we should take a nap of some sort between polls
2951 */
nap_check(int tile_cnt)2952 static void nap_check(int tile_cnt) {
2953 time_t now;
2954
2955 nap_diff_count += tile_cnt;
2956
2957 if (! take_naps) {
2958 return;
2959 }
2960
2961 now = time(NULL);
2962
2963 if (screen_blank > 0) {
2964 int dt_ev, dt_fbu;
2965 static int ms = 0;
2966 if (ms == 0) {
2967 ms = 2000;
2968 if (getenv("X11VNC_SB_FACTOR")) {
2969 ms = ms * atof(getenv("X11VNC_SB_FACTOR"));
2970 }
2971 if (ms <= 0) {
2972 ms = 2000;
2973 }
2974 }
2975
2976 /* if no activity, pause here for a second or so. */
2977 dt_ev = (int) (now - last_event);
2978 dt_fbu = (int) (now - last_fb_bytes_sent);
2979 if (dt_fbu > screen_blank) {
2980 /* sleep longer for no fb requests */
2981 if (debug_tiles > 1) {
2982 fprintf(stderr, "screen blank sleep1: %d ms / 16, load: %s\n", 2 * ms, get_load());
2983 }
2984 nap_sleep(2 * ms, 16);
2985 return;
2986 }
2987 if (dt_ev > screen_blank) {
2988 if (debug_tiles > 1) {
2989 fprintf(stderr, "screen blank sleep2: %d ms / 8, load: %s\n", ms, get_load());
2990 }
2991 nap_sleep(ms, 8);
2992 return;
2993 }
2994 }
2995 if (naptile && nap_ok && tile_cnt < naptile) {
2996 int ms = napfac * waitms;
2997 ms = ms > napmax ? napmax : ms;
2998 if (now - last_input <= 3) {
2999 nap_ok = 0;
3000 } else if (now - last_local_input <= 3) {
3001 nap_ok = 0;
3002 } else {
3003 if (debug_tiles > 1) {
3004 fprintf(stderr, "nap_check sleep: %d ms / 1, load: %s\n", ms, get_load());
3005 }
3006 nap_sleep(ms, 1);
3007 }
3008 }
3009 }
3010
3011 /*
3012 * This is called to avoid a ~20 second timeout in libvncserver.
3013 * May no longer be needed.
3014 */
ping_clients(int tile_cnt)3015 static void ping_clients(int tile_cnt) {
3016 static time_t last_send = 0;
3017 time_t now = time(NULL);
3018
3019 if (rfbMaxClientWait < 20000) {
3020 rfbMaxClientWait = 20000;
3021 rfbLog("reset rfbMaxClientWait to %d msec.\n",
3022 rfbMaxClientWait);
3023 }
3024 if (tile_cnt > 0) {
3025 last_send = now;
3026 } else if (tile_cnt < 0) {
3027 /* negative tile_cnt is -ping case */
3028 if (now >= last_send - tile_cnt) {
3029 mark_rect_as_modified(0, 0, 1, 1, 1);
3030 last_send = now;
3031 }
3032 } else if (now - last_send > 5) {
3033 /* Send small heartbeat to client */
3034 mark_rect_as_modified(0, 0, 1, 1, 1);
3035 last_send = now;
3036 }
3037 }
3038
3039 /*
3040 * scan_display() wants to know if this tile can be skipped due to
3041 * blackout regions: (no data compare is done, just a quick geometric test)
3042 */
blackout_line_skip(int n,int x,int y,int rescan,int * tile_count)3043 static int blackout_line_skip(int n, int x, int y, int rescan,
3044 int *tile_count) {
3045
3046 if (tile_blackout[n].cover == 2) {
3047 tile_has_diff[n] = 0;
3048 return 1; /* skip it */
3049
3050 } else if (tile_blackout[n].cover == 1) {
3051 int w, x1, y1, x2, y2, b, hit = 0;
3052 if (x + NSCAN > dpy_x) {
3053 w = dpy_x - x;
3054 } else {
3055 w = NSCAN;
3056 }
3057
3058 for (b=0; b < tile_blackout[n].count; b++) {
3059
3060 /* n.b. these coords are in full display space: */
3061 x1 = tile_blackout[n].bo[b].x1;
3062 x2 = tile_blackout[n].bo[b].x2;
3063 y1 = tile_blackout[n].bo[b].y1;
3064 y2 = tile_blackout[n].bo[b].y2;
3065
3066 if (x2 - x1 < w) {
3067 /* need to cover full width */
3068 continue;
3069 }
3070 if (y1 <= y && y < y2) {
3071 hit = 1;
3072 break;
3073 }
3074 }
3075 if (hit) {
3076 if (! rescan) {
3077 tile_has_diff[n] = 0;
3078 } else {
3079 *tile_count += tile_has_diff[n];
3080 }
3081 return 1; /* skip */
3082 }
3083 }
3084 return 0; /* do not skip */
3085 }
3086
blackout_line_cmpskip(int n,int x,int y,char * dst,char * src,int w,int pixelsize)3087 static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src,
3088 int w, int pixelsize) {
3089
3090 int i, x1, y1, x2, y2, b, hit = 0;
3091 int beg = -1, end = -1;
3092
3093 if (tile_blackout[n].cover == 0) {
3094 return 0; /* 0 means do not skip it. */
3095 } else if (tile_blackout[n].cover == 2) {
3096 return 1; /* 1 means skip it. */
3097 }
3098
3099 /* tile has partial coverage: */
3100
3101 for (i=0; i < w * pixelsize; i++) {
3102 if (*(dst+i) != *(src+i)) {
3103 beg = i/pixelsize; /* beginning difference */
3104 break;
3105 }
3106 }
3107 for (i = w * pixelsize - 1; i >= 0; i--) {
3108 if (*(dst+i) != *(src+i)) {
3109 end = i/pixelsize; /* ending difference */
3110 break;
3111 }
3112 }
3113 if (beg < 0 || end < 0) {
3114 /* problem finding range... */
3115 return 0;
3116 }
3117
3118 /* loop over blackout rectangles: */
3119 for (b=0; b < tile_blackout[n].count; b++) {
3120
3121 /* y in full display space: */
3122 y1 = tile_blackout[n].bo[b].y1;
3123 y2 = tile_blackout[n].bo[b].y2;
3124
3125 /* x relative to tile origin: */
3126 x1 = tile_blackout[n].bo[b].x1 - x;
3127 x2 = tile_blackout[n].bo[b].x2 - x;
3128
3129 if (y1 > y || y >= y2) {
3130 continue;
3131 }
3132 if (x1 <= beg && end <= x2) {
3133 hit = 1;
3134 break;
3135 }
3136 }
3137 if (hit) {
3138 return 1;
3139 } else {
3140 return 0;
3141 }
3142 }
3143
3144 /*
3145 * For the subwin case follows the window if it is moved.
3146 */
set_offset(void)3147 void set_offset(void) {
3148 Window w;
3149 if (! subwin) {
3150 return;
3151 }
3152 X_LOCK;
3153 xtranslate(window, rootwin, 0, 0, &off_x, &off_y, &w, 0);
3154 X_UNLOCK;
3155 }
3156
3157 static int xd_samples = 0, xd_misses = 0, xd_do_check = 0;
3158
3159 /*
3160 * Loop over 1-pixel tall horizontal scanlines looking for changes.
3161 * Record the changes in tile_has_diff[]. Scanlines in the loop are
3162 * equally spaced along y by NSCAN pixels, but have a slightly random
3163 * starting offset ystart ( < NSCAN ) from scanlines[].
3164 */
3165
scan_display(int ystart,int rescan)3166 static int scan_display(int ystart, int rescan) {
3167 char *src, *dst;
3168 int pixelsize = bpp/8;
3169 int x, y, w, n;
3170 int tile_count = 0;
3171 int nodiffs = 0, diff_hint;
3172 int xd_check = 0, xd_freq = 1;
3173 static int xd_tck = 0;
3174
3175 y = ystart;
3176
3177 g_now = dnow();
3178
3179 if (! main_fb) {
3180 rfbLog("scan_display: no main_fb!\n");
3181 return 0;
3182 }
3183
3184 X_LOCK;
3185
3186 while (y < dpy_y) {
3187
3188 if (use_xdamage) {
3189 XD_tot++;
3190 xd_check = 0;
3191 if (xdamage_hint_skip(y)) {
3192 if (xd_do_check && dpy && use_xdamage == 1) {
3193 xd_tck++;
3194 xd_tck = xd_tck % xd_freq;
3195 if (xd_tck == 0) {
3196 xd_check = 1;
3197 xd_samples++;
3198 }
3199 }
3200 if (!xd_check) {
3201 XD_skip++;
3202 y += NSCAN;
3203 continue;
3204 }
3205 } else {
3206 if (xd_do_check && 0) {
3207 fprintf(stderr, "ns y=%d\n", y);
3208 }
3209 }
3210 }
3211
3212 /* grab the horizontal scanline from the display: */
3213
3214 #ifndef NO_NCACHE
3215 /* XXX Y test */
3216 if (ncache > 0) {
3217 int gotone = 0;
3218 if (macosx_console) {
3219 if (macosx_checkevent(NULL)) {
3220 gotone = 1;
3221 }
3222 } else {
3223 #if !NO_X11
3224 XEvent ev;
3225 if (raw_fb_str) {
3226 ;
3227 } else if (XEventsQueued(dpy, QueuedAlready) == 0) {
3228 ; /* XXX Y resp */
3229 } else if (XCheckTypedEvent(dpy, MapNotify, &ev)) {
3230 gotone = 1;
3231 } else if (XCheckTypedEvent(dpy, UnmapNotify, &ev)) {
3232 gotone = 2;
3233 } else if (XCheckTypedEvent(dpy, CreateNotify, &ev)) {
3234 gotone = 3;
3235 } else if (XCheckTypedEvent(dpy, ConfigureNotify, &ev)) {
3236 gotone = 4;
3237 } else if (XCheckTypedEvent(dpy, VisibilityNotify, &ev)) {
3238 gotone = 5;
3239 }
3240 if (gotone) {
3241 XPutBackEvent(dpy, &ev);
3242 }
3243 #endif
3244 }
3245 if (gotone) {
3246 static int nomsg = 1;
3247 if (nomsg) {
3248 if (dnowx() > 20) {
3249 nomsg = 0;
3250 }
3251 } else {
3252 if (ncdb) fprintf(stderr, "\n*** SCAN_DISPLAY CHECK_NCACHE/%d *** %d rescan=%d\n", gotone, y, rescan);
3253 }
3254 X_UNLOCK;
3255 check_ncache(0, 1);
3256 X_LOCK;
3257 }
3258 }
3259 #endif
3260
3261 XRANDR_SET_TRAP_RET(-1, "scan_display-set");
3262 copy_image(scanline, 0, y, 0, 0);
3263 XRANDR_CHK_TRAP_RET(-1, "scan_display-chk");
3264
3265 /* for better memory i/o try the whole line at once */
3266 src = scanline->data;
3267 dst = main_fb + y * main_bytes_per_line;
3268
3269 if (! memcmp(dst, src, main_bytes_per_line)) {
3270 /* no changes anywhere in scan line */
3271 nodiffs = 1;
3272 if (! rescan) {
3273 y += NSCAN;
3274 continue;
3275 }
3276 }
3277 if (xd_check) {
3278 xd_misses++;
3279 }
3280
3281 x = 0;
3282 while (x < dpy_x) {
3283 n = (x/tile_x) + (y/tile_y) * ntiles_x;
3284 diff_hint = 0;
3285
3286 if (blackouts) {
3287 if (blackout_line_skip(n, x, y, rescan,
3288 &tile_count)) {
3289 x += NSCAN;
3290 continue;
3291 }
3292 }
3293
3294 if (rescan) {
3295 if (nodiffs || tile_has_diff[n]) {
3296 tile_count += tile_has_diff[n];
3297 x += NSCAN;
3298 continue;
3299 }
3300 } else if (xdamage_tile_count &&
3301 tile_has_xdamage_diff[n]) {
3302 tile_has_xdamage_diff[n] = 2;
3303 diff_hint = 1;
3304 }
3305
3306 /* set ptrs to correspond to the x offset: */
3307 src = scanline->data + x * pixelsize;
3308 dst = main_fb + y * main_bytes_per_line + x * pixelsize;
3309
3310 /* compute the width of data to be compared: */
3311 if (x + NSCAN > dpy_x) {
3312 w = dpy_x - x;
3313 } else {
3314 w = NSCAN;
3315 }
3316
3317 if (diff_hint || memcmp(dst, src, w * pixelsize)) {
3318 /* found a difference, record it: */
3319 if (! blackouts) {
3320 tile_has_diff[n] = 1;
3321 tile_count++;
3322 } else {
3323 if (blackout_line_cmpskip(n, x, y,
3324 dst, src, w, pixelsize)) {
3325 tile_has_diff[n] = 0;
3326 } else {
3327 tile_has_diff[n] = 1;
3328 tile_count++;
3329 }
3330 }
3331 }
3332 x += NSCAN;
3333 }
3334 y += NSCAN;
3335 }
3336
3337 X_UNLOCK;
3338
3339 return tile_count;
3340 }
3341
3342
3343 int scanlines[NSCAN] = {
3344 0, 16, 8, 24, 4, 20, 12, 28,
3345 10, 26, 18, 2, 22, 6, 30, 14,
3346 1, 17, 9, 25, 7, 23, 15, 31,
3347 19, 3, 27, 11, 29, 13, 5, 21
3348 };
3349
3350 /*
3351 * toplevel for the scanning, rescanning, and applying the heuristics.
3352 * returns number of changed tiles.
3353 */
scan_for_updates(int count_only)3354 int scan_for_updates(int count_only) {
3355 int i, tile_count, tile_diffs;
3356 int old_copy_tile;
3357 double frac1 = 0.1; /* tweak parameter to try a 2nd scan_display() */
3358 double frac2 = 0.35; /* or 3rd */
3359 double frac3 = 0.02; /* do scan_display() again after copy_tiles() */
3360 static double last_poll = 0.0;
3361 double dtmp = 0.0;
3362
3363 if (unixpw_in_progress) return 0;
3364
3365 if (slow_fb > 0.0) {
3366 double now = dnow();
3367 if (now < last_poll + slow_fb) {
3368 return 0;
3369 }
3370 last_poll = now;
3371 }
3372
3373 for (i=0; i < ntiles; i++) {
3374 tile_has_diff[i] = 0;
3375 tile_has_xdamage_diff[i] = 0;
3376 tile_tried[i] = 0;
3377 tile_copied[i] = 0;
3378 }
3379 for (i=0; i < ntiles_y; i++) {
3380 /* could be useful, currently not used */
3381 tile_row_has_xdamage_diff[i] = 0;
3382 }
3383 xdamage_tile_count = 0;
3384
3385 /*
3386 * n.b. this program has only been tested so far with
3387 * tile_x = tile_y = NSCAN = 32!
3388 */
3389
3390 if (!count_only) {
3391 scan_count++;
3392 scan_count %= NSCAN;
3393
3394 /* some periodic maintenance */
3395 if (subwin && scan_count % 4 == 0) {
3396 set_offset(); /* follow the subwindow */
3397 }
3398 if (indexed_color && scan_count % 4 == 0) {
3399 /* check for changed colormap */
3400 set_colormap(0);
3401 }
3402 if (cmap8to24 && scan_count % 1 == 0) {
3403 check_for_multivis();
3404 }
3405 #ifdef MACOSX
3406 if (macosx_console) {
3407 macosx_event_loop();
3408 }
3409 #endif
3410 if (use_xdamage) {
3411 /* first pass collecting DAMAGE events: */
3412 #ifdef MACOSX
3413 if (macosx_console) {
3414 collect_non_X_xdamage(-1, -1, -1, -1, 0);
3415 } else
3416 #endif
3417 {
3418 if (rawfb_vnc_reflect) {
3419 collect_non_X_xdamage(-1, -1, -1, -1, 0);
3420 } else {
3421 collect_xdamage(scan_count, 0);
3422 }
3423 }
3424 }
3425 }
3426
3427 #define SCAN_FATAL(x) \
3428 if (x < 0) { \
3429 scan_in_progress = 0; \
3430 fb_copy_in_progress = 0; \
3431 return 0; \
3432 }
3433
3434 /* scan with the initial y to the jitter value from scanlines: */
3435 scan_in_progress = 1;
3436 tile_count = scan_display(scanlines[scan_count], 0);
3437 SCAN_FATAL(tile_count);
3438
3439 /*
3440 * we do the XDAMAGE here too since after scan_display()
3441 * there is a better chance we have received the events from
3442 * the X server (otherwise the DAMAGE events will be processed
3443 * in the *next* call, usually too late and wasteful since
3444 * the unchanged tiles are read in again).
3445 */
3446 if (use_xdamage) {
3447 #ifdef MACOSX
3448 if (macosx_console) {
3449 ;
3450 } else
3451 #endif
3452 {
3453 if (rawfb_vnc_reflect) {
3454 ;
3455 } else {
3456 collect_xdamage(scan_count, 1);
3457 }
3458 }
3459 }
3460 if (count_only) {
3461 scan_in_progress = 0;
3462 fb_copy_in_progress = 0;
3463 return tile_count;
3464 }
3465
3466 if (xdamage_tile_count) {
3467 /* pick up "known" damaged tiles we missed in scan_display() */
3468 for (i=0; i < ntiles; i++) {
3469 if (tile_has_diff[i]) {
3470 continue;
3471 }
3472 if (tile_has_xdamage_diff[i]) {
3473 tile_has_diff[i] = 1;
3474 if (tile_has_xdamage_diff[i] == 1) {
3475 tile_has_xdamage_diff[i] = 2;
3476 tile_count++;
3477 }
3478 }
3479 }
3480 }
3481 if (dpy && use_xdamage == 1) {
3482 static time_t last_xd_check = 0;
3483 if (time(NULL) > last_xd_check + 2) {
3484 int cp = (scan_count + 3) % NSCAN;
3485 xd_do_check = 1;
3486 tile_count = scan_display(scanlines[cp], 0);
3487 xd_do_check = 0;
3488 SCAN_FATAL(tile_count);
3489 last_xd_check = time(NULL);
3490 if (xd_samples > 200) {
3491 static int bad = 0;
3492 if (xd_misses > (20 * xd_samples) / 100) {
3493 rfbLog("XDAMAGE is not working well... misses: %d/%d\n", xd_misses, xd_samples);
3494 rfbLog("Maybe an OpenGL app like Beryl or Compiz is the problem?\n");
3495 rfbLog("Use x11vnc -noxdamage or disable the Beryl/Compiz app.\n");
3496 rfbLog("To disable this check and warning specify -xdamage twice.\n");
3497 if (++bad >= 10) {
3498 rfbLog("XDAMAGE appears broken (OpenGL app?), turning it off.\n");
3499 use_xdamage = 0;
3500 initialize_xdamage();
3501 destroy_xdamage_if_needed();
3502 }
3503 }
3504 xd_samples = 0;
3505 xd_misses = 0;
3506 }
3507 }
3508 }
3509
3510 nap_set(tile_count);
3511
3512 if (fs_factor && frac1 >= fs_frac) {
3513 /* make frac1 < fs_frac if fullscreen updates are enabled */
3514 frac1 = fs_frac/2.0;
3515 }
3516
3517 if (tile_count > frac1 * ntiles) {
3518 /*
3519 * many tiles have changed, so try a rescan (since it should
3520 * be short compared to the many upcoming copy_tiles() calls)
3521 */
3522
3523 /* this check is done to skip the extra scan_display() call */
3524 if (! fs_factor || tile_count <= fs_frac * ntiles) {
3525 int cp, tile_count_old = tile_count;
3526
3527 /* choose a different y shift for the 2nd scan: */
3528 cp = (NSCAN - scan_count) % NSCAN;
3529
3530 tile_count = scan_display(scanlines[cp], 1);
3531 SCAN_FATAL(tile_count);
3532
3533 if (tile_count >= (1 + frac2) * tile_count_old) {
3534 /* on a roll... do a 3rd scan */
3535 cp = (NSCAN - scan_count + 7) % NSCAN;
3536 tile_count = scan_display(scanlines[cp], 1);
3537 SCAN_FATAL(tile_count);
3538 }
3539 }
3540 scan_in_progress = 0;
3541
3542 /*
3543 * At some number of changed tiles it is better to just
3544 * copy the full screen at once. I.e. time = c1 + m * r1
3545 * where m is number of tiles, r1 is the copy_tiles()
3546 * time, and c1 is the scan_display() time: for some m
3547 * it crosses the full screen update time.
3548 *
3549 * We try to predict that crossover with the fs_frac
3550 * fudge factor... seems to be about 1/2 the total number
3551 * of tiles. n.b. this ignores network bandwidth,
3552 * compression time etc...
3553 *
3554 * Use -fs 1.0 to disable on slow links.
3555 */
3556 if (fs_factor && tile_count > fs_frac * ntiles) {
3557 int cs;
3558 fb_copy_in_progress = 1;
3559 cs = copy_screen();
3560 fb_copy_in_progress = 0;
3561 SCAN_FATAL(cs);
3562 if (use_threads && pointer_mode != 1) {
3563 pointer_event(-1, 0, 0, NULL);
3564 }
3565 nap_check(tile_count);
3566 return tile_count;
3567 }
3568 }
3569 scan_in_progress = 0;
3570
3571 /* copy all tiles with differences from display to rfb framebuffer: */
3572 fb_copy_in_progress = 1;
3573
3574 if (single_copytile || tile_shm_count < ntiles_x) {
3575 /*
3576 * Old way, copy I/O one tile at a time.
3577 */
3578 old_copy_tile = 1;
3579 } else {
3580 /*
3581 * New way, does runs of horizontal tiles at once.
3582 * Note that below, for simplicity, the extra tile finding
3583 * (e.g. copy_tiles_backward_pass) is done the old way.
3584 */
3585 old_copy_tile = 0;
3586 }
3587
3588 if (unixpw_in_progress) return 0;
3589
3590 /* XXX Y */
3591 if (0 && tile_count > 20) print_tiles();
3592 #if 0
3593 dtmp = dnow();
3594 #else
3595 dtmp = 0.0;
3596 #endif
3597
3598 if (old_copy_tile) {
3599 tile_diffs = copy_all_tiles();
3600 } else {
3601 tile_diffs = copy_all_tile_runs();
3602 }
3603 SCAN_FATAL(tile_diffs);
3604
3605 #if 0
3606 if (tile_count) fprintf(stderr, "XX copytile: %.4f tile_count: %d\n", dnow() - dtmp, tile_count);
3607 #endif
3608
3609 /*
3610 * This backward pass for upward and left tiles complements what
3611 * was done in copy_all_tiles() for downward and right tiles.
3612 */
3613 tile_diffs = copy_tiles_backward_pass();
3614 SCAN_FATAL(tile_diffs);
3615
3616 if (tile_diffs > frac3 * ntiles) {
3617 /*
3618 * we spent a lot of time in those copy_tiles, run
3619 * another scan, maybe more of the screen changed.
3620 */
3621 int cp = (NSCAN - scan_count + 13) % NSCAN;
3622
3623 scan_in_progress = 1;
3624 tile_count = scan_display(scanlines[cp], 1);
3625 SCAN_FATAL(tile_count);
3626 scan_in_progress = 0;
3627
3628 tile_diffs = copy_tiles_additional_pass();
3629 SCAN_FATAL(tile_diffs);
3630 }
3631
3632 /* Given enough tile diffs, try the islands: */
3633 if (grow_fill && tile_diffs > 4) {
3634 tile_diffs = grow_islands();
3635 }
3636 SCAN_FATAL(tile_diffs);
3637
3638 /* Given enough tile diffs, try the gaps: */
3639 if (gaps_fill && tile_diffs > 4) {
3640 tile_diffs = fill_tile_gaps();
3641 }
3642 SCAN_FATAL(tile_diffs);
3643
3644 fb_copy_in_progress = 0;
3645 if (use_threads && pointer_mode != 1) {
3646 /*
3647 * tell the pointer handler it can process any queued
3648 * pointer events:
3649 */
3650 pointer_event(-1, 0, 0, NULL);
3651 }
3652
3653 if (blackouts) {
3654 /* ignore any diffs in completely covered tiles */
3655 int x, y, n;
3656 for (y=0; y < ntiles_y; y++) {
3657 for (x=0; x < ntiles_x; x++) {
3658 n = x + y * ntiles_x;
3659 if (tile_blackout[n].cover == 2) {
3660 tile_has_diff[n] = 0;
3661 }
3662 }
3663 }
3664 }
3665
3666 hint_updates(); /* use x0rfbserver hints algorithm */
3667
3668 /* Work around threaded rfbProcessClientMessage() calls timeouts */
3669 if (use_threads) {
3670 ping_clients(tile_diffs);
3671 } else if (saw_ultra_chat || saw_ultra_file) {
3672 ping_clients(-1);
3673 } else if (use_openssl && !tile_diffs) {
3674 ping_clients(0);
3675 }
3676 /* -ping option: */
3677 if (ping_interval) {
3678 int td = ping_interval > 0 ? ping_interval : -ping_interval;
3679 ping_clients(-td);
3680 }
3681
3682
3683 nap_check(tile_diffs);
3684 return tile_diffs;
3685 }
3686
3687
3688