9
#include "scrollevent_t.h"
12
#define SCR_EV_MAX 128
13
scroll_event_t scr_ev[SCR_EV_MAX];
17
int xrecord_set_by_keys = 0;
18
int xrecord_set_by_mouse = 0;
19
Window xrecord_focus_window = None;
20
Window xrecord_wm_window = None;
21
Window xrecord_ptr_window = None;
22
KeySym xrecord_keysym = NoSymbol;
25
char xrecord_name_info[NAMEINFO];
27
#define SCR_ATTR_CACHE 8
28
winattr_t scr_attr_cache[SCR_ATTR_CACHE];
29
static double attr_cache_max_age = 1.5;
31
Display *rdpy_data = NULL; /* Data connection for RECORD */
32
Display *rdpy_ctrl = NULL; /* Control connection for RECORD */
34
Display *gdpy_ctrl = NULL;
35
int xserver_grabbed = 0;
37
int trap_record_xerror(Display *, XErrorEvent *);
39
void initialize_xrecord(void);
40
void shutdown_xrecord(void);
41
int xrecord_skip_keysym(rfbKeySym keysym);
42
int xrecord_skip_button(int new, int old);
43
int xrecord_scroll_keysym(rfbKeySym keysym);
44
void check_xrecord_reset(int force);
45
void xrecord_watch(int start, int setby);
48
#if LIBVNCSERVER_HAVE_RECORD
49
static XRecordRange *rr_CA = NULL;
50
static XRecordRange *rr_CW = NULL;
51
static XRecordRange *rr_GS = NULL;
52
static XRecordRange *rr_scroll[10];
53
static XRecordContext rc_scroll;
54
static XRecordClientSpec rcs_scroll;
55
static XRecordRange *rr_grab[10];
56
static XRecordContext rc_grab;
57
static XRecordClientSpec rcs_grab;
59
static XErrorEvent *trapped_record_xerror_event;
60
static Display *gdpy_data = NULL;
62
static void xrecord_grabserver(int start);
63
static int xrecord_vi_scroll_keysym(rfbKeySym keysym);
64
static int xrecord_emacs_scroll_keysym(rfbKeySym keysym);
65
static int lookup_attr_cache(Window win, int *cache_index, int *next_index);
66
#if LIBVNCSERVER_HAVE_RECORD
67
static void record_CA(XPointer ptr, XRecordInterceptData *rec_data);
68
static void record_CW(XPointer ptr, XRecordInterceptData *rec_data);
69
static void record_switch(XPointer ptr, XRecordInterceptData *rec_data);
70
static void record_grab(XPointer ptr, XRecordInterceptData *rec_data);
71
static void shutdown_record_context(XRecordContext rc, int bequiet, int reopen);
73
static void check_xrecord_grabserver(void);
76
int trap_record_xerror(Display *d, XErrorEvent *error) {
77
trapped_record_xerror = 1;
78
trapped_record_xerror_event = error;
80
if (d) {} /* unused vars warning: */
85
static void xrecord_grabserver(int start) {
86
XErrorHandler old_handler = NULL;
90
fprintf(stderr, "xrecord_grabserver%d/%d %.5f\n",
91
xserver_grabbed, start, dnowx());
94
if (! gdpy_ctrl || ! gdpy_data) {
97
#if LIBVNCSERVER_HAVE_RECORD
102
XRecordDisableContext(gdpy_ctrl, rc_grab);
103
XRecordFreeContext(gdpy_ctrl, rc_grab);
104
XFlush_wr(gdpy_ctrl);
112
rcs_grab = XRecordAllClients;
114
rc_grab = XRecordCreateContext(gdpy_ctrl, 0, &rcs_grab, 1, rr_grab, 1);
115
trapped_record_xerror = 0;
116
old_handler = XSetErrorHandler(trap_record_xerror);
118
XSync(gdpy_ctrl, True);
120
if (! rc_grab || trapped_record_xerror) {
121
XCloseDisplay_wr(gdpy_ctrl);
122
XCloseDisplay_wr(gdpy_data);
125
XSetErrorHandler(old_handler);
128
rc = XRecordEnableContextAsync(gdpy_data, rc_grab, record_grab, NULL);
129
if (!rc || trapped_record_xerror) {
130
XCloseDisplay_wr(gdpy_ctrl);
131
XCloseDisplay_wr(gdpy_data);
134
XSetErrorHandler(old_handler);
137
XSetErrorHandler(old_handler);
138
XFlush_wr(gdpy_data);
140
if (!rc || !old_handler) {}
143
fprintf(stderr, "xrecord_grabserver-done: %.5f\n", dnowx());
147
void initialize_xrecord(void) {
149
if (! xrecord_present) {
159
#if LIBVNCSERVER_HAVE_RECORD
161
if (rr_CA) XFree_wr(rr_CA);
162
if (rr_CW) XFree_wr(rr_CW);
163
if (rr_GS) XFree_wr(rr_GS);
165
rr_CA = XRecordAllocRange();
166
rr_CW = XRecordAllocRange();
167
rr_GS = XRecordAllocRange();
168
if (!rr_CA || !rr_CW || !rr_GS) {
171
/* protocol request ranges: */
172
rr_CA->core_requests.first = X_CopyArea;
173
rr_CA->core_requests.last = X_CopyArea;
175
rr_CW->core_requests.first = X_ConfigureWindow;
176
rr_CW->core_requests.last = X_ConfigureWindow;
178
rr_GS->core_requests.first = X_GrabServer;
179
rr_GS->core_requests.last = X_UngrabServer;
182
/* open a 2nd control connection to DISPLAY: */
184
XCloseDisplay_wr(rdpy_data);
188
XCloseDisplay_wr(rdpy_ctrl);
191
rdpy_ctrl = XOpenDisplay_wr(DisplayString(dpy));
193
fprintf(stderr, "rdpy_ctrl open failed: %s / %s / %s / %s\n", getenv("DISPLAY"), DisplayString(dpy), getenv("XAUTHORITY"), getenv("XAUTHORIT_"));
196
XSync(rdpy_ctrl, True);
197
/* open datalink connection to DISPLAY: */
198
rdpy_data = XOpenDisplay_wr(DisplayString(dpy));
200
fprintf(stderr, "rdpy_data open failed\n");
202
if (!rdpy_ctrl || ! rdpy_data) {
206
disable_grabserver(rdpy_ctrl, 0);
207
disable_grabserver(rdpy_data, 0);
212
* now set up the GrabServer watcher. We get GrabServer
213
* deadlock in XRecordCreateContext() even with XTestGrabServer
214
* in place, why? Not sure, so we manually watch for grabs...
217
XCloseDisplay_wr(gdpy_data);
221
XCloseDisplay_wr(gdpy_ctrl);
226
gdpy_ctrl = XOpenDisplay_wr(DisplayString(dpy));
228
fprintf(stderr, "gdpy_ctrl open failed\n");
231
XSync(gdpy_ctrl, True);
232
gdpy_data = XOpenDisplay_wr(DisplayString(dpy));
234
fprintf(stderr, "gdpy_data open failed\n");
236
if (gdpy_ctrl && gdpy_data) {
237
disable_grabserver(gdpy_ctrl, 0);
238
disable_grabserver(gdpy_data, 0);
239
xrecord_grabserver(1);
245
void shutdown_xrecord(void) {
246
#if LIBVNCSERVER_HAVE_RECORD
249
fprintf(stderr, "shutdown_xrecord%d %.5f\n",
250
xserver_grabbed, dnowx());
253
if (rr_CA) XFree_wr(rr_CA);
254
if (rr_CW) XFree_wr(rr_CW);
255
if (rr_GS) XFree_wr(rr_GS);
262
if (rdpy_ctrl && rc_scroll) {
263
XRecordDisableContext(rdpy_ctrl, rc_scroll);
264
XRecordFreeContext(rdpy_ctrl, rc_scroll);
265
XSync(rdpy_ctrl, False);
269
if (gdpy_ctrl && rc_grab) {
270
XRecordDisableContext(gdpy_ctrl, rc_grab);
271
XRecordFreeContext(gdpy_ctrl, rc_grab);
272
XSync(gdpy_ctrl, False);
277
XCloseDisplay_wr(rdpy_data);
281
XCloseDisplay_wr(rdpy_ctrl);
285
XCloseDisplay_wr(gdpy_data);
289
XCloseDisplay_wr(gdpy_ctrl);
298
fprintf(stderr, "shutdown_xrecord-done: %.5f\n", dnowx());
302
int xrecord_skip_keysym(rfbKeySym keysym) {
303
KeySym sym = (KeySym) keysym;
304
int ok = -1, matched = 0;
306
if (scroll_key_list) {
308
if (scroll_key_list[0]) {
312
while (scroll_key_list[k] != NoSymbol) {
313
if (scroll_key_list[k++] == sym) {
334
} else if (ok == 0) {
338
/* apply various heuristics: */
340
if (IsModifierKey(sym)) {
341
/* Shift, Control, etc, usu. generate no scrolls */
344
if (sym == XK_space && scroll_term) {
345
/* space in a terminal is usu. full page... */
347
static Window prev_top = None;
349
static char name[256];
352
win = query_pointer(rootwin);
354
if (win != None && win != rootwin) {
355
if (prev_top != None && win == prev_top) {
356
; /* use cached result */
360
win = descend_pointer(6, win, name, size);
363
if (match_str_list(name, scroll_term)) {
369
/* TBD use typing_rate() so */
373
int xrecord_skip_button(int new, int old) {
374
/* unused vars warning: */
380
static int xrecord_vi_scroll_keysym(rfbKeySym keysym) {
381
KeySym sym = (KeySym) keysym;
382
if (sym == XK_J || sym == XK_j || sym == XK_K || sym == XK_k) {
385
if (sym == XK_D || sym == XK_d || sym == XK_U || sym == XK_u) {
386
return 1; /* Ctrl-d/u */
388
if (sym == XK_Z || sym == XK_z) {
389
return 1; /* zz, zt, zb .. */
394
static int xrecord_emacs_scroll_keysym(rfbKeySym keysym) {
395
KeySym sym = (KeySym) keysym;
396
if (sym == XK_N || sym == XK_n || sym == XK_P || sym == XK_p) {
397
return 1; /* emacs */
399
/* Must be some more ... */
403
int xrecord_scroll_keysym(rfbKeySym keysym) {
404
KeySym sym = (KeySym) keysym;
405
/* X11/keysymdef.h */
407
if (sym == XK_Return || sym == XK_KP_Enter || sym == XK_Linefeed) {
408
return 1; /* Enter */
410
if (sym==XK_Up || sym==XK_KP_Up || sym==XK_Down || sym==XK_KP_Down) {
411
return 1; /* U/D arrows */
413
if (sym == XK_Left || sym == XK_KP_Left || sym == XK_Right ||
414
sym == XK_KP_Right) {
415
return 1; /* L/R arrows */
417
if (xrecord_vi_scroll_keysym(keysym)) {
420
if (xrecord_emacs_scroll_keysym(keysym)) {
426
static int lookup_attr_cache(Window win, int *cache_index, int *next_index) {
427
double now, t, oldest = 0.0;
428
int i, old_index = -1, count = 0;
437
if (attr_cache_max_age == 0.0) {
442
for (i=0; i < SCR_ATTR_CACHE; i++) {
444
cwin = scr_attr_cache[i].win;
445
t = scr_attr_cache[i].time;
447
if (now > t + attr_cache_max_age) {
448
/* expire it even if it is the one we want */
449
scr_attr_cache[i].win = cwin = None;
450
scr_attr_cache[i].fetched = 0;
451
scr_attr_cache[i].valid = 0;
454
if (*next_index == -1 && cwin == None) {
457
if (*next_index == -1) {
459
if (old_index == -1 || t < oldest) {
468
if (*cache_index == -1) {
472
scr_attr_cache[i].win = None;
473
scr_attr_cache[i].fetched = 0;
474
scr_attr_cache[i].valid = 0;
478
if (*next_index == -1) {
479
*next_index = old_index;
482
if (0) fprintf(stderr, "lookup_attr_cache count: %d\n", count);
483
if (*cache_index != -1) {
491
static XID xrecord_seq = 0;
492
static double xrecord_start = 0.0;
494
#if LIBVNCSERVER_HAVE_RECORD
495
static void record_CA(XPointer ptr, XRecordInterceptData *rec_data) {
497
Window src = None, dst = None, c;
498
XWindowAttributes attr;
499
int src_x, src_y, dst_x, dst_y, rx, ry;
500
int good = 1, dx, dy, k=0, i;
502
int dba = 0, db = debug_scroll;
503
int cache_index, next_index, valid;
506
if (rec_data->category == XRecordFromClient) {
507
req = (xCopyAreaReq *) rec_data->data;
508
if (req->reqType == X_CopyArea) {
509
src = req->srcDrawable;
510
dst = req->dstDrawable;
515
if (dba || db > 1) fprintf(stderr, "record_CA-%d id_base: 0x%lx ptr: 0x%lx "
516
"seq: 0x%lx rc: 0x%lx cat: %d swapped: %d 0x%lx/0x%lx\n", k++,
517
rec_data->id_base, (unsigned long) ptr, xrecord_seq, rc_scroll,
518
rec_data->category, rec_data->client_swapped, src, dst);
523
if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
525
if (rec_data->id_base == 0) {
528
if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
530
if ((XID) ptr != xrecord_seq) {
533
if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
535
if (rec_data->category != XRecordFromClient) {
538
if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
540
req = (xCopyAreaReq *) rec_data->data;
542
if (req->reqType != X_CopyArea) {
545
if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
549
xterm, gnome-terminal, others.
551
Note we miss the X_ImageText8 that clears the block cursor. So there is a
552
short period of time with a painting error: two cursors, one above the other.
555
draw: 0x8c00017 nChars: 1, gc: 0x8c00013, x: 101, y: 585, chars=' '
557
window: 0x8c00018, x: 2, y: 217, w: 10, h: 5
559
draw: 0x8c00018 gc: 0x8c0000a, shape: 0, coordMode: 0,
561
draw: 0x8c00018 gc: 0x8c0000b, shape: 0, coordMode: 0,
563
src: 0x8c00017, dst: 0x8c00017, gc: 0x8c00013, srcX: 17, srcY: 15, dstX: 17, dstY: 2, w: 480, h: 572
564
X_ChangeWindowAttributes
566
window: 0x8c00017, x: 17, y: 574, w: 480, h: 13
567
X_ChangeWindowAttributes
571
src = req->srcDrawable;
572
dst = req->dstDrawable;
580
if (w*h < (unsigned int) scrollcopyrect_min_area) {
582
} else if (!src || !dst) {
584
} else if (src != dst) {
586
} else if (scr_ev_cnt >= SCR_EV_MAX) {
593
if (dx != 0 && dy != 0) {
600
if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
603
* after all of the above succeeds, now contact X server.
604
* we try to get away with some caching here.
606
if (lookup_attr_cache(src, &cache_index, &next_index)) {
608
attr.x = scr_attr_cache[i].x;
609
attr.y = scr_attr_cache[i].y;
610
attr.width = scr_attr_cache[i].width;
611
attr.height = scr_attr_cache[i].height;
612
attr.map_state = scr_attr_cache[i].map_state;
613
rx = scr_attr_cache[i].rx;
614
ry = scr_attr_cache[i].ry;
615
valid = scr_attr_cache[i].valid;
618
valid = valid_window(src, &attr, 1);
621
if (!xtranslate(src, rootwin, 0, 0, &rx, &ry, &c, 1)) {
625
if (next_index >= 0) {
627
scr_attr_cache[i].win = src;
628
scr_attr_cache[i].fetched = 1;
629
scr_attr_cache[i].valid = valid;
630
scr_attr_cache[i].time = dnow();
632
scr_attr_cache[i].x = attr.x;
633
scr_attr_cache[i].y = attr.y;
634
scr_attr_cache[i].width = attr.width;
635
scr_attr_cache[i].height = attr.height;
636
scr_attr_cache[i].border_width = attr.border_width;
637
scr_attr_cache[i].depth = attr.depth;
638
scr_attr_cache[i].class = attr.class;
639
scr_attr_cache[i].backing_store =
641
scr_attr_cache[i].map_state = attr.map_state;
643
scr_attr_cache[i].rx = rx;
644
scr_attr_cache[i].ry = ry;
652
if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
654
if (attr.map_state != IsViewable) {
659
if (0 || dba || db) {
661
st = (double) rec_data->server_time/1000.0;
662
dt = (dnow() - servertime_diff) - st;
663
fprintf(stderr, "record_CA-%d *FOUND_SCROLL: src: 0x%lx dx: %d dy: %d "
664
"x: %d y: %d w: %d h: %d st: %.4f %.4f %.4f\n", k++, src, dx, dy,
665
src_x, src_y, w, h, st, dt, dnowx());
671
scr_ev[i].frame = None;
674
scr_ev[i].x = rx + dst_x;
675
scr_ev[i].y = ry + dst_y;
678
scr_ev[i].t = ((double) rec_data->server_time)/1000.0;
679
scr_ev[i].win_x = rx;
680
scr_ev[i].win_y = ry;
681
scr_ev[i].win_w = attr.width;
682
scr_ev[i].win_h = attr.height;
690
scr_ev[i].new_x = rx + src_x;
691
scr_ev[i].new_y = ry + src_y;
693
scr_ev[i].new_h = dy;
695
scr_ev[i].new_x = rx + src_x;
696
scr_ev[i].new_y = ry + dst_y + h;
698
scr_ev[i].new_h = -dy;
700
} else if (dy == 0) {
702
scr_ev[i].new_x = rx + src_x;
703
scr_ev[i].new_y = rx + src_y;
704
scr_ev[i].new_w = dx;
707
scr_ev[i].new_x = rx + dst_x + w;
708
scr_ev[i].new_y = ry + src_y;
709
scr_ev[i].new_w = -dx;
717
typedef struct cw_event {
723
static cw_event_t cw_events[MAX_CW];
725
static void record_CW(XPointer ptr, XRecordInterceptData *rec_data) {
726
xConfigureWindowReq *req;
727
Window win = None, c;
728
Window src = None, dst = None;
729
XWindowAttributes attr;
730
int absent = 0x100000;
731
int src_x, src_y, dst_x, dst_y, rx, ry;
732
int good = 1, dx, dy, k=0, i, j, match, list[3];
733
int f_x, f_y, f_w, f_h;
735
int x0, y0, w0, h0, x1, y1, w1, h1, x2, y2, w2, h2;
736
static int index = 0;
737
unsigned int vals[4];
740
int dba = 0, db = debug_scroll;
741
int cache_index, next_index, valid;
744
if (rec_data->category == XRecordFromClient) {
745
req = (xConfigureWindowReq *) rec_data->data;
746
if (req->reqType == X_ConfigureWindow) {
752
if (dba || db > 1) fprintf(stderr, "record_CW-%d id_base: 0x%lx ptr: 0x%lx "
753
"seq: 0x%lx rc: 0x%lx cat: %d swapped: %d 0x%lx/0x%lx\n", k++,
754
rec_data->id_base, (unsigned long) ptr, xrecord_seq, rc_scroll,
755
rec_data->category, rec_data->client_swapped, src, dst);
761
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
763
if ((XID) ptr != xrecord_seq) {
766
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
768
if (rec_data->id_base == 0) {
771
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
773
if (rec_data->category == XRecordStartOfData) {
777
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
779
if (rec_data->category != XRecordFromClient) {
782
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
784
if (rec_data->client_swapped) {
787
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
789
req = (xConfigureWindowReq *) rec_data->data;
791
if (req->reqType != X_ConfigureWindow) {
794
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
804
/* require no more than these 4 flags */
807
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
809
f_x = req->mask & CWX;
810
f_y = req->mask & CWY;
811
f_w = req->mask & CWWidth;
812
f_h = req->mask & CWHeight;
814
if (! f_x || ! f_y) {
816
; /* netscape 4.x style */
821
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
823
if ( (f_w && !f_h) || (!f_w && f_h) ) {
826
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
828
for (i=0; i<4; i++) {
833
data += sz_xConfigureWindowReq;
835
for (i=0; i<req->length; i++) {
838
* We use unsigned int for the values. There were
839
* some crashes on 64bit machines with unsigned longs.
840
* Need to check that X protocol sends 32bit values.
842
v = *( (unsigned int *) data);
843
if (db > 1) fprintf(stderr, " vals[%d] 0x%x/%d\n", i, v, v);
845
data += sizeof(unsigned int);
848
if (index >= MAX_CW) {
851
/* FIXME, circular, etc. */
852
for (i=0; i<2; i++) {
854
cw_events[i].win = cw_events[j].win;
855
cw_events[i].x = cw_events[j].x;
856
cw_events[i].y = cw_events[j].y;
857
cw_events[i].w = cw_events[j].w;
858
cw_events[i].h = cw_events[j].h;
863
if (! f_x && ! f_y) {
864
/* netscape 4.x style CWWidth,CWHeight */
871
cw_events[index].win = req->window;
874
cw_events[index].x = absent;
876
cw_events[index].x = (int) vals[0];
879
cw_events[index].y = absent;
881
cw_events[index].y = (int) vals[1];
885
cw_events[index].w = absent;
887
cw_events[index].w = (int) vals[2];
890
cw_events[index].h = absent;
892
cw_events[index].h = (int) vals[3];
895
x = cw_events[index].x;
896
y = cw_events[index].y;
897
w = cw_events[index].w;
898
h = cw_events[index].h;
899
win = cw_events[index].win;
901
if (dba || db) fprintf(stderr, " record_CW ind: %d win: 0x%lx x: %d y: %d w: %d h: %d\n",
902
index, win, x, y, w, h);
908
} else if (w != absent && h != absent &&
909
w*h < scrollcopyrect_min_area) {
916
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
919
for (j=index - 1; j >= 0; j--) {
920
if (cw_events[j].win == win) {
931
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
937
Up arrow: window moves down a bit (dy > 0):
940
length: 7, window: 0x2e000cd, mask: 0xf, v0 0, v1 -18, v2 760, v3 906, v4 327692, v5 48234701, v6 3,
941
CW-mask: CWX,CWY,CWWidth,CWHeight,
943
length: 5, window: 0x2e000cd, mask: 0x3, v0 0, v1 0, v2 506636, v3 48234701, v4 48234511,
946
length: 7, window: 0x2e000cd, mask: 0xf, v0 0, v1 0, v2 760, v3 888, v4 65579, v5 0, v6 108009,
947
CW-mask: CWX,CWY,CWWidth,CWHeight,
949
Down arrow: window moves up a bit (dy < 0):
952
length: 7, window: 0x2e000cd, mask: 0xf, v0 0, v1 0, v2 760, v3 906, v4 327692, v5 48234701, v6 262147,
953
CW-mask: CWX,CWY,CWWidth,CWHeight,
955
length: 5, window: 0x2e000cd, mask: 0x3, v0 0, v1 -18, v2 506636, v3 48234701, v4 48234511,
958
length: 7, window: 0x2e000cd, mask: 0xf, v0 0, v1 0, v2 760, v3 888, v4 96555, v5 48265642, v6 48265262,
959
CW-mask: CWX,CWY,CWWidth,CWHeight,
965
71.76142 0.01984 X_ConfigureWindow
966
length: 7, window: 0x9800488, mask: 0xf, v0 0, v1 -15, v2 785, v3 882, v4 327692, v5 159384712, v6 1769484,
967
CW-mask: CWX,CWY,CWWidth,CWHeight,
968
71.76153 0.00011 X_ConfigureWindow
969
length: 5, window: 0x9800488, mask: 0xc, v0 785, v1 867, v2 329228, v3 159384712, v4 159383555,
970
CW-mask: CWWidth,CWHeight,
972
71.76157 0.00003 X_ConfigureWindow
973
length: 5, window: 0x9800488, mask: 0x3, v0 0, v1 0, v2 131132, v3 159385313, v4 328759,
978
72.93147 0.01990 X_ConfigureWindow
979
length: 5, window: 0x9800488, mask: 0xc, v0 785, v1 882, v2 328972, v3 159384712, v4 159383555,
980
CW-mask: CWWidth,CWHeight,
982
72.93156 0.00009 X_ConfigureWindow
983
length: 5, window: 0x9800488, mask: 0x3, v0 0, v1 -15, v2 458764, v3 159384712, v4 159383567,
985
72.93160 0.00004 X_ConfigureWindow
986
length: 7, window: 0x9800488, mask: 0xf, v0 0, v1 0, v2 785, v3 867, v4 131132, v5 159385335, v6 328759,
987
CW-mask: CWX,CWY,CWWidth,CWHeight,
990
sadly, probably need to handle some more...
993
x0 = cw_events[list[2]].x;
994
y0 = cw_events[list[2]].y;
995
w0 = cw_events[list[2]].w;
996
h0 = cw_events[list[2]].h;
998
x1 = cw_events[list[1]].x;
999
y1 = cw_events[list[1]].y;
1000
w1 = cw_events[list[1]].w;
1001
h1 = cw_events[list[1]].h;
1003
x2 = cw_events[list[0]].x;
1004
y2 = cw_events[list[0]].y;
1005
w2 = cw_events[list[0]].w;
1006
h2 = cw_events[list[0]].h;
1008
/* see NS4 XXX's above: */
1009
if (w2 == absent || h2 == absent) {
1018
if (x1 == absent || y1 == absent) {
1027
if (x0 == absent || y0 == absent) {
1030
/* hmmm... what to do */
1038
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);
1048
/* check w and h before we modify them */
1049
if (w <= 0 || h <= 0) {
1051
} else if (w == absent || h == absent) {
1057
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
1075
if (x0 == absent || x1 == absent || x2 == absent) {
1077
} else if (y0 == absent || y1 == absent || y2 == absent) {
1079
} else if (dx != 0 && dy != 0) {
1081
} else if (w0 - w2 != nabs(dx)) {
1083
} else if (h0 - h2 != nabs(dy)) {
1085
} else if (scr_ev_cnt >= SCR_EV_MAX) {
1092
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
1096
* after all of the above succeeds, now contact X server.
1098
if (lookup_attr_cache(win, &cache_index, &next_index)) {
1100
attr.x = scr_attr_cache[i].x;
1101
attr.y = scr_attr_cache[i].y;
1102
attr.width = scr_attr_cache[i].width;
1103
attr.height = scr_attr_cache[i].height;
1104
attr.map_state = scr_attr_cache[i].map_state;
1105
rx = scr_attr_cache[i].rx;
1106
ry = scr_attr_cache[i].ry;
1107
valid = scr_attr_cache[i].valid;
1109
if (0) fprintf(stderr, "lookup_attr_cache hit: %2d %2d 0x%lx %d\n",
1110
cache_index, next_index, win, valid);
1113
valid = valid_window(win, &attr, 1);
1115
if (0) fprintf(stderr, "lookup_attr_cache MISS: %2d %2d 0x%lx %d\n",
1116
cache_index, next_index, win, valid);
1119
if (!xtranslate(win, rootwin, 0, 0, &rx, &ry, &c, 1)) {
1123
if (next_index >= 0) {
1125
scr_attr_cache[i].win = win;
1126
scr_attr_cache[i].fetched = 1;
1127
scr_attr_cache[i].valid = valid;
1128
scr_attr_cache[i].time = dnow();
1130
scr_attr_cache[i].x = attr.x;
1131
scr_attr_cache[i].y = attr.y;
1132
scr_attr_cache[i].width = attr.width;
1133
scr_attr_cache[i].height = attr.height;
1134
scr_attr_cache[i].depth = attr.depth;
1135
scr_attr_cache[i].class = attr.class;
1136
scr_attr_cache[i].backing_store =
1138
scr_attr_cache[i].map_state = attr.map_state;
1140
scr_attr_cache[i].rx = rx;
1141
scr_attr_cache[i].ry = ry;
1149
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
1151
if (attr.map_state != IsViewable) {
1154
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
1156
if (0 || dba || db) {
1158
st = (double) rec_data->server_time/1000.0;
1159
dt = (dnow() - servertime_diff) - st;
1160
fprintf(stderr, "record_CW-%d *FOUND_SCROLL: win: 0x%lx dx: %d dy: %d "
1161
"x: %d y: %d w: %d h: %d st: %.4f dt: %.4f %.4f\n", k++, win,
1162
dx, dy, src_x, src_y, w, h, st, dt, dnowx());
1167
scr_ev[i].win = win;
1168
scr_ev[i].frame = None;
1171
scr_ev[i].x = rx + dst_x;
1172
scr_ev[i].y = ry + dst_y;
1175
scr_ev[i].t = ((double) rec_data->server_time)/1000.0;
1176
scr_ev[i].win_x = rx;
1177
scr_ev[i].win_y = ry;
1178
scr_ev[i].win_w = attr.width;
1179
scr_ev[i].win_h = attr.height;
1180
scr_ev[i].new_x = 0;
1181
scr_ev[i].new_y = 0;
1182
scr_ev[i].new_w = 0;
1183
scr_ev[i].new_h = 0;
1187
scr_ev[i].new_x = rx + src_x;
1188
scr_ev[i].new_y = ry + src_y;
1189
scr_ev[i].new_w = w;
1190
scr_ev[i].new_h = dy;
1192
scr_ev[i].new_x = rx + src_x;
1193
scr_ev[i].new_y = ry + dst_y + h;
1194
scr_ev[i].new_w = w;
1195
scr_ev[i].new_h = -dy;
1197
} else if (dy == 0) {
1199
scr_ev[i].new_x = rx + src_x;
1200
scr_ev[i].new_y = rx + src_y;
1201
scr_ev[i].new_w = dx;
1202
scr_ev[i].new_h = h;
1204
scr_ev[i].new_x = rx + dst_x + w;
1205
scr_ev[i].new_y = ry + src_y;
1206
scr_ev[i].new_w = -dx;
1207
scr_ev[i].new_h = h;
1211
/* indicate we have a new one */
1217
static void record_switch(XPointer ptr, XRecordInterceptData *rec_data) {
1218
static int first = 1;
1223
for (i=0; i<SCR_ATTR_CACHE; i++) {
1224
scr_attr_cache[i].win = None;
1225
scr_attr_cache[i].fetched = 0;
1226
scr_attr_cache[i].valid = 0;
1227
scr_attr_cache[i].time = 0.0;
1232
/* should handle control msgs, start/stop/etc */
1233
if (rec_data->category == XRecordStartOfData) {
1234
record_CW(ptr, rec_data);
1235
} else if (rec_data->category == XRecordEndOfData) {
1237
} else if (rec_data->category == XRecordClientStarted) {
1239
} else if (rec_data->category == XRecordClientDied) {
1241
} else if (rec_data->category == XRecordFromServer) {
1245
if (rec_data->category != XRecordFromClient) {
1246
XRecordFreeData(rec_data);
1250
req = (xReq *) rec_data->data;
1252
if (req->reqType == X_CopyArea) {
1253
record_CA(ptr, rec_data);
1254
} else if (req->reqType == X_ConfigureWindow) {
1255
record_CW(ptr, rec_data);
1259
XRecordFreeData(rec_data);
1262
static void record_grab(XPointer ptr, XRecordInterceptData *rec_data) {
1266
if (debug_grabs) db = 1;
1268
/* should handle control msgs, start/stop/etc */
1269
if (rec_data->category == XRecordStartOfData) {
1271
} else if (rec_data->category == XRecordEndOfData) {
1273
} else if (rec_data->category == XRecordClientStarted) {
1275
} else if (rec_data->category == XRecordClientDied) {
1277
} else if (rec_data->category == XRecordFromServer) {
1281
if (rec_data->category != XRecordFromClient) {
1282
XRecordFreeData(rec_data);
1286
req = (xReq *) rec_data->data;
1288
if (req->reqType == X_GrabServer) {
1289
double now = dnowx();
1291
if (db) rfbLog("X server Grabbed: %d %.5f\n", xserver_grabbed, now);
1292
if (xserver_grabbed > 1) {
1294
* some apps do multiple grabs... very unlikely
1295
* two apps will be doing it at same time.
1297
xserver_grabbed = 1;
1299
} else if (req->reqType == X_UngrabServer) {
1300
double now = dnowx();
1302
if (xserver_grabbed < 0) {
1303
xserver_grabbed = 0;
1305
if (db) rfbLog("X server Un-Grabbed: %d %.5f\n", xserver_grabbed, now);
1309
XRecordFreeData(rec_data);
1311
/* unused vars warning: */
1316
static void check_xrecord_grabserver(void) {
1317
#if LIBVNCSERVER_HAVE_RECORD
1318
int last_val, cnt = 0, i, max = 10;
1320
if (!gdpy_ctrl || !gdpy_data) {
1323
if (unixpw_in_progress) return;
1326
XFlush_wr(gdpy_ctrl);
1327
for (i=0; i<max; i++) {
1328
last_val = xserver_grabbed;
1329
XRecordProcessReplies(gdpy_data);
1330
if (xserver_grabbed != last_val) {
1337
XFlush_wr(gdpy_ctrl);
1339
if (debug_grabs && cnt > 0) {
1341
fprintf(stderr, "check_xrecord_grabserver: cnt=%d i=%d %.4f\n", cnt, i, d);
1346
#if LIBVNCSERVER_HAVE_RECORD
1347
static void shutdown_record_context(XRecordContext rc, int bequiet, int reopen) {
1349
int verb = (!bequiet && !quiet);
1352
if (0 || debug_scroll) {
1353
rfbLog("shutdown_record_context(0x%lx, %d, %d)\n", rc,
1358
ret1 = XRecordDisableContext(rdpy_ctrl, rc);
1359
if (!ret1 && verb) {
1360
rfbLog("XRecordDisableContext(0x%lx) failed.\n", rc);
1362
ret2 = XRecordFreeContext(rdpy_ctrl, rc);
1363
if (!ret2 && verb) {
1364
rfbLog("XRecordFreeContext(0x%lx) failed.\n", rc);
1366
XFlush_wr(rdpy_ctrl);
1368
if (reopen == 2 && ret1 && ret2) {
1369
reopen = 0; /* 2 means reopen only on failure */
1371
if (reopen && gdpy_ctrl) {
1372
check_xrecord_grabserver();
1373
if (xserver_grabbed) {
1374
rfbLog("shutdown_record_context: skip reopen,"
1375
" server grabbed\n");
1380
char *dpystr = DisplayString(dpy);
1383
rfbLog("closing RECORD data connection.\n");
1385
XCloseDisplay_wr(rdpy_data);
1389
rfbLog("closing RECORD control connection.\n");
1391
XCloseDisplay_wr(rdpy_ctrl);
1394
rdpy_ctrl = XOpenDisplay_wr(dpystr);
1397
rfbLog("Failed to reopen RECORD control connection:"
1399
rfbLog(" disabling RECORD scroll detection.\n");
1405
disable_grabserver(rdpy_ctrl, 0);
1406
XSync(rdpy_ctrl, True);
1408
rdpy_data = XOpenDisplay_wr(dpystr);
1411
rfbLog("Failed to reopen RECORD data connection:"
1413
rfbLog(" disabling RECORD scroll detection.\n");
1414
XCloseDisplay_wr(rdpy_ctrl);
1419
disable_grabserver(rdpy_data, 0);
1421
if (debug_scroll || (! bequiet && reopen == 2)) {
1422
rfbLog("reopened RECORD data and control display"
1423
" connections: %s\n", dpystr);
1429
void check_xrecord_reset(int force) {
1430
static double last_reset = 0.0;
1431
int reset_time = 60, require_idle = 10;
1432
int reset_time2 = 600, require_idle2 = 40;
1434
XErrorHandler old_handler = NULL;
1438
check_xrecord_grabserver();
1441
/* more dicey if not watching grabserver */
1442
reset_time = reset_time2;
1443
require_idle = require_idle2;
1455
if (xserver_grabbed) {
1459
if (unixpw_in_progress) return;
1461
#if LIBVNCSERVER_HAVE_RECORD
1466
if (last_reset == 0.0) {
1471
* try to wait for a break in input to reopen the displays
1472
* this is only to avoid XGrabServer deadlock on the repopens.
1476
} else if (now < last_reset + reset_time) {
1478
} else if (now < last_pointer_click_time + require_idle) {
1480
} else if (now < last_keyboard_time + require_idle) {
1484
trapped_record_xerror = 0;
1485
old_handler = XSetErrorHandler(trap_record_xerror);
1487
/* unlikely, but check again since we will definitely be doing it. */
1489
check_xrecord_grabserver();
1490
if (xserver_grabbed) {
1491
XSetErrorHandler(old_handler);
1497
shutdown_record_context(rc_scroll, 0, 1);
1500
XSetErrorHandler(old_handler);
1505
if (!old_handler || now == 0.0 || !last_reset || !force) {}
1509
#define RECORD_ERROR_MSG \
1511
rfbLog("trapped RECORD XError: %s %d/%d/%d (0x%lx)\n", \
1512
xerror_string(trapped_record_xerror_event), \
1513
(int) trapped_record_xerror_event->error_code, \
1514
(int) trapped_record_xerror_event->request_code, \
1515
(int) trapped_record_xerror_event->minor_code, \
1516
(int) trapped_record_xerror_event->resourceid); \
1519
void xrecord_watch(int start, int setby) {
1520
#if LIBVNCSERVER_HAVE_RECORD
1521
Window focus, wm, c, clast;
1522
static double create_time = 0.0;
1524
int do_shutdown = 0;
1525
int reopen_dpys = 1;
1526
XErrorHandler old_handler = NULL;
1527
static Window last_win = None, last_result = None;
1529
int db = debug_scroll;
1531
static double last_error = 0.0;
1540
/* XXX not working */
1547
if (now < last_error + 0.5) {
1553
check_xrecord_grabserver();
1555
if (xserver_grabbed) {
1556
if (db || debug_grabs) fprintf(stderr, "xrecord_watch: %d/%d out xserver_grabbed\n", start, setby);
1561
#if LIBVNCSERVER_HAVE_RECORD
1563
int shut_reopen = 2, shut_time = 25;
1564
if (db || debug_grabs) fprintf(stderr, "XRECORD OFF: %d/%d %.4f\n", xrecording, setby, now - x11vnc_start);
1567
xrecord_focus_window = None;
1568
xrecord_wm_window = None;
1569
xrecord_ptr_window = None;
1570
xrecord_keysym = NoSymbol;
1575
if (! do_shutdown && now > create_time + shut_time) {
1576
/* XXX unstable if we keep a RECORD going forever */
1583
if (db > 1) fprintf(stderr, "=== shutdown-scroll 0x%lx\n", rc_scroll);
1585
trapped_record_xerror = 0;
1586
old_handler = XSetErrorHandler(trap_record_xerror);
1588
shutdown_record_context(rc_scroll, 0, shut_reopen);
1592
* n.b. there is a grabserver issue wrt
1593
* XRecordCreateContext() even though rdpy_ctrl
1594
* is set imprevious to grabs. Perhaps a bug
1595
* in the X server or library...
1597
* If there are further problems, a thought
1598
* to recreate rc_scroll right after the
1602
if (! use_xrecord) {
1603
XSetErrorHandler(old_handler);
1609
XRecordProcessReplies(rdpy_data);
1611
if (trapped_record_xerror) {
1616
XSetErrorHandler(old_handler);
1622
if (db > 1) fprintf(stderr, "=== disab-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll);
1624
trapped_record_xerror = 0;
1626
XSetErrorHandler(trap_record_xerror);
1628
rcs_scroll = XRecordCurrentClients;
1629
XRecordUnregisterClients(rdpy_ctrl, rc_scroll,
1631
XRecordDisableContext(rdpy_ctrl, rc_scroll);
1632
XFlush_wr(rdpy_ctrl);
1633
XRecordProcessReplies(rdpy_data);
1635
if (trapped_record_xerror) {
1638
shutdown_record_context(rc_scroll,
1644
if (! use_xrecord) {
1645
XSetErrorHandler(old_handler);
1651
XSetErrorHandler(old_handler);
1658
* XXX if we do a XFlush_wr(rdpy_ctrl) here we get:
1661
X Error of failed request: XRecordBadContext
1662
Major opcode of failed request: 145 (RECORD)
1663
Minor opcode of failed request: 5 (XRecordEnableContext)
1664
Context in failed request: 0x2200013
1665
Serial number of failed request: 29
1666
Current serial number in output stream: 29
1669
* need to figure out what is going on... since it may lead
1670
* infrequent failures.
1672
xrecord_focus_window = None;
1673
xrecord_wm_window = None;
1674
xrecord_ptr_window = None;
1675
xrecord_keysym = NoSymbol;
1679
if (db || debug_grabs) fprintf(stderr, "XRECORD ON: %d/%d %.4f\n", xrecording, setby, now - x11vnc_start);
1685
if (do_shutdown && rc_scroll) {
1686
static int didmsg = 0;
1687
/* should not happen... */
1689
rfbLog("warning: do_shutdown && rc_scroll\n");
1692
xrecord_watch(0, SCR_NONE);
1696
xrecord_focus_window = None;
1697
xrecord_wm_window = None;
1698
xrecord_ptr_window = None;
1699
xrecord_keysym = NoSymbol;
1700
xrecord_set_by_keys = 0;
1701
xrecord_set_by_mouse = 0;
1703
/* get the window with focus and mouse pointer: */
1712
* xrecord_focus_window / focus not currently used... save a
1713
* round trip to the X server for now.
1714
* N.B. our heuristic is inaccurate: if he is scrolling and
1715
* drifts off of the scrollbar onto another application we
1716
* will catch that application, not the starting ones.
1717
* check_xrecord_{keys,mouse} mitigates this somewhat by
1718
* delaying calls to xrecord_watch as much as possible.
1720
XGetInputFocus(dpy, &focus, &i);
1723
wm = query_pointer(rootwin);
1730
/* descend a bit to avoid wm frames: */
1731
if (c != rootwin && c == last_win) {
1732
/* use cached results to avoid roundtrips: */
1733
clast = last_result;
1734
} else if (scroll_good_all == NULL && scroll_skip_all == NULL) {
1735
/* more efficient if name info not needed. */
1736
xrecord_name_info[0] = '\0';
1737
clast = descend_pointer(6, c, NULL, 0);
1740
int matched_good = 0, matched_skip = 0;
1742
clast = descend_pointer(6, c, xrecord_name_info, NAMEINFO);
1743
if (db) fprintf(stderr, "name_info: %s\n", xrecord_name_info);
1745
nm = xrecord_name_info;
1747
if (scroll_good_all) {
1748
matched_good += match_str_list(nm, scroll_good_all);
1750
if (setby == SCR_KEY && scroll_good_key) {
1751
matched_good += match_str_list(nm, scroll_good_key);
1753
if (setby == SCR_MOUSE && scroll_good_mouse) {
1754
matched_good += match_str_list(nm, scroll_good_mouse);
1756
if (scroll_skip_all) {
1757
matched_skip += match_str_list(nm, scroll_skip_all);
1759
if (setby == SCR_KEY && scroll_skip_key) {
1760
matched_skip += match_str_list(nm, scroll_skip_key);
1762
if (setby == SCR_MOUSE && scroll_skip_mouse) {
1763
matched_skip += match_str_list(nm, scroll_skip_mouse);
1766
if (!matched_good && matched_skip) {
1771
/* cache results for possible use next call */
1773
last_result = clast;
1776
if (!clast || clast == rootwin) {
1777
if (db) fprintf(stderr, "--- xrecord_watch: SKIP.\n");
1783
/* set protocol request ranges: */
1784
rr_scroll[0] = rr_CA;
1785
rr_scroll[1] = rr_CW;
1788
* start trapping... there still are some occasional failures
1789
* not yet understood, likely some race condition WRT the
1790
* context being setup.
1792
trapped_record_xerror = 0;
1793
old_handler = XSetErrorHandler(trap_record_xerror);
1796
/* do_shutdown case or first time in */
1800
* Even though rdpy_ctrl is impervious to grabs
1801
* at this point, we still get deadlock, why?
1802
* It blocks in the library find_display() call.
1804
check_xrecord_grabserver();
1805
if (xserver_grabbed) {
1806
XSetErrorHandler(old_handler);
1812
rcs_scroll = (XRecordClientSpec) clast;
1813
rc_scroll = XRecordCreateContext(rdpy_ctrl, 0, &rcs_scroll, 1,
1816
if (! do_shutdown) {
1817
XSync(rdpy_ctrl, False);
1819
if (db) fprintf(stderr, "NEW rc: 0x%lx\n", rc_scroll);
1821
dtime0(&create_time);
1826
} else if (! do_shutdown) {
1829
* should have been unregistered in xrecord_watch(0)...
1831
rcs_scroll = XRecordCurrentClients;
1832
XRecordUnregisterClients(rdpy_ctrl, rc_scroll,
1835
if (db > 1) fprintf(stderr, "=2= unreg-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll);
1839
rcs_scroll = (XRecordClientSpec) clast;
1841
if (db > 1) fprintf(stderr, "=-= reg-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll);
1843
if (!XRecordRegisterClients(rdpy_ctrl, rc_scroll, 0,
1844
&rcs_scroll, 1, rr_scroll, 2)) {
1845
if (1 || now > last_error + 60) {
1846
rfbLog("failed to register client 0x%lx with"
1847
" X RECORD context rc_scroll.\n", clast);
1851
/* continue on for now... */
1855
XFlush_wr(rdpy_ctrl);
1857
if (db) fprintf(stderr, "rc_scroll: 0x%lx\n", rc_scroll);
1858
if (trapped_record_xerror) {
1863
XSetErrorHandler(old_handler);
1867
rfbLog("failed to create X RECORD context rc_scroll.\n");
1868
rfbLog(" switching to -noscrollcopyrect mode.\n");
1870
} else if (! rcs_scroll || trapped_record_xerror) {
1871
/* try again later */
1872
shutdown_record_context(rc_scroll, 0, reopen_dpys);
1876
XSetErrorHandler(old_handler);
1882
xrecord_focus_window = focus;
1884
/* xrecord_focus_window currently unused. */
1885
if (! xrecord_focus_window) {
1886
xrecord_focus_window = clast;
1889
xrecord_wm_window = wm;
1890
if (! xrecord_wm_window) {
1891
xrecord_wm_window = clast;
1894
xrecord_ptr_window = clast;
1898
dtime0(&xrecord_start);
1900
rc = XRecordEnableContextAsync(rdpy_data, rc_scroll, record_switch,
1901
(XPointer) xrecord_seq);
1903
if (!rc || trapped_record_xerror) {
1904
if (1 || now > last_error + 60) {
1905
rfbLog("failed to enable RECORD context "
1906
"rc_scroll: 0x%lx rc: %d\n", rc_scroll, rc);
1907
if (trapped_record_xerror) {
1911
shutdown_record_context(rc_scroll, 0, reopen_dpys);
1915
/* continue on for now... */
1917
XSetErrorHandler(old_handler);
1919
/* XXX this may cause more problems than it solves... */
1921
XFlush_wr(rdpy_data);