blob: 84bcb7b05eeb1c0ff222191e1ef6d7de42416ce0 [file] [log] [blame]
San Mehata430b2b2014-09-23 08:30:51 -07001/*
2 Copyright (C) 2002-2010 Karl J. Runge <runge@karlrunge.com>
3 All rights reserved.
4
5This file is part of x11vnc.
6
7x11vnc is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2 of the License, or (at
10your option) any later version.
11
12x11vnc is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with x11vnc; if not, write to the Free Software
19Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
20or see <http://www.gnu.org/licenses/>.
21
22In addition, as a special exception, Karl J. Runge
23gives permission to link the code of its release of x11vnc with the
24OpenSSL project's "OpenSSL" library (or with modified versions of it
25that use the same license as the "OpenSSL" library), and distribute
26the linked executables. You must obey the GNU General Public License
27in all respects for all of the code used other than "OpenSSL". If you
28modify this file, you may extend this exception to your version of the
29file, but you are not obligated to do so. If you do not wish to do
30so, delete this exception statement from your version.
31*/
32
33/* -- userinput.c -- */
34
35#include "x11vnc.h"
36#include "xwrappers.h"
37#include "xdamage.h"
38#include "xrecord.h"
39#include "xinerama.h"
40#include "win_utils.h"
41#include "xevents.h"
42#include "user.h"
43#include "scan.h"
44#include "cleanup.h"
45#include "pointer.h"
46#include "rates.h"
47#include "keyboard.h"
48#include "solid.h"
49#include "xrandr.h"
50#include "8to24.h"
51#include "unixpw.h"
52#include "macosx.h"
53#include "macosxCGS.h"
54#include "cursor.h"
55#include "screen.h"
56#include "connections.h"
57
58/*
59 * user input handling heuristics
60 */
61int defer_update_nofb = 4; /* defer a shorter time under -nofb */
62int last_scroll_type = SCR_NONE;
63
64
65int get_wm_frame_pos(int *px, int *py, int *x, int *y, int *w, int *h,
66 Window *frame, Window *win);
67void parse_scroll_copyrect(void);
68void parse_fixscreen(void);
69void parse_wireframe(void);
70
71void set_wirecopyrect_mode(char *str);
72void set_scrollcopyrect_mode(char *str);
73void initialize_scroll_keys(void);
74void initialize_scroll_matches(void);
75void initialize_scroll_term(void);
76void initialize_max_keyrepeat(void);
77
78int direct_fb_copy(int x1, int y1, int x2, int y2, int mark);
79void fb_push(void);
80int fb_push_wait(double max_wait, int flags);
81void eat_viewonly_input(int max_eat, int keep);
82
83void mark_for_xdamage(int x, int y, int w, int h);
84void mark_region_for_xdamage(sraRegionPtr region);
85void set_xdamage_mark(int x, int y, int w, int h);
86int near_wm_edge(int x, int y, int w, int h, int px, int py);
87int near_scrollbar_edge(int x, int y, int w, int h, int px, int py);
88
89void check_fixscreen(void);
90int check_xrecord(void);
91int check_wireframe(void);
92int fb_update_sent(int *count);
93int check_user_input(double dt, double dtr, int tile_diffs, int *cnt);
94void do_copyregion(sraRegionPtr region, int dx, int dy, int mode);
95
96int check_ncache(int reset, int mode);
97int find_rect(int idx, int x, int y, int w, int h);
98int bs_restore(int idx, int *nbatch, sraRegionPtr rmask, XWindowAttributes *attr, int clip, int nopad, int *valid, int verb);
99int try_to_fix_su(Window win, int idx, Window above, int *nbatch, char *mode);
100int try_to_fix_resize_su(Window orig_frame, int orig_x, int orig_y, int orig_w, int orig_h,
101 int x, int y, int w, int h, int try_batch);
102int lookup_win_index(Window);
103void set_ncache_xrootpmap(void);
104
105static void get_client_regions(int *req, int *mod, int *cpy, int *num) ;
106static void parse_scroll_copyrect_str(char *scr);
107static void parse_wireframe_str(char *wf);
108static void destroy_str_list(char **list);
109static void draw_box(int x, int y, int w, int h, int restore);
110static int do_bdpush(Window wm_win, int x0, int y0, int w0, int h0, int bdx,
111 int bdy, int bdskinny);
112static int set_ypad(void);
113static void scale_mark(int x1, int y1, int x2, int y2, int mark);
114static int push_scr_ev(double *age, int type, int bdpush, int bdx, int bdy,
115 int bdskinny, int first_push);
116static int crfix(int x, int dx, int Lx);
117static int scrollability(Window win, int set);
118static int eat_pointer(int max_ptr_eat, int keep);
119static void set_bdpush(int type, double *last_bdpush, int *pushit);
120static int repeat_check(double last_key_scroll);
121static int check_xrecord_keys(void);
122static int check_xrecord_mouse(void);
123static int try_copyrect(Window orig_frame, Window frame, int x, int y, int w, int h,
124 int dx, int dy, int *obscured, sraRegionPtr extra_clip, double max_wait, int *nbatch);
125static int wireframe_mod_state();
126static void check_user_input2(double dt);
127static void check_user_input3(double dt, double dtr, int tile_diffs);
128static void check_user_input4(double dt, double dtr, int tile_diffs);
129
130winattr_t *cache_list;
131
132/*
133 * For -wireframe: find the direct child of rootwin that has the
134 * pointer, assume that is the WM frame that contains the application
135 * (i.e. wm reparents the app toplevel) return frame position and size
136 * if successful.
137 */
138int get_wm_frame_pos(int *px, int *py, int *x, int *y, int *w, int *h,
139 Window *frame, Window *win) {
140#if !NO_X11
141 Window r, c;
142 XWindowAttributes attr;
143 Bool ret;
144 int rootx, rooty, wx, wy;
145 unsigned int mask;
146#endif
147
148#ifdef MACOSX
149 if (macosx_console) {
150 return macosx_get_wm_frame_pos(px, py, x, y, w, h, frame, win);
151 }
152#endif
153
154 RAWFB_RET(0)
155
156#if NO_X11
157 if (!px || !py || !x || !y || !w || !h || !frame || !win) {}
158 return 0;
159#else
160
161
162 ret = XQueryPointer_wr(dpy, rootwin, &r, &c, &rootx, &rooty, &wx, &wy,
163 &mask);
164
165 *frame = c;
166
167 /* current pointer position is returned too */
168 *px = rootx;
169 *py = rooty;
170
171 if (!ret || ! c || c == rootwin) {
172 /* no immediate child */
173 return 0;
174 }
175
176 /* child window position and size */
177 if (! valid_window(c, &attr, 1)) {
178 return 0;
179 }
180
181 *x = attr.x;
182 *y = attr.y;
183 *w = attr.width;
184 *h = attr.height;
185
186#if 0
187 /* more accurate, but the animation is bogus anyway */
188 if (attr.border_width > 0) {
189 *w += 2 * attr.border_width;
190 *h += 2 * attr.border_width;
191 }
192#endif
193
194 if (win != NULL) {
195 *win = descend_pointer(5, c, NULL, 0);
196 }
197
198 return 1;
199#endif /* NO_X11 */
200}
201
202static int scrollcopyrect_top, scrollcopyrect_bot;
203static int scrollcopyrect_left, scrollcopyrect_right;
204static double scr_key_time, scr_key_persist;
205static double scr_mouse_time, scr_mouse_persist, scr_mouse_maxtime;
206static double scr_mouse_pointer_delay;
207static double scr_key_bdpush_time, scr_mouse_bdpush_time;
208
209static void parse_scroll_copyrect_str(char *scr) {
210 char *p, *str;
211 int i;
212 char *part[16];
213
214 for (i=0; i<16; i++) {
215 part[i] = NULL;
216 }
217
218 if (scr == NULL || *scr == '\0') {
219 return;
220 }
221
222 str = strdup(scr);
223
224 p = strtok(str, ",");
225 i = 0;
226 while (p) {
227 part[i++] = strdup(p);
228 p = strtok(NULL, ",");
229 if (i >= 16) break;
230 }
231 free(str);
232
233
234 /*
235 * Top, Bottom, Left, Right tolerances for scrollbar locations.
236 */
237 if ((str = part[0]) != NULL) {
238 int t, b, l, r;
239 if (sscanf(str, "%d+%d+%d+%d", &t, &b, &l, &r) == 4) {
240 scrollcopyrect_top = t;
241 scrollcopyrect_bot = b;
242 scrollcopyrect_left = l;
243 scrollcopyrect_right = r;
244 }
245 free(str);
246 }
247
248 /* key scrolling timing heuristics. */
249 if ((str = part[1]) != NULL) {
250 double t1, t2, t3;
251 if (sscanf(str, "%lf+%lf+%lf", &t1, &t2, &t3) == 3) {
252 scr_key_time = t1;
253 scr_key_persist = t2;
254 scr_key_bdpush_time = t3;
255 }
256 free(str);
257 }
258
259 /* mouse scrolling timing heuristics. */
260 if ((str = part[2]) != NULL) {
261 double t1, t2, t3, t4, t5;
262 if (sscanf(str, "%lf+%lf+%lf+%lf+%lf", &t1, &t2, &t3, &t4,
263 &t5) == 5) {
264 scr_mouse_time = t1;
265 scr_mouse_persist = t2;
266 scr_mouse_bdpush_time = t3;
267 scr_mouse_pointer_delay = t4;
268 scr_mouse_maxtime = t5;
269 }
270 free(str);
271 }
272}
273
274void parse_scroll_copyrect(void) {
275 parse_scroll_copyrect_str(SCROLL_COPYRECT_PARMS);
276 if (! scroll_copyrect_str) {
277 scroll_copyrect_str = strdup(SCROLL_COPYRECT_PARMS);
278 }
279 parse_scroll_copyrect_str(scroll_copyrect_str);
280}
281
282void parse_fixscreen(void) {
283 char *str, *p;
284
285 screen_fixup_V = 0.0;
286 screen_fixup_C = 0.0;
287 screen_fixup_X = 0.0;
288 screen_fixup_8 = 0.0;
289
290 if (! screen_fixup_str) {
291 return;
292 }
293
294 str = strdup(screen_fixup_str);
295
296 p = strtok(str, ",");
297 while (p) {
298 double t;
299 if (*p == 'V' && sscanf(p, "V=%lf", &t) == 1) {
300 screen_fixup_V = t;
301 } else if (*p == 'C' && sscanf(p, "C=%lf", &t) == 1) {
302 screen_fixup_C = t;
303 } else if (*p == 'X' && sscanf(p, "X=%lf", &t) == 1) {
304 screen_fixup_X = t;
305 } else if (*p == 'X' && sscanf(p, "8=%lf", &t) == 1) {
306 screen_fixup_8 = t;
307 }
308 p = strtok(NULL, ",");
309 }
310 free(str);
311
312 if (screen_fixup_V < 0.0) screen_fixup_V = 0.0;
313 if (screen_fixup_C < 0.0) screen_fixup_C = 0.0;
314 if (screen_fixup_X < 0.0) screen_fixup_X = 0.0;
315 if (screen_fixup_8 < 0.0) screen_fixup_8 = 0.0;
316}
317
318/*
319WIREFRAME_PARMS "0xff,2,0,30+6+6+6,Alt,0.05+0.3+2.0,8"
320 0xff,2,0,32+8+8+8,all,0.15+0.30+5.0+0.125
321shade,linewidth,percent,T+B+L+R,mods,t1+t2+t3+t4
322 */
323#define LW_MAX 8
324static unsigned long wireframe_shade = 0xff;
325static int wireframe_lw;
326static double wireframe_frac;
327static int wireframe_top, wireframe_bot, wireframe_left, wireframe_right;
328static double wireframe_t1, wireframe_t2, wireframe_t3, wireframe_t4;
329static char *wireframe_mods = NULL;
330
331/*
332 * Parse the gory -wireframe string for parameters.
333 */
334static void parse_wireframe_str(char *wf) {
335 char *p, *str;
336 int i;
337 char *part[16];
338
339 for (i=0; i<16; i++) {
340 part[i] = NULL;
341 }
342
343 if (wf == NULL || *wf == '\0') {
344 return;
345 }
346
347 str = strdup(wf);
348
349 /* leading ",", make it start with ignorable string "z" */
350 if (*str == ',') {
351 char *tmp = (char *) malloc(strlen(str)+2);
352 strcpy(tmp, "z");
353 strcat(tmp, str);
354 free(str);
355 str = tmp;
356 }
357
358 p = strtok(str, ",");
359 i = 0;
360 while (p) {
361 part[i++] = strdup(p);
362 p = strtok(NULL, ",");
363 if (i >= 16) break;
364 }
365 free(str);
366
367
368 /* Wireframe shade, color, RGB: */
369 if ((str = part[0]) != NULL) {
370 unsigned long n;
371 int r, g, b, ok = 0;
372 XColor cdef;
373 Colormap cmap;
374 if (dpy && (bpp == 32 || bpp == 16)) {
375#if !NO_X11
376 X_LOCK;
377 cmap = DefaultColormap (dpy, scr);
378 if (XParseColor(dpy, cmap, str, &cdef) &&
379 XAllocColor(dpy, cmap, &cdef)) {
380 r = cdef.red >> 8;
381 g = cdef.green >> 8;
382 b = cdef.blue >> 8;
383 if (r == 0 && g == 0) {
384 g = 1; /* need to be > 255 */
385 }
386 n = 0;
387 n |= (r << main_red_shift);
388 n |= (g << main_green_shift);
389 n |= (b << main_blue_shift);
390 wireframe_shade = n;
391 ok = 1;
392 }
393 X_UNLOCK;
394#else
395 r = g = b = 0;
396 cmap = 0;
397 cdef.pixel = 0;
398#endif
399 }
400 if (ok) {
401 ;
402 } else if (sscanf(str, "0x%lx", &n) == 1) {
403 wireframe_shade = n;
404 } else if (sscanf(str, "%lu", &n) == 1) {
405 wireframe_shade = n;
406 } else if (sscanf(str, "%lx", &n) == 1) {
407 wireframe_shade = n;
408 }
409 free(str);
410 }
411
412 /* linewidth: # of pixels wide for the wireframe lines */
413 if ((str = part[1]) != NULL) {
414 int n;
415 if (sscanf(str, "%d", &n) == 1) {
416 wireframe_lw = n;
417 if (wireframe_lw < 1) {
418 wireframe_lw = 1;
419 }
420 if (wireframe_lw > LW_MAX) {
421 wireframe_lw = LW_MAX;
422 }
423 }
424 free(str);
425 }
426
427 /* percentage cutoff for opaque move/resize (like WM's) */
428 if ((str = part[2]) != NULL) {
429 if (*str == '\0') {
430 ;
431 } else if (strchr(str, '.')) {
432 wireframe_frac = atof(str);
433 } else {
434 wireframe_frac = ((double) atoi(str))/100.0;
435 }
436 free(str);
437 }
438
439 /*
440 * Top, Bottom, Left, Right tolerances to guess the wm frame is
441 * being grabbed (Top is traditionally bigger, i.e. titlebar):
442 */
443 if ((str = part[3]) != NULL) {
444 int t, b, l, r;
445 if (sscanf(str, "%d+%d+%d+%d", &t, &b, &l, &r) == 4) {
446 wireframe_top = t;
447 wireframe_bot = b;
448 wireframe_left = l;
449 wireframe_right = r;
450 }
451 free(str);
452 }
453
454 /*
455 * wireframe in interior with Modifier down.
456 * 0 => no mods
457 * 1 => all mods
458 * Shift,Alt,Control,Meta,Super,Hyper
459 */
460 if (wireframe_mods) {
461 free(wireframe_mods);
462 }
463 wireframe_mods = NULL;
464 if ((str = part[4]) != NULL) {
465 if (*str == '0' || !strcmp(str, "none")) {
466 ;
467 } else if (*str == '1' || !strcmp(str, "all")) {
468 wireframe_mods = strdup("all");
469 } else if (!strcmp(str, "Alt") || !strcmp(str, "Shift")
470 || !strcmp(str, "Control") || !strcmp(str, "Meta")
471 || !strcmp(str, "Super") || !strcmp(str, "Hyper")) {
472 wireframe_mods = strdup(str);
473 }
474 }
475
476 /* check_wireframe() timing heuristics. */
477 if ((str = part[5]) != NULL) {
478 double t1, t2, t3, t4;
479 if (sscanf(str, "%lf+%lf+%lf+%lf", &t1, &t2, &t3, &t4) == 4) {
480 wireframe_t1 = t1;
481 wireframe_t2 = t2;
482 wireframe_t3 = t3;
483 wireframe_t4 = t4;
484 }
485 free(str);
486 }
487}
488
489/*
490 * First parse the defaults and apply any user supplied ones (may be a subset)
491 */
492void parse_wireframe(void) {
493 parse_wireframe_str(WIREFRAME_PARMS);
494 if (! wireframe_str) {
495 wireframe_str = strdup(WIREFRAME_PARMS);
496 }
497 parse_wireframe_str(wireframe_str);
498}
499
500/*
501 * Set wireframe_copyrect based on desired mode.
502 */
503void set_wirecopyrect_mode(char *str) {
504 char *orig = wireframe_copyrect;
505 if (str == NULL || *str == '\0') {
506 wireframe_copyrect = strdup(wireframe_copyrect_default);
507 } else if (!strcmp(str, "always") || !strcmp(str, "all")) {
508 wireframe_copyrect = strdup("always");
509 } else if (!strcmp(str, "top")) {
510 wireframe_copyrect = strdup("top");
511 } else if (!strcmp(str, "never") || !strcmp(str, "none")) {
512 wireframe_copyrect = strdup("never");
513 } else {
514 if (! wireframe_copyrect) {
515 wireframe_copyrect = strdup(wireframe_copyrect_default);
516 } else {
517 orig = NULL;
518 }
519 rfbLog("unknown -wirecopyrect mode: %s, using: %s\n", str,
520 wireframe_copyrect);
521 }
522 if (orig) {
523 free(orig);
524 }
525}
526
527/*
528 * Set scroll_copyrect based on desired mode.
529 */
530void set_scrollcopyrect_mode(char *str) {
531 char *orig = scroll_copyrect;
532 if (str == NULL || *str == '\0') {
533 scroll_copyrect = strdup(scroll_copyrect_default);
534 } else if (!strcmp(str, "always") || !strcmp(str, "all") ||
535 !strcmp(str, "both")) {
536 scroll_copyrect = strdup("always");
537 } else if (!strcmp(str, "keys") || !strcmp(str, "keyboard")) {
538 scroll_copyrect = strdup("keys");
539 } else if (!strcmp(str, "mouse") || !strcmp(str, "pointer")) {
540 scroll_copyrect = strdup("mouse");
541 } else if (!strcmp(str, "never") || !strcmp(str, "none")) {
542 scroll_copyrect = strdup("never");
543 } else {
544 if (! scroll_copyrect) {
545 scroll_copyrect = strdup(scroll_copyrect_default);
546 } else {
547 orig = NULL;
548 }
549 rfbLog("unknown -scrollcopyrect mode: %s, using: %s\n", str,
550 scroll_copyrect);
551 }
552 if (orig) {
553 free(orig);
554 }
555}
556
557void initialize_scroll_keys(void) {
558 char *str, *p;
559 int i, nkeys = 0, saw_builtin = 0;
560 int ks_max = 2 * 0xFFFF;
561
562 if (scroll_key_list) {
563 free(scroll_key_list);
564 scroll_key_list = NULL;
565 }
566 if (! scroll_key_list_str || *scroll_key_list_str == '\0') {
567 return;
568 }
569
570 if (strstr(scroll_key_list_str, "builtin")) {
571 int k;
572 /* add in number of keysyms builtin gives */
573 for (k=1; k<ks_max; k++) {
574 if (xrecord_scroll_keysym((rfbKeySym) k)) {
575 nkeys++;
576 }
577 }
578 }
579
580 nkeys++; /* first key, i.e. no commas. */
581 p = str = strdup(scroll_key_list_str);
582 while(*p) {
583 if (*p == ',') {
584 nkeys++; /* additional key. */
585 }
586 p++;
587 }
588
589 nkeys++; /* exclude/include 0 element */
590 nkeys++; /* trailing NoSymbol */
591
592 scroll_key_list = (KeySym *) malloc(nkeys*sizeof(KeySym));
593 for (i=0; i<nkeys; i++) {
594 scroll_key_list[i] = NoSymbol;
595 }
596 if (*str == '-') {
597 scroll_key_list[0] = 1;
598 p = strtok(str+1, ",");
599 } else {
600 p = strtok(str, ",");
601 }
602 i = 1;
603 while (p) {
604 if (!strcmp(p, "builtin")) {
605 int k;
606 if (saw_builtin) {
607 p = strtok(NULL, ",");
608 continue;
609 }
610 saw_builtin = 1;
611 for (k=1; k<ks_max; k++) {
612 if (xrecord_scroll_keysym((rfbKeySym) k)) {
613 scroll_key_list[i++] = (rfbKeySym) k;
614 }
615 }
616 } else {
617 unsigned int in;
618 if (sscanf(p, "%u", &in) == 1) {
619 scroll_key_list[i++] = (rfbKeySym) in;
620 } else if (sscanf(p, "0x%x", &in) == 1) {
621 scroll_key_list[i++] = (rfbKeySym) in;
622 } else if (XStringToKeysym(p) != NoSymbol) {
623 scroll_key_list[i++] = XStringToKeysym(p);
624 } else {
625 rfbLog("initialize_scroll_keys: skip unknown "
626 "keysym: %s\n", p);
627 }
628 }
629 p = strtok(NULL, ",");
630 }
631 free(str);
632}
633
634static void destroy_str_list(char **list) {
635 int i = 0;
636 if (! list) {
637 return;
638 }
639 while (list[i] != NULL) {
640 free(list[i++]);
641 }
642 free(list);
643}
644
645void initialize_scroll_matches(void) {
646 char *str, *imp = "__IMPOSSIBLE_STR__";
647 int i, n, nkey, nmouse;
648
649 destroy_str_list(scroll_good_all);
650 scroll_good_all = NULL;
651 destroy_str_list(scroll_good_key);
652 scroll_good_key = NULL;
653 destroy_str_list(scroll_good_mouse);
654 scroll_good_mouse = NULL;
655
656 destroy_str_list(scroll_skip_all);
657 scroll_skip_all = NULL;
658 destroy_str_list(scroll_skip_key);
659 scroll_skip_key = NULL;
660 destroy_str_list(scroll_skip_mouse);
661 scroll_skip_mouse = NULL;
662
663 /* scroll_good: */
664 if (scroll_good_str != NULL && *scroll_good_str != '\0') {
665 str = scroll_good_str;
666 } else {
667 str = scroll_good_str0;
668 }
669 scroll_good_all = create_str_list(str);
670
671 nkey = 0;
672 nmouse = 0;
673 n = 0;
674 while (scroll_good_all[n] != NULL) {
675 char *s = scroll_good_all[n++];
676 if (strstr(s, "KEY:") == s) nkey++;
677 if (strstr(s, "MOUSE:") == s) nmouse++;
678 }
679 if (nkey++) {
680 scroll_good_key = (char **) malloc(nkey*sizeof(char *));
681 for (i=0; i<nkey; i++) scroll_good_key[i] = NULL;
682 }
683 if (nmouse++) {
684 scroll_good_mouse = (char **) malloc(nmouse*sizeof(char *));
685 for (i=0; i<nmouse; i++) scroll_good_mouse[i] = NULL;
686 }
687 nkey = 0;
688 nmouse = 0;
689 for (i=0; i<n; i++) {
690 char *s = scroll_good_all[i];
691 if (strstr(s, "KEY:") == s) {
692 scroll_good_key[nkey++] = strdup(s+strlen("KEY:"));
693 free(s);
694 scroll_good_all[i] = strdup(imp);
695 } else if (strstr(s, "MOUSE:") == s) {
696 scroll_good_mouse[nmouse++]=strdup(s+strlen("MOUSE:"));
697 free(s);
698 scroll_good_all[i] = strdup(imp);
699 }
700 }
701
702 /* scroll_skip: */
703 if (scroll_skip_str != NULL && *scroll_skip_str != '\0') {
704 str = scroll_skip_str;
705 } else {
706 str = scroll_skip_str0;
707 }
708 scroll_skip_all = create_str_list(str);
709
710 nkey = 0;
711 nmouse = 0;
712 n = 0;
713 while (scroll_skip_all[n] != NULL) {
714 char *s = scroll_skip_all[n++];
715 if (strstr(s, "KEY:") == s) nkey++;
716 if (strstr(s, "MOUSE:") == s) nmouse++;
717 }
718 if (nkey++) {
719 scroll_skip_key = (char **) malloc(nkey*sizeof(char *));
720 for (i=0; i<nkey; i++) scroll_skip_key[i] = NULL;
721 }
722 if (nmouse++) {
723 scroll_skip_mouse = (char **) malloc(nmouse*sizeof(char *));
724 for (i=0; i<nmouse; i++) scroll_skip_mouse[i] = NULL;
725 }
726 nkey = 0;
727 nmouse = 0;
728 for (i=0; i<n; i++) {
729 char *s = scroll_skip_all[i];
730 if (strstr(s, "KEY:") == s) {
731 scroll_skip_key[nkey++] = strdup(s+strlen("KEY:"));
732 free(s);
733 scroll_skip_all[i] = strdup(imp);
734 } else if (strstr(s, "MOUSE:") == s) {
735 scroll_skip_mouse[nmouse++]=strdup(s+strlen("MOUSE:"));
736 free(s);
737 scroll_skip_all[i] = strdup(imp);
738 }
739 }
740}
741
742void initialize_scroll_term(void) {
743 char *str;
744 int n;
745
746 destroy_str_list(scroll_term);
747 scroll_term = NULL;
748
749 if (scroll_term_str != NULL && *scroll_term_str != '\0') {
750 str = scroll_term_str;
751 } else {
752 str = scroll_term_str0;
753 }
754 if (!strcmp(str, "none")) {
755 return;
756 }
757 scroll_term = create_str_list(str);
758
759 n = 0;
760 while (scroll_term[n] != NULL) {
761 char *s = scroll_good_all[n++];
762 /* pull parameters out at some point */
763 s = NULL;
764 }
765}
766
767void initialize_max_keyrepeat(void) {
768 char *str;
769 int lo, hi;
770
771 if (max_keyrepeat_str != NULL && *max_keyrepeat_str != '\0') {
772 str = max_keyrepeat_str;
773 } else {
774 str = max_keyrepeat_str0;
775 }
776
777 if (sscanf(str, "%d-%d", &lo, &hi) != 2) {
778 rfbLog("skipping invalid -scr_keyrepeat string: %s\n", str);
779 sscanf(max_keyrepeat_str0, "%d-%d", &lo, &hi);
780 }
781 max_keyrepeat_lo = lo;
782 max_keyrepeat_hi = hi;
783 if (max_keyrepeat_lo < 1) {
784 max_keyrepeat_lo = 1;
785 }
786 if (max_keyrepeat_hi > 40) {
787 max_keyrepeat_hi = 40;
788 }
789}
790
791typedef struct saveline {
792 int x0, y0, x1, y1;
793 int shift;
794 int vert;
795 int saved;
796 char *data;
797} saveline_t;
798
799/*
800 * Draw the wireframe box onto the framebuffer. Saves the real
801 * framebuffer data to some storage lines. Restores previous lines.
802 * use restore = 1 to clean up (done with animation).
803 * This works with -scale.
804 */
805static void draw_box(int x, int y, int w, int h, int restore) {
806 int x0, y0, x1, y1, i, pixelsize = bpp/8;
807 char *dst, *src, *use_fb;
808 static saveline_t *save[4];
809 static int first = 1, len = 0;
810 int max = dpy_x > dpy_y ? dpy_x : dpy_y;
811 int use_Bpl, lw = wireframe_lw;
812 unsigned long shade = wireframe_shade;
813 int color = 0;
814 unsigned short us = 0;
815 unsigned long ul = 0;
816
817 if (clipshift) {
818 x -= coff_x;
819 y -= coff_y;
820 }
821
822 /* handle -8to24 mode: use 2nd fb only */
823 use_fb = main_fb;
824 use_Bpl = main_bytes_per_line;
825
826 if (cmap8to24 && cmap8to24_fb) {
827 use_fb = cmap8to24_fb;
828 pixelsize = 4;
829 if (depth <= 8) {
830 use_Bpl *= 4;
831 } else if (depth <= 16) {
832 use_Bpl *= 2;
833 }
834 }
835
836 if (max > len) {
837 /* create/resize storage lines: */
838 for (i=0; i<4; i++) {
839 len = max;
840 if (! first && save[i]) {
841 if (save[i]->data) {
842 free(save[i]->data);
843 save[i]->data = NULL;
844 }
845 free(save[i]);
846 }
847 save[i] = (saveline_t *) malloc(sizeof(saveline_t));
848 save[i]->saved = 0;
849 save[i]->data = (char *) malloc( (LW_MAX+1)*len*4 );
850
851 /*
852 * Four types of lines:
853 * 0) top horizontal
854 * 1) bottom horizontal
855 * 2) left vertical
856 * 3) right vertical
857 *
858 * shift means shifted by width or height.
859 */
860 if (i == 0) {
861 save[i]->vert = 0;
862 save[i]->shift = 0;
863 } else if (i == 1) {
864 save[i]->vert = 0;
865 save[i]->shift = 1;
866 } else if (i == 2) {
867 save[i]->vert = 1;
868 save[i]->shift = 0;
869 } else if (i == 3) {
870 save[i]->vert = 1;
871 save[i]->shift = 1;
872 }
873 }
874 }
875 first = 0;
876
877 /*
878 * restore any saved lines. see below for algorithm and
879 * how x0, etc. are used. we just reverse those steps.
880 */
881 for (i=0; i<4; i++) {
882 int s = save[i]->shift;
883 int yu, y_min = -1, y_max = -1;
884 int y_start, y_stop, y_step;
885
886 if (! save[i]->saved) {
887 continue;
888 }
889 x0 = save[i]->x0;
890 y0 = save[i]->y0;
891 x1 = save[i]->x1;
892 y1 = save[i]->y1;
893 if (save[i]->vert) {
894 y_start = y0+lw;
895 y_stop = y1-lw;
896 y_step = lw*pixelsize;
897 } else {
898 y_start = y0 - s*lw;
899 y_stop = y_start + lw;
900 y_step = max*pixelsize;
901 }
902 for (yu = y_start; yu < y_stop; yu++) {
903 if (x0 == x1) {
904 continue;
905 }
906 if (yu < 0 || yu >= dpy_y) {
907 continue;
908 }
909 if (y_min < 0 || yu < y_min) {
910 y_min = yu;
911 }
912 if (y_max < 0 || yu > y_max) {
913 y_max = yu;
914 }
915 src = save[i]->data + (yu-y_start)*y_step;
916 dst = use_fb + yu*use_Bpl + x0*pixelsize;
917 memcpy(dst, src, (x1-x0)*pixelsize);
918 }
919 if (y_min >= 0) {
920if (0) fprintf(stderr, "Mark-1 %d %d %d %d\n", x0, y_min, x1, y_max+1);
921 mark_rect_as_modified(x0, y_min, x1, y_max+1, 0);
922 }
923 save[i]->saved = 0;
924 }
925
926if (0) fprintf(stderr, " DrawBox: %04dx%04d+%04d+%04d B=%d rest=%d lw=%d %.4f\n", w, h, x, y, 2*(w+h)*(2-restore)*pixelsize*lw, restore, lw, dnowx());
927
928 if (restore) {
929 return;
930 }
931
932
933 /*
934 * work out shade/color for the wireframe line, could be a color
935 * for 16bpp or 24bpp.
936 */
937 if (shade > 255) {
938 if (pixelsize == 2) {
939 us = (unsigned short) (shade & 0xffff);
940 color = 1;
941 } else if (pixelsize == 4) {
942 ul = (unsigned long) shade;
943 color = 1;
944 } else {
945 shade = shade % 256;
946 }
947 }
948
949 for (i=0; i<4; i++) {
950 int s = save[i]->shift;
951 int yu, y_min = -1, y_max = -1;
952 int yblack = -1, xblack1 = -1, xblack2 = -1;
953 int y_start, y_stop, y_step;
954
955 if (save[i]->vert) {
956 /*
957 * make the narrow x's be on the screen, let
958 * the y's hang off (not drawn).
959 */
960 save[i]->x0 = x0 = nfix(x + s*w - s*lw, dpy_x);
961 save[i]->y0 = y0 = y;
962 save[i]->x1 = x1 = nfix(x + s*w - s*lw + lw, dpy_x);
963 save[i]->y1 = y1 = y + h;
964
965 /*
966 * start and stop a linewidth away from true edge,
967 * to avoid interfering with horizontal lines.
968 */
969 y_start = y0+lw;
970 y_stop = y1-lw;
971 y_step = lw*pixelsize;
972
973 /* draw a black pixel for the border if lw > 1 */
974 if (s) {
975 xblack1 = x1-1;
976 } else {
977 xblack1 = x0;
978 }
979 } else {
980 /*
981 * make the wide x's be on the screen, let the y's
982 * hang off (not drawn).
983 */
984 save[i]->x0 = x0 = nfix(x, dpy_x);
985 save[i]->y0 = y0 = y + s*h;
986 save[i]->x1 = x1 = nfix(x + w, dpy_x);
987 save[i]->y1 = y1 = y0 + lw;
988 y_start = y0 - s*lw;
989 y_stop = y_start + lw;
990 y_step = max*pixelsize;
991
992 /* draw a black pixels for the border if lw > 1 */
993 if (s) {
994 yblack = y_stop - 1;
995 } else {
996 yblack = y_start;
997 }
998 xblack1 = x0;
999 xblack2 = x1-1;
1000 }
1001
1002 /* now loop over the allowed y's for either case */
1003 for (yu = y_start; yu < y_stop; yu++) {
1004 if (x0 == x1) {
1005 continue;
1006 }
1007 if (yu < 0 || yu >= dpy_y) {
1008 continue;
1009 }
1010
1011 /* record min and max y's for marking rectangle: */
1012 if (y_min < 0 || yu < y_min) {
1013 y_min = yu;
1014 }
1015 if (y_max < 0 || yu > y_max) {
1016 y_max = yu;
1017 }
1018
1019 /* save fb data for this line: */
1020 save[i]->saved = 1;
1021 src = use_fb + yu*use_Bpl + x0*pixelsize;
1022 dst = save[i]->data + (yu-y_start)*y_step;
1023 memcpy(dst, src, (x1-x0)*pixelsize);
1024
1025 /* apply the shade/color to make the wireframe line: */
1026 if (! color) {
1027 memset(src, shade, (x1-x0)*pixelsize);
1028 } else {
1029 char *csrc = src;
1030 unsigned short *usp;
1031 unsigned long *ulp;
1032 int k;
1033 for (k=0; k < x1 - x0; k++) {
1034 if (pixelsize == 4) {
1035 ulp = (unsigned long *)csrc;
1036 *ulp = ul;
1037 } else if (pixelsize == 2) {
1038 usp = (unsigned short *)csrc;
1039 *usp = us;
1040 }
1041 csrc += pixelsize;
1042 }
1043 }
1044
1045 /* apply black border for lw >= 2 */
1046 if (lw > 1) {
1047 if (yu == yblack) {
1048 memset(src, 0, (x1-x0)*pixelsize);
1049 }
1050 if (xblack1 >= 0) {
1051 src = src + (xblack1 - x0)*pixelsize;
1052 memset(src, 0, pixelsize);
1053 }
1054 if (xblack2 >= 0) {
1055 src = src + (xblack2 - x0)*pixelsize;
1056 memset(src, 0, pixelsize);
1057 }
1058 }
1059 }
1060 /* mark it for sending: */
1061 if (save[i]->saved) {
1062if (0) fprintf(stderr, "Mark-2 %d %d %d %d\n", x0, y_min, x1, y_max+1);
1063 mark_rect_as_modified(x0, y_min, x1, y_max+1, 0);
1064 }
1065 }
1066}
1067
1068int direct_fb_copy(int x1, int y1, int x2, int y2, int mark) {
1069 char *src, *dst;
1070 int y, pixelsize = bpp/8;
1071 int xmin = -1, xmax = -1, ymin = -1, ymax = -1;
1072 int do_cmp = 2;
1073 double tm;
1074 int db = 0;
1075
1076if (db) dtime0(&tm);
1077
1078 x1 = nfix(x1, dpy_x);
1079 y1 = nfix(y1, dpy_y);
1080 x2 = nfix(x2, dpy_x+1);
1081 y2 = nfix(y2, dpy_y+1);
1082
1083 if (x1 == x2) {
1084 return 1;
1085 }
1086 if (y1 == y2) {
1087 return 1;
1088 }
1089
1090 X_LOCK;
1091 for (y = y1; y < y2; y++) {
1092 XRANDR_SET_TRAP_RET(0, "direct_fb_copy-set");
1093 copy_image(scanline, x1, y, x2 - x1, 1);
1094 XRANDR_CHK_TRAP_RET(0, "direct_fb_copy-chk");
1095
1096 src = scanline->data;
1097 dst = main_fb + y * main_bytes_per_line + x1 * pixelsize;
1098
1099 if (do_cmp == 0 || !mark) {
1100 memcpy(dst, src, (x2 - x1)*pixelsize);
1101
1102 } else if (do_cmp == 1) {
1103 if (memcmp(dst, src, (x2 - x1)*pixelsize)) {
1104 if (ymin == -1 || y < ymin) {
1105 ymin = y;
1106 }
1107 if (ymax == -1 || y > ymax) {
1108 ymax = y;
1109 }
1110 memcpy(dst, src, (x2 - x1)*pixelsize);
1111 }
1112
1113 } else if (do_cmp == 2) {
1114 int n, shift, xlo, xhi, k, block = 32;
1115 char *dst2, *src2;
1116
1117 for (k=0; k*block < (x2 - x1); k++) {
1118 shift = k*block;
1119 xlo = x1 + shift;
1120 xhi = xlo + block;
1121 if (xhi > x2) {
1122 xhi = x2;
1123 }
1124 n = xhi - xlo;
1125 if (n < 1) {
1126 continue;
1127 }
1128 src2 = src + shift*pixelsize;
1129 dst2 = dst + shift*pixelsize;
1130 if (memcmp(dst2, src2, n*pixelsize)) {
1131 if (ymin == -1 || y < ymin) {
1132 ymin = y;
1133 }
1134 if (ymax == -1 || y > ymax) {
1135 ymax = y;
1136 }
1137 if (xmin == -1 || xlo < xmin) {
1138 xmin = xlo;
1139 }
1140 if (xmax == -1 || xhi > xmax) {
1141 xmax = xhi;
1142 }
1143 memcpy(dst2, src2, n*pixelsize);
1144 }
1145 }
1146 }
1147 }
1148 X_UNLOCK;
1149
1150 if (do_cmp == 0) {
1151 xmin = x1;
1152 ymin = y1;
1153 xmax = x2;
1154 ymax = y2;
1155 } else if (do_cmp == 1) {
1156 xmin = x1;
1157 xmax = x2;
1158 }
1159
1160 if (xmin < 0 || ymin < 0 || xmax < 0 || xmin < 0) {
1161 /* no diffs */
1162 return 1;
1163 }
1164
1165 if (xmax < x2) {
1166 xmax++;
1167 }
1168 if (ymax < y2) {
1169 ymax++;
1170 }
1171
1172 if (mark) {
1173 mark_rect_as_modified(xmin, ymin, xmax, ymax, 0);
1174 }
1175
1176 if (db) {
1177 fprintf(stderr, "direct_fb_copy: %dx%d+%d+%d - %d %.4f\n",
1178 x2 - x1, y2 - y1, x1, y1, mark, dtime(&tm));
1179 }
1180
1181 return 1;
1182}
1183
1184static int do_bdpush(Window wm_win, int x0, int y0, int w0, int h0, int bdx,
1185 int bdy, int bdskinny) {
1186
1187 XWindowAttributes attr;
1188 sraRectangleIterator *iter;
1189 sraRect rect;
1190 sraRegionPtr frame, whole, tmpregion;
1191 int tx1, ty1, tx2, ty2;
1192 static Window last_wm_win = None;
1193 static int last_x, last_y, last_w, last_h;
1194 int do_fb_push = 0;
1195 int db = debug_scroll;
1196
1197 if (wm_win == last_wm_win) {
1198 attr.x = last_x;
1199 attr.y = last_y;
1200 attr.width = last_w;
1201 attr.height = last_h;
1202 } else {
1203 if (!valid_window(wm_win, &attr, 1)) {
1204 return do_fb_push;
1205 }
1206 last_wm_win = wm_win;
1207 last_x = attr.x;
1208 last_y = attr.y;
1209 last_w = attr.width;
1210 last_h = attr.height;
1211 }
1212if (db > 1) fprintf(stderr, "BDP %d %d %d %d %d %d %d %d %d %d %d\n",
1213 x0, y0, w0, h0, bdx, bdy, bdskinny, last_x, last_y, last_w, last_h);
1214
1215 /* wm frame: */
1216 tx1 = attr.x;
1217 ty1 = attr.y;
1218 tx2 = attr.x + attr.width;
1219 ty2 = attr.y + attr.height;
1220
1221 whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
1222 if (clipshift) {
1223 sraRgnOffset(whole, coff_x, coff_y);
1224 }
1225 if (subwin) {
1226 sraRgnOffset(whole, off_x, off_y);
1227 }
1228 frame = sraRgnCreateRect(tx1, ty1, tx2, ty2);
1229 sraRgnAnd(frame, whole);
1230
1231 /* scrolling window: */
1232 tmpregion = sraRgnCreateRect(x0, y0, x0 + w0, y0 + h0);
1233 sraRgnAnd(tmpregion, whole);
1234
1235 sraRgnSubtract(frame, tmpregion);
1236 sraRgnDestroy(tmpregion);
1237
1238 if (!sraRgnEmpty(frame)) {
1239 double dt = 0.0, dm;
1240 dtime0(&dm);
1241 iter = sraRgnGetIterator(frame);
1242 while (sraRgnIteratorNext(iter, &rect)) {
1243 tx1 = rect.x1;
1244 ty1 = rect.y1;
1245 tx2 = rect.x2;
1246 ty2 = rect.y2;
1247
1248 if (bdskinny > 0) {
1249 int ok = 0;
1250 if (nabs(ty2-ty1) <= bdskinny) {
1251 ok = 1;
1252 }
1253 if (nabs(tx2-tx1) <= bdskinny) {
1254 ok = 1;
1255 }
1256 if (! ok) {
1257 continue;
1258 }
1259 }
1260
1261 if (bdx >= 0) {
1262 if (bdx < tx1 || tx2 <= bdx) {
1263 continue;
1264 }
1265 }
1266 if (bdy >= 0) {
1267 if (bdy < ty1 || ty2 <= bdy) {
1268 continue;
1269 }
1270 }
1271 if (clipshift) {
1272 tx1 -= coff_x;
1273 ty1 -= coff_y;
1274 tx2 -= coff_x;
1275 ty2 -= coff_y;
1276 }
1277 if (subwin) {
1278 tx1 -= off_x;
1279 ty1 -= off_y;
1280 tx2 -= off_x;
1281 ty2 -= off_y;
1282 }
1283
1284 direct_fb_copy(tx1, ty1, tx2, ty2, 1);
1285
1286 do_fb_push++;
1287 dt += dtime(&dm);
1288if (db > 1) fprintf(stderr, " BDP(%d,%d-%d,%d) dt: %.4f\n", tx1, ty1, tx2, ty2, dt);
1289 }
1290 sraRgnReleaseIterator(iter);
1291 }
1292 sraRgnDestroy(whole);
1293 sraRgnDestroy(frame);
1294
1295 return do_fb_push;
1296}
1297
1298static int set_ypad(void) {
1299 int ev, ev_tot = scr_ev_cnt;
1300 static Window last_win = None;
1301 static double last_time = 0.0;
1302 static int y_accum = 0, last_sign = 0;
1303 double now, cut = 0.1;
1304 int dy_sum = 0, ys = 0, sign;
1305 int font_size = 15;
1306 int win_y, scr_y, loc_cut = 4*font_size, y_cut = 10*font_size;
1307
1308 if (!xrecord_set_by_keys || !xrecord_name_info) {
1309 return 0;
1310 }
1311 if (xrecord_name_info[0] == '\0') {
1312 return 0;
1313 }
1314 if (! ev_tot) {
1315 return 0;
1316 }
1317 if (xrecord_keysym == NoSymbol) {
1318 return 0;
1319 }
1320 if (!xrecord_scroll_keysym(xrecord_keysym)) {
1321 return 0;
1322 }
1323 if (!scroll_term) {
1324 return 0;
1325 }
1326 if (!match_str_list(xrecord_name_info, scroll_term)) {
1327 return 0;
1328 }
1329
1330 for (ev=0; ev < ev_tot; ev++) {
1331 dy_sum += nabs(scr_ev[ev].dy);
1332 if (scr_ev[ev].dy < 0) {
1333 ys--;
1334 } else if (scr_ev[ev].dy > 0) {
1335 ys++;
1336 } else {
1337 ys = 0;
1338 break;
1339 }
1340 if (scr_ev[ev].win != scr_ev[0].win) {
1341 ys = 0;
1342 break;
1343 }
1344 if (scr_ev[ev].dx != 0) {
1345 ys = 0;
1346 break;
1347 }
1348 }
1349 if (ys != ev_tot && ys != -ev_tot) {
1350 return 0;
1351 }
1352 if (ys < 0) {
1353 sign = -1;
1354 } else {
1355 sign = 1;
1356 }
1357
1358 if (sign > 0) {
1359 /*
1360 * this case is not as useful as scrolling near the
1361 * bottom of a terminal. But there are problems for it too.
1362 */
1363 return 0;
1364 }
1365
1366 win_y = scr_ev[0].win_y + scr_ev[0].win_h;
1367 scr_y = scr_ev[0].y + scr_ev[0].h;
1368 if (nabs(scr_y - win_y) > loc_cut) {
1369 /* require it to be near the bottom. */
1370 return 0;
1371 }
1372
1373 now = dnow();
1374
1375 if (now < last_time + cut) {
1376 int ok = 1;
1377 if (last_win && scr_ev[0].win != last_win) {
1378 ok = 0;
1379 }
1380 if (last_sign && sign != last_sign) {
1381 ok = 0;
1382 }
1383 if (! ok) {
1384 last_win = None;
1385 last_sign = 0;
1386 y_accum = 0;
1387 last_time = 0.0;
1388 return 0;
1389 }
1390 } else {
1391 last_win = None;
1392 last_sign = 0;
1393 last_time = 0.0;
1394 y_accum = 0;
1395 }
1396
1397 y_accum += sign * dy_sum;
1398
1399 if (4 * nabs(y_accum) > scr_ev[0].h && y_cut) {
1400 ; /* TBD */
1401 }
1402
1403 last_sign = sign;
1404 last_win = scr_ev[0].win;
1405 last_time = now;
1406
1407 return y_accum;
1408}
1409
1410static void scale_mark(int x1, int y1, int x2, int y2, int mark) {
1411 int s = 2;
1412 x1 = nfix(x1 - s, dpy_x);
1413 y1 = nfix(y1 - s, dpy_y);
1414 x2 = nfix(x2 + s, dpy_x+1);
1415 y2 = nfix(y2 + s, dpy_y+1);
1416 scale_and_mark_rect(x1, y1, x2, y2, mark);
1417}
1418
1419#define PUSH_TEST(n) \
1420if (n) { \
1421 double dt = 0.0, tm; dtime0(&tm); \
1422 fprintf(stderr, "PUSH---\n"); \
1423 while (dt < 2.0) { rfbPE(50000); dt += dtime(&tm); } \
1424 fprintf(stderr, "---PUSH\n"); \
1425}
1426
1427int batch_dxs[], batch_dys[];
1428sraRegionPtr batch_reg[];
1429void batch_push(int ncr, double delay);
1430
1431static int push_scr_ev(double *age, int type, int bdpush, int bdx, int bdy,
1432 int bdskinny, int first_push) {
1433 Window frame, win, win0;
1434 int x, y, w, h, wx, wy, ww, wh, dx, dy;
1435 int x0, y0, w0, h0;
1436 int nx, ny, nw, nh;
1437 int dret = 1, do_fb_push = 0, obscured;
1438 int ev, ev_tot = scr_ev_cnt;
1439 double tm, dt, st, waittime = 0.125;
1440 double max_age = *age;
1441 int db = debug_scroll, rrate = get_read_rate();
1442 sraRegionPtr backfill, whole, tmpregion, tmpregion2;
1443 int link, latency, netrate;
1444 int ypad = 0;
1445 double last_scroll_event_save = last_scroll_event;
1446 int fast_push = 0, rc;
1447
1448 /* we return the oldest one. */
1449 *age = 0.0;
1450
1451 if (ev_tot == 0) {
1452 return dret;
1453 }
1454
1455 link = link_rate(&latency, &netrate);
1456
1457 if (link == LR_DIALUP) {
1458 waittime *= 5;
1459 } else if (link == LR_BROADBAND) {
1460 waittime *= 3;
1461 } else if (latency > 80 || netrate < 40) {
1462 waittime *= 3;
1463 }
1464
1465 backfill = sraRgnCreate();
1466 whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
1467 if (clipshift) {
1468 sraRgnOffset(whole, coff_x, coff_y);
1469 }
1470 if (subwin) {
1471 sraRgnOffset(whole, off_x, off_y);
1472 }
1473
1474 win0 = scr_ev[0].win;
1475 x0 = scr_ev[0].win_x;
1476 y0 = scr_ev[0].win_y;
1477 w0 = scr_ev[0].win_w;
1478 h0 = scr_ev[0].win_h;
1479
1480 ypad = set_ypad();
1481
1482if (db) fprintf(stderr, "ypad: %d dy[0]: %d ev_tot: %d\n", ypad, scr_ev[0].dy, ev_tot);
1483
1484 for (ev=0; ev < ev_tot; ev++) {
1485 double ag;
1486
1487 x = scr_ev[ev].x;
1488 y = scr_ev[ev].y;
1489 w = scr_ev[ev].w;
1490 h = scr_ev[ev].h;
1491 dx = scr_ev[ev].dx;
1492 dy = scr_ev[ev].dy;
1493 win = scr_ev[ev].win;
1494 wx = scr_ev[ev].win_x;
1495 wy = scr_ev[ev].win_y;
1496 ww = scr_ev[ev].win_w;
1497 wh = scr_ev[ev].win_h;
1498 nx = scr_ev[ev].new_x;
1499 ny = scr_ev[ev].new_y;
1500 nw = scr_ev[ev].new_w;
1501 nh = scr_ev[ev].new_h;
1502 st = scr_ev[ev].t;
1503
1504 ag = (dnow() - servertime_diff) - st;
1505 if (ag > *age) {
1506 *age = ag;
1507 }
1508
1509 if (dabs(ag) > max_age) {
1510if (db) fprintf(stderr, "push_scr_ev: TOO OLD: %.4f :: (%.4f - %.4f) "
1511 "- %.4f \n", ag, dnow(), servertime_diff, st);
1512 dret = 0;
1513 break;
1514 } else {
1515if (db) fprintf(stderr, "push_scr_ev: AGE: %.4f\n", ag);
1516 }
1517 if (win != win0) {
1518if (db) fprintf(stderr, "push_scr_ev: DIFF WIN: 0x%lx != 0x%lx\n", win, win0);
1519 dret = 0;
1520 break;
1521 }
1522 if (wx != x0 || wy != y0) {
1523if (db) fprintf(stderr, "push_scr_ev: WIN SHIFT: %d %d, %d %d", wx, x0, wy, y0);
1524 dret = 0;
1525 break;
1526 }
1527 if (ww != w0 || wh != h0) {
1528if (db) fprintf(stderr, "push_scr_ev: WIN RESIZE: %d %d, %d %d", ww, w0, wh, h0);
1529 dret = 0;
1530 break;
1531 }
1532 if (w < 1 || h < 1 || ww < 1 || wh < 1) {
1533if (db) fprintf(stderr, "push_scr_ev: NEGATIVE h/w: %d %d %d %d\n", w, h, ww, wh);
1534 dret = 0;
1535 break;
1536 }
1537
1538if (db > 1) fprintf(stderr, "push_scr_ev: got: %d x: %4d y: %3d"
1539 " w: %4d h: %3d dx: %d dy: %d %dx%d+%d+%d win: 0x%lx\n",
1540 ev, x, y, w, h, dx, dy, w, h, x, y, win);
1541
1542if (db > 1) fprintf(stderr, "------------ got: %d x: %4d y: %3d"
1543 " w: %4d h: %3d %dx%d+%d+%d\n",
1544 ev, wx, wy, ww, wh, ww, wh, wx, wy);
1545
1546if (db > 1) fprintf(stderr, "------------ got: %d x: %4d y: %3d"
1547 " w: %4d h: %3d %dx%d+%d+%d\n",
1548 ev, nx, ny, nw, nh, nw, nh, nx, ny);
1549
1550 frame = None;
1551 if (xrecord_wm_window) {
1552 frame = xrecord_wm_window;
1553 }
1554 if (! frame) {
1555 X_LOCK;
1556 frame = query_pointer(rootwin);
1557 X_UNLOCK;
1558 }
1559 if (! frame) {
1560 frame = win;
1561 }
1562
1563 dtime0(&tm);
1564
1565 tmpregion = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
1566 if (clipshift) {
1567 sraRgnOffset(tmpregion, coff_x, coff_y);
1568 }
1569 if (subwin) {
1570 sraRgnOffset(tmpregion, off_x, off_y);
1571 }
1572 tmpregion2 = sraRgnCreateRect(wx, wy, wx+ww, wy+wh);
1573 sraRgnAnd(tmpregion2, whole);
1574 sraRgnSubtract(tmpregion, tmpregion2);
1575 sraRgnDestroy(tmpregion2);
1576
1577 /* do the wm frame just incase the above is bogus too. */
1578 if (frame && frame != win) {
1579 int k, gotk = -1;
1580 for (k = stack_list_num - 1; k >= 0; k--) {
1581 if (stack_list[k].win == frame &&
1582 stack_list[k].fetched &&
1583 stack_list[k].valid &&
1584 stack_list[k].map_state == IsViewable) {
1585 gotk = k;
1586 break;
1587 }
1588 }
1589 if (gotk != -1) {
1590 int tx1, ty1, tx2, ty2;
1591 tx1 = stack_list[gotk].x;
1592 ty1 = stack_list[gotk].y;
1593 tx2 = tx1 + stack_list[gotk].width;
1594 ty2 = ty1 + stack_list[gotk].height;
1595 tmpregion2 = sraRgnCreateRect(tx1,ty1,tx2,ty2);
1596 sraRgnAnd(tmpregion2, whole);
1597 sraRgnSubtract(tmpregion, tmpregion2);
1598 sraRgnDestroy(tmpregion2);
1599 }
1600 }
1601
1602 /*
1603 * XXX Need to also clip:
1604 * children of win
1605 * siblings of win higher in stacking order.
1606 * ignore for now... probably will make some apps
1607 * act very strangely.
1608 */
1609 if (ypad) {
1610 if (ypad < 0) {
1611 if (h > -ypad) {
1612 h += ypad;
1613 } else {
1614 ypad = 0;
1615 }
1616 } else {
1617 if (h > ypad) {
1618 y += ypad;
1619 } else {
1620 ypad = 0;
1621 }
1622 }
1623 }
1624
1625 if (fast_push) {
1626 int nbatch = 0;
1627 double delay, d1 = 0.1, d2 = 0.02;
1628 rc = try_copyrect(frame, frame, x, y, w, h, dx, dy, &obscured,
1629 tmpregion, waittime, &nbatch);
1630
1631 if (first_push) {
1632 delay = d1;
1633 } else {
1634 delay = d2;
1635 }
1636
1637 batch_push(nbatch, delay);
1638 fb_push();
1639 } else {
1640 rc = try_copyrect(frame, frame, x, y, w, h, dx, dy, &obscured,
1641 tmpregion, waittime, NULL);
1642 if (rc) {
1643 last_scroll_type = type;
1644 dtime0(&last_scroll_event);
1645
1646 do_fb_push++;
1647 urgent_update = 1;
1648 sraRgnDestroy(tmpregion);
1649PUSH_TEST(0);
1650 }
1651 }
1652
1653 if (! rc) {
1654 dret = 0;
1655 sraRgnDestroy(tmpregion);
1656 break;
1657 }
1658 dt = dtime(&tm);
1659if (0) fprintf(stderr, " try_copyrect dt: %.4f\n", dt);
1660
1661 if (ev > 0) {
1662 sraRgnOffset(backfill, dx, dy);
1663 sraRgnAnd(backfill, whole);
1664 }
1665
1666 if (ypad) {
1667 if (ypad < 0) {
1668 ny += ypad;
1669 nh -= ypad;
1670 } else {
1671 ;
1672 }
1673 }
1674
1675 tmpregion = sraRgnCreateRect(nx, ny, nx + nw, ny + nh);
1676 sraRgnAnd(tmpregion, whole);
1677 sraRgnOr(backfill, tmpregion);
1678 sraRgnDestroy(tmpregion);
1679 }
1680
1681 /* try to update the backfill region (new window contents) */
1682 if (dret != 0) {
1683 double est, win_area = 0.0, area = 0.0;
1684 sraRectangleIterator *iter;
1685 sraRect rect;
1686 int tx1, ty1, tx2, ty2;
1687
1688 tmpregion = sraRgnCreateRect(x0, y0, x0 + w0, y0 + h0);
1689 sraRgnAnd(tmpregion, whole);
1690
1691 sraRgnAnd(backfill, tmpregion);
1692
1693 iter = sraRgnGetIterator(tmpregion);
1694 while (sraRgnIteratorNext(iter, &rect)) {
1695 tx1 = rect.x1;
1696 ty1 = rect.y1;
1697 tx2 = rect.x2;
1698 ty2 = rect.y2;
1699
1700 win_area += (tx2 - tx1)*(ty2 - ty1);
1701 }
1702 sraRgnReleaseIterator(iter);
1703
1704 sraRgnDestroy(tmpregion);
1705
1706
1707 iter = sraRgnGetIterator(backfill);
1708 while (sraRgnIteratorNext(iter, &rect)) {
1709 tx1 = rect.x1;
1710 ty1 = rect.y1;
1711 tx2 = rect.x2;
1712 ty2 = rect.y2;
1713
1714 area += (tx2 - tx1)*(ty2 - ty1);
1715 }
1716 sraRgnReleaseIterator(iter);
1717
1718 est = (area * (bpp/8)) / (1000000.0 * rrate);
1719if (db) fprintf(stderr, " area %.1f win_area %.1f est: %.4f", area, win_area, est);
1720 if (area > 0.90 * win_area) {
1721if (db) fprintf(stderr, " AREA_TOO_MUCH");
1722 dret = 0;
1723 } else if (est > 0.6) {
1724if (db) fprintf(stderr, " EST_TOO_LARGE");
1725 dret = 0;
1726 } else if (area <= 0.0) {
1727 ;
1728 } else {
1729 dtime0(&tm);
1730 iter = sraRgnGetIterator(backfill);
1731 while (sraRgnIteratorNext(iter, &rect)) {
1732 tx1 = rect.x1;
1733 ty1 = rect.y1;
1734 tx2 = rect.x2;
1735 ty2 = rect.y2;
1736
1737 if (clipshift) {
1738 tx1 -= coff_x;
1739 ty1 -= coff_y;
1740 tx2 -= coff_x;
1741 ty2 -= coff_y;
1742 }
1743 if (subwin) {
1744 tx1 -= off_x;
1745 ty1 -= off_y;
1746 tx2 -= off_x;
1747 ty2 -= off_y;
1748 }
1749 tx1 = nfix(tx1, dpy_x);
1750 ty1 = nfix(ty1, dpy_y);
1751 tx2 = nfix(tx2, dpy_x+1);
1752 ty2 = nfix(ty2, dpy_y+1);
1753
1754 dtime(&tm);
1755if (db) fprintf(stderr, " DFC(%d,%d-%d,%d)", tx1, ty1, tx2, ty2);
1756 direct_fb_copy(tx1, ty1, tx2, ty2, 1);
1757 if (fast_push) {
1758 fb_push();
1759 }
1760 do_fb_push++;
1761PUSH_TEST(0);
1762 }
1763 sraRgnReleaseIterator(iter);
1764
1765 dt = dtime(&tm);
1766if (db) fprintf(stderr, " dfc---- dt: %.4f", dt);
1767
1768 }
1769if (db && dret) fprintf(stderr, " **** dret=%d", dret);
1770if (db && !dret) fprintf(stderr, " ---- dret=%d", dret);
1771if (db) fprintf(stderr, "\n");
1772 }
1773
1774if (db && bdpush) fprintf(stderr, "BDPUSH-TIME: 0x%lx\n", xrecord_wm_window);
1775
1776 if (bdpush && xrecord_wm_window != None) {
1777 int x, y, w, h;
1778 x = scr_ev[0].x;
1779 y = scr_ev[0].y;
1780 w = scr_ev[0].w;
1781 h = scr_ev[0].h;
1782 do_fb_push += do_bdpush(xrecord_wm_window, x, y, w, h,
1783 bdx, bdy, bdskinny);
1784 if (fast_push) {
1785 fb_push();
1786 }
1787 }
1788
1789 if (do_fb_push) {
1790 dtime0(&tm);
1791 fb_push();
1792 dt = dtime(&tm);
1793if (0) fprintf(stderr, " fb_push dt: %.4f", dt);
1794 if (scaling) {
1795 static double last_time = 0.0;
1796 double now = dnow(), delay = 0.4, first_wait = 3.0;
1797 double trate;
1798 int repeating, lat, rate;
1799 int link = link_rate(&lat, &rate);
1800 int skip_first = 0;
1801
1802 if (link == LR_DIALUP || rate < 35) {
1803 delay *= 4;
1804 } else if (link != LR_LAN || rate < 100) {
1805 delay *= 2;
1806 }
1807
1808 trate = typing_rate(0.0, &repeating);
1809
1810 if (xrecord_set_by_mouse || repeating >= 3) {
1811 if (now > last_scroll_event_save + first_wait) {
1812 skip_first = 1;
1813 }
1814 }
1815
1816 if (skip_first) {
1817 /*
1818 * try not to send the first one, but a
1819 * single keystroke scroll would be OK.
1820 */
1821 } else if (now > last_time + delay) {
1822
1823 scale_mark(x0, y0, x0 + w0, y0 + h0, 1);
1824 last_copyrect_fix = now;
1825 }
1826 last_time = now;
1827 }
1828 }
1829
1830 sraRgnDestroy(backfill);
1831 sraRgnDestroy(whole);
1832 return dret;
1833}
1834
1835static void get_client_regions(int *req, int *mod, int *cpy, int *num) {
1836
1837 rfbClientIteratorPtr i;
1838 rfbClientPtr cl;
1839
1840 *req = 0;
1841 *mod = 0;
1842 *cpy = 0;
1843 *num = 0;
1844
1845 i = rfbGetClientIterator(screen);
1846 while( (cl = rfbClientIteratorNext(i)) ) {
1847 if (use_threads) LOCK(cl->updateMutex);
1848 *req += sraRgnCountRects(cl->requestedRegion);
1849 *mod += sraRgnCountRects(cl->modifiedRegion);
1850 *cpy += sraRgnCountRects(cl->copyRegion);
1851 *num += 1;
1852 if (use_threads) UNLOCK(cl->updateMutex);
1853 }
1854 rfbReleaseClientIterator(i);
1855}
1856
1857/*
1858 * Wrapper to apply the rfbDoCopyRegion taking into account if scaling
1859 * is being done. Note that copyrect under the scaling case is often
1860 * only approximate.
1861 */
1862int DCR_Normal = 0;
1863int DCR_FBOnly = 1;
1864int DCR_Direct = 2;
1865
1866void do_copyregion(sraRegionPtr region, int dx, int dy, int mode) {
1867 sraRectangleIterator *iter;
1868 sraRect rect;
1869 int Bpp0 = bpp/8, Bpp;
1870 int x1, y1, x2, y2, w, stride, stride0;
1871 int sx1, sy1, sx2, sy2, sdx, sdy;
1872 int req, mod, cpy, ncli;
1873 char *dst = NULL, *src = NULL;
1874
1875 last_copyrect = dnow();
1876
1877 if (rfb_fb == main_fb && ! rotating && mode == DCR_Normal) {
1878 /* normal case, no -scale or -8to24 */
1879 get_client_regions(&req, &mod, &cpy, &ncli);
1880if (0 || debug_scroll > 1) fprintf(stderr, ">>>-rfbDoCopyRect req: %d mod: %d cpy: %d\n", req, mod, cpy);
1881
1882 rfbDoCopyRegion(screen, region, dx, dy);
1883
1884 get_client_regions(&req, &mod, &cpy, &ncli);
1885if (0 || debug_scroll > 1) fprintf(stderr, "<<<-rfbDoCopyRect req: %d mod: %d cpy: %d\n", req, mod, cpy);
1886
1887 return;
1888 }
1889
1890 /* rarer case, we need to call rfbDoCopyRect with scaled xy */
1891 stride0 = dpy_x * Bpp0;
1892
1893 iter = sraRgnGetReverseIterator(region, dx < 0, dy < 0);
1894 while(sraRgnIteratorNext(iter, &rect)) {
1895 int j, c, t;
1896
1897 x1 = rect.x1;
1898 y1 = rect.y1;
1899 x2 = rect.x2;
1900 y2 = rect.y2;
1901
1902 for (c= 0; c < 2; c++) {
1903
1904 Bpp = Bpp0;
1905 stride = stride0;
1906
1907 if (c == 0) {
1908 dst = main_fb + y1*stride + x1*Bpp;
1909 src = main_fb + (y1-dy)*stride + (x1-dx)*Bpp;
1910
1911 } else if (c == 1) {
1912 if (!cmap8to24 || !cmap8to24_fb) {
1913 continue;
1914 }
1915 if (cmap8to24_fb == rfb_fb) {
1916 if (mode == DCR_FBOnly) {
1917 ;
1918 } else if (mode == DCR_Direct) {
1919 ;
1920 } else if (mode == DCR_Normal) {
1921 continue;
1922 }
1923 }
1924if (0) fprintf(stderr, "copyrect: cmap8to24_fb: mode=%d\n", mode);
1925 if (cmap8to24) {
1926 if (depth <= 8) {
1927 Bpp = 4 * Bpp0;
1928 stride = 4 * stride0;
1929 } else if (depth <= 16) {
1930 Bpp = 2 * Bpp0;
1931 stride = 2 * stride0;
1932 }
1933 }
1934 dst = cmap8to24_fb + y1*stride + x1*Bpp;
1935 src = cmap8to24_fb + (y1-dy)*stride + (x1-dx)*Bpp;
1936 }
1937
1938 w = (x2 - x1)*Bpp;
1939
1940 if (dy < 0) {
1941 for (j=y1; j<y2; j++) {
1942 memmove(dst, src, w);
1943 dst += stride;
1944 src += stride;
1945 }
1946 } else {
1947 dst += (y2 - y1 - 1)*stride;
1948 src += (y2 - y1 - 1)*stride;
1949 for (j=y2-1; j>=y1; j--) {
1950 memmove(dst, src, w);
1951 dst -= stride;
1952 src -= stride;
1953 }
1954 }
1955 }
1956
1957 if (mode == DCR_FBOnly) {
1958 continue;
1959 }
1960
1961
1962 if (scaling) {
1963 sx1 = ((double) x1 / dpy_x) * scaled_x;
1964 sy1 = ((double) y1 / dpy_y) * scaled_y;
1965 sx2 = ((double) x2 / dpy_x) * scaled_x;
1966 sy2 = ((double) y2 / dpy_y) * scaled_y;
1967 sdx = ((double) dx / dpy_x) * scaled_x;
1968 sdy = ((double) dy / dpy_y) * scaled_y;
1969 } else {
1970 sx1 = x1;
1971 sy1 = y1;
1972 sx2 = x2;
1973 sy2 = y2;
1974 sdx = dx;
1975 sdy = dy;
1976 }
1977if (0) fprintf(stderr, "sa.. %d %d %d %d %d %d\n", sx1, sy1, sx2, sy2, sdx, sdy);
1978
1979 if (rotating) {
1980 rotate_coords(sx1, sy1, &sx1, &sy1, -1, -1);
1981 rotate_coords(sx2, sy2, &sx2, &sy2, -1, -1);
1982 if (rotating == ROTATE_X) {
1983 sdx = -sdx;
1984 } else if (rotating == ROTATE_Y) {
1985 sdy = -sdy;
1986 } else if (rotating == ROTATE_XY) {
1987 sdx = -sdx;
1988 sdy = -sdy;
1989 } else if (rotating == ROTATE_90) {
1990 t = sdx;
1991 sdx = -sdy;
1992 sdy = t;
1993 } else if (rotating == ROTATE_90X) {
1994 t = sdx;
1995 sdx = sdy;
1996 sdy = t;
1997 } else if (rotating == ROTATE_90Y) {
1998 t = sdx;
1999 sdx = -sdy;
2000 sdy = -t;
2001 } else if (rotating == ROTATE_270) {
2002 t = sdx;
2003 sdx = sdy;
2004 sdy = -t;
2005 }
2006 }
2007
2008 /* XXX -1? */
2009 if (sx2 < 0) sx2 = 0;
2010 if (sy2 < 0) sy2 = 0;
2011
2012 if (sx2 < sx1) {
2013 t = sx1;
2014 sx1 = sx2;
2015 sx2 = t;
2016 }
2017 if (sy2 < sy1) {
2018 t = sy1;
2019 sy1 = sy2;
2020 sy2 = t;
2021 }
2022if (0) fprintf(stderr, "sb.. %d %d %d %d %d %d\n", sx1, sy1, sx2, sy2, sdx, sdy);
2023
2024 if (mode == DCR_Direct) {
2025 rfbClientIteratorPtr i;
2026 rfbClientPtr cl;
2027 sraRegionPtr r = sraRgnCreateRect(sx1, sy1, sx2, sy2);
2028
2029 i = rfbGetClientIterator(screen);
2030 while( (cl = rfbClientIteratorNext(i)) ) {
2031 if (use_threads) LOCK(cl->updateMutex);
2032 rfbSendCopyRegion(cl, r, sdx, sdy);
2033 if (use_threads) UNLOCK(cl->updateMutex);
2034 }
2035 rfbReleaseClientIterator(i);
2036 sraRgnDestroy(r);
2037
2038 } else {
2039 rfbDoCopyRect(screen, sx1, sy1, sx2, sy2, sdx, sdy);
2040 }
2041 }
2042 sraRgnReleaseIterator(iter);
2043}
2044
2045void batch_copyregion(sraRegionPtr* region, int *dx, int *dy, int ncr, double delay) {
2046 rfbClientIteratorPtr i;
2047 rfbClientPtr cl;
2048 int k, direct, mode, nrects = 0, bad = 0;
2049 double t1, t2, start = dnow();
2050
2051 for (k=0; k < ncr; k++) {
2052 sraRectangleIterator *iter;
2053 sraRect rect;
2054
2055 iter = sraRgnGetIterator(region[k]);
2056 while (sraRgnIteratorNext(iter, &rect)) {
2057 int x1 = rect.x1;
2058 int y1 = rect.y1;
2059 int x2 = rect.x2;
2060 int y2 = rect.y2;
2061 int ym = dpy_y * (ncache+1);
2062 int xm = dpy_x;
2063 if (x1 > xm || y1 > ym || x2 > xm || y2 > ym) {
2064 if (ncdb) fprintf(stderr, "batch_copyregion: BAD RECTANGLE: %d,%d %d,%d\n", x1, y1, x2, y2);
2065 bad = 1;
2066 }
2067 if (x1 < 0 || y1 < 0 || x2 < 0 || y2 < 0) {
2068 if (ncdb) fprintf(stderr, "batch_copyregion: BAD RECTANGLE: %d,%d %d,%d\n", x1, y1, x2, y2);
2069 bad = 1;
2070 }
2071 }
2072 sraRgnReleaseIterator(iter);
2073 nrects += sraRgnCountRects(region[k]);
2074 }
2075 if (bad || nrects == 0) {
2076 return;
2077 }
2078
2079 if (delay < 0.0) {
2080 delay = 0.1;
2081 }
2082 if (!fb_push_wait(delay, FB_COPY|FB_MOD)) {
2083 if (use_threads) usleep(100 * 1000);
2084 fb_push_wait(0.75, FB_COPY|FB_MOD);
2085 }
2086
2087 t1 = dnow();
2088
2089 bad = 0;
2090 i = rfbGetClientIterator(screen);
2091 while( (cl = rfbClientIteratorNext(i)) ) {
2092
2093 if (use_threads) LOCK(cl->updateMutex);
2094
2095 if (cl->ublen != 0) {
2096 fprintf(stderr, "batch_copyregion: *** BAD ublen != 0: %d\n", cl->ublen);
2097 bad++;
2098 }
2099
2100 if (use_threads) UNLOCK(cl->updateMutex);
2101 }
2102 rfbReleaseClientIterator(i);
2103
2104 if (bad) {
2105 return;
2106 }
2107
2108 i = rfbGetClientIterator(screen);
2109 while( (cl = rfbClientIteratorNext(i)) ) {
2110 rfbFramebufferUpdateMsg *fu;
2111
2112 if (use_threads) LOCK(cl->updateMutex);
2113
2114 fu = (rfbFramebufferUpdateMsg *)cl->updateBuf;
2115 fu->nRects = Swap16IfLE((uint16_t)(nrects));
2116 fu->type = rfbFramebufferUpdate;
2117
2118 if (cl->ublen != 0) fprintf(stderr, "batch_copyregion: *** BAD-2 ublen != 0: %d\n", cl->ublen);
2119
2120 cl->ublen = sz_rfbFramebufferUpdateMsg;
2121
2122 if (use_threads) UNLOCK(cl->updateMutex);
2123 }
2124 rfbReleaseClientIterator(i);
2125
2126 if (rfb_fb == main_fb && !rotating) {
2127 direct = 0;
2128 mode = DCR_FBOnly;
2129 } else {
2130 direct = 1;
2131 mode = DCR_Direct;
2132 }
2133 for (k=0; k < ncr; k++) {
2134 do_copyregion(region[k], dx[k], dy[k], mode);
2135 }
2136
2137 t2 = dnow();
2138
2139 i = rfbGetClientIterator(screen);
2140 while( (cl = rfbClientIteratorNext(i)) ) {
2141
2142 if (use_threads) LOCK(cl->updateMutex);
2143
2144 if (!direct) {
2145 for (k=0; k < ncr; k++) {
2146 rfbSendCopyRegion(cl, region[k], dx[k], dy[k]);
2147 }
2148 }
2149 rfbSendUpdateBuf(cl);
2150
2151 if (use_threads) UNLOCK(cl->updateMutex);
2152 }
2153 rfbReleaseClientIterator(i);
2154
2155 last_copyrect = dnow();
2156
2157if (0) fprintf(stderr, "batch_copyregion: nrects: %d nregions: %d tot=%.4f t10=%.4f t21=%.4f t32=%.4f %.4f\n",
2158 nrects, ncr, last_copyrect - start, t1 - start, t2 - t1, last_copyrect - t2, dnowx());
2159
2160}
2161
2162void batch_push(int nreg, double delay) {
2163 int k;
2164 batch_copyregion(batch_reg, batch_dxs, batch_dys, nreg, delay);
2165 /* XXX Y */
2166 fb_push();
2167 for (k=0; k < nreg; k++) {
2168 sraRgnDestroy(batch_reg[k]);
2169 }
2170}
2171
2172void fb_push(void) {
2173 int req0, mod0, cpy0, req1, mod1, cpy1, ncli;
2174 int db = (debug_scroll || debug_wireframe);
2175 rfbClientIteratorPtr i;
2176 rfbClientPtr cl;
2177
2178 if (use_threads) {
2179 return;
2180 }
2181
2182if (db) get_client_regions(&req0, &mod0, &cpy0, &ncli);
2183
2184 i = rfbGetClientIterator(screen);
2185 while( (cl = rfbClientIteratorNext(i)) ) {
2186 if (use_threads) LOCK(cl->updateMutex);
2187 if (cl->sock >= 0 && !cl->onHold && FB_UPDATE_PENDING(cl) &&
2188 !sraRgnEmpty(cl->requestedRegion)) {
2189 if (!rfbSendFramebufferUpdate(cl, cl->modifiedRegion)) {
2190 fprintf(stderr, "*** rfbSendFramebufferUpdate *FAILED* #1\n");
2191 if (cl->ublen) fprintf(stderr, "*** fb_push ublen not zero: %d\n", cl->ublen);
2192 if (use_threads) UNLOCK(cl->updateMutex);
2193 break;
2194 }
2195 if (cl->ublen) fprintf(stderr, "*** fb_push ublen NOT ZERO: %d\n", cl->ublen);
2196 }
2197 if (use_threads) UNLOCK(cl->updateMutex);
2198 }
2199 rfbReleaseClientIterator(i);
2200
2201if (db) {
2202 get_client_regions(&req1, &mod1, &cpy1, &ncli);
2203 fprintf(stderr, "\nFB_push: req: %d/%d mod: %d/%d cpy: %d/%d %.4f\n",
2204 req0, req1, mod0, mod1, cpy0, cpy1, dnowx());
2205}
2206
2207}
2208
2209int fb_push_wait(double max_wait, int flags) {
2210 double tm, dt = 0.0;
2211 int req, mod, cpy, ncli;
2212 int ok = 0, first = 1;
2213
2214 dtime0(&tm);
2215 while (dt < max_wait) {
2216 int done = 1;
2217 fb_push();
2218 get_client_regions(&req, &mod, &cpy, &ncli);
2219 if (flags & FB_COPY && cpy) {
2220 done = 0;
2221 }
2222 if (flags & FB_MOD && mod) {
2223 done = 0;
2224 }
2225 if (flags & FB_REQ && req) {
2226 done = 0;
2227 }
2228 if (done) {
2229 ok = 1;
2230 break;
2231 }
2232 if (first) {
2233 first = 0;
2234 continue;
2235 }
2236
2237 rfbCFD(0);
2238 usleep(1000);
2239 dt += dtime(&tm);
2240 }
2241 return ok;
2242}
2243
2244/*
2245 * utility routine for CopyRect of the window (but not CopyRegion)
2246 */
2247static int crfix(int x, int dx, int Lx) {
2248 /* adjust x so that copy source is on screen */
2249 if (dx > 0) {
2250 if (x-dx < 0) {
2251 /* off on the left */
2252 x = dx;
2253 }
2254 } else {
2255 if (x-dx >= Lx) {
2256 /* off on the right */
2257 x = Lx + dx - 1;
2258 }
2259 }
2260 return x;
2261}
2262
2263typedef struct scroll_result {
2264 Window win;
2265 double time;
2266 int result;
2267} scroll_result_t;
2268
2269#define SCR_RESULTS_MAX 256
2270static scroll_result_t scroll_results[SCR_RESULTS_MAX];
2271
2272static int scrollability(Window win, int set) {
2273 double oldest = -1.0;
2274 int i, index = -1, next_index = -1;
2275 static int first = 1;
2276
2277 if (first) {
2278 for (i=0; i<SCR_RESULTS_MAX; i++) {
2279 scroll_results[i].win = None;
2280 scroll_results[i].time = 0.0;
2281 scroll_results[i].result = 0;
2282 }
2283 first = 0;
2284 }
2285
2286 if (win == None) {
2287 return 0;
2288 }
2289 if (set == SCR_NONE) {
2290 /* lookup case */
2291 for (i=0; i<SCR_RESULTS_MAX; i++) {
2292 if (win == scroll_results[i].win) {
2293 return scroll_results[i].result;
2294 }
2295 if (scroll_results[i].win == None) {
2296 break;
2297 }
2298 }
2299 return 0;
2300 }
2301
2302 for (i=0; i<SCR_RESULTS_MAX; i++) {
2303 if (oldest == -1.0 || scroll_results[i].time < oldest) {
2304 next_index = i;
2305 oldest = scroll_results[i].time;
2306 }
2307 if (win == scroll_results[i].win) {
2308 index = i;
2309 break;
2310 }
2311 if (next_index >= 0 && scroll_results[i].win == None) {
2312 break;
2313 }
2314 }
2315
2316 if (set == SCR_SUCCESS) {
2317 set = 1;
2318 } else if (set == SCR_FAIL) {
2319 set = -1;
2320 } else {
2321 set = 0;
2322 }
2323 if (index == -1) {
2324 scroll_results[next_index].win = win;
2325 scroll_results[next_index].time = dnow();
2326 scroll_results[next_index].result = set;
2327 } else {
2328 if (scroll_results[index].result == 1) {
2329 /*
2330 * once a success, always a success, until they
2331 * forget about us...
2332 */
2333 set = 1;
2334 } else {
2335 scroll_results[index].result = set;
2336 }
2337 scroll_results[index].time = dnow();
2338 }
2339
2340 return set;
2341}
2342
2343void eat_viewonly_input(int max_eat, int keep) {
2344 int i, gp, gk;
2345
2346 for (i=0; i<max_eat; i++) {
2347 int cont = 0;
2348 gp = got_pointer_calls;
2349 gk = got_keyboard_calls;
2350 rfbCFD(0);
2351 if (got_pointer_calls > gp) {
2352 if (debug_pointer) {
2353 rfbLog("eat_viewonly_input: pointer: %d\n", i);
2354 }
2355 cont++;
2356 }
2357 if (got_keyboard_calls > gk) {
2358 if (debug_keyboard) {
2359 rfbLog("eat_viewonly_input: keyboard: %d\n", i);
2360 }
2361 cont++;
2362 }
2363 if (i >= keep - 1 && ! cont) {
2364 break;
2365 }
2366 }
2367}
2368
2369static int eat_pointer(int max_ptr_eat, int keep) {
2370 int i, count = 0, gp = got_pointer_input;
2371
2372 for (i=0; i<max_ptr_eat; i++) {
2373 rfbCFD(0);
2374 if (got_pointer_input > gp) {
2375 count++;
2376if (0) fprintf(stderr, "GP*-%d\n", i);
2377 gp = got_pointer_input;
2378 } else if (i > keep) {
2379 break;
2380 }
2381 }
2382 return count;
2383}
2384
2385static void set_bdpush(int type, double *last_bdpush, int *pushit) {
2386 double now, delay = 0.0;
2387 int link, latency, netrate;
2388
2389 *pushit = 0;
2390
2391 if (type == SCR_MOUSE) {
2392 delay = scr_mouse_bdpush_time;
2393 } else if (type == SCR_KEY) {
2394 delay = scr_key_bdpush_time;
2395 }
2396
2397 link = link_rate(&latency, &netrate);
2398 if (link == LR_DIALUP) {
2399 delay *= 1.5;
2400 } else if (link == LR_BROADBAND) {
2401 delay *= 1.25;
2402 }
2403
2404 dtime0(&now);
2405 if (delay > 0.0 && now > *last_bdpush + delay) {
2406 *pushit = 1;
2407 *last_bdpush = now;
2408 }
2409}
2410
2411void mark_for_xdamage(int x, int y, int w, int h) {
2412 int tx1, ty1, tx2, ty2;
2413 sraRegionPtr tmpregion;
2414
2415 if (! use_xdamage) {
2416 return;
2417 }
2418
2419 tx1 = nfix(x, dpy_x);
2420 ty1 = nfix(y, dpy_y);
2421 tx2 = nfix(x + w, dpy_x+1);
2422 ty2 = nfix(y + h, dpy_y+1);
2423
2424 tmpregion = sraRgnCreateRect(tx1, ty1, tx2, ty2);
2425 add_region_xdamage(tmpregion);
2426 sraRgnDestroy(tmpregion);
2427}
2428
2429void mark_region_for_xdamage(sraRegionPtr region) {
2430 sraRectangleIterator *iter;
2431 sraRect rect;
2432 iter = sraRgnGetIterator(region);
2433 while (sraRgnIteratorNext(iter, &rect)) {
2434 int x1 = rect.x1;
2435 int y1 = rect.y1;
2436 int x2 = rect.x2;
2437 int y2 = rect.y2;
2438 mark_for_xdamage(x1, y1, x2 - x1, y2 - y1);
2439 }
2440 sraRgnReleaseIterator(iter);
2441}
2442
2443void set_xdamage_mark(int x, int y, int w, int h) {
2444 sraRegionPtr region;
2445
2446 if (! use_xdamage) {
2447 return;
2448 }
2449 mark_for_xdamage(x, y, w, h);
2450
2451 if (xdamage_scheduled_mark == 0.0) {
2452 xdamage_scheduled_mark = dnow() + 2.0;
2453 }
2454
2455 if (xdamage_scheduled_mark_region == NULL) {
2456 xdamage_scheduled_mark_region = sraRgnCreate();
2457 }
2458 region = sraRgnCreateRect(x, y, x + w, y + w);
2459 sraRgnOr(xdamage_scheduled_mark_region, region);
2460 sraRgnDestroy(region);
2461}
2462
2463static int repeat_check(double last_key_scroll) {
2464 int repeating;
2465 double rate = typing_rate(0.0, &repeating);
2466 double now = dnow(), delay = 0.5;
2467 if (rate > 2.0 && repeating && now > last_key_scroll + delay) {
2468 return 0;
2469 } else {
2470 return 1;
2471 }
2472}
2473
2474static int check_xrecord_keys(void) {
2475 static int last_wx, last_wy, last_ww, last_wh;
2476 double spin = 0.0, tm, tnow;
2477 int scr_cnt = 0, input = 0, scroll_rep;
2478 int get_out, got_one = 0, flush1 = 0, flush2 = 0;
2479 int gk, gk0, ret = 0, db = debug_scroll;
2480 int fail = 0;
2481 int link, latency, netrate;
2482
2483 static double last_key_scroll = 0.0;
2484 static double persist_start = 0.0;
2485 static double last_bdpush = 0.0;
2486 static int persist_count = 0;
2487 int scroll_keysym = 0;
2488 double last_scroll, scroll_persist = scr_key_persist;
2489 double spin_fac = 1.0, scroll_fac = 2.0, noscroll_fac = 0.75;
2490 double max_spin, max_long_spin = 0.3;
2491 double set_repeat_in;
2492 static double set_repeat = 0.0;
2493
2494
2495 RAWFB_RET(0)
2496
2497 if (unixpw_in_progress) return 0;
2498
2499 set_repeat_in = set_repeat;
2500 set_repeat = 0.0;
2501
2502 get_out = 1;
2503 if (got_keyboard_input) {
2504 get_out = 0;
2505 }
2506
2507 dtime0(&tnow);
2508 if (tnow < last_key_scroll + scroll_persist) {
2509 get_out = 0;
2510 }
2511
2512 if (set_repeat_in > 0.0 && tnow < last_key_scroll + set_repeat_in) {
2513 get_out = 0;
2514 }
2515
2516 if (get_out) {
2517 persist_start = 0.0;
2518 persist_count = 0;
2519 last_bdpush = 0.0;
2520 if (xrecording) {
2521 xrecord_watch(0, SCR_KEY);
2522 }
2523 return 0;
2524 }
2525
2526#if 0
2527 /* not used for keyboard yet */
2528 scroll_rep = scrollability(xrecord_ptr_window, SCR_NONE) + 1;
2529 if (scroll_rep == 1) {
2530 scroll_rep = 2; /* if no info, assume the best. */
2531 }
2532#endif
2533
2534 scroll_keysym = xrecord_scroll_keysym(last_rfb_keysym);
2535
2536 max_spin = scr_key_time;
2537
2538 if (set_repeat_in > 0.0 && tnow < last_key_scroll + 2*set_repeat_in) {
2539 max_spin = 2 * set_repeat_in;
2540 } else if (tnow < last_key_scroll + scroll_persist) {
2541 max_spin = 1.25*(tnow - last_key_scroll);
2542 } else if (tnow < last_key_to_button_remap_time + 1.5*scroll_persist) {
2543 /* mostly a hack I use for testing -remap key -> btn4/btn5 */
2544 max_spin = scroll_persist;
2545 } else if (scroll_keysym) {
2546 if (repeat_check(last_key_scroll)) {
2547 spin_fac = scroll_fac;
2548 } else {
2549 spin_fac = noscroll_fac;
2550 }
2551 }
2552 if (max_spin > max_long_spin) {
2553 max_spin = max_long_spin;
2554 }
2555
2556 /* XXX use this somehow */
2557if (0) link = link_rate(&latency, &netrate);
2558
2559 gk = gk0 = got_keyboard_input;
2560 dtime0(&tm);
2561
2562if (db) fprintf(stderr, "check_xrecord_keys: BEGIN LOOP: scr_ev_cnt: "
2563 "%d max: %.3f %.4f\n", scr_ev_cnt, max_spin, tm - x11vnc_start);
2564
2565 while (1) {
2566
2567 if (scr_ev_cnt) {
2568 got_one = 1;
2569
2570 scrollability(xrecord_ptr_window, SCR_SUCCESS);
2571 scroll_rep = 2;
2572
2573 dtime0(&last_scroll);
2574 last_key_scroll = last_scroll;
2575 scr_cnt++;
2576 break;
2577 }
2578
2579 X_LOCK;
2580 flush1 = 1;
2581 XFlush_wr(dpy);
2582 X_UNLOCK;
2583
2584 if (set_repeat_in > 0.0) {
2585 max_keyrepeat_time = set_repeat_in;
2586 }
2587
2588 if (use_threads) {
2589 usleep(1000);
2590 } else {
2591 rfbCFD(1000);
2592 }
2593 spin += dtime(&tm);
2594
2595 X_LOCK;
2596 if (got_keyboard_input > gk) {
2597 gk = got_keyboard_input;
2598 input++;
2599 if (set_repeat_in) {
2600 ;
2601 } else if (xrecord_scroll_keysym(last_rfb_keysym)) {
2602 if (repeat_check(last_key_scroll)) {
2603 spin_fac = scroll_fac;
2604 } else {
2605 spin_fac = noscroll_fac;
2606 }
2607 }
2608if (0 || db) fprintf(stderr, "check_xrecord: more keys: %.3f 0x%x "
2609 " %.4f %s %s\n", spin, last_rfb_keysym, last_rfb_keytime - x11vnc_start,
2610 last_rfb_down ? "down":"up ", last_rfb_key_accepted ? "accept":"skip");
2611 flush2 = 1;
2612 XFlush_wr(dpy);
2613 }
2614#if LIBVNCSERVER_HAVE_RECORD
2615 SCR_LOCK;
2616 XRecordProcessReplies(rdpy_data);
2617 SCR_UNLOCK;
2618#endif
2619 X_UNLOCK;
2620
2621 if (spin >= max_spin * spin_fac) {
2622if (0 || db) fprintf(stderr, "check_xrecord: SPIN-OUT: %.3f/%.3f\n", spin,
2623 max_spin * spin_fac);
2624 fail = 1;
2625 break;
2626 }
2627 }
2628
2629 max_keyrepeat_time = 0.0;
2630
2631 if (scr_ev_cnt) {
2632 int dret, ev = scr_ev_cnt - 1;
2633 int bdx, bdy, bdskinny, bdpush = 0;
2634 double max_age = 0.25, age, tm, dt;
2635 static double last_scr_ev = 0.0;
2636
2637 last_wx = scr_ev[ev].win_x;
2638 last_wy = scr_ev[ev].win_y;
2639 last_ww = scr_ev[ev].win_w;
2640 last_wh = scr_ev[ev].win_h;
2641
2642 /* assume scrollbar on rhs: */
2643 bdx = last_wx + last_ww + 3;
2644 bdy = last_wy + last_wh/2;
2645 bdskinny = 32;
2646
2647 if (persist_start == 0.0) {
2648 bdpush = 0;
2649 } else {
2650 set_bdpush(SCR_KEY, &last_bdpush, &bdpush);
2651 }
2652
2653 dtime0(&tm);
2654 age = max_age;
2655 dret = push_scr_ev(&age, SCR_KEY, bdpush, bdx, bdy, bdskinny, 1);
2656 dt = dtime(&tm);
2657
2658 ret = 1 + dret;
2659 scr_ev_cnt = 0;
2660
2661 if (ret == 2 && xrecord_scroll_keysym(last_rfb_keysym)) {
2662 int repeating;
2663 double time_lo = 1.0/max_keyrepeat_lo;
2664 double time_hi = 1.0/max_keyrepeat_hi;
2665 double rate = typing_rate(0.0, &repeating);
2666if (0 || db) fprintf(stderr, "Typing: dt: %.4f rate: %.1f\n", dt, rate);
2667 if (repeating) {
2668 /* n.b. the "quantum" is about 1/30 sec. */
2669 max_keyrepeat_time = 1.0*dt;
2670 if (max_keyrepeat_time > time_lo ||
2671 max_keyrepeat_time < time_hi) {
2672 max_keyrepeat_time = 0.0;
2673 } else {
2674 set_repeat = max_keyrepeat_time;
2675if (0 || db) fprintf(stderr, "set max_keyrepeat_time: %.2f\n", max_keyrepeat_time);
2676 }
2677 }
2678 }
2679
2680 last_scr_ev = dnow();
2681 }
2682
2683 if ((got_one && ret < 2) || persist_count) {
2684 set_xdamage_mark(last_wx, last_wy, last_ww, last_wh);
2685 }
2686
2687 if (fail) {
2688 scrollability(xrecord_ptr_window, SCR_FAIL);
2689 }
2690
2691 if (xrecording) {
2692 if (ret < 2) {
2693 xrecord_watch(0, SCR_KEY);
2694 }
2695 }
2696
2697 if (ret == 2) {
2698 if (persist_start == 0.0) {
2699 dtime(&persist_start);
2700 last_bdpush = persist_start;
2701 }
2702 } else {
2703 persist_start = 0.0;
2704 last_bdpush = 0.0;
2705 }
2706
2707 /* since we've flushed it, we might as well avoid -input_skip */
2708 if (flush1 || flush2) {
2709 got_keyboard_input = 0;
2710 got_pointer_input = 0;
2711 }
2712
2713 return ret;
2714}
2715
2716static int check_xrecord_mouse(void) {
2717 static int last_wx, last_wy, last_ww, last_wh;
2718 double spin = 0.0, tm, tnow;
2719 int i, scr_cnt = 0, input = 0, scroll_rep;
2720 int get_out, got_one = 0, flush1 = 0, flush2 = 0;
2721 int gp, gp0, ret = 0, db = debug_scroll;
2722 int gk, gk0;
2723 int fail = 0;
2724 int link, latency, netrate;
2725
2726 int start_x, start_y, last_x, last_y;
2727 static double last_mouse_scroll = 0.0;
2728 double last_scroll;
2729 double max_spin[3], max_long[3], persist[3];
2730 double flush1_time = 0.01;
2731 static double last_flush = 0.0;
2732 double last_bdpush = 0.0, button_up_time = 0.0;
2733 int button_mask_save;
2734 int already_down = 0, max_ptr_eat = 20;
2735 static int want_back_in = 0;
2736 int came_back_in;
2737 int first_push = 1;
2738
2739 int scroll_wheel = 0;
2740 int btn4 = (1<<3);
2741 int btn5 = (1<<4);
2742
2743 RAWFB_RET(0)
2744
2745 get_out = 1;
2746 if (button_mask) {
2747 get_out = 0;
2748 }
2749 if (want_back_in) {
2750 get_out = 0;
2751 }
2752 dtime0(&tnow);
2753if (0) fprintf(stderr, "check_xrecord_mouse: IN xrecording: %d\n", xrecording);
2754
2755 if (get_out) {
2756 if (xrecording) {
2757 xrecord_watch(0, SCR_MOUSE);
2758 }
2759 return 0;
2760 }
2761
2762 scroll_rep = scrollability(xrecord_ptr_window, SCR_NONE) + 1;
2763 if (scroll_rep == 1) {
2764 scroll_rep = 2; /* if no info, assume the best. */
2765 }
2766
2767 if (button_mask_prev) {
2768 already_down = 1;
2769 }
2770 if (want_back_in) {
2771 came_back_in = 1;
2772 first_push = 0;
2773 } else {
2774 came_back_in = 0;
2775 }
2776 want_back_in = 0;
2777
2778 if (button_mask & (btn4|btn5)) {
2779 scroll_wheel = 1;
2780 }
2781
2782 /*
2783 * set up times for the various "reputations"
2784 *
2785 * 0 => -1, has been tried but never found a scroll.
2786 * 1 => 0, has not been tried.
2787 * 2 => +1, has been tried and found a scroll.
2788 */
2789
2790 /* first spin-out time (no events) */
2791 max_spin[0] = 1*scr_mouse_time;
2792 max_spin[1] = 2*scr_mouse_time;
2793 max_spin[2] = 4*scr_mouse_time;
2794 if (!already_down) {
2795 for (i=0; i<3; i++) {
2796 max_spin[i] *= 1.5;
2797 }
2798 }
2799
2800 /* max time between events */
2801 persist[0] = 1*scr_mouse_persist;
2802 persist[1] = 2*scr_mouse_persist;
2803 persist[2] = 4*scr_mouse_persist;
2804
2805 /* absolute max time in the loop */
2806 max_long[0] = scr_mouse_maxtime;
2807 max_long[1] = scr_mouse_maxtime;
2808 max_long[2] = scr_mouse_maxtime;
2809
2810 pointer_flush_delay = scr_mouse_pointer_delay;
2811
2812 /* slow links: */
2813 link = link_rate(&latency, &netrate);
2814 if (link == LR_DIALUP) {
2815 for (i=0; i<3; i++) {
2816 max_spin[i] *= 2.0;
2817 }
2818 pointer_flush_delay *= 2;
2819 } else if (link == LR_BROADBAND) {
2820 pointer_flush_delay *= 2;
2821 }
2822
2823 gp = gp0 = got_pointer_input;
2824 gk = gk0 = got_keyboard_input;
2825 dtime0(&tm);
2826
2827 /*
2828 * this is used for border pushes (bdpush) to guess location
2829 * of scrollbar (region rects containing this point are pushed).
2830 */
2831 last_x = start_x = cursor_x;
2832 last_y = start_y = cursor_y;
2833
2834if (db) fprintf(stderr, "check_xrecord_mouse: BEGIN LOOP: scr_ev_cnt: "
2835 "%d max: %.3f %.4f\n", scr_ev_cnt, max_spin[scroll_rep], tm - x11vnc_start);
2836
2837 while (1) {
2838 double spin_check;
2839 if (scr_ev_cnt) {
2840 int dret, ev = scr_ev_cnt - 1;
2841 int bdpush = 0, bdx, bdy, bdskinny;
2842 double tm, dt, age = 0.35;
2843
2844 got_one = 1;
2845 scrollability(xrecord_ptr_window, SCR_SUCCESS);
2846 scroll_rep = 2;
2847
2848 scr_cnt++;
2849
2850 dtime0(&last_scroll);
2851 last_mouse_scroll = last_scroll;
2852
2853 if (last_bdpush == 0.0) {
2854 last_bdpush = last_scroll;
2855 }
2856
2857 bdx = start_x;
2858 bdy = start_y;
2859 if (clipshift) {
2860 bdx += coff_x;
2861 bdy += coff_y;
2862 }
2863 if (subwin) {
2864 bdx += off_x;
2865 bdy += off_y;
2866 }
2867 bdskinny = 32;
2868
2869 set_bdpush(SCR_MOUSE, &last_bdpush, &bdpush);
2870
2871 dtime0(&tm);
2872
2873 dret = push_scr_ev(&age, SCR_MOUSE, bdpush, bdx,
2874 bdy, bdskinny, first_push);
2875 if (first_push) first_push = 0;
2876 ret = 1 + dret;
2877
2878 dt = dtime(&tm);
2879
2880if (db) fprintf(stderr, " dret: %d scr_ev_cnt: %d dt: %.4f\n",
2881 dret, scr_ev_cnt, dt);
2882
2883 last_wx = scr_ev[ev].win_x;
2884 last_wy = scr_ev[ev].win_y;
2885 last_ww = scr_ev[ev].win_w;
2886 last_wh = scr_ev[ev].win_h;
2887 scr_ev_cnt = 0;
2888
2889 if (! dret) {
2890 break;
2891 }
2892 if (0 && button_up_time > 0.0) {
2893 /* we only take 1 more event with button up */
2894if (db) fprintf(stderr, "check_xrecord: BUTTON_UP_SCROLL: %.3f\n", spin);
2895 break;
2896 }
2897 }
2898
2899
2900 if (! flush1) {
2901 if (! already_down || (!scr_cnt && spin>flush1_time)) {
2902 flush1 = 1;
2903 X_LOCK;
2904 XFlush_wr(dpy);
2905 X_UNLOCK;
2906 dtime0(&last_flush);
2907 }
2908 }
2909
2910 if (use_threads) {
2911 usleep(1000);
2912 } else {
2913 rfbCFD(1000);
2914 rfbCFD(0);
2915 }
2916 spin += dtime(&tm);
2917
2918 if (got_pointer_input > gp) {
2919 flush2 = 1;
2920 input += eat_pointer(max_ptr_eat, 1);
2921 gp = got_pointer_input;
2922 }
2923 if (got_keyboard_input > gk) {
2924 gk = got_keyboard_input;
2925 input++;
2926 }
2927 X_LOCK;
2928#if LIBVNCSERVER_HAVE_RECORD
2929 SCR_LOCK;
2930 XRecordProcessReplies(rdpy_data);
2931 SCR_UNLOCK;
2932#endif
2933 X_UNLOCK;
2934
2935 if (! input) {
2936 spin_check = 1.5 * max_spin[scroll_rep];
2937 } else {
2938 spin_check = max_spin[scroll_rep];
2939 }
2940
2941 if (button_up_time > 0.0) {
2942 if (tm > button_up_time + max_spin[scroll_rep]) {
2943if (db) fprintf(stderr, "check_xrecord: SPIN-OUT-BUTTON_UP: %.3f/%.3f\n", spin, tm - button_up_time);
2944 break;
2945 }
2946 } else if (!scr_cnt) {
2947 if (spin >= spin_check) {
2948
2949if (db) fprintf(stderr, "check_xrecord: SPIN-OUT-1: %.3f/%.3f\n", spin, spin_check);
2950 fail = 1;
2951 break;
2952 }
2953 } else {
2954 if (tm >= last_scroll + persist[scroll_rep]) {
2955
2956if (db) fprintf(stderr, "check_xrecord: SPIN-OUT-2: %.3f/%.3f\n", spin, tm - last_scroll);
2957 break;
2958 }
2959 }
2960 if (spin >= max_long[scroll_rep]) {
2961
2962if (db) fprintf(stderr, "check_xrecord: SPIN-OUT-3: %.3f/%.3f\n", spin, max_long[scroll_rep]);
2963 break;
2964 }
2965
2966 if (! button_mask) {
2967 int doflush = 0;
2968 if (button_up_time > 0.0) {
2969 ;
2970 } else if (came_back_in) {
2971 dtime0(&button_up_time);
2972 doflush = 1;
2973 } else if (scroll_wheel) {
2974if (db) fprintf(stderr, "check_xrecord: SCROLL-WHEEL-BUTTON-UP-KEEP-GOING: %.3f/%.3f %d/%d %d/%d\n", spin, max_long[scroll_rep], last_x, last_y, cursor_x, cursor_y);
2975 doflush = 1;
2976 dtime0(&button_up_time);
2977 } else if (last_x == cursor_x && last_y == cursor_y) {
2978if (db) fprintf(stderr, "check_xrecord: BUTTON-UP: %.3f/%.3f %d/%d %d/%d\n", spin, max_long[scroll_rep], last_x, last_y, cursor_x, cursor_y);
2979 break;
2980 } else {
2981if (db) fprintf(stderr, "check_xrecord: BUTTON-UP-KEEP-GOING: %.3f/%.3f %d/%d %d/%d\n", spin, max_long[scroll_rep], last_x, last_y, cursor_x, cursor_y);
2982 doflush = 1;
2983 dtime0(&button_up_time);
2984 }
2985 if (doflush) {
2986 flush1 = 1;
2987 X_LOCK;
2988 XFlush_wr(dpy);
2989 X_UNLOCK;
2990 dtime0(&last_flush);
2991 }
2992 }
2993
2994 last_x = cursor_x;
2995 last_y = cursor_y;
2996 }
2997
2998 if (got_one) {
2999 set_xdamage_mark(last_wx, last_wy, last_ww, last_wh);
3000 }
3001
3002 if (fail) {
3003 scrollability(xrecord_ptr_window, SCR_FAIL);
3004 }
3005
3006 /* flush any remaining pointer events. */
3007 button_mask_save = button_mask;
3008 pointer_queued_sent = 0;
3009 last_x = cursor_x;
3010 last_y = cursor_y;
3011 pointer_event(-1, 0, 0, NULL);
3012 pointer_flush_delay = 0.0;
3013
3014 if (xrecording && pointer_queued_sent && button_mask_save &&
3015 (last_x != cursor_x || last_y != cursor_y) ) {
3016if (db) fprintf(stderr, " pointer() push yields events on: ret=%d\n", ret);
3017 if (ret == 2) {
3018if (db) fprintf(stderr, " we decide to send ret=3\n");
3019 want_back_in = 1;
3020 ret = 3;
3021 flush2 = 1;
3022 } else {
3023 if (ret) {
3024 ret = 1;
3025 } else {
3026 ret = 0;
3027 }
3028 xrecord_watch(0, SCR_MOUSE);
3029 }
3030 } else {
3031 if (ret) {
3032 ret = 1;
3033 } else {
3034 ret = 0;
3035 }
3036 if (xrecording) {
3037 xrecord_watch(0, SCR_MOUSE);
3038 }
3039 }
3040
3041 if (flush2) {
3042 X_LOCK;
3043 XFlush_wr(dpy);
3044 XFlush_wr(rdpy_ctrl);
3045 X_UNLOCK;
3046
3047 flush2 = 1;
3048 dtime0(&last_flush);
3049
3050if (db) fprintf(stderr, "FLUSH-2\n");
3051 }
3052
3053 /* since we've flushed it, we might as well avoid -input_skip */
3054 if (flush1 || flush2) {
3055 got_keyboard_input = 0;
3056 got_pointer_input = 0;
3057 }
3058
3059 if (ret) {
3060 return ret;
3061 } else if (scr_cnt) {
3062 return 1;
3063 } else {
3064 return 0;
3065 }
3066}
3067
3068int check_xrecord(void) {
3069 int watch_keys = 0, watch_mouse = 0, consider_mouse;
3070 static int mouse_wants_back_in = 0;
3071
3072 RAWFB_RET(0)
3073
3074 if (! use_xrecord) {
3075 return 0;
3076 }
3077 if (unixpw_in_progress) return 0;
3078
3079 if (skip_cr_when_scaling("scroll")) {
3080 return 0;
3081 }
3082
3083if (0) fprintf(stderr, "check_xrecord: IN xrecording: %d\n", xrecording);
3084
3085 if (! xrecording) {
3086 return 0;
3087 }
3088
3089 if (!strcmp(scroll_copyrect, "always")) {
3090 watch_keys = 1;
3091 watch_mouse = 1;
3092 } else if (!strcmp(scroll_copyrect, "keys")) {
3093 watch_keys = 1;
3094 } else if (!strcmp(scroll_copyrect, "mouse")) {
3095 watch_mouse = 1;
3096 }
3097
3098 if (button_mask || mouse_wants_back_in) {
3099 consider_mouse = 1;
3100 } else {
3101 consider_mouse = 0;
3102 }
3103if (0) fprintf(stderr, "check_xrecord: button_mask: %d mouse_wants_back_in: %d\n", button_mask, mouse_wants_back_in);
3104
3105 if (watch_mouse && consider_mouse && xrecord_set_by_mouse) {
3106 int ret = check_xrecord_mouse();
3107 if (ret == 3) {
3108 mouse_wants_back_in = 1;
3109 } else {
3110 mouse_wants_back_in = 0;
3111 }
3112 return ret;
3113 } else if (watch_keys && xrecord_set_by_keys) {
3114 mouse_wants_back_in = 0;
3115 return check_xrecord_keys();
3116 } else {
3117 mouse_wants_back_in = 0;
3118 return 0;
3119 }
3120}
3121
3122#define DB_SET \
3123 int db = 0; \
3124 int db2 = 0; \
3125 if (debug_wireframe == 1) { \
3126 db = 1; \
3127 } \
3128 if (debug_wireframe == 2) { \
3129 db2 = 1; \
3130 } \
3131 if (debug_wireframe == 3) { \
3132 db = 1; \
3133 db2 = 1; \
3134 }
3135
3136#define NBATCHMAX 1024
3137int batch_dxs[NBATCHMAX], batch_dys[NBATCHMAX];
3138sraRegionPtr batch_reg[NBATCHMAX];
3139
3140static int try_copyrect(Window orig_frame, Window frame, int x, int y, int w, int h,
3141 int dx, int dy, int *obscured, sraRegionPtr extra_clip, double max_wait, int *nbatch) {
3142
3143 static int dt_bad = 0;
3144 static time_t dt_bad_check = 0;
3145 int x1, y1, x2, y2, sent_copyrect = 0;
3146 int req, mod, cpy, ncli;
3147 double tm, dt;
3148 DB_SET
3149
3150 if (nbatch == NULL) {
3151 get_client_regions(&req, &mod, &cpy, &ncli);
3152 if (cpy) {
3153 /* one is still pending... try to force it out: */
3154 if (!fb_push_wait(max_wait, FB_COPY)) {
3155 fb_push_wait(max_wait/2, FB_COPY);
3156 }
3157
3158 get_client_regions(&req, &mod, &cpy, &ncli);
3159 }
3160 if (cpy) {
3161 return 0;
3162 }
3163 }
3164
3165 *obscured = 0;
3166 /*
3167 * XXX KDE and xfce do some weird things with the
3168 * stacking, it does not match XQueryTree. Work around
3169 * it for now by CopyRect-ing the *whole* on-screen
3170 * rectangle (whether obscured or not!)
3171 */
3172 if (time(NULL) > dt_bad_check + 5) {
3173 char *dt = guess_desktop();
3174 if (!strcmp(dt, "kde_maybe_is_ok_now...")) {
3175 dt_bad = 1;
3176 } else if (!strcmp(dt, "xfce")) {
3177 dt_bad = 1;
3178 } else {
3179 dt_bad = 0;
3180 }
3181 dt_bad_check = time(NULL);
3182 }
3183
3184 if (clipshift) {
3185 x -= coff_x;
3186 y -= coff_y;
3187 }
3188 if (subwin) {
3189 x -= off_x;
3190 y -= off_y;
3191 }
3192if (db2) fprintf(stderr, "try_copyrect: 0x%lx/0x%lx bad: %d stack_list_num: %d\n", orig_frame, frame, dt_bad, stack_list_num);
3193
3194/* XXX Y dt_bad = 0 */
3195 if (dt_bad && wireframe_in_progress) {
3196 sraRegionPtr rect;
3197 /* send the whole thing... */
3198 x1 = crfix(nfix(x, dpy_x), dx, dpy_x);
3199 y1 = crfix(nfix(y, dpy_y), dy, dpy_y);
3200 x2 = crfix(nfix(x+w, dpy_x+1), dx, dpy_x+1);
3201 y2 = crfix(nfix(y+h, dpy_y+1), dy, dpy_y+1);
3202
3203 rect = sraRgnCreateRect(x1, y1, x2, y2);
3204
3205 if (blackouts) {
3206 int i;
3207 sraRegionPtr bo_rect;
3208 for (i=0; i<blackouts; i++) {
3209 bo_rect = sraRgnCreateRect(blackr[i].x1,
3210 blackr[i].y1, blackr[i].x2, blackr[i].y2);
3211 sraRgnSubtract(rect, bo_rect);
3212 sraRgnDestroy(bo_rect);
3213 }
3214 }
3215 if (!nbatch) {
3216 do_copyregion(rect, dx, dy, 0);
3217 } else {
3218 batch_dxs[*nbatch] = dx;
3219 batch_dys[*nbatch] = dy;
3220 batch_reg[*nbatch] = sraRgnCreateRgn(rect);
3221 (*nbatch)++;
3222 }
3223 sraRgnDestroy(rect);
3224
3225 sent_copyrect = 1;
3226 *obscured = 1; /* set to avoid an aggressive push */
3227
3228 } else if (stack_list_num || dt_bad) {
3229 int k, tx1, tx2, ty1, ty2;
3230 sraRegionPtr moved_win, tmp_win, whole;
3231 sraRectangleIterator *iter;
3232 sraRect rect;
3233 int saw_me = 0;
3234 int orig_x, orig_y;
3235 int boff, bwin;
3236 XWindowAttributes attr;
3237
3238 orig_x = x - dx;
3239 orig_y = y - dy;
3240
3241 tx1 = nfix(orig_x, dpy_x);
3242 ty1 = nfix(orig_y, dpy_y);
3243 tx2 = nfix(orig_x+w, dpy_x+1);
3244 ty2 = nfix(orig_y+h, dpy_y+1);
3245
3246if (db2) fprintf(stderr, "moved_win: %4d %3d, %4d %3d 0x%lx ---\n",
3247 tx1, ty1, tx2, ty2, frame);
3248
3249 moved_win = sraRgnCreateRect(tx1, ty1, tx2, ty2);
3250
3251 dtime0(&tm);
3252
3253 boff = get_boff();
3254 bwin = get_bwin();
3255
3256 X_LOCK;
3257
3258 /*
3259 * loop over the stack, top to bottom until we
3260 * find our wm frame:
3261 */
3262 for (k = stack_list_num - 1; k >= 0; k--) {
3263 Window swin;
3264
3265 if (0 && dt_bad) {
3266 break;
3267 }
3268
3269 swin = stack_list[k].win;
3270if (db2) fprintf(stderr, "sw: %d/%lx\n", k, swin);
3271 if (swin == frame || swin == orig_frame) {
3272 if (db2) {
3273 saw_me = 1; fprintf(stderr, " ----------\n");
3274 } else {
3275 break;
3276 }
3277 }
3278
3279 /* skip some unwanted cases: */
3280#ifndef MACOSX
3281 if (swin == None) {
3282 continue;
3283 }
3284#endif
3285 if (boff <= (int) swin && (int) swin < boff + bwin) {
3286 ; /* blackouts */
3287 } else if (! stack_list[k].fetched ||
3288 stack_list[k].time > tm + 2.0) {
3289 if (!valid_window(swin, &attr, 1)) {
3290 stack_list[k].valid = 0;
3291 } else {
3292 stack_list[k].valid = 1;
3293 stack_list[k].x = attr.x;
3294 stack_list[k].y = attr.y;
3295 stack_list[k].width = attr.width;
3296 stack_list[k].height = attr.height;
3297 stack_list[k].border_width = attr.border_width;
3298 stack_list[k].depth = attr.depth;
3299 stack_list[k].class = attr.class;
3300 stack_list[k].backing_store =
3301 attr.backing_store;
3302 stack_list[k].map_state =
3303 attr.map_state;
3304 }
3305 stack_list[k].fetched = 1;
3306 stack_list[k].time = tm;
3307 }
3308 if (!stack_list[k].valid) {
3309 continue;
3310 }
3311
3312 attr.x = stack_list[k].x;
3313 attr.y = stack_list[k].y;
3314 attr.depth = stack_list[k].depth;
3315 attr.width = stack_list[k].width;
3316 attr.height = stack_list[k].height;
3317 attr.border_width = stack_list[k].border_width;
3318 attr.map_state = stack_list[k].map_state;
3319
3320 if (attr.map_state != IsViewable) {
3321 continue;
3322 }
3323if (db2) fprintf(stderr, "sw: %d/%lx %dx%d+%d+%d\n", k, swin, stack_list[k].width, stack_list[k].height, stack_list[k].x, stack_list[k].y);
3324
3325 if (clipshift) {
3326 attr.x -= coff_x;
3327 attr.y -= coff_y;
3328 }
3329 if (subwin) {
3330 attr.x -= off_x;
3331 attr.y -= off_y;
3332 }
3333
3334 /*
3335 * first subtract any overlap from the initial
3336 * window rectangle
3337 */
3338
3339 /* clip the window to the visible screen: */
3340 tx1 = nfix(attr.x, dpy_x);
3341 ty1 = nfix(attr.y, dpy_y);
3342 tx2 = nfix(attr.x + attr.width, dpy_x+1);
3343 ty2 = nfix(attr.y + attr.height, dpy_y+1);
3344
3345if (db2) fprintf(stderr, " tmp_win-1: %4d %3d, %4d %3d 0x%lx\n",
3346 tx1, ty1, tx2, ty2, swin);
3347if (db2 && saw_me) continue;
3348
3349 /* see if window clips us: */
3350 tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2);
3351 if (sraRgnAnd(tmp_win, moved_win)) {
3352 *obscured = 1;
3353if (db2) fprintf(stderr, " : clips it.\n");
3354 }
3355 sraRgnDestroy(tmp_win);
3356
3357 /* subtract it from our region: */
3358 tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2);
3359 sraRgnSubtract(moved_win, tmp_win);
3360 sraRgnDestroy(tmp_win);
3361
3362 /*
3363 * next, subtract from the initial window rectangle
3364 * anything that would clip it.
3365 */
3366
3367 /* clip the window to the visible screen: */
3368 tx1 = nfix(attr.x - dx, dpy_x);
3369 ty1 = nfix(attr.y - dy, dpy_y);
3370 tx2 = nfix(attr.x - dx + attr.width, dpy_x+1);
3371 ty2 = nfix(attr.y - dy + attr.height, dpy_y+1);
3372
3373if (db2) fprintf(stderr, " tmp_win-2: %4d %3d, %4d %3d 0x%lx\n",
3374 tx1, ty1, tx2, ty2, swin);
3375if (db2 && saw_me) continue;
3376
3377 /* subtract it from our region: */
3378 tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2);
3379 sraRgnSubtract(moved_win, tmp_win);
3380 sraRgnDestroy(tmp_win);
3381 }
3382
3383 X_UNLOCK;
3384
3385 if (extra_clip && ! sraRgnEmpty(extra_clip)) {
3386 whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
3387
3388 if (clipshift) {
3389 sraRgnOffset(extra_clip, -coff_x, -coff_y);
3390 }
3391 if (subwin) {
3392 sraRgnOffset(extra_clip, -off_x, -off_y);
3393 }
3394
3395 iter = sraRgnGetIterator(extra_clip);
3396 while (sraRgnIteratorNext(iter, &rect)) {
3397 /* clip the window to the visible screen: */
3398 tx1 = rect.x1;
3399 ty1 = rect.y1;
3400 tx2 = rect.x2;
3401 ty2 = rect.y2;
3402 tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2);
3403 sraRgnAnd(tmp_win, whole);
3404
3405 /* see if window clips us: */
3406 if (sraRgnAnd(tmp_win, moved_win)) {
3407 *obscured = 1;
3408 }
3409 sraRgnDestroy(tmp_win);
3410
3411 /* subtract it from our region: */
3412 tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2);
3413 sraRgnSubtract(moved_win, tmp_win);
3414 sraRgnDestroy(tmp_win);
3415
3416 /*
3417 * next, subtract from the initial window rectangle
3418 * anything that would clip it.
3419 */
3420 tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2);
3421 sraRgnOffset(tmp_win, -dx, -dy);
3422
3423 /* clip the window to the visible screen: */
3424 sraRgnAnd(tmp_win, whole);
3425
3426 /* subtract it from our region: */
3427 sraRgnSubtract(moved_win, tmp_win);
3428 sraRgnDestroy(tmp_win);
3429 }
3430 sraRgnReleaseIterator(iter);
3431 sraRgnDestroy(whole);
3432 }
3433
3434 dt = dtime(&tm);
3435if (db2) fprintf(stderr, " stack_work dt: %.4f\n", dt);
3436
3437 if (*obscured && !strcmp(wireframe_copyrect, "top")) {
3438 ; /* cannot send CopyRegion */
3439 } else if (! sraRgnEmpty(moved_win)) {
3440 sraRegionPtr whole, shifted_region;
3441
3442 whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
3443 shifted_region = sraRgnCreateRgn(moved_win);
3444 sraRgnOffset(shifted_region, dx, dy);
3445 sraRgnAnd(shifted_region, whole);
3446
3447 sraRgnDestroy(whole);
3448
3449 /* now send the CopyRegion: */
3450 if (! sraRgnEmpty(shifted_region)) {
3451 dtime0(&tm);
3452 if (!nbatch) {
3453 do_copyregion(shifted_region, dx, dy, 0);
3454 } else {
3455 batch_dxs[*nbatch] = dx;
3456 batch_dys[*nbatch] = dy;
3457 batch_reg[*nbatch] = sraRgnCreateRgn(shifted_region);
3458 (*nbatch)++;
3459
3460 }
3461 dt = dtime(&tm);
3462if (0 || db2) fprintf(stderr, "do_copyregion: %d %d %d %d dx: %d dy: %d dt: %.4f\n",
3463 tx1, ty1, tx2, ty2, dx, dy, dt);
3464 sent_copyrect = 1;
3465 }
3466 sraRgnDestroy(shifted_region);
3467 }
3468 sraRgnDestroy(moved_win);
3469 }
3470 return sent_copyrect;
3471}
3472
3473int near_wm_edge(int x, int y, int w, int h, int px, int py) {
3474 /* heuristics: */
3475 int wf_t = wireframe_top;
3476 int wf_b = wireframe_bot;
3477 int wf_l = wireframe_left;
3478 int wf_r = wireframe_right;
3479
3480 int near_edge = 0;
3481
3482 if (wf_t || wf_b || wf_l || wf_r) {
3483 if (nabs(y - py) < wf_t) {
3484 near_edge = 1;
3485 }
3486 if (nabs(y + h - py) < wf_b) {
3487 near_edge = 1;
3488 }
3489 if (nabs(x - px) < wf_l) {
3490 near_edge = 1;
3491 }
3492 if (nabs(x + w - px) < wf_r) {
3493 near_edge = 1;
3494 }
3495 } else {
3496 /* all zero; always "near" edge: */
3497 near_edge = 1;
3498 }
3499 return near_edge;
3500}
3501
3502int near_scrollbar_edge(int x, int y, int w, int h, int px, int py) {
3503 /* heuristics: */
3504 int sb_t = scrollcopyrect_top;
3505 int sb_b = scrollcopyrect_bot;
3506 int sb_l = scrollcopyrect_left;
3507 int sb_r = scrollcopyrect_right;
3508
3509 int near_edge = 0;
3510
3511 if (sb_t || sb_b || sb_l || sb_r) {
3512 if (nabs(y - py) < sb_t) {
3513 near_edge = 1;
3514 }
3515 if (nabs(y + h - py) < sb_b) {
3516 near_edge = 1;
3517 }
3518 if (nabs(x - px) < sb_l) {
3519 near_edge = 1;
3520 }
3521 if (nabs(x + w - px) < sb_r) {
3522 near_edge = 1;
3523 }
3524 } else {
3525 /* all zero; always "near" edge: */
3526 near_edge = 1;
3527 }
3528 return near_edge;
3529}
3530
3531void check_fixscreen(void) {
3532 double now = dnow();
3533 int didfull = 0, db = 0;
3534
3535 if (!client_count) {
3536 return;
3537 }
3538 if (unixpw_in_progress) return;
3539
3540 if (screen_fixup_X > 0.0) {
3541 static double last = 0.0;
3542 if (now > last + screen_fixup_X) {
3543 if (db) rfbLog("doing screen_fixup_X\n");
3544 do_copy_screen = 1;
3545 last = now;
3546 didfull = 1;
3547 }
3548
3549 }
3550 if (screen_fixup_V > 0.0) {
3551 static double last = 0.0;
3552 if (now > last + screen_fixup_V) {
3553 if (! didfull) {
3554 refresh_screen(0);
3555 if (db) rfbLog("doing screen_fixup_V\n");
3556 }
3557 last = now;
3558 didfull = 1;
3559 }
3560 }
3561 if (screen_fixup_C > 0.0) {
3562 static double last = 0.0;
3563 if (last_copyrect_fix < last_copyrect &&
3564 now > last_copyrect + screen_fixup_C) {
3565 if (! didfull) {
3566 refresh_screen(0);
3567 if (db) rfbLog("doing screen_fixup_C\n");
3568 }
3569 last_copyrect_fix = now;
3570 last = now;
3571 didfull = 1;
3572 }
3573 }
3574 if (scaling && last_copyrect_fix < last_copyrect) {
3575 static double last = 0.0;
3576 double delay = 3.0;
3577 if (now > last + delay) {
3578 if (! didfull) {
3579 scale_and_mark_rect(0, 0, dpy_x, dpy_y, 1);
3580 if (db) rfbLog("doing scale screen_fixup\n");
3581 }
3582 last_copyrect_fix = now;
3583 last = now;
3584 didfull = 1;
3585 }
3586 }
3587 if (advertise_truecolor && advertise_truecolor_reset && indexed_color) {
3588 /* this will reset framebuffer to correct colors, if needed */
3589 static double dlast = 0.0;
3590 now = dnow();
3591 if (now > last_client + 1.0 && now < last_client + 3.0 && now > dlast + 5.0) {
3592 rfbLog("advertise truecolor reset framebuffer\n");
3593 do_new_fb(1);
3594 dlast = dnow();
3595 return;
3596 }
3597 }
3598}
3599
3600static int wireframe_mod_state() {
3601 if (! wireframe_mods) {
3602 return 0;
3603 }
3604 if (!strcmp(wireframe_mods, "all")) {
3605 if (track_mod_state(NoSymbol, FALSE, FALSE)) {
3606 return 1;
3607 } else {
3608 return 0;
3609 }
3610
3611 } else if (!strcmp(wireframe_mods, "Alt")) {
3612 if (track_mod_state(XK_Alt_L, FALSE, FALSE) == 1) {
3613 return 1;
3614 } else if (track_mod_state(XK_Alt_R, FALSE, FALSE) == 1) {
3615 return 1;
3616 }
3617 } else if (!strcmp(wireframe_mods, "Shift")) {
3618 if (track_mod_state(XK_Shift_L, FALSE, FALSE) == 1) {
3619 return 1;
3620 } else if (track_mod_state(XK_Shift_R, FALSE, FALSE) == 1) {
3621 return 1;
3622 }
3623 } else if (!strcmp(wireframe_mods, "Control")) {
3624 if (track_mod_state(XK_Control_L, FALSE, FALSE) == 1) {
3625 return 1;
3626 } else if (track_mod_state(XK_Control_R, FALSE, FALSE) == 1) {
3627 return 1;
3628 }
3629 } else if (!strcmp(wireframe_mods, "Meta")) {
3630 if (track_mod_state(XK_Meta_L, FALSE, FALSE) == 1) {
3631 return 1;
3632 } else if (track_mod_state(XK_Meta_R, FALSE, FALSE) == 1) {
3633 return 1;
3634 }
3635 } else if (!strcmp(wireframe_mods, "Super")) {
3636 if (track_mod_state(XK_Super_L, FALSE, FALSE) == 1) {
3637 return 1;
3638 } else if (track_mod_state(XK_Super_R, FALSE, FALSE) == 1) {
3639 return 1;
3640 }
3641 } else if (!strcmp(wireframe_mods, "Hyper")) {
3642 if (track_mod_state(XK_Hyper_L, FALSE, FALSE) == 1) {
3643 return 1;
3644 } else if (track_mod_state(XK_Hyper_R, FALSE, FALSE) == 1) {
3645 return 1;
3646 }
3647 }
3648 return 0;
3649}
3650
3651static int NPP_nreg = 0;
3652static sraRegionPtr NPP_roffscreen = NULL;
3653static sraRegionPtr NPP_r_bs_tmp = NULL;
3654static Window NPP_nwin = None;
3655
3656void clear_win_events(Window win, int vis) {
3657#if !NO_X11
3658 if (dpy && win != None && ncache) {
3659 XEvent ev;
3660 XErrorHandler old_handler;
3661 old_handler = XSetErrorHandler(trap_xerror);
3662 trapped_xerror = 0;
3663 while (XCheckTypedWindowEvent(dpy, win, ConfigureNotify, &ev)) {
3664 if (ncdb) fprintf(stderr, ".");
3665 if (trapped_xerror) {
3666 break;
3667 }
3668 trapped_xerror = 0;
3669 }
3670/* XXX Y */
3671 if (vis) {
3672 while (XCheckTypedWindowEvent(dpy, win, VisibilityNotify, &ev)) {
3673 if (ncdb) fprintf(stderr, "+");
3674 if (trapped_xerror) {
3675 break;
3676 }
3677 trapped_xerror = 0;
3678 }
3679 }
3680 XSetErrorHandler(old_handler);
3681 if (ncdb) fprintf(stderr, " 0x%lx\n", win);
3682 }
3683#endif
3684}
3685
3686void push_borders(sraRect *rects, int nrect) {
3687 int i, s = 2;
3688 sraRegionPtr r0, r1, r2;
3689
3690 r0 = sraRgnCreate();
3691 r1 = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
3692
3693 for (i=0; i<nrect; i++) {
3694 int x = rects[i].x1;
3695 int y = rects[i].y1;
3696 int w = rects[i].x2;
3697 int h = rects[i].y2;
3698
3699 if (w > 0 && h > 0 && w * h > 64 * 64) {
3700 r2 = sraRgnCreateRect(x - s, y , x , y + h);
3701 sraRgnOr(r0, r2);
3702 sraRgnDestroy(r2);
3703
3704 r2 = sraRgnCreateRect(x + w, y , x + w + s, y + h);
3705 sraRgnOr(r0, r2);
3706 sraRgnDestroy(r2);
3707
3708 r2 = sraRgnCreateRect(x - s, y - s, x + w + s, y + s);
3709 sraRgnOr(r0, r2);
3710 sraRgnDestroy(r2);
3711
3712 r2 = sraRgnCreateRect(x - s, y , x + w + s, y + h + s);
3713 sraRgnOr(r0, r2);
3714 sraRgnDestroy(r2);
3715 }
3716 }
3717
3718 sraRgnAnd(r0, r1);
3719
3720 if (!sraRgnEmpty(r0)) {
3721 double d = dnow();
3722 sraRectangleIterator *iter;
3723 sraRect rect;
3724 int db = 0;
3725
3726 if (db) fprintf(stderr, "SCALE_BORDER\n");
3727 fb_push_wait(0.05, FB_MOD|FB_COPY);
3728
3729 iter = sraRgnGetIterator(r0);
3730 while (sraRgnIteratorNext(iter, &rect)) {
3731 /* clip the window to the visible screen: */
3732 int tx1 = rect.x1;
3733 int ty1 = rect.y1;
3734 int tx2 = rect.x2;
3735 int ty2 = rect.y2;
3736 scale_and_mark_rect(tx1, ty1, tx2, ty2, 1);
3737 }
3738 sraRgnReleaseIterator(iter);
3739
3740 if (db) fprintf(stderr, "SCALE_BORDER %.4f\n", dnow() - d);
3741 fb_push_wait(0.1, FB_MOD|FB_COPY);
3742 if (db) fprintf(stderr, "SCALE_BORDER %.4f\n", dnow() - d);
3743 }
3744 sraRgnDestroy(r0);
3745 sraRgnDestroy(r1);
3746}
3747
3748void ncache_pre_portions(Window orig_frame, Window frame, int *nidx_in, int try_batch, int *use_batch,
3749 int orig_x, int orig_y, int orig_w, int orig_h, int x, int y, int w, int h, double ntim) {
3750 int nidx, np = ncache_pad;
3751
3752 if (!ntim) {}
3753 *use_batch = 0;
3754 *nidx_in = -1;
3755 NPP_nreg = 0;
3756 NPP_roffscreen = NULL;
3757 NPP_r_bs_tmp = NULL;
3758 NPP_nwin = None;
3759
3760 if (ncache <= 0) {
3761 return;
3762 }
3763
3764 if (rotating) {
3765 try_batch = 0;
3766 }
3767
3768 if (*nidx_in == -1) {
3769 nidx = lookup_win_index(orig_frame);
3770 NPP_nwin = orig_frame;
3771 if (nidx < 0) {
3772 nidx = lookup_win_index(frame);
3773 NPP_nwin = frame;
3774 }
3775 } else {
3776 nidx = *nidx_in;
3777 }
3778 if (nidx > 0) {
3779 sraRegionPtr r0, r1, r2;
3780 int dx, dy;
3781 int bs_x = cache_list[nidx].bs_x;
3782 int bs_y = cache_list[nidx].bs_y;
3783 int bs_w = cache_list[nidx].bs_w;
3784 int bs_h = cache_list[nidx].bs_h;
3785
3786 *nidx_in = nidx;
3787
3788 if (bs_x < 0) {
3789 if (!find_rect(nidx, x, y, w, h)) {
3790 nidx = -1;
3791 return;
3792 }
3793 bs_x = cache_list[nidx].bs_x;
3794 bs_y = cache_list[nidx].bs_y;
3795 bs_w = cache_list[nidx].bs_w;
3796 bs_h = cache_list[nidx].bs_h;
3797 }
3798 if (bs_x < 0) {
3799 nidx = -1;
3800 return;
3801 }
3802
3803 if (try_batch) {
3804 *use_batch = 1;
3805 }
3806
3807 if (ncache_pad) {
3808 orig_x -= np;
3809 orig_y -= np;
3810 orig_w += 2 * np;
3811 orig_h += 2 * np;
3812 x -= np;
3813 y -= np;
3814 w += 2 * np;
3815 h += 2 * np;
3816 }
3817
3818 if (clipshift) {
3819 orig_x -= coff_x;
3820 orig_y -= coff_y;
3821 x -= coff_x;
3822 y -= coff_y;
3823 }
3824
3825 r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
3826
3827 r2 = sraRgnCreateRect(orig_x, orig_y, orig_x + orig_w, orig_y + orig_h);
3828 sraRgnSubtract(r2, r0);
3829 if (! sraRgnEmpty(r2) && cache_list[nidx].bs_time > 0.0) {
3830 /* some is initially offscreen */
3831 dx = bs_x - orig_x;
3832 dy = bs_y - orig_y;
3833 sraRgnOffset(r2, dx, dy);
3834 dx = 0;
3835 dy = dpy_y;
3836 sraRgnOffset(r2, dx, dy);
3837if (ncdb) fprintf(stderr, "FB_COPY: %.4f 1) offscreen: dx, dy: %d, %d -> %d, %d orig %dx%d+%d+%d bs_xy: %d %d\n",
3838 dnow() - ntim, bs_x - orig_x, bs_y - orig_y, dx, dy, orig_w, orig_h, orig_x, orig_y, bs_x, bs_y);
3839
3840 /* 0) save it in the invalid (offscreen) SU portion */
3841 if (! *use_batch) {
3842 do_copyregion(r2, dx, dy, 0);
3843 if (! fb_push_wait(0.2, FB_COPY)) {
3844 fb_push_wait(0.1, FB_COPY);
3845 }
3846 } else {
3847 batch_dxs[NPP_nreg] = dx;
3848 batch_dys[NPP_nreg] = dy;
3849 batch_reg[NPP_nreg++] = sraRgnCreateRgn(r2);
3850 }
3851 NPP_roffscreen = sraRgnCreateRgn(r2);
3852 }
3853 sraRgnDestroy(r2);
3854
3855 /* 1) use bs for temp storage of the new save under. */
3856 r1 = sraRgnCreateRect(x, y, x + w, y + h);
3857 sraRgnAnd(r1, r0);
3858
3859 dx = bs_x - x;
3860 dy = bs_y - y;
3861 sraRgnOffset(r1, dx, dy);
3862
3863if (ncdb) fprintf(stderr, "FB_COPY: %.4f 1) use tmp bs:\n", dnow() - ntim);
3864 if (! *use_batch) {
3865 do_copyregion(r1, dx, dy, 0);
3866 if (! fb_push_wait(0.2, FB_COPY)) {
3867if (ncdb) fprintf(stderr, "FB_COPY: %.4f 1) FAILED.\n", dnow() - ntim);
3868 fb_push_wait(0.1, FB_COPY);
3869 }
3870 } else {
3871 batch_dxs[NPP_nreg] = dx;
3872 batch_dys[NPP_nreg] = dy;
3873 batch_reg[NPP_nreg++] = sraRgnCreateRgn(r1);
3874 }
3875 NPP_r_bs_tmp = sraRgnCreateRgn(r1);
3876 sraRgnDestroy(r0);
3877 sraRgnDestroy(r1);
3878 }
3879}
3880
3881void ncache_post_portions(int nidx, int use_batch, int orig_x, int orig_y, int orig_w, int orig_h,
3882 int x, int y, int w, int h, double batch_delay, double ntim) {
3883 int np = ncache_pad;
3884 int db = 0;
3885
3886 if (ncache > 0 && nidx >= 0) {
3887 sraRegionPtr r0, r1, r2, r3;
3888 int dx, dy;
3889 int su_x = cache_list[nidx].su_x;
3890 int su_y = cache_list[nidx].su_y;
3891 int su_w = cache_list[nidx].su_w;
3892 int su_h = cache_list[nidx].su_h;
3893 int bs_x = cache_list[nidx].bs_x;
3894 int bs_y = cache_list[nidx].bs_y;
3895 int bs_w = cache_list[nidx].bs_w;
3896 int bs_h = cache_list[nidx].bs_h;
3897 int some_su = 0;
3898
3899if (db) fprintf(stderr, "su: %dx%d+%d+%d bs: %dx%d+%d+%d\n", su_w, su_h, su_x, su_y, bs_w, bs_h, bs_x, bs_y);
3900
3901 if (bs_x < 0) {
3902 if (!find_rect(nidx, x, y, w, h)) {
3903 return;
3904 }
3905 su_x = cache_list[nidx].su_x;
3906 su_y = cache_list[nidx].su_y;
3907 su_w = cache_list[nidx].su_w;
3908 su_h = cache_list[nidx].su_h;
3909 bs_x = cache_list[nidx].bs_x;
3910 bs_y = cache_list[nidx].bs_y;
3911 bs_w = cache_list[nidx].bs_w;
3912 bs_h = cache_list[nidx].bs_h;
3913 }
3914 if (bs_x < 0) {
3915 return;
3916 }
3917
3918 if (ncache_pad) {
3919 orig_x -= np;
3920 orig_y -= np;
3921 orig_w += 2 * np;
3922 orig_h += 2 * np;
3923 x -= np;
3924 y -= np;
3925 w += 2 * np;
3926 h += 2 * np;
3927 }
3928
3929 if (clipshift) {
3930 orig_x -= coff_x;
3931 orig_y -= coff_y;
3932 x -= coff_x;
3933 y -= coff_y;
3934 }
3935
3936 r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
3937
3938 /* 0b) copy this bs part stored in saveunder */
3939 if (NPP_roffscreen != NULL) {
3940 dx = x - su_x;
3941 dy = y - su_y;
3942 sraRgnOffset(NPP_roffscreen, dx, dy);
3943 sraRgnAnd(NPP_roffscreen, r0);
3944
3945 if (! use_batch) {
3946 do_copyregion(NPP_roffscreen, dx, dy, 0);
3947 if (!fb_push_wait(0.2, FB_COPY)) {
3948 fb_push_wait(0.1, FB_COPY);
3949 }
3950 } else {
3951 batch_dxs[NPP_nreg] = dx;
3952 batch_dys[NPP_nreg] = dy;
3953 batch_reg[NPP_nreg++] = sraRgnCreateRgn(NPP_roffscreen);
3954 }
3955 sraRgnDestroy(NPP_roffscreen);
3956 }
3957
3958 /* 3) copy from the saveunder to where orig win was */
3959 r1 = sraRgnCreateRect(orig_x, orig_y, orig_x + orig_w, orig_y + orig_h);
3960 sraRgnAnd(r1, r0);
3961 r2 = sraRgnCreateRect(x+np, y+np, x + w-np, y + h-np);
3962 sraRgnAnd(r2, r0);
3963 sraRgnSubtract(r1, r2);
3964
3965 dx = orig_x - su_x;
3966 dy = orig_y - su_y;
3967if (db && ncdb) fprintf(stderr, "FB_COPY: %.4f 3) sent_copyrect: su_restore: %d %d\n", dnow() - ntim, dx, dy);
3968 if (cache_list[nidx].su_time == 0.0) {
3969 ;
3970 } else if (! use_batch) {
3971 do_copyregion(r1, dx, dy, 0);
3972 if (!fb_push_wait(0.2, FB_COPY)) {
3973if (db && ncdb) fprintf(stderr, "FB_COPY: %.4f 3) FAILED.\n", dnow() - ntim);
3974 fb_push_wait(0.1, FB_COPY);
3975 }
3976 } else {
3977 batch_dxs[NPP_nreg] = dx;
3978 batch_dys[NPP_nreg] = dy;
3979 batch_reg[NPP_nreg++] = sraRgnCreateRgn(r1);
3980 }
3981if (db && ncdb) fprintf(stderr, "sent_copyrect: %.4f su_restore: done.\n", dnow() - ntim);
3982 sraRgnDestroy(r0);
3983 sraRgnDestroy(r1);
3984 sraRgnDestroy(r2);
3985
3986 /* 4) if overlap between orig and displaced, move the corner that will still be su: */
3987 r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
3988
3989 r1 = sraRgnCreateRect(orig_x, orig_y, orig_x + orig_w, orig_y + orig_h);
3990 sraRgnAnd(r1, r0);
3991 r2 = sraRgnCreateRect(x, y, x + w, y + h);
3992 sraRgnAnd(r2, r0);
3993 r3 = NULL;
3994 if (sraRgnAnd(r2, r1) && cache_list[nidx].su_time > 0.0) {
3995 int dx2 = su_x - orig_x;
3996 int dy2 = su_y - orig_y;
3997
3998 r3 = sraRgnCreateRgn(r2);
3999 sraRgnOffset(r2, dx2, dy2);
4000
4001 dx = su_x - x;
4002 dy = su_y - y;
4003 sraRgnOffset(r3, dx, dy);
4004
4005 dx = dx - dx2;
4006 dy = dy - dy2;
4007
4008if (db && ncdb) fprintf(stderr, "FB_COPY: %.4f 4) move overlap inside su:\n", dnow() - ntim);
4009 if (! use_batch) {
4010 do_copyregion(r3, dx, dy, 0);
4011 if (!fb_push_wait(0.2, FB_COPY)) {
4012if (db) fprintf(stderr, "FB_COPY: %.4f 4) FAILED.\n", dnow() - ntim);
4013 fb_push_wait(0.1, FB_COPY);
4014 }
4015 } else {
4016 batch_dxs[NPP_nreg] = dx;
4017 batch_dys[NPP_nreg] = dy;
4018 batch_reg[NPP_nreg++] = sraRgnCreateRgn(r3);
4019 }
4020 }
4021 sraRgnDestroy(r0);
4022 sraRgnDestroy(r1);
4023 sraRgnDestroy(r2);
4024
4025 /* 5) copy our temporary stuff from bs to su: */
4026 dx = su_x - bs_x;
4027 dy = su_y - bs_y;
4028 if (NPP_r_bs_tmp == NULL) {
4029 r1 = sraRgnCreateRect(su_x, su_y, su_x + su_w, su_y + su_h);
4030 } else {
4031 r1 = sraRgnCreateRgn(NPP_r_bs_tmp);
4032 sraRgnOffset(r1, dx, dy);
4033 sraRgnDestroy(NPP_r_bs_tmp);
4034 }
4035 if (r3 != NULL) {
4036 sraRgnSubtract(r1, r3);
4037 sraRgnDestroy(r3);
4038 }
4039if (db) fprintf(stderr, "FB_COPY: %.4f 5) move tmp bs to su:\n", dnow() - ntim);
4040 if (! use_batch) {
4041 do_copyregion(r1, dx, dy, 0);
4042 if (!fb_push_wait(0.2, FB_COPY)) {
4043if (db) fprintf(stderr, "FB_COPY: %.4f 5) FAILED.\n", dnow() - ntim);
4044 fb_push_wait(0.1, FB_COPY);
4045 }
4046 } else {
4047 batch_dxs[NPP_nreg] = dx;
4048 batch_dys[NPP_nreg] = dy;
4049 batch_reg[NPP_nreg++] = sraRgnCreateRgn(r1);
4050 }
4051 if (! sraRgnEmpty(r1)) {
4052 some_su = 1;
4053 }
4054 sraRgnDestroy(r1);
4055
4056 /* 6) not really necessary, update bs with current view: */
4057 r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
4058 r1 = sraRgnCreateRect(x, y, x + w, y + h);
4059 sraRgnAnd(r1, r0);
4060 dx = bs_x - x;
4061 dy = bs_y - y;
4062 sraRgnOffset(r1, dx, dy);
4063if (db) fprintf(stderr, "FB_COPY: %.4f 6) snapshot bs:\n", dnow() - ntim);
4064 if (! use_batch) {
4065 do_copyregion(r1, dx, dy, 0);
4066 if (!fb_push_wait(0.2, FB_COPY)) {
4067if (db) fprintf(stderr, "FB_COPY: %.4f 6) FAILED.\n", dnow() - ntim);
4068 fb_push_wait(0.1, FB_COPY);
4069 }
4070 } else {
4071 batch_dxs[NPP_nreg] = dx;
4072 batch_dys[NPP_nreg] = dy;
4073 batch_reg[NPP_nreg++] = sraRgnCreateRgn(r1);
4074 }
4075 sraRgnDestroy(r0);
4076 sraRgnDestroy(r1);
4077
4078 if (use_batch) {
4079 batch_push(NPP_nreg, batch_delay);
4080if (ncdb) fprintf(stderr, "FB_COPY: %.4f XX did batch 0x%x %3d su: %dx%d+%d+%d bs: %dx%d+%d+%d\n", dnow() - ntim,
4081 (unsigned int) cache_list[nidx].win, nidx, su_w, su_h, su_x, su_y, bs_w, bs_h, bs_x, bs_y);
4082 }
4083 cache_list[nidx].x = x + np;
4084 cache_list[nidx].y = y + np;
4085
4086 /* XXX Y */
4087 cache_list[nidx].bs_time = dnow();
4088 if (some_su) {
4089 cache_list[nidx].su_time = dnow();
4090 }
4091 } else {
4092 if (use_batch) {
4093 batch_push(NPP_nreg, batch_delay);
4094 }
4095 }
4096
4097 if (scaling) {
4098 sraRect rects[2];
4099
4100 rects[0].x1 = orig_x;
4101 rects[0].y1 = orig_y;
4102 rects[0].x2 = orig_w;
4103 rects[0].y2 = orig_h;
4104
4105 rects[1].x1 = x;
4106 rects[1].y1 = y;
4107 rects[1].x2 = w;
4108 rects[1].y2 = h;
4109 push_borders(rects, 2);
4110 }
4111}
4112
4113void do_copyrect_drag_move(Window orig_frame, Window frame, int *nidx, int try_batch,
4114 int now_x, int now_y, int orig_w, int orig_h, int x, int y, int w, int h, double batch_delay) {
4115
4116 int sent_copyrect = 1, obscured = 0;
4117 int dx, dy;
4118 int use_batch = 0;
4119 double ntim = dnow();
4120 static int nob = -1;
4121 sraRegionPtr r0, r1;
4122
4123 if (nob < 0) {
4124 if (getenv("NOCRBATCH")) {
4125 nob = 1;
4126 } else {
4127 nob = 0;
4128 }
4129 }
4130 if (nob) {
4131 try_batch = 0;
4132 }
4133
4134 dx = x - now_x;
4135 dy = y - now_y;
4136 if (dx == 0 && dy == 0) {
4137 return;
4138 }
4139if (ncdb) fprintf(stderr, "do_COPY: now_xy: %d %d, orig_wh: %d %d, xy: %d %d, wh: %d %d\n",now_x, now_y, orig_w, orig_h, x, y, w, h);
4140
4141 ncache_pre_portions(orig_frame, frame, nidx, try_batch, &use_batch,
4142 now_x, now_y, orig_w, orig_h, x, y, w, h, ntim);
4143
4144 r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
4145 r1 = sraRgnCreateRect(x, y, x + w, y + h);
4146 sraRgnAnd(r1, r0);
4147
4148 dx = x - now_x;
4149 dy = y - now_y;
4150
4151 /* make sure the source is on-screen too */
4152 sraRgnOffset(r1, -dx, -dy);
4153 sraRgnAnd(r1, r0);
4154 sraRgnOffset(r1, +dx, +dy);
4155 sraRgnAnd(r1, r0); /* just to be sure, problably not needed */
4156
4157 if (! use_batch) {
4158 do_copyregion(r1, dx, dy, 0);
4159 if (!fb_push_wait(0.2, FB_COPY)) {
4160if (ncdb) fprintf(stderr, "FB_COPY: %.4f 3) *FAILED*\n", dnow() - ntim);
4161 fb_push_wait(0.1, FB_COPY);
4162 }
4163 } else {
4164 batch_dxs[NPP_nreg] = dx;
4165 batch_dys[NPP_nreg] = dy;
4166 batch_reg[NPP_nreg++] = sraRgnCreateRgn(r1);
4167 }
4168 sraRgnDestroy(r0);
4169 sraRgnDestroy(r1);
4170
4171 if (sent_copyrect) {
4172 if (use_batch) {
4173 ;
4174 } else if (! obscured) {
4175 fb_push_wait(0.1, FB_COPY);
4176 } else {
4177 /* no diff for now... */
4178 fb_push_wait(0.1, FB_COPY);
4179 }
4180 ncache_post_portions(*nidx, use_batch,
4181 now_x, now_y, orig_w, orig_h, x, y, w, h, batch_delay, ntim);
4182 }
4183if (ncdb) fprintf(stderr, "do_COPY: %.4f -- post_portion done.\n", dnow() - ntim);
4184}
4185
4186void check_macosx_iconify(Window orig_frame, Window frame, int flush) {
4187#ifdef MACOSX
4188 static double last = 0.0;
4189 double now;
4190 int j, m = 5, idx = -1, ok = 0, unmapped = 0;
4191
4192 if (! macosx_console) {
4193 return;
4194 }
4195
4196 now = dnow();
4197 if (now < last + 0.3) {
4198 return;
4199 }
4200 last = now;
4201
4202 if (ncache > 0 && orig_frame != None) {
4203 idx = lookup_win_index(orig_frame);
4204 if (idx >= 0) {
4205 if (cache_list[idx].map_state == IsUnmapped) {
4206if (0) fprintf(stderr, "FAW orig_frame unmapped.\n");
4207 unmapped = 1;
4208 m = 3;
4209 }
4210 }
4211 }
4212
4213 if (unmapped) {
4214 ;
4215 } else if (orig_frame && macosxCGS_follow_animation_win(orig_frame, -1, 0)) {
4216 if (0) fprintf(stderr, "FAW orig_frame %d\n", (int) orig_frame);
4217 } else if (0 && frame && macosxCGS_follow_animation_win(frame, -1, 0)) {
4218 if (0) fprintf(stderr, "FAW frame %d\n", (int) frame);
4219 }
4220 for (j=0; j<m; j++) {
4221 macosxCGS_get_all_windows();
4222 if (macosx_checkevent(NULL)) {
4223 ok = 1;
4224 if (0) fprintf(stderr, "Check Event 1\n");
4225 } else {
4226 if (0) fprintf(stderr, "Check Event 0\n");
4227 }
4228 if (ok) {
4229 break;
4230 }
4231 usleep(10 * 1000);
4232 }
4233 if (ok) {
4234 if (flush) {
4235 fb_push_wait(0.1, FB_COPY|FB_MOD);
4236 }
4237 check_ncache(0, 2);
4238 }
4239#else
4240 if (!orig_frame || !frame || !flush) {}
4241#endif
4242}
4243
4244void check_macosx_click_frame(void) {
4245#ifdef MACOSX
4246 if (macosx_console) {
4247if (0) fprintf(stderr, "macosx_click_frame: 0x%x\n", macosx_click_frame);
4248 check_macosx_iconify(macosx_click_frame, None, 0);
4249 macosx_click_frame = None;
4250 if (button_mask && !macosx_checkevent(NULL)) {
4251 check_macosx_iconify(None, None, 0);
4252 }
4253 }
4254#endif
4255}
4256
4257int clipped(int idx);
4258void snap_old(void);
4259
4260int check_copyrect_raise(int idx, Window orig_frame, int try_batch) {
4261 char *no = "none";
4262 int doraise = 1;
4263 int valid;
4264 XWindowAttributes attr;
4265
4266 if (! ncache_wf_raises) {
4267 doraise = 0;
4268 no = "ncache_wf_raises";
4269 } else if (cache_list[idx].bs_time == 0.0) {
4270 doraise = 0;
4271 no = "bs_time";
4272 } else if (0 && cache_list[idx].vis_state == VisibilityUnobscured) {
4273 doraise = 0;
4274 no = "VisibilityUnobscured";
4275 } else if (!clipped(idx)) {
4276 doraise = 0;
4277 no = "!clipped()";
4278 }
4279 if (doraise) {
4280 int nr = 0, *nb = NULL;
4281if (ncdb) fprintf(stderr, "--YES, wf_raise\n");
4282 if (try_batch) {
4283 nb = &nr;
4284 }
4285 valid = 1;
4286 bs_restore(idx, nb, NULL, &attr, 0, 1, &valid, 1);
4287 try_to_fix_su(orig_frame, idx, 0x1, nb, NULL);
4288 if (nb && nr) {
4289 batch_push(nr, -1.0);
4290 }
4291 fb_push(); /* XXX Y */
4292 } else {
4293if (ncdb && no) fprintf(stderr, "--NO, wf_raise: %s\n", no);
4294 }
4295 if (ncache_wf_raises) {
4296 snapshot_stack_list(0, 0.0);
4297 snap_old();
4298 }
4299 return 1;
4300}
4301
4302int set_copyrect_drag(int idx, Window orig_frame, int try_batch) {
4303 if (idx < 0) {
4304 return 0;
4305 }
4306 if (cache_list[idx].su_time > 0.0) {
4307 check_copyrect_raise(idx, orig_frame, try_batch);
4308 return 1;
4309 }
4310 return 0;
4311}
4312
4313/*
4314 * Applied just before any check_user_input() modes. Look for a
4315 * ButtonPress; find window it happened in; find the wm frame window
4316 * for it; watch for that window moving or resizing. If it does, do the
4317 * wireframe animation. Do this until ButtonRelease or timeouts occur.
4318 * Remove wireframe.
4319 *
4320 * Under -nowirecopyrect, return control to base scheme
4321 * (check_user_input() ...) that will repaint the screen with the window
4322 * in the new postion or size. Under -wirecopyrect, apply rfbDoCopyRect
4323 * or rfbDoCopyRegion: this "pollutes" our framebuffer, but the normal
4324 * polling will quickly repair it. Under happy circumstances, this
4325 * reduces actual XShmGetImage work (i.e. if we correctly predicted how
4326 * the X fb has changed.
4327 *
4328 * -scale doesn't always work under -wirecopyrect, but the wireframe does.
4329 *
4330 * testing of this mode under -threads is incomplete.
4331 *
4332 * returns 1 if it did an animation, 0 if no move/resize triggers
4333 * went off.
4334 *
4335 * TBD: see if we can select StructureNotify ConfigureNotify events for
4336 * the toplevel windows to get better info on moves and resizes.
4337 */
4338int check_wireframe(void) {
4339 Window frame = None, orig_frame = None;
4340 XWindowAttributes attr;
4341 int dx, dy;
4342
4343 int orig_px, orig_py, orig_x, orig_y, orig_w, orig_h;
4344 int px, py, x, y, w, h;
4345 int box_x, box_y, box_w, box_h;
4346 int orig_cursor_x, orig_cursor_y, g, gd;
4347 int already_down = 0, win_gone = 0, win_unmapped = 0;
4348 double spin = 0.0, tm, last_ptr = 0.0, last_draw;
4349
4350 int frame_changed = 0, drew_box = 0, got_2nd_pointer = 0;
4351 int try_copyrect_drag = 1, do_copyrect_drag = -1;
4352 int now_x = 0, now_y = 0, nidx = -1;
4353 double copyrect_drag_delay = -1.0;
4354 int try_batch = 1; /* XXX Y */
4355 int mac_skip = 0;
4356
4357 int special_t1 = 0, break_reason = 0, last_draw_cnt = 0, gpi = 0;
4358 static double first_dt_ave = 0.0;
4359 static int first_dt_cnt = 0;
4360 static time_t last_save_stacklist = 0;
4361 int bdown0, bdown, gotui, cnt = 0;
4362
4363 /* heuristics: */
4364 double first_event_spin = wireframe_t1;
4365 double frame_changed_spin = wireframe_t2;
4366 double max_spin = wireframe_t3;
4367 double min_draw = wireframe_t4;
4368 int try_it = 0;
4369 DB_SET
4370
4371 if (unixpw_in_progress) return 0;
4372 if (copyrect_drag_delay) {}
4373
4374#ifdef MACOSX
4375 if (macosx_console) {
4376 ;
4377 } else {
4378 RAWFB_RET(0)
4379 }
4380#else
4381 RAWFB_RET(0)
4382#endif
4383
4384 if (nofb) {
4385 return 0;
4386 }
4387 if (subwin) {
4388 return 0; /* don't even bother for -id case */
4389 }
4390
4391if (db > 1 && button_mask) fprintf(stderr, "check_wireframe: bm: %d gpi: %d\n", button_mask, got_pointer_input);
4392
4393 bdown0 = 0;
4394 if (button_mask) {
4395 bdown0 = 1;
4396 } else if (wireframe_local && display_button_mask) {
4397 bdown0 = 2;
4398 }
4399 if (! bdown0) {
4400 return 0; /* no button pressed down */
4401 }
4402
4403 gotui = 0;
4404 if (got_pointer_input) {
4405 gotui = 1;
4406 } else if (wireframe_local && display_button_mask) {
4407 gotui = 2;
4408 }
4409 if (!use_threads && !gotui) {
4410 return 0; /* need ptr input, e.g. button down, motion */
4411 }
4412
4413if (db > 1) fprintf(stderr, "check_wireframe: %d\n", db);
4414
4415if (db) fprintf(stderr, "\n*** button down!! x: %d y: %d\n", cursor_x, cursor_y);
4416
4417 /*
4418 * Query where the pointer is and which child of the root
4419 * window. We will assume this is the frame the window manager
4420 * makes when it reparents the toplevel window.
4421 */
4422 X_LOCK;
4423 if (! get_wm_frame_pos(&px, &py, &x, &y, &w, &h, &frame, NULL)) {
4424if (db) fprintf(stderr, "NO get_wm_frame_pos-1: 0x%lx\n", frame);
4425 X_UNLOCK;
4426#ifdef MACOSX
4427 check_macosx_click_frame();
4428#endif
4429 return 0;
4430 }
4431 X_UNLOCK;
4432
4433 last_get_wm_frame_time = dnow();
4434 last_get_wm_frame = frame;
4435
4436if (db) fprintf(stderr, "a: %d wf: %.3f A: %d origfrm: 0x%lx\n", w*h, wireframe_frac, (dpy_x*dpy_y), frame);
4437
4438 /*
4439 * apply the percentage size criterion (allow opaque moves for
4440 * small windows)
4441 */
4442 if ((double) w*h < wireframe_frac * (dpy_x * dpy_y)) {
4443if (db) fprintf(stderr, "small window %.3f\n", ((double) w*h)/(dpy_x * dpy_y));
4444 return 0;
4445 }
4446if (db) fprintf(stderr, " frame: x: %d y: %d w: %d h: %d px: %d py: %d fr: 0x%lx\n", x, y, w, h, px, py, frame);
4447
4448 /*
4449 * see if the pointer is within range of the assumed wm frame
4450 * decorations on the edge of the window.
4451 */
4452
4453 try_it = near_wm_edge(x, y, w, h, px, py);
4454
4455 /* Often Alt+ButtonDown starts a window move: */
4456 if (! try_it && wireframe_mod_state()) {
4457 try_it = 1;
4458 }
4459 if (try_it && clipshift) {
4460 sraRegionPtr r1, r2;
4461 int xc = off_x + coff_x;
4462 int yc = off_y + coff_y;
4463 r1 = sraRgnCreateRect(x, y, x+w, y+h);
4464 r2 = sraRgnCreateRect(xc, yc, xc+dpy_x, yc+dpy_y);
4465 if (!sraRgnAnd(r1, r2)) {
4466if (db) fprintf(stderr, "OUTSIDE CLIPSHIFT\n");
4467 try_it = 0;
4468 }
4469 sraRgnDestroy(r1);
4470 sraRgnDestroy(r2);
4471 }
4472 if (! try_it) {
4473if (db) fprintf(stderr, "INTERIOR\n");
4474#ifdef MACOSX
4475 check_macosx_click_frame();
4476#endif
4477 return 0;
4478 }
4479
4480 wireframe_in_progress = 1;
4481
4482 if (button_mask_prev) {
4483 already_down = 1;
4484 }
4485
4486 if (! wireframe_str || !strcmp(wireframe_str, WIREFRAME_PARMS)) {
4487 int link, latency, netrate;
4488 static int didmsg = 0;
4489
4490 link = link_rate(&latency, &netrate);
4491 if (link == LR_DIALUP || link == LR_BROADBAND) {
4492 /* slow link, e.g. dialup, increase timeouts: */
4493 first_event_spin *= 2.0;
4494 frame_changed_spin *= 2.0;
4495 max_spin *= 2.0;
4496 min_draw *= 1.5;
4497 if (link == LR_DIALUP) {
4498 max_spin *= 1.2;
4499 min_draw *= 1.7;
4500 }
4501 if (! didmsg) {
4502 rfbLog("increased wireframe timeouts for "
4503 "slow network connection.\n");
4504 rfbLog("netrate: %d KB/sec, latency: %d ms\n",
4505 netrate, latency);
4506 didmsg = 1;
4507 }
4508 }
4509 }
4510
4511 /*
4512 * pointer() should have snapped the stacking list for us, if
4513 * not, do it now (if the XFakeButtonEvent has been flushed by
4514 * now the stacking order may be incorrect).
4515 */
4516 if (strcmp(wireframe_copyrect, "never")) {
4517 if (already_down) {
4518 double age = 0.0;
4519 /*
4520 * see if we can reuse the stack list (pause
4521 * with button down)
4522 */
4523 if (stack_list_num) {
4524 int k, got_me = 0;
4525 for (k = stack_list_num -1; k >=0; k--) {
4526 if (frame == stack_list[k].win) {
4527 got_me = 1;
4528 break;
4529 }
4530 }
4531 if (got_me) {
4532 age = 1.0;
4533 }
4534 snapshot_stack_list(0, age);
4535 }
4536 }
4537 if (! stack_list_num) {
4538 snapshot_stack_list(0, 0.0);
4539 }
4540 }
4541
4542
4543 /* store initial parameters, we look for changes in them */
4544 orig_frame = frame;
4545 orig_px = px; /* pointer position */
4546 orig_py = py;
4547 orig_x = x; /* frame position */
4548 orig_y = y;
4549 orig_w = w; /* frame size */
4550 orig_h = h;
4551
4552 orig_cursor_x = cursor_x;
4553 orig_cursor_y = cursor_y;
4554
4555 /* this is the box frame we would draw */
4556 box_x = x;
4557 box_y = y;
4558 box_w = w;
4559 box_h = h;
4560
4561 dtime0(&tm);
4562
4563 last_draw = spin;
4564
4565 /* -threads support for check_wireframe() is rough... crash? */
4566 if (use_threads) {
4567 /* purge any stored up pointer events: */
4568 pointer_event(-1, 0, 0, NULL);
4569 }
4570
4571 if (cursor_noshape_updates_clients(screen)) {
4572 try_batch = 0;
4573 }
4574 if (rotating) {
4575 try_batch = 0;
4576 }
4577 if (use_threads && ncache > 0 && ncache_copyrect) {
4578 try_batch = 0;
4579 }
4580
4581 g = got_pointer_input;
4582 gd = got_local_pointer_input;
4583
4584 while (1) {
4585
4586 X_LOCK;
4587 XFlush_wr(dpy);
4588 X_UNLOCK;
4589
4590 /* try to induce/waitfor some more user input */
4591 if (use_threads) {
4592 usleep(1000);
4593 } else if (drew_box && do_copyrect_drag != 1) {
4594 rfbPE(1000);
4595 } else {
4596 rfbCFD(1000);
4597 }
4598 if (bdown0 == 2) {
4599 /*
4600 * This is to just update display_button_mask
4601 * which will also update got_local_pointer_input.
4602 */
4603 check_x11_pointer();
4604#if 0
4605 /* what was this for? */
4606 Window frame;
4607 int px, py, x, y, w, h;
4608#ifdef MACOSX
4609 if (macosx_console) {
4610 macosx_get_cursor_pos(&x, &y);
4611 }
4612 else
4613#endif
4614 get_wm_frame_pos(&px, &py, &x, &y, &w, &h, &frame, NULL);
4615#endif
4616 }
4617
4618 cnt++;
4619 spin += dtime(&tm);
4620
4621if (0) fprintf(stderr, "wf-spin: %.3f\n", spin);
4622
4623 /* check for any timeouts: */
4624 if (frame_changed) {
4625 double delay;
4626 /* max time we play this game: */
4627 if (spin > max_spin) {
4628if (db || db2) fprintf(stderr, " SPIN-OUT-MAX: %.3f\n", spin);
4629 break_reason = 1;
4630 break;
4631 }
4632 /* watch for pointer events slowing down: */
4633 if (special_t1) {
4634 delay = max_spin;
4635 } else {
4636 delay = 2.0* frame_changed_spin;
4637 if (spin > 3.0 * frame_changed_spin) {
4638 delay = 1.5 * delay;
4639 }
4640 }
4641 if (spin > last_ptr + delay) {
4642if (db || db2) fprintf(stderr, " SPIN-OUT-NOT-FAST: %.3f\n", spin);
4643 break_reason = 2;
4644 break;
4645 }
4646 } else if (got_2nd_pointer) {
4647 /*
4648 * pointer is moving, max time we wait for wm
4649 * move or resize to be detected
4650 */
4651 if (spin > frame_changed_spin) {
4652if (db || db2) fprintf(stderr, " SPIN-OUT-NOFRAME-SPIN: %.3f\n", spin);
4653 break_reason = 3;
4654 break;
4655 }
4656 } else {
4657 /* max time we wait for any pointer input */
4658 if (spin > first_event_spin) {
4659if (db || db2) fprintf(stderr, " SPIN-OUT-NO2ND_PTR: %.3f\n", spin);
4660 break_reason = 4;
4661 break;
4662 }
4663 }
4664
4665 gpi = 0;
4666 /* see if some pointer input occurred: */
4667 if (got_pointer_input > g ||
4668 (wireframe_local && (got_local_pointer_input > gd))) {
4669
4670if (db) fprintf(stderr, " ++pointer event!! [%02d] dt: %.3f x: %d y: %d mask: %d\n",
4671 got_2nd_pointer+1, spin, cursor_x, cursor_y, button_mask);
4672
4673 g = got_pointer_input;
4674 gd = got_local_pointer_input;
4675 gpi = 1;
4676
4677 X_LOCK;
4678 XFlush_wr(dpy);
4679 X_UNLOCK;
4680
4681 /* periodically try to let the wm get moving: */
4682 if (!frame_changed && got_2nd_pointer % 4 == 0) {
4683 if (got_2nd_pointer == 0) {
4684 usleep(50 * 1000);
4685 } else {
4686 usleep(25 * 1000);
4687 }
4688 }
4689 got_2nd_pointer++;
4690 last_ptr = spin;
4691
4692 /*
4693 * see where the pointer currently is. It may
4694 * not be our starting frame (i.e. mouse now
4695 * outside of the moving window).
4696 */
4697 frame = 0x0;
4698 X_LOCK;
4699
4700 if (! get_wm_frame_pos(&px, &py, &x, &y, &w, &h,
4701 &frame, NULL)) {
4702 frame = 0x0;
4703if (db) fprintf(stderr, "NO get_wm_frame_pos-2: 0x%lx\n", frame);
4704 }
4705
4706 if (frame != orig_frame) {
4707 /* see if our original frame is still there */
4708 if (!valid_window(orig_frame, &attr, 1)) {
4709 X_UNLOCK;
4710 /* our window frame went away! */
4711 win_gone = 1;
4712if (db) fprintf(stderr, "FRAME-GONE: 0x%lx\n", orig_frame);
4713 break_reason = 5;
4714 break;
4715 }
4716 if (attr.map_state == IsUnmapped) {
4717 X_UNLOCK;
4718 /* our window frame is now unmapped! */
4719 win_unmapped = 1;
4720if (db) fprintf(stderr, "FRAME-UNMAPPED: 0x%lx\n", orig_frame);
4721 break_reason = 5;
4722 break;
4723 }
4724
4725if (db) fprintf(stderr, "OUT-OF-FRAME: old: x: %d y: %d px: %d py: %d 0x%lx\n", x, y, px, py, frame);
4726
4727 /* new parameters for our frame */
4728 x = attr.x; /* n.b. rootwin is parent */
4729 y = attr.y;
4730 w = attr.width;
4731 h = attr.height;
4732 }
4733 X_UNLOCK;
4734
4735if (db) fprintf(stderr, " frame: x: %d y: %d w: %d h: %d px: %d py: %d fr: 0x%lx\n", x, y, w, h, px, py, frame);
4736if (db) fprintf(stderr, " MO,PT,FR: %d/%d %d/%d %d/%d\n", cursor_x - orig_cursor_x, cursor_y - orig_cursor_y, px - orig_px, py - orig_py, x - orig_x, y - orig_y);
4737
4738 if (frame_changed && frame != orig_frame) {
4739if (db) fprintf(stderr, "CHANGED and window switch: 0x%lx\n", frame);
4740 }
4741 if (frame_changed && px - orig_px != x - orig_x) {
4742if (db) fprintf(stderr, "MOVED and diff DX\n");
4743 }
4744 if (frame_changed && py - orig_py != y - orig_y) {
4745if (db) fprintf(stderr, "MOVED and diff DY\n");
4746 }
4747
4748 /* check and see if our frame has been resized: */
4749 if (!frame_changed && (w != orig_w || h != orig_h)) {
4750 int n;
4751 if (!already_down) {
4752 first_dt_ave += spin;
4753 first_dt_cnt++;
4754 }
4755 n = first_dt_cnt ? first_dt_cnt : 1;
4756 frame_changed = 2;
4757
4758if (db) fprintf(stderr, "WIN RESIZE 1st-dt: %.3f\n", first_dt_ave/n);
4759 }
4760
4761 /* check and see if our frame has been moved: */
4762 if (!frame_changed && (x != orig_x || y != orig_y)) {
4763 int n;
4764 if (!already_down) {
4765 first_dt_ave += spin;
4766 first_dt_cnt++;
4767 }
4768 n = first_dt_cnt ? first_dt_cnt : 1;
4769 frame_changed = 1;
4770if (db) fprintf(stderr, "FRAME MOVE 1st-dt: %.3f\n", first_dt_ave/n);
4771 }
4772 }
4773
4774 /*
4775 * see if it is time to draw any or a new wireframe box
4776 */
4777
4778 if (frame_changed) {
4779 int drawit = 0;
4780 if (x != box_x || y != box_y) {
4781 /* moved since last */
4782if (0) fprintf(stderr, "DRAW1 %d %d\n", x - box_x, y - box_y);
4783 drawit = 1;
4784 } else if (w != box_w || h != box_h) {
4785 /* resize since last */
4786 drawit = 1;
4787 }
4788 if (drawit) {
4789 int doit = 0;
4790 /*
4791 * check time (to avoid too much
4792 * animations on slow machines
4793 * or links).
4794 */
4795 if (gpi) {
4796 if (spin > last_draw + min_draw || ! drew_box) {
4797 doit = 1;
4798 }
4799 if (macosx_console && doit && !mac_skip) {
4800 if (x != box_x && y != box_y && w != box_w && h != box_h) {
4801 doit = 0;
4802 } else if (!button_mask) {
4803 doit = 0;
4804 }
4805 mac_skip++;
4806 }
4807 } else {
4808 if (drew_box && cnt > last_draw_cnt) {
4809 doit = 1;
4810if (0) fprintf(stderr, "*** NO GPI DRAW_BOX\n");
4811 }
4812 }
4813
4814 if (doit) {
4815 if (try_copyrect_drag && ncache > 0) {
4816 if (!ncache_copyrect) {
4817 do_copyrect_drag = 0;
4818 } else if (w != box_w || h != box_h) {
4819 do_copyrect_drag = 0;
4820 } else if (do_copyrect_drag < 0) {
4821 Window fr = orig_frame;
4822 int idx = lookup_win_index(fr);
4823 if (idx < 0) {
4824 fr = frame;
4825 idx = lookup_win_index(fr);
4826 }
4827 if (idx >= 0) {
4828 do_copyrect_drag = set_copyrect_drag(idx, fr, try_batch);
4829 if (do_copyrect_drag) {
4830 min_draw *= 0.66;
4831 }
4832 nidx = idx;
4833 } else {
4834 do_copyrect_drag = 0;
4835 }
4836 now_x = orig_x;
4837 now_y = orig_y;
4838 }
4839 if (do_copyrect_drag) {
4840 if (orig_w != w || orig_h != h) {
4841 do_copyrect_drag = 0;
4842 }
4843 }
4844 }
4845
4846 if (do_copyrect_drag <= 0) {
4847 if (ncache <= 0) {
4848 ;
4849 } else if (!drew_box && ncache_wf_raises) {
4850 Window fr = orig_frame;
4851 int idx = lookup_win_index(fr);
4852 if (idx < 0) {
4853 fr = frame;
4854 idx = lookup_win_index(fr);
4855 }
4856 if (idx >= 0) {
4857 check_copyrect_raise(idx, fr, try_batch);
4858 }
4859 }
4860 draw_box(x, y, w, h, 0);
4861 fb_push(); /* XXX Y */
4862 rfbPE(1000);
4863 } else {
4864#ifndef NO_NCACHE
4865 int tb = use_threads ? 0 : try_batch;
4866 do_copyrect_drag_move(orig_frame, frame, &nidx,
4867 tb, now_x, now_y, orig_w, orig_h, x, y, w, h,
4868 copyrect_drag_delay);
4869 now_x = x;
4870 now_y = y;
4871 if (copyrect_drag_delay == -1.0) {
4872 copyrect_drag_delay = 0.04;
4873 }
4874#endif
4875 }
4876 drew_box = 1;
4877 last_wireframe = dnow();
4878
4879 last_draw = spin;
4880 last_draw_cnt = cnt;
4881 }
4882 }
4883 box_x = x;
4884 box_y = y;
4885 box_w = w;
4886 box_h = h;
4887 }
4888
4889 /*
4890 * Now (not earlier) check if the button has come back up.
4891 * we check here to get a better location and size of
4892 * the final window.
4893 */
4894 bdown = 0;
4895 if (button_mask) {
4896 bdown = 1;
4897 } else if (wireframe_local && display_button_mask) {
4898 bdown = 2;
4899 }
4900 if (! bdown) {
4901if (db || db2) fprintf(stderr, "NO button_mask\n");
4902 break_reason = 6;
4903 break;
4904 }
4905 }
4906
4907 if (! drew_box) {
4908 /* nice try, but no move or resize detected. cleanup. */
4909 if (stack_list_num) {
4910 stack_list_num = 0;
4911 }
4912 wireframe_in_progress = 0;
4913 if (macosx_console && (break_reason == 6 || break_reason == 5)) {
4914 check_macosx_iconify(orig_frame, frame, drew_box);
4915 }
4916 return 0;
4917 }
4918
4919 /* remove the wireframe */
4920 if (do_copyrect_drag <= 0) {
4921 draw_box(0, 0, 0, 0, 1);
4922 fb_push(); /* XXX Y */
4923 } else {
4924 int tb = use_threads ? 0 : try_batch;
4925 do_copyrect_drag_move(orig_frame, frame, &nidx,
4926 tb, now_x, now_y, orig_w, orig_h, x, y, w, h, -1.0);
4927 fb_push_wait(0.15, FB_COPY|FB_MOD);
4928 }
4929
4930 dx = x - orig_x;
4931 dy = y - orig_y;
4932
4933 /*
4934 * see if we can apply CopyRect or CopyRegion to the change:
4935 */
4936 if (!strcmp(wireframe_copyrect, "never")) {
4937 ;
4938 } else if (win_gone || win_unmapped) {
4939 ;
4940 } else if (skip_cr_when_scaling("wireframe")) {
4941 ;
4942 } else if (w != orig_w || h != orig_h) {
4943 if (ncache > 0) {
4944 try_to_fix_resize_su(orig_frame, orig_x, orig_y, orig_w, orig_h, x, y, w, h, try_batch);
4945 X_LOCK;
4946 clear_win_events(orig_frame, 1);
4947 if (frame != orig_frame) {
4948 clear_win_events(frame, 1);
4949 }
4950 X_UNLOCK;
4951 }
4952 } else if (dx == 0 && dy == 0) {
4953 ;
4954 } else if (do_copyrect_drag > 0) {
4955 X_LOCK;
4956 clear_win_events(NPP_nwin, 0);
4957 X_UNLOCK;
4958 } else {
4959 int spin_ms = (int) (spin * 1000 * 1000);
4960 int obscured, sent_copyrect = 0;
4961
4962 int nidx = -1;
4963 int use_batch = 0;
4964 double ntim;
4965
4966 /*
4967 * set a timescale comparable to the spin time,
4968 * but not too short or too long.
4969 */
4970 if (spin_ms < 30) {
4971 spin_ms = 30;
4972 } else if (spin_ms > 400) {
4973 spin_ms = 400;
4974 }
4975 ntim = dnow();
4976
4977 /* try to flush the wireframe removal: */
4978if (ncdb && ncache) fprintf(stderr, "\nSEND_COPYRECT %.4f %.4f\n", dnowx(), dnow() - ntim);
4979
4980 if (! fb_push_wait(0.15, FB_COPY|FB_MOD)) {
4981
4982if (ncdb && ncache) fprintf(stderr, "FB_COPY *FAILED*, try one more... %.4f", dnow() - ntim);
4983
4984 if (! fb_push_wait(0.15, FB_COPY|FB_MOD)) {
4985
4986if (ncdb && ncache) fprintf(stderr, "FB_COPY *FAILED* again! %.4f", dnow() - ntim);
4987
4988 }
4989 }
4990
4991 ncache_pre_portions(orig_frame, frame, &nidx, try_batch, &use_batch,
4992 orig_x, orig_y, orig_w, orig_h, x, y, w, h, ntim);
4993
4994 /* 2) try to send a clipped copyrect of translation: */
4995
4996 if (! try_batch) {
4997 sent_copyrect = try_copyrect(orig_frame, frame, x, y, w, h, dx, dy,
4998 &obscured, NULL, 0.15, NULL);
4999 } else {
5000 try_copyrect(orig_frame, frame, x, y, w, h, dx, dy,
5001 &obscured, NULL, 0.15, &NPP_nreg); /* XXX */
5002 sent_copyrect = 1;
5003 use_batch = 1;
5004 }
5005
5006if ((ncache || db) && ncdb) fprintf(stderr, "sent_copyrect: %d - obs: %d frame: 0x%lx\n", sent_copyrect, obscured, frame);
5007 if (sent_copyrect) {
5008 /* try to push the changes to viewers: */
5009 if (use_batch) {
5010 ;
5011 } else if (! obscured) {
5012 fb_push_wait(0.1, FB_COPY);
5013 } else {
5014 /* no diff for now... */
5015 fb_push_wait(0.1, FB_COPY);
5016 }
5017 ncache_post_portions(nidx, use_batch,
5018 orig_x, orig_y, orig_w, orig_h, x, y, w, h, -1.0, ntim);
5019 X_LOCK;
5020 clear_win_events(NPP_nwin, 0);
5021 X_UNLOCK;
5022
5023 if (scaling && !use_batch) {
5024 static double last_time = 0.0;
5025 double now = dnow(), delay = 0.35;
5026
5027 fb_push_wait(0.1, FB_COPY);
5028
5029 if (now > last_time + delay) {
5030 int xt = x, yt = y;
5031
5032 if (clipshift) {
5033 xt -= coff_x;
5034 yt -= coff_y;
5035 }
5036 if (subwin) {
5037 xt -= off_x;
5038 yt -= off_y;
5039 }
5040
5041 scale_mark(xt, yt, xt+w, yt+h, 1);
5042 last_time = now;
5043 last_copyrect_fix = now;
5044 }
5045 }
5046 }
5047 }
5048
5049 if (stack_list_num) {
5050 /* clean up stack_list for next time: */
5051 if (break_reason == 1 || break_reason == 2) {
5052 /*
5053 * save the stack list, perhaps the user has
5054 * paused with button down.
5055 */
5056 last_save_stacklist = time(NULL);
5057 } else {
5058 stack_list_num = 0;
5059 }
5060 }
5061
5062 /* final push (for -nowirecopyrect) */
5063 rfbPE(1000);
5064 wireframe_in_progress = 0;
5065
5066 if (1) {
5067 /* In principle no longer needed... see draw_box() */
5068 if (frame_changed && cmap8to24 /* && multivis_count */) {
5069 /* handle -8to24 kludge, mark area and check 8bpp... */
5070 int x1, x2, y1, y2, f = 16;
5071 x1 = nmin(box_x, orig_x) - f;
5072 y1 = nmin(box_y, orig_y) - f;
5073 x2 = nmax(box_x + box_w, orig_x + orig_w) + f;
5074 y2 = nmax(box_y + box_h, orig_y + orig_h) + f;
5075 x1 = nfix(x1, dpy_x);
5076 x2 = nfix(x2, dpy_x+1);
5077 y1 = nfix(y1, dpy_y);
5078 y2 = nfix(y2, dpy_y+1);
5079 if (0) {
5080 check_for_multivis();
5081 mark_rect_as_modified(x1, y1, x2, y2, 0);
5082 } else {
5083 if (1) {
5084 bpp8to24(x1, y1, x2, y2);
5085 } else {
5086 bpp8to24(0, 0, dpy_x, dpy_y);
5087 }
5088 }
5089 }
5090 }
5091
5092 urgent_update = 1;
5093 if (use_xdamage) {
5094 /* DAMAGE can queue ~1000 rectangles for a move */
5095 clear_xdamage_mark_region(NULL, 1);
5096 xdamage_scheduled_mark = dnow() + 2.0;
5097 }
5098
5099 if (macosx_console && (break_reason == 6 || break_reason == 5)) {
5100 check_macosx_iconify(orig_frame, frame, drew_box);
5101 }
5102
5103 return 1;
5104}
5105
5106/*
5107 * We need to handle user input, particularly pointer input, carefully.
5108 * This function is only called when non-threaded. Note that
5109 * rfbProcessEvents() only processes *one* pointer event per call,
5110 * so if we interlace it with scan_for_updates(), we can get swamped
5111 * with queued up pointer inputs. And if the pointer inputs are inducing
5112 * large changes on the screen (e.g. window drags), the whole thing
5113 * bogs down miserably and only comes back to life at some time after
5114 * one stops moving the mouse. So, to first approximation, we are trying
5115 * to eat as much user input here as we can using some hints from the
5116 * duration of the previous scan_for_updates() call (in dt).
5117 *
5118 * note: we do this even under -nofb
5119 *
5120 * return of 1 means watch_loop should short-circuit and reloop,
5121 * return of 0 means watch_loop should proceed to scan_for_updates().
5122 * (this is for pointer_mode == 1 mode, the others do it all internally,
5123 * cnt is also only for that mode).
5124 */
5125
5126static void check_user_input2(double dt) {
5127
5128 int eaten = 0, miss = 0, max_eat = 50, do_flush = 1;
5129 int g, g_in;
5130 double spin = 0.0, tm;
5131 double quick_spin_fac = 0.40;
5132 double grind_spin_time = 0.175;
5133
5134 dtime0(&tm);
5135 g = g_in = got_pointer_input;
5136 if (!got_pointer_input) {
5137 return;
5138 }
5139 /*
5140 * Try for some "quick" pointer input processing.
5141 *
5142 * About as fast as we can, we try to process user input calling
5143 * rfbProcessEvents or rfbCheckFds. We do this for a time on
5144 * order of the last scan_for_updates() time, dt, but if we stop
5145 * getting user input we break out. We will also break out if
5146 * we have processed max_eat inputs.
5147 *
5148 * Note that rfbCheckFds() does not send any framebuffer updates,
5149 * so is more what we want here, although it is likely they have
5150 * all be sent already.
5151 */
5152 while (1) {
5153 if (show_multiple_cursors) {
5154 rfbPE(1000);
5155 } else {
5156 rfbCFD(1000);
5157 }
5158 rfbCFD(0);
5159
5160 spin += dtime(&tm);
5161
5162 if (spin > quick_spin_fac * dt) {
5163 /* get out if spin time comparable to last scan time */
5164 break;
5165 }
5166 if (got_pointer_input > g) {
5167 int i, max_extra = max_eat / 2;
5168 g = got_pointer_input;
5169 eaten++;
5170 for (i=0; i<max_extra; i++) {
5171 rfbCFD(0);
5172 if (got_pointer_input > g) {
5173 g = got_pointer_input;
5174 eaten++;
5175 } else if (i > 1) {
5176 break;
5177 }
5178 }
5179 X_LOCK;
5180 do_flush = 0;
5181if (0) fprintf(stderr, "check_user_input2-A: XFlush %.4f\n", tm);
5182 XFlush_wr(dpy);
5183 X_UNLOCK;
5184 if (eaten < max_eat) {
5185 continue;
5186 }
5187 } else {
5188 miss++;
5189 }
5190 if (miss > 1) { /* 1 means out on 2nd miss */
5191 break;
5192 }
5193 }
5194 if (do_flush) {
5195 X_LOCK;
5196if (0) fprintf(stderr, "check_user_input2-B: XFlush %.4f\n", tm);
5197 XFlush_wr(dpy);
5198 X_UNLOCK;
5199 }
5200
5201
5202 /*
5203 * Probably grinding with a lot of fb I/O if dt is this large.
5204 * (need to do this more elegantly)
5205 *
5206 * Current idea is to spin our wheels here *not* processing any
5207 * fb I/O, but still processing the user input. This user input
5208 * goes to the X display and changes it, but we don't poll it
5209 * while we "rest" here for a time on order of dt, the previous
5210 * scan_for_updates() time. We also break out if we miss enough
5211 * user input.
5212 */
5213 if (dt > grind_spin_time) {
5214 int i, ms, split = 30;
5215 double shim;
5216
5217 /*
5218 * Break up our pause into 'split' steps. We get at
5219 * most one input per step.
5220 */
5221 shim = 0.75 * dt / split;
5222
5223 ms = (int) (1000 * shim);
5224
5225 /* cutoff how long the pause can be */
5226 if (split * ms > 300) {
5227 ms = 300 / split;
5228 }
5229
5230 spin = 0.0;
5231 dtime0(&tm);
5232
5233 g = got_pointer_input;
5234 miss = 0;
5235 for (i=0; i<split; i++) {
5236 usleep(ms * 1000);
5237 if (show_multiple_cursors) {
5238 rfbPE(1000);
5239 } else {
5240 rfbCFD(1000);
5241 }
5242 spin += dtime(&tm);
5243 if (got_pointer_input > g) {
5244 int i, max_extra = max_eat / 2;
5245 for (i=0; i<max_extra; i++) {
5246 rfbCFD(0);
5247 if (got_pointer_input > g) {
5248 g = got_pointer_input;
5249 } else if (i > 1) {
5250 break;
5251 }
5252 }
5253 X_LOCK;
5254if (0) fprintf(stderr, "check_user_input2-C: XFlush %.4f\n", tm);
5255 XFlush_wr(dpy);
5256 X_UNLOCK;
5257 miss = 0;
5258 } else {
5259 miss++;
5260 }
5261 g = got_pointer_input;
5262 if (miss > 2) {
5263 break;
5264 }
5265 if (1000 * spin > ms * split) {
5266 break;
5267 }
5268 }
5269 }
5270}
5271
5272static void check_user_input3(double dt, double dtr, int tile_diffs) {
5273
5274 int allowed_misses, miss_tweak, i, g, g_in;
5275 int last_was_miss, consecutive_misses;
5276 double spin, spin_max, tm, to, dtm;
5277 int rfb_wait_ms = 2;
5278 static double dt_cut = 0.075;
5279 int gcnt, ginput;
5280 static int first = 1;
5281
5282 if (dtr || tile_diffs) {} /* unused vars warning: */
5283
5284 if (first) {
5285 char *p = getenv("SPIN");
5286 if (p) {
5287 double junk;
5288 sscanf(p, "%lf,%lf", &dt_cut, &junk);
5289 }
5290 first = 0;
5291 }
5292
5293 if (!got_pointer_input) {
5294 return;
5295 }
5296
5297
5298 if (dt < dt_cut) {
5299 dt = dt_cut; /* this is to try to avoid early exit */
5300 }
5301 spin_max = 0.5;
5302
5303 spin = 0.0; /* amount of time spinning */
5304 allowed_misses = 10; /* number of ptr inputs we can miss */
5305 miss_tweak = 8;
5306 last_was_miss = 0;
5307 consecutive_misses = 1;
5308 gcnt = 0;
5309 ginput = 0;
5310
5311 dtime0(&tm);
5312 to = tm; /* last time we did rfbPE() */
5313
5314 g = g_in = got_pointer_input;
5315
5316 while (1) {
5317 int got_input = 0;
5318
5319 gcnt++;
5320
5321 if (button_mask) {
5322 drag_in_progress = 1;
5323 }
5324
5325 rfbCFD(rfb_wait_ms * 1000);
5326
5327 dtm = dtime(&tm);
5328 spin += dtm;
5329
5330 if (got_pointer_input == g) {
5331 if (last_was_miss) {
5332 consecutive_misses++;
5333 }
5334 last_was_miss = 1;
5335 } else {
5336 ginput++;
5337 if (ginput % miss_tweak == 0) {
5338 allowed_misses++;
5339 }
5340 consecutive_misses = 1;
5341 last_was_miss = 0;
5342 }
5343
5344 if (spin > spin_max) {
5345 /* get out if spin time over limit */
5346 break;
5347
5348 } else if (got_pointer_input > g) {
5349 /* received some input, flush to display. */
5350 got_input = 1;
5351 g = got_pointer_input;
5352 X_LOCK;
5353 XFlush_wr(dpy);
5354 X_UNLOCK;
5355 } else if (--allowed_misses <= 0) {
5356 /* too many misses */
5357 break;
5358 } else if (consecutive_misses >=3) {
5359 /* too many misses */
5360 break;
5361 } else {
5362 /* these are misses */
5363 int wms = 0;
5364 if (gcnt == 1 && button_mask) {
5365 /*
5366 * missed our first input, wait
5367 * for a defer time. (e.g. on
5368 * slow link) hopefully client
5369 * will batch them.
5370 */
5371 wms = 50;
5372 } else if (button_mask) {
5373 wms = 10;
5374 } else {
5375 }
5376 if (wms) {
5377 usleep(wms * 1000);
5378 }
5379 }
5380 }
5381
5382 if (ginput >= 2) {
5383 /* try for a couple more quick ones */
5384 for (i=0; i<2; i++) {
5385 rfbCFD(rfb_wait_ms * 1000);
5386 }
5387 }
5388
5389 drag_in_progress = 0;
5390}
5391
5392int fb_update_sent(int *count) {
5393 static int last_count = 0;
5394 int sent = 0, rc = 0;
5395 rfbClientIteratorPtr i;
5396 rfbClientPtr cl;
5397
5398 if (nofb) {
5399 return 0;
5400 }
5401
5402 i = rfbGetClientIterator(screen);
5403 while( (cl = rfbClientIteratorNext(i)) ) {
5404#if 0
5405 sent += cl->framebufferUpdateMessagesSent;
5406#else
5407#if LIBVNCSERVER_HAS_STATS
5408 sent += rfbStatGetMessageCountSent(cl, rfbFramebufferUpdate);
5409#endif
5410#endif
5411 }
5412 rfbReleaseClientIterator(i);
5413 if (sent != last_count) {
5414 rc = 1;
5415 }
5416 if (count != NULL) {
5417 *count = sent;
5418 }
5419 last_count = sent;
5420 return rc;
5421}
5422
5423static void check_user_input4(double dt, double dtr, int tile_diffs) {
5424
5425 int g, g_in, i, ginput, gcnt, tmp;
5426 int last_was_miss, consecutive_misses;
5427 int min_frame_size = 10; /* 10 tiles */
5428 double spin, tm, to, tc, dtm, rpe_last;
5429 int rfb_wait_ms = 2;
5430 static double dt_cut = 0.050;
5431 static int first = 1;
5432
5433 int Btile = tile_x * tile_y * bpp/8; /* Bytes per tile */
5434 double Ttile, dt_use;
5435 double screen_rate = 6000000.; /* 5 MB/sec */
5436 double vnccpu_rate = 80 * 100000.; /* 20 KB/sec @ 80X compression */
5437 double net_rate = 50000.;
5438 static double Tfac_r = 1.0, Tfac_v = 1.0, Tfac_n = 1.0, Tdelay = 0.001;
5439 static double dt_min = -1.0, dt_max = -1.0;
5440 double dt_min_fallback = 0.050;
5441 static int ssec = 0, total_calls = 0;
5442 static int push_frame = 0, update_count = 0;
5443
5444 if (first) {
5445 char *p = getenv("SPIN");
5446 if (p) {
5447 sscanf(p, "%lf,%lf,%lf,%lf", &dt_cut, &Tfac_r, &Tfac_v, &Tfac_n);
5448 }
5449 first = 0;
5450 ssec = time(NULL);
5451
5452 if (dtr) {} /* unused vars warning: */
5453 }
5454
5455 total_calls++;
5456
5457 if (dt_min < 0.0 || dt < dt_min) {
5458 if (dt > 0.0) {
5459 dt_min = dt;
5460 }
5461 }
5462 if (dt_min < 0.0) {
5463 /* sensible value for the very 1st call if dt = 0.0 */
5464 dt_min = dt_min_fallback;
5465 }
5466 if (dt_max < 0.0 || dt > dt_max) {
5467 dt_max = dt;
5468 }
5469
5470 if (total_calls > 30 && dt_min > 0.0) {
5471 static int first = 1;
5472 /*
5473 * dt_min will soon be the quickest time to do
5474 * one scan_for_updates with no tiles copied.
5475 * use this (instead of copy_tiles) to estimate
5476 * screen read rate.
5477 */
5478 screen_rate = (main_bytes_per_line * ntiles_y) / dt_min;
5479 if (first) {
5480 rfbLog("measured screen read rate: %.2f Bytes/sec\n",
5481 screen_rate);
5482 }
5483 first = 0;
5484 }
5485
5486 dtime0(&tm);
5487
5488 if (dt < dt_cut) {
5489 dt_use = dt_cut;
5490 } else {
5491 dt_use = dt;
5492 }
5493
5494 if (push_frame) {
5495 int cnt, iter = 0;
5496 double tp, push_spin = 0.0;
5497 dtime0(&tp);
5498 while (push_spin < dt_use * 0.5) {
5499 fb_update_sent(&cnt);
5500 if (cnt != update_count) {
5501 break;
5502 }
5503 /* damn, they didn't push our frame! */
5504 iter++;
5505 rfbPE(rfb_wait_ms * 1000);
5506
5507 push_spin += dtime(&tp);
5508 }
5509 if (iter) {
5510 X_LOCK;
5511 XFlush_wr(dpy);
5512 X_UNLOCK;
5513 }
5514 push_frame = 0;
5515 update_count = 0;
5516 }
5517
5518 /*
5519 * when we first enter we require some pointer input
5520 */
5521 if (!got_pointer_input) {
5522 return;
5523 }
5524
5525 vnccpu_rate = get_raw_rate();
5526
5527 if ((tmp = get_read_rate()) != 0) {
5528 screen_rate = (double) tmp;
5529 }
5530 if ((tmp = get_net_rate()) != 0) {
5531 net_rate = (double) tmp;
5532 }
5533 net_rate = (vnccpu_rate/get_cmp_rate()) * net_rate;
5534
5535 if ((tmp = get_net_latency()) != 0) {
5536 Tdelay = 0.5 * ((double) tmp)/1000.;
5537 }
5538
5539 Ttile = Btile * (Tfac_r/screen_rate + Tfac_v/vnccpu_rate + Tfac_n/net_rate);
5540
5541 spin = 0.0; /* amount of time spinning */
5542 last_was_miss = 0;
5543 consecutive_misses = 1;
5544 gcnt = 0;
5545 ginput = 0;
5546
5547 rpe_last = to = tc = tm; /* last time we did rfbPE() */
5548 g = g_in = got_pointer_input;
5549
5550 tile_diffs = 0; /* reset our knowlegde of tile_diffs to zero */
5551
5552 while (1) {
5553 int got_input = 0;
5554
5555 gcnt++;
5556
5557 if (button_mask) {
5558 /* this varible is used by our pointer handler */
5559 drag_in_progress = 1;
5560 }
5561
5562 /* turn libvncserver crank to process events: */
5563 rfbCFD(rfb_wait_ms * 1000);
5564
5565 dtm = dtime(&tm);
5566 spin += dtm;
5567
5568 if ( (gcnt == 1 && got_pointer_input > g) || tm-tc > 2*dt_min) {
5569 tile_diffs = scan_for_updates(1);
5570 tc = tm;
5571 }
5572
5573 if (got_pointer_input == g) {
5574 if (last_was_miss) {
5575 consecutive_misses++;
5576 }
5577 last_was_miss = 1;
5578 } else {
5579 ginput++;
5580 consecutive_misses = 1;
5581 last_was_miss = 0;
5582 }
5583
5584 if (tile_diffs > min_frame_size && spin > Ttile * tile_diffs + Tdelay) {
5585 /* we think we can push the frame */
5586 push_frame = 1;
5587 fb_update_sent(&update_count);
5588 break;
5589
5590 } else if (got_pointer_input > g) {
5591 /* received some input, flush it to display. */
5592 got_input = 1;
5593 g = got_pointer_input;
5594 X_LOCK;
5595 XFlush_wr(dpy);
5596 X_UNLOCK;
5597
5598 } else if (consecutive_misses >= 2) {
5599 /* too many misses in a row */
5600 break;
5601
5602 } else {
5603 /* these are pointer input misses */
5604 int wms;
5605 if (gcnt == 1 && button_mask) {
5606 /*
5607 * missed our first input, wait for
5608 * a defer time. (e.g. on slow link)
5609 * hopefully client will batch many
5610 * of them for the next read.
5611 */
5612 wms = 50;
5613
5614 } else if (button_mask) {
5615 wms = 10;
5616 } else {
5617 wms = 0;
5618 }
5619 if (wms) {
5620 usleep(wms * 1000);
5621 }
5622 }
5623 }
5624 if (ginput >= 2) {
5625 /* try for a couple more quick ones */
5626 for (i=0; i<2; i++) {
5627 rfbCFD(rfb_wait_ms * 1000);
5628 }
5629 }
5630 drag_in_progress = 0;
5631}
5632
5633int check_user_input(double dt, double dtr, int tile_diffs, int *cnt) {
5634
5635 if (rawfb_vnc_reflect) {
5636 if (got_user_input) {
5637 if (0) vnc_reflect_process_client();
5638 }
5639 if (got_user_input && *cnt % ui_skip != 0) {
5640 /* every n-th drops thru to scan */
5641 *cnt = *cnt + 1;
5642 return 1; /* short circuit watch_loop */
5643 }
5644 }
5645#ifdef MACOSX
5646 if (! macosx_console) {
5647 RAWFB_RET(0)
5648 }
5649#else
5650 RAWFB_RET(0)
5651#endif
5652
5653 if (use_xrecord) {
5654 int rc = check_xrecord();
5655 /*
5656 * 0: nothing found, proceed to other user input schemes.
5657 * 1: events found, want to do a screen update now.
5658 * 2: events found, want to loop back for some more.
5659 * 3: events found, want to loop back for some more,
5660 * and not have rfbPE() called.
5661 *
5662 * For 0, we precede below, otherwise return rc-1.
5663 */
5664if (debug_scroll && rc > 1) fprintf(stderr, " CXR: check_user_input ret %d\n", rc - 1);
5665 if (rc == 0) {
5666 ; /* proceed below. */
5667 } else {
5668 return rc - 1;
5669 }
5670 }
5671
5672 if (wireframe) {
5673 if (check_wireframe()) {
5674 return 0;
5675 }
5676 }
5677
5678 if (pointer_mode == 1) {
5679 if ((got_user_input || ui_skip < 0) && *cnt % ui_skip != 0) {
5680 /* every ui_skip-th drops thru to scan */
5681 *cnt = *cnt + 1;
5682 X_LOCK;
5683 XFlush_wr(dpy);
5684 X_UNLOCK;
5685 return 1; /* short circuit watch_loop */
5686 } else {
5687 return 0;
5688 }
5689 }
5690 if (pointer_mode >= 2 && pointer_mode <= 4) {
5691 if (got_keyboard_input) {
5692 /*
5693 * for these modes, short circuit watch_loop on
5694 * *keyboard* input.
5695 */
5696 if (*cnt % ui_skip != 0) {
5697 *cnt = *cnt + 1;
5698 return 1;
5699 }
5700 }
5701 /* otherwise continue below with pointer input method */
5702 }
5703
5704 if (pointer_mode == 2) {
5705 check_user_input2(dt);
5706 } else if (pointer_mode == 3) {
5707 check_user_input3(dt, dtr, tile_diffs);
5708 } else if (pointer_mode == 4) {
5709 check_user_input4(dt, dtr, tile_diffs);
5710 }
5711 return 0;
5712}
5713
5714#if defined(NO_NCACHE) || (NO_X11 && !defined(MACOSX))
5715int check_ncache(int a, int b) {
5716 if (!a || !b) {}
5717 ncache = 0;
5718 return 0;
5719}
5720int lookup_win_index(Window win) {
5721 if (!win) {}
5722 return -1;
5723}
5724int find_rect(int idx, int x, int y, int w, int h) {
5725 if (!idx || !x || !y || !w || !h) {}
5726 return 0;
5727}
5728void snap_old(void) {
5729 return;
5730}
5731int clipped(int idx) {
5732 if (!idx) {}
5733 return 0;
5734}
5735int bs_restore(int idx, int *nbatch, sraRegionPtr rmask, XWindowAttributes *attr, int clip, int nopad, int *valid, int verb) {
5736 if (!idx || !nbatch || !rmask || !attr || !clip || !nopad || !valid || !verb) {}
5737 return 0;
5738}
5739int try_to_fix_su(Window win, int idx, Window above, int *nbatch, char *mode) {
5740 if (!win || !idx || !above || !nbatch || !mode) {}
5741 return 0;
5742}
5743int try_to_fix_resize_su(Window orig_frame, int orig_x, int orig_y, int orig_w, int orig_h,
5744 int x, int y, int w, int h, int try_batch) {
5745 if (!orig_frame || !orig_x || !orig_y || !orig_w || !orig_h || !x || !y || !w || !h || !try_batch) {}
5746 return 0;
5747}
5748void set_ncache_xrootpmap(void) {
5749 return;
5750}
5751#else
5752/* maybe ncache.c it if works */
5753
5754winattr_t* cache_list = NULL;
5755int cache_list_num = 0;
5756int cache_list_len = 0;
5757
5758void snapshot_cache_list(int free_only, double allowed_age) {
5759 static double last_snap = 0.0, last_free = 0.0;
5760 double now;
5761 int num, rc, i;
5762 unsigned int ui;
5763 Window r, w;
5764 Window *list;
5765 int start = 512;
5766
5767 if (! cache_list) {
5768 cache_list = (winattr_t *) calloc(start*sizeof(winattr_t), 1);
5769 cache_list_num = 0;
5770 cache_list_len = start;
5771 }
5772
5773 dtime0(&now);
5774 if (free_only) {
5775 /* we really don't free it, just reset to zero windows */
5776 cache_list_num = 0;
5777 last_free = now;
5778 return;
5779 }
5780
5781 if (cache_list_num && now < last_snap + allowed_age) {
5782 return;
5783 }
5784
5785 cache_list_num = 0;
5786 last_free = now;
5787
5788#ifdef MACOSX
5789 if (! macosx_console) {
5790 RAWFB_RET_VOID
5791 }
5792#else
5793 RAWFB_RET_VOID
5794#endif
5795
5796
5797#if NO_X11 && !defined(MACOSX)
5798 num = rc = i = 0; /* compiler warnings */
5799 ui = 0;
5800 r = w = None;
5801 list = NULL;
5802 return;
5803#else
5804
5805 X_LOCK;
5806 /* no need to trap error since rootwin */
5807 rc = XQueryTree_wr(dpy, rootwin, &r, &w, &list, &ui);
5808 X_UNLOCK;
5809 num = (int) ui;
5810
5811 if (! rc) {
5812 cache_list_num = 0;
5813 last_free = now;
5814 last_snap = 0.0;
5815 return;
5816 }
5817
5818 last_snap = now;
5819 if (num > cache_list_len) {
5820 int n = 2*num;
5821 n = num + 3;
5822 free(cache_list);
5823 cache_list = (winattr_t *) calloc(n*sizeof(winattr_t), 1);
5824 cache_list_len = n;
5825 }
5826 for (i=0; i<num; i++) {
5827 cache_list[i].win = list[i];
5828 cache_list[i].fetched = 0;
5829 cache_list[i].valid = 0;
5830 cache_list[i].time = now;
5831 cache_list[i].selectinput = 0;
5832 cache_list[i].vis_cnt = 0;
5833 cache_list[i].map_cnt = 0;
5834 cache_list[i].unmap_cnt = 0;
5835 cache_list[i].create_cnt = 0;
5836 cache_list[i].vis_state = -1;
5837 cache_list[i].above = None;
5838 }
5839 if (num == 0) {
5840 cache_list[0].win = None;
5841 cache_list[0].fetched = 0;
5842 cache_list[0].valid = 0;
5843 cache_list[0].time = now;
5844 cache_list[0].selectinput = 0;
5845 cache_list[0].vis_cnt = 0;
5846 cache_list[0].map_cnt = 0;
5847 cache_list[0].unmap_cnt = 0;
5848 cache_list[0].create_cnt = 0;
5849 cache_list[0].vis_state = -1;
5850 cache_list[0].above = None;
5851 num++;
5852 }
5853
5854 cache_list_num = num;
5855
5856 if (num) {
5857 X_LOCK;
5858 XFree_wr(list);
5859 X_UNLOCK;
5860 }
5861#endif /* NO_X11 */
5862}
5863
5864void quick_snap(Window *wins, int *size) {
5865 int num, rc, i;
5866 unsigned int ui;
5867 Window r, w;
5868 Window *list;
5869
5870#ifdef MACOSX
5871 if (1 || ! macosx_console) {
5872 RAWFB_RET_VOID
5873 }
5874#else
5875 RAWFB_RET_VOID
5876#endif
5877
5878
5879#if NO_X11 && !defined(MACOSX)
5880 num = rc = i = 0; /* compiler warnings */
5881 ui = 0;
5882 r = w = None;
5883 list = NULL;
5884 return;
5885#else
5886
5887 X_LOCK;
5888 /* no need to trap error since rootwin */
5889 rc = XQueryTree_wr(dpy, rootwin, &r, &w, &list, &ui);
5890 X_UNLOCK;
5891 num = (int) ui;
5892
5893 if (! rc || num == 0) {
5894 *size = 0;
5895 return;
5896 } else {
5897 int m = *size;
5898 if (num < m) {
5899 m = num;
5900 }
5901 for (i=0; i < m; i++) {
5902 wins[i] = list[i];
5903 }
5904 if (num) {
5905 X_LOCK;
5906 XFree_wr(list);
5907 X_UNLOCK;
5908 }
5909 *size = m;
5910 }
5911#endif /* NO_X11 */
5912}
5913
5914int get_bs_n(int y) {
5915 int n;
5916 for (n = 1; n < ncache; n += 2) {
5917 if (n*dpy_y <= y && y < (n+1)*dpy_y) {
5918 return n;
5919 }
5920 }
5921 return -1;
5922}
5923
5924#define NRECENT 32
5925Window recent[NRECENT];
5926int recidx[NRECENT];
5927int rlast, rfree;
5928
5929int lookup_win_index(Window win) {
5930 int k, idx = -1;
5931 int foundfree = 0;
5932 static int s1 = 0, s2 = 0, s3 = 0;
5933
5934 if (win == rootwin || win == None) {
5935 return -1;
5936 }
5937 for (k = 0; k < NRECENT; k++) {
5938 if (recent[k] == win) {
5939 int k2 = recidx[k];
5940 if (cache_list[k2].win == win) {
5941 idx = k2;
5942if (0) fprintf(stderr, "recentA(shortcut): %d 0x%lx\n", idx, win);
5943 s1++;
5944 break;
5945 }
5946 }
5947 }
5948 if (idx < 0) {
5949 for(k=0; k<cache_list_num; k++) {
5950 if (!foundfree && cache_list[k].win == None) {
5951 rfree = k;
5952 foundfree = 1;
5953 }
5954 if (cache_list[k].win == win) {
5955 idx = k;
5956if (0) fprintf(stderr, "recentB(normal): %d 0x%lx\n", idx, win);
5957 s2++;
5958 break;
5959 }
5960 }
5961 if (idx >= 0) {
5962 recent[rlast] = win;
5963 recidx[rlast++] = idx;
5964 rlast = rlast % NRECENT;
5965 }
5966 }
5967 if (idx < 0) {
5968if (ncdb) fprintf(stderr, "recentC(fail): %d 0x%lx\n", idx, win);
5969 s3++;
5970 }
5971 if (s1 + s2 + s3 >= 1000) {
5972if (ncdb) fprintf(stderr, "lookup_win_index recent hit stats: %d/%d/%d\n", s1, s2, s3);
5973 s1 = s2 = s3 = 0;
5974 }
5975 return idx;
5976}
5977
5978int lookup_free_index(void) {
5979 int k;
5980
5981 if (rfree >= 0) {
5982 if (cache_list[rfree].win == None) {
5983if (ncdb) fprintf(stderr, "lookup_freeA: %d\n", rfree);
5984 return rfree;
5985 }
5986 }
5987 rfree = -1;
5988 for(k=0; k<cache_list_num; k++) {
5989 if (cache_list[k].win == None) {
5990 rfree = k;
5991 break;
5992 }
5993 }
5994 if (rfree < 0) {
5995 if (ncdb) fprintf(stderr, "*** LOOKUP_FREE_INDEX: incrementing cache_list_num %d/%d\n", cache_list_num, cache_list_len);
5996
5997 rfree = cache_list_num++;
5998 if (rfree >= cache_list_len) {
5999 int i, n = 2*cache_list_len;
6000 winattr_t *cache_new;
6001
6002 if (ncdb) fprintf(stderr, "lookup_free_index: growing cache_list_len: %d -> %d\n", cache_list_len, n);
6003
6004 cache_new = (winattr_t *) calloc(n*sizeof(winattr_t), 1);
6005 for (i=0; i<cache_list_num-1; i++) {
6006 cache_new[i] = cache_list[i];
6007 }
6008 cache_list_len = n;
6009 free(cache_list);
6010 cache_list = cache_new;
6011 }
6012 cache_list[rfree].win = None;
6013 cache_list[rfree].fetched = 0;
6014 cache_list[rfree].valid = 0;
6015 cache_list[rfree].time = 0.0;
6016 cache_list[rfree].selectinput = 0;
6017 cache_list[rfree].vis_cnt = 0;
6018 cache_list[rfree].map_cnt = 0;
6019 cache_list[rfree].unmap_cnt = 0;
6020 cache_list[rfree].create_cnt = 0;
6021 cache_list[rfree].vis_state = -1;
6022 cache_list[rfree].above = None;
6023 }
6024
6025if (ncdb) fprintf(stderr, "lookup_freeB: %d\n", rfree);
6026 return rfree;
6027}
6028
6029#define STACKMAX 4096
6030Window old_stack[STACKMAX];
6031Window new_stack[STACKMAX];
6032Window old_stack_map[STACKMAX];
6033Window new_stack_map[STACKMAX];
6034int old_stack_index[STACKMAX];
6035int old_stack_mapped[STACKMAX];
6036int old_stack_n = 0;
6037int new_stack_n = 0;
6038int old_stack_map_n = 0;
6039int new_stack_map_n = 0;
6040
6041void snap_old(void) {
6042 int i;
6043 old_stack_n = STACKMAX;
6044 quick_snap(old_stack, &old_stack_n);
6045if (0) fprintf(stderr, "snap_old: %d %.4f\n", old_stack_n, dnowx());
6046#if 0
6047 for (i= old_stack_n - 1; i >= 0; i--) {
6048 int idx = lookup_win_index(old_stack[i]);
6049 if (idx >= 0) {
6050 if (cache_list[idx].map_state == IsViewable) {
6051 if (ncdb) fprintf(stderr, " %03d 0x%x\n", i, old_stack[i]);
6052 }
6053 }
6054 }
6055#endif
6056 for (i=0; i < old_stack_n; i++) {
6057 old_stack_mapped[i] = -1;
6058 }
6059}
6060
6061void snap_old_index(void) {
6062 int i, idx;
6063 for (i=0; i < old_stack_n; i++) {
6064 idx = lookup_win_index(old_stack[i]);
6065 old_stack_index[i] = idx;
6066 if (idx >= 0) {
6067 if (cache_list[idx].map_state == IsViewable) {
6068 old_stack_mapped[i] = 1;
6069 } else {
6070 old_stack_mapped[i] = 0;
6071 }
6072 }
6073 }
6074}
6075
6076int lookup_old_stack_index(int ic) {
6077 int idx = old_stack_index[ic];
6078
6079 if (idx < 0) {
6080 return -1;
6081 }
6082 if (cache_list[idx].win != old_stack[ic]) {
6083 snap_old_index();
6084 }
6085 idx = old_stack_index[ic];
6086 if (idx < 0 || cache_list[idx].win != old_stack[ic]) {
6087 return -1;
6088 }
6089 if (cache_list[idx].map_state == IsViewable) {
6090 old_stack_mapped[ic] = 1;
6091 } else {
6092 old_stack_mapped[ic] = 0;
6093 }
6094 return idx;
6095}
6096
6097#define STORE(k, w, attr) \
6098 if (0) fprintf(stderr, "STORE(%d) = 0x%lx\n", k, w); \
6099 cache_list[k].win = w; \
6100 cache_list[k].fetched = 1; \
6101 cache_list[k].valid = 1; \
6102 cache_list[k].x = attr.x; \
6103 cache_list[k].y = attr.y; \
6104 cache_list[k].width = attr.width; \
6105 cache_list[k].height = attr.height; \
6106 cache_list[k].border_width = attr.border_width; \
6107 cache_list[k].map_state = attr.map_state; \
6108 cache_list[k].time = dnow();
6109
6110#if 0
6111 cache_list[k].width = attr.width + 2*attr.border_width; \
6112 cache_list[k].height = attr.height + 2*attr.border_width; \
6113
6114#endif
6115
6116#define CLEAR(k) \
6117 if (0) fprintf(stderr, "CLEAR(%d)\n", k); \
6118 cache_list[k].bs_x = -1; \
6119 cache_list[k].bs_y = -1; \
6120 cache_list[k].bs_w = -1; \
6121 cache_list[k].bs_h = -1; \
6122 cache_list[k].su_x = -1; \
6123 cache_list[k].su_y = -1; \
6124 cache_list[k].su_w = -1; \
6125 cache_list[k].su_h = -1; \
6126 cache_list[k].time = 0.0; \
6127 cache_list[k].bs_time = 0.0; \
6128 cache_list[k].su_time = 0.0; \
6129 cache_list[k].vis_obs_time = 0.0; \
6130 cache_list[k].vis_unobs_time = 0.0;
6131
6132#define DELETE(k) \
6133 if (0) fprintf(stderr, "DELETE(%d) = 0x%lx\n", k, cache_list[k].win); \
6134 cache_list[k].win = None; \
6135 cache_list[k].fetched = 0; \
6136 cache_list[k].valid = 0; \
6137 cache_list[k].selectinput = 0; \
6138 cache_list[k].vis_cnt = 0; \
6139 cache_list[k].map_cnt = 0; \
6140 cache_list[k].unmap_cnt = 0; \
6141 cache_list[k].create_cnt = 0; \
6142 cache_list[k].vis_state = -1; \
6143 cache_list[k].above = None; \
6144 free_rect(k); /* does CLEAR(k) */
6145
6146static char unk[32];
6147
6148char *Etype(int type) {
6149 if (type == KeyPress) return "KeyPress";
6150 if (type == KeyRelease) return "KeyRelease";
6151 if (type == ButtonPress) return "ButtonPress";
6152 if (type == ButtonRelease) return "ButtonRelease";
6153 if (type == MotionNotify) return "MotionNotify";
6154 if (type == EnterNotify) return "EnterNotify";
6155 if (type == LeaveNotify) return "LeaveNotify";
6156 if (type == FocusIn) return "FocusIn";
6157 if (type == FocusOut) return "FocusOut";
6158 if (type == KeymapNotify) return "KeymapNotify";
6159 if (type == Expose) return "Expose";
6160 if (type == GraphicsExpose) return "GraphicsExpose";
6161 if (type == NoExpose) return "NoExpose";
6162 if (type == VisibilityNotify) return "VisibilityNotify";
6163 if (type == CreateNotify) return "CreateNotify";
6164 if (type == DestroyNotify) return "DestroyNotify";
6165 if (type == UnmapNotify) return "UnmapNotify";
6166 if (type == MapNotify) return "MapNotify";
6167 if (type == MapRequest) return "MapRequest";
6168 if (type == ReparentNotify) return "ReparentNotify";
6169 if (type == ConfigureNotify) return "ConfigureNotify";
6170 if (type == ConfigureRequest) return "ConfigureRequest";
6171 if (type == GravityNotify) return "GravityNotify";
6172 if (type == ResizeRequest) return "ResizeRequest";
6173 if (type == CirculateNotify) return "CirculateNotify";
6174 if (type == CirculateRequest) return "CirculateRequest";
6175 if (type == PropertyNotify) return "PropertyNotify";
6176 if (type == SelectionClear) return "SelectionClear";
6177 if (type == SelectionRequest) return "SelectionRequest";
6178 if (type == SelectionNotify) return "SelectionNotify";
6179 if (type == ColormapNotify) return "ColormapNotify";
6180 if (type == ClientMessage) return "ClientMessage";
6181 if (type == MappingNotify) return "MappingNotify";
6182 if (type == LASTEvent) return "LASTEvent";
6183 sprintf(unk, "Unknown %d", type);
6184 return unk;
6185}
6186char *VState(int state) {
6187 if (state == VisibilityFullyObscured) return "VisibilityFullyObscured";
6188 if (state == VisibilityPartiallyObscured) return "VisibilityPartiallyObscured";
6189 if (state == VisibilityUnobscured) return "VisibilityUnobscured";
6190 sprintf(unk, "Unknown %d", state);
6191 return unk;
6192}
6193char *MState(int state) {
6194 if (state == IsViewable) return "IsViewable";
6195 if (state == IsUnmapped) return "IsUnmapped";
6196 sprintf(unk, "Unknown %d", state);
6197 return unk;
6198}
6199sraRegionPtr rect_reg[64];
6200sraRegionPtr zero_rects = NULL;
6201
6202int free_rect(int idx) {
6203 int n, ok = 0;
6204 sraRegionPtr r1, r2;
6205 int x, y, w, h;
6206
6207 if (idx < 0 || idx >= cache_list_num) {
6208if (0) fprintf(stderr, "free_rect: bad index: %d\n", idx);
6209 clean_up_exit(1);
6210 }
6211
6212 x = cache_list[idx].bs_x;
6213 y = cache_list[idx].bs_y;
6214 w = cache_list[idx].bs_w;
6215 h = cache_list[idx].bs_h;
6216
6217 if (x < 0) {
6218 CLEAR(idx);
6219if (dnow() > last_client + 5 && ncdb) fprintf(stderr, "free_rect: already bs_x invalidated: %d bs_x: %d\n", idx, x);
6220 return 1;
6221 }
6222
6223 r2 = sraRgnCreateRect(x, y, x+w, y+h);
6224
6225 n = get_bs_n(y);
6226 if (n >= 0) {
6227 r1 = rect_reg[n];
6228 sraRgnOr(r1, r2);
6229 ok = 1;
6230 }
6231
6232 if (zero_rects) {
6233 sraRgnOr(zero_rects, r2);
6234 x = cache_list[idx].su_x;
6235 y = cache_list[idx].su_y;
6236 w = cache_list[idx].su_w;
6237 h = cache_list[idx].su_h;
6238 if (x >= 0) {
6239 sraRgnDestroy(r2);
6240 r2 = sraRgnCreateRect(x, y, x+w, y+h);
6241 sraRgnOr(zero_rects, r2);
6242 }
6243 }
6244 sraRgnDestroy(r2);
6245
6246 CLEAR(idx);
6247if (! ok && ncdb) fprintf(stderr, "**** free_rect: not-found %d\n", idx);
6248 return ok;
6249}
6250
6251int fr_BIG1 = 0;
6252int fr_BIG2 = 0;
6253int fr_REGION = 0;
6254int fr_GRID = 0;
6255int fr_EXPIRE = 0;
6256int fr_FORCE = 0;
6257int fr_FAIL = 0;
6258int fr_BIG1t = 0;
6259int fr_BIG2t = 0;
6260int fr_REGIONt = 0;
6261int fr_GRIDt = 0;
6262int fr_EXPIREt = 0;
6263int fr_FORCEt = 0;
6264int fr_FAILt = 0;
6265
6266void expire_rects1(int idx, int w, int h, int *x_hit, int *y_hit, int big1, int big2, int cram) {
6267 sraRegionPtr r1, r2, r3;
6268 int x = -1, y = -1, n;
6269
6270 if (*x_hit < 0) {
6271 int i, k, old[10], N = 4;
6272 double dold[10], fa, d, d1, d2, d3;
6273 int a0 = w * h, a1;
6274
6275 for (k=1; k<=N; k++) {
6276 old[k] = -1;
6277 dold[k] = -1.0;
6278 }
6279 for (i=0; i<cache_list_num; i++) {
6280 int wb = cache_list[i].bs_w;
6281 int hb = cache_list[i].bs_h;
6282 if (cache_list[i].bs_x < 0) {
6283 continue;
6284 }
6285 if (w > wb || h > hb) {
6286 continue;
6287 }
6288 if (wb == 0 || hb == 0) {
6289 continue;
6290 }
6291 if (a0 == 0) {
6292 continue;
6293 }
6294 if (i == idx) {
6295 continue;
6296 }
6297 a1 = wb * hb;
6298 fa = ((double) a1) / a0;
6299 k = (int) fa;
6300
6301 if (k < 1) k = 1;
6302 if (k > N) continue;
6303
6304 d1 = cache_list[i].time;
6305 d2 = cache_list[i].bs_time;
6306 d3 = cache_list[i].su_time;
6307
6308 d = d1;
6309 if (d2 > d) d = d2;
6310 if (d3 > d) d = d3;
6311
6312 if (dold[k] == -1.0 || d < dold[k]) {
6313 old[k] = i;
6314 dold[k] = d;
6315 }
6316 }
6317
6318 for (k=1; k<=N; k++) {
6319 if (old[k] >= 0) {
6320 int ik = old[k];
6321 int k_x = cache_list[ik].bs_x;
6322 int k_y = cache_list[ik].bs_y;
6323 int k_w = cache_list[ik].bs_w;
6324 int k_h = cache_list[ik].bs_h;
6325
6326if (ncdb) fprintf(stderr, ">>**--**>> found rect via EXPIRE: %d 0x%lx -- %dx%d+%d+%d %d %d -- %dx%d+%d+%d A: %d/%d\n",
6327 ik, cache_list[ik].win, w, h, x, y, *x_hit, *y_hit, k_w, k_h, k_x, k_y, k_w * k_h, w * h);
6328
6329 free_rect(ik);
6330 fr_EXPIRE++;
6331 fr_EXPIREt++;
6332 *x_hit = k_x;
6333 *y_hit = k_y;
6334 n = get_bs_n(*y_hit);
6335 if (n >= 0) {
6336 r1 = rect_reg[n];
6337 r2 = sraRgnCreateRect(*x_hit, *y_hit, *x_hit + w, *y_hit + h);
6338 sraRgnSubtract(r1, r2);
6339 sraRgnDestroy(r2);
6340 } else {
6341 fprintf(stderr, "failure to find y n in find_rect\n");
6342 clean_up_exit(1);
6343 }
6344 break;
6345 }
6346 }
6347 }
6348
6349 /* next, force ourselves into some corner, expiring many */
6350 if (*x_hit < 0) {
6351 int corner_x = (int) (2 * rfac());
6352 int corner_y = (int) (2 * rfac());
6353 int x0 = 0, y0 = 0, i, nrand, nr = ncache/2;
6354 if (nr == 1) {
6355 nrand = 1;
6356 } else {
6357 if (! big1) {
6358 nrand = 1;
6359 } else {
6360 if (big2 && nr > 2) {
6361 nrand = 1 + (int) ((nr - 2) * rfac());
6362 nrand += 2;
6363 } else {
6364 nrand = 1 + (int) ((nr - 1) * rfac());
6365 nrand += 1;
6366 }
6367 }
6368 }
6369 if (nrand < 0 || nrand > nr) {
6370 nrand = nr;
6371 }
6372 if (cram && big1) {
6373 corner_x = 1;
6374 }
6375
6376 y0 += dpy_y;
6377 if (nrand > 1) {
6378 y0 += 2 * (nrand - 1) * dpy_y;
6379 }
6380 if (corner_y) {
6381 y0 += dpy_y - h;
6382 }
6383 if (corner_x) {
6384 x0 += dpy_x - w;
6385 }
6386 r1 = sraRgnCreateRect(x0, y0, x0+w, y0+h);
6387
6388 for (i=0; i<cache_list_num; i++) {
6389 int xb = cache_list[i].bs_x;
6390 int yb = cache_list[i].bs_y;
6391 int wb = cache_list[i].bs_w;
6392 int hb = cache_list[i].bs_h;
6393 if (xb < 0) {
6394 continue;
6395 }
6396 if (nabs(yb - y0) > dpy_y) {
6397 continue;
6398 }
6399 r2 = sraRgnCreateRect(xb, yb, xb+wb, yb+hb);
6400 if (sraRgnAnd(r2, r1)) {
6401 free_rect(i);
6402 }
6403 sraRgnDestroy(r2);
6404 }
6405 *x_hit = x0;
6406 *y_hit = y0;
6407 r3 = rect_reg[2*nrand-1];
6408 sraRgnSubtract(r3, r1);
6409 sraRgnDestroy(r1);
6410
6411if (ncdb) fprintf(stderr, ">>**--**>> found rect via FORCE: %dx%d+%d+%d -- %d %d\n", w, h, x, y, *x_hit, *y_hit);
6412
6413 fr_FORCE++;
6414 fr_FORCEt++;
6415 }
6416}
6417
6418void expire_rects2(int idx, int w, int h, int *x_hit, int *y_hit, int big1, int big2, int cram) {
6419 sraRegionPtr r1, r2, r3;
6420 int x = -1, y = -1, n, i, j, k;
6421 int nwgt_max = 128, nwgt = 0;
6422 int type[128];
6423 int val[4][128];
6424 double wgt[128], norm;
6425 int Expire = 1, Force = 2;
6426 int do_expire = 1;
6427 int do_force = 1;
6428 double now = dnow(), r;
6429 double newest = -1.0, oldest = -1.0, basetime;
6430 double map_factor = 0.25;
6431
6432 for (i=0; i<cache_list_num; i++) {
6433 double d, d1, d2;
6434
6435 d1 = cache_list[i].bs_time;
6436 d2 = cache_list[i].su_time;
6437
6438 d = d1;
6439 if (d2 > d) d = d2;
6440
6441 if (d == 0.0) {
6442 continue;
6443 }
6444
6445 if (oldest == -1.0 || d < oldest) {
6446 oldest = d;
6447 }
6448 if (newest == -1.0 || d > newest) {
6449 newest = d;
6450 }
6451 }
6452 if (newest == -1.0) {
6453 newest = now;
6454 }
6455 if (oldest == -1.0) {
6456 oldest = newest - 1800;
6457 }
6458
6459 basetime = newest + 0.1 * (newest - oldest);
6460
6461 if (do_expire) {
6462 int old[10], N = 4;
6463 double dold[10], fa, d, d1, d2;
6464 int a0 = w * h, a1;
6465
6466 for (k=1; k<=N; k++) {
6467 old[k] = -1;
6468 dold[k] = -1.0;
6469 }
6470 for (i=0; i<cache_list_num; i++) {
6471 int wb = cache_list[i].bs_w;
6472 int hb = cache_list[i].bs_h;
6473 if (cache_list[i].bs_x < 0) {
6474 continue;
6475 }
6476 if (w > wb || h > hb) {
6477 continue;
6478 }
6479 if (wb == 0 || hb == 0) {
6480 continue;
6481 }
6482 if (a0 == 0) {
6483 continue;
6484 }
6485 if (i == idx) {
6486 continue;
6487 }
6488
6489 a1 = wb * hb;
6490 fa = ((double) a1) / a0;
6491 k = (int) fa;
6492
6493 if (k < 1) k = 1;
6494 if (k > N) continue;
6495
6496 d1 = cache_list[i].bs_time;
6497 d2 = cache_list[i].su_time;
6498
6499 d = d1;
6500 if (d2 > d) d = d2;
6501 if (d == 0.0) d = oldest;
6502
6503 if (dold[k] == -1.0 || d < dold[k]) {
6504 old[k] = i;
6505 dold[k] = d;
6506 }
6507 }
6508
6509 for (k=1; k<=N; k++) {
6510 if (old[k] >= 0) {
6511 int ik = old[k];
6512 int k_w = cache_list[ik].bs_w;
6513 int k_h = cache_list[ik].bs_h;
6514
6515 wgt[nwgt] = (basetime - dold[k]) / (k_w * k_h);
6516 if (cache_list[ik].map_state == IsViewable) {
6517 wgt[nwgt] *= map_factor;
6518 }
6519 type[nwgt] = Expire;
6520 val[0][nwgt] = ik;
6521if (ncdb) fprintf(stderr, "Expire[%02d] %9.5f age=%9.4f area=%8d need=%8d\n", nwgt, 10000 * wgt[nwgt], basetime - dold[k], k_w * k_h, w*h);
6522 nwgt++;
6523 if (nwgt >= nwgt_max) {
6524 break;
6525 }
6526 }
6527 }
6528 }
6529
6530 /* next, force ourselves into some corner, expiring many rect */
6531 if (do_force) {
6532 int corner_x, corner_y;
6533 int x0, y0;
6534
6535 for (n = 1; n < ncache; n += 2) {
6536 if (big1 && ncache > 2 && n == 1) {
6537 continue;
6538 }
6539 if (big2 && ncache > 4 && n <= 3) {
6540 continue;
6541 }
6542 for (corner_x = 0; corner_x < 2; corner_x++) {
6543 if (cram && big1 && corner_x == 0) {
6544 continue;
6545 }
6546 for (corner_y = 0; corner_y < 2; corner_y++) {
6547 double age = 0.0, area = 0.0, amap = 0.0, a;
6548 double d, d1, d2, score;
6549 int nc = 0;
6550
6551 x0 = 0;
6552 y0 = 0;
6553 y0 += n * dpy_y;
6554
6555 if (corner_y) {
6556 y0 += dpy_y - h;
6557 }
6558 if (corner_x) {
6559 x0 += dpy_x - w;
6560 }
6561 r1 = sraRgnCreateRect(x0, y0, x0+w, y0+h);
6562
6563 for (i=0; i<cache_list_num; i++) {
6564 int xb = cache_list[i].bs_x;
6565 int yb = cache_list[i].bs_y;
6566 int wb = cache_list[i].bs_w;
6567 int hb = cache_list[i].bs_h;
6568
6569 if (xb < 0) {
6570 continue;
6571 }
6572 if (nabs(yb - y0) > dpy_y) {
6573 continue;
6574 }
6575
6576 r2 = sraRgnCreateRect(xb, yb, xb+wb, yb+hb);
6577 if (! sraRgnAnd(r2, r1)) {
6578 sraRgnDestroy(r2);
6579 continue;
6580 }
6581 sraRgnDestroy(r2);
6582
6583 a = wb * hb;
6584
6585 d1 = cache_list[i].bs_time;
6586 d2 = cache_list[i].su_time;
6587
6588 d = d1;
6589 if (d2 > d) d = d2;
6590 if (d == 0.0) d = oldest;
6591
6592 if (cache_list[i].map_state == IsViewable) {
6593 amap += a;
6594 }
6595 area += a;
6596 age += (basetime - d) * a;
6597 nc++;
6598 }
6599 if (nc == 0) {
6600 score = 999999.9;
6601 } else {
6602 double fac;
6603 age = age / area;
6604 score = age / area;
6605 fac = 1.0 * (1.0 - amap/area) + map_factor * (amap/area);
6606 score *= fac;
6607 }
6608
6609 wgt[nwgt] = score;
6610 type[nwgt] = Force;
6611 val[0][nwgt] = n;
6612 val[1][nwgt] = x0;
6613 val[2][nwgt] = y0;
6614if (ncdb) fprintf(stderr, "Force [%02d] %9.5f age=%9.4f area=%8d amap=%8d need=%8d\n", nwgt, 10000 * wgt[nwgt], age, (int) area, (int) amap, w*h);
6615 nwgt++;
6616 if (nwgt >= nwgt_max) break;
6617 sraRgnDestroy(r1);
6618 }
6619 if (nwgt >= nwgt_max) break;
6620 }
6621 if (nwgt >= nwgt_max) break;
6622 }
6623 }
6624
6625 if (nwgt == 0) {
6626if (ncdb) fprintf(stderr, "nwgt=0\n");
6627 *x_hit = -1;
6628 return;
6629 }
6630
6631 norm = 0.0;
6632 for (i=0; i < nwgt; i++) {
6633 norm += wgt[i];
6634 }
6635 for (i=0; i < nwgt; i++) {
6636 wgt[i] /= norm;
6637 }
6638
6639 r = rfac();
6640
6641 norm = 0.0;
6642 for (j=0; j < nwgt; j++) {
6643 norm += wgt[j];
6644if (ncdb) fprintf(stderr, "j=%2d acc=%.6f r=%.6f\n", j, norm, r);
6645 if (r < norm) {
6646 break;
6647 }
6648 }
6649 if (j >= nwgt) {
6650 j = nwgt - 1;
6651 }
6652
6653 if (type[j] == Expire) {
6654 int ik = val[0][j];
6655 int k_x = cache_list[ik].bs_x;
6656 int k_y = cache_list[ik].bs_y;
6657 int k_w = cache_list[ik].bs_w;
6658 int k_h = cache_list[ik].bs_h;
6659
6660if (ncdb) fprintf(stderr, ">>**--**>> found rect [%d] via RAN EXPIRE: %d 0x%lx -- %dx%d+%d+%d %d %d -- %dx%d+%d+%d A: %d/%d\n",
6661 get_bs_n(*y_hit), ik, cache_list[ik].win, w, h, x, y, *x_hit, *y_hit, k_w, k_h, k_x, k_y, k_w * k_h, w * h);
6662
6663 free_rect(ik);
6664 fr_EXPIRE++;
6665 fr_EXPIREt++;
6666 *x_hit = k_x;
6667 *y_hit = k_y;
6668 n = get_bs_n(*y_hit);
6669 if (n >= 0) {
6670 r1 = rect_reg[n];
6671 r2 = sraRgnCreateRect(*x_hit, *y_hit, *x_hit + w, *y_hit + h);
6672 sraRgnSubtract(r1, r2);
6673 sraRgnDestroy(r2);
6674 } else {
6675 fprintf(stderr, "failure to find y n in find_rect\n");
6676 clean_up_exit(1);
6677 }
6678
6679 } else if (type[j] == Force) {
6680
6681 int x0 = val[1][j];
6682 int y0 = val[2][j];
6683 n = val[0][j];
6684
6685 r1 = sraRgnCreateRect(x0, y0, x0+w, y0+h);
6686
6687 for (i=0; i<cache_list_num; i++) {
6688 int xb = cache_list[i].bs_x;
6689 int yb = cache_list[i].bs_y;
6690 int wb = cache_list[i].bs_w;
6691 int hb = cache_list[i].bs_h;
6692 if (xb < 0) {
6693 continue;
6694 }
6695 if (nabs(yb - y0) > dpy_y) {
6696 continue;
6697 }
6698 r2 = sraRgnCreateRect(xb, yb, xb+wb, yb+hb);
6699 if (sraRgnAnd(r2, r1)) {
6700 free_rect(i);
6701 }
6702 sraRgnDestroy(r2);
6703 }
6704 *x_hit = x0;
6705 *y_hit = y0;
6706 r3 = rect_reg[2*n-1];
6707 sraRgnSubtract(r3, r1);
6708 sraRgnDestroy(r1);
6709
6710if (ncdb) fprintf(stderr, ">>**--**>> found rect [%d] via RAN FORCE: %dx%d+%d+%d -- %d %d\n", n, w, h, x, y, *x_hit, *y_hit);
6711
6712 fr_FORCE++;
6713 fr_FORCEt++;
6714 }
6715}
6716
6717void expire_rects(int idx, int w, int h, int *x_hit, int *y_hit, int big1, int big2, int cram) {
6718 int method = 2;
6719 if (method == 1) {
6720 expire_rects1(idx, w, h, x_hit, y_hit, big1, big2, cram);
6721 } else if (method == 2) {
6722 expire_rects2(idx, w, h, x_hit, y_hit, big1, big2, cram);
6723 }
6724}
6725
6726int find_rect(int idx, int x, int y, int w, int h) {
6727 sraRegionPtr r1, r2;
6728 sraRectangleIterator *iter;
6729 sraRect rt;
6730 int n, x_hit = -1, y_hit = -1;
6731 int big1 = 0, big2 = 0, cram = 0;
6732 double fac1 = 0.1, fac2 = 0.25;
6733 double last_clean = 0.0;
6734 double now = dnow();
6735 static int nobigs = -1;
6736
6737 if (rect_reg[1] == NULL) {
6738 for (n = 1; n <= ncache; n++) {
6739 rect_reg[n] = sraRgnCreateRect(0, n * dpy_y, dpy_x, (n+1) * dpy_y);
6740 }
6741 } else if (now > last_clean + 60) {
6742 last_clean = now;
6743 for (n = 1; n < ncache; n += 2) {
6744 int i, n2 = n+1;
6745
6746 /* n */
6747 sraRgnDestroy(rect_reg[n]);
6748 r1 = sraRgnCreateRect(0, n * dpy_y, dpy_x, (n+1) * dpy_y);
6749 for (i=0; i<cache_list_num; i++) {
6750 int bs_x = cache_list[i].bs_x;
6751 int bs_y = cache_list[i].bs_y;
6752 int bs_w = cache_list[i].bs_w;
6753 int bs_h = cache_list[i].bs_h;
6754 if (bs_x < 0) {
6755 continue;
6756 }
6757 if (get_bs_n(bs_y) != n) {
6758 continue;
6759 }
6760 r2 = sraRgnCreateRect(bs_x, bs_y, bs_x+bs_w, bs_y+bs_h);
6761 sraRgnSubtract(r1, r2);
6762 }
6763 rect_reg[n] = r1;
6764
6765 /* n+1 */
6766 sraRgnDestroy(rect_reg[n2]);
6767 r1 = sraRgnCreateRect(0, n2 * dpy_y, dpy_x, (n2+1) * dpy_y);
6768 for (i=0; i<cache_list_num; i++) {
6769 int bs_x = cache_list[i].bs_x;
6770 int su_x = cache_list[i].su_x;
6771 int su_y = cache_list[i].su_y;
6772 int su_w = cache_list[i].su_w;
6773 int su_h = cache_list[i].su_h;
6774 if (bs_x < 0) {
6775 continue;
6776 }
6777 if (get_bs_n(su_y) != n2) {
6778 continue;
6779 }
6780 r2 = sraRgnCreateRect(su_x, su_y, su_x+su_w, su_y+su_h);
6781 sraRgnSubtract(r1, r2);
6782 }
6783 rect_reg[n2] = r1;
6784 }
6785 }
6786
6787 if (idx < 0 || idx >= cache_list_num) {
6788if (ncdb) fprintf(stderr, "free_rect: bad index: %d\n", idx);
6789 clean_up_exit(1);
6790 }
6791
6792 cache_list[idx].bs_x = -1;
6793 cache_list[idx].su_x = -1;
6794 cache_list[idx].bs_time = 0.0;
6795 cache_list[idx].su_time = 0.0;
6796
6797 if (ncache_pad) {
6798 x -= ncache_pad;
6799 y -= ncache_pad;
6800 w += 2 * ncache_pad;
6801 h += 2 * ncache_pad;
6802 }
6803
6804 if (ncache <= 2) {
6805 cram = 1;
6806 fac2 = 0.45;
6807 } else if (ncache <= 4) {
6808 fac1 = 0.18;
6809 fac2 = 0.35;
6810 }
6811 if (macosx_console && !macosx_ncache_macmenu) {
6812 if (cram) {
6813 fac1 *= 1.5;
6814 fac2 *= 1.5;
6815 } else {
6816 fac1 *= 2.5;
6817 fac2 *= 2.5;
6818 }
6819 }
6820 if (w * h > fac1 * (dpy_x * dpy_y)) {
6821 big1 = 1;
6822 }
6823 if (w * h > fac2 * (dpy_x * dpy_y)) {
6824 big2 = 1;
6825 }
6826
6827 if (nobigs < 0) {
6828 if (getenv("NOBIGS")) {
6829 nobigs = 1;
6830 } else {
6831 nobigs = 0;
6832 }
6833 }
6834 if (nobigs) {
6835 big1 = big2 = 0;
6836 }
6837
6838 if (w > dpy_x || h > dpy_y) {
6839if (ncdb) fprintf(stderr, ">>**--**>> BIG1 rect: %dx%d+%d+%d -- %d %d\n", w, h, x, y, x_hit, y_hit);
6840 fr_BIG1++;
6841 fr_BIG1t++;
6842 return 0;
6843 }
6844 if (w == dpy_x && h == dpy_y) {
6845if (ncdb) fprintf(stderr, ">>**--**>> BIG1 rect: %dx%d+%d+%d -- %d %d (FULL DISPLAY)\n", w, h, x, y, x_hit, y_hit);
6846 fr_BIG1++;
6847 fr_BIG1t++;
6848 return 0;
6849 }
6850 if (cram && big2) {
6851if (ncdb) fprintf(stderr, ">>**--**>> BIG2 rect: %dx%d+%d+%d -- %d %d\n", w, h, x, y, x_hit, y_hit);
6852 fr_BIG2++;
6853 fr_BIG2t++;
6854 return 0;
6855 }
6856
6857 /* first try individual rects of unused region */
6858 for (n = 1; n < ncache; n += 2) {
6859 r1 = rect_reg[n];
6860 r2 = NULL;
6861 if (big1 && n == 1 && ncache > 2) {
6862 continue;
6863 }
6864 if (big2 && n <= 3 && ncache > 4) {
6865 continue;
6866 }
6867 iter = sraRgnGetIterator(r1);
6868 while (sraRgnIteratorNext(iter, &rt)) {
6869 int rw = rt.x2 - rt.x1;
6870 int rh = rt.y2 - rt.y1;
6871 if (cram && big1 && rt.x1 < dpy_x/4) {
6872 continue;
6873 }
6874 if (rw >= w && rh >= h) {
6875 x_hit = rt.x1;
6876 y_hit = rt.y1;
6877 if (cram && big1) {
6878 x_hit = rt.x2 - w;
6879 }
6880 r2 = sraRgnCreateRect(x_hit, y_hit, x_hit + w, y_hit + h);
6881 break;
6882 }
6883 }
6884 sraRgnReleaseIterator(iter);
6885 if (r2 != NULL) {
6886if (ncdb) fprintf(stderr, ">>**--**>> found rect via REGION: %dx%d+%d+%d -- %d %d\n", w, h, x, y, x_hit, y_hit);
6887 fr_REGION++;
6888 fr_REGIONt++;
6889 sraRgnSubtract(r1, r2);
6890 sraRgnDestroy(r2);
6891 break;
6892 }
6893 }
6894
6895
6896 /* next try moving corner to grid points */
6897 if (x_hit < 0) {
6898 for (n = 1; n < ncache; n += 2) {
6899 int rx, ry, Nx = 48, Ny = 24, ny = n * dpy_y;
6900
6901 if (big1 && n == 1 && ncache > 2) {
6902 continue;
6903 }
6904 if (big2 && n == 3 && ncache > 4) {
6905 continue;
6906 }
6907
6908 r1 = sraRgnCreateRect(0, n * dpy_y, dpy_x, (n+1) * dpy_y);
6909 sraRgnSubtract(r1, rect_reg[n]);
6910 r2 = NULL;
6911
6912 rx = 0;
6913 while (rx + w <= dpy_x) {
6914 ry = 0;
6915 if (cram && big1 && rx < dpy_x/4) {
6916 rx += dpy_x/Nx;
6917 continue;
6918 }
6919 while (ry + h <= dpy_y) {
6920 r2 = sraRgnCreateRect(rx, ry+ny, rx + w, ry+ny + h);
6921 if (sraRgnAnd(r2, r1)) {
6922 sraRgnDestroy(r2);
6923 r2 = NULL;
6924 } else {
6925 sraRgnDestroy(r2);
6926 r2 = sraRgnCreateRect(rx, ry+ny, rx + w, ry+ny + h);
6927 x_hit = rx;
6928 y_hit = ry+ny;
6929 }
6930 ry += dpy_y/Ny;
6931 if (r2) break;
6932 }
6933 rx += dpy_x/Nx;
6934 if (r2) break;
6935 }
6936 sraRgnDestroy(r1);
6937 if (r2 != NULL) {
6938 sraRgnSubtract(rect_reg[n], r2);
6939 sraRgnDestroy(r2);
6940if (ncdb) fprintf(stderr, ">>**--**>> found rect via GRID: %dx%d+%d+%d -- %d %d\n", w, h, x, y, x_hit, y_hit);
6941 fr_GRID++;
6942 fr_GRIDt++;
6943 break;
6944 }
6945 }
6946 }
6947
6948 /* next, try expiring the oldest/smallest used bs/su rectangle we fit in */
6949
6950 if (x_hit < 0) {
6951 expire_rects(idx, w, h, &x_hit, &y_hit, big1, big2, cram);
6952 }
6953
6954 cache_list[idx].bs_x = x_hit;
6955 cache_list[idx].bs_y = y_hit;
6956 cache_list[idx].bs_w = w;
6957 cache_list[idx].bs_h = h;
6958
6959 cache_list[idx].su_x = x_hit;
6960 cache_list[idx].su_y = y_hit + dpy_y;
6961 cache_list[idx].su_w = w;
6962 cache_list[idx].su_h = h;
6963
6964 if (x_hit < 0) {
6965 /* bad news, can it still happen? */
6966 if (ncdb) fprintf(stderr, ">>**--**>> *FAIL rect: %dx%d+%d+%d -- %d %d\n", w, h, x, y, x_hit, y_hit);
6967 fr_FAIL++;
6968 fr_FAILt++;
6969 return 0;
6970 } else {
6971 if (0) fprintf(stderr, ">>**--**>> found rect: %dx%d+%d+%d -- %d %d\n", w, h, x, y, x_hit, y_hit);
6972 }
6973
6974 if (zero_rects) {
6975 r1 = sraRgnCreateRect(x_hit, y_hit, x_hit+w, y_hit+h);
6976 sraRgnSubtract(zero_rects, r1);
6977 sraRgnDestroy(r1);
6978 r1 = sraRgnCreateRect(x_hit, y_hit+dpy_y, x_hit+w, y_hit+dpy_y+h);
6979 sraRgnSubtract(zero_rects, r1);
6980 sraRgnDestroy(r1);
6981 }
6982
6983 return 1;
6984}
6985
6986static void cache_cr(sraRegionPtr r, int dx, int dy, double d0, double d1, int *nbatch) {
6987 if (sraRgnEmpty(r)) {
6988 return;
6989 }
6990 if (nbatch == NULL) {
6991 if (!fb_push_wait(d0, FB_COPY)) {
6992 fb_push_wait(d0/2, FB_COPY);
6993 }
6994 do_copyregion(r, dx, dy, 0);
6995 if (!fb_push_wait(d1, FB_COPY)) {
6996 fb_push_wait(d1/2, FB_COPY);
6997 }
6998 } else {
6999 batch_dxs[*nbatch] = dx;
7000 batch_dys[*nbatch] = dy;
7001 batch_reg[*nbatch] = sraRgnCreateRgn(r);
7002 (*nbatch)++;
7003 }
7004}
7005
7006double save_delay0 = 0.02;
7007double restore_delay0 = 0.02;
7008double save_delay1 = 0.05;
7009double restore_delay1 = 0.05;
7010static double dtA, dtB;
7011
7012int valid_wr(int idx, Window win, XWindowAttributes *attr) {
7013#ifdef MACOSX
7014 if (macosx_console) {
7015 /* this is all to avoid animation changing WxH+X+Y... */
7016 if (idx >= 0) {
7017 int rc = valid_window(win, attr, 1);
7018 attr->x = cache_list[idx].x;
7019 attr->y = cache_list[idx].y;
7020 attr->width = cache_list[idx].width;
7021 attr->height = cache_list[idx].height;
7022 return rc;
7023 } else {
7024 return valid_window(win, attr, 1);
7025 }
7026 }
7027#else
7028 if (!idx) {}
7029#endif
7030 return valid_window(win, attr, 1);
7031}
7032
7033int clipped(int idx) {
7034 int ic;
7035 sraRegionPtr r0, r1, r2;
7036 int x1, y1, w1, h1;
7037 Window win;
7038 int clip = 0;
7039
7040 if (idx < 0) {
7041 return 0;
7042 }
7043 r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
7044
7045 x1 = cache_list[idx].x;
7046 y1 = cache_list[idx].y;
7047 w1 = cache_list[idx].width;
7048 h1 = cache_list[idx].height;
7049
7050 win = cache_list[idx].win;
7051
7052 r1 = sraRgnCreateRect(x1, y1, x1+w1, y1+h1);
7053 sraRgnAnd(r1, r0);
7054
7055 for (ic = old_stack_n - 1; ic >= 0; ic--) {
7056 int xc, yc, wc, hc, idx2;
7057
7058 if (old_stack[ic] == win) {
7059 break;
7060 }
7061 if (old_stack_mapped[ic] == 0) {
7062 continue;
7063 }
7064 idx2 = lookup_old_stack_index(ic);
7065 if (idx2 < 0) {
7066 continue;
7067 }
7068 if (cache_list[idx2].win == win) {
7069 break;
7070 }
7071 if (cache_list[idx2].map_state != IsViewable) {
7072 continue;
7073 }
7074 xc = cache_list[idx2].x;
7075 yc = cache_list[idx2].y;
7076 wc = cache_list[idx2].width;
7077 hc = cache_list[idx2].height;
7078
7079 r2 = sraRgnCreateRect(xc, yc, xc+wc, yc+hc);
7080 sraRgnAnd(r2, r0);
7081 if (sraRgnAnd(r2, r1)) {
7082if (0) fprintf(stderr, "clip[0x%lx]: 0x%lx, %d/%d\n", win, cache_list[idx2].win, ic, idx2);
7083 clip = 1;
7084 }
7085 sraRgnDestroy(r2);
7086 if (clip) {
7087 break;
7088 }
7089 }
7090 sraRgnDestroy(r0);
7091 sraRgnDestroy(r1);
7092if (0) fprintf(stderr, "clip[0x%lx]: %s\n", win, clip ? "clipped" : "no-clipped");
7093 return clip;
7094}
7095
7096void clip_region(sraRegionPtr r, Window win) {
7097 int ic, idx2;
7098 sraRegionPtr r1;
7099 for (ic = old_stack_n - 1; ic >= 0; ic--) {
7100 int xc, yc, wc, hc;
7101
7102if (0) fprintf(stderr, "----[0x%lx]: 0x%lx, %d %d\n", win, old_stack[ic], ic, old_stack_mapped[ic]);
7103 if (old_stack[ic] == win) {
7104 break;
7105 }
7106 if (old_stack_mapped[ic] == 0) {
7107 continue;
7108 }
7109 idx2 = lookup_old_stack_index(ic);
7110 if (idx2 < 0) {
7111 continue;
7112 }
7113 if (cache_list[idx2].win == win) {
7114 break;
7115 }
7116 if (cache_list[idx2].map_state != IsViewable) {
7117 continue;
7118 }
7119 xc = cache_list[idx2].x;
7120 yc = cache_list[idx2].y;
7121 wc = cache_list[idx2].width;
7122 hc = cache_list[idx2].height;
7123 r1 = sraRgnCreateRect(xc, yc, xc+wc, yc+hc);
7124 if (sraRgnAnd(r1, r)) {
7125 sraRgnSubtract(r, r1);
7126if (0) fprintf(stderr, "clip[0x%lx]: 0x%lx, %d/%d\n", win, cache_list[idx2].win, ic, idx2);
7127 }
7128 sraRgnDestroy(r1);
7129 }
7130}
7131
7132int bs_save(int idx, int *nbatch, XWindowAttributes *attr, int clip, int only_if_tracking, int *valid, int verb) {
7133 Window win = cache_list[idx].win;
7134 int x1, y1, w1, h1;
7135 int x2, y2, w2, h2;
7136 int x, y, w, h;
7137 int dx, dy, rc = 1;
7138 sraRegionPtr r, r0;
7139
7140 x1 = cache_list[idx].x;
7141 y1 = cache_list[idx].y;
7142 w1 = cache_list[idx].width;
7143 h1 = cache_list[idx].height;
7144
7145if (ncdb && verb) fprintf(stderr, "backingstore save: 0x%lx %3d clip=%d\n", win, idx, clip);
7146
7147 X_LOCK;
7148 if (*valid) {
7149 attr->x = x1;
7150 attr->y = y1;
7151 attr->width = w1;
7152 attr->height = h1;
7153 } else if (! valid_wr(idx, win, attr)) {
7154if (ncdb) fprintf(stderr, "bs_save: not a valid X window: 0x%lx\n", win);
7155 X_UNLOCK;
7156 *valid = 0;
7157 cache_list[idx].valid = 0;
7158 return 0;
7159 } else {
7160 *valid = 1;
7161 }
7162 X_UNLOCK;
7163
7164 if (only_if_tracking && cache_list[idx].bs_x < 0) {
7165 return 0;
7166 }
7167
7168 x2 = attr->x;
7169 y2 = attr->y;
7170 w2 = attr->width;
7171 h2 = attr->height;
7172
7173 if (cache_list[idx].bs_x < 0) {
7174 rc = find_rect(idx, x2, y2, w2, h2);
7175 } else if (w2 > cache_list[idx].bs_w || h2 > cache_list[idx].bs_h) {
7176 free_rect(idx);
7177 rc = find_rect(idx, x2, y2, w2, h2);
7178 }
7179
7180 x = cache_list[idx].bs_x;
7181 y = cache_list[idx].bs_y;
7182 w = cache_list[idx].bs_w;
7183 h = cache_list[idx].bs_h;
7184
7185 if (x < 0 || ! rc) {
7186if (ncdb) fprintf(stderr, "BS_save: FAIL FOR: %d\n", idx);
7187 return 0;
7188 }
7189
7190 if (ncache_pad) {
7191 x2 -= ncache_pad;
7192 y2 -= ncache_pad;
7193 w2 += 2 * ncache_pad;
7194 h2 += 2 * ncache_pad;
7195 }
7196
7197 if (clipshift) {
7198 x2 -= coff_x;
7199 y2 -= coff_y;
7200 }
7201
7202 r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
7203 r = sraRgnCreateRect(x2, y2, x2+w2, y2+h2);
7204 sraRgnAnd(r, r0);
7205
7206 if (clip) {
7207 clip_region(r, win);
7208 }
7209
7210 if (sraRgnEmpty(r)) {
7211if (ncdb && verb) fprintf(stderr, "BS_save: Region Empty: %d\n", idx);
7212 sraRgnDestroy(r0);
7213 sraRgnDestroy(r);
7214 return 0;
7215 }
7216
7217 dx = x - x2;
7218 dy = y - y2;
7219
7220 sraRgnOffset(r, dx, dy);
7221
7222 dtA = dnowx();
7223if (ncdb && verb) fprintf(stderr, "BS_save: %.4f %d dx=%d dy=%d\n", dtA, idx, dx, dy);
7224 if (w2 > 0 && h2 > 0) {
7225 cache_cr(r, dx, dy, save_delay0, save_delay1, nbatch);
7226 }
7227 dtB = dnowx();
7228if (ncdb && verb) fprintf(stderr, "BS_save: %.4f %.2f %d done. %dx%d+%d+%d %dx%d+%d+%d %.2f %.2f\n", dtB, dtB-dtA, idx, w1, h1, x1, y1, w2, h2, x2, y2, cache_list[idx].bs_time - x11vnc_start, dnowx());
7229
7230 sraRgnDestroy(r0);
7231 sraRgnDestroy(r);
7232
7233 last_bs_save = cache_list[idx].bs_time = dnow();
7234
7235 return 1;
7236}
7237
7238int su_save(int idx, int *nbatch, XWindowAttributes *attr, int clip, int *valid, int verb) {
7239 Window win = cache_list[idx].win;
7240 int x1, y1, w1, h1;
7241 int x2, y2, w2, h2;
7242 int x, y, w, h;
7243 int dx, dy, rc = 1;
7244 sraRegionPtr r, r0;
7245
7246if (ncdb && verb) fprintf(stderr, "save-unders save: 0x%lx %3d \n", win, idx);
7247
7248 x1 = cache_list[idx].x;
7249 y1 = cache_list[idx].y;
7250 w1 = cache_list[idx].width;
7251 h1 = cache_list[idx].height;
7252
7253 X_LOCK;
7254 if (*valid) {
7255 attr->x = x1;
7256 attr->y = y1;
7257 attr->width = w1;
7258 attr->height = h1;
7259 } else if (! valid_wr(idx, win, attr)) {
7260if (ncdb) fprintf(stderr, "su_save: not a valid X window: 0x%lx\n", win);
7261 X_UNLOCK;
7262 *valid = 0;
7263 cache_list[idx].valid = 0;
7264 return 0;
7265 } else {
7266 *valid = 1;
7267 }
7268 X_UNLOCK;
7269
7270 x2 = attr->x;
7271 y2 = attr->y;
7272 w2 = attr->width;
7273 h2 = attr->height;
7274
7275 if (cache_list[idx].bs_x < 0) {
7276 rc = find_rect(idx, x2, y2, w2, h2);
7277 } else if (w2 > cache_list[idx].su_w || h2 > cache_list[idx].su_h) {
7278 free_rect(idx);
7279 rc = find_rect(idx, x2, y2, w2, h2);
7280 }
7281 x = cache_list[idx].su_x;
7282 y = cache_list[idx].su_y;
7283 w = cache_list[idx].su_w;
7284 h = cache_list[idx].su_h;
7285
7286 if (x < 0 || ! rc) {
7287if (ncdb) fprintf(stderr, "SU_save: FAIL FOR: %d\n", idx);
7288 return 0;
7289 }
7290
7291 if (ncache_pad) {
7292 x2 -= ncache_pad;
7293 y2 -= ncache_pad;
7294 w2 += 2 * ncache_pad;
7295 h2 += 2 * ncache_pad;
7296 }
7297
7298 if (clipshift) {
7299 x2 -= coff_x;
7300 y2 -= coff_y;
7301 }
7302
7303 r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
7304 r = sraRgnCreateRect(x2, y2, x2+w2, y2+h2);
7305 sraRgnAnd(r, r0);
7306
7307 if (clip) {
7308 clip_region(r, win);
7309 }
7310
7311 if (sraRgnEmpty(r)) {
7312if (ncdb && verb) fprintf(stderr, "SU_save: Region Empty: %d\n", idx);
7313 sraRgnDestroy(r0);
7314 sraRgnDestroy(r);
7315 return 0;
7316 }
7317
7318
7319 dx = x - x2;
7320 dy = y - y2;
7321
7322 sraRgnOffset(r, dx, dy);
7323
7324 dtA = dnowx();
7325if (ncdb && verb) fprintf(stderr, "SU_save: %.4f %d dx=%d dy=%d\n", dtA, idx, dx, dy);
7326 if (w2 > 0 && h2 > 0) {
7327 cache_cr(r, dx, dy, save_delay0, save_delay1, nbatch);
7328 }
7329 dtB = dnowx();
7330if (ncdb && verb) fprintf(stderr, "SU_save: %.4f %.2f %d done. %dx%d+%d+%d %dx%d+%d+%d %.2f %.2f\n", dtB, dtB-dtA, idx, w1, h1, x1, y1, w2, h2, x2, y2, cache_list[idx].su_time - x11vnc_start, dnowx());
7331
7332 sraRgnDestroy(r0);
7333 sraRgnDestroy(r);
7334
7335 last_su_save = cache_list[idx].su_time = dnow();
7336
7337 return 1;
7338}
7339
7340int bs_restore(int idx, int *nbatch, sraRegionPtr rmask, XWindowAttributes *attr, int clip, int nopad, int *valid, int verb) {
7341 Window win = cache_list[idx].win;
7342 int x1, y1, w1, h1;
7343 int x2, y2, w2, h2;
7344 int x, y, w, h;
7345 int dx, dy;
7346 sraRegionPtr r, r0;
7347
7348if (ncdb && verb) fprintf(stderr, "backingstore restore: 0x%lx %3d \n", win, idx);
7349
7350 x1 = cache_list[idx].x;
7351 y1 = cache_list[idx].y;
7352 w1 = cache_list[idx].width;
7353 h1 = cache_list[idx].height;
7354
7355 X_LOCK;
7356 if (*valid) {
7357 attr->x = x1;
7358 attr->y = y1;
7359 attr->width = w1;
7360 attr->height = h1;
7361 } else if (! valid_wr(idx, win, attr)) {
7362if (ncdb) fprintf(stderr, "BS_restore: not a valid X window: 0x%lx\n", win);
7363 *valid = 0;
7364 X_UNLOCK;
7365 return 0;
7366 } else {
7367 *valid = 1;
7368 }
7369 X_UNLOCK;
7370
7371 x2 = attr->x;
7372 y2 = attr->y;
7373 w2 = attr->width;
7374 h2 = attr->height;
7375
7376 x = cache_list[idx].bs_x;
7377 y = cache_list[idx].bs_y;
7378 w = cache_list[idx].bs_w;
7379 h = cache_list[idx].bs_h;
7380
7381 if (x < 0 || cache_list[idx].bs_time == 0.0) {
7382 return 0;
7383 }
7384
7385 if (ncache_pad) {
7386 if (nopad) {
7387 x += ncache_pad;
7388 y += ncache_pad;
7389 w -= 2 * ncache_pad;
7390 h -= 2 * ncache_pad;
7391 } else {
7392 x2 -= ncache_pad;
7393 y2 -= ncache_pad;
7394 w2 += 2 * ncache_pad;
7395 h2 += 2 * ncache_pad;
7396 }
7397 }
7398
7399 if (clipshift) {
7400 x2 -= coff_x;
7401 y2 -= coff_y;
7402 }
7403
7404 if (w2 > w) {
7405 w2 = w;
7406 }
7407 if (h2 > h) {
7408 h2 = h;
7409 }
7410
7411 r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
7412 r = sraRgnCreateRect(x, y, x+w2, y+h2);
7413
7414 dx = x2 - x;
7415 dy = y2 - y;
7416
7417 sraRgnOffset(r, dx, dy);
7418 sraRgnAnd(r, r0);
7419
7420 if (clip) {
7421 clip_region(r, win);
7422 }
7423 if (rmask != NULL) {
7424 sraRgnAnd(r, rmask);
7425 }
7426
7427 dtA = dnowx();
7428if (ncdb && verb) fprintf(stderr, "BS_rest: %.4f %d dx=%d dy=%d\n", dtA, idx, dx, dy);
7429 if (w2 > 0 && h2 > 0) {
7430 cache_cr(r, dx, dy, restore_delay0, restore_delay1, nbatch);
7431 }
7432 dtB = dnowx();
7433if (ncdb && verb) fprintf(stderr, "BS_rest: %.4f %.2f %d done. %dx%d+%d+%d %dx%d+%d+%d %.2f %.2f\n", dtB, dtB-dtA, idx, w1, h1, x1, y1, w2, h2, x2, y2, cache_list[idx].bs_time - x11vnc_start, dnowx());
7434
7435 sraRgnDestroy(r0);
7436 sraRgnDestroy(r);
7437
7438 last_bs_restore = dnow();
7439
7440 return 1;
7441}
7442
7443int su_restore(int idx, int *nbatch, sraRegionPtr rmask, XWindowAttributes *attr, int clip, int nopad, int *valid, int verb) {
7444 Window win = cache_list[idx].win;
7445 int x1, y1, w1, h1;
7446 int x2 = 0, y2 = 0, w2 = 0, h2 = 0;
7447 int x, y, w, h;
7448 int dx, dy;
7449 sraRegionPtr r, r0;
7450
7451if (ncdb && verb) fprintf(stderr, "save-unders restore: 0x%lx %3d \n", win, idx);
7452
7453 x1 = cache_list[idx].x;
7454 y1 = cache_list[idx].y;
7455 w1 = cache_list[idx].width;
7456 h1 = cache_list[idx].height;
7457
7458 X_LOCK;
7459 if (*valid) {
7460 attr->x = x1;
7461 attr->y = y1;
7462 attr->width = w1;
7463 attr->height = h1;
7464 x2 = attr->x;
7465 y2 = attr->y;
7466 w2 = attr->width;
7467 h2 = attr->height;
7468 } else if (! valid_wr(idx, win, attr)) {
7469if (ncdb) fprintf(stderr, "SU_restore: not a valid X window: 0x%lx\n", win);
7470 *valid = 0;
7471 x2 = x1;
7472 y2 = y1;
7473 w2 = w1;
7474 h2 = h1;
7475 } else {
7476 x2 = attr->x;
7477 y2 = attr->y;
7478 w2 = attr->width;
7479 h2 = attr->height;
7480 *valid = 1;
7481 }
7482 X_UNLOCK;
7483
7484 x = cache_list[idx].su_x;
7485 y = cache_list[idx].su_y;
7486 w = cache_list[idx].su_w;
7487 h = cache_list[idx].su_h;
7488
7489 if (x < 0 || cache_list[idx].bs_x < 0 || cache_list[idx].su_time == 0.0) {
7490if (ncdb) fprintf(stderr, "SU_rest: su_x/bs_x/su_time: %d %d %.3f\n", x, cache_list[idx].bs_x, cache_list[idx].su_time);
7491 return 0;
7492 }
7493
7494 if (ncache_pad) {
7495 if (nopad) {
7496 x += ncache_pad;
7497 y += ncache_pad;
7498 w -= 2 * ncache_pad;
7499 h -= 2 * ncache_pad;
7500 } else {
7501 x2 -= ncache_pad;
7502 y2 -= ncache_pad;
7503 w2 += 2 * ncache_pad;
7504 h2 += 2 * ncache_pad;
7505 }
7506 }
7507
7508 if (clipshift) {
7509 x2 -= coff_x;
7510 y2 -= coff_y;
7511 }
7512
7513 if (w2 > w) {
7514 w2 = w;
7515 }
7516 if (h2 > h) {
7517 h2 = h;
7518 }
7519
7520 r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
7521 r = sraRgnCreateRect(x, y, x+w2, y+h2);
7522
7523 dx = x2 - x;
7524 dy = y2 - y;
7525
7526 sraRgnOffset(r, dx, dy);
7527 sraRgnAnd(r, r0);
7528
7529 if (clip) {
7530 clip_region(r, win);
7531 }
7532 if (rmask != NULL) {
7533 sraRgnAnd(r, rmask);
7534 }
7535
7536 dtA = dnowx();
7537if (ncdb && verb) fprintf(stderr, "SU_rest: %.4f %d dx=%d dy=%d\n", dtA, idx, dx, dy);
7538 if (w2 > 0 && h2 > 0) {
7539 cache_cr(r, dx, dy, restore_delay0, restore_delay1, nbatch);
7540 }
7541 dtB = dnowx();
7542if (ncdb && verb) fprintf(stderr, "SU_rest: %.4f %.2f %d done. %dx%d+%d+%d %dx%d+%d+%d %.2f %.2f\n", dtB, dtB-dtA, idx, w1, h1, x1, y1, w2, h2, x2, y2, cache_list[idx].su_time - x11vnc_start, dnowx());
7543
7544 sraRgnDestroy(r0);
7545 sraRgnDestroy(r);
7546
7547 last_su_restore = dnow();
7548
7549 return 1;
7550}
7551
7552void check_zero_rects(void) {
7553 sraRect rt;
7554 sraRectangleIterator *iter;
7555 if (! zero_rects) {
7556 zero_rects = sraRgnCreate();
7557 }
7558 if (sraRgnEmpty(zero_rects)) {
7559 return;
7560 }
7561
7562 iter = sraRgnGetIterator(zero_rects);
7563 while (sraRgnIteratorNext(iter, &rt)) {
7564 zero_fb(rt.x1, rt.y1, rt.x2, rt.y2);
7565 mark_rect_as_modified(rt.x1, rt.y1, rt.x2, rt.y2, 0);
7566 }
7567 sraRgnReleaseIterator(iter);
7568 sraRgnMakeEmpty(zero_rects);
7569}
7570
7571void block_stats(void) {
7572 int n, k, s1, s2;
7573 static int t = -1;
7574 int vcnt, icnt, tcnt, vtot = 0, itot = 0, ttot = 0;
7575 t++;
7576 for (n = 1; n < ncache+1; n += 2) {
7577 double area = 0.0, frac;
7578 vcnt = 0;
7579 icnt = 0;
7580 tcnt = 0;
7581 for(k=0; k<cache_list_num; k++) {
7582 XWindowAttributes attr;
7583 int x = cache_list[k].bs_x;
7584 int y = cache_list[k].bs_y;
7585 int w = cache_list[k].bs_w;
7586 int h = cache_list[k].bs_h;
7587 int rc = 0;
7588 Window win = cache_list[k].win;
7589
7590 if (win == None) {
7591 continue;
7592 }
7593 if (n == 1) {
7594 X_LOCK;
7595 rc = valid_window(win, &attr, 1);
7596 X_UNLOCK;
7597 if (rc) {
7598 vtot++;
7599 } else {
7600 itot++;
7601 }
7602 if (x >= 0) {
7603 ttot++;
7604 }
7605 }
7606 if (y < n*dpy_y || y > (n+1)*dpy_y) {
7607 continue;
7608 }
7609 if (n != 1) {
7610 X_LOCK;
7611 rc = valid_window(win, &attr, 1);
7612 X_UNLOCK;
7613 }
7614 if (rc) {
7615 vcnt++;
7616 } else {
7617 icnt++;
7618 }
7619 if (x >= 0) {
7620 tcnt++;
7621 }
7622 if (x < 0) {
7623 continue;
7624 }
7625 area += cache_list[k].width * cache_list[k].height;
7626 if (! rc && ! macosx_console) {
7627 char *u = getenv("USER");
7628 if (u && !strcmp(u, "runge")) fprintf(stderr, "\a");
7629 if (ncdb) fprintf(stderr, "\n *** UNRECLAIMED WINDOW: 0x%lx %dx%d+%d+%d\n\n", win, w, h, x, y);
7630 DELETE(k);
7631 }
7632 if (t < 3 || (t % 4) == 0 || hack_val || macosx_console) {
7633 double t1 = cache_list[k].su_time;
7634 double t2 = cache_list[k].bs_time;
7635 if (t1 > 0.0) {t1 = dnow() - t1;} else {t1 = -1.0;}
7636 if (t2 > 0.0) {t2 = dnow() - t2;} else {t2 = -1.0;}
7637 if (ncdb) fprintf(stderr, " [%02d] %04d 0x%08lx bs: %04dx%04d+%04d+%05d vw: %04dx%04d+%04d+%04d cl: %04dx%04d+%04d+%04d map=%d su=%9.3f bs=%9.3f cnt=%d/%d\n",
7638 n, k, win, w, h, x, y, attr.width, attr.height, attr.x, attr.y,
7639 cache_list[k].width, cache_list[k].height, cache_list[k].x, cache_list[k].y,
7640 attr.map_state == IsViewable, t1, t2, cache_list[k].create_cnt, cache_list[k].map_cnt);
7641 }
7642 }
7643 frac = area /(dpy_x * dpy_y);
7644 if (ncdb) fprintf(stderr, "block[%02d] %.3f %8d trak/val/inval: %d/%d/%d of %d\n", n, frac, (int) area, tcnt, vcnt, icnt, vcnt+icnt);
7645 }
7646
7647 if (ncdb) fprintf(stderr, "\n");
7648 if (ncdb) fprintf(stderr, "block: trak/val/inval %d/%d/%d of %d\n", ttot, vtot, itot, vtot+itot);
7649
7650 s1 = fr_REGION + fr_GRID + fr_EXPIRE + fr_FORCE + fr_BIG1 + fr_BIG2 + fr_FAIL;
7651 s2 = fr_REGIONt + fr_GRIDt + fr_EXPIREt + fr_FORCEt + fr_BIG1t + fr_BIG2t + fr_FAILt;
7652 if (ncdb) fprintf(stderr, "\n");
7653 if (ncdb) fprintf(stderr, "find_rect: REGION/GRID/EXPIRE/FORCE - BIG1/BIG2/FAIL %d/%d/%d/%d - %d/%d/%d of %d\n",
7654 fr_REGION, fr_GRID, fr_EXPIRE, fr_FORCE, fr_BIG1, fr_BIG2, fr_FAIL, s1);
7655 if (ncdb) fprintf(stderr, " totals: %d/%d/%d/%d - %d/%d/%d of %d\n",
7656 fr_REGIONt, fr_GRIDt, fr_EXPIREt, fr_FORCEt, fr_BIG1t, fr_BIG2t, fr_FAILt, s2);
7657
7658 fr_BIG1 = 0;
7659 fr_BIG2 = 0;
7660 fr_REGION = 0;
7661 fr_GRID = 0;
7662 fr_EXPIRE = 0;
7663 fr_FORCE = 0;
7664 fr_FAIL = 0;
7665 if (ncdb) fprintf(stderr, "\n");
7666}
7667
7668#define NSCHED 128
7669Window sched_bs[NSCHED];
7670double sched_tm[NSCHED];
7671double last_sched_bs = 0.0;
7672
7673#define SCHED(w, v) \
7674{ \
7675 int k, save = -1, empty = 1; \
7676 for (k=0; k < NSCHED; k++) { \
7677 if (sched_bs[k] == None) { \
7678 save = k; \
7679 } \
7680 if (sched_bs[k] == w) { \
7681 save = k; \
7682 empty = 0; \
7683 break; \
7684 } \
7685 } \
7686 if (save >= 0) { \
7687 sched_bs[save] = w; \
7688 if (empty) { \
7689 sched_tm[save] = dnow(); \
7690 if (v && ncdb) fprintf(stderr, "SCHED: %d %f\n", save, dnowx()); \
7691 } \
7692 } \
7693}
7694
7695void xselectinput(Window w, unsigned long evmask, int sync) {
7696#if NO_X11
7697 trapped_xerror = 0;
7698 trapped_xioerror = 0;
7699 if (!evmask) {}
7700#else
7701 XErrorHandler old_handler1;
7702 XIOErrorHandler old_handler2;
7703
7704 if (macosx_console || !dpy) {
7705 return;
7706 }
7707
7708 old_handler1 = XSetErrorHandler(trap_xerror);
7709 old_handler2 = XSetIOErrorHandler(trap_xioerror);
7710 trapped_xerror = 0;
7711 trapped_xioerror = 0;
7712
7713 XSelectInput(dpy, w, evmask);
7714
7715 /*
7716 * We seem to need to synchronize right away since the window
7717 * might go away quickly.
7718 */
7719 if (sync) {
7720 XSync(dpy, False);
7721 } else {
7722 XFlush_wr(dpy);
7723 }
7724
7725 XSetErrorHandler(old_handler1);
7726 XSetIOErrorHandler(old_handler2);
7727#endif
7728
7729 if (trapped_xerror) {
7730 if (ncdb) fprintf(stderr, "XSELECTINPUT: trapped X Error.");
7731 }
7732 if (trapped_xioerror) {
7733 if (ncdb) fprintf(stderr, "XSELECTINPUT: trapped XIO Error.");
7734 }
7735if (sync && ncdb) fprintf(stderr, "XSELECTINPUT: 0x%lx sync=%d err=%d/%d\n", w, sync, trapped_xerror, trapped_xioerror);
7736}
7737
7738Bool xcheckmaskevent(Display *d, long mask, XEvent *ev) {
7739#ifdef MACOSX
7740 if (macosx_console) {
7741 if (macosx_checkevent(ev)) {
7742 return True;
7743 } else {
7744 return False;
7745 }
7746 }
7747#endif
7748 RAWFB_RET(False);
7749
7750#if NO_X11
7751 if (!d || !mask) {}
7752 return False;
7753#else
7754 return XCheckMaskEvent(d, mask, ev);
7755#endif
7756}
7757
7758#include <rfb/default8x16.h>
7759
7760#define EVMAX 2048
7761XEvent Ev[EVMAX];
7762int Ev_done[EVMAX];
7763int Ev_order[EVMAX];
7764int Ev_area[EVMAX];
7765int Ev_tmp[EVMAX];
7766int Ev_tmp2[EVMAX];
7767Window Ev_tmpwin[EVMAX];
7768Window Ev_win[EVMAX];
7769Window Ev_map[EVMAX];
7770Window Ev_unmap[EVMAX];
7771sraRect Ev_rects[EVMAX];
7772
7773int tmp_stack[STACKMAX];
7774sraRegionPtr tmp_reg[STACKMAX];
7775
7776#define CLEAN_OUT \
7777 for (i=0; i < n; i++) { \
7778 sraRgnDestroy(tmp_reg[i]); \
7779 } \
7780 if (r1) sraRgnDestroy(r1); \
7781 if (r0) sraRgnDestroy(r0);
7782
7783int try_to_fix_resize_su(Window orig_frame, int orig_x, int orig_y, int orig_w, int orig_h,
7784 int x, int y, int w, int h, int try_batch) {
7785
7786 int idx = lookup_win_index(orig_frame);
7787 sraRegionPtr r0, r1, r2, r3;
7788 int sx1, sy1, sw1, sh1, dx, dy;
7789 int bx1, by1, bw1, bh1;
7790 int nr = 0, *nbat = NULL;
7791
7792 if (idx < 0) {
7793 return 0;
7794 }
7795 if (cache_list[idx].bs_x < 0 || cache_list[idx].su_time == 0.0) {
7796 return 0;
7797 }
7798
7799 r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
7800 r1 = sraRgnCreateRect(orig_x, orig_y, orig_x+orig_w, orig_y+orig_h);
7801 r2 = sraRgnCreateRect(x, y, x+w, y+h);
7802
7803 sraRgnAnd(r1, r0);
7804 sraRgnAnd(r2, r0);
7805
7806 if (try_batch) {
7807 nbat = &nr;
7808 }
7809
7810 if (orig_w >= w && orig_h >= h) {
7811
7812if (0) fprintf(stderr, "Shrinking resize %d %dx%d+%d+%d -> %dx%d+%d+%d\n", idx, orig_w, orig_h, orig_x, orig_y, w, h, x, y);
7813 r3 = sraRgnCreateRgn(r1);
7814 sraRgnSubtract(r3, r2);
7815
7816 sx1 = cache_list[idx].su_x;
7817 sy1 = cache_list[idx].su_y;
7818 sw1 = cache_list[idx].su_w;
7819 sh1 = cache_list[idx].su_h;
7820
7821 dx = orig_x - sx1;
7822 dy = orig_y - sy1;
7823
7824 cache_cr(r3, dx, dy, 0.075, 0.05, nbat);
7825 sraRgnDestroy(r3);
7826
7827 r3 = sraRgnCreateRgn(r1);
7828 sraRgnAnd(r3, r2);
7829
7830 dx = sx1 - orig_x;
7831 dy = sy1 - orig_y;
7832 sraRgnOffset(r3, dx, dy);
7833
7834 dx = orig_x - x;
7835 dy = orig_y - y;
7836 sraRgnOffset(r3, dx, dy);
7837
7838 cache_cr(r3, dx, dy, 0.075, 0.05, nbat);
7839 sraRgnDestroy(r3);
7840
7841 if (nr) {
7842 batch_push(nr, -1.0);
7843 }
7844
7845 cache_list[idx].x = x;
7846 cache_list[idx].y = y;
7847 cache_list[idx].width = w;
7848 cache_list[idx].height = h;
7849
7850 cache_list[idx].bs_w = w;
7851 cache_list[idx].bs_h = h;
7852 cache_list[idx].su_w = w;
7853 cache_list[idx].su_h = h;
7854
7855 cache_list[idx].bs_time = 0.0;
7856 /* XXX Y */
7857 if (0) cache_list[idx].su_time = dnow();
7858 } else {
7859if (0) fprintf(stderr, "Growing resize %d %dx%d+%d+%d -> %dx%d+%d+%d\n", idx, orig_w, orig_h, orig_x, orig_y, w, h, x, y);
7860
7861 sx1 = cache_list[idx].su_x;
7862 sy1 = cache_list[idx].su_y;
7863 sw1 = cache_list[idx].su_w;
7864 sh1 = cache_list[idx].su_h;
7865
7866 bx1 = cache_list[idx].bs_x;
7867 by1 = cache_list[idx].bs_y;
7868 bw1 = cache_list[idx].bs_w;
7869 bh1 = cache_list[idx].bs_h;
7870
7871 if (find_rect(idx, x, y, w, h)) {
7872 r3 = sraRgnCreateRgn(r2);
7873 sraRgnAnd(r3, r1);
7874
7875 dx = cache_list[idx].su_x - x;
7876 dy = cache_list[idx].su_y - y;
7877
7878 sraRgnOffset(r3, dx, dy);
7879
7880 dx = dx - (sx1 - orig_x);
7881 dy = dy - (sy1 - orig_y);
7882
7883 cache_cr(r3, dx, dy, 0.075, 0.05, nbat);
7884 sraRgnDestroy(r3);
7885
7886 r3 = sraRgnCreateRgn(r2);
7887 sraRgnSubtract(r3, r1);
7888
7889 dx = cache_list[idx].su_x - x;
7890 dy = cache_list[idx].su_y - y;
7891
7892 sraRgnOffset(r3, dx, dy);
7893
7894 cache_cr(r3, dx, dy, 0.075, 0.05, nbat);
7895 sraRgnDestroy(r3);
7896
7897 if (nr) {
7898 batch_push(nr, -1.0);
7899 }
7900
7901 cache_list[idx].bs_time = 0.0;
7902 /* XXX Y */
7903 if (0) cache_list[idx].su_time = dnow();
7904 }
7905 }
7906
7907 sraRgnDestroy(r0);
7908 sraRgnDestroy(r1);
7909 sraRgnDestroy(r2);
7910
7911 return 1;
7912}
7913
7914int try_to_fix_su(Window win, int idx, Window above, int *nbatch, char *mode) {
7915 int i, idx2, n = 0, found = 0, found_above = 0;
7916 sraRegionPtr r0, r1, r2;
7917 Window win2;
7918 int x, y, w, h, on = 0;
7919 int x0, y0, w0, h0;
7920 int x1, y1, w1, h1;
7921 int x2, y2, w2, h2;
7922 int unmapped = 0;
7923 int moved = 0;
7924
7925
7926 if (mode && !strcmp(mode, "unmapped")) {
7927 unmapped = 1;
7928 } else if (mode && !strcmp(mode, "moved")) {
7929 moved = 1;
7930 }
7931 if (idx < 0) {
7932 return 0;
7933 }
7934if (ncdb) fprintf(stderr, "TRY_TO_FIX_SU(%d) 0x%lx 0x%lx was_unmapped=%d map_state=%s\n", idx, win, above, unmapped, MState(cache_list[idx].map_state));
7935
7936 if (cache_list[idx].map_state != IsViewable && !unmapped) {
7937 return 0;
7938 }
7939 if (cache_list[idx].su_time == 0.0) {
7940 return 0;
7941 }
7942 if (cache_list[idx].bs_x < 0) {
7943 return 0;
7944 }
7945
7946 r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
7947
7948 x = cache_list[idx].x;
7949 y = cache_list[idx].y;
7950 w = cache_list[idx].width;
7951 h = cache_list[idx].height;
7952
7953 r1 = sraRgnCreateRect(x, y, x+w, y+h);
7954
7955 sraRgnAnd(r1, r0);
7956
7957 if (sraRgnEmpty(r1)) {
7958 CLEAN_OUT
7959 return 0;
7960 }
7961
7962 if (unmapped) {
7963 on = 1;
7964 }
7965 if (above == 0x1) {
7966 on = 1;
7967 }
7968 for (i = old_stack_n - 1; i >= 0; i--) {
7969 win2 = old_stack[i];
7970 if (win2 == above) {
7971if (0) fprintf(stderr, "0x%lx turn on: 0x%lx i=%d\n", win, win2, i);
7972 on = 1;
7973 found_above = 1;
7974 }
7975 if (win2 == win) {
7976if (0) fprintf(stderr, "0x%lx turn off: 0x%lx i=%d\n", win, win2, i);
7977 found = 1;
7978 on = 0;
7979 break;
7980 }
7981 if (! on) {
7982 continue;
7983 }
7984 idx2 = lookup_win_index(win2);
7985 if (idx2 < 0) {
7986 continue;
7987 }
7988 if (cache_list[idx2].map_state != IsViewable) {
7989 continue;
7990 }
7991 if (cache_list[idx2].bs_x < 0) {
7992 continue;
7993 }
7994 /* XXX Invalidate? */
7995
7996 x2 = cache_list[idx2].x;
7997 y2 = cache_list[idx2].y;
7998 w2 = cache_list[idx2].width;
7999 h2 = cache_list[idx2].height;
8000
8001 r2 = sraRgnCreateRect(x2, y2, x2+w2, y2+h2);
8002 sraRgnAnd(r2, r0);
8003 if (! sraRgnAnd(r2, r1)) {
8004 sraRgnDestroy(r2);
8005 continue;
8006 }
8007
8008 tmp_reg[n] = r2;
8009 tmp_stack[n++] = idx2;
8010 }
8011
8012 if (! found) {
8013 CLEAN_OUT
8014 return 0;
8015 }
8016
8017 for (i = n - 1; i >= 0; i--) {
8018 int i2;
8019 r2 = sraRgnCreateRgn(tmp_reg[i]);
8020 for (i2 = i + 1; i2 < n; i2++) {
8021 sraRgnSubtract(r2, tmp_reg[i2]);
8022 }
8023 idx2 = tmp_stack[i];
8024 if (!sraRgnEmpty(r2)) {
8025 int dx, dy;
8026 int dx2, dy2;
8027
8028 x0 = cache_list[idx2].x;
8029 y0 = cache_list[idx2].y;
8030 w0 = cache_list[idx2].width;
8031 h0 = cache_list[idx2].height;
8032
8033 x1 = cache_list[idx].su_x; /* SU -> SU */
8034 y1 = cache_list[idx].su_y;
8035 w1 = cache_list[idx].su_w;
8036 h1 = cache_list[idx].su_h;
8037
8038 x2 = cache_list[idx2].su_x;
8039 y2 = cache_list[idx2].su_y;
8040 w2 = cache_list[idx2].su_w;
8041 h2 = cache_list[idx2].su_h;
8042
8043 dx = x2 - x0;
8044 dy = y2 - y0;
8045 sraRgnOffset(r2, dx, dy);
8046
8047 dx2 = x1 - x;
8048 dy2 = y1 - y;
8049 dx = dx - dx2;
8050 dy = dy - dy2;
8051 cache_cr(r2, dx, dy, save_delay0, save_delay1, nbatch);
8052 }
8053 sraRgnDestroy(r2);
8054 }
8055
8056 if (unmapped) {
8057 CLEAN_OUT
8058 return found_above;
8059 }
8060
8061 for (i = n - 1; i >= 0; i--) {
8062 r2 = sraRgnCreateRgn(tmp_reg[i]);
8063 idx2 = tmp_stack[i];
8064 if (!sraRgnEmpty(r2)) {
8065 int dx, dy;
8066 int dx2, dy2;
8067
8068 x0 = cache_list[idx2].x;
8069 y0 = cache_list[idx2].y;
8070 w0 = cache_list[idx2].width;
8071 h0 = cache_list[idx2].height;
8072
8073 x1 = cache_list[idx].su_x; /* BS -> SU */
8074 y1 = cache_list[idx].su_y;
8075 w1 = cache_list[idx].su_w;
8076 h1 = cache_list[idx].su_h;
8077
8078 x2 = cache_list[idx2].bs_x;
8079 y2 = cache_list[idx2].bs_y;
8080 w2 = cache_list[idx2].bs_w;
8081 h2 = cache_list[idx2].bs_h;
8082
8083 dx = x1 - x;
8084 dy = y1 - y;
8085 sraRgnOffset(r2, dx, dy);
8086
8087 dx2 = x2 - x0;
8088 dy2 = y2 - y0;
8089 dx = dx - dx2;
8090 dy = dy - dy2;
8091 cache_cr(r2, dx, dy, save_delay0, save_delay1, nbatch);
8092 }
8093 sraRgnDestroy(r2);
8094 }
8095
8096 CLEAN_OUT
8097 return found_above;
8098}
8099
8100void idx_add_rgn(sraRegionPtr r, sraRegionPtr r0, int idx) {
8101 int x, y, w, h;
8102 sraRegionPtr rtmp;
8103
8104 if (idx < 0) {
8105 return;
8106 }
8107 x = cache_list[idx].x;
8108 y = cache_list[idx].y;
8109 w = cache_list[idx].width;
8110 h = cache_list[idx].height;
8111
8112 rtmp = sraRgnCreateRect(x, y, w, h);
8113 if (r0) {
8114 sraRgnAnd(rtmp, r0);
8115 }
8116 sraRgnOr(r, rtmp);
8117 sraRgnDestroy(rtmp);
8118}
8119
8120sraRegionPtr idx_create_rgn(sraRegionPtr r0, int idx) {
8121 int x, y, w, h;
8122 sraRegionPtr rtmp;
8123
8124 if (idx < 0) {
8125 return NULL;
8126 }
8127 x = cache_list[idx].x;
8128 y = cache_list[idx].y;
8129 w = cache_list[idx].width;
8130 h = cache_list[idx].height;
8131
8132 rtmp = sraRgnCreateRect(x, y, w, h);
8133 if (r0) {
8134 sraRgnAnd(rtmp, r0);
8135 }
8136 return rtmp;
8137}
8138
8139void scale_mark_xrootpmap(void) {
8140 char *dst_fb, *src_fb = main_fb;
8141 int dst_bpl, Bpp = bpp/8, fac = 1;
8142 int yn = (ncache+1) * dpy_y;
8143 int yfac = (ncache+2);
8144 int mark = 1;
8145
8146 if (!scaling || !rfb_fb || rfb_fb == main_fb) {
8147 mark_rect_as_modified(0, yn, dpy_x, yn + dpy_y, 0);
8148 return;
8149 }
8150
8151 if (cmap8to24 && cmap8to24_fb) {
8152 src_fb = cmap8to24_fb;
8153 if (scaling) {
8154 if (depth <= 8) {
8155 fac = 4;
8156 } else if (depth <= 16) {
8157 fac = 2;
8158 }
8159 }
8160 }
8161 dst_fb = rfb_fb;
8162 dst_bpl = rfb_bytes_per_line;
8163
8164 scale_rect(scale_fac_x, scale_fac_y, scaling_blend, scaling_interpolate, fac * Bpp,
8165 src_fb, fac * main_bytes_per_line, dst_fb, dst_bpl, dpy_x, yfac * dpy_y,
8166 scaled_x, yfac * scaled_y, 0, yn, dpy_x, yn + dpy_y, mark);
8167}
8168
8169void set_ncache_xrootpmap(void) {
8170 Atom pmap, type;
8171 int format;
8172 unsigned long length, after;
8173 XImage *image = NULL;
8174 XErrorHandler old_handler;
8175
8176 RAWFB_RET_VOID
8177#if !NO_X11
8178 if (!ncache) {
8179 return;
8180 }
8181 X_LOCK;
8182 old_handler = XSetErrorHandler(trap_xerror);
8183 trapped_xerror = 0;
8184 pmap = XInternAtom(dpy, "_XROOTPMAP_ID", True);
8185
8186 if (use_solid_bg) {
8187 image = solid_image(NULL);
8188 if (!quiet) {
8189 rfbLog("set_ncache_xrootpmap: solid_image\n");
8190 }
8191 } else if (pmap != None) {
8192 Pixmap pixmap = None;
8193 unsigned char *d_pmap;
8194
8195 XGetWindowProperty(dpy, rootwin, pmap, 0L, 1L, False,
8196 AnyPropertyType, &type, &format, &length, &after, &d_pmap);
8197
8198 if (length != 0) {
8199 pixmap = *((Pixmap *) d_pmap);
8200 if (pixmap != None) {
8201 image = XGetImage(dpy, pixmap, 0, 0, dpy_x, dpy_y, AllPlanes, ZPixmap);
8202 }
8203 }
8204 if (!quiet) {
8205 rfbLog("set_ncache_xrootpmap: loading background pixmap: 0x%lx\n", pixmap);
8206 }
8207 } else {
8208 if (!quiet) {
8209 rfbLog("set_ncache_xrootpmap: trying root background\n");
8210 }
8211 }
8212 if (image == NULL) {
8213 image = solid_root((char *) 0x1);
8214 }
8215 if (image != NULL) {
8216 char *src, *dst;
8217 int line;
8218 int pixelsize = bpp/8;
8219 int y1 = dpy_y * (ncache+1);
8220
8221 src = image->data;
8222 dst = main_fb + y1 * main_bytes_per_line;
8223 line = 0;
8224 while (line++ < dpy_y) {
8225 memcpy(dst, src, dpy_x * pixelsize);
8226 src += image->bytes_per_line;
8227 dst += main_bytes_per_line;
8228 }
8229 XDestroyImage(image);
8230 X_UNLOCK;
8231 scale_mark_xrootpmap();
8232 X_LOCK;
8233 } else {
8234 int yn = (ncache+1) * dpy_y;
8235 zero_fb(0, yn, dpy_x, yn + dpy_y);
8236 }
8237 XSetErrorHandler(old_handler);
8238 X_UNLOCK;
8239#endif
8240}
8241
8242#define EVLISTMAX 256
8243#define EV_RESET 0
8244#define EV_CREATE 1
8245#define EV_DESTROY 2
8246#define EV_UNMAP 3
8247#define EV_MAP 4
8248#define EV_REPARENT 5
8249#define EV_CONFIGURE 6
8250#define EV_CONFIGURE_SIZE 7
8251#define EV_CONFIGURE_POS 8
8252#define EV_CONFIGURE_STACK 9
8253#define EV_VISIBILITY_UNOBS 10
8254#define EV_VISIBILITY_OBS 11
8255#define EV_PROPERTY 12
8256#define EV_OLD_WM_MAP 13
8257#define EV_OLD_WM_UNMAP 14
8258#define EV_OLD_WM_OFF 15
8259#define EV_OLD_WM_NOTMAPPED 16
8260Window _ev_list[EVLISTMAX];
8261int _ev_case[EVLISTMAX];
8262int _ev_list_cnt;
8263
8264int n_CN = 0, n_RN = 0, n_DN = 0, n_ON = 0, n_MN = 0, n_UN = 0;
8265int n_VN = 0, n_VN_p = 0, n_VN_u = 0, n_ST = 0, n_PN = 0, n_DC = 0;
8266int n_ON_sz = 0, n_ON_po = 0, n_ON_st = 0;
8267
8268int ev_store(Window win, int type) {
8269 if (type == EV_RESET) {
8270 n_CN = 0; n_RN = 0; n_DN = 0; n_ON = 0; n_MN = 0; n_UN = 0;
8271 n_VN = 0; n_VN_p = 0; n_VN_u = 0; n_ST = 0; n_PN = 0; n_DC = 0;
8272 n_ON_sz = 0; n_ON_po = 0; n_ON_st = 0;
8273 _ev_list_cnt = 0;
8274 return 1;
8275 }
8276 if (_ev_list_cnt >= EVLISTMAX) {
8277 return 0;
8278 }
8279 _ev_list[_ev_list_cnt] = win;
8280 _ev_case[_ev_list_cnt++] = type;
8281 return 1;
8282}
8283
8284int ev_lookup(Window win, int type) {
8285 int i;
8286 for(i=0; i < _ev_list_cnt; i++) {
8287 if (_ev_list[i] == win && _ev_case[i] == type) {
8288 return 1;
8289 }
8290 }
8291 return 0;
8292}
8293
8294unsigned long all_ev = SubstructureNotifyMask|StructureNotifyMask|VisibilityChangeMask;
8295unsigned long win_ev = StructureNotifyMask|VisibilityChangeMask;
8296
8297void read_events(int *n_in) {
8298 int n = *n_in;
8299 Window win, win2;
8300 XEvent ev;
8301
8302 while (xcheckmaskevent(dpy, all_ev, &Ev[n])) {
8303 int cfg_size = 0;
8304 int cfg_pos = 0;
8305 int cfg_stack = 0;
8306 int type = Ev[n].type;
8307 Window w = None;
8308
8309 win = Ev[n].xany.window;
8310 Ev_done[n] = 0;
8311 Ev_area[n] = 0;
8312 Ev_win[n] = win;
8313 Ev_map[n] = None;
8314 Ev_unmap[n] = None;
8315 Ev_order[n] = n;
8316
8317 ev = Ev[n];
8318
8319 if (type == DestroyNotify) w = Ev[n].xcreatewindow.window;
8320 if (type == CreateNotify) w = Ev[n].xdestroywindow.window;
8321 if (type == ReparentNotify) w = Ev[n].xreparent.window;
8322 if (type == UnmapNotify) w = Ev[n].xunmap.window;
8323 if (type == MapNotify) w = Ev[n].xmap.window;
8324 if (type == Expose) w = Ev[n].xexpose.window;
8325 if (type == ConfigureNotify) w = Ev[n].xconfigure.window;
8326 if (type == VisibilityNotify) w = win;
8327 if (n == *n_in && ncdb) fprintf(stderr, "\n");
8328 if (1) {
8329 char *msg = "";
8330 int idx = -1, x = 0, y = 0, wd = 0, ht = 0;
8331 if (w != None) {
8332 idx = lookup_win_index(w);
8333 if (idx >= 0) {
8334 x = cache_list[idx].x;
8335 y = cache_list[idx].y;
8336 wd = cache_list[idx].width;
8337 ht = cache_list[idx].height;
8338 }
8339 }
8340 if (type == VisibilityNotify) {
8341 msg = VState(Ev[n].xvisibility.state);
8342 } else if (type == ConfigureNotify) {
8343 int x_new = Ev[n].xconfigure.x;
8344 int y_new = Ev[n].xconfigure.y;
8345 int w_new = Ev[n].xconfigure.width;
8346 int h_new = Ev[n].xconfigure.height;
8347 if (idx >= 0) {
8348 if (w_new != wd || h_new != ht) {
8349 msg = "change size";
8350 cfg_size = 1;
8351 }
8352 if (x_new != x || y_new != y) {
8353 if (!strcmp(msg, "")) {
8354 msg = "change position";
8355 }
8356 cfg_pos = 1;
8357 } else if (! cfg_size) {
8358 msg = "change stacking";
8359 cfg_stack = 1;
8360 }
8361 }
8362 }
8363
8364 if (ncdb) fprintf(stderr, "----- %02d inputev 0x%08lx w: 0x%08lx %04dx%04d+%04d+%04d %s %s\n", n, win, w, wd, ht, x, y, Etype(type), msg);
8365 }
8366
8367 if (win == rootwin) {
8368 if (type == CreateNotify) {
8369 win2 = ev.xcreatewindow.window;
8370 ev_store(win2, EV_CREATE);
8371 n++;
8372 n_CN++;
8373 } else if (type == ReparentNotify) {
8374 if (ev.xreparent.parent != rootwin) {
8375 win2 = ev.xreparent.window;
8376 if (win2 != rootwin) {
8377 ev_store(win2, EV_REPARENT);
8378 }
8379 }
8380 n++;
8381 n_RN++;
8382 } else if (type == PropertyNotify) {
8383 set_prop_atom(Ev[n].xproperty.atom);
8384 n++;
8385 n_PN++;
8386 } else if (type == MapNotify) {
8387 win2 = ev.xmap.window;
8388 ev_store(win2, EV_MAP);
8389 n++;
8390 n_CN++;
8391 } else {
8392 /* skip rest */
8393#if 0
8394 Window w = None;
8395if (type == DestroyNotify) w = Ev[n].xdestroywindow.window;
8396if (type == UnmapNotify) w = Ev[n].xunmap.window;
8397if (type == MapNotify) w = Ev[n].xmap.window;
8398if (type == Expose) w = Ev[n].xexpose.window;
8399if (type == ConfigureNotify) w = Ev[n].xconfigure.window;
8400if (type != ConfigureNotify) fprintf(stderr, "root: skip %s for 0x%lx\n", Etype(type), w);
8401#endif
8402
8403 }
8404 } else {
8405 if (type == ReparentNotify) {
8406 ev_store(win, EV_REPARENT);
8407 n++;
8408 n_RN++;
8409 } else if (type == DestroyNotify) {
8410 ev_store(win, EV_DESTROY);
8411 n++;
8412 n_DN++;
8413 } else if (type == ConfigureNotify) {
8414 ev_store(win, EV_CONFIGURE);
8415 if (cfg_size) {
8416 ev_store(win, EV_CONFIGURE_SIZE);
8417 n_ON_sz++;
8418 }
8419 if (cfg_pos) {
8420 ev_store(win, EV_CONFIGURE_POS);
8421 n_ON_po++;
8422 }
8423 if (cfg_stack) {
8424 ev_store(win, EV_CONFIGURE_STACK);
8425 n_ON_st++;
8426 }
8427 n++;
8428 n_ON++;
8429 } else if (type == VisibilityNotify) {
8430 if (Ev[n].xvisibility.state == VisibilityUnobscured) {
8431 ev_store(win, EV_VISIBILITY_UNOBS);
8432 n_VN_u++;
8433 } else {
8434 ev_store(win, EV_VISIBILITY_OBS);
8435 n_VN_p++;
8436 }
8437 n++;
8438 n_VN++;
8439 } else if (type == MapNotify) {
8440 ev_store(win, EV_MAP);
8441 Ev_map[n] = win;
8442 n++;
8443 n_MN++;
8444 } else if (type == UnmapNotify) {
8445 ev_store(win, EV_UNMAP);
8446 Ev_unmap[n] = win;
8447 n++;
8448 n_UN++;
8449 } else {
8450 /* skip rest */
8451if (ncdb) fprintf(stderr, "----- skip %s\n", Etype(type));
8452 }
8453 }
8454 if (n >= EVMAX) {
8455 break;
8456 }
8457 }
8458 *n_in = n;
8459}
8460
8461int try_to_synthesize_su(int force, int urgent, int *nbatch) {
8462 int i, idx, idx2, n = 0;
8463 sraRegionPtr r0, r1, r2;
8464 Window win = None;
8465 int x0, y0, w0, h0;
8466 int x1, y1, w1, h1;
8467 int x2, y2, w2, h2;
8468 int x3, y3, w3, h3;
8469 XWindowAttributes attr;
8470
8471 r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
8472
8473 snap_old();
8474
8475 X_LOCK;
8476 for (i = old_stack_n - 1; i >= 0; i--) {
8477 win = old_stack[i];
8478 if (urgent) { /* XXX Y resp */
8479 if (!valid_window(win, &attr, 1)) {
8480 continue;
8481 }
8482 idx = lookup_win_index(win);
8483 if (idx >= 0) {
8484 STORE(idx, win, attr);
8485 }
8486 } else {
8487 idx = lookup_win_index(win);
8488 if (idx >= 0) {
8489 attr.map_state = cache_list[idx].map_state;
8490 attr.x = cache_list[idx].x;
8491 attr.y = cache_list[idx].y;
8492 attr.width = cache_list[idx].width;
8493 attr.height = cache_list[idx].height;
8494 } else {
8495 attr.map_state = IsUnmapped;
8496 attr.x = 0;
8497 attr.y = 0;
8498 attr.width = 0;
8499 attr.height = 0;
8500 }
8501
8502 }
8503 if (attr.map_state != IsViewable) {
8504 continue;
8505 }
8506if (0) fprintf(stderr, "win: 0x%lx %d idx=%d\n", win, i, idx);
8507
8508 x2 = attr.x;
8509 y2 = attr.y;
8510 w2 = attr.width;
8511 h2 = attr.height;
8512
8513 r2 = sraRgnCreateRect(x2, y2, x2+w2, y2+h2);
8514 sraRgnAnd(r2, r0);
8515
8516 tmp_reg[n] = r2;
8517 tmp_stack[n++] = idx;
8518 }
8519 X_UNLOCK;
8520
8521 if (! n) {
8522 r1 = NULL;
8523 CLEAN_OUT
8524 return 0;
8525 }
8526
8527 for (i = 0; i < n; i++) {
8528 int i2, cnt = 0;
8529 idx = tmp_stack[i];
8530 if (idx < 0 || cache_list[idx].bs_x < 0) {
8531 continue;
8532 }
8533 r1 = tmp_reg[i];
8534 if (r1 == NULL || sraRgnEmpty(r1)) {
8535 continue;
8536 }
8537 if (cache_list[idx].su_time > 0.0) {
8538 if (force) {
8539if (ncdb) fprintf(stderr, "forcing synth: 0x%lx %d\n", cache_list[idx].win, idx);
8540 } else {
8541 continue;
8542 }
8543 }
8544 if (ncache_xrootpmap) {
8545 int dx, dy;
8546
8547 x0 = cache_list[idx].x;
8548 y0 = cache_list[idx].y;
8549 w0 = cache_list[idx].width;
8550 h0 = cache_list[idx].height;
8551
8552 x1 = cache_list[idx].su_x;
8553 y1 = cache_list[idx].su_y;
8554 w1 = cache_list[idx].su_w;
8555 h1 = cache_list[idx].su_h;
8556
8557 r2 = sraRgnCreateRgn(tmp_reg[i]);
8558 dx = x1 - x0;
8559 dy = y1 - y0;
8560
8561 sraRgnOffset(r2, dx, dy);
8562
8563 x2 = x0;
8564 y2 = y0 + (ncache+1) * dpy_y;
8565
8566 dx = x1 - x2;
8567 dy = y1 - y2;
8568 cache_cr(r2, dx, dy, save_delay0, save_delay1, nbatch);
8569 cnt++;
8570
8571 sraRgnDestroy(r2);
8572 }
8573
8574 for (i2 = n - 1; i2 > i; i2--) {
8575 r2 = sraRgnCreateRgn(tmp_reg[i2]);
8576 if (sraRgnAnd(r2, r1)) {
8577 int dx, dy;
8578 int dx2, dy2;
8579
8580 idx2 = tmp_stack[i2];
8581 /* XXX Y */
8582 if (idx2 < 0 || cache_list[idx2].bs_x < 0 || cache_list[idx2].bs_time == 0.0) {
8583 continue;
8584 }
8585
8586 x0 = cache_list[idx].x;
8587 y0 = cache_list[idx].y;
8588 w0 = cache_list[idx].width;
8589 h0 = cache_list[idx].height;
8590
8591 x1 = cache_list[idx].su_x;
8592 y1 = cache_list[idx].su_y;
8593 w1 = cache_list[idx].su_w;
8594 h1 = cache_list[idx].su_h;
8595
8596 x2 = cache_list[idx2].x;
8597 y2 = cache_list[idx2].y;
8598 w2 = cache_list[idx2].width;
8599 h2 = cache_list[idx2].height;
8600
8601 x3 = cache_list[idx2].bs_x;
8602 y3 = cache_list[idx2].bs_y;
8603 w3 = cache_list[idx2].bs_w;
8604 h3 = cache_list[idx2].bs_h;
8605
8606 dx = x1 - x0;
8607 dy = y1 - y0;
8608 sraRgnOffset(r2, dx, dy);
8609
8610 dx2 = x3 - x2;
8611 dy2 = y3 - y2;
8612 dx = dx - dx2;
8613 dy = dy - dy2;
8614 cache_cr(r2, dx, dy, save_delay0, save_delay1, nbatch);
8615 cnt++;
8616 }
8617 sraRgnDestroy(r2);
8618 }
8619 if (cnt) {
8620 cache_list[idx].su_time = dnow();
8621 }
8622if (ncdb) fprintf(stderr, " try_to_synth_su: 0x%lx %d idx=%d cnt=%d\n", win, i, idx, cnt);
8623 }
8624
8625 r1 = NULL;
8626 CLEAN_OUT
8627 return 1;
8628}
8629
8630static double last_vis_unobs_time = 0.0;
8631static double last_vis_obs_time = 0.0;
8632
8633static int saw_desktop_change = 0;
8634
8635void check_sched(int try_batch, int *did_sched) {
8636 static double last_root = 0.0;
8637 static double last_pixmap = 0.0;
8638 double refresh = 60.0;
8639 int i, k, valid;
8640 Window win;
8641 XWindowAttributes attr;
8642 double now = dnow();
8643
8644 if (now > last_root + refresh) {
8645
8646if (ncdb) fprintf(stderr, "\n**** checking cache_list[%d]\n\n", cache_list_num);
8647 block_stats();
8648
8649 for(k=0; k<cache_list_num; k++) {
8650 valid = 0;
8651 win = cache_list[k].win;
8652 X_LOCK;
8653 if (win == None) {
8654 ;
8655 } else if (cache_list[k].selectinput && cache_list[k].time > now - refresh) {
8656 valid = 1;
8657 } else if (valid_window(win, &attr, 1)) {
8658 STORE(k, win, attr);
8659 if (! cache_list[k].selectinput) {
8660 xselectinput(win, win_ev, 0);
8661 CLEAR(k);
8662 cache_list[k].selectinput = 1;
8663 }
8664 valid = 1;
8665 } else {
8666if (ncdb) fprintf(stderr, "DELETE(%d) %dx%d+%d+%d\n", k, cache_list[k].width, cache_list[k].height, cache_list[k].x, cache_list[k].y);
8667 DELETE(k);
8668 }
8669 X_UNLOCK;
8670/* XXX Y */
8671 if (valid) {
8672 if (cache_list[k].create_cnt && cache_list[k].map_state != IsViewable && cache_list[k].map_cnt == 0) {
8673 if (cache_list[k].bs_x >= 0) {
8674if (ncdb) fprintf(stderr, "Created window never mapped: freeing(%d) 0x%lx\n", k, win);
8675 free_rect(k);
8676 }
8677 }
8678 }
8679 }
8680 last_root = dnow();
8681 }
8682
8683 if (now > last_sched_bs + 0.30) {
8684 static double last_sched_vis = 0.0;
8685 int nr = 0, *bat = NULL;
8686
8687 if (try_batch) {
8688 bat = &nr;
8689 }
8690 if (now < last_wireframe + 2.0) {
8691 for (i=0; i < NSCHED; i++) {
8692 sched_bs[i] = None;
8693 }
8694 }
8695 if (now < last_get_wm_frame_time + 1.0) {
8696 if (last_get_wm_frame != None) {
8697 int idx = lookup_win_index(last_get_wm_frame);
8698 if (idx >= 0) {
8699 if (cache_list[idx].bs_x < 0) {
8700 int x = cache_list[idx].x;
8701 int y = cache_list[idx].y;
8702 int w = cache_list[idx].width;
8703 int h = cache_list[idx].height;
8704 if (find_rect(idx, x, y, w, h)) {
8705 SCHED(last_get_wm_frame, 1);
8706 }
8707 }
8708 }
8709 }
8710 }
8711
8712 for (i=0; i < NSCHED; i++) {
8713 if (sched_bs[i] != None) {
8714 int idx;
8715 win = sched_bs[i];
8716 if (now < sched_tm[i] + 0.55) {
8717 continue;
8718 }
8719 if (n_MN || n_UN || n_ST || n_DC) {
8720 sched_tm[i] = now;
8721 continue;
8722 }
8723 idx = lookup_win_index(win);
8724 if (idx >= 0) {
8725 int aw = cache_list[idx].width;
8726 int ah = cache_list[idx].height;
8727 if (cache_list[idx].map_state != IsViewable) {
8728 ;
8729 } else if (cache_list[idx].vis_state != VisibilityUnobscured) {
8730 ;
8731 } else if (aw * ah < 64 * 64) {
8732 ;
8733 } else {
8734if (ncdb) fprintf(stderr, "*SNAP BS_save: 0x%lx %d %d %d\n", win, aw, ah, cache_list[idx].map_state);
8735 valid = 0;
8736 bs_save(idx, bat, &attr, 1, 0, &valid, 0);
8737 if (valid) {
8738 STORE(idx, win, attr);
8739 } else {
8740 DELETE(idx);
8741 }
8742 }
8743 } else {
8744if (ncdb) fprintf(stderr, "*SCHED LOOKUP FAIL: i=%d 0x%lx\n", i, win);
8745 }
8746 }
8747 sched_bs[i] = None;
8748 }
8749 *did_sched = 1;
8750
8751 if (n_MN || n_UN || n_ST || n_DC) {
8752 if (last_sched_vis < now) {
8753 last_sched_vis += 1.0;
8754 }
8755 } else if (now > last_sched_vis + 3.0 && now > last_wireframe + 2.0) {
8756 static double last_vis = 0.0;
8757 int vis_now[32], top_now[32];
8758 static int vis_prev[32], freq = 0;
8759 int diff, nv = 32, vis_now_n = 0;
8760 Window win;
8761
8762 freq++;
8763
8764 for (i=0; i < cache_list_num; i++) {
8765 int ok = 0;
8766 int top_only = 1;
8767 int aw = cache_list[i].width;
8768 int ah = cache_list[i].height;
8769 int map_prev = cache_list[i].map_state;
8770
8771 win = cache_list[i].win;
8772
8773 if (saw_desktop_change) {
8774 top_only = 0;
8775 }
8776
8777 if (win == None) {
8778 continue;
8779 }
8780 /* XXX Y resp */
8781 if (saw_desktop_change || freq % 5 == 0) {
8782 int vret = 0;
8783 X_LOCK;
8784 vret = valid_window(win, &attr, 1);
8785 X_UNLOCK;
8786 if (!vret) {
8787 continue;
8788 }
8789 STORE(i, win, attr);
8790 }
8791 if (!cache_list[i].valid) {
8792 continue;
8793 }
8794 if (cache_list[i].map_state != IsViewable) {
8795 continue;
8796 }
8797 if (cache_list[i].vis_state == VisibilityFullyObscured) {
8798 continue;
8799 }
8800 if (map_prev != IsViewable) {
8801 /* we hope to catch it below in the normal event processing */
8802 continue;
8803 }
8804 if (aw * ah < 64 * 64) {
8805 continue;
8806 }
8807 if (top_only) {
8808 if (cache_list[i].vis_state == VisibilityUnobscured) {
8809 ok = 1;
8810 } else if (!clipped(i)) {
8811 ok = 1;
8812 }
8813 } else {
8814 ok = 1;
8815 }
8816 if (ok) {
8817 if (vis_now_n < nv) {
8818 vis_now[vis_now_n] = i;
8819 top_now[vis_now_n++] = top_only;
8820 }
8821 }
8822 }
8823 diff = 0;
8824 for (k = 0; k < vis_now_n; k++) {
8825 if (vis_now[k] != vis_prev[k]) {
8826 diff = 1;
8827 }
8828 }
8829 if (diff == 0) {
8830 if (now > last_vis + 45.0) {
8831 diff = 1;
8832 }
8833 }
8834 if (diff) {
8835if (ncdb && vis_now_n) fprintf(stderr, "*VIS snapshot all %.4f\n", dnowx());
8836 for (k = 0; k < vis_now_n; k++) {
8837 i = vis_now[k];
8838 win = cache_list[i].win;
8839 valid = 0;
8840if (ncdb) fprintf(stderr, "*VIS BS_save: 0x%lx %d %d %d\n", win, cache_list[i].width, cache_list[i].height, cache_list[i].map_state);
8841 if (now < cache_list[i].vis_unobs_time + 0.75 && now < cache_list[i].vis_obs_time + 0.75) {
8842 continue;
8843 }
8844 bs_save(i, bat, &attr, !top_now[k], 0, &valid, 1);
8845 if (valid) {
8846 STORE(i, win, attr);
8847 } else {
8848 DELETE(i);
8849 }
8850 vis_prev[k] = vis_now[k];
8851 }
8852 last_vis = dnow();
8853 }
8854 last_sched_vis = dnow();
8855 if (! n_DC) {
8856 saw_desktop_change = 0;
8857 }
8858 /* XXX Y */
8859 try_to_synthesize_su(0, 0, bat);
8860 }
8861
8862 if (nr) {
8863 batch_push(nr, -1.0);
8864 }
8865 last_sched_bs = dnow();
8866 }
8867#if !NO_X11
8868 if (dpy && atom_XROOTPMAP_ID == None && now > last_pixmap + 5.0) {
8869 atom_XROOTPMAP_ID = XInternAtom(dpy, "_XROOTPMAP_ID", True);
8870 last_pixmap = now;
8871 }
8872#endif
8873 if (got_XROOTPMAP_ID > 0.0) {
8874if (ncdb) fprintf(stderr, "got_XROOTPMAP_ID\n");
8875 if (ncache_xrootpmap) {
8876 set_ncache_xrootpmap();
8877 }
8878 got_XROOTPMAP_ID = 0.0;
8879 }
8880}
8881
8882int check_ncache(int reset, int mode) {
8883 static int first = 1;
8884 static int last_client_count = -1;
8885 int i, k, n;
8886 int did_sched = 0;
8887
8888 Window win, win2;
8889 XWindowAttributes attr;
8890 int valid;
8891 int try_batch = 1; /* XXX Y */
8892 int use_batch = 0;
8893 int nreg = 0, *nbatch;
8894 int create_cnt;
8895 int su_fix_cnt;
8896 int pixels = 0, ttot;
8897 int desktop_change = 0, n1, n2;
8898 int desktop_change_old_wm = 0;
8899 int missed_su_restore = 0;
8900 int missed_bs_restore = 0;
8901 sraRegionPtr r0, r;
8902 sraRegionPtr missed_su_restore_rgn;
8903 sraRegionPtr missed_bs_restore_rgn;
8904 sraRegionPtr unmapped_rgn;
8905
8906 int nrects = 0;
8907 int nsave, nxsel;
8908 double now;
8909
8910 int skipwins_n = 0;
8911 int skipwins_max = 256;
8912 Window skipwins[256];
8913
8914 static char *dt_guess = NULL;
8915 static double dt_last = 0.0;
8916 int dt_gnome = 0, gnome_animation = 0;
8917 int dt_kde = 0;
8918
8919 if (unixpw_in_progress) return -1;
8920
8921#ifdef MACOSX
8922 if (! macosx_console) {
8923 RAWFB_RET(-1)
8924 }
8925 if (! screen) {
8926 return -1;
8927 }
8928#else
8929 RAWFB_RET(-1)
8930 if (! screen || ! dpy) {
8931 return -1;
8932 }
8933#endif
8934
8935 now = dnow();
8936
8937#ifdef NO_NCACHE
8938 ncache = 0;
8939#endif
8940
8941 if (reset && (first || cache_list_len == 0)) {
8942 return -1;
8943 }
8944 if (use_threads) {
8945 try_batch = 0;
8946 }
8947
8948 if (ncache0) {
8949 if (reset) {
8950 ;
8951 } else if (!client_count || !ncache || nofb) {
8952 static double last_purge = 0.0;
8953 double delay = client_count ? 0.5 : 2.0;
8954 if (now > last_purge + delay) {
8955 int c = 0;
8956 XEvent ev;
8957 X_LOCK;
8958 while (xcheckmaskevent(dpy, all_ev, &ev)) {
8959 c++;
8960 }
8961 X_UNLOCK;
8962 last_purge = dnow();
8963if (ncdb && c) fprintf(stderr, "check_ncache purged %d events\n", c);
8964 }
8965 if (!client_count && last_client_count >= 0 &&
8966 client_count != last_client_count) {
8967 /* this should use less RAM when no clients */
8968 do_new_fb(1);
8969 }
8970 last_client_count = client_count;
8971 return -1;
8972 }
8973 }
8974 last_client_count = client_count;
8975
8976 if (ncache && ! ncache0) {
8977 ncache0 = ncache;
8978 }
8979
8980 if (! ncache || ! ncache0) {
8981 return -1;
8982 }
8983 if (subwin) {
8984 return -1;
8985 }
8986 if (nofb) {
8987 return -1;
8988 }
8989 if (now < last_client + 4) {
8990 return -1;
8991 }
8992 if (! all_clients_initialized()) {
8993 /* play it safe */
8994 return -1;
8995 }
8996
8997
8998
8999 if (reset) {
9000 rfbLog("check_ncache: resetting cache: %d/%d %d %d\n", cache_list_num, cache_list_len, ncache, first);
9001 for (i=0; i < cache_list_num; i++) {
9002 free_rect(i);
9003 }
9004 for (n = 1; n <= ncache; n++) {
9005 if (rect_reg[n] != NULL) {
9006 sraRgnDestroy(rect_reg[n]);
9007 rect_reg[n] = NULL;
9008 }
9009 }
9010 zero_fb(0, dpy_y, dpy_x, (ncache+1)*dpy_y);
9011 mark_rect_as_modified(0, dpy_y, dpy_x, (ncache+1)*dpy_y, 0);
9012
9013 if (ncache_xrootpmap) {
9014 set_ncache_xrootpmap();
9015 }
9016
9017 snap_old();
9018 return -1;
9019 }
9020
9021 if (first) {
9022 int dx = 10, dy = 24, ds = 0;
9023 int Dx = dpy_x, Dy = dpy_y;
9024 first = 0;
9025 for (i=0; i < NRECENT; i++) {
9026 recent[i] = None;
9027 }
9028 for (i=0; i < NSCHED; i++) {
9029 sched_bs[i] = None;
9030 }
9031 rlast = 0;
9032
9033 X_LOCK;
9034 /* event leak with client_count == 0 */
9035 xselectinput_rootwin |= SubstructureNotifyMask;
9036 XSelectInput_wr(dpy, rootwin, xselectinput_rootwin);
9037 X_UNLOCK;
9038
9039 if (scaling) {
9040 Dx = scaled_x;
9041 Dy = scaled_y;
9042 }
9043 if (!rotating_same) {
9044 int t = Dx;
9045 Dx = Dy;
9046 Dy = t;
9047 }
9048
9049 for (i = 0; i < 3; i++) {
9050 rfbDrawString(screen, &default8x16Font, dx, ds + Dy+1*dy,
9051 "This is the Pixel buffer cache region. Your VNC Viewer is not hiding it from you.",
9052 white_pixel());
9053 rfbDrawString(screen, &default8x16Font, dx, ds + Dy+2*dy,
9054 "Try resizing your VNC Viewer so you don't see it!!",
9055 white_pixel());
9056 rfbDrawString(screen, &default8x16Font, dx, ds + Dy+3*dy,
9057 "Pay no attention to the man behind the curtain...",
9058 white_pixel());
9059 rfbDrawString(screen, &default8x16Font, dx, ds + Dy+4*dy,
9060 "To disable caching run the server with: x11vnc -noncache ...",
9061 white_pixel());
9062 rfbDrawString(screen, &default8x16Font, dx, ds + Dy+5*dy,
9063 "If there are painting errors press 3 Alt_L's (Left \"Alt\" key) in a row to repaint the screen.",
9064 white_pixel());
9065 rfbDrawString(screen, &default8x16Font, dx, ds + Dy+6*dy,
9066 "More info: http://www.karlrunge.com/x11vnc/faq.html#faq-client-caching",
9067 white_pixel());
9068
9069 ds += 11 * dy;
9070 }
9071
9072 snapshot_cache_list(0, 100.0);
9073 for (i=0; i < cache_list_num; i++) {
9074 CLEAR(i);
9075 }
9076 for (n = 1; n <= ncache; n++) {
9077 rect_reg[n] = NULL;
9078 }
9079
9080 if (ncache_xrootpmap) {
9081 set_ncache_xrootpmap();
9082 }
9083
9084 snap_old();
9085 }
9086
9087 check_zero_rects();
9088
9089if (hack_val == 2) {
9090 block_stats();
9091 hack_val = 1;
9092}
9093#ifdef MACOSX
9094 if (macosx_console) {
9095 static double last_all_windows = 0.0;
9096 if (! macosx_checkevent(NULL)) {
9097 if (now > last_all_windows + 0.05) {
9098 macosxCGS_get_all_windows();
9099 last_all_windows = dnow();
9100 }
9101 }
9102 /* XXX Y */
9103 rootwin = -1;
9104 }
9105#endif
9106
9107 n = 0;
9108 ttot = 0;
9109
9110 if (dt_guess == NULL || now > dt_last + 60) {
9111 static char *dt_prev = NULL;
9112 dt_prev = dt_guess;
9113 dt_guess = strdup(guess_desktop());
9114 if (ncache_xrootpmap && dt_prev && dt_guess) {
9115 if (strcmp(dt_prev, dt_guess)) {
9116 set_ncache_xrootpmap();
9117 }
9118 }
9119 dt_last = now;
9120 if (dt_prev) {
9121 free(dt_prev);
9122 }
9123 }
9124 if (dt_guess && !strcmp(dt_guess, "gnome")) {
9125 dt_gnome = 1;
9126 } else if (dt_guess && !strcmp(dt_guess, "kde")) {
9127 dt_kde = 1;
9128 }
9129 if (dt_kde) {
9130 kde_no_animate(0);
9131 }
9132
9133 ev_store(None, EV_RESET);
9134
9135 X_LOCK;
9136 for (k = 1; k <= 3; k++) {
9137 int j, retry = 0;
9138
9139 if (retry) {}
9140
9141 nsave = n;
9142
9143 if (k > 1 && ncdb) fprintf(stderr, "read_events-%d\n", k);
9144 read_events(&n);
9145
9146#if 0
9147 if (dt_gnome && (n_MN || n_UN)) {
9148 retry = 1;
9149 } else if (ncache_old_wm && n_ON_po >= 2) {
9150 retry = 1;
9151 } else if (n > nsave) {
9152 /* XXX Y */
9153 retry = 1;
9154 }
9155
9156 if (retry) {
9157 int n0 = n;
9158 usleep(25 * 1000);
9159 XFlush_wr(dpy);
9160 read_events(&n);
9161 if (ncdb) fprintf(stderr, "read_events retry: %d -> %d\n", n0, n);
9162 }
9163#endif
9164
9165 if (n > nsave) {
9166 int n0 = n;
9167
9168 for (j=0; j<4; j++) {
9169 if (j < 2) {
9170 usleep(30 * 1000);
9171 } else {
9172 usleep(10 * 1000);
9173 }
9174 XFlush_wr(dpy);
9175 read_events(&n);
9176 if (ncdb) fprintf(stderr, "read_events retry: %d -> %d\n", n0, n);
9177 if (n == n0) {
9178 break;
9179 }
9180 n0 = n;
9181 }
9182 }
9183
9184 nxsel = 0;
9185
9186 /* handle creates and reparenting: */
9187 for (n1 = nsave; n1 < n; n1++) {
9188 Window win2;
9189 int idx;
9190 XEvent ev = Ev[n1];
9191 win = Ev_win[n1];
9192 if (ev.type == CreateNotify) {
9193 win2 = ev.xcreatewindow.window;
9194 if (ev_lookup(win2, EV_REPARENT) || ev_lookup(win2, EV_DESTROY)) {
9195 if (skipwins_n < skipwins_max) {
9196if (ncdb) fprintf(stderr, "SKIPWINS: CreateNotify: 0x%lx %d\n", win2, n1);
9197 skipwins[skipwins_n++] = win2;
9198 }
9199 } else {
9200 idx = lookup_win_index(win);
9201 if (idx < 0) {
9202 idx = lookup_free_index();
9203 if (idx < 0) {
9204 continue;
9205 }
9206 CLEAR(idx);
9207 }
9208if (ncdb) fprintf(stderr, "PRELOOP: CreateNotify: 0x%lx %d valid_window\n", win2, n1);
9209 if (valid_window(win2, &attr, 1)) {
9210 STORE(idx, win2, attr);
9211 CLEAR(idx);
9212 cache_list[idx].selectinput = 1;
9213 cache_list[idx].create_cnt = 1;
9214if (ncdb) fprintf(stderr, "PRELOOP: CreateNotify: 0x%lx %d xselectinput\n", win2, n1);
9215 xselectinput(win2, win_ev, 1);
9216 nxsel++;
9217 } else {
9218 DELETE(idx);
9219 }
9220 nxsel++;
9221 }
9222 } else if (ev.type == ReparentNotify) {
9223 if (ev.xreparent.parent != rootwin) {
9224 win2 = ev.xreparent.window;
9225 if (win2 != rootwin) {
9226 idx = lookup_win_index(win2);
9227if (ncdb) fprintf(stderr, "PRELOOP: RepartNotify: 0x%lx %d idx=%d\n", win2, n1, idx);
9228 if (idx >= 0) {
9229 DELETE(idx);
9230 }
9231 if (! ev_lookup(win2, EV_CREATE)) {
9232 xselectinput(win2, 0, 1);
9233 nxsel++;
9234 }
9235 }
9236 }
9237 }
9238 }
9239 if (nxsel == 0) {
9240 break;
9241 }
9242 }
9243
9244 X_UNLOCK;
9245
9246 if (got_NET_CURRENT_DESKTOP > 0.0) {
9247 if (dnow() < got_NET_CURRENT_DESKTOP + 0.25) {
9248 if (ncdb) fprintf(stderr, "***got_NET_CURRENT_DESKTOP n=%d\n", n);
9249 desktop_change = 1;
9250 n_DC++;
9251 } else {
9252 if (ncdb) fprintf(stderr, "***got_NET_CURRENT_DESKTOP n=%d STALE\n", n);
9253 }
9254 got_NET_CURRENT_DESKTOP = 0.0;
9255 }
9256
9257 if (n == 0) {
9258 check_sched(try_batch, &did_sched);
9259 return 0;
9260 }
9261if (ncdb) fprintf(stderr, "\n"); if (ncdb) rfbLog("IN check_ncache() %d events. %.4f\n", n, now - x11vnc_start);
9262
9263 if (try_batch) {
9264 use_batch = 1;
9265 }
9266
9267 if (rotating) {
9268 use_batch = 0;
9269 }
9270 if (cursor_noshape_updates_clients(screen)) {
9271 use_batch = 0;
9272 }
9273
9274 if (! use_batch) {
9275 nbatch = NULL;
9276 } else {
9277 nreg = 0;
9278 nbatch = &nreg;
9279 }
9280
9281 /* XXX Y */
9282 for (n1 = 0; n1 < n; n1++) {
9283 Window twin = Ev_map[n1];
9284 if (twin == None || twin == rootwin) {
9285 continue;
9286 }
9287 for (n2 = 0; n2 < n; n2++) {
9288 if (Ev_unmap[n2] == twin) {
9289 if (skipwins_n < skipwins_max) {
9290if (ncdb) fprintf(stderr, "SKIPWINS: Ev_unmap/map: 0x%lx %d\n", twin, n2);
9291 skipwins[skipwins_n++] = twin;
9292 break;
9293 }
9294 }
9295 }
9296 }
9297
9298 if (!desktop_change) {
9299 if (skipwins_n) {
9300 if (n_MN + n_UN >= 2 + 2*skipwins_n) {
9301 desktop_change = 1;
9302 n_DC++;
9303 }
9304 } else {
9305 if (n_MN + n_UN >= 3) {
9306 desktop_change = 1;
9307 n_DC++;
9308 }
9309 }
9310 }
9311 if (ncache_old_wm) {
9312 int shifts = 0;
9313 for (i=0; i < n; i++) {
9314 XEvent ev;
9315 int type, idx = -1;
9316 int ik = Ev_order[i];
9317 int x_new, y_new, w_new, h_new;
9318 int x_old, y_old, w_old, h_old;
9319 int old_wm = 0;
9320
9321 if (Ev_done[ik]) continue;
9322 win = Ev_win[ik];
9323
9324 ev = Ev[ik];
9325 type = ev.type;
9326 if (type != ConfigureNotify) {
9327 continue;
9328 }
9329 if (ev_lookup(win, EV_MAP)) {
9330 continue;
9331 } else if (ev_lookup(win, EV_UNMAP)) {
9332 continue;
9333 } else if (ev_lookup(win, EV_DESTROY)) {
9334 continue;
9335 }
9336
9337 idx = lookup_win_index(win);
9338 if (idx < 0) {
9339 continue;
9340 }
9341 x_new = ev.xconfigure.x;
9342 y_new = ev.xconfigure.y;
9343 w_new = ev.xconfigure.width;
9344 h_new = ev.xconfigure.height;
9345
9346 x_old = cache_list[idx].x;
9347 y_old = cache_list[idx].y;
9348 w_old = cache_list[idx].width;
9349 h_old = cache_list[idx].height;
9350
9351 if (w_new == w_old && h_new == h_old) {
9352 if (nabs(x_new - x_old) >= dpy_x || nabs(y_new - y_old) >= dpy_y) {
9353 sraRegionPtr r_old, r_new, r0;
9354 r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
9355 r_old = sraRgnCreateRect(x_old, y_old, x_old+w_old, y_old+h_old);
9356 sraRgnAnd(r_old, r0);
9357 r_new = sraRgnCreateRect(x_new, y_new, x_new+w_new, y_new+h_new);
9358 sraRgnAnd(r_new, r0);
9359 if (cache_list[idx].map_state != IsViewable) {
9360 ev_store(win, EV_OLD_WM_NOTMAPPED);
9361 } else if (sraRgnEmpty(r_old) && !sraRgnEmpty(r_new)) {
9362 old_wm = 1;
9363 ev_store(win, EV_OLD_WM_MAP);
9364 Ev_map[i] = win;
9365 } else if (!sraRgnEmpty(r_old) && sraRgnEmpty(r_new)) {
9366 ev_store(win, EV_OLD_WM_UNMAP);
9367 old_wm = -1;
9368 Ev_unmap[i] = win;
9369 } else {
9370 ev_store(win, EV_OLD_WM_OFF);
9371 }
9372 sraRgnDestroy(r_old);
9373 sraRgnDestroy(r_new);
9374 sraRgnDestroy(r0);
9375 shifts++;
9376if (ncdb) fprintf(stderr, "old_wm[%d] +%04d+%04d +%04d+%04d old_wm: %d\n", i, x_old, y_old, x_new, y_new, old_wm);
9377 }
9378 }
9379 }
9380 if (shifts >= 3) {
9381if (ncdb) fprintf(stderr, "DESKTOP_CHANGE_OLD_WM: %d\n", shifts);
9382 desktop_change = 1;
9383 desktop_change_old_wm = 1;
9384 }
9385 }
9386
9387#define SKIPUMS \
9388 ok = 1; \
9389 if (twin == None || twin == rootwin) { \
9390 continue; \
9391 } \
9392 for (ns = 0; ns < skipwins_n; ns++) { \
9393 if (skipwins[ns] == twin) { \
9394 ok = 0; \
9395 break; \
9396 } \
9397 }
9398
9399 if (desktop_change) {
9400 Window twin;
9401 int ok, s, k, add, cnt, ns;
9402
9403 cnt = 0;
9404 add = 0;
9405 for (i=0; i < n; i++) {
9406 twin = Ev_unmap[i];
9407 SKIPUMS
9408 if (ok) {
9409if (ncdb) fprintf(stderr, "U Ev_tmp[%d] = %d\n", cnt, i);
9410 Ev_tmp[cnt++] = i;
9411 }
9412 }
9413 for (i=0; i < n; i++) {
9414 twin = Ev_map[i];
9415 SKIPUMS
9416 if (ok) {
9417if (ncdb) fprintf(stderr, "M Ev_tmp[%d] = %d\n", cnt, i);
9418 Ev_tmp[cnt++] = i;
9419 }
9420 }
9421 for (k = 0; k < cnt; k++) {
9422 Ev_tmp2[k] = -1;
9423 }
9424 /* unmap from top to bottom */
9425 for (s = old_stack_n - 1; s >= 0; s--) {
9426 twin = old_stack[s];
9427 if (twin == None || twin == rootwin) {
9428 continue;
9429 }
9430 for (k = 0; k < cnt; k++) {
9431 i = Ev_tmp[k];
9432 if (twin == Ev_unmap[i]) {
9433if (ncdb) fprintf(stderr, "U Ev_tmp2[%d] = %d\n", add, i);
9434 Ev_tmp2[add++] = i;
9435 break;
9436 }
9437 }
9438 }
9439 /* map from bottom to top */
9440 for (s = 0; s < old_stack_n; s++) {
9441 twin = old_stack[s];
9442 if (twin == None || twin == rootwin) {
9443 continue;
9444 }
9445 for (k = 0; k < cnt; k++) {
9446 i = Ev_tmp[k];
9447 if (twin == Ev_map[i]) {
9448if (ncdb) fprintf(stderr, "M Ev_tmp2[%d] = %d\n", add, i);
9449 Ev_tmp2[add++] = i;
9450 break;
9451 }
9452 }
9453 }
9454 k = 0;
9455 for (i=0; i < n; i++) {
9456 Window wu, wm;
9457 int j;
9458 int oku = 0, okm = 0;
9459 wu = Ev_unmap[i];
9460 wm = Ev_map[i];
9461 ok = 0;
9462 if (wu != None && wu != rootwin) oku = 1;
9463 if (wm != None && wm != rootwin) okm = 1;
9464 if (!oku && !okm) {
9465 continue;
9466 }
9467 if (oku) {
9468 twin = wu;
9469 SKIPUMS
9470 if (!ok) {
9471 oku = 0;
9472 }
9473 }
9474 if (okm) {
9475 twin = wm;
9476 SKIPUMS
9477 if (!ok) {
9478 okm = 0;
9479 }
9480 }
9481 if (!oku && !okm) {
9482 continue;
9483 }
9484 j = Ev_tmp2[k++];
9485 if (j >= 0) {
9486if (ncdb) fprintf(stderr, "UM Ev_order[%d] = %d oku=%d okm=%d\n", i, j, oku, okm);
9487 Ev_order[i] = j;
9488 }
9489 }
9490 }
9491
9492#if 0
9493 if (desktop_change) {
9494 Window twin;
9495 int ok, s, k, add, cnt, ns;
9496
9497 cnt = 0;
9498 add = 0;
9499 for (i=0; i < n; i++) {
9500 twin = Ev_unmap[i];
9501 SKIPUMS
9502 if (ok) {
9503 Ev_tmp[cnt++] = i;
9504 }
9505 }
9506 for (k = 0; k < cnt; k++) {
9507 Ev_tmp2[k] = -1;
9508 }
9509 /* unmap from top to bottom */
9510 for (s = old_stack_n - 1; s >= 0; s--) {
9511 twin = old_stack[s];
9512 for (k = 0; k < cnt; k++) {
9513 i = Ev_tmp[k];
9514 if (twin == Ev_unmap[i]) {
9515 Ev_tmp2[add++] = i;
9516 break;
9517 }
9518 }
9519 }
9520 k = 0;
9521 for (i=0; i < n; i++) {
9522 int j;
9523 twin = Ev_unmap[i];
9524 SKIPUMS
9525 if (ok) {
9526 j = Ev_tmp2[k++];
9527 if (j >= 0) {
9528 Ev_order[i] = j;
9529 }
9530 }
9531 }
9532
9533 cnt = 0;
9534 add = 0;
9535 for (i=0; i < n; i++) {
9536 twin = Ev_map[i];
9537 SKIPUMS
9538 if (ok) {
9539 Ev_tmp[cnt++] = i;
9540 }
9541 }
9542 for (k = 0; k < cnt; k++) {
9543 Ev_tmp2[k] = -1;
9544 }
9545 /* map from bottom to top */
9546 for (s = 0; s < old_stack_n; s++) {
9547 twin = old_stack[s];
9548 for (k = 0; k < cnt; k++) {
9549 i = Ev_tmp[k];
9550 if (twin == Ev_map[i]) {
9551 Ev_tmp2[add++] = i;
9552 break;
9553 }
9554 }
9555 }
9556 k = 0;
9557 for (i=0; i < n; i++) {
9558 int j;
9559 twin = Ev_map[i];
9560 SKIPUMS
9561 if (ok) {
9562 j = Ev_tmp2[k++];
9563 if (j >= 0) {
9564 Ev_order[i] = j;
9565 }
9566 }
9567 }
9568 }
9569#endif
9570
9571 if (!desktop_change && (n_VN_p && !n_UN && (n_MN || n_ON_st))) {
9572 if (now < last_vis_unobs_time + 0.75 || now < last_vis_obs_time + 0.75) {
9573 ;
9574 } else if (n_MN <= 2 && n_ON_st <= 1) {
9575 for (i=0; i < n; i++) {
9576 XEvent ev;
9577 int type, idx = -1, state, valid;
9578 int ik = Ev_order[i];
9579
9580 if (Ev_done[ik]) continue;
9581 win = Ev_win[ik];
9582
9583 ev = Ev[ik];
9584 type = ev.type;
9585 if (type != VisibilityNotify) {
9586 continue;
9587 }
9588
9589 state = ev.xvisibility.state;
9590 if (state == VisibilityUnobscured) {
9591 continue;
9592 }
9593 if (ev_lookup(win, EV_MAP)) {
9594 continue;
9595 } else if (ev_lookup(win, EV_UNMAP)) {
9596 continue;
9597 } else if (ev_lookup(win, EV_DESTROY)) {
9598 continue;
9599 }
9600 idx = lookup_win_index(win);
9601
9602 if (idx < 0) {
9603 continue;
9604 }
9605 if (cache_list[idx].vis_state == VisibilityFullyObscured) {
9606 continue;
9607 }
9608 if (now < cache_list[idx].vis_unobs_time + 3.00 || now < cache_list[idx].vis_obs_time + 3.00) {
9609 continue;
9610 }
9611
9612if (ncdb) fprintf(stderr, "----%02d: VisibilityNotify 0x%lx %3d (*PRELOOP*) state: %s U/P %d/%d\n", ik, win, idx, VState(state), n_VN_u, n_VN_p);
9613 valid = 0;
9614 bs_save(idx, nbatch, &attr, 1, 0, &valid, 1);
9615 if (valid) {
9616 STORE(idx, win, attr);
9617 } else {
9618 DELETE(idx);
9619 }
9620
9621 cache_list[idx].vis_state = state;
9622 cache_list[idx].vis_obs_time = last_vis_obs_time = dnow();
9623 Ev_done[ik] = 1;
9624 }
9625 }
9626 }
9627 if (desktop_change) {
9628 if (ncache_dt_change) {
9629 if (ncdb) fprintf(stderr, "GUESSED DESKTOP CHANGE.\n");
9630 saw_desktop_change = 1;
9631 } else {
9632 if (ncdb) fprintf(stderr, "GUESSED DESKTOP CHANGE. Skipping.\n");
9633 desktop_change = 0;
9634 }
9635 }
9636
9637
9638 create_cnt = 0;
9639 missed_su_restore = 0;
9640 missed_bs_restore = 0;
9641 missed_su_restore_rgn = sraRgnCreate();
9642 missed_bs_restore_rgn = sraRgnCreate();
9643 r0 = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
9644 unmapped_rgn = sraRgnCreate();
9645 su_fix_cnt = 0;
9646
9647for (k = 0; k < skipwins_n; k++) {
9648 if (ncdb) fprintf(stderr, "skipwins[%d] 0x%lx\n", k, skipwins[k]);
9649}
9650
9651 X_LOCK;
9652 for (i=0; i < n; i++) {
9653 XEvent ev;
9654 int ns, skip = 0, type, idx = -1;
9655 int ik = Ev_order[i];
9656
9657 if (Ev_done[ik]) continue;
9658 win = Ev_win[ik];
9659
9660 ev = Ev[ik];
9661 type = ev.type;
9662 Ev_done[ik] = 1;
9663
9664 win2 = win;
9665 if (win == rootwin) {
9666 if (type == CreateNotify) {
9667 win2 = ev.xcreatewindow.window;
9668 } else if (type == ReparentNotify) {
9669 win2 = ev.xreparent.window;
9670 }
9671 }
9672 for (ns = 0; ns < skipwins_n; ns++) {
9673 if (win2 == skipwins[ns]) {
9674 skip = 1;
9675 break;
9676 }
9677 }
9678 if (skip) {
9679if (ncdb) fprintf(stderr, "skip%02d: ** SpecialSkip 0x%lx/0x%lx type: %s\n", ik, win, win2, Etype(type));
9680 continue;
9681 }
9682
9683 if (win == rootwin) {
9684 if (type == CreateNotify) {
9685 int x=0, y=0, w=0, h=0;
9686 valid = 0;
9687 win2 = ev.xcreatewindow.window;
9688 idx = lookup_win_index(win2);
9689 if (idx < 0) {
9690 continue;
9691 }
9692 if (cache_list[idx].valid) {
9693 valid = 1;
9694 x=cache_list[idx].x;
9695 y=cache_list[idx].y;
9696 w=cache_list[idx].width;
9697 h=cache_list[idx].height;
9698 if (w*h > 64 * 64 && ev_lookup(win2, EV_MAP)) {
9699 X_UNLOCK;
9700 valid = 1;
9701 su_save(idx, nbatch, &attr, 0, &valid, 1);
9702 STORE(idx, win2, attr);
9703
9704 X_LOCK;
9705
9706 if (! desktop_change) {
9707 SCHED(win2, 1)
9708 }
9709 create_cnt++;
9710 }
9711 }
9712if (ncdb) fprintf(stderr, "root%02d: ** CreateNotify 0x%lx %3d -- %dx%d+%d+%d valid=%d\n", ik, win2, idx, w, h, x, y, valid);
9713
9714 } else if (type == ReparentNotify) {
9715 if (ev.xreparent.parent != rootwin) {
9716 win2 = ev.xreparent.window;
9717 idx = lookup_win_index(win2);
9718if (ncdb) fprintf(stderr, "root%02d: ReparentNotifyRM 0x%lx %3d\n", ik, win2, idx);
9719 }
9720 } else {
9721if (ncdb) fprintf(stderr, "root%02d: ** IgnoringRoot 0x%lx type: %s\n", ik, win, Etype(type));
9722 }
9723 } else {
9724 if (type == ConfigureNotify) {
9725 int x_new, y_new, w_new, h_new;
9726 int x_old, y_old, w_old, h_old;
9727 int stack_change, old_wm = 0;
9728 Window oabove = None;
9729
9730 idx = lookup_win_index(win);
9731
9732 if (idx >= 0) {
9733 oabove = cache_list[idx].above;
9734 }
9735
9736if (ncdb) fprintf(stderr, "----%02d: ConfigureNotify 0x%lx %3d -- above: 0x%lx -> 0x%lx %dx%d+%d+%d\n", ik, win, idx,
9737 oabove, ev.xconfigure.above, ev.xconfigure.width, ev.xconfigure.height, ev.xconfigure.x, ev.xconfigure.y);
9738
9739 if (idx < 0) {
9740 continue;
9741 }
9742
9743 x_new = ev.xconfigure.x;
9744 y_new = ev.xconfigure.y;
9745 w_new = ev.xconfigure.width;
9746 h_new = ev.xconfigure.height;
9747
9748 x_old = cache_list[idx].x;
9749 y_old = cache_list[idx].y;
9750 w_old = cache_list[idx].width;
9751 h_old = cache_list[idx].height;
9752
9753 if (desktop_change_old_wm) {
9754 if (ev_lookup(win, EV_OLD_WM_MAP)) {
9755 if (Ev_map[ik] == win) {
9756 old_wm = 1;
9757 } else {
9758 old_wm = 2;
9759 }
9760 } else if (ev_lookup(win, EV_OLD_WM_UNMAP)) {
9761 if (Ev_unmap[ik] == win) {
9762 old_wm = -1;
9763 } else {
9764 old_wm = 2;
9765 }
9766 } else if (ev_lookup(win, EV_OLD_WM_OFF)) {
9767 old_wm = 2;
9768 } else if (ev_lookup(win, EV_OLD_WM_NOTMAPPED)) {
9769 old_wm = 3;
9770 }
9771 }
9772
9773 if (!old_wm) {
9774 if (x_old != x_new || y_old != y_new) {
9775 /* invalidate su */
9776 cache_list[idx].su_time = 0.0;
9777if (ncdb) fprintf(stderr, " INVALIDATE su: 0x%lx xy: +%d+%d +%d+%d \n", win, x_old, y_old, x_new, y_new);
9778 }
9779 if (w_old != w_new || h_old != h_new) {
9780 /* invalidate bs */
9781 cache_list[idx].bs_time = 0.0;
9782if (ncdb) fprintf(stderr, " INVALIDATE bs: 0x%lx wh: %dx%d %dx%d \n", win, w_old, h_old, w_new, h_new);
9783 }
9784 } else {
9785 int valid;
9786 X_UNLOCK;
9787 if (old_wm == 1) {
9788 /* XXX Y */
9789if (ncdb) fprintf(stderr, " OLD_WM_MAP: 0x%lx wh: %dx%d+%d+%d %dx%d+%d+%d \n", win, w_old, h_old, x_old, y_old, w_new, h_new, x_new, y_new);
9790 valid = 0;
9791 bs_restore(idx, nbatch, NULL, &attr, 0, 0, &valid, 1);
9792
9793 } else if (old_wm == -1) {
9794if (ncdb) fprintf(stderr, " OLD_WM_UNMAP: 0x%lx wh: %dx%d+%d+%d %dx%d+%d+%d \n", win, w_old, h_old, x_old, y_old, w_new, h_new, x_new, y_new);
9795 valid = 1;
9796 su_restore(idx, nbatch, NULL, &attr, 1, 0, &valid, 1);
9797 } else {
9798if (ncdb) fprintf(stderr, " OLD_WM_OFF:: 0x%lx wh: %dx%d+%d+%d %dx%d+%d+%d old_wm=%d\n", win, w_old, h_old, x_old, y_old, w_new, h_new, x_new, y_new, old_wm);
9799 }
9800 X_LOCK;
9801 }
9802
9803 stack_change = 0;
9804 if (old_wm) {
9805 ;
9806 } else if (cache_list[idx].above != ev.xconfigure.above) {
9807 stack_change = 1;
9808 } else if (x_new == x_old && y_new == y_old && w_new == w_old && h_new == h_old) {
9809 stack_change = 1;
9810 }
9811 if (stack_change) {
9812 int i2, ok = 1;
9813 for (i2=0; i2 < n; i2++) {
9814 if (Ev_map[i2] == win) {
9815 ok = 0;
9816 break;
9817 }
9818 }
9819 if (ok) {
9820 if (n_MN == 0 && n_UN == 0) {
9821 if (su_fix_cnt > 0) {
9822 ok = 0;
9823if (ncdb) fprintf(stderr, " CONF_IGNORE: Too many stacking changes: 0x%lx\n", win);
9824 }
9825 }
9826
9827 }
9828 if (ok) {
9829 if (ev_lookup(ev.xconfigure.above, EV_UNMAP)) {
9830 if (ncdb) fprintf(stderr, " skip try_to_fix_su for GNOME deiconify #1\n");
9831 if (dt_gnome) {
9832 gnome_animation = 1;
9833 }
9834 ok = 0;
9835 }
9836 }
9837 if (ok && dt_gnome) {
9838 if (valid_window(ev.xconfigure.above, &attr, 1)) {
9839 if (attr.map_state != IsViewable) {
9840 if (ncdb) fprintf(stderr, " skip try_to_fix_su for GNOME deiconify #2\n");
9841 gnome_animation = 1;
9842 ok = 0;
9843 }
9844 }
9845 }
9846 if (ok) {
9847 int rc = try_to_fix_su(win, idx, ev.xconfigure.above, nbatch, NULL);
9848 if (rc == 0 && su_fix_cnt == 0 && n_MN == 0 && n_UN == 0) {
9849 X_UNLOCK;
9850 try_to_synthesize_su(1, 1, nbatch);
9851 X_LOCK;
9852 }
9853 n_ST++;
9854 su_fix_cnt++;
9855 }
9856 }
9857
9858 cache_list[idx].x = x_new;
9859 cache_list[idx].y = y_new;
9860 cache_list[idx].width = w_new;
9861 cache_list[idx].height = h_new;
9862
9863 cache_list[idx].above = ev.xconfigure.above;
9864 cache_list[idx].time = dnow();
9865
9866 } else if (type == VisibilityNotify) {
9867 int state = ev.xvisibility.state;
9868 idx = lookup_win_index(win);
9869if (ncdb) fprintf(stderr, "----%02d: VisibilityNotify 0x%lx %3d state: %s U/P %d/%d\n", ik, win, idx, VState(state), n_VN_u, n_VN_p);
9870
9871 if (idx < 0) {
9872 continue;
9873 }
9874 if (desktop_change) {
9875 ;
9876 } else if (macosx_console && n_VN_p == 0) {
9877 ; /* XXXX not working well yet with UnmapNotify ... */
9878 } else if (state == VisibilityUnobscured) {
9879 int ok = 1;
9880 if (ncache <= 2) {
9881 ok = 0;
9882 } else if (ev_lookup(win, EV_MAP)) {
9883 ok = 0;
9884 } else if (ev_lookup(win, EV_UNMAP)) {
9885 ok = 0;
9886 } else if (ev_lookup(win, EV_DESTROY)) {
9887 ok = 0;
9888 } else if (gnome_animation) {
9889 ok = 0;
9890 } else {
9891 /* this is for gnome iconify */
9892 int i2;
9893 for (i2=i+1; i2 < n; i2++) {
9894 int idx2, ik2 = Ev_order[i2];
9895 sraRegionPtr ro1, ro2;
9896 Window win2 = Ev_unmap[ik2];
9897
9898 if (win2 == None) {
9899 continue;
9900 }
9901 idx2 = lookup_win_index(win2);
9902 if (idx2 < 0) {
9903 continue;
9904 }
9905
9906 ro1 = idx_create_rgn(r0, idx);
9907 ro2 = idx_create_rgn(r0, idx2);
9908
9909 if (sraRgnAnd(ro1, ro2)) {
9910 if (ncdb) fprintf(stderr, " skip VisibilityUnobscured for GNOME iconify.\n");
9911 ok = 0;
9912 }
9913 sraRgnDestroy(ro1);
9914 sraRgnDestroy(ro2);
9915 if (! ok) {
9916 break;
9917 }
9918 }
9919 }
9920 if (ok) {
9921 int x2, y2, w2, h2;
9922 sraRegionPtr rmask = NULL;
9923 valid = 0;
9924 if (dnow() < cache_list[idx].vis_unobs_time + 3.00 && !sraRgnEmpty(unmapped_rgn)) {
9925 x2 = cache_list[idx].x;
9926 y2 = cache_list[idx].y;
9927 w2 = cache_list[idx].width;
9928 h2 = cache_list[idx].height;
9929 rmask = sraRgnCreateRect(x2, y2, x2+w2, y2+h2);
9930 sraRgnAnd(rmask, unmapped_rgn);
9931 if (sraRgnEmpty(rmask)) {
9932 sraRgnDestroy(rmask);
9933 rmask = NULL;
9934 }
9935 }
9936 if (ev_lookup(win, EV_CONFIGURE_SIZE)) {
9937 valid = valid_window(win, &attr, 1);
9938 } else {
9939 X_UNLOCK;
9940 bs_restore(idx, nbatch, rmask, &attr, 0, 1, &valid, 1);
9941 X_LOCK;
9942 }
9943 if (rmask != NULL) {
9944 sraRgnDestroy(rmask);
9945 }
9946 if (valid) {
9947 STORE(idx, win, attr);
9948
9949 cache_list[idx].time = dnow();
9950 cache_list[idx].vis_cnt++;
9951 Ev_map[ik] = win;
9952 Ev_rects[nrects].x1 = cache_list[idx].x;
9953 Ev_rects[nrects].y1 = cache_list[idx].y;
9954 Ev_rects[nrects].x2 = cache_list[idx].width;
9955 Ev_rects[nrects].y2 = cache_list[idx].height;
9956 nrects++;
9957 SCHED(win, 1)
9958 } else {
9959 DELETE(idx);
9960 }
9961 }
9962 }
9963 if (state == VisibilityUnobscured) {
9964 cache_list[idx].vis_unobs_time = last_vis_unobs_time = dnow();
9965 } else if (cache_list[idx].vis_state == VisibilityUnobscured) {
9966 cache_list[idx].vis_obs_time = last_vis_obs_time = dnow();
9967 }
9968 cache_list[idx].vis_state = state;
9969
9970 } else if (type == MapNotify) {
9971 idx = lookup_win_index(win);
9972if (ncdb) fprintf(stderr, "----%02d: MapNotify 0x%lx %3d\n", ik, win, idx);
9973
9974 if (idx < 0) {
9975 continue;
9976 }
9977
9978#if 0
9979/*
9980 if (cache_list[idx].map_state == IsUnmapped || desktop_change || macosx_console)
9981 */
9982#endif
9983 if (1) {
9984 X_UNLOCK;
9985 if (desktop_change) {
9986 /* XXX Y */
9987 int save = 1;
9988 sraRegionPtr r;
9989 if (cache_list[idx].su_time != 0.0) {
9990 save = 0;
9991 } else if (missed_su_restore) {
9992 r = idx_create_rgn(r0, idx);
9993 if (sraRgnAnd(r, missed_su_restore_rgn)) {
9994 save = 0;
9995 }
9996 sraRgnDestroy(r);
9997 }
9998 if (missed_bs_restore) {
9999 r = idx_create_rgn(r0, idx);
10000 if (sraRgnAnd(r, missed_bs_restore_rgn)) {
10001 save = 0;
10002 }
10003 sraRgnDestroy(r);
10004 }
10005 if (save) {
10006 valid = 0;
10007 su_save(idx, nbatch, &attr, 1, &valid, 1);
10008 if (valid) {
10009 STORE(idx, win, attr);
10010 }
10011 }
10012 } else {
10013 valid = 0;
10014 su_save(idx, nbatch, &attr, 0, &valid, 1);
10015 if (valid) {
10016 STORE(idx, win, attr);
10017 }
10018 }
10019 valid = 0;
10020 if (ev_lookup(win, EV_CONFIGURE_SIZE)) {
10021 X_LOCK;
10022 valid = valid_window(win, &attr, 1);
10023 X_UNLOCK;
10024 idx_add_rgn(missed_bs_restore_rgn, r0, idx);
10025 missed_bs_restore++;
10026 } else if (bs_restore(idx, nbatch, NULL, &attr, 0, 0, &valid, 1)) { /* XXX clip? */
10027 ;
10028 } else {
10029 idx_add_rgn(missed_bs_restore_rgn, r0, idx);
10030 missed_bs_restore++;
10031 }
10032 if (valid) {
10033 STORE(idx, win, attr);
10034 }
10035
10036 if (macosx_console) {
10037#ifdef MACOSX
10038 macosxCGS_follow_animation_win(win, -1, 1);
10039 if (valid_window(win, &attr, 1)) {
10040 STORE(idx, win, attr);
10041 SCHED(win, 1);
10042 }
10043 /* XXX Y */
10044 if (cache_list[idx].vis_state == -1) {
10045 cache_list[idx].vis_state = VisibilityUnobscured;
10046 }
10047#endif
10048 }
10049 X_LOCK;
10050 pixels += cache_list[idx].width * cache_list[idx].height;
10051 cache_list[idx].time = dnow();
10052 cache_list[idx].map_cnt++;
10053 Ev_map[ik] = win;
10054 Ev_rects[nrects].x1 = cache_list[idx].x;
10055 Ev_rects[nrects].y1 = cache_list[idx].y;
10056 Ev_rects[nrects].x2 = cache_list[idx].width;
10057 Ev_rects[nrects].y2 = cache_list[idx].height;
10058 nrects++;
10059
10060 if (! valid) {
10061 DELETE(idx);
10062 }
10063 }
10064 cache_list[idx].map_state = IsViewable;
10065
10066 } else if (type == UnmapNotify) {
10067 int x2, y2, w2, h2;
10068 idx = lookup_win_index(win);
10069if (ncdb) fprintf(stderr, "----%02d: UnmapNotify 0x%lx %3d\n", ik, win, idx);
10070
10071 if (idx < 0) {
10072 continue;
10073 }
10074 if (macosx_console) {
10075 if (mode == 2) {
10076 cache_list[idx].map_state = IsViewable;
10077 }
10078 }
10079
10080#if 0
10081/*
10082 if (cache_list[idx].map_state == IsViewable || desktop_change || macosx_console)
10083 */
10084#endif
10085 if (1) {
10086 X_UNLOCK;
10087 if (desktop_change) {
10088 int save = 1;
10089 sraRegionPtr r;
10090 if (cache_list[idx].bs_time > 0.0) {
10091 save = 0;
10092 } else if (missed_su_restore) {
10093 r = idx_create_rgn(r0, idx);
10094 if (sraRgnAnd(r, missed_su_restore_rgn)) {
10095 save = 0;
10096 }
10097 sraRgnDestroy(r);
10098 }
10099 if (missed_bs_restore) {
10100 r = idx_create_rgn(r0, idx);
10101 if (sraRgnAnd(r, missed_bs_restore_rgn)) {
10102 save = 0;
10103 }
10104 sraRgnDestroy(r);
10105 }
10106 if (save) {
10107 valid = 0;
10108 bs_save(idx, nbatch, &attr, 1, 0, &valid, 1);
10109 }
10110 } else {
10111 valid = 0;
10112 bs_save(idx, nbatch, &attr, 1, 0, &valid, 1);
10113 }
10114 valid = 0;
10115 if (su_restore(idx, nbatch, NULL, &attr, 1, 0, &valid, 1)) {
10116 try_to_fix_su(win, idx, None, nbatch, "unmapped");
10117 if (valid) {
10118 STORE(idx, win, attr);
10119 } else {
10120 DELETE(idx);
10121 }
10122 } else {
10123 idx_add_rgn(missed_su_restore_rgn, r0, idx);
10124 missed_su_restore++;
10125 }
10126 X_LOCK;
10127
10128 pixels += cache_list[idx].width * cache_list[idx].height;
10129 cache_list[idx].time = dnow();
10130 cache_list[idx].unmap_cnt++;
10131 Ev_unmap[ik] = win;
10132 Ev_rects[nrects].x1 = cache_list[idx].x;
10133 Ev_rects[nrects].y1 = cache_list[idx].y;
10134 Ev_rects[nrects].x2 = cache_list[idx].width;
10135 Ev_rects[nrects].y2 = cache_list[idx].height;
10136 nrects++;
10137 }
10138
10139 x2 = cache_list[idx].x;
10140 y2 = cache_list[idx].y;
10141 w2 = cache_list[idx].width;
10142 h2 = cache_list[idx].height;
10143 r = sraRgnCreateRect(x2, y2, x2+w2, y2+h2);
10144 sraRgnAnd(r, r0);
10145 sraRgnOr(unmapped_rgn, r);
10146 sraRgnDestroy(r);
10147
10148 cache_list[idx].map_state = IsUnmapped;
10149
10150 } else if (type == ReparentNotify) {
10151 if (ev.xreparent.parent != rootwin) {
10152 win2 = ev.xreparent.window;
10153 if (win2 != rootwin) {
10154 idx = lookup_win_index(win2);
10155if (ncdb) fprintf(stderr, "----%02d: ReparentNotifyRM 0x%lx %3d\n", ik, win2, idx);
10156 }
10157 }
10158
10159 } else if (type == DestroyNotify) {
10160 win2 = ev.xdestroywindow.window;
10161 idx = lookup_win_index(win2);
10162if (ncdb) fprintf(stderr, "----%02d: DestroyNotify 0x%lx %3d\n", ik, win2, idx);
10163
10164 if (idx >= 0) {
10165 DELETE(idx);
10166 }
10167 } else {
10168if (ncdb) fprintf(stderr, "igno%02d: ** Ignoring 0x%lx type: %s\n", ik, win, Etype(type));
10169 }
10170
10171 }
10172 }
10173 X_UNLOCK;
10174
10175 if (use_batch && nreg) {
10176 batch_push(nreg, -1.0);
10177 }
10178 if (nrects) {
10179 if (scaling) {
10180 push_borders(Ev_rects, nrects);
10181 }
10182 }
10183
10184 check_sched(try_batch, &did_sched);
10185
10186 if (n_CN || n_RN || n_DN || n_MN || n_UN || n_ST || n_DC || did_sched) {
10187 snap_old();
10188 }
10189
10190 sraRgnDestroy(r0);
10191 sraRgnDestroy(missed_su_restore_rgn);
10192 sraRgnDestroy(missed_bs_restore_rgn);
10193
10194if (ncdb) rfbLog("OUT check_ncache(): %.4f %.6f events: %d pixels: %d\n", dnowx(), dnow() - now, n, pixels);
10195if (ncdb) fprintf(stderr, "\n");
10196 return pixels;
10197}
10198#endif
10199