/* -- 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; int xserver_grabbed = 0; int trap_record_xerror(Display *, XErrorEvent *); void initialize_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 Display *gdpy_data = NULL; 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; 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(gdpy_ctrl); XCloseDisplay(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(gdpy_ctrl); XCloseDisplay(gdpy_data); gdpy_ctrl = NULL; gdpy_data = NULL; XSetErrorHandler(old_handler); return; } XSetErrorHandler(old_handler); XFlush_wr(gdpy_data); #endif if (debug_grabs) { fprintf(stderr, "xrecord_grabserver-done: %.5f\n", dnowx()); } } 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(rr_CA); if (rr_CW) XFree(rr_CW); if (rr_GS) XFree(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(rdpy_data); rdpy_data = NULL; } if (rdpy_ctrl) { XCloseDisplay(rdpy_ctrl); rdpy_ctrl = NULL; } rdpy_ctrl = XOpenDisplay(DisplayString(dpy)); XSync(dpy, True); XSync(rdpy_ctrl, True); /* open datalink connection to DISPLAY: */ rdpy_data = XOpenDisplay(DisplayString(dpy)); 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(gdpy_data); gdpy_data = NULL; } if (gdpy_ctrl) { XCloseDisplay(gdpy_ctrl); gdpy_ctrl = NULL; } xserver_grabbed = 0; gdpy_ctrl = XOpenDisplay(DisplayString(dpy)); XSync(dpy, True); XSync(gdpy_ctrl, True); gdpy_data = XOpenDisplay(DisplayString(dpy)); 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(rr_CA); if (rr_CW) XFree(rr_CW); if (rr_GS) XFree(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(rdpy_data); rdpy_data = NULL; } if (rdpy_ctrl) { XCloseDisplay(rdpy_ctrl); rdpy_ctrl = NULL; } if (gdpy_data) { XCloseDisplay(gdpy_data); gdpy_data = NULL; } if (gdpy_ctrl) { XCloseDisplay(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].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, dnow() - x11vnc_start); } 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; ilength; 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, dnow() - x11vnc_start); } 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; icategory == 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 = dnow() - x11vnc_start; 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 = dnow() - x11vnc_start; 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) { int last_val, cnt = 0, i, max = 10; double d; #if LIBVNCSERVER_HAVE_RECORD if (!gdpy_ctrl || !gdpy_data) { return; } if (unixpw_in_progress) return; dtime0(&d); XFlush_wr(gdpy_ctrl); for (i=0; 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(rdpy_data); rdpy_data = NULL; if (debug_scroll) { rfbLog("closing RECORD control connection.\n"); } XCloseDisplay(rdpy_ctrl); rdpy_ctrl = NULL; rdpy_ctrl = XOpenDisplay(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(dpystr); if (! rdpy_data) { rfbLog("Failed to reopen RECORD data connection:" "%s\n", dpystr); rfbLog(" disabling RECORD scroll detection.\n"); XCloseDisplay(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; 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; #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) { Window focus, wm, c, clast; static double create_time = 0.0; double now; static double last_error = 0.0; int rc, db = debug_scroll; int do_shutdown = 0; int reopen_dpys = 1; XErrorHandler old_handler = NULL; static Window last_win = None, last_result = None; 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 }