You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libtdevnc/x11vnc/xrecord.c

1938 lines
44 KiB

/* -- xrecord.c -- */
#include "x11vnc.h"
#include "xwrappers.h"
#include "win_utils.h"
#include "cleanup.h"
#include "userinput.h"
#include "winattr_t.h"
#include "scrollevent_t.h"
#include "unixpw.h"
#define SCR_EV_MAX 128
scroll_event_t scr_ev[SCR_EV_MAX];
int scr_ev_cnt;
int xrecording = 0;
int xrecord_set_by_keys = 0;
int xrecord_set_by_mouse = 0;
Window xrecord_focus_window = None;
Window xrecord_wm_window = None;
Window xrecord_ptr_window = None;
KeySym xrecord_keysym = NoSymbol;
#define NAMEINFO 2048
char xrecord_name_info[NAMEINFO];
#define SCR_ATTR_CACHE 8
winattr_t scr_attr_cache[SCR_ATTR_CACHE];
static double attr_cache_max_age = 1.5;
Display *rdpy_data = NULL; /* Data connection for RECORD */
Display *rdpy_ctrl = NULL; /* Control connection for RECORD */
Display *gdpy_ctrl = NULL;
Display *gdpy_data = NULL;
int xserver_grabbed = 0;
int trap_record_xerror(Display *, XErrorEvent *);
void initialize_xrecord(void);
void zerodisp_xrecord(void);
void shutdown_xrecord(void);
int xrecord_skip_keysym(rfbKeySym keysym);
int xrecord_skip_button(int new, int old);
int xrecord_scroll_keysym(rfbKeySym keysym);
void check_xrecord_reset(int force);
void xrecord_watch(int start, int setby);
#if LIBVNCSERVER_HAVE_RECORD
static XRecordRange *rr_CA = NULL;
static XRecordRange *rr_CW = NULL;
static XRecordRange *rr_GS = NULL;
static XRecordRange *rr_scroll[10];
static XRecordContext rc_scroll;
static XRecordClientSpec rcs_scroll;
static XRecordRange *rr_grab[10];
static XRecordContext rc_grab;
static XRecordClientSpec rcs_grab;
#endif
static XErrorEvent *trapped_record_xerror_event;
static void xrecord_grabserver(int start);
static int xrecord_vi_scroll_keysym(rfbKeySym keysym);
static int xrecord_emacs_scroll_keysym(rfbKeySym keysym);
static int lookup_attr_cache(Window win, int *cache_index, int *next_index);
#if LIBVNCSERVER_HAVE_RECORD
static void record_CA(XPointer ptr, XRecordInterceptData *rec_data);
static void record_CW(XPointer ptr, XRecordInterceptData *rec_data);
static void record_switch(XPointer ptr, XRecordInterceptData *rec_data);
static void record_grab(XPointer ptr, XRecordInterceptData *rec_data);
static void shutdown_record_context(XRecordContext rc, int bequiet, int reopen);
#endif
static void check_xrecord_grabserver(void);
int trap_record_xerror(Display *d, XErrorEvent *error) {
trapped_record_xerror = 1;
trapped_record_xerror_event = error;
if (d) {} /* unused vars warning: */
return 0;
}
static void xrecord_grabserver(int start) {
XErrorHandler old_handler = NULL;
int rc = 0;
if (debug_grabs) {
fprintf(stderr, "xrecord_grabserver%d/%d %.5f\n",
xserver_grabbed, start, dnowx());
}
if (! gdpy_ctrl || ! gdpy_data) {
return;
}
#if LIBVNCSERVER_HAVE_RECORD
if (!start) {
if (! rc_grab) {
return;
}
XRecordDisableContext(gdpy_ctrl, rc_grab);
XRecordFreeContext(gdpy_ctrl, rc_grab);
XFlush_wr(gdpy_ctrl);
rc_grab = 0;
return;
}
xserver_grabbed = 0;
rr_grab[0] = rr_GS;
rcs_grab = XRecordAllClients;
rc_grab = XRecordCreateContext(gdpy_ctrl, 0, &rcs_grab, 1, rr_grab, 1);
trapped_record_xerror = 0;
old_handler = XSetErrorHandler(trap_record_xerror);
XSync(gdpy_ctrl, True);
if (! rc_grab || trapped_record_xerror) {
XCloseDisplay_wr(gdpy_ctrl);
XCloseDisplay_wr(gdpy_data);
gdpy_ctrl = NULL;
gdpy_data = NULL;
XSetErrorHandler(old_handler);
return;
}
rc = XRecordEnableContextAsync(gdpy_data, rc_grab, record_grab, NULL);
if (!rc || trapped_record_xerror) {
XCloseDisplay_wr(gdpy_ctrl);
XCloseDisplay_wr(gdpy_data);
gdpy_ctrl = NULL;
gdpy_data = NULL;
XSetErrorHandler(old_handler);
return;
}
XSetErrorHandler(old_handler);
XFlush_wr(gdpy_data);
#else
if (!rc || !old_handler) {}
#endif
if (debug_grabs) {
fprintf(stderr, "xrecord_grabserver-done: %.5f\n", dnowx());
}
}
void zerodisp_xrecord(void) {
rdpy_data = NULL;
rdpy_ctrl = NULL;
gdpy_data = NULL;
gdpy_ctrl = NULL;
}
void initialize_xrecord(void) {
use_xrecord = 0;
if (! xrecord_present) {
return;
}
if (nofb) {
return;
}
if (noxrecord) {
return;
}
RAWFB_RET_VOID
#if LIBVNCSERVER_HAVE_RECORD
if (rr_CA) XFree_wr(rr_CA);
if (rr_CW) XFree_wr(rr_CW);
if (rr_GS) XFree_wr(rr_GS);
rr_CA = XRecordAllocRange();
rr_CW = XRecordAllocRange();
rr_GS = XRecordAllocRange();
if (!rr_CA || !rr_CW || !rr_GS) {
return;
}
/* protocol request ranges: */
rr_CA->core_requests.first = X_CopyArea;
rr_CA->core_requests.last = X_CopyArea;
rr_CW->core_requests.first = X_ConfigureWindow;
rr_CW->core_requests.last = X_ConfigureWindow;
rr_GS->core_requests.first = X_GrabServer;
rr_GS->core_requests.last = X_UngrabServer;
X_LOCK;
/* open a 2nd control connection to DISPLAY: */
if (rdpy_data) {
XCloseDisplay_wr(rdpy_data);
rdpy_data = NULL;
}
if (rdpy_ctrl) {
XCloseDisplay_wr(rdpy_ctrl);
rdpy_ctrl = NULL;
}
rdpy_ctrl = XOpenDisplay_wr(DisplayString(dpy));
if (!rdpy_ctrl) {
fprintf(stderr, "rdpy_ctrl open failed: %s / %s / %s / %s\n", getenv("DISPLAY"), DisplayString(dpy), getenv("XAUTHORITY"), getenv("XAUTHORIT_"));
}
XSync(dpy, True);
XSync(rdpy_ctrl, True);
/* open datalink connection to DISPLAY: */
rdpy_data = XOpenDisplay_wr(DisplayString(dpy));
if (!rdpy_data) {
fprintf(stderr, "rdpy_data open failed\n");
}
if (!rdpy_ctrl || ! rdpy_data) {
X_UNLOCK;
return;
}
disable_grabserver(rdpy_ctrl, 0);
disable_grabserver(rdpy_data, 0);
use_xrecord = 1;
/*
* now set up the GrabServer watcher. We get GrabServer
* deadlock in XRecordCreateContext() even with XTestGrabServer
* in place, why? Not sure, so we manually watch for grabs...
*/
if (gdpy_data) {
XCloseDisplay_wr(gdpy_data);
gdpy_data = NULL;
}
if (gdpy_ctrl) {
XCloseDisplay_wr(gdpy_ctrl);
gdpy_ctrl = NULL;
}
xserver_grabbed = 0;
gdpy_ctrl = XOpenDisplay_wr(DisplayString(dpy));
if (!gdpy_ctrl) {
fprintf(stderr, "gdpy_ctrl open failed\n");
}
XSync(dpy, True);
XSync(gdpy_ctrl, True);
gdpy_data = XOpenDisplay_wr(DisplayString(dpy));
if (!gdpy_data) {
fprintf(stderr, "gdpy_data open failed\n");
}
if (gdpy_ctrl && gdpy_data) {
disable_grabserver(gdpy_ctrl, 0);
disable_grabserver(gdpy_data, 0);
xrecord_grabserver(1);
}
X_UNLOCK;
#endif
}
void shutdown_xrecord(void) {
#if LIBVNCSERVER_HAVE_RECORD
if (debug_grabs) {
fprintf(stderr, "shutdown_xrecord%d %.5f\n",
xserver_grabbed, dnowx());
}
if (rr_CA) XFree_wr(rr_CA);
if (rr_CW) XFree_wr(rr_CW);
if (rr_GS) XFree_wr(rr_GS);
rr_CA = NULL;
rr_CW = NULL;
rr_GS = NULL;
X_LOCK;
if (rdpy_ctrl && rc_scroll) {
XRecordDisableContext(rdpy_ctrl, rc_scroll);
XRecordFreeContext(rdpy_ctrl, rc_scroll);
XSync(rdpy_ctrl, False);
rc_scroll = 0;
}
if (gdpy_ctrl && rc_grab) {
XRecordDisableContext(gdpy_ctrl, rc_grab);
XRecordFreeContext(gdpy_ctrl, rc_grab);
XSync(gdpy_ctrl, False);
rc_grab = 0;
}
if (rdpy_data) {
XCloseDisplay_wr(rdpy_data);
rdpy_data = NULL;
}
if (rdpy_ctrl) {
XCloseDisplay_wr(rdpy_ctrl);
rdpy_ctrl = NULL;
}
if (gdpy_data) {
XCloseDisplay_wr(gdpy_data);
gdpy_data = NULL;
}
if (gdpy_ctrl) {
XCloseDisplay_wr(gdpy_ctrl);
gdpy_ctrl = NULL;
}
xserver_grabbed = 0;
X_UNLOCK;
#endif
use_xrecord = 0;
if (debug_grabs) {
fprintf(stderr, "shutdown_xrecord-done: %.5f\n", dnowx());
}
}
int xrecord_skip_keysym(rfbKeySym keysym) {
KeySym sym = (KeySym) keysym;
int ok = -1, matched = 0;
if (scroll_key_list) {
int k, exclude = 0;
if (scroll_key_list[0]) {
exclude = 1;
}
k = 1;
while (scroll_key_list[k] != NoSymbol) {
if (scroll_key_list[k++] == sym) {
matched = 1;
break;
}
}
if (exclude) {
if (matched) {
return 1;
} else {
ok = 1;
}
} else {
if (matched) {
ok = 1;
} else {
ok = 0;
}
}
}
if (ok == 1) {
return 0;
} else if (ok == 0) {
return 1;
}
/* apply various heuristics: */
if (IsModifierKey(sym)) {
/* Shift, Control, etc, usu. generate no scrolls */
return 1;
}
if (sym == XK_space && scroll_term) {
/* space in a terminal is usu. full page... */
Window win;
static Window prev_top = None;
int size = 256;
static char name[256];
X_LOCK;
win = query_pointer(rootwin);
X_UNLOCK;
if (win != None && win != rootwin) {
if (prev_top != None && win == prev_top) {
; /* use cached result */
} else {
prev_top = win;
X_LOCK;
win = descend_pointer(6, win, name, size);
X_UNLOCK;
}
if (match_str_list(name, scroll_term)) {
return 1;
}
}
}
/* TBD use typing_rate() so */
return 0;
}
int xrecord_skip_button(int new, int old) {
/* unused vars warning: */
if (new || old) {}
return 0;
}
static int xrecord_vi_scroll_keysym(rfbKeySym keysym) {
KeySym sym = (KeySym) keysym;
if (sym == XK_J || sym == XK_j || sym == XK_K || sym == XK_k) {
return 1; /* vi */
}
if (sym == XK_D || sym == XK_d || sym == XK_U || sym == XK_u) {
return 1; /* Ctrl-d/u */
}
if (sym == XK_Z || sym == XK_z) {
return 1; /* zz, zt, zb .. */
}
return 0;
}
static int xrecord_emacs_scroll_keysym(rfbKeySym keysym) {
KeySym sym = (KeySym) keysym;
if (sym == XK_N || sym == XK_n || sym == XK_P || sym == XK_p) {
return 1; /* emacs */
}
/* Must be some more ... */
return 0;
}
int xrecord_scroll_keysym(rfbKeySym keysym) {
KeySym sym = (KeySym) keysym;
/* X11/keysymdef.h */
if (sym == XK_Return || sym == XK_KP_Enter || sym == XK_Linefeed) {
return 1; /* Enter */
}
if (sym==XK_Up || sym==XK_KP_Up || sym==XK_Down || sym==XK_KP_Down) {
return 1; /* U/D arrows */
}
if (sym == XK_Left || sym == XK_KP_Left || sym == XK_Right ||
sym == XK_KP_Right) {
return 1; /* L/R arrows */
}
if (xrecord_vi_scroll_keysym(keysym)) {
return 1;
}
if (xrecord_emacs_scroll_keysym(keysym)) {
return 1;
}
return 0;
}
static int lookup_attr_cache(Window win, int *cache_index, int *next_index) {
double now, t, oldest = 0.0;
int i, old_index = -1, count = 0;
Window cwin;
*cache_index = -1;
*next_index = -1;
if (win == None) {
return 0;
}
if (attr_cache_max_age == 0.0) {
return 0;
}
dtime0(&now);
for (i=0; i < SCR_ATTR_CACHE; i++) {
cwin = scr_attr_cache[i].win;
t = scr_attr_cache[i].time;
if (now > t + attr_cache_max_age) {
/* expire it even if it is the one we want */
scr_attr_cache[i].win = cwin = None;
scr_attr_cache[i].fetched = 0;
scr_attr_cache[i].valid = 0;
}
if (*next_index == -1 && cwin == None) {
*next_index = i;
}
if (*next_index == -1) {
/* record oldest */
if (old_index == -1 || t < oldest) {
oldest = t;
old_index = i;
}
}
if (cwin != None) {
count++;
}
if (cwin == win) {
if (*cache_index == -1) {
*cache_index = i;
} else {
/* remove dups */
scr_attr_cache[i].win = None;
scr_attr_cache[i].fetched = 0;
scr_attr_cache[i].valid = 0;
}
}
}
if (*next_index == -1) {
*next_index = old_index;
}
if (0) fprintf(stderr, "lookup_attr_cache count: %d\n", count);
if (*cache_index != -1) {
return 1;
} else {
return 0;
}
}
static XID xrecord_seq = 0;
static double xrecord_start = 0.0;
#if LIBVNCSERVER_HAVE_RECORD
static void record_CA(XPointer ptr, XRecordInterceptData *rec_data) {
xCopyAreaReq *req;
Window src = None, dst = None, c;
XWindowAttributes attr;
int src_x, src_y, dst_x, dst_y, rx, ry;
int good = 1, dx, dy, k=0, i;
unsigned int w, h;
int dba = 0, db = debug_scroll;
int cache_index, next_index, valid;
if (dba || db) {
if (rec_data->category == XRecordFromClient) {
req = (xCopyAreaReq *) rec_data->data;
if (req->reqType == X_CopyArea) {
src = req->srcDrawable;
dst = req->dstDrawable;
}
}
}
if (dba || db > 1) fprintf(stderr, "record_CA-%d id_base: 0x%lx ptr: 0x%lx "
"seq: 0x%lx rc: 0x%lx cat: %d swapped: %d 0x%lx/0x%lx\n", k++,
rec_data->id_base, (unsigned long) ptr, xrecord_seq, rc_scroll,
rec_data->category, rec_data->client_swapped, src, dst);
if (! xrecording) {
return;
}
if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
if (rec_data->id_base == 0) {
return;
}
if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
if ((XID) ptr != xrecord_seq) {
return;
}
if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
if (rec_data->category != XRecordFromClient) {
return;
}
if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
req = (xCopyAreaReq *) rec_data->data;
if (req->reqType != X_CopyArea) {
return;
}
if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
/*
xterm, gnome-terminal, others.
Note we miss the X_ImageText8 that clears the block cursor. So there is a
short period of time with a painting error: two cursors, one above the other.
X_ImageText8
draw: 0x8c00017 nChars: 1, gc: 0x8c00013, x: 101, y: 585, chars=' '
X_ClearArea
window: 0x8c00018, x: 2, y: 217, w: 10, h: 5
X_FillPoly
draw: 0x8c00018 gc: 0x8c0000a, shape: 0, coordMode: 0,
X_FillPoly
draw: 0x8c00018 gc: 0x8c0000b, shape: 0, coordMode: 0,
X_CopyArea
src: 0x8c00017, dst: 0x8c00017, gc: 0x8c00013, srcX: 17, srcY: 15, dstX: 17, dstY: 2, w: 480, h: 572
X_ChangeWindowAttributes
X_ClearArea
window: 0x8c00017, x: 17, y: 574, w: 480, h: 13
X_ChangeWindowAttributes
*/
src = req->srcDrawable;
dst = req->dstDrawable;
src_x = req->srcX;
src_y = req->srcY;
dst_x = req->dstX;
dst_y = req->dstY;
w = req->width;
h = req->height;
if (w*h < (unsigned int) scrollcopyrect_min_area) {
good = 0;
} else if (!src || !dst) {
good = 0;
} else if (src != dst) {
good = 0;
} else if (scr_ev_cnt >= SCR_EV_MAX) {
good = 0;
}
dx = dst_x - src_x;
dy = dst_y - src_y;
if (dx != 0 && dy != 0) {
good = 0;
}
if (! good) {
return;
}
if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
/*
* after all of the above succeeds, now contact X server.
* we try to get away with some caching here.
*/
if (lookup_attr_cache(src, &cache_index, &next_index)) {
i = cache_index;
attr.x = scr_attr_cache[i].x;
attr.y = scr_attr_cache[i].y;
attr.width = scr_attr_cache[i].width;
attr.height = scr_attr_cache[i].height;
attr.map_state = scr_attr_cache[i].map_state;
rx = scr_attr_cache[i].rx;
ry = scr_attr_cache[i].ry;
valid = scr_attr_cache[i].valid;
} else {
valid = valid_window(src, &attr, 1);
if (valid) {
if (!xtranslate(src, rootwin, 0, 0, &rx, &ry, &c, 1)) {
valid = 0;
}
}
if (next_index >= 0) {
i = next_index;
scr_attr_cache[i].win = src;
scr_attr_cache[i].fetched = 1;
scr_attr_cache[i].valid = valid;
scr_attr_cache[i].time = dnow();
if (valid) {
scr_attr_cache[i].x = attr.x;
scr_attr_cache[i].y = attr.y;
scr_attr_cache[i].width = attr.width;
scr_attr_cache[i].height = attr.height;
scr_attr_cache[i].border_width = attr.border_width;
scr_attr_cache[i].depth = attr.depth;
scr_attr_cache[i].class = attr.class;
scr_attr_cache[i].backing_store =
attr.backing_store;
scr_attr_cache[i].map_state = attr.map_state;
scr_attr_cache[i].rx = rx;
scr_attr_cache[i].ry = ry;
}
}
}
if (! valid) {
return;
}
if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
if (attr.map_state != IsViewable) {
return;
}
if (0 || dba || db) {
double st, dt;
st = (double) rec_data->server_time/1000.0;
dt = (dnow() - servertime_diff) - st;
fprintf(stderr, "record_CA-%d *FOUND_SCROLL: src: 0x%lx dx: %d dy: %d "
"x: %d y: %d w: %d h: %d st: %.4f %.4f %.4f\n", k++, src, dx, dy,
src_x, src_y, w, h, st, dt, dnowx());
}
i = scr_ev_cnt;
scr_ev[i].win = src;
scr_ev[i].frame = None;
scr_ev[i].dx = dx;
scr_ev[i].dy = dy;
scr_ev[i].x = rx + dst_x;
scr_ev[i].y = ry + dst_y;
scr_ev[i].w = w;
scr_ev[i].h = h;
scr_ev[i].t = ((double) rec_data->server_time)/1000.0;
scr_ev[i].win_x = rx;
scr_ev[i].win_y = ry;
scr_ev[i].win_w = attr.width;
scr_ev[i].win_h = attr.height;
scr_ev[i].new_x = 0;
scr_ev[i].new_y = 0;
scr_ev[i].new_w = 0;
scr_ev[i].new_h = 0;
if (dx == 0) {
if (dy > 0) {
scr_ev[i].new_x = rx + src_x;
scr_ev[i].new_y = ry + src_y;
scr_ev[i].new_w = w;
scr_ev[i].new_h = dy;
} else {
scr_ev[i].new_x = rx + src_x;
scr_ev[i].new_y = ry + dst_y + h;
scr_ev[i].new_w = w;
scr_ev[i].new_h = -dy;
}
} else if (dy == 0) {
if (dx > 0) {
scr_ev[i].new_x = rx + src_x;
scr_ev[i].new_y = rx + src_y;
scr_ev[i].new_w = dx;
scr_ev[i].new_h = h;
} else {
scr_ev[i].new_x = rx + dst_x + w;
scr_ev[i].new_y = ry + src_y;
scr_ev[i].new_w = -dx;
scr_ev[i].new_h = h;
}
}
scr_ev_cnt++;
}
typedef struct cw_event {
Window win;
int x, y, w, h;
} cw_event_t;
#define MAX_CW 128
static cw_event_t cw_events[MAX_CW];
static void record_CW(XPointer ptr, XRecordInterceptData *rec_data) {
xConfigureWindowReq *req;
Window win = None, c;
Window src = None, dst = None;
XWindowAttributes attr;
int absent = 0x100000;
int src_x, src_y, dst_x, dst_y, rx, ry;
int good = 1, dx, dy, k=0, i, j, match, list[3];
int f_x, f_y, f_w, f_h;
int x, y, w, h;
int x0, y0, w0, h0, x1, y1, w1, h1, x2, y2, w2, h2;
static int index = 0;
unsigned int vals[4];
unsigned tmask;
char *data;
int dba = 0, db = debug_scroll;
int cache_index, next_index, valid;
if (db) {
if (rec_data->category == XRecordFromClient) {
req = (xConfigureWindowReq *) rec_data->data;
if (req->reqType == X_ConfigureWindow) {
src = req->window;
}
}
}
if (dba || db > 1) fprintf(stderr, "record_CW-%d id_base: 0x%lx ptr: 0x%lx "
"seq: 0x%lx rc: 0x%lx cat: %d swapped: %d 0x%lx/0x%lx\n", k++,
rec_data->id_base, (unsigned long) ptr, xrecord_seq, rc_scroll,
rec_data->category, rec_data->client_swapped, src, dst);
if (! xrecording) {
return;
}
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
if ((XID) ptr != xrecord_seq) {
return;
}
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
if (rec_data->id_base == 0) {
return;
}
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
if (rec_data->category == XRecordStartOfData) {
index = 0;
return;
}
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
if (rec_data->category != XRecordFromClient) {
return;
}
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
if (rec_data->client_swapped) {
return;
}
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
req = (xConfigureWindowReq *) rec_data->data;
if (req->reqType != X_ConfigureWindow) {
return;
}
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
tmask = req->mask;
tmask &= ~CWX;
tmask &= ~CWY;
tmask &= ~CWWidth;
tmask &= ~CWHeight;
if (tmask) {
/* require no more than these 4 flags */
return;
}
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
f_x = req->mask & CWX;
f_y = req->mask & CWY;
f_w = req->mask & CWWidth;
f_h = req->mask & CWHeight;
if (! f_x || ! f_y) {
if (f_w && f_h) {
; /* netscape 4.x style */
} else {
return;
}
}
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
if ( (f_w && !f_h) || (!f_w && f_h) ) {
return;
}
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
for (i=0; i<4; i++) {
vals[i] = 0;
}
data = (char *)req;
data += sz_xConfigureWindowReq;
for (i=0; i<req->length; i++) {
unsigned int v;
/*
* We use unsigned int for the values. There were
* some crashes on 64bit machines with unsigned longs.
* Need to check that X protocol sends 32bit values.
*/
v = *( (unsigned int *) data);
if (db > 1) fprintf(stderr, " vals[%d] 0x%x/%d\n", i, v, v);
vals[i] = v;
data += sizeof(unsigned int);
}
if (index >= MAX_CW) {
int i, j;
/* FIXME, circular, etc. */
for (i=0; i<2; i++) {
j = MAX_CW - 2 + i;
cw_events[i].win = cw_events[j].win;
cw_events[i].x = cw_events[j].x;
cw_events[i].y = cw_events[j].y;
cw_events[i].w = cw_events[j].w;
cw_events[i].h = cw_events[j].h;
}
index = 2;
}
if (! f_x && ! f_y) {
/* netscape 4.x style CWWidth,CWHeight */
vals[2] = vals[0];
vals[3] = vals[1];
vals[0] = 0;
vals[1] = 0;
}
cw_events[index].win = req->window;
if (! f_x) {
cw_events[index].x = absent;
} else {
cw_events[index].x = (int) vals[0];
}
if (! f_y) {
cw_events[index].y = absent;
} else {
cw_events[index].y = (int) vals[1];
}
if (! f_w) {
cw_events[index].w = absent;
} else {
cw_events[index].w = (int) vals[2];
}
if (! f_h) {
cw_events[index].h = absent;
} else {
cw_events[index].h = (int) vals[3];
}
x = cw_events[index].x;
y = cw_events[index].y;
w = cw_events[index].w;
h = cw_events[index].h;
win = cw_events[index].win;
if (dba || db) fprintf(stderr, " record_CW ind: %d win: 0x%lx x: %d y: %d w: %d h: %d\n",
index, win, x, y, w, h);
index++;
if (index < 3) {
good = 0;
} else if (w != absent && h != absent &&
w*h < scrollcopyrect_min_area) {
good = 0;
}
if (! good) {
return;
}
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
match = 0;
for (j=index - 1; j >= 0; j--) {
if (cw_events[j].win == win) {
list[match++] = j;
}
if (match >= 3) {
break;
}
}
if (match != 3) {
return;
}
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
/*
Mozilla:
Up arrow: window moves down a bit (dy > 0):
X_ConfigureWindow
length: 7, window: 0x2e000cd, mask: 0xf, v0 0, v1 -18, v2 760, v3 906, v4 327692, v5 48234701, v6 3,
CW-mask: CWX,CWY,CWWidth,CWHeight,
X_ConfigureWindow
length: 5, window: 0x2e000cd, mask: 0x3, v0 0, v1 0, v2 506636, v3 48234701, v4 48234511,
CW-mask: CWX,CWY,
X_ConfigureWindow
length: 7, window: 0x2e000cd, mask: 0xf, v0 0, v1 0, v2 760, v3 888, v4 65579, v5 0, v6 108009,
CW-mask: CWX,CWY,CWWidth,CWHeight,
Down arrow: window moves up a bit (dy < 0):
X_ConfigureWindow
length: 7, window: 0x2e000cd, mask: 0xf, v0 0, v1 0, v2 760, v3 906, v4 327692, v5 48234701, v6 262147,
CW-mask: CWX,CWY,CWWidth,CWHeight,
X_ConfigureWindow
length: 5, window: 0x2e000cd, mask: 0x3, v0 0, v1 -18, v2 506636, v3 48234701, v4 48234511,
CW-mask: CWX,CWY,
X_ConfigureWindow
length: 7, window: 0x2e000cd, mask: 0xf, v0 0, v1 0, v2 760, v3 888, v4 96555, v5 48265642, v6 48265262,
CW-mask: CWX,CWY,CWWidth,CWHeight,
Netscape 4.x
Up arrow:
71.76142 0.01984 X_ConfigureWindow
length: 7, window: 0x9800488, mask: 0xf, v0 0, v1 -15, v2 785, v3 882, v4 327692, v5 159384712, v6 1769484,
CW-mask: CWX,CWY,CWWidth,CWHeight,
71.76153 0.00011 X_ConfigureWindow
length: 5, window: 0x9800488, mask: 0xc, v0 785, v1 867, v2 329228, v3 159384712, v4 159383555,
CW-mask: CWWidth,CWHeight,
XXX,XXX
71.76157 0.00003 X_ConfigureWindow
length: 5, window: 0x9800488, mask: 0x3, v0 0, v1 0, v2 131132, v3 159385313, v4 328759,
CW-mask: CWX,CWY,
XXX,XXX
Down arrow:
72.93147 0.01990 X_ConfigureWindow
length: 5, window: 0x9800488, mask: 0xc, v0 785, v1 882, v2 328972, v3 159384712, v4 159383555,
CW-mask: CWWidth,CWHeight,
XXX,XXX
72.93156 0.00009 X_ConfigureWindow
length: 5, window: 0x9800488, mask: 0x3, v0 0, v1 -15, v2 458764, v3 159384712, v4 159383567,
CW-mask: CWX,CWY,
72.93160 0.00004 X_ConfigureWindow
length: 7, window: 0x9800488, mask: 0xf, v0 0, v1 0, v2 785, v3 867, v4 131132, v5 159385335, v6 328759,
CW-mask: CWX,CWY,CWWidth,CWHeight,
sadly, probably need to handle some more...
*/
x0 = cw_events[list[2]].x;
y0 = cw_events[list[2]].y;
w0 = cw_events[list[2]].w;
h0 = cw_events[list[2]].h;
x1 = cw_events[list[1]].x;
y1 = cw_events[list[1]].y;
w1 = cw_events[list[1]].w;
h1 = cw_events[list[1]].h;
x2 = cw_events[list[0]].x;
y2 = cw_events[list[0]].y;
w2 = cw_events[list[0]].w;
h2 = cw_events[list[0]].h;
/* see NS4 XXX's above: */
if (w2 == absent || h2 == absent) {
/* up arrow */
if (w2 == absent) {
w2 = w1;
}
if (h2 == absent) {
h2 = h1;
}
}
if (x1 == absent || y1 == absent) {
/* up arrow */
if (x1 == absent) {
x1 = x2;
}
if (y1 == absent) {
y1 = y2;
}
}
if (x0 == absent || y0 == absent) {
/* down arrow */
if (x0 == absent) {
/* hmmm... what to do */
x0 = x2;
}
if (y0 == absent) {
y0 = y2;
}
}
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);
dy = y1 - y0;
dx = x1 - x0;
src_x = x2;
src_y = y2;
w = w2;
h = h2;
/* check w and h before we modify them */
if (w <= 0 || h <= 0) {
good = 0;
} else if (w == absent || h == absent) {
good = 0;
}
if (! good) {
return;
}
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
if (dy > 0) {
h -= dy;
} else {
h += dy;
src_y -= dy;
}
if (dx > 0) {
w -= dx;
} else {
w += dx;
src_x -= dx;
}
dst_x = src_x + dx;
dst_y = src_y + dy;
if (x0 == absent || x1 == absent || x2 == absent) {
good = 0;
} else if (y0 == absent || y1 == absent || y2 == absent) {
good = 0;
} else if (dx != 0 && dy != 0) {
good = 0;
} else if (w0 - w2 != nabs(dx)) {
good = 0;
} else if (h0 - h2 != nabs(dy)) {
good = 0;
} else if (scr_ev_cnt >= SCR_EV_MAX) {
good = 0;
}
if (! good) {
return;
}
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
/*
* geometry OK.
* after all of the above succeeds, now contact X server.
*/
if (lookup_attr_cache(win, &cache_index, &next_index)) {
i = cache_index;
attr.x = scr_attr_cache[i].x;
attr.y = scr_attr_cache[i].y;
attr.width = scr_attr_cache[i].width;
attr.height = scr_attr_cache[i].height;
attr.map_state = scr_attr_cache[i].map_state;
rx = scr_attr_cache[i].rx;
ry = scr_attr_cache[i].ry;
valid = scr_attr_cache[i].valid;
if (0) fprintf(stderr, "lookup_attr_cache hit: %2d %2d 0x%lx %d\n",
cache_index, next_index, win, valid);
} else {
valid = valid_window(win, &attr, 1);
if (0) fprintf(stderr, "lookup_attr_cache MISS: %2d %2d 0x%lx %d\n",
cache_index, next_index, win, valid);
if (valid) {
if (!xtranslate(win, rootwin, 0, 0, &rx, &ry, &c, 1)) {
valid = 0;
}
}
if (next_index >= 0) {
i = next_index;
scr_attr_cache[i].win = win;
scr_attr_cache[i].fetched = 1;
scr_attr_cache[i].valid = valid;
scr_attr_cache[i].time = dnow();
if (valid) {
scr_attr_cache[i].x = attr.x;
scr_attr_cache[i].y = attr.y;
scr_attr_cache[i].width = attr.width;
scr_attr_cache[i].height = attr.height;
scr_attr_cache[i].depth = attr.depth;
scr_attr_cache[i].class = attr.class;
scr_attr_cache[i].backing_store =
attr.backing_store;
scr_attr_cache[i].map_state = attr.map_state;
scr_attr_cache[i].rx = rx;
scr_attr_cache[i].ry = ry;
}
}
}
if (! valid) {
return;
}
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
if (attr.map_state != IsViewable) {
return;
}
if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
if (0 || dba || db) {
double st, dt;
st = (double) rec_data->server_time/1000.0;
dt = (dnow() - servertime_diff) - st;
fprintf(stderr, "record_CW-%d *FOUND_SCROLL: win: 0x%lx dx: %d dy: %d "
"x: %d y: %d w: %d h: %d st: %.4f dt: %.4f %.4f\n", k++, win,
dx, dy, src_x, src_y, w, h, st, dt, dnowx());
}
i = scr_ev_cnt;
scr_ev[i].win = win;
scr_ev[i].frame = None;
scr_ev[i].dx = dx;
scr_ev[i].dy = dy;
scr_ev[i].x = rx + dst_x;
scr_ev[i].y = ry + dst_y;
scr_ev[i].w = w;
scr_ev[i].h = h;
scr_ev[i].t = ((double) rec_data->server_time)/1000.0;
scr_ev[i].win_x = rx;
scr_ev[i].win_y = ry;
scr_ev[i].win_w = attr.width;
scr_ev[i].win_h = attr.height;
scr_ev[i].new_x = 0;
scr_ev[i].new_y = 0;
scr_ev[i].new_w = 0;
scr_ev[i].new_h = 0;
if (dx == 0) {
if (dy > 0) {
scr_ev[i].new_x = rx + src_x;
scr_ev[i].new_y = ry + src_y;
scr_ev[i].new_w = w;
scr_ev[i].new_h = dy;
} else {
scr_ev[i].new_x = rx + src_x;
scr_ev[i].new_y = ry + dst_y + h;
scr_ev[i].new_w = w;
scr_ev[i].new_h = -dy;
}
} else if (dy == 0) {
if (dx > 0) {
scr_ev[i].new_x = rx + src_x;
scr_ev[i].new_y = rx + src_y;
scr_ev[i].new_w = dx;
scr_ev[i].new_h = h;
} else {
scr_ev[i].new_x = rx + dst_x + w;
scr_ev[i].new_y = ry + src_y;
scr_ev[i].new_w = -dx;
scr_ev[i].new_h = h;
}
}
/* indicate we have a new one */
scr_ev_cnt++;
index = 0;
}
static void record_switch(XPointer ptr, XRecordInterceptData *rec_data) {
static int first = 1;
xReq *req;
if (first) {
int i;
for (i=0; i<SCR_ATTR_CACHE; i++) {
scr_attr_cache[i].win = None;
scr_attr_cache[i].fetched = 0;
scr_attr_cache[i].valid = 0;
scr_attr_cache[i].time = 0.0;
}
first = 0;
}
/* should handle control msgs, start/stop/etc */
if (rec_data->category == XRecordStartOfData) {
record_CW(ptr, rec_data);
} else if (rec_data->category == XRecordEndOfData) {
;
} else if (rec_data->category == XRecordClientStarted) {
;
} else if (rec_data->category == XRecordClientDied) {
;
} else if (rec_data->category == XRecordFromServer) {
;
}
if (rec_data->category != XRecordFromClient) {
XRecordFreeData(rec_data);
return;
}
req = (xReq *) rec_data->data;
if (req->reqType == X_CopyArea) {
record_CA(ptr, rec_data);
} else if (req->reqType == X_ConfigureWindow) {
record_CW(ptr, rec_data);
} else {
;
}
XRecordFreeData(rec_data);
}
static void record_grab(XPointer ptr, XRecordInterceptData *rec_data) {
xReq *req;
int db = 0;
if (debug_grabs) db = 1;
/* should handle control msgs, start/stop/etc */
if (rec_data->category == XRecordStartOfData) {
;
} else if (rec_data->category == XRecordEndOfData) {
;
} else if (rec_data->category == XRecordClientStarted) {
;
} else if (rec_data->category == XRecordClientDied) {
;
} else if (rec_data->category == XRecordFromServer) {
;
}
if (rec_data->category != XRecordFromClient) {
XRecordFreeData(rec_data);
return;
}
req = (xReq *) rec_data->data;
if (req->reqType == X_GrabServer) {
double now = dnowx();
xserver_grabbed++;
if (db) rfbLog("X server Grabbed: %d %.5f\n", xserver_grabbed, now);
if (xserver_grabbed > 1) {
/*
* some apps do multiple grabs... very unlikely
* two apps will be doing it at same time.
*/
xserver_grabbed = 1;
}
} else if (req->reqType == X_UngrabServer) {
double now = dnowx();
xserver_grabbed--;
if (xserver_grabbed < 0) {
xserver_grabbed = 0;
}
if (db) rfbLog("X server Un-Grabbed: %d %.5f\n", xserver_grabbed, now);
} else {
;
}
XRecordFreeData(rec_data);
/* unused vars warning: */
if (ptr) {}
}
#endif
static void check_xrecord_grabserver(void) {
#if LIBVNCSERVER_HAVE_RECORD
int last_val, cnt = 0, i, max = 10;
double d;
if (!gdpy_ctrl || !gdpy_data) {
return;
}
if (unixpw_in_progress) return;
dtime0(&d);
XFlush_wr(gdpy_ctrl);
for (i=0; i<max; i++) {
last_val = xserver_grabbed;
XRecordProcessReplies(gdpy_data);
if (xserver_grabbed != last_val) {
cnt++;
} else if (i > 2) {
break;
}
}
if (cnt) {
XFlush_wr(gdpy_ctrl);
}
if (debug_grabs && cnt > 0) {
d = dtime(&d);
fprintf(stderr, "check_xrecord_grabserver: cnt=%d i=%d %.4f\n", cnt, i, d);
}
#endif
}
#if LIBVNCSERVER_HAVE_RECORD
static void shutdown_record_context(XRecordContext rc, int bequiet, int reopen) {
int ret1, ret2;
int verb = (!bequiet && !quiet);
RAWFB_RET_VOID
if (0 || debug_scroll) {
rfbLog("shutdown_record_context(0x%lx, %d, %d)\n", rc,
bequiet, reopen);
verb = 1;
}
ret1 = XRecordDisableContext(rdpy_ctrl, rc);
if (!ret1 && verb) {
rfbLog("XRecordDisableContext(0x%lx) failed.\n", rc);
}
ret2 = XRecordFreeContext(rdpy_ctrl, rc);
if (!ret2 && verb) {
rfbLog("XRecordFreeContext(0x%lx) failed.\n", rc);
}
XFlush_wr(rdpy_ctrl);
if (reopen == 2 && ret1 && ret2) {
reopen = 0; /* 2 means reopen only on failure */
}
if (reopen && gdpy_ctrl) {
check_xrecord_grabserver();
if (xserver_grabbed) {
rfbLog("shutdown_record_context: skip reopen,"
" server grabbed\n");
reopen = 0;
}
}
if (reopen) {
char *dpystr = DisplayString(dpy);
if (debug_scroll) {
rfbLog("closing RECORD data connection.\n");
}
XCloseDisplay_wr(rdpy_data);
rdpy_data = NULL;
if (debug_scroll) {
rfbLog("closing RECORD control connection.\n");
}
XCloseDisplay_wr(rdpy_ctrl);
rdpy_ctrl = NULL;
rdpy_ctrl = XOpenDisplay_wr(dpystr);
if (! rdpy_ctrl) {
rfbLog("Failed to reopen RECORD control connection:"
"%s\n", dpystr);
rfbLog(" disabling RECORD scroll detection.\n");
use_xrecord = 0;
return;
}
XSync(dpy, False);
disable_grabserver(rdpy_ctrl, 0);
XSync(rdpy_ctrl, True);
rdpy_data = XOpenDisplay_wr(dpystr);
if (! rdpy_data) {
rfbLog("Failed to reopen RECORD data connection:"
"%s\n", dpystr);
rfbLog(" disabling RECORD scroll detection.\n");
XCloseDisplay_wr(rdpy_ctrl);
rdpy_ctrl = NULL;
use_xrecord = 0;
return;
}
disable_grabserver(rdpy_data, 0);
if (debug_scroll || (! bequiet && reopen == 2)) {
rfbLog("reopened RECORD data and control display"
" connections: %s\n", dpystr);
}
}
}
#endif
void check_xrecord_reset(int force) {
static double last_reset = 0.0;
int reset_time = 60, require_idle = 10;
int reset_time2 = 600, require_idle2 = 40;
double now = 0.0;
XErrorHandler old_handler = NULL;
if (gdpy_ctrl) {
X_LOCK;
check_xrecord_grabserver();
X_UNLOCK;
} else {
/* more dicey if not watching grabserver */
reset_time = reset_time2;
require_idle = require_idle2;
}
if (!use_xrecord) {
return;
}
if (xrecording) {
return;
}
if (button_mask) {
return;
}
if (xserver_grabbed) {
return;
}
if (unixpw_in_progress) return;
#if LIBVNCSERVER_HAVE_RECORD
if (! rc_scroll) {
return;
}
now = dnow();
if (last_reset == 0.0) {
last_reset = now;
return;
}
/*
* try to wait for a break in input to reopen the displays
* this is only to avoid XGrabServer deadlock on the repopens.
*/
if (force) {
;
} else if (now < last_reset + reset_time) {
return;
} else if (now < last_pointer_click_time + require_idle) {
return;
} else if (now < last_keyboard_time + require_idle) {
return;
}
X_LOCK;
trapped_record_xerror = 0;
old_handler = XSetErrorHandler(trap_record_xerror);
/* unlikely, but check again since we will definitely be doing it. */
if (gdpy_ctrl) {
check_xrecord_grabserver();
if (xserver_grabbed) {
XSetErrorHandler(old_handler);
X_UNLOCK;
return;
}
}
shutdown_record_context(rc_scroll, 0, 1);
rc_scroll = 0;
XSetErrorHandler(old_handler);
X_UNLOCK;
last_reset = now;
#else
if (!old_handler || now == 0.0 || !last_reset || !force) {}
#endif
}
#define RECORD_ERROR_MSG \
if (! quiet) { \
rfbLog("trapped RECORD XError: %s %d/%d/%d (0x%lx)\n", \
xerror_string(trapped_record_xerror_event), \
(int) trapped_record_xerror_event->error_code, \
(int) trapped_record_xerror_event->request_code, \
(int) trapped_record_xerror_event->minor_code, \
(int) trapped_record_xerror_event->resourceid); \
}
void xrecord_watch(int start, int setby) {
#if LIBVNCSERVER_HAVE_RECORD
Window focus, wm, c, clast;
static double create_time = 0.0;
int rc;
int do_shutdown = 0;
int reopen_dpys = 1;
XErrorHandler old_handler = NULL;
static Window last_win = None, last_result = None;
#endif
int db = debug_scroll;
double now;
static double last_error = 0.0;
if (0) db = 1;
if (nofb) {
xrecording = 0;
return;
}
if (use_threads) {
/* XXX not working */
use_xrecord = 0;
xrecording = 0;
return;
}
dtime0(&now);
if (now < last_error + 0.5) {
return;
}
if (gdpy_ctrl) {
X_LOCK;
check_xrecord_grabserver();
X_UNLOCK;
if (xserver_grabbed) {
if (db || debug_grabs) fprintf(stderr, "xrecord_watch: %d/%d out xserver_grabbed\n", start, setby);
return;
}
}
#if LIBVNCSERVER_HAVE_RECORD
if (! start) {
int shut_reopen = 2, shut_time = 25;
if (db || debug_grabs) fprintf(stderr, "XRECORD OFF: %d/%d %.4f\n", xrecording, setby, now - x11vnc_start);
xrecording = 0;
if (! rc_scroll) {
xrecord_focus_window = None;
xrecord_wm_window = None;
xrecord_ptr_window = None;
xrecord_keysym = NoSymbol;
rcs_scroll = 0;
return;
}
if (! do_shutdown && now > create_time + shut_time) {
/* XXX unstable if we keep a RECORD going forever */
do_shutdown = 1;
}
SCR_LOCK;
if (do_shutdown) {
if (db > 1) fprintf(stderr, "=== shutdown-scroll 0x%lx\n", rc_scroll);
X_LOCK;
trapped_record_xerror = 0;
old_handler = XSetErrorHandler(trap_record_xerror);
shutdown_record_context(rc_scroll, 0, shut_reopen);
rc_scroll = 0;
/*
* n.b. there is a grabserver issue wrt
* XRecordCreateContext() even though rdpy_ctrl
* is set imprevious to grabs. Perhaps a bug
* in the X server or library...
*
* If there are further problems, a thought
* to recreate rc_scroll right after the
* reopen.
*/
if (! use_xrecord) {
XSetErrorHandler(old_handler);
X_UNLOCK;
SCR_UNLOCK;
return;
}
XRecordProcessReplies(rdpy_data);
if (trapped_record_xerror) {
RECORD_ERROR_MSG;
last_error = now;
}
XSetErrorHandler(old_handler);
X_UNLOCK;
SCR_UNLOCK;
} else {
if (rcs_scroll) {
if (db > 1) fprintf(stderr, "=== disab-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll);
X_LOCK;
trapped_record_xerror = 0;
old_handler =
XSetErrorHandler(trap_record_xerror);
rcs_scroll = XRecordCurrentClients;
XRecordUnregisterClients(rdpy_ctrl, rc_scroll,
&rcs_scroll, 1);
XRecordDisableContext(rdpy_ctrl, rc_scroll);
XFlush_wr(rdpy_ctrl);
XRecordProcessReplies(rdpy_data);
if (trapped_record_xerror) {
RECORD_ERROR_MSG;
shutdown_record_context(rc_scroll,
0, reopen_dpys);
rc_scroll = 0;
last_error = now;
if (! use_xrecord) {
XSetErrorHandler(old_handler);
X_UNLOCK;
SCR_UNLOCK;
return;
}
}
XSetErrorHandler(old_handler);
X_UNLOCK;
}
}
SCR_UNLOCK;
/*
* XXX if we do a XFlush_wr(rdpy_ctrl) here we get:
*
X Error of failed request: XRecordBadContext
Major opcode of failed request: 145 (RECORD)
Minor opcode of failed request: 5 (XRecordEnableContext)
Context in failed request: 0x2200013
Serial number of failed request: 29
Current serial number in output stream: 29
*
* need to figure out what is going on... since it may lead
* infrequent failures.
*/
xrecord_focus_window = None;
xrecord_wm_window = None;
xrecord_ptr_window = None;
xrecord_keysym = NoSymbol;
rcs_scroll = 0;
return;
}
if (db || debug_grabs) fprintf(stderr, "XRECORD ON: %d/%d %.4f\n", xrecording, setby, now - x11vnc_start);
if (xrecording) {
return;
}
if (do_shutdown && rc_scroll) {
static int didmsg = 0;
/* should not happen... */
if (0 || !didmsg) {
rfbLog("warning: do_shutdown && rc_scroll\n");
didmsg = 1;
}
xrecord_watch(0, SCR_NONE);
}
xrecording = 0;
xrecord_focus_window = None;
xrecord_wm_window = None;
xrecord_ptr_window = None;
xrecord_keysym = NoSymbol;
xrecord_set_by_keys = 0;
xrecord_set_by_mouse = 0;
/* get the window with focus and mouse pointer: */
clast = None;
focus = None;
wm = None;
X_LOCK;
SCR_LOCK;
#if 0
/*
* xrecord_focus_window / focus not currently used... save a
* round trip to the X server for now.
* N.B. our heuristic is inaccurate: if he is scrolling and
* drifts off of the scrollbar onto another application we
* will catch that application, not the starting ones.
* check_xrecord_{keys,mouse} mitigates this somewhat by
* delaying calls to xrecord_watch as much as possible.
*/
XGetInputFocus(dpy, &focus, &i);
#endif
wm = query_pointer(rootwin);
if (wm) {
c = wm;
} else {
c = rootwin;
}
/* descend a bit to avoid wm frames: */
if (c != rootwin && c == last_win) {
/* use cached results to avoid roundtrips: */
clast = last_result;
} else if (scroll_good_all == NULL && scroll_skip_all == NULL) {
/* more efficient if name info not needed. */
xrecord_name_info[0] = '\0';
clast = descend_pointer(6, c, NULL, 0);
} else {
char *nm;
int matched_good = 0, matched_skip = 0;
clast = descend_pointer(6, c, xrecord_name_info, NAMEINFO);
if (db) fprintf(stderr, "name_info: %s\n", xrecord_name_info);
nm = xrecord_name_info;
if (scroll_good_all) {
matched_good += match_str_list(nm, scroll_good_all);
}
if (setby == SCR_KEY && scroll_good_key) {
matched_good += match_str_list(nm, scroll_good_key);
}
if (setby == SCR_MOUSE && scroll_good_mouse) {
matched_good += match_str_list(nm, scroll_good_mouse);
}
if (scroll_skip_all) {
matched_skip += match_str_list(nm, scroll_skip_all);
}
if (setby == SCR_KEY && scroll_skip_key) {
matched_skip += match_str_list(nm, scroll_skip_key);
}
if (setby == SCR_MOUSE && scroll_skip_mouse) {
matched_skip += match_str_list(nm, scroll_skip_mouse);
}
if (!matched_good && matched_skip) {
clast = None;
}
}
if (c != rootwin) {
/* cache results for possible use next call */
last_win = c;
last_result = clast;
}
if (!clast || clast == rootwin) {
if (db) fprintf(stderr, "--- xrecord_watch: SKIP.\n");
X_UNLOCK;
SCR_UNLOCK;
return;
}
/* set protocol request ranges: */
rr_scroll[0] = rr_CA;
rr_scroll[1] = rr_CW;
/*
* start trapping... there still are some occasional failures
* not yet understood, likely some race condition WRT the
* context being setup.
*/
trapped_record_xerror = 0;
old_handler = XSetErrorHandler(trap_record_xerror);
if (! rc_scroll) {
/* do_shutdown case or first time in */
if (gdpy_ctrl) {
/*
* Even though rdpy_ctrl is impervious to grabs
* at this point, we still get deadlock, why?
* It blocks in the library find_display() call.
*/
check_xrecord_grabserver();
if (xserver_grabbed) {
XSetErrorHandler(old_handler);
X_UNLOCK;
SCR_UNLOCK;
return;
}
}
rcs_scroll = (XRecordClientSpec) clast;
rc_scroll = XRecordCreateContext(rdpy_ctrl, 0, &rcs_scroll, 1,
rr_scroll, 2);
if (! do_shutdown) {
XSync(rdpy_ctrl, False);
}
if (db) fprintf(stderr, "NEW rc: 0x%lx\n", rc_scroll);
if (rc_scroll) {
dtime0(&create_time);
} else {
rcs_scroll = 0;
}
} else if (! do_shutdown) {
if (rcs_scroll) {
/*
* should have been unregistered in xrecord_watch(0)...
*/
rcs_scroll = XRecordCurrentClients;
XRecordUnregisterClients(rdpy_ctrl, rc_scroll,
&rcs_scroll, 1);
if (db > 1) fprintf(stderr, "=2= unreg-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll);
}
rcs_scroll = (XRecordClientSpec) clast;
if (db > 1) fprintf(stderr, "=-= reg-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll);
if (!XRecordRegisterClients(rdpy_ctrl, rc_scroll, 0,
&rcs_scroll, 1, rr_scroll, 2)) {
if (1 || now > last_error + 60) {
rfbLog("failed to register client 0x%lx with"
" X RECORD context rc_scroll.\n", clast);
}
last_error = now;
rcs_scroll = 0;
/* continue on for now... */
}
}
XFlush_wr(rdpy_ctrl);
if (db) fprintf(stderr, "rc_scroll: 0x%lx\n", rc_scroll);
if (trapped_record_xerror) {
RECORD_ERROR_MSG;
}
if (! rc_scroll) {
XSetErrorHandler(old_handler);
X_UNLOCK;
SCR_UNLOCK;
use_xrecord = 0;
rfbLog("failed to create X RECORD context rc_scroll.\n");
rfbLog(" switching to -noscrollcopyrect mode.\n");
return;
} else if (! rcs_scroll || trapped_record_xerror) {
/* try again later */
shutdown_record_context(rc_scroll, 0, reopen_dpys);
rc_scroll = 0;
last_error = now;
XSetErrorHandler(old_handler);
X_UNLOCK;
SCR_UNLOCK;
return;
}
xrecord_focus_window = focus;
#if 0
/* xrecord_focus_window currently unused. */
if (! xrecord_focus_window) {
xrecord_focus_window = clast;
}
#endif
xrecord_wm_window = wm;
if (! xrecord_wm_window) {
xrecord_wm_window = clast;
}
xrecord_ptr_window = clast;
xrecording = 1;
xrecord_seq++;
dtime0(&xrecord_start);
rc = XRecordEnableContextAsync(rdpy_data, rc_scroll, record_switch,
(XPointer) xrecord_seq);
if (!rc || trapped_record_xerror) {
if (1 || now > last_error + 60) {
rfbLog("failed to enable RECORD context "
"rc_scroll: 0x%lx rc: %d\n", rc_scroll, rc);
if (trapped_record_xerror) {
RECORD_ERROR_MSG;
}
}
shutdown_record_context(rc_scroll, 0, reopen_dpys);
rc_scroll = 0;
last_error = now;
xrecording = 0;
/* continue on for now... */
}
XSetErrorHandler(old_handler);
/* XXX this may cause more problems than it solves... */
if (use_xrecord) {
XFlush_wr(rdpy_data);
}
X_UNLOCK;
SCR_UNLOCK;
#endif
}