From ad02e15542abd55dd8bed89f4e267d5fd7f595b8 Mon Sep 17 00:00:00 2001 From: runge Date: Sat, 9 Jul 2005 03:52:20 +0000 Subject: x11vnc: -grab_buster for XGrabServer deadlock; fix scrolls and copyrect for -clip and -id --- x11vnc/x11vnc.c | 579 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 563 insertions(+), 16 deletions(-) (limited to 'x11vnc/x11vnc.c') diff --git a/x11vnc/x11vnc.c b/x11vnc/x11vnc.c index 0d30ef8..82c7f65 100644 --- a/x11vnc/x11vnc.c +++ b/x11vnc/x11vnc.c @@ -382,7 +382,7 @@ double xdamage_scheduled_mark = 0.0; sraRegionPtr xdamage_scheduled_mark_region = NULL; /* date +'lastmod: %Y-%m-%d' */ -char lastmod[] = "0.7.2 lastmod: 2005-07-06"; +char lastmod[] = "0.7.2 lastmod: 2005-07-09"; int hack_val = 0; /* X display info */ @@ -594,6 +594,8 @@ double dtime(double *); double dtime0(double *); double dnow(void); double dnowx(void); +double rnow(void); +double rfac(void); void initialize_blackouts(char *); void initialize_blackouts_and_xinerama(void); @@ -904,6 +906,12 @@ Display *gdpy_data = NULL; /* Ditto for GrabServer watcher */ Display *gdpy_ctrl = NULL; int xserver_grabbed = 0; +/* XXX CHECK BEFORE RELEASE */ +int grab_buster = 1; +int grab_buster_delay = 20; +int sync_tod_delay = 3; +pid_t grab_buster_pid = 0; + char *client_connect = NULL; /* strings for -connect option */ char *client_connect_file = NULL; int vnc_connect = 1; /* -vncconnect option */ @@ -3008,6 +3016,7 @@ void disable_grabserver(Display *in_dpy, int change) { rfbLog("No XTEST or DEC-XTRAP protection from XGrabServer.\n"); rfbLog("Deadlock if your window manager calls XGrabServer!!\n"); } + XFlush(in_dpy); } Bool XRecordQueryVersion_wr(Display *dpy, int *maj, int *min) { @@ -5100,6 +5109,7 @@ char *xerror_string(XErrorEvent *error) { char *crash_stack_command1 = NULL; char *crash_stack_command2 = NULL; char *crash_debug_command = NULL; +/* XXX CHECK BEFORE RELEASE */ int crash_debug = 1; void initialize_crash_handler(void) { @@ -11651,17 +11661,406 @@ void print_xevent_bases(void) { fprintf(stderr, " SelClear=%d, Expose=%d\n", SelectionClear, Expose); } -void sync_tod_with_servertime() { - static Atom servertime = None; + +void get_prop(char *str, int len, Atom prop) { + Atom type; + int format, slen, dlen, i; + unsigned long nitems = 0, bytes_after = 0; + unsigned char* data = NULL; + + for (i=0; i len) { + /* too big */ + XFree(data); + break; + } + memcpy(str+slen, data, dlen); + slen += dlen; + str[slen] = '\0'; + XFree(data); + } + } while (bytes_after > 0); +} + +void bust_grab(int reset) { + static int bust_count = 0; + static time_t last_bust = 0; + time_t now = time(0); + KeyCode key; + int button, x, y, nb; + + if (now > last_bust + 180) { + bust_count = 0; + } + if (reset) { + bust_count = 0; + return; + } + + x = 0; + y = 0; + button = 0; + key = NoSymbol; + + nb = 8; + if (bust_count >= 3 * nb) { + fprintf(stderr, "too many bust_grab's %d for me\n", bust_count); + exit(0); + } + if (bust_count % nb == 0) { + button = 1; + } else if (bust_count % nb == 1) { + button = 1; + } else if (bust_count % nb == 2) { + key = XKeysymToKeycode(dpy, XK_Escape); + } else if (bust_count % nb == 3) { + button = 3; + } else if (bust_count % nb == 4) { + key = XKeysymToKeycode(dpy, XK_space); + } else if (bust_count % nb == 5) { + x = bust_count * 23; + y = bust_count * 17; + } else if (bust_count % nb == 5) { + button = 2; + } else if (bust_count % nb == 6) { + key = XKeysymToKeycode(dpy, XK_a); + } + + if (key == NoSymbol) { + key = XKeysymToKeycode(dpy, XK_a); + if (key == NoSymbol) { + button = 1; + } + } + + bust_count++; + + if (button) { + /* try button press+release */ + fprintf(stderr, "**bust_grab: button%d %.4f\n", + button, dnowx()); + XTestFakeButtonEvent_wr(dpy, button, True, CurrentTime); + XFlush(dpy); + usleep(50 * 1000); + XTestFakeButtonEvent_wr(dpy, button, False, CurrentTime); + } else if (x > 0) { + /* try button motion*/ + int scr = DefaultScreen(dpy); + + fprintf(stderr, "**bust_grab: x=%d y=%d %.4f\n", x, y, + dnowx()); + XTestFakeMotionEvent_wr(dpy, scr, x, y, CurrentTime); + XFlush(dpy); + usleep(50 * 1000); + + /* followed by button press */ + button = 1; + fprintf(stderr, "**bust_grab: button%d\n", button); + XTestFakeButtonEvent_wr(dpy, button, True, CurrentTime); + XFlush(dpy); + usleep(50 * 1000); + XTestFakeButtonEvent_wr(dpy, button, False, CurrentTime); + } else { + /* try Escape or Space press+release */ + fprintf(stderr, "**bust_grab: keycode: %d %.4f\n", + (int) key, dnowx()); + XTestFakeKeyEvent_wr(dpy, key, True, CurrentTime); + XFlush(dpy); + usleep(50 * 1000); + XTestFakeKeyEvent_wr(dpy, key, False, CurrentTime); + } + XFlush(dpy); + last_bust = time(0); +} + +typedef struct _grabwatch { + int pid; + int tick; + unsigned long time; + time_t change; +} grabwatch_t; +#define GRABWATCH 16 + +int grab_npids = 1; + +int process_watch(char *str, int parent, int db) { + int i, pid, ticker, npids; + char diff[128]; + unsigned long xtime; + static grabwatch_t watches[GRABWATCH]; + static int first = 1; + time_t now = time(0); + static time_t last_bust = 0; + int too_long, problems = 0; + + if (first) { + for (i=0; i < GRABWATCH; i++) { + watches[i].pid = 0; + watches[i].tick = 0; + watches[i].time = 0; + watches[i].change = 0; + } + first = 0; + } + + /* record latest value of prop */ + if (str && *str != '\0') { + if (sscanf(str, "%d/%d/%lu/%s", &pid, &ticker, &xtime, diff) + == 4) { + int got = -1, free = -1; + + if (db) fprintf(stderr, "grab_buster %d - %d - %lu - %s" + "\n", pid, ticker, xtime, diff); + + if (pid == parent && !strcmp(diff, "QUIT")) { + /* that's it. */ + return 0; + } + if (pid == 0 || ticker == 0 || xtime == 0) { + /* bad prop read. */ + goto badtickerstr; + } + for (i=0; i < GRABWATCH; i++) { + if (watches[i].pid == pid) { + got = i; + break; + } + if (free == -1 && watches[i].pid == 0) { + free = i; + } + } + if (got == -1) { + if (free == -1) { + /* bad news */; + free = GRABWATCH - 1; + } + watches[free].pid = pid; + watches[free].tick = ticker; + watches[free].time = xtime; + watches[free].change = now; + if (db) fprintf(stderr, "grab_buster free slot: %d\n", free); + } else { + if (db) fprintf(stderr, "grab_buster got slot: %d\n", got); + if (watches[got].tick != ticker) { + watches[got].change = now; + } + if (watches[got].time != xtime) { + watches[got].change = now; + } + watches[got].tick = ticker; + watches[got].time = xtime; + } + } else { + if (db) fprintf(stderr, "grab_buster bad prop str: %s\n", str); + } + } + + badtickerstr: + + too_long = grab_buster_delay; + if (too_long < 3 * sync_tod_delay) { + too_long = 3 * sync_tod_delay; + } + + npids = 0; + for (i=0; i < GRABWATCH; i++) { + if (watches[i].pid) { + npids++; + } + } + grab_npids = npids; + if (npids > 4) { + npids = 4; + } + + /* now check everyone we are tracking */ + for (i=0; i < GRABWATCH; i++) { + int fac = 1; + if (!watches[i].pid) { + continue; + } + if (watches[i].change == 0) { + watches[i].change = now; /* just to be sure */ + continue; + } + + pid = watches[i].pid; + + if (pid != parent) { + fac = 2; + } + if (npids > 0) { + fac *= npids; + } + + if (now > watches[i].change + fac*too_long) { + int process_alive = 1; + + fprintf(stderr, "grab_buster: problem with pid: " + "%d - %d/%d/%d\n", pid, (int) now, + (int) watches[i].change, too_long); + + if (kill((pid_t) pid, 0) != 0) { + if (1 || errno == ESRCH) { + process_alive = 0; + } + } + + if (!process_alive) { + watches[i].pid = 0; + watches[i].tick = 0; + watches[i].time = 0; + watches[i].change = 0; + fprintf(stderr, "grab_buster: pid gone: %d\n", + pid); + if (pid == parent) { + /* that's it */ + return 0; + } + } else { + int sleep = sync_tod_delay * 1000 * 1000; + + bust_grab(0); + problems++; + last_bust = now; + usleep(1 * sleep); + break; + } + } + } + + if (!problems) { + bust_grab(1); + } + return 1; +} + +void grab_buster_watch(int parent, char *dstr) { + Atom ticker_atom = None; + int sleep = sync_tod_delay * 921 * 1000; + char propval[200]; + int ev, er, maj, min; + int db = 0; + + if (grab_buster > 1) { + db = 1; + } + + /* overwrite original dpy, we let orig connection sit unused. */ + dpy = XOpenDisplay(dstr); + if (!dpy) { + fprintf(stderr, "grab_buster_watch: could not reopen: %s\n", + dstr); + return; + } + rfbLogEnable(0); + + /* check for XTEST, etc, and then disable grabs for us */ + if (! XTestQueryExtension_wr(dpy, &ev, &er, &maj, &min)) { + xtest_present = 0; + } else { + xtest_present = 1; + } + if (! XETrapQueryExtension_wr(dpy, &ev, &er, &maj)) { + xtrap_present = 0; + } else { + xtrap_present = 1; + } + + if (! xtest_present && ! xtrap_present) { + fprintf(stderr, "grab_buster_watch: no grabserver " + "protection on display: %s\n", dstr); + return; + } + disable_grabserver(dpy, 0); + + usleep(3 * sleep); + + ticker_atom = XInternAtom(dpy, "X11VNC_TICKER", False); + if (! ticker_atom) { + fprintf(stderr, "grab_buster_watch: no ticker atom\n"); + return; + } + + while(1) { + int slp = sleep; + if (grab_npids > 1) { + slp = slp / 8; + } + usleep(slp); + usleep((int) (0.60 * rfac() * slp)); + + if (kill((pid_t) parent, 0) != 0) { + break; + } + + get_prop(propval, 128, ticker_atom); + if (db) fprintf(stderr, "got_prop: %s\n", propval); + + if (!process_watch(propval, parent, db)) { + break; + } + } +} + +void spawn_grab_buster(void) { +#if LIBVNCSERVER_HAVE_FORK + pid_t pid; + int parent = (int) getpid(); + char *dstr = strdup(DisplayString(dpy)); + + XCloseDisplay(dpy); + dpy = NULL; + + if ((pid = fork()) > 0) { + grab_buster_pid = pid; + if (! quiet) { + rfbLog("grab_buster pid is: %d\n", (int) pid); + } + } else if (pid == -1) { + fprintf(stderr, "spawn_grab_buster: could not fork\n"); + rfbLogPerror("fork"); + } else { + grab_buster_watch(parent, dstr); + exit(0); + } + + dpy = XOpenDisplay(dstr); + if (!dpy) { + rfbLog("failed to reopen display %s in spawn_grab_buster\n", + dstr); + exit(1); + } +#endif +} + +void sync_tod_with_servertime(void) { + static Atom ticker_atom = None; XEvent xev; - char diff[64]; + char diff[128]; static int seq = 0; + static unsigned long xserver_ticks = 1; int i, db = 0; - if (! servertime) { - servertime = XInternAtom(dpy, "X11VNC_SERVERTIME_DIFF", False); + if (! ticker_atom) { + ticker_atom = XInternAtom(dpy, "X11VNC_TICKER", False); } - if (! servertime) { + if (! ticker_atom) { return; } @@ -11670,8 +12069,9 @@ void sync_tod_with_servertime() { ; } - snprintf(diff, 64, "%.6f/%08d", servertime_diff, seq++); - XChangeProperty(dpy, rootwin, servertime, XA_STRING, 8, + snprintf(diff, 128, "%d/%08d/%lu/%.6f", (int) getpid(), seq++, + xserver_ticks, servertime_diff); + XChangeProperty(dpy, rootwin, ticker_atom, XA_STRING, 8, PropModeReplace, (unsigned char *) diff, strlen(diff)); XSync(dpy, False); @@ -11680,9 +12080,10 @@ void sync_tod_with_servertime() { for (k=0; k < 5; k++) { while (XCheckTypedEvent(dpy, PropertyNotify, &xev)) { - if (xev.xproperty.atom == servertime) { + if (xev.xproperty.atom == ticker_atom) { double stime; + xserver_ticks = xev.xproperty.time; stime = (double) xev.xproperty.time; stime = stime/1000.0; servertime_diff = dnow() - stime; @@ -11793,7 +12194,7 @@ void check_autorepeat(void) { */ void check_xevents(void) { XEvent xev; - int have_clients = 0; + int tmp, have_clients = 0; static int sent_some_sel = 0; static time_t last_request = 0; static time_t last_call = 0; @@ -11899,7 +12300,12 @@ void check_xevents(void) { } } - if (now > last_time_sync + 30) { + /* do this now that we have just cleared PropertyNotify */ + tmp = 0; + if (rfac() < 0.6) { + tmp = 1; + } + if (now > last_time_sync + sync_tod_delay + tmp) { sync_tod_with_servertime(); last_time_sync = now; } @@ -18289,6 +18695,9 @@ void set_nofb_params(void) { take_naps = 0; measure_speeds = 0; + /* got_grab_buster? */ + grab_buster = 0; + show_cursor = 0; show_multiple_cursors = 0; cursor_shape_updates = 0; @@ -19440,7 +19849,8 @@ void initialize_screen(int *argc, char **argv, XImage *fb) { for (i=1; i< *argc; i++) { rfbLog("\t[%d] %s\n", i, argv[i]); } - rfbLog("For a list of options run: x11vnc -help\n"); + rfbLog("For a list of options run: x11vnc -opts\n"); + rfbLog("or for the full help: x11vnc -help\n"); rfbLog("\n"); rfbLog("Here is a list of removed or obsolete" " options:\n"); @@ -23663,9 +24073,11 @@ void run_gui(char *gui_xdisplay, int connect_to_x11vnc, int simple_gui, if (connect_to_x11vnc) { set_env("X11VNC_STARTED", "1"); } - if (icon_mode && icon_mode_file) { + if (icon_mode) { set_env("X11VNC_ICON_MODE", "1"); - set_env("X11VNC_CLIENT_FILE", icon_mode_file); + if (icon_mode_file) { + set_env("X11VNC_CLIENT_FILE", icon_mode_file); + } if (icon_in_tray) { if (tray_manager_ok) { set_env("X11VNC_ICON_MODE", "TRAY:RUNNING"); @@ -23841,6 +24253,7 @@ void do_gui(char *opts) { " to display on.\n"); exit(1); } + fprintf(stderr, "starting gui, trying display: %s\n", gui_xdisplay); test_dpy = XOpenDisplay(gui_xdisplay); if (! test_dpy && auth_file) { if (getenv("XAUTHORITY") != NULL) { @@ -24746,6 +25159,12 @@ void draw_box(int x, int y, int w, int h, int restore) { unsigned short us; unsigned long ul; + if (clipshift) { + x -= coff_x; + y -= coff_y; + } + /* no subwin for wireframe */ + if (max > len) { /* create/resize storage lines: */ for (i=0; i<4; i++) { @@ -25132,6 +25551,12 @@ if (db > 1) fprintf(stderr, "BDP %d %d %d %d %d %d %d %d %d %d %d\n", ty2 = attr.y + attr.height; whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y); + if (clipshift) { + sraRgnOffset(whole, coff_x, coff_y); + } + if (subwin) { + sraRgnOffset(whole, off_x, off_y); + } frame = sraRgnCreateRect(tx1, ty1, tx2, ty2); sraRgnAnd(frame, whole); @@ -25175,6 +25600,18 @@ if (db > 1) fprintf(stderr, "BDP %d %d %d %d %d %d %d %d %d %d %d\n", continue; } } + if (clipshift) { + tx1 -= coff_x; + ty1 -= coff_y; + tx2 -= coff_x; + ty2 -= coff_y; + } + if (subwin) { + tx1 -= off_x; + ty1 -= off_y; + tx2 -= off_x; + ty2 -= off_y; + } direct_fb_copy(tx1, ty1, tx2, ty2, 1); @@ -25354,6 +25791,12 @@ int push_scr_ev(double *age, int type, int bdpush, int bdx, int bdy, backfill = sraRgnCreate(); whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y); + if (clipshift) { + sraRgnOffset(whole, coff_x, coff_y); + } + if (subwin) { + sraRgnOffset(whole, off_x, off_y); + } win0 = scr_ev[0].win; x0 = scr_ev[0].win_x; @@ -25447,6 +25890,12 @@ if (db > 1) fprintf(stderr, "------------ got: %d x: %4d y: %3d" dtime0(&tm); tmpregion = sraRgnCreateRect(0, 0, dpy_x, dpy_y); + if (clipshift) { + sraRgnOffset(tmpregion, coff_x, coff_y); + } + if (subwin) { + sraRgnOffset(tmpregion, off_x, off_y); + } tmpregion2 = sraRgnCreateRect(wx, wy, wx+ww, wy+wh); sraRgnAnd(tmpregion2, whole); sraRgnSubtract(tmpregion, tmpregion2); @@ -25593,6 +26042,19 @@ if (db) fprintf(stderr, " EST_TOO_LARGE"); ty1 = rect.y1; tx2 = rect.x2; ty2 = rect.y2; + + if (clipshift) { + tx1 -= coff_x; + ty1 -= coff_y; + tx2 -= coff_x; + ty2 -= coff_y; + } + if (subwin) { + tx1 -= off_x; + ty1 -= off_y; + tx2 -= off_x; + ty2 -= off_y; + } tx1 = nfix(tx1, dpy_x); ty1 = nfix(ty1, dpy_y); tx2 = nfix(tx2, dpy_x+1); @@ -26396,6 +26858,14 @@ if (db) fprintf(stderr, "check_xrecord_mouse: BEGIN LOOP: scr_ev_cnt: " bdx = start_x; bdy = start_y; + if (clipshift) { + bdx += coff_x; + bdy += coff_y; + } + if (subwin) { + bdx += off_x; + bdy += off_y; + } bdskinny = 32; set_bdpush(SCR_MOUSE, &last_bdpush, &bdpush); @@ -26700,6 +27170,15 @@ int try_copyrect(Window frame, int x, int y, int w, int h, int dx, int dy, dt_bad_check = time(0); } + if (clipshift) { + x -= coff_x; + y -= coff_y; + } + if (subwin) { + x -= off_x; + y -= off_y; + } + if (dt_bad && wireframe_in_progress) { sraRegionPtr rect; /* send the whole thing... */ @@ -26800,6 +27279,15 @@ saw_me = 1; fprintf(stderr, " ----------\n"); continue; } + if (clipshift) { + attr.x -= coff_x; + attr.y -= coff_y; + } + if (subwin) { + attr.x -= off_x; + attr.y -= off_y; + } + /* * first subtract any overlap from the initial * window rectangle @@ -26853,6 +27341,13 @@ if (db2 && saw_me) continue; if (extra_clip && ! sraRgnEmpty(extra_clip)) { whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y); + if (clipshift) { + sraRgnOffset(extra_clip, -coff_x, -coff_y); + } + if (subwin) { + sraRgnOffset(extra_clip, -off_x, -off_y); + } + iter = sraRgnGetIterator(extra_clip); while (sraRgnIteratorNext(iter, &rect)) { /* clip the window to the visible screen: */ @@ -27556,8 +28051,18 @@ if (db) fprintf(stderr, "send_copyrect: %d\n", sent_copyrect); fb_push_wait(0.1, FB_COPY); if (now > last_time + delay) { + int xt = x, yt = y; - scale_mark(x, y, x+w, y+h); + if (clipshift) { + xt -= coff_x; + yt -= coff_y; + } + if (subwin) { + xt -= off_x; + yt -= off_y; + } + + scale_mark(xt, yt, xt+w, yt+h); last_time = now; last_copyrect_fix = now; } @@ -28220,6 +28725,23 @@ double dnowx(void) { return dnow() - x11vnc_start; } +double rnow(void) { + double t = dnowx(); + t = t - ((int) t); + if (t > 1.0) { + t = 1.0; + } else if (t < 0.0) { + t = 0.0; + } + return t; +} + +double rfac(void) { + double f = (double) rand(); + f = f / ((double) RAND_MAX); + return f; +} + void measure_display_hook(rfbClientPtr cl) { ClientData *cd = (ClientData *) cl->clientData; dtime0(&cd->timer); @@ -30258,6 +30780,21 @@ static void print_help(int mode) { " currently used by the -scrollcopyrect scheme and to\n" " monitor X server grabs.\n" "\n" +"-grab_buster Some of the use of the RECORD extension can leave a\n" +"-nograb_buster tiny window for XGrabServer deadlock. This is only if\n" +" the whole-server grabbing application expects mouse\n" +" or keyboard input before releasing the grab. It is\n" +" usually a window manager that does this. x11vnc takes\n" +" care to avoid the the problem, but if caught x11vnc\n" +" will freeze. Without -grab_buster, the only solution\n" +" is to go the physical display and give it some input\n" +" to satisfy the grabbing app. Or manually kill and\n" +" restart the window manager. With -grab_buster, x11vnc\n" +" will fork a helper thread and if x11vnc appears to be\n" +" stuck in a grab after a period of time (20-30 sec)\n" +" then it will inject some user input: button clicks,\n" +" Escape, mouse motion, etc to try to break the grab.\n" +"\n" "-debug_grabs Turn on debugging info printout with respect to\n" " XGrabServer() deadlock for -scrollcopyrect mode.\n" "\n" @@ -32062,6 +32599,10 @@ int main(int argc, char* argv[]) { debug_tiles++; } else if (!strcmp(arg, "-debug_grabs")) { debug_grabs++; + } else if (!strcmp(arg, "-grab_buster")) { + grab_buster++; + } else if (!strcmp(arg, "-nograb_buster")) { + grab_buster = 0; } else if (!strcmp(arg, "-snapfb")) { use_snapfb = 1; } else if (!strcmp(arg, "-rawfb")) { @@ -32599,6 +33140,7 @@ int main(int argc, char* argv[]) { fprintf(stderr, " fixscreen: %s\n", screen_fixup_str ? screen_fixup_str : "null"); fprintf(stderr, " noxrecord: %d\n", noxrecord); + fprintf(stderr, " grabbuster: %d\n", grab_buster); fprintf(stderr, " ptr_mode: %d\n", pointer_mode); fprintf(stderr, " inputskip: %d\n", ui_skip); fprintf(stderr, " speeds: %s\n", speeds_str @@ -32758,6 +33300,11 @@ int main(int argc, char* argv[]) { } } + sync_tod_with_servertime(); + if (grab_buster) { + spawn_grab_buster(); + } + #if LIBVNCSERVER_HAVE_LIBXFIXES if (! XFixesQueryExtension(dpy, &xfixes_base_event_type, &er)) { if (! quiet) { -- cgit v1.2.1