Home | History | Annotate | Download | only in x11vnc
      1 /*
      2    Copyright (C) 2002-2010 Karl J. Runge <runge (at) karlrunge.com>
      3    All rights reserved.
      4 
      5 This file is part of x11vnc.
      6 
      7 x11vnc is free software; you can redistribute it and/or modify
      8 it under the terms of the GNU General Public License as published by
      9 the Free Software Foundation; either version 2 of the License, or (at
     10 your option) any later version.
     11 
     12 x11vnc is distributed in the hope that it will be useful,
     13 but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with x11vnc; if not, write to the Free Software
     19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
     20 or see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, as a special exception, Karl J. Runge
     23 gives permission to link the code of its release of x11vnc with the
     24 OpenSSL project's "OpenSSL" library (or with modified versions of it
     25 that use the same license as the "OpenSSL" library), and distribute
     26 the linked executables.  You must obey the GNU General Public License
     27 in all respects for all of the code used other than "OpenSSL".  If you
     28 modify this file, you may extend this exception to your version of the
     29 file, but you are not obligated to do so.  If you do not wish to do
     30 so, delete this exception statement from your version.
     31 */
     32 
     33 /* -- 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  */
     61 int defer_update_nofb = 4;	/* defer a shorter time under -nofb */
     62 int last_scroll_type = SCR_NONE;
     63 
     64 
     65 int get_wm_frame_pos(int *px, int *py, int *x, int *y, int *w, int *h,
     66     Window *frame, Window *win);
     67 void parse_scroll_copyrect(void);
     68 void parse_fixscreen(void);
     69 void parse_wireframe(void);
     70 
     71 void set_wirecopyrect_mode(char *str);
     72 void set_scrollcopyrect_mode(char *str);
     73 void initialize_scroll_keys(void);
     74 void initialize_scroll_matches(void);
     75 void initialize_scroll_term(void);
     76 void initialize_max_keyrepeat(void);
     77 
     78 int direct_fb_copy(int x1, int y1, int x2, int y2, int mark);
     79 void fb_push(void);
     80 int fb_push_wait(double max_wait, int flags);
     81 void eat_viewonly_input(int max_eat, int keep);
     82 
     83 void mark_for_xdamage(int x, int y, int w, int h);
     84 void mark_region_for_xdamage(sraRegionPtr region);
     85 void set_xdamage_mark(int x, int y, int w, int h);
     86 int near_wm_edge(int x, int y, int w, int h, int px, int py);
     87 int near_scrollbar_edge(int x, int y, int w, int h, int px, int py);
     88 
     89 void check_fixscreen(void);
     90 int check_xrecord(void);
     91 int check_wireframe(void);
     92 int fb_update_sent(int *count);
     93 int check_user_input(double dt, double dtr, int tile_diffs, int *cnt);
     94 void do_copyregion(sraRegionPtr region, int dx, int dy, int mode);
     95 
     96 int check_ncache(int reset, int mode);
     97 int find_rect(int idx, int x, int y, int w, int h);
     98 int bs_restore(int idx, int *nbatch, sraRegionPtr rmask, XWindowAttributes *attr, int clip, int nopad, int *valid, int verb);
     99 int try_to_fix_su(Window win, int idx, Window above, int *nbatch, char *mode);
    100 int 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);
    102 int lookup_win_index(Window);
    103 void set_ncache_xrootpmap(void);
    104 
    105 static void get_client_regions(int *req, int *mod, int *cpy, int *num) ;
    106 static void parse_scroll_copyrect_str(char *scr);
    107 static void parse_wireframe_str(char *wf);
    108 static void destroy_str_list(char **list);
    109 static void draw_box(int x, int y, int w, int h, int restore);
    110 static int do_bdpush(Window wm_win, int x0, int y0, int w0, int h0, int bdx,
    111     int bdy, int bdskinny);
    112 static int set_ypad(void);
    113 static void scale_mark(int x1, int y1, int x2, int y2, int mark);
    114 static int push_scr_ev(double *age, int type, int bdpush, int bdx, int bdy,
    115     int bdskinny, int first_push);
    116 static int crfix(int x, int dx, int Lx);
    117 static int scrollability(Window win, int set);
    118 static int eat_pointer(int max_ptr_eat, int keep);
    119 static void set_bdpush(int type, double *last_bdpush, int *pushit);
    120 static int repeat_check(double last_key_scroll);
    121 static int check_xrecord_keys(void);
    122 static int check_xrecord_mouse(void);
    123 static 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);
    125 static int wireframe_mod_state();
    126 static void check_user_input2(double dt);
    127 static void check_user_input3(double dt, double dtr, int tile_diffs);
    128 static void check_user_input4(double dt, double dtr, int tile_diffs);
    129 
    130 winattr_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  */
    138 int 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 
    202 static int scrollcopyrect_top, scrollcopyrect_bot;
    203 static int scrollcopyrect_left, scrollcopyrect_right;
    204 static double scr_key_time, scr_key_persist;
    205 static double scr_mouse_time, scr_mouse_persist, scr_mouse_maxtime;
    206 static double scr_mouse_pointer_delay;
    207 static double scr_key_bdpush_time, scr_mouse_bdpush_time;
    208 
    209 static 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 
    274 void 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 
    282 void 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 /*
    319 WIREFRAME_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
    321 shade,linewidth,percent,T+B+L+R,mods,t1+t2+t3+t4
    322  */
    323 #define LW_MAX 8
    324 static unsigned long wireframe_shade = 0xff;
    325 static int wireframe_lw;
    326 static double wireframe_frac;
    327 static int wireframe_top, wireframe_bot, wireframe_left, wireframe_right;
    328 static double wireframe_t1, wireframe_t2, wireframe_t3, wireframe_t4;
    329 static char *wireframe_mods = NULL;
    330 
    331 /*
    332  * Parse the gory -wireframe string for parameters.
    333  */
    334 static 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  */
    492 void 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  */
    503 void 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  */
    530 void 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 
    557 void 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 
    634 static 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 
    645 void 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 
    742 void 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 
    767 void 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 
    791 typedef 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  */
    805 static 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) {
    920 if (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 
    926 if (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) {
   1062 if (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 
   1068 int 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 
   1076 if (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 
   1184 static 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 	}
   1212 if (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);
   1288 if (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 
   1298 static 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 
   1410 static 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)  \
   1420 if (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 
   1427 int batch_dxs[], batch_dys[];
   1428 sraRegionPtr batch_reg[];
   1429 void batch_push(int ncr, double delay);
   1430 
   1431 static 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 
   1482 if (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) {
   1510 if (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 {
   1515 if (db) fprintf(stderr, "push_scr_ev: AGE:     %.4f\n", ag);
   1516 		}
   1517 		if (win != win0) {
   1518 if (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) {
   1523 if (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) {
   1528 if (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) {
   1533 if (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 
   1538 if (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 
   1542 if (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 
   1546 if (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);
   1649 PUSH_TEST(0);
   1650 			}
   1651 		}
   1652 
   1653 		if (! rc) {
   1654 			dret = 0;
   1655 			sraRgnDestroy(tmpregion);
   1656 			break;
   1657 		}
   1658 		dt = dtime(&tm);
   1659 if (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);
   1719 if (db) fprintf(stderr, "  area %.1f win_area %.1f est: %.4f", area, win_area, est);
   1720 		if (area > 0.90 * win_area) {
   1721 if (db) fprintf(stderr, "  AREA_TOO_MUCH");
   1722 			dret = 0;
   1723 		} else if (est > 0.6) {
   1724 if (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);
   1755 if (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++;
   1761 PUSH_TEST(0);
   1762 			}
   1763 			sraRgnReleaseIterator(iter);
   1764 
   1765 			dt = dtime(&tm);
   1766 if (db) fprintf(stderr, "  dfc---- dt: %.4f", dt);
   1767 
   1768 		}
   1769 if (db &&  dret) fprintf(stderr, " **** dret=%d", dret);
   1770 if (db && !dret) fprintf(stderr, " ---- dret=%d", dret);
   1771 if (db) fprintf(stderr, "\n");
   1772 	}
   1773 
   1774 if (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);
   1793 if (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 
   1835 static 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  */
   1862 int DCR_Normal = 0;
   1863 int DCR_FBOnly = 1;
   1864 int DCR_Direct = 2;
   1865 
   1866 void 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);
   1880 if (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);
   1885 if (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 				}
   1924 if (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 		}
   1977 if (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 		}
   2022 if (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 
   2045 void 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 
   2157 if (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 
   2162 void 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 
   2172 void 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 
   2182 if (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 
   2201 if (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 
   2209 int 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  */
   2247 static 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 
   2263 typedef struct scroll_result {
   2264 	Window win;
   2265 	double time;
   2266 	int result;
   2267 } scroll_result_t;
   2268 
   2269 #define SCR_RESULTS_MAX 256
   2270 static scroll_result_t scroll_results[SCR_RESULTS_MAX];
   2271 
   2272 static 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 
   2343 void 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 
   2369 static 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++;
   2376 if (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 
   2385 static 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 
   2411 void 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 
   2429 void 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 
   2443 void 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 
   2463 static 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 
   2474 static 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  */
   2557 if (0)	link = link_rate(&latency, &netrate);
   2558 
   2559 	gk = gk0 = got_keyboard_input;
   2560 	dtime0(&tm);
   2561 
   2562 if (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 			}
   2608 if (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) {
   2622 if (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);
   2666 if (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;
   2675 if (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 
   2716 static 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);
   2753 if (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 
   2834 if (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 
   2880 if (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 */
   2894 if (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]) {
   2943 if (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 
   2949 if (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 
   2956 if (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 
   2962 if (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) {
   2974 if (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) {
   2978 if (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 {
   2981 if (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) ) {
   3016 if (db) fprintf(stderr, "  pointer() push yields events on: ret=%d\n", ret);
   3017 		if (ret == 2) {
   3018 if (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 
   3050 if (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 
   3068 int 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 
   3083 if (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 	}
   3103 if (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
   3137 int batch_dxs[NBATCHMAX], batch_dys[NBATCHMAX];
   3138 sraRegionPtr batch_reg[NBATCHMAX];
   3139 
   3140 static 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 	}
   3192 if (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 
   3246 if (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;
   3270 if (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 			}
   3323 if (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 
   3345 if (db2) fprintf(stderr, "  tmp_win-1: %4d %3d, %4d %3d  0x%lx\n",
   3346 	tx1, ty1, tx2, ty2, swin);
   3347 if (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;
   3353 if (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 
   3373 if (db2) fprintf(stderr, "  tmp_win-2: %4d %3d, %4d %3d  0x%lx\n",
   3374 	tx1, ty1, tx2, ty2, swin);
   3375 if (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);
   3435 if (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);
   3462 if (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 
   3473 int 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 
   3502 int 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 
   3531 void 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 
   3600 static 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 
   3651 static int NPP_nreg = 0;
   3652 static sraRegionPtr NPP_roffscreen = NULL;
   3653 static sraRegionPtr NPP_r_bs_tmp = NULL;
   3654 static Window NPP_nwin = None;
   3655 
   3656 void 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 
   3686 void 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 
   3748 void 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);
   3837 if (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 
   3863 if (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)) {
   3867 if (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 
   3881 void 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 
   3899 if (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;
   3967 if (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)) {
   3973 if (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 		}
   3981 if (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 
   4008 if (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)) {
   4012 if (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 		}
   4039 if (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)) {
   4043 if (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);
   4063 if (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)) {
   4067 if (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);
   4080 if (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 
   4113 void 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 	}
   4139 if (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)) {
   4160 if (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 	}
   4183 if (ncdb) fprintf(stderr, "do_COPY: %.4f -- post_portion done.\n", dnow() - ntim);
   4184 }
   4185 
   4186 void 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) {
   4206 if (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 
   4244 void check_macosx_click_frame(void) {
   4245 #ifdef MACOSX
   4246 	if (macosx_console) {
   4247 if (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 
   4257 int clipped(int idx);
   4258 void snap_old(void);
   4259 
   4260 int 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;
   4281 if (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 {
   4293 if (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 
   4302 int 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  */
   4338 int 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 
   4391 if (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 
   4413 if (db > 1) fprintf(stderr, "check_wireframe: %d\n", db);
   4414 
   4415 if (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)) {
   4424 if (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 
   4436 if (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)) {
   4443 if (db) fprintf(stderr, "small window %.3f\n", ((double) w*h)/(dpy_x * dpy_y));
   4444 		return 0;
   4445 	}
   4446 if (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)) {
   4466 if (db) fprintf(stderr, "OUTSIDE CLIPSHIFT\n");
   4467 			try_it = 0;
   4468 		}
   4469 		sraRgnDestroy(r1);
   4470 		sraRgnDestroy(r2);
   4471 	}
   4472 	if (! try_it) {
   4473 if (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 
   4621 if (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) {
   4628 if (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) {
   4642 if (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) {
   4652 if (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) {
   4659 if (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 
   4670 if (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;
   4703 if (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;
   4712 if (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;
   4720 if (db) fprintf(stderr, "FRAME-UNMAPPED: 0x%lx\n", orig_frame);
   4721 					break_reason = 5;
   4722 					break;
   4723 				}
   4724 
   4725 if (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 
   4735 if (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);
   4736 if (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) {
   4739 if (db) fprintf(stderr, "CHANGED and window switch: 0x%lx\n", frame);
   4740 			}
   4741 			if (frame_changed && px - orig_px != x - orig_x) {
   4742 if (db) fprintf(stderr, "MOVED and diff DX\n");
   4743 			}
   4744 			if (frame_changed && py - orig_py != y - orig_y) {
   4745 if (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 
   4758 if (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;
   4770 if (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 */
   4782 if (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;
   4810 if (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) {
   4901 if (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: */
   4978 if (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 
   4982 if (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 
   4986 if (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 
   5006 if ((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 
   5126 static 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;
   5181 if (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;
   5196 if (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;
   5254 if (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 
   5272 static 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 
   5392 int 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 
   5423 static 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 
   5633 int 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 		 */
   5664 if (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))
   5715 int check_ncache(int a, int b) {
   5716 	if (!a || !b) {}
   5717 	ncache = 0;
   5718 	return 0;
   5719 }
   5720 int lookup_win_index(Window win) {
   5721 	if (!win) {}
   5722 	return -1;
   5723 }
   5724 int find_rect(int idx, int x, int y, int w, int h) {
   5725 	if (!idx || !x || !y || !w || !h) {}
   5726 	return 0;
   5727 }
   5728 void snap_old(void) {
   5729 	return;
   5730 }
   5731 int clipped(int idx) {
   5732 	if (!idx) {}
   5733 	return 0;
   5734 }
   5735 int 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 }
   5739 int 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 }
   5743 int 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 }
   5748 void set_ncache_xrootpmap(void) {
   5749 	return;
   5750 }
   5751 #else
   5752 /* maybe ncache.c it if works */
   5753 
   5754 winattr_t* cache_list = NULL;
   5755 int cache_list_num = 0;
   5756 int cache_list_len = 0;
   5757 
   5758 void 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 
   5864 void 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 
   5914 int 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
   5925 Window recent[NRECENT];
   5926 int    recidx[NRECENT];
   5927 int rlast, rfree;
   5928 
   5929 int 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;
   5942 if (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;
   5956 if (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) {
   5968 if (ncdb) fprintf(stderr, "recentC(fail): %d  0x%lx\n", idx, win);
   5969 		s3++;
   5970 	}
   5971 	if (s1 + s2 + s3 >= 1000) {
   5972 if (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 
   5978 int lookup_free_index(void) {
   5979 	int k;
   5980 
   5981 	if (rfree >= 0) {
   5982 		if (cache_list[rfree].win == None) {
   5983 if (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 
   6025 if (ncdb) fprintf(stderr, "lookup_freeB: %d\n", rfree);
   6026 	return rfree;
   6027 }
   6028 
   6029 #define STACKMAX 4096
   6030 Window old_stack[STACKMAX];
   6031 Window new_stack[STACKMAX];
   6032 Window old_stack_map[STACKMAX];
   6033 Window new_stack_map[STACKMAX];
   6034 int old_stack_index[STACKMAX];
   6035 int old_stack_mapped[STACKMAX];
   6036 int old_stack_n = 0;
   6037 int new_stack_n = 0;
   6038 int old_stack_map_n = 0;
   6039 int new_stack_map_n = 0;
   6040 
   6041 void snap_old(void) {
   6042 	int i;
   6043 	old_stack_n = STACKMAX;
   6044 	quick_snap(old_stack, &old_stack_n);
   6045 if (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 
   6061 void 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 
   6076 int 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 
   6146 static	char unk[32];
   6147 
   6148 char *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 }
   6186 char *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 }
   6193 char *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 }
   6199 sraRegionPtr rect_reg[64];
   6200 sraRegionPtr zero_rects = NULL;
   6201 
   6202 int 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) {
   6208 if (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);
   6219 if (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);
   6247 if (! ok && ncdb) fprintf(stderr, "**** free_rect: not-found %d\n", idx);
   6248 	return ok;
   6249 }
   6250 
   6251 int fr_BIG1 = 0;
   6252 int fr_BIG2 = 0;
   6253 int fr_REGION = 0;
   6254 int fr_GRID = 0;
   6255 int fr_EXPIRE = 0;
   6256 int fr_FORCE = 0;
   6257 int fr_FAIL = 0;
   6258 int fr_BIG1t = 0;
   6259 int fr_BIG2t = 0;
   6260 int fr_REGIONt = 0;
   6261 int fr_GRIDt = 0;
   6262 int fr_EXPIREt = 0;
   6263 int fr_FORCEt = 0;
   6264 int fr_FAILt = 0;
   6265 
   6266 void 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 
   6326 if (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 
   6411 if (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 
   6418 void 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;
   6521 if (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;
   6614 if (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) {
   6626 if (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];
   6644 if (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 
   6660 if (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 
   6710 if (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 
   6717 void 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 
   6726 int 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) {
   6788 if (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) {
   6839 if (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) {
   6845 if (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) {
   6851 if (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) {
   6886 if (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);
   6940 if (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 
   6986 static 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 
   7006 double save_delay0    = 0.02;
   7007 double restore_delay0 = 0.02;
   7008 double save_delay1    = 0.05;
   7009 double restore_delay1 = 0.05;
   7010 static double dtA, dtB;
   7011 
   7012 int 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 
   7033 int 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)) {
   7082 if (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);
   7092 if (0) fprintf(stderr, "clip[0x%lx]: %s\n", win, clip ? "clipped" : "no-clipped");
   7093 	return clip;
   7094 }
   7095 
   7096 void 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 
   7102 if (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);
   7126 if (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 
   7132 int 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 
   7145 if (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)) {
   7154 if (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) {
   7186 if (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)) {
   7211 if (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();
   7223 if (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();
   7228 if (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 
   7238 int 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 
   7246 if (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)) {
   7260 if (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) {
   7287 if (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)) {
   7312 if (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();
   7325 if (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();
   7330 if (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 
   7340 int 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 
   7348 if (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)) {
   7362 if (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();
   7428 if (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();
   7433 if (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 
   7443 int 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 
   7451 if (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)) {
   7469 if (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) {
   7490 if (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();
   7537 if (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();
   7542 if (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 
   7552 void 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 
   7571 void 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
   7669 Window sched_bs[NSCHED];
   7670 double sched_tm[NSCHED];
   7671 double 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 
   7695 void 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 	}
   7735 if (sync && ncdb) fprintf(stderr, "XSELECTINPUT: 0x%lx  sync=%d err=%d/%d\n", w, sync, trapped_xerror, trapped_xioerror);
   7736 }
   7737 
   7738 Bool 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
   7761 XEvent Ev[EVMAX];
   7762 int Ev_done[EVMAX];
   7763 int Ev_order[EVMAX];
   7764 int Ev_area[EVMAX];
   7765 int Ev_tmp[EVMAX];
   7766 int Ev_tmp2[EVMAX];
   7767 Window Ev_tmpwin[EVMAX];
   7768 Window Ev_win[EVMAX];
   7769 Window Ev_map[EVMAX];
   7770 Window Ev_unmap[EVMAX];
   7771 sraRect Ev_rects[EVMAX];
   7772 
   7773 int tmp_stack[STACKMAX];
   7774 sraRegionPtr 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 
   7783 int 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 
   7812 if (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 {
   7859 if (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 
   7914 int 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 	}
   7934 if (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) {
   7971 if (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) {
   7976 if (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 
   8100 void 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 
   8120 sraRegionPtr 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 
   8139 void 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 
   8169 void 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
   8260 Window _ev_list[EVLISTMAX];
   8261 int _ev_case[EVLISTMAX];
   8262 int _ev_list_cnt;
   8263 
   8264 int n_CN = 0, n_RN = 0, n_DN = 0, n_ON = 0, n_MN = 0, n_UN = 0;
   8265 int n_VN = 0, n_VN_p = 0, n_VN_u = 0, n_ST = 0, n_PN = 0, n_DC = 0;
   8266 int n_ON_sz = 0, n_ON_po = 0, n_ON_st = 0;
   8267 
   8268 int 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 
   8284 int 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 
   8294 unsigned long all_ev = SubstructureNotifyMask|StructureNotifyMask|VisibilityChangeMask;
   8295 unsigned long win_ev = StructureNotifyMask|VisibilityChangeMask;
   8296 
   8297 void 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;
   8395 if (type == DestroyNotify) w = Ev[n].xdestroywindow.window;
   8396 if (type == UnmapNotify)   w = Ev[n].xunmap.window;
   8397 if (type == MapNotify)     w = Ev[n].xmap.window;
   8398 if (type == Expose)        w = Ev[n].xexpose.window;
   8399 if (type == ConfigureNotify) w = Ev[n].xconfigure.window;
   8400 if (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 */
   8451 if (ncdb) fprintf(stderr, "----- skip %s\n", Etype(type));
   8452 			}
   8453 		}
   8454 		if (n >= EVMAX) {
   8455 			break;
   8456 		}
   8457 	}
   8458 	*n_in = n;
   8459 }
   8460 
   8461 int 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 		}
   8506 if (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) {
   8539 if (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 		}
   8622 if (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 
   8630 static double last_vis_unobs_time = 0.0;
   8631 static double last_vis_obs_time = 0.0;
   8632 
   8633 static int saw_desktop_change = 0;
   8634 
   8635 void 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 
   8646 if (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 {
   8666 if (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) {
   8674 if (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 {
   8734 if (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 {
   8744 if (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) {
   8835 if (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;
   8840 if (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) {
   8874 if (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 
   8882 int 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();
   8963 if (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 
   9089 if (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) {
   9196 if (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 					}
   9208 if (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;
   9214 if (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);
   9227 if (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 	}
   9261 if (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) {
   9290 if (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++;
   9376 if (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) {
   9381 if (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) {
   9409 if (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) {
   9417 if (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]) {
   9433 if (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]) {
   9448 if (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) {
   9486 if (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 
   9612 if (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 
   9647 for (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) {
   9679 if (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 				}
   9712 if (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);
   9718 if (ncdb) fprintf(stderr, "root%02d: ReparentNotifyRM 0x%lx  %3d\n", ik, win2, idx);
   9719 				}
   9720 			} else {
   9721 if (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 
   9736 if (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;
   9777 if (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;
   9782 if (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 */
   9789 if (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) {
   9794 if (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 {
   9798 if (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;
   9823 if (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);
   9869 if (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);
   9972 if (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);
   10069 if (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);
   10155 if (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);
   10162 if (ncdb) fprintf(stderr, "----%02d: DestroyNotify    0x%lx  %3d\n", ik, win2, idx);
   10163 
   10164 				if (idx >= 0) {
   10165 					DELETE(idx);
   10166 				}
   10167 			} else {
   10168 if (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 
   10194 if (ncdb) rfbLog("OUT check_ncache(): %.4f %.6f events: %d  pixels: %d\n", dnowx(), dnow() - now, n, pixels);
   10195 if (ncdb) fprintf(stderr, "\n");
   10196 	return pixels;
   10197 }
   10198 #endif
   10199 
   10200