1/*
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
10200