1 /*
2    Copyright (C) 2002-2010 Karl J. Runge <runge@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 
trap_record_xerror(Display * d,XErrorEvent * error)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 
xrecord_grabserver(int start)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 
zerodisp_xrecord(void)180 void zerodisp_xrecord(void) {
181 	rdpy_data = NULL;
182 	rdpy_ctrl = NULL;
183 	gdpy_data = NULL;
184 	gdpy_ctrl = NULL;
185 }
186 
initialize_xrecord(void)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 
shutdown_xrecord(void)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 
xrecord_skip_keysym(rfbKeySym keysym)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 
xrecord_skip_button(int new_button,int old)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 
xrecord_vi_scroll_keysym(rfbKeySym keysym)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 
xrecord_emacs_scroll_keysym(rfbKeySym keysym)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 
xrecord_scroll_keysym(rfbKeySym keysym)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 
lookup_attr_cache(Window win,int * cache_index,int * next_index)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
record_CA(XPointer ptr,XRecordInterceptData * rec_data)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 
record_CW(XPointer ptr,XRecordInterceptData * rec_data)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 
record_switch(XPointer ptr,XRecordInterceptData * rec_data)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 
record_grab(XPointer ptr,XRecordInterceptData * rec_data)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 
check_xrecord_grabserver(void)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
shutdown_record_context(XRecordContext rc,int bequiet,int reopen)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 
check_xrecord_reset(int force)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 
xrecord_watch(int start,int setby)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