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 /* -- xrecord.c -- */
     34 
     35 #include "x11vnc.h"
     36 #include "xwrappers.h"
     37 #include "win_utils.h"
     38 #include "cleanup.h"
     39 #include "userinput.h"
     40 #include "winattr_t.h"
     41 #include "scrollevent_t.h"
     42 #include "unixpw.h"
     43 
     44 #define SCR_EV_MAX 128
     45 scroll_event_t scr_ev[SCR_EV_MAX];
     46 int scr_ev_cnt;
     47 
     48 int xrecording = 0;
     49 int xrecord_set_by_keys = 0;
     50 int xrecord_set_by_mouse = 0;
     51 Window xrecord_focus_window = None;
     52 Window xrecord_wm_window = None;
     53 Window xrecord_ptr_window = None;
     54 KeySym xrecord_keysym = NoSymbol;
     55 
     56 #define NAMEINFO 2048
     57 char xrecord_name_info[NAMEINFO];
     58 
     59 #define SCR_ATTR_CACHE 8
     60 winattr_t scr_attr_cache[SCR_ATTR_CACHE];
     61 static double attr_cache_max_age = 1.5;
     62 
     63 Display *rdpy_data = NULL;		/* Data connection for RECORD */
     64 Display *rdpy_ctrl = NULL;		/* Control connection for RECORD */
     65 
     66 Display *gdpy_ctrl = NULL;
     67 Display *gdpy_data = NULL;
     68 int xserver_grabbed = 0;
     69 
     70 int trap_record_xerror(Display *, XErrorEvent *);
     71 
     72 void initialize_xrecord(void);
     73 void zerodisp_xrecord(void);
     74 void shutdown_xrecord(void);
     75 int xrecord_skip_keysym(rfbKeySym keysym);
     76 int xrecord_skip_button(int newb, int old);
     77 int xrecord_scroll_keysym(rfbKeySym keysym);
     78 void check_xrecord_reset(int force);
     79 void xrecord_watch(int start, int setby);
     80 
     81 
     82 #if LIBVNCSERVER_HAVE_RECORD
     83 static XRecordRange *rr_CA = NULL;
     84 static XRecordRange *rr_CW = NULL;
     85 static XRecordRange *rr_GS = NULL;
     86 static XRecordRange *rr_scroll[10];
     87 static XRecordContext rc_scroll;
     88 static XRecordClientSpec rcs_scroll;
     89 static XRecordRange *rr_grab[10];
     90 static XRecordContext rc_grab;
     91 static XRecordClientSpec rcs_grab;
     92 #endif
     93 static XErrorEvent *trapped_record_xerror_event;
     94 
     95 static void xrecord_grabserver(int start);
     96 static int xrecord_vi_scroll_keysym(rfbKeySym keysym);
     97 static int xrecord_emacs_scroll_keysym(rfbKeySym keysym);
     98 static int lookup_attr_cache(Window win, int *cache_index, int *next_index);
     99 #if LIBVNCSERVER_HAVE_RECORD
    100 static void record_CA(XPointer ptr, XRecordInterceptData *rec_data);
    101 static void record_CW(XPointer ptr, XRecordInterceptData *rec_data);
    102 static void record_switch(XPointer ptr, XRecordInterceptData *rec_data);
    103 static void record_grab(XPointer ptr, XRecordInterceptData *rec_data);
    104 static void shutdown_record_context(XRecordContext rc, int bequiet, int reopen);
    105 #endif
    106 static void check_xrecord_grabserver(void);
    107 
    108 
    109 int trap_record_xerror(Display *d, XErrorEvent *error) {
    110 	trapped_record_xerror = 1;
    111 	trapped_record_xerror_event = error;
    112 
    113 	if (d) {} /* unused vars warning: */
    114 
    115 	return 0;
    116 }
    117 
    118 static void xrecord_grabserver(int start) {
    119 	XErrorHandler old_handler = NULL;
    120 	int rc = 0;
    121 
    122 	if (debug_grabs) {
    123 		fprintf(stderr, "xrecord_grabserver%d/%d %.5f\n",
    124 			xserver_grabbed, start, dnowx());
    125 	}
    126 
    127 	if (! gdpy_ctrl || ! gdpy_data) {
    128 		return;
    129 	}
    130 #if LIBVNCSERVER_HAVE_RECORD
    131 	if (!start) {
    132 		if (! rc_grab) {
    133 			return;
    134 		}
    135 		XRecordDisableContext(gdpy_ctrl, rc_grab);
    136 		XRecordFreeContext(gdpy_ctrl, rc_grab);
    137 		XFlush_wr(gdpy_ctrl);
    138 		rc_grab = 0;
    139 		return;
    140 	}
    141 
    142 	xserver_grabbed = 0;
    143 
    144 	rr_grab[0] = rr_GS;
    145 	rcs_grab = XRecordAllClients;
    146 
    147 	rc_grab = XRecordCreateContext(gdpy_ctrl, 0, &rcs_grab, 1, rr_grab, 1);
    148 	trapped_record_xerror = 0;
    149 	old_handler = XSetErrorHandler(trap_record_xerror);
    150 
    151 	XSync(gdpy_ctrl, True);
    152 
    153 	if (! rc_grab || trapped_record_xerror) {
    154 		XCloseDisplay_wr(gdpy_ctrl);
    155 		XCloseDisplay_wr(gdpy_data);
    156 		gdpy_ctrl = NULL;
    157 		gdpy_data = NULL;
    158 		XSetErrorHandler(old_handler);
    159 		return;
    160 	}
    161 	rc = XRecordEnableContextAsync(gdpy_data, rc_grab, record_grab, NULL);
    162 	if (!rc || trapped_record_xerror) {
    163 		XCloseDisplay_wr(gdpy_ctrl);
    164 		XCloseDisplay_wr(gdpy_data);
    165 		gdpy_ctrl = NULL;
    166 		gdpy_data = NULL;
    167 		XSetErrorHandler(old_handler);
    168 		return;
    169 	}
    170 	XFlush_wr(gdpy_data);
    171 	XSetErrorHandler(old_handler);
    172 #else
    173 	if (!rc || !old_handler) {}
    174 #endif
    175 	if (debug_grabs) {
    176 		fprintf(stderr, "xrecord_grabserver-done: %.5f\n", dnowx());
    177 	}
    178 }
    179 
    180 void zerodisp_xrecord(void) {
    181 	rdpy_data = NULL;
    182 	rdpy_ctrl = NULL;
    183 	gdpy_data = NULL;
    184 	gdpy_ctrl = NULL;
    185 }
    186 
    187 void initialize_xrecord(void) {
    188 	use_xrecord = 0;
    189 	if (! xrecord_present) {
    190 		return;
    191 	}
    192 	if (nofb) {
    193 		return;
    194 	}
    195 	if (noxrecord) {
    196 		return;
    197 	}
    198 	RAWFB_RET_VOID
    199 #if LIBVNCSERVER_HAVE_RECORD
    200 
    201 	if (rr_CA) XFree_wr(rr_CA);
    202 	if (rr_CW) XFree_wr(rr_CW);
    203 	if (rr_GS) XFree_wr(rr_GS);
    204 
    205 	rr_CA = XRecordAllocRange();
    206 	rr_CW = XRecordAllocRange();
    207 	rr_GS = XRecordAllocRange();
    208 	if (!rr_CA || !rr_CW || !rr_GS) {
    209 		return;
    210 	}
    211 	/* protocol request ranges: */
    212 	rr_CA->core_requests.first = X_CopyArea;
    213 	rr_CA->core_requests.last  = X_CopyArea;
    214 
    215 	rr_CW->core_requests.first = X_ConfigureWindow;
    216 	rr_CW->core_requests.last  = X_ConfigureWindow;
    217 
    218 	rr_GS->core_requests.first = X_GrabServer;
    219 	rr_GS->core_requests.last  = X_UngrabServer;
    220 
    221 	X_LOCK;
    222 	/* open a 2nd control connection to DISPLAY: */
    223 	if (rdpy_data) {
    224 		XCloseDisplay_wr(rdpy_data);
    225 		rdpy_data = NULL;
    226 	}
    227 	if (rdpy_ctrl) {
    228 		XCloseDisplay_wr(rdpy_ctrl);
    229 		rdpy_ctrl = NULL;
    230 	}
    231 	rdpy_ctrl = XOpenDisplay_wr(DisplayString(dpy));
    232 	if (!rdpy_ctrl) {
    233 		fprintf(stderr, "rdpy_ctrl open failed: %s / %s / %s / %s\n", getenv("DISPLAY"), DisplayString(dpy), getenv("XAUTHORITY"), getenv("XAUTHORIT_"));
    234 	}
    235 	XSync(dpy, True);
    236 	XSync(rdpy_ctrl, True);
    237 	/* open datalink connection to DISPLAY: */
    238 	rdpy_data = XOpenDisplay_wr(DisplayString(dpy));
    239 	if (!rdpy_data) {
    240 		fprintf(stderr, "rdpy_data open failed\n");
    241 	}
    242 	if (!rdpy_ctrl || ! rdpy_data) {
    243 		X_UNLOCK;
    244 		return;
    245 	}
    246 	disable_grabserver(rdpy_ctrl, 0);
    247 	disable_grabserver(rdpy_data, 0);
    248 
    249 	use_xrecord = 1;
    250 
    251 	/*
    252 	 * now set up the GrabServer watcher.  We get GrabServer
    253 	 * deadlock in XRecordCreateContext() even with XTestGrabServer
    254 	 * in place, why?  Not sure, so we manually watch for grabs...
    255 	 */
    256 	if (gdpy_data) {
    257 		XCloseDisplay_wr(gdpy_data);
    258 		gdpy_data = NULL;
    259 	}
    260 	if (gdpy_ctrl) {
    261 		XCloseDisplay_wr(gdpy_ctrl);
    262 		gdpy_ctrl = NULL;
    263 	}
    264 	xserver_grabbed = 0;
    265 
    266 	gdpy_ctrl = XOpenDisplay_wr(DisplayString(dpy));
    267 	if (!gdpy_ctrl) {
    268 		fprintf(stderr, "gdpy_ctrl open failed\n");
    269 	}
    270 	XSync(dpy, True);
    271 	XSync(gdpy_ctrl, True);
    272 	gdpy_data = XOpenDisplay_wr(DisplayString(dpy));
    273 	if (!gdpy_data) {
    274 		fprintf(stderr, "gdpy_data open failed\n");
    275 	}
    276 	if (gdpy_ctrl && gdpy_data) {
    277 		disable_grabserver(gdpy_ctrl, 0);
    278 		disable_grabserver(gdpy_data, 0);
    279 		xrecord_grabserver(1);
    280 	}
    281 	X_UNLOCK;
    282 #endif
    283 }
    284 
    285 void shutdown_xrecord(void) {
    286 #if LIBVNCSERVER_HAVE_RECORD
    287 
    288 	if (debug_grabs) {
    289 		fprintf(stderr, "shutdown_xrecord%d %.5f\n",
    290 			xserver_grabbed, dnowx());
    291 	}
    292 
    293 	if (rr_CA) XFree_wr(rr_CA);
    294 	if (rr_CW) XFree_wr(rr_CW);
    295 	if (rr_GS) XFree_wr(rr_GS);
    296 
    297 	rr_CA = NULL;
    298 	rr_CW = NULL;
    299 	rr_GS = NULL;
    300 
    301 	X_LOCK;
    302 	if (rdpy_ctrl && rc_scroll) {
    303 		XRecordDisableContext(rdpy_ctrl, rc_scroll);
    304 		XRecordFreeContext(rdpy_ctrl, rc_scroll);
    305 		XSync(rdpy_ctrl, False);
    306 		rc_scroll = 0;
    307 	}
    308 
    309 	if (gdpy_ctrl && rc_grab) {
    310 		XRecordDisableContext(gdpy_ctrl, rc_grab);
    311 		XRecordFreeContext(gdpy_ctrl, rc_grab);
    312 		XSync(gdpy_ctrl, False);
    313 		rc_grab = 0;
    314 	}
    315 
    316 	if (rdpy_data) {
    317 		XCloseDisplay_wr(rdpy_data);
    318 		rdpy_data = NULL;
    319 	}
    320 	if (rdpy_ctrl) {
    321 		XCloseDisplay_wr(rdpy_ctrl);
    322 		rdpy_ctrl = NULL;
    323 	}
    324 	if (gdpy_data) {
    325 		XCloseDisplay_wr(gdpy_data);
    326 		gdpy_data = NULL;
    327 	}
    328 	if (gdpy_ctrl) {
    329 		XCloseDisplay_wr(gdpy_ctrl);
    330 		gdpy_ctrl = NULL;
    331 	}
    332 	xserver_grabbed = 0;
    333 	X_UNLOCK;
    334 #endif
    335 	use_xrecord = 0;
    336 
    337 	if (debug_grabs) {
    338 		fprintf(stderr, "shutdown_xrecord-done: %.5f\n", dnowx());
    339 	}
    340 }
    341 
    342 int xrecord_skip_keysym(rfbKeySym keysym) {
    343 	KeySym sym = (KeySym) keysym;
    344 	int ok = -1, matched = 0;
    345 
    346 	if (scroll_key_list) {
    347 		int k, exclude = 0;
    348 		if (scroll_key_list[0]) {
    349 			exclude = 1;
    350 		}
    351 		k = 1;
    352 		while (scroll_key_list[k] != NoSymbol) {
    353 			if (scroll_key_list[k++] == sym) {
    354 				matched = 1;
    355 				break;
    356 			}
    357 		}
    358 		if (exclude) {
    359 			if (matched) {
    360 				return 1;
    361 			} else {
    362 				ok = 1;
    363 			}
    364 		} else {
    365 			if (matched) {
    366 				ok = 1;
    367 			} else {
    368 				ok = 0;
    369 			}
    370 		}
    371 	}
    372 	if (ok == 1) {
    373 		return 0;
    374 	} else if (ok == 0) {
    375 		return 1;
    376 	}
    377 
    378 	/* apply various heuristics: */
    379 
    380 	if (IsModifierKey(sym)) {
    381 		/* Shift, Control, etc, usu. generate no scrolls */
    382 		return 1;
    383 	}
    384 	if (sym == XK_space && scroll_term) {
    385 		/* space in a terminal is usu. full page... */
    386 		Window win;
    387 		static Window prev_top = None;
    388 		int size = 256;
    389 		static char name[256];
    390 
    391 		X_LOCK;
    392 		win = query_pointer(rootwin);
    393 		X_UNLOCK;
    394 		if (win != None && win != rootwin) {
    395 			if (prev_top != None && win == prev_top) {
    396 				;	/* use cached result */
    397 			} else {
    398 				prev_top = win;
    399 				X_LOCK;
    400 				win = descend_pointer(6, win, name, size);
    401 				X_UNLOCK;
    402 			}
    403 			if (match_str_list(name, scroll_term)) {
    404 				return 1;
    405 			}
    406 		}
    407 	}
    408 
    409 	/* TBD use typing_rate() so */
    410 	return 0;
    411 }
    412 
    413 int xrecord_skip_button(int new_button, int old) {
    414 	/* unused vars warning: */
    415 	if (new_button || old) {}
    416 
    417 	return 0;
    418 }
    419 
    420 static int xrecord_vi_scroll_keysym(rfbKeySym keysym) {
    421 	KeySym sym = (KeySym) keysym;
    422 	if (sym == XK_J || sym == XK_j || sym == XK_K || sym == XK_k) {
    423 		return 1;	/* vi */
    424 	}
    425 	if (sym == XK_D || sym == XK_d || sym == XK_U || sym == XK_u) {
    426 		return 1;	/* Ctrl-d/u */
    427 	}
    428 	if (sym == XK_Z || sym == XK_z) {
    429 		return 1;	/* zz, zt, zb .. */
    430 	}
    431 	return 0;
    432 }
    433 
    434 static int xrecord_emacs_scroll_keysym(rfbKeySym keysym) {
    435 	KeySym sym = (KeySym) keysym;
    436 	if (sym == XK_N || sym == XK_n || sym == XK_P || sym == XK_p) {
    437 		return 1;	/* emacs */
    438 	}
    439 	/* Must be some more ... */
    440 	return 0;
    441 }
    442 
    443 int xrecord_scroll_keysym(rfbKeySym keysym) {
    444 	KeySym sym = (KeySym) keysym;
    445 	/* X11/keysymdef.h */
    446 
    447 	if (sym == XK_Return || sym == XK_KP_Enter || sym == XK_Linefeed) {
    448 		return 1;	/* Enter */
    449 	}
    450 	if (sym==XK_Up || sym==XK_KP_Up || sym==XK_Down || sym==XK_KP_Down) {
    451 		return 1;	/* U/D arrows */
    452 	}
    453 	if (sym == XK_Left || sym == XK_KP_Left || sym == XK_Right ||
    454 	    sym == XK_KP_Right) {
    455 		return 1;	/* L/R arrows */
    456 	}
    457 	if (xrecord_vi_scroll_keysym(keysym)) {
    458 		return 1;
    459 	}
    460 	if (xrecord_emacs_scroll_keysym(keysym)) {
    461 		return 1;
    462 	}
    463 	return 0;
    464 }
    465 
    466 static int lookup_attr_cache(Window win, int *cache_index, int *next_index) {
    467 	double now, t, oldest = 0.0;
    468 	int i, old_index = -1, count = 0;
    469 	Window cwin;
    470 
    471 	*cache_index = -1;
    472 	*next_index  = -1;
    473 
    474 	if (win == None) {
    475 		return 0;
    476 	}
    477 	if (attr_cache_max_age == 0.0) {
    478 		return 0;
    479 	}
    480 
    481 	dtime0(&now);
    482 	for (i=0; i < SCR_ATTR_CACHE; i++) {
    483 
    484 		cwin = scr_attr_cache[i].win;
    485 		t = scr_attr_cache[i].time;
    486 
    487 		if (now > t + attr_cache_max_age) {
    488 			/* expire it even if it is the one we want */
    489 			scr_attr_cache[i].win = cwin = None;
    490 			scr_attr_cache[i].fetched = 0;
    491 			scr_attr_cache[i].valid = 0;
    492 		}
    493 
    494 		if (*next_index == -1 && cwin == None) {
    495 			*next_index = i;
    496 		}
    497 		if (*next_index == -1) {
    498 			/* record oldest */
    499 			if (old_index == -1 || t < oldest) {
    500 				oldest = t;
    501 				old_index = i;
    502 			}
    503 		}
    504 		if (cwin != None) {
    505 			count++;
    506 		}
    507 		if (cwin == win) {
    508 			if (*cache_index == -1) {
    509 				*cache_index = i;
    510 			} else {
    511 				/* remove dups */
    512 				scr_attr_cache[i].win = None;
    513 				scr_attr_cache[i].fetched = 0;
    514 				scr_attr_cache[i].valid = 0;
    515 			}
    516 		}
    517 	}
    518 	if (*next_index == -1) {
    519 		*next_index = old_index;
    520 	}
    521 
    522 if (0) fprintf(stderr, "lookup_attr_cache count: %d\n", count);
    523 	if (*cache_index != -1) {
    524 		return 1;
    525 	} else {
    526 		return 0;
    527 	}
    528 }
    529 
    530 
    531 static XID xrecord_seq = 0;
    532 static double xrecord_start = 0.0;
    533 
    534 #if LIBVNCSERVER_HAVE_RECORD
    535 static void record_CA(XPointer ptr, XRecordInterceptData *rec_data) {
    536 	xCopyAreaReq *req;
    537 	Window src = None, dst = None, c;
    538 	XWindowAttributes attr, attr2;
    539 	int src_x, src_y, dst_x, dst_y, rx, ry, rx2, ry2;
    540 	int good = 1, dx = 0, dy = 0, k=0, i;
    541 	unsigned int w, h;
    542 	int dba = 0, db = debug_scroll;
    543 	int cache_index, next_index, valid;
    544 	static int must_equal = -1;
    545 
    546 	if (dba || db) {
    547 		if (rec_data->category == XRecordFromClient) {
    548 			req = (xCopyAreaReq *) rec_data->data;
    549 			if (req->reqType == X_CopyArea) {
    550 				src = req->srcDrawable;
    551 				dst = req->dstDrawable;
    552 			}
    553 		}
    554 	}
    555 
    556 if (dba || db > 1) fprintf(stderr, "record_CA-%d id_base: 0x%lx  ptr: 0x%lx "
    557 	"seq: 0x%lx rc: 0x%lx  cat: %d  swapped: %d 0x%lx/0x%lx\n", k++,
    558 	rec_data->id_base, (unsigned long) ptr, xrecord_seq, rc_scroll,
    559 	rec_data->category, rec_data->client_swapped, src, dst);
    560 
    561 	if (! xrecording) {
    562 		return;
    563 	}
    564 if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
    565 
    566 	if (rec_data->id_base == 0) {
    567 		return;
    568 	}
    569 if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
    570 
    571 	if ((XID) ptr != xrecord_seq) {
    572 		return;
    573 	}
    574 if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
    575 
    576 	if (rec_data->category != XRecordFromClient) {
    577 		return;
    578 	}
    579 if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
    580 
    581 	req = (xCopyAreaReq *) rec_data->data;
    582 
    583 	if (req->reqType != X_CopyArea) {
    584 		return;
    585 	}
    586 if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
    587 
    588 	if (must_equal < 0) {
    589 		must_equal = 0;
    590 		if (getenv("X11VNC_SCROLL_MUST_EQUAL")) {
    591 			must_equal = 1;
    592 		}
    593 	}
    594 
    595 /*
    596 
    597 xterm, gnome-terminal, others.
    598 
    599 Note we miss the X_ImageText8 that clears the block cursor.  So there is a
    600 short period of time with a painting error: two cursors, one above the other.
    601 
    602  X_ImageText8
    603     draw: 0x8c00017 nChars: 1, gc: 0x8c00013, x: 101, y: 585, chars=' '
    604  X_ClearArea
    605     window: 0x8c00018, x:   2, y: 217, w:  10, h:   5
    606  X_FillPoly
    607     draw: 0x8c00018 gc: 0x8c0000a, shape: 0, coordMode: 0,
    608  X_FillPoly
    609     draw: 0x8c00018 gc: 0x8c0000b, shape: 0, coordMode: 0,
    610  X_CopyArea
    611     src: 0x8c00017, dst: 0x8c00017, gc: 0x8c00013, srcX:  17, srcY:  15, dstX:  17, dstY:   2, w: 480, h: 572
    612  X_ChangeWindowAttributes
    613  X_ClearArea
    614     window: 0x8c00017, x:  17, y: 574, w: 480, h:  13
    615  X_ChangeWindowAttributes
    616 
    617  */
    618 
    619 	src = req->srcDrawable;
    620 	dst = req->dstDrawable;
    621 	src_x = req->srcX;
    622 	src_y = req->srcY;
    623 	dst_x = req->dstX;
    624 	dst_y = req->dstY;
    625 	w = req->width;
    626 	h = req->height;
    627 
    628 	if (w*h < (unsigned int) scrollcopyrect_min_area) {
    629 		if (db > 1) fprintf(stderr, "record_CA scroll area too small.\n");
    630 		good = 0;
    631 	} else if (!src || !dst) {
    632 		if (db > 1) fprintf(stderr, "record_CA null src or dst.\n");
    633 		good = 0;
    634 	} else if (scr_ev_cnt >= SCR_EV_MAX) {
    635 		if (db > 1) fprintf(stderr, "record_CA null too many scr events.\n");
    636 		good = 0;
    637 	} else if (must_equal && src != dst) {
    638 		if (db > 1) fprintf(stderr, "record_CA src not equal dst.\n");
    639 		good = 0;
    640 	}
    641 
    642 	if (src == dst) {
    643 		dx = dst_x - src_x;
    644 		dy = dst_y - src_y;
    645 
    646 		if (dx != 0 && dy != 0) {
    647 			good = 0;
    648 		}
    649 	}
    650 
    651 if (!good && (dba || db > 1)) fprintf(stderr, "record_CA-x src_x: %d src_y: %d "
    652 	"dst_x: %d dst_y: %d w: %d h: %d scr_ev_cnt: %d 0x%lx/0x%lx\n",
    653 	src_x, src_y, dst_x, dst_y, w, h, scr_ev_cnt, src, dst);
    654 
    655 	if (! good) {
    656 		return;
    657 	}
    658 
    659 if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
    660 
    661 	/*
    662 	 * after all of the above succeeds, now contact X server.
    663 	 * we try to get away with some caching here.
    664 	 */
    665 	if (lookup_attr_cache(src, &cache_index, &next_index)) {
    666 		i = cache_index;
    667 		attr.x = scr_attr_cache[i].x;
    668 		attr.y = scr_attr_cache[i].y;
    669 		attr.width = scr_attr_cache[i].width;
    670 		attr.height = scr_attr_cache[i].height;
    671 		attr.map_state = scr_attr_cache[i].map_state;
    672 		rx = scr_attr_cache[i].rx;
    673 		ry = scr_attr_cache[i].ry;
    674 		valid = scr_attr_cache[i].valid;
    675 
    676 	} else {
    677 		valid = valid_window(src, &attr, 1);
    678 
    679 		if (valid) {
    680 			if (!xtranslate(src, rootwin, 0, 0, &rx, &ry, &c, 1)) {
    681 				valid = 0;
    682 			}
    683 		}
    684 		if (next_index >= 0) {
    685 			i = next_index;
    686 			scr_attr_cache[i].win = src;
    687 			scr_attr_cache[i].fetched = 1;
    688 			scr_attr_cache[i].valid = valid;
    689 			scr_attr_cache[i].time = dnow();
    690 			if (valid) {
    691 				scr_attr_cache[i].x = attr.x;
    692 				scr_attr_cache[i].y = attr.y;
    693 				scr_attr_cache[i].width = attr.width;
    694 				scr_attr_cache[i].height = attr.height;
    695 				scr_attr_cache[i].border_width = attr.border_width;
    696 				scr_attr_cache[i].depth = attr.depth;
    697 				scr_attr_cache[i].class = attr.class;
    698 				scr_attr_cache[i].backing_store =
    699 				    attr.backing_store;
    700 				scr_attr_cache[i].map_state = attr.map_state;
    701 
    702 				scr_attr_cache[i].rx = rx;
    703 				scr_attr_cache[i].ry = ry;
    704 			}
    705 		}
    706 	}
    707 
    708 	if (! valid) {
    709 		if (db > 1) fprintf(stderr, "record_CA not valid-1.\n");
    710 		return;
    711 	}
    712 if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
    713 
    714 	if (attr.map_state != IsViewable) {
    715 		if (db > 1) fprintf(stderr, "record_CA not viewable-1.\n");
    716 		return;
    717 	}
    718 
    719 	/* recent gdk/gtk windows use different src and dst. for compositing? */
    720 	if (src != dst) {
    721 	    if (lookup_attr_cache(dst, &cache_index, &next_index)) {
    722 		i = cache_index;
    723 		attr2.x = scr_attr_cache[i].x;
    724 		attr2.y = scr_attr_cache[i].y;
    725 		attr2.width = scr_attr_cache[i].width;
    726 		attr2.height = scr_attr_cache[i].height;
    727 		attr2.map_state = scr_attr_cache[i].map_state;
    728 		rx2 = scr_attr_cache[i].rx;
    729 		ry2 = scr_attr_cache[i].ry;
    730 		valid = scr_attr_cache[i].valid;
    731 
    732 	    } else {
    733 		valid = valid_window(dst, &attr2, 1);
    734 
    735 		if (valid) {
    736 			if (!xtranslate(dst, rootwin, 0, 0, &rx2, &ry2, &c, 1)) {
    737 				valid = 0;
    738 			}
    739 		}
    740 		if (next_index >= 0) {
    741 			i = next_index;
    742 			scr_attr_cache[i].win = dst;
    743 			scr_attr_cache[i].fetched = 1;
    744 			scr_attr_cache[i].valid = valid;
    745 			scr_attr_cache[i].time = dnow();
    746 			if (valid) {
    747 				scr_attr_cache[i].x = attr2.x;
    748 				scr_attr_cache[i].y = attr2.y;
    749 				scr_attr_cache[i].width = attr2.width;
    750 				scr_attr_cache[i].height = attr2.height;
    751 				scr_attr_cache[i].border_width = attr2.border_width;
    752 				scr_attr_cache[i].depth = attr2.depth;
    753 				scr_attr_cache[i].class = attr2.class;
    754 				scr_attr_cache[i].backing_store =
    755 				    attr2.backing_store;
    756 				scr_attr_cache[i].map_state = attr2.map_state;
    757 
    758 				scr_attr_cache[i].rx = rx2;
    759 				scr_attr_cache[i].ry = ry2;
    760 			}
    761 		}
    762 	    }
    763 
    764 if (dba || db > 1) fprintf(stderr, "record_CA-? src_x: %d src_y: %d "
    765 	"dst_x: %d dst_y: %d w: %d h: %d scr_ev_cnt: %d 0x%lx/0x%lx\n",
    766 	src_x, src_y, dst_x, dst_y, w, h, scr_ev_cnt, src, dst);
    767 
    768 		if (! valid) {
    769 			if (db > 1) fprintf(stderr, "record_CA not valid-2.\n");
    770 			return;
    771 		}
    772 		if (attr2.map_state != IsViewable) {
    773 			if (db > 1) fprintf(stderr, "record_CA not viewable-2.\n");
    774 			return;
    775 		}
    776 		dst_x = dst_x - (rx - rx2);
    777 		dst_y = dst_y - (ry - ry2);
    778 
    779 		dx = dst_x - src_x;
    780 		dy = dst_y - src_y;
    781 
    782 		if (dx != 0 && dy != 0) {
    783 			return;
    784 		}
    785 	}
    786 
    787 
    788  if (0 || dba || db) {
    789 	double st, dt;
    790 	st = (double) rec_data->server_time/1000.0;
    791 	dt = (dnow() - servertime_diff) - st;
    792 	fprintf(stderr, "record_CA-%d *FOUND_SCROLL: src: 0x%lx dx: %d dy: %d "
    793 	"x: %d y: %d w: %d h: %d st: %.4f %.4f  %.4f\n", k++, src, dx, dy,
    794 	src_x, src_y, w, h, st, dt, dnowx());
    795  }
    796 
    797 	i = scr_ev_cnt;
    798 
    799 	scr_ev[i].win = src;
    800 	scr_ev[i].frame = None;
    801 	scr_ev[i].dx = dx;
    802 	scr_ev[i].dy = dy;
    803 	scr_ev[i].x = rx + dst_x;
    804 	scr_ev[i].y = ry + dst_y;
    805 	scr_ev[i].w = w;
    806 	scr_ev[i].h = h;
    807 	scr_ev[i].t = ((double) rec_data->server_time)/1000.0;
    808 	scr_ev[i].win_x = rx;
    809 	scr_ev[i].win_y = ry;
    810 	scr_ev[i].win_w = attr.width;
    811 	scr_ev[i].win_h = attr.height;
    812 	scr_ev[i].new_x = 0;
    813 	scr_ev[i].new_y = 0;
    814 	scr_ev[i].new_w = 0;
    815 	scr_ev[i].new_h = 0;
    816 
    817 	if (dx == 0) {
    818 		if (dy > 0) {
    819 			scr_ev[i].new_x = rx + src_x;
    820 			scr_ev[i].new_y = ry + src_y;
    821 			scr_ev[i].new_w = w;
    822 			scr_ev[i].new_h = dy;
    823 		} else {
    824 			scr_ev[i].new_x = rx + src_x;
    825 			scr_ev[i].new_y = ry + dst_y + h;
    826 			scr_ev[i].new_w = w;
    827 			scr_ev[i].new_h = -dy;
    828 		}
    829 	} else if (dy == 0) {
    830 		if (dx > 0) {
    831 			scr_ev[i].new_x = rx + src_x;
    832 			scr_ev[i].new_y = rx + src_y;
    833 			scr_ev[i].new_w = dx;
    834 			scr_ev[i].new_h = h;
    835 		} else {
    836 			scr_ev[i].new_x = rx + dst_x + w;
    837 			scr_ev[i].new_y = ry + src_y;
    838 			scr_ev[i].new_w = -dx;
    839 			scr_ev[i].new_h = h;
    840 		}
    841 	}
    842 
    843 	scr_ev_cnt++;
    844 }
    845 
    846 typedef struct cw_event {
    847 	Window win;
    848 	int x, y, w, h;
    849 } cw_event_t;
    850 
    851 #define MAX_CW 128
    852 static cw_event_t cw_events[MAX_CW];
    853 
    854 static void record_CW(XPointer ptr, XRecordInterceptData *rec_data) {
    855 	xConfigureWindowReq *req;
    856 	Window win = None, c;
    857 	Window src = None, dst = None;
    858 	XWindowAttributes attr;
    859 	int absent = 0x100000;
    860 	int src_x, src_y, dst_x, dst_y, rx, ry;
    861 	int good = 1, dx, dy, k=0, i, j, match, list[3];
    862 	int f_x, f_y, f_w, f_h;
    863 	int x, y, w, h;
    864 	int x0, y0, w0, h0, x1, y1, w1, h1, x2, y2, w2, h2;
    865 	static int index = 0;
    866 	unsigned int vals[4];
    867 	unsigned tmask;
    868 	char *data;
    869 	int dba = 0, db = debug_scroll;
    870 	int cache_index, next_index, valid;
    871 
    872 	if (db) {
    873 		if (rec_data->category == XRecordFromClient) {
    874 			req = (xConfigureWindowReq *) rec_data->data;
    875 			if (req->reqType == X_ConfigureWindow) {
    876 				src = req->window;
    877 			}
    878 		}
    879 	}
    880 
    881 if (dba || db > 1) fprintf(stderr, "record_CW-%d id_base: 0x%lx  ptr: 0x%lx "
    882 	"seq: 0x%lx rc: 0x%lx  cat: %d  swapped: %d 0x%lx/0x%lx\n", k++,
    883 	rec_data->id_base, (unsigned long) ptr, xrecord_seq, rc_scroll,
    884 	rec_data->category, rec_data->client_swapped, src, dst);
    885 
    886 
    887 	if (! xrecording) {
    888 		return;
    889 	}
    890 if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
    891 
    892 	if ((XID) ptr != xrecord_seq) {
    893 		return;
    894 	}
    895 if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
    896 
    897 	if (rec_data->id_base == 0) {
    898 		return;
    899 	}
    900 if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
    901 
    902 	if (rec_data->category == XRecordStartOfData) {
    903 		index = 0;
    904 		return;
    905 	}
    906 if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
    907 
    908 	if (rec_data->category != XRecordFromClient) {
    909 		return;
    910 	}
    911 if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
    912 
    913 	if (rec_data->client_swapped) {
    914 		return;
    915 	}
    916 if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
    917 
    918 	req = (xConfigureWindowReq *) rec_data->data;
    919 
    920 	if (req->reqType != X_ConfigureWindow) {
    921 		return;
    922 	}
    923 if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
    924 
    925 	tmask = req->mask;
    926 
    927 	tmask &= ~CWX;
    928 	tmask &= ~CWY;
    929 	tmask &= ~CWWidth;
    930 	tmask &= ~CWHeight;
    931 
    932 	if (tmask) {
    933 		/* require no more than these 4 flags */
    934 		return;
    935 	}
    936 if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
    937 
    938 	f_x = req->mask & CWX;
    939 	f_y = req->mask & CWY;
    940 	f_w = req->mask & CWWidth;
    941 	f_h = req->mask & CWHeight;
    942 
    943 	if (! f_x || ! f_y)  {
    944 		if (f_w && f_h) {
    945 			;	/* netscape 4.x style */
    946 		} else {
    947 			return;
    948 		}
    949 	}
    950 if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
    951 
    952 	if ( (f_w && !f_h) || (!f_w && f_h) ) {
    953 		return;
    954 	}
    955 if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
    956 
    957 	for (i=0; i<4; i++) {
    958 		vals[i] = 0;
    959 	}
    960 
    961 	data = (char *)req;
    962 	data += sz_xConfigureWindowReq;
    963 
    964 	for (i=0; i<req->length; i++) {
    965 		unsigned int v;
    966 		/*
    967 		 * We use unsigned int for the values.  There were
    968 		 * some crashes on 64bit machines with unsigned longs.
    969 		 * Need to check that X protocol sends 32bit values.
    970 		 */
    971 		v = *( (unsigned int *) data);
    972 if (db > 1) fprintf(stderr, "  vals[%d]  0x%x/%d\n", i, v, v);
    973 		vals[i] = v;
    974 		data += sizeof(unsigned int);
    975 	}
    976 
    977 	if (index >= MAX_CW) {
    978 		int i, j;
    979 
    980 		/* FIXME, circular, etc. */
    981 		for (i=0; i<2; i++) {
    982 			j = MAX_CW - 2 + i;
    983 			cw_events[i].win = cw_events[j].win;
    984 			cw_events[i].x = cw_events[j].x;
    985 			cw_events[i].y = cw_events[j].y;
    986 			cw_events[i].w = cw_events[j].w;
    987 			cw_events[i].h = cw_events[j].h;
    988 		}
    989 		index = 2;
    990 	}
    991 
    992 	if (! f_x && ! f_y) {
    993 		/* netscape 4.x style  CWWidth,CWHeight */
    994 		vals[2] = vals[0];
    995 		vals[3] = vals[1];
    996 		vals[0] = 0;
    997 		vals[1] = 0;
    998 	}
    999 
   1000 	cw_events[index].win = req->window;
   1001 
   1002 	if (! f_x) {
   1003 		cw_events[index].x = absent;
   1004 	} else {
   1005 		cw_events[index].x = (int) vals[0];
   1006 	}
   1007 	if (! f_y) {
   1008 		cw_events[index].y = absent;
   1009 	} else {
   1010 		cw_events[index].y = (int) vals[1];
   1011 	}
   1012 
   1013 	if (! f_w) {
   1014 		cw_events[index].w = absent;
   1015 	} else {
   1016 		cw_events[index].w = (int) vals[2];
   1017 	}
   1018 	if (! f_h) {
   1019 		cw_events[index].h = absent;
   1020 	} else {
   1021 		cw_events[index].h = (int) vals[3];
   1022 	}
   1023 
   1024 	x = cw_events[index].x;
   1025 	y = cw_events[index].y;
   1026 	w = cw_events[index].w;
   1027 	h = cw_events[index].h;
   1028 	win = cw_events[index].win;
   1029 
   1030 if (dba || db) fprintf(stderr, "  record_CW ind: %d win: 0x%lx x: %d y: %d w: %d h: %d\n",
   1031 	index, win, x, y, w, h);
   1032 
   1033 	index++;
   1034 
   1035 	if (index < 3) {
   1036 		good = 0;
   1037 	} else if (w != absent && h != absent &&
   1038 	    w*h < scrollcopyrect_min_area) {
   1039 		good = 0;
   1040 	}
   1041 
   1042 	if (! good) {
   1043 		return;
   1044 	}
   1045 if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
   1046 
   1047 	match = 0;
   1048 	for (j=index - 1; j >= 0; j--) {
   1049 		if (cw_events[j].win == win) {
   1050 			list[match++] = j;
   1051 		}
   1052 		if (match >= 3) {
   1053 			break;
   1054 		}
   1055 	}
   1056 
   1057 	if (match != 3) {
   1058 		return;
   1059 	}
   1060 if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
   1061 
   1062 /*
   1063 
   1064 Mozilla:
   1065 
   1066 Up arrow: window moves down a bit (dy > 0):
   1067 
   1068  X_ConfigureWindow
   1069     length: 7, window: 0x2e000cd, mask: 0xf, v0 0,  v1 -18,  v2 760,  v3 906,  v4 327692,  v5 48234701,  v6 3,
   1070         CW-mask: CWX,CWY,CWWidth,CWHeight,
   1071  X_ConfigureWindow
   1072     length: 5, window: 0x2e000cd, mask: 0x3, v0 0,  v1 0,  v2 506636,  v3 48234701,  v4 48234511,
   1073         CW-mask: CWX,CWY,
   1074  X_ConfigureWindow
   1075     length: 7, window: 0x2e000cd, mask: 0xf, v0 0,  v1 0,  v2 760,  v3 888,  v4 65579,  v5 0,  v6 108009,
   1076         CW-mask: CWX,CWY,CWWidth,CWHeight,
   1077 
   1078 Down arrow: window moves up a bit (dy < 0):
   1079 
   1080  X_ConfigureWindow
   1081     length: 7, window: 0x2e000cd, mask: 0xf, v0 0,  v1 0,  v2 760,  v3 906,  v4 327692,  v5 48234701,  v6 262147,
   1082         CW-mask: CWX,CWY,CWWidth,CWHeight,
   1083  X_ConfigureWindow
   1084     length: 5, window: 0x2e000cd, mask: 0x3, v0 0,  v1 -18,  v2 506636,  v3 48234701,  v4 48234511,
   1085         CW-mask: CWX,CWY,
   1086  X_ConfigureWindow
   1087     length: 7, window: 0x2e000cd, mask: 0xf, v0 0,  v1 0,  v2 760,  v3 888,  v4 96555,  v5 48265642,  v6 48265262,
   1088         CW-mask: CWX,CWY,CWWidth,CWHeight,
   1089 
   1090 
   1091 Netscape 4.x
   1092 
   1093 Up arrow:
   1094 71.76142   0.01984 X_ConfigureWindow
   1095     length: 7, window: 0x9800488, mask: 0xf, v0 0,  v1 -15,  v2 785,  v3 882,  v4 327692,  v5 159384712,  v6 1769484,
   1096         CW-mask: CWX,CWY,CWWidth,CWHeight,
   1097 71.76153   0.00011 X_ConfigureWindow
   1098     length: 5, window: 0x9800488, mask: 0xc, v0 785,  v1 867,  v2 329228,  v3 159384712,  v4 159383555,
   1099         CW-mask:       CWWidth,CWHeight,
   1100                 XXX,XXX
   1101 71.76157   0.00003 X_ConfigureWindow
   1102     length: 5, window: 0x9800488, mask: 0x3, v0 0,  v1 0,  v2 131132,  v3 159385313,  v4 328759,
   1103         CW-mask: CWX,CWY,
   1104                          XXX,XXX
   1105 
   1106 Down arrow:
   1107 72.93147   0.01990 X_ConfigureWindow
   1108     length: 5, window: 0x9800488, mask: 0xc, v0 785,  v1 882,  v2 328972,  v3 159384712,  v4 159383555,
   1109         CW-mask:       CWWidth,CWHeight,
   1110                 XXX,XXX
   1111 72.93156   0.00009 X_ConfigureWindow
   1112     length: 5, window: 0x9800488, mask: 0x3, v0 0,  v1 -15,  v2 458764,  v3 159384712,  v4 159383567,
   1113         CW-mask: CWX,CWY,
   1114 72.93160   0.00004 X_ConfigureWindow
   1115     length: 7, window: 0x9800488, mask: 0xf, v0 0,  v1 0,  v2 785,  v3 867,  v4 131132,  v5 159385335,  v6 328759,
   1116         CW-mask: CWX,CWY,CWWidth,CWHeight,
   1117 
   1118 
   1119 sadly, probably need to handle some more...
   1120 
   1121  */
   1122 	x0 = cw_events[list[2]].x;
   1123 	y0 = cw_events[list[2]].y;
   1124 	w0 = cw_events[list[2]].w;
   1125 	h0 = cw_events[list[2]].h;
   1126 
   1127 	x1 = cw_events[list[1]].x;
   1128 	y1 = cw_events[list[1]].y;
   1129 	w1 = cw_events[list[1]].w;
   1130 	h1 = cw_events[list[1]].h;
   1131 
   1132 	x2 = cw_events[list[0]].x;
   1133 	y2 = cw_events[list[0]].y;
   1134 	w2 = cw_events[list[0]].w;
   1135 	h2 = cw_events[list[0]].h;
   1136 
   1137 	/* see NS4 XXX's above: */
   1138 	if (w2 == absent || h2 == absent) {
   1139 		/* up arrow */
   1140 		if (w2 == absent) {
   1141 			w2 = w1;
   1142 		}
   1143 		if (h2 == absent) {
   1144 			h2 = h1;
   1145 		}
   1146 	}
   1147 	if (x1 == absent || y1 == absent) {
   1148 		/* up arrow */
   1149 		if (x1 == absent) {
   1150 			x1 = x2;
   1151 		}
   1152 		if (y1 == absent) {
   1153 			y1 = y2;
   1154 		}
   1155 	}
   1156 	if (x0 == absent || y0 == absent) {
   1157 		/* down arrow */
   1158 		if (x0 == absent) {
   1159 			/* hmmm... what to do */
   1160 			x0 = x2;
   1161 		}
   1162 		if (y0 == absent) {
   1163 			y0 = y2;
   1164 		}
   1165 	}
   1166 
   1167 if (dba) fprintf(stderr, "%d/%d/%d/%d  %d/%d/%d/%d  %d/%d/%d/%d\n", x0, y0, w0, h0, x1, y1, w1, h1, x2, y2, w2, h2);
   1168 
   1169 	dy = y1 - y0;
   1170 	dx = x1 - x0;
   1171 
   1172 	src_x = x2;
   1173 	src_y = y2;
   1174 	w = w2;
   1175 	h = h2;
   1176 
   1177 	/* check w and h before we modify them */
   1178 	if (w <= 0 || h <= 0) {
   1179 		good = 0;
   1180 	} else if (w == absent || h == absent) {
   1181 		good = 0;
   1182 	}
   1183 	if (! good) {
   1184 		return;
   1185 	}
   1186 if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
   1187 
   1188 	if (dy > 0) {
   1189 		h -= dy;
   1190 	} else {
   1191 		h += dy;
   1192 		src_y -= dy;
   1193 	}
   1194 	if (dx > 0) {
   1195 		w -= dx;
   1196 	} else {
   1197 		w += dx;
   1198 		src_x -= dx;
   1199 	}
   1200 
   1201 	dst_x = src_x + dx;
   1202 	dst_y = src_y + dy;
   1203 
   1204 	if (x0 == absent || x1 == absent || x2 == absent) {
   1205 		good = 0;
   1206 	} else if (y0 == absent || y1 == absent || y2 == absent) {
   1207 		good = 0;
   1208 	} else if (dx != 0 && dy != 0) {
   1209 		good = 0;
   1210 	} else if (w0 - w2 != nabs(dx)) {
   1211 		good = 0;
   1212 	} else if (h0 - h2 != nabs(dy)) {
   1213 		good = 0;
   1214 	} else if (scr_ev_cnt >= SCR_EV_MAX) {
   1215 		good = 0;
   1216 	}
   1217 
   1218 	if (! good) {
   1219 		return;
   1220 	}
   1221 if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
   1222 
   1223 	/*
   1224 	 * geometry OK.
   1225 	 * after all of the above succeeds, now contact X server.
   1226 	 */
   1227 	if (lookup_attr_cache(win, &cache_index, &next_index)) {
   1228 		i = cache_index;
   1229 		attr.x = scr_attr_cache[i].x;
   1230 		attr.y = scr_attr_cache[i].y;
   1231 		attr.width = scr_attr_cache[i].width;
   1232 		attr.height = scr_attr_cache[i].height;
   1233 		attr.map_state = scr_attr_cache[i].map_state;
   1234 		rx = scr_attr_cache[i].rx;
   1235 		ry = scr_attr_cache[i].ry;
   1236 		valid = scr_attr_cache[i].valid;
   1237 
   1238 if (0) fprintf(stderr, "lookup_attr_cache hit:  %2d %2d 0x%lx %d\n",
   1239     cache_index, next_index, win, valid);
   1240 
   1241 	} else {
   1242 		valid = valid_window(win, &attr, 1);
   1243 
   1244 if (0) fprintf(stderr, "lookup_attr_cache MISS: %2d %2d 0x%lx %d\n",
   1245     cache_index, next_index, win, valid);
   1246 
   1247 		if (valid) {
   1248 			if (!xtranslate(win, rootwin, 0, 0, &rx, &ry, &c, 1)) {
   1249 				valid = 0;
   1250 			}
   1251 		}
   1252 		if (next_index >= 0) {
   1253 			i = next_index;
   1254 			scr_attr_cache[i].win = win;
   1255 			scr_attr_cache[i].fetched = 1;
   1256 			scr_attr_cache[i].valid = valid;
   1257 			scr_attr_cache[i].time = dnow();
   1258 			if (valid) {
   1259 				scr_attr_cache[i].x = attr.x;
   1260 				scr_attr_cache[i].y = attr.y;
   1261 				scr_attr_cache[i].width = attr.width;
   1262 				scr_attr_cache[i].height = attr.height;
   1263 				scr_attr_cache[i].depth = attr.depth;
   1264 				scr_attr_cache[i].class = attr.class;
   1265 				scr_attr_cache[i].backing_store =
   1266 				    attr.backing_store;
   1267 				scr_attr_cache[i].map_state = attr.map_state;
   1268 
   1269 				scr_attr_cache[i].rx = rx;
   1270 				scr_attr_cache[i].ry = ry;
   1271 			}
   1272 		}
   1273 	}
   1274 
   1275 	if (! valid) {
   1276 		return;
   1277 	}
   1278 if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
   1279 
   1280 	if (attr.map_state != IsViewable) {
   1281 		return;
   1282 	}
   1283 if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
   1284 
   1285  if (0 || dba || db) {
   1286 	double st, dt;
   1287 	st = (double) rec_data->server_time/1000.0;
   1288 	dt = (dnow() - servertime_diff) - st;
   1289 	fprintf(stderr, "record_CW-%d *FOUND_SCROLL: win: 0x%lx dx: %d dy: %d "
   1290 	"x: %d y: %d w: %d h: %d  st: %.4f  dt: %.4f  %.4f\n", k++, win,
   1291 	dx, dy, src_x, src_y, w, h, st, dt, dnowx());
   1292  }
   1293 
   1294 	i = scr_ev_cnt;
   1295 
   1296 	scr_ev[i].win = win;
   1297 	scr_ev[i].frame = None;
   1298 	scr_ev[i].dx = dx;
   1299 	scr_ev[i].dy = dy;
   1300 	scr_ev[i].x = rx + dst_x;
   1301 	scr_ev[i].y = ry + dst_y;
   1302 	scr_ev[i].w = w;
   1303 	scr_ev[i].h = h;
   1304 	scr_ev[i].t = ((double) rec_data->server_time)/1000.0;
   1305 	scr_ev[i].win_x = rx;
   1306 	scr_ev[i].win_y = ry;
   1307 	scr_ev[i].win_w = attr.width;
   1308 	scr_ev[i].win_h = attr.height;
   1309 	scr_ev[i].new_x = 0;
   1310 	scr_ev[i].new_y = 0;
   1311 	scr_ev[i].new_w = 0;
   1312 	scr_ev[i].new_h = 0;
   1313 
   1314 	if (dx == 0) {
   1315 		if (dy > 0) {
   1316 			scr_ev[i].new_x = rx + src_x;
   1317 			scr_ev[i].new_y = ry + src_y;
   1318 			scr_ev[i].new_w = w;
   1319 			scr_ev[i].new_h = dy;
   1320 		} else {
   1321 			scr_ev[i].new_x = rx + src_x;
   1322 			scr_ev[i].new_y = ry + dst_y + h;
   1323 			scr_ev[i].new_w = w;
   1324 			scr_ev[i].new_h = -dy;
   1325 		}
   1326 	} else if (dy == 0) {
   1327 		if (dx > 0) {
   1328 			scr_ev[i].new_x = rx + src_x;
   1329 			scr_ev[i].new_y = rx + src_y;
   1330 			scr_ev[i].new_w = dx;
   1331 			scr_ev[i].new_h = h;
   1332 		} else {
   1333 			scr_ev[i].new_x = rx + dst_x + w;
   1334 			scr_ev[i].new_y = ry + src_y;
   1335 			scr_ev[i].new_w = -dx;
   1336 			scr_ev[i].new_h = h;
   1337 		}
   1338 	}
   1339 
   1340 	/* indicate we have a new one */
   1341 	scr_ev_cnt++;
   1342 
   1343 	index = 0;
   1344 }
   1345 
   1346 static void record_switch(XPointer ptr, XRecordInterceptData *rec_data) {
   1347 	static int first = 1;
   1348 	xReq *req;
   1349 
   1350 	if (first) {
   1351 		int i;
   1352 		for (i=0; i<SCR_ATTR_CACHE; i++) {
   1353 			scr_attr_cache[i].win = None;
   1354 			scr_attr_cache[i].fetched = 0;
   1355 			scr_attr_cache[i].valid = 0;
   1356 			scr_attr_cache[i].time = 0.0;
   1357 		}
   1358 		first = 0;
   1359 	}
   1360 
   1361 	/* should handle control msgs, start/stop/etc */
   1362 	if (rec_data->category == XRecordStartOfData) {
   1363 		record_CW(ptr, rec_data);
   1364 	} else if (rec_data->category == XRecordEndOfData) {
   1365 		;
   1366 	} else if (rec_data->category == XRecordClientStarted) {
   1367 		;
   1368 	} else if (rec_data->category == XRecordClientDied) {
   1369 		;
   1370 	} else if (rec_data->category == XRecordFromServer) {
   1371 		;
   1372 	}
   1373 
   1374 	if (rec_data->category != XRecordFromClient) {
   1375 		XRecordFreeData(rec_data);
   1376 		return;
   1377 	}
   1378 
   1379 	req = (xReq *) rec_data->data;
   1380 
   1381 	if (req->reqType == X_CopyArea) {
   1382 		record_CA(ptr, rec_data);
   1383 	} else if (req->reqType == X_ConfigureWindow) {
   1384 		record_CW(ptr, rec_data);
   1385 	} else {
   1386 		;
   1387 	}
   1388 	XRecordFreeData(rec_data);
   1389 }
   1390 
   1391 static void record_grab(XPointer ptr, XRecordInterceptData *rec_data) {
   1392 	xReq *req;
   1393 	int db = 0;
   1394 
   1395 	if (debug_grabs) db = 1;
   1396 
   1397 	/* should handle control msgs, start/stop/etc */
   1398 	if (rec_data->category == XRecordStartOfData) {
   1399 		;
   1400 	} else if (rec_data->category == XRecordEndOfData) {
   1401 		;
   1402 	} else if (rec_data->category == XRecordClientStarted) {
   1403 		;
   1404 	} else if (rec_data->category == XRecordClientDied) {
   1405 		;
   1406 	} else if (rec_data->category == XRecordFromServer) {
   1407 		;
   1408 	}
   1409 
   1410 	if (rec_data->category != XRecordFromClient) {
   1411 		XRecordFreeData(rec_data);
   1412 		return;
   1413 	}
   1414 
   1415 	req = (xReq *) rec_data->data;
   1416 
   1417 	if (req->reqType == X_GrabServer) {
   1418 		double now = dnowx();
   1419 		xserver_grabbed++;
   1420 		if (db) rfbLog("X server Grabbed:    %d %.5f\n", xserver_grabbed, now);
   1421 		if (xserver_grabbed > 1) {
   1422 			/*
   1423 			 * some apps do multiple grabs... very unlikely
   1424 			 * two apps will be doing it at same time.
   1425 			 */
   1426 			xserver_grabbed = 1;
   1427 		}
   1428 	} else if (req->reqType == X_UngrabServer) {
   1429 		double now = dnowx();
   1430 		xserver_grabbed--;
   1431 		if (xserver_grabbed < 0) {
   1432 			xserver_grabbed = 0;
   1433 		}
   1434 		if (db) rfbLog("X server Un-Grabbed: %d %.5f\n", xserver_grabbed, now);
   1435 	} else {
   1436 		;
   1437 	}
   1438 	XRecordFreeData(rec_data);
   1439 
   1440 	/* unused vars warning: */
   1441 	if (ptr) {}
   1442 }
   1443 #endif
   1444 
   1445 static void check_xrecord_grabserver(void) {
   1446 #if LIBVNCSERVER_HAVE_RECORD
   1447 	int last_val, cnt = 0, i, max = 10;
   1448 	double d;
   1449 	if (!gdpy_ctrl || !gdpy_data) {
   1450 		return;
   1451 	}
   1452 	if (unixpw_in_progress) return;
   1453 
   1454 	dtime0(&d);
   1455 	XFlush_wr(gdpy_ctrl);
   1456 	for (i=0; i<max; i++) {
   1457 		last_val = xserver_grabbed;
   1458 		XRecordProcessReplies(gdpy_data);
   1459 		if (xserver_grabbed != last_val) {
   1460 			cnt++;
   1461 		} else if (i > 2) {
   1462 			break;
   1463 		}
   1464 	}
   1465 	if (cnt) {
   1466 		XFlush_wr(gdpy_ctrl);
   1467 	}
   1468  if (debug_grabs && cnt > 0) {
   1469 	d = dtime(&d);
   1470 fprintf(stderr, "check_xrecord_grabserver: cnt=%d i=%d %.4f\n", cnt, i, d);
   1471  }
   1472 #endif
   1473 }
   1474 
   1475 #if LIBVNCSERVER_HAVE_RECORD
   1476 static void shutdown_record_context(XRecordContext rc, int bequiet, int reopen) {
   1477 	int ret1, ret2;
   1478 	int verb = (!bequiet && !quiet);
   1479 
   1480 	RAWFB_RET_VOID
   1481 	if (0 || debug_scroll) {
   1482 		rfbLog("shutdown_record_context(0x%lx, %d, %d)\n", rc,
   1483 		    bequiet, reopen);
   1484 		verb = 1;
   1485 	}
   1486 
   1487 	ret1 = XRecordDisableContext(rdpy_ctrl, rc);
   1488 	if (!ret1 && verb) {
   1489 		rfbLog("XRecordDisableContext(0x%lx) failed.\n", rc);
   1490 	}
   1491 	ret2 = XRecordFreeContext(rdpy_ctrl, rc);
   1492 	if (!ret2 && verb) {
   1493 		rfbLog("XRecordFreeContext(0x%lx) failed.\n", rc);
   1494 	}
   1495 	XFlush_wr(rdpy_ctrl);
   1496 
   1497 	if (reopen == 2 && ret1 && ret2) {
   1498 		reopen = 0;	/* 2 means reopen only on failure  */
   1499 	}
   1500 	if (reopen && gdpy_ctrl) {
   1501 		check_xrecord_grabserver();
   1502 		if (xserver_grabbed) {
   1503 			rfbLog("shutdown_record_context: skip reopen,"
   1504 			    " server grabbed\n");
   1505 			reopen = 0;
   1506 		}
   1507 	}
   1508 	if (reopen) {
   1509 		char *dpystr = DisplayString(dpy);
   1510 
   1511 		if (debug_scroll) {
   1512 			rfbLog("closing RECORD data connection.\n");
   1513 		}
   1514 		XCloseDisplay_wr(rdpy_data);
   1515 		rdpy_data = NULL;
   1516 
   1517 		if (debug_scroll) {
   1518 			rfbLog("closing RECORD control connection.\n");
   1519 		}
   1520 		XCloseDisplay_wr(rdpy_ctrl);
   1521 		rdpy_ctrl = NULL;
   1522 
   1523 		rdpy_ctrl = XOpenDisplay_wr(dpystr);
   1524 
   1525 		if (! rdpy_ctrl) {
   1526 			rfbLog("Failed to reopen RECORD control connection:"
   1527 			    "%s\n", dpystr);
   1528 			rfbLog("  disabling RECORD scroll detection.\n");
   1529 			use_xrecord = 0;
   1530 			return;
   1531 		}
   1532 		XSync(dpy, False);
   1533 
   1534 		disable_grabserver(rdpy_ctrl, 0);
   1535 		XSync(rdpy_ctrl, True);
   1536 
   1537 		rdpy_data = XOpenDisplay_wr(dpystr);
   1538 
   1539 		if (! rdpy_data) {
   1540 			rfbLog("Failed to reopen RECORD data connection:"
   1541 			    "%s\n", dpystr);
   1542 			rfbLog("  disabling RECORD scroll detection.\n");
   1543 			XCloseDisplay_wr(rdpy_ctrl);
   1544 			rdpy_ctrl = NULL;
   1545 			use_xrecord = 0;
   1546 			return;
   1547 		}
   1548 		disable_grabserver(rdpy_data, 0);
   1549 
   1550 		if (debug_scroll || (! bequiet && reopen == 2)) {
   1551 			rfbLog("reopened RECORD data and control display"
   1552 			    " connections: %s\n", dpystr);
   1553 		}
   1554 	}
   1555 }
   1556 #endif
   1557 
   1558 void check_xrecord_reset(int force) {
   1559 	static double last_reset = 0.0;
   1560 	int reset_time  = 60, require_idle  = 10;
   1561 	int reset_time2 = 600, require_idle2 = 40;
   1562 	double now = 0.0;
   1563 	XErrorHandler old_handler = NULL;
   1564 
   1565 	if (gdpy_ctrl) {
   1566 		X_LOCK;
   1567 		check_xrecord_grabserver();
   1568 		X_UNLOCK;
   1569 	} else {
   1570 		/* more dicey if not watching grabserver */
   1571 		reset_time = reset_time2;
   1572 		require_idle = require_idle2;
   1573 	}
   1574 
   1575 	if (!use_xrecord) {
   1576 		return;
   1577 	}
   1578 	if (xrecording) {
   1579 		return;
   1580 	}
   1581 	if (button_mask) {
   1582 		return;
   1583 	}
   1584 	if (xserver_grabbed) {
   1585 		return;
   1586 	}
   1587 
   1588 	if (unixpw_in_progress) return;
   1589 
   1590 #if LIBVNCSERVER_HAVE_RECORD
   1591 	if (! rc_scroll) {
   1592 		return;
   1593 	}
   1594 	now = dnow();
   1595 	if (last_reset == 0.0) {
   1596 		last_reset = now;
   1597 		return;
   1598 	}
   1599 	/*
   1600 	 * try to wait for a break in input to reopen the displays
   1601 	 * this is only to avoid XGrabServer deadlock on the repopens.
   1602 	 */
   1603 	if (force) {
   1604 		;
   1605 	} else if (now < last_reset + reset_time) {
   1606 		return;
   1607 	} else if (now < last_pointer_click_time + require_idle)  {
   1608 		return;
   1609 	} else if (now < last_keyboard_time + require_idle)  {
   1610 		return;
   1611 	}
   1612 	X_LOCK;
   1613 	trapped_record_xerror = 0;
   1614 	old_handler = XSetErrorHandler(trap_record_xerror);
   1615 
   1616 	/* unlikely, but check again since we will definitely be doing it. */
   1617 	if (gdpy_ctrl) {
   1618 		check_xrecord_grabserver();
   1619 		if (xserver_grabbed) {
   1620 			XSetErrorHandler(old_handler);
   1621 			X_UNLOCK;
   1622 			return;
   1623 		}
   1624 	}
   1625 
   1626 	shutdown_record_context(rc_scroll, 0, 1);
   1627 	rc_scroll = 0;
   1628 
   1629 	XSetErrorHandler(old_handler);
   1630 	X_UNLOCK;
   1631 
   1632 	last_reset = now;
   1633 #else
   1634 	if (!old_handler || now == 0.0 || !last_reset || !force) {}
   1635 #endif
   1636 }
   1637 
   1638 #define RECORD_ERROR_MSG(tag) \
   1639 	if (! quiet) { \
   1640 		static int cnt = 0; \
   1641 		static time_t last = 0; \
   1642 		int show = 0; \
   1643 		cnt++; \
   1644 		if (debug_scroll || cnt < 20) { \
   1645 			show = 1; \
   1646 		} else if (cnt == 20) { \
   1647 			last = time(NULL); \
   1648 			rfbLog("disabling RECORD XError messages for 600s\n"); \
   1649 			show = 1; \
   1650 		} else if (time(NULL) > last + 600) { \
   1651 			cnt = 0; \
   1652 			show = 1; \
   1653 		} \
   1654 		if (show) { \
   1655 			rfbLog("trapped RECORD XError: %s %s %d/%d/%d (0x%lx)\n", \
   1656 			    tag, xerror_string(trapped_record_xerror_event), \
   1657 			    (int) trapped_record_xerror_event->error_code, \
   1658 			    (int) trapped_record_xerror_event->request_code, \
   1659 			    (int) trapped_record_xerror_event->minor_code, \
   1660 			    (int) trapped_record_xerror_event->resourceid); \
   1661 		} \
   1662 	}
   1663 
   1664 void xrecord_watch(int start, int setby) {
   1665 #if LIBVNCSERVER_HAVE_RECORD
   1666 	Window focus, wm, c, clast;
   1667 	static double create_time = 0.0;
   1668 	int rc;
   1669 	int do_shutdown = 0;
   1670 	int reopen_dpys = 1;
   1671 	XErrorHandler old_handler = NULL;
   1672 	static Window last_win = None, last_result = None;
   1673 #endif
   1674 	int db = debug_scroll;
   1675 	double now;
   1676 	static double last_error = 0.0;
   1677 
   1678 if (0) db = 1;
   1679 
   1680 	if (nofb) {
   1681 		xrecording = 0;
   1682 		return;
   1683 	}
   1684 	if (use_threads) {
   1685 		/* XXX not working.  Still?  Painting errors. */
   1686 		static int first = 1;
   1687 		if (first) {
   1688 			if (use_xrecord && !getenv("XRECORD_THREADS")) {
   1689 				rfbLog("xrecord_watch: disabling scroll detection in -threads mode.\n");
   1690 				rfbLog("xrecord_watch: Set -env XRECORD_THREADS=1 to enable it.\n");
   1691 				use_xrecord = 0;
   1692 				xrecording = 0;
   1693 			}
   1694 			first = 0;
   1695 		}
   1696 		if (!use_xrecord && !xrecording) {
   1697 			return;
   1698 		}
   1699 	}
   1700 
   1701 	dtime0(&now);
   1702 	if (now < last_error + 0.5) {
   1703 		return;
   1704 	}
   1705 
   1706 	if (gdpy_ctrl) {
   1707 		X_LOCK;
   1708 		check_xrecord_grabserver();
   1709 		X_UNLOCK;
   1710 		if (xserver_grabbed) {
   1711 if (db || debug_grabs) fprintf(stderr, "xrecord_watch: %d/%d  out xserver_grabbed\n", start, setby);
   1712 			return;
   1713 		}
   1714 	}
   1715 
   1716 #if LIBVNCSERVER_HAVE_RECORD
   1717 	if (! start) {
   1718 		int shut_reopen = 2, shut_time = 25;
   1719 if (db || debug_grabs) fprintf(stderr, "XRECORD OFF: %d/%d  %.4f\n", xrecording, setby, now - x11vnc_start);
   1720 		xrecording = 0;
   1721 		if (! rc_scroll) {
   1722 			xrecord_focus_window = None;
   1723 			xrecord_wm_window = None;
   1724 			xrecord_ptr_window = None;
   1725 			xrecord_keysym = NoSymbol;
   1726 			rcs_scroll = 0;
   1727 			return;
   1728 		}
   1729 
   1730 		if (! do_shutdown && now > create_time + shut_time) {
   1731 			/* XXX unstable if we keep a RECORD going forever */
   1732 			do_shutdown = 1;
   1733 		}
   1734 
   1735 		SCR_LOCK;
   1736 
   1737 		if (do_shutdown) {
   1738 if (db > 1) fprintf(stderr, "=== shutdown-scroll 0x%lx\n", rc_scroll);
   1739 			X_LOCK;
   1740 			trapped_record_xerror = 0;
   1741 			old_handler = XSetErrorHandler(trap_record_xerror);
   1742 
   1743 			shutdown_record_context(rc_scroll, 0, shut_reopen);
   1744 			rc_scroll = 0;
   1745 
   1746 			/*
   1747 			 * n.b. there is a grabserver issue wrt
   1748 			 * XRecordCreateContext() even though rdpy_ctrl
   1749 			 * is set imprevious to grabs.  Perhaps a bug
   1750 			 * in the X server or library...
   1751 			 *
   1752 			 * If there are further problems, a thought
   1753 			 * to recreate rc_scroll right after the
   1754 			 * reopen.
   1755 			 */
   1756 
   1757 			if (! use_xrecord) {
   1758 				XSetErrorHandler(old_handler);
   1759 				X_UNLOCK;
   1760 				SCR_UNLOCK;
   1761 				return;
   1762 			}
   1763 
   1764 			XRecordProcessReplies(rdpy_data);
   1765 
   1766 			if (trapped_record_xerror) {
   1767 				RECORD_ERROR_MSG("shutdown");
   1768 				last_error = now;
   1769 			}
   1770 
   1771 			XSetErrorHandler(old_handler);
   1772 			X_UNLOCK;
   1773 			SCR_UNLOCK;
   1774 
   1775 		} else {
   1776 			if (rcs_scroll) {
   1777 if (db > 1) fprintf(stderr, "=== disab-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll);
   1778 				X_LOCK;
   1779 				trapped_record_xerror = 0;
   1780 				old_handler =
   1781 				    XSetErrorHandler(trap_record_xerror);
   1782 
   1783 				rcs_scroll = XRecordCurrentClients;
   1784 				XRecordUnregisterClients(rdpy_ctrl, rc_scroll,
   1785 				    &rcs_scroll, 1);
   1786 				XRecordDisableContext(rdpy_ctrl, rc_scroll);
   1787 				XFlush_wr(rdpy_ctrl);
   1788 				XRecordProcessReplies(rdpy_data);
   1789 
   1790 				if (trapped_record_xerror) {
   1791 					RECORD_ERROR_MSG("disable");
   1792 
   1793 					shutdown_record_context(rc_scroll,
   1794 					    0, reopen_dpys);
   1795 					rc_scroll = 0;
   1796 
   1797 					last_error = now;
   1798 
   1799 					if (! use_xrecord) {
   1800 						XSetErrorHandler(old_handler);
   1801 						X_UNLOCK;
   1802 						SCR_UNLOCK;
   1803 						return;
   1804 					}
   1805 				}
   1806 				XSetErrorHandler(old_handler);
   1807 				X_UNLOCK;
   1808 			}
   1809 		}
   1810 
   1811 		SCR_UNLOCK;
   1812 		/*
   1813 		 * XXX if we do a XFlush_wr(rdpy_ctrl) here we get:
   1814 		 *
   1815 
   1816 		X Error of failed request:  XRecordBadContext
   1817 		  Major opcode of failed request:  145 (RECORD)
   1818 		  Minor opcode of failed request:  5 (XRecordEnableContext)
   1819 		  Context in failed request:  0x2200013
   1820 		  Serial number of failed request:  29
   1821 		  Current serial number in output stream:  29
   1822 
   1823 		 *
   1824 		 * need to figure out what is going on... since it may lead
   1825 		 * infrequent failures.
   1826 		 */
   1827 		xrecord_focus_window = None;
   1828 		xrecord_wm_window = None;
   1829 		xrecord_ptr_window = None;
   1830 		xrecord_keysym = NoSymbol;
   1831 		rcs_scroll = 0;
   1832 		return;
   1833 	}
   1834 if (db || debug_grabs) fprintf(stderr, "XRECORD ON:  %d/%d  %.4f\n", xrecording, setby, now - x11vnc_start);
   1835 
   1836 	if (xrecording) {
   1837 		return;
   1838 	}
   1839 
   1840 	if (do_shutdown && rc_scroll) {
   1841 		static int didmsg = 0;
   1842 		/* should not happen... */
   1843 		if (0 || !didmsg) {
   1844 			rfbLog("warning: do_shutdown && rc_scroll\n");
   1845 			didmsg = 1;
   1846 		}
   1847 		xrecord_watch(0, SCR_NONE);
   1848 	}
   1849 
   1850 	xrecording = 0;
   1851 	xrecord_focus_window = None;
   1852 	xrecord_wm_window = None;
   1853 	xrecord_ptr_window = None;
   1854 	xrecord_keysym = NoSymbol;
   1855 	xrecord_set_by_keys  = 0;
   1856 	xrecord_set_by_mouse = 0;
   1857 
   1858 	/* get the window with focus and mouse pointer: */
   1859 	clast = None;
   1860 	focus = None;
   1861 	wm = None;
   1862 
   1863 	X_LOCK;
   1864 	SCR_LOCK;
   1865 #if 0
   1866 	/*
   1867 	 * xrecord_focus_window / focus not currently used... save a
   1868 	 * round trip to the X server for now.
   1869 	 * N.B. our heuristic is inaccurate: if he is scrolling and
   1870 	 * drifts off of the scrollbar onto another application we
   1871 	 * will catch that application, not the starting ones.
   1872 	 * check_xrecord_{keys,mouse} mitigates this somewhat by
   1873 	 * delaying calls to xrecord_watch as much as possible.
   1874 	 */
   1875 	XGetInputFocus(dpy, &focus, &i);
   1876 #endif
   1877 
   1878 	wm = query_pointer(rootwin);
   1879 	if (wm) {
   1880 		c = wm;
   1881 	} else {
   1882 		c = rootwin;
   1883 	}
   1884 
   1885 	/* descend a bit to avoid wm frames: */
   1886 	if (c != rootwin && c == last_win) {
   1887 		/* use cached results to avoid roundtrips: */
   1888 		clast = last_result;
   1889 	} else if (scroll_good_all == NULL && scroll_skip_all == NULL) {
   1890 		/* more efficient if name info not needed. */
   1891 		xrecord_name_info[0] = '\0';
   1892 		clast = descend_pointer(6, c, NULL, 0);
   1893 	} else {
   1894 		char *nm;
   1895 		int matched_good = 0, matched_skip = 0;
   1896 
   1897 		clast = descend_pointer(6, c, xrecord_name_info, NAMEINFO);
   1898 if (db) fprintf(stderr, "name_info: %s\n", xrecord_name_info);
   1899 
   1900 		nm = xrecord_name_info;
   1901 
   1902 		if (scroll_good_all) {
   1903 			matched_good += match_str_list(nm, scroll_good_all);
   1904 		}
   1905 		if (setby == SCR_KEY && scroll_good_key) {
   1906 			matched_good += match_str_list(nm, scroll_good_key);
   1907 		}
   1908 		if (setby == SCR_MOUSE && scroll_good_mouse) {
   1909 			matched_good += match_str_list(nm, scroll_good_mouse);
   1910 		}
   1911 		if (scroll_skip_all) {
   1912 			matched_skip += match_str_list(nm, scroll_skip_all);
   1913 		}
   1914 		if (setby == SCR_KEY && scroll_skip_key) {
   1915 			matched_skip += match_str_list(nm, scroll_skip_key);
   1916 		}
   1917 		if (setby == SCR_MOUSE && scroll_skip_mouse) {
   1918 			matched_skip += match_str_list(nm, scroll_skip_mouse);
   1919 		}
   1920 
   1921 		if (!matched_good && matched_skip) {
   1922 			clast = None;
   1923 		}
   1924 	}
   1925 	if (c != rootwin) {
   1926 		/* cache results for possible use next call */
   1927 		last_win = c;
   1928 		last_result = clast;
   1929 	}
   1930 
   1931 	if (!clast || clast == rootwin) {
   1932 if (db) fprintf(stderr, "--- xrecord_watch: SKIP.\n");
   1933 		X_UNLOCK;
   1934 		SCR_UNLOCK;
   1935 		return;
   1936 	}
   1937 
   1938 	/* set protocol request ranges: */
   1939 	rr_scroll[0] = rr_CA;
   1940 	rr_scroll[1] = rr_CW;
   1941 
   1942 	/*
   1943 	 * start trapping... there still are some occasional failures
   1944 	 * not yet understood, likely some race condition WRT the
   1945 	 * context being setup.
   1946 	 */
   1947 	trapped_record_xerror = 0;
   1948 	old_handler = XSetErrorHandler(trap_record_xerror);
   1949 
   1950 	if (! rc_scroll) {
   1951 		/* do_shutdown case or first time in */
   1952 
   1953 		if (gdpy_ctrl) {
   1954 			/*
   1955 			 * Even though rdpy_ctrl is impervious to grabs
   1956 			 * at this point, we still get deadlock, why?
   1957 			 * It blocks in the library find_display() call.
   1958 			 */
   1959 			check_xrecord_grabserver();
   1960 			if (xserver_grabbed) {
   1961 				XSetErrorHandler(old_handler);
   1962 				X_UNLOCK;
   1963 				SCR_UNLOCK;
   1964 				return;
   1965 			}
   1966 		}
   1967 		rcs_scroll = (XRecordClientSpec) clast;
   1968 		rc_scroll = XRecordCreateContext(rdpy_ctrl, 0, &rcs_scroll, 1,
   1969 		    rr_scroll, 2);
   1970 
   1971 		if (! do_shutdown) {
   1972 			XSync(rdpy_ctrl, False);
   1973 		}
   1974 if (db) fprintf(stderr, "NEW rc:    0x%lx\n", rc_scroll);
   1975 		if (rc_scroll) {
   1976 			dtime0(&create_time);
   1977 		} else {
   1978 			rcs_scroll = 0;
   1979 		}
   1980 
   1981 	} else if (! do_shutdown) {
   1982 		if (rcs_scroll) {
   1983 			/*
   1984 			 * should have been unregistered in xrecord_watch(0)...
   1985 			 */
   1986 			rcs_scroll = XRecordCurrentClients;
   1987 			XRecordUnregisterClients(rdpy_ctrl, rc_scroll,
   1988 			    &rcs_scroll, 1);
   1989 
   1990 if (db > 1) fprintf(stderr, "=2= unreg-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll);
   1991 
   1992 		}
   1993 
   1994 		rcs_scroll = (XRecordClientSpec) clast;
   1995 
   1996 if (db > 1) fprintf(stderr, "=-=   reg-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll);
   1997 
   1998 		if (!XRecordRegisterClients(rdpy_ctrl, rc_scroll, 0,
   1999 		    &rcs_scroll, 1, rr_scroll, 2)) {
   2000 			if (1 || now > last_error + 60) {
   2001 				rfbLog("failed to register client 0x%lx with"
   2002 				    " X RECORD context rc_scroll.\n", clast);
   2003 			}
   2004 			last_error = now;
   2005 			rcs_scroll = 0;
   2006 			/* continue on for now... */
   2007 		}
   2008 	}
   2009 
   2010 	XFlush_wr(rdpy_ctrl);
   2011 
   2012 if (db) fprintf(stderr, "rc_scroll: 0x%lx\n", rc_scroll);
   2013 	if (trapped_record_xerror) {
   2014 		RECORD_ERROR_MSG("register");
   2015 	}
   2016 
   2017 	if (! rc_scroll) {
   2018 		XSetErrorHandler(old_handler);
   2019 		X_UNLOCK;
   2020 		SCR_UNLOCK;
   2021 		use_xrecord = 0;
   2022 		rfbLog("failed to create X RECORD context rc_scroll.\n");
   2023 		rfbLog("  switching to -noscrollcopyrect mode.\n");
   2024 		return;
   2025 	} else if (! rcs_scroll || trapped_record_xerror) {
   2026 		/* try again later */
   2027 		shutdown_record_context(rc_scroll, 0, reopen_dpys);
   2028 		rc_scroll = 0;
   2029 		last_error = now;
   2030 
   2031 		XSetErrorHandler(old_handler);
   2032 		X_UNLOCK;
   2033 		SCR_UNLOCK;
   2034 		return;
   2035 	}
   2036 
   2037 	xrecord_focus_window = focus;
   2038 #if 0
   2039 	/* xrecord_focus_window currently unused. */
   2040 	if (! xrecord_focus_window) {
   2041 		xrecord_focus_window = clast;
   2042 	}
   2043 #endif
   2044 	xrecord_wm_window = wm;
   2045 	if (! xrecord_wm_window) {
   2046 		xrecord_wm_window = clast;
   2047 	}
   2048 
   2049 	xrecord_ptr_window = clast;
   2050 
   2051 	xrecording = 1;
   2052 	xrecord_seq++;
   2053 	dtime0(&xrecord_start);
   2054 
   2055 	rc = XRecordEnableContextAsync(rdpy_data, rc_scroll, record_switch,
   2056 	    (XPointer) xrecord_seq);
   2057 
   2058 	if (!rc || trapped_record_xerror) {
   2059 		if (1 || now > last_error + 60) {
   2060 			rfbLog("failed to enable RECORD context "
   2061 			    "rc_scroll: 0x%lx rc: %d\n", rc_scroll, rc);
   2062 			if (trapped_record_xerror) {
   2063 				RECORD_ERROR_MSG("enable-failed");
   2064 			}
   2065 		}
   2066 		shutdown_record_context(rc_scroll, 0, reopen_dpys);
   2067 		rc_scroll = 0;
   2068 		last_error = now;
   2069 		xrecording = 0;
   2070 		/* continue on for now... */
   2071 	}
   2072 	XSetErrorHandler(old_handler);
   2073 
   2074 	/* XXX this may cause more problems than it solves... */
   2075 	if (use_xrecord) {
   2076 		XFlush_wr(rdpy_data);
   2077 	}
   2078 
   2079 	X_UNLOCK;
   2080 	SCR_UNLOCK;
   2081 #endif
   2082 }
   2083 
   2084 
   2085