~ubuntu-branches/debian/lenny/italc/lenny

« back to all changes in this revision

Viewing changes to ica/x11/x11vnc/scan.c

  • Committer: Bazaar Package Importer
  • Author(s): Patrick Winnertz
  • Date: 2008-06-17 13:46:54 UTC
  • mfrom: (1.2.1 upstream) (4.1.1 gutsy)
  • Revision ID: james.westby@ubuntu.com-20080617134654-cl0gi4u524cv1ici
Tags: 1:1.0.9~rc3-1
* Package new upstream version
  - upstream ported the code to qt4.4 (Closes: #481974)

Show diffs side-by-side

added added

removed removed

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