blob: 87e15b9091f24241657101c541445617fd44322a [file] [log] [blame]
San Mehata430b2b2014-09-23 08:30:51 -07001/*
2 Copyright (C) 2002-2010 Karl J. Runge <runge@karlrunge.com>
3 All rights reserved.
4
5This file is part of x11vnc.
6
7x11vnc is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2 of the License, or (at
10your option) any later version.
11
12x11vnc is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with x11vnc; if not, write to the Free Software
19Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
20or see <http://www.gnu.org/licenses/>.
21
22In addition, as a special exception, Karl J. Runge
23gives permission to link the code of its release of x11vnc with the
24OpenSSL project's "OpenSSL" library (or with modified versions of it
25that use the same license as the "OpenSSL" library), and distribute
26the linked executables. You must obey the GNU General Public License
27in all respects for all of the code used other than "OpenSSL". If you
28modify this file, you may extend this exception to your version of the
29file, but you are not obligated to do so. If you do not wish to do
30so, delete this exception statement from your version.
31*/
32
33/* -- 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
45scroll_event_t scr_ev[SCR_EV_MAX];
46int scr_ev_cnt;
47
48int xrecording = 0;
49int xrecord_set_by_keys = 0;
50int xrecord_set_by_mouse = 0;
51Window xrecord_focus_window = None;
52Window xrecord_wm_window = None;
53Window xrecord_ptr_window = None;
54KeySym xrecord_keysym = NoSymbol;
55
56#define NAMEINFO 2048
57char xrecord_name_info[NAMEINFO];
58
59#define SCR_ATTR_CACHE 8
60winattr_t scr_attr_cache[SCR_ATTR_CACHE];
61static double attr_cache_max_age = 1.5;
62
63Display *rdpy_data = NULL; /* Data connection for RECORD */
64Display *rdpy_ctrl = NULL; /* Control connection for RECORD */
65
66Display *gdpy_ctrl = NULL;
67Display *gdpy_data = NULL;
68int xserver_grabbed = 0;
69
70int trap_record_xerror(Display *, XErrorEvent *);
71
72void initialize_xrecord(void);
73void zerodisp_xrecord(void);
74void shutdown_xrecord(void);
75int xrecord_skip_keysym(rfbKeySym keysym);
76int xrecord_skip_button(int newb, int old);
77int xrecord_scroll_keysym(rfbKeySym keysym);
78void check_xrecord_reset(int force);
79void xrecord_watch(int start, int setby);
80
81
82#if LIBVNCSERVER_HAVE_RECORD
83static XRecordRange *rr_CA = NULL;
84static XRecordRange *rr_CW = NULL;
85static XRecordRange *rr_GS = NULL;
86static XRecordRange *rr_scroll[10];
87static XRecordContext rc_scroll;
88static XRecordClientSpec rcs_scroll;
89static XRecordRange *rr_grab[10];
90static XRecordContext rc_grab;
91static XRecordClientSpec rcs_grab;
92#endif
93static XErrorEvent *trapped_record_xerror_event;
94
95static void xrecord_grabserver(int start);
96static int xrecord_vi_scroll_keysym(rfbKeySym keysym);
97static int xrecord_emacs_scroll_keysym(rfbKeySym keysym);
98static int lookup_attr_cache(Window win, int *cache_index, int *next_index);
99#if LIBVNCSERVER_HAVE_RECORD
100static void record_CA(XPointer ptr, XRecordInterceptData *rec_data);
101static void record_CW(XPointer ptr, XRecordInterceptData *rec_data);
102static void record_switch(XPointer ptr, XRecordInterceptData *rec_data);
103static void record_grab(XPointer ptr, XRecordInterceptData *rec_data);
104static void shutdown_record_context(XRecordContext rc, int bequiet, int reopen);
105#endif
106static void check_xrecord_grabserver(void);
107
108
109int 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
118static 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
180void zerodisp_xrecord(void) {
181 rdpy_data = NULL;
182 rdpy_ctrl = NULL;
183 gdpy_data = NULL;
184 gdpy_ctrl = NULL;
185}
186
187void 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
285void 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
342int 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
413int xrecord_skip_button(int new_button, int old) {
414 /* unused vars warning: */
415 if (new_button || old) {}
416
417 return 0;
418}
419
420static 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
434static 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
443int 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
466static 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
522if (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
531static XID xrecord_seq = 0;
532static double xrecord_start = 0.0;
533
534#if LIBVNCSERVER_HAVE_RECORD
535static 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
556if (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 }
564if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
565
566 if (rec_data->id_base == 0) {
567 return;
568 }
569if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
570
571 if ((XID) ptr != xrecord_seq) {
572 return;
573 }
574if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
575
576 if (rec_data->category != XRecordFromClient) {
577 return;
578 }
579if (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 }
586if (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
597xterm, gnome-terminal, others.
598
599Note we miss the X_ImageText8 that clears the block cursor. So there is a
600short 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
651if (!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
659if (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 }
712if (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
764if (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
846typedef struct cw_event {
847 Window win;
848 int x, y, w, h;
849} cw_event_t;
850
851#define MAX_CW 128
852static cw_event_t cw_events[MAX_CW];
853
854static 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
881if (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 }
890if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
891
892 if ((XID) ptr != xrecord_seq) {
893 return;
894 }
895if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
896
897 if (rec_data->id_base == 0) {
898 return;
899 }
900if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
901
902 if (rec_data->category == XRecordStartOfData) {
903 index = 0;
904 return;
905 }
906if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
907
908 if (rec_data->category != XRecordFromClient) {
909 return;
910 }
911if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
912
913 if (rec_data->client_swapped) {
914 return;
915 }
916if (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 }
923if (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 }
936if (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 }
950if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
951
952 if ( (f_w && !f_h) || (!f_w && f_h) ) {
953 return;
954 }
955if (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);
972if (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
1030if (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 }
1045if (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 }
1060if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
1061
1062/*
1063
1064Mozilla:
1065
1066Up 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
1078Down 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
1091Netscape 4.x
1092
1093Up arrow:
109471.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,
109771.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
110171.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
1106Down arrow:
110772.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
111172.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,
111472.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
1119sadly, 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
1167if (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 }
1186if (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 }
1221if (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
1238if (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
1244if (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 }
1278if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
1279
1280 if (attr.map_state != IsViewable) {
1281 return;
1282 }
1283if (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
1346static 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
1391static 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
1445static 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);
1470fprintf(stderr, "check_xrecord_grabserver: cnt=%d i=%d %.4f\n", cnt, i, d);
1471 }
1472#endif
1473}
1474
1475#if LIBVNCSERVER_HAVE_RECORD
1476static 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
1558void 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
1664void 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
1678if (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) {
1711if (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;
1719if (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) {
1738if (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) {
1777if (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 }
1834if (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);
1898if (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) {
1932if (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 }
1974if (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
1990if (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
1996if (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
2012if (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