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 --- ChangeLog | 4 + x11vnc/ChangeLog | 5 + x11vnc/README | 28 ++- x11vnc/tkx11vnc | 52 +++-- x11vnc/tkx11vnc.h | 52 +++-- x11vnc/x11vnc.1 | 19 +- x11vnc/x11vnc.c | 579 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 7 files changed, 682 insertions(+), 57 deletions(-) diff --git a/ChangeLog b/ChangeLog index 826d906..6af189f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2005-07-09 Karl Runge + * x11vnc: -grab_buster for breaking XGrabServer deadlock, fix + scrolls and copyrect for -clip and -id cases. + 2005-07-06 Karl Runge * x11vnc: -gui tray now embeds in systray; more improvements to gui. diff --git a/x11vnc/ChangeLog b/x11vnc/ChangeLog index bb3eba8..babdaaa 100644 --- a/x11vnc/ChangeLog +++ b/x11vnc/ChangeLog @@ -1,3 +1,8 @@ +2005-07-09 Karl Runge + * add -grab_buster helper thread to break up grabs (might not be + need any longer due to gett XFlush-ing). Fix scrolls and + copyrect for -clip and -id cases. + 2005-07-06 Karl Runge * many improvements to the gui. now embeds into system tray ok. x11vnc -debug_grabs, -printgui, -nosync diff --git a/x11vnc/README b/x11vnc/README index ba771a8..b08bae1 100644 --- a/x11vnc/README +++ b/x11vnc/README @@ -1,5 +1,5 @@ -x11vnc README file Date: Wed Jul 6 22:35:15 EDT 2005 +x11vnc README file Date: Sat Jul 9 00:07:21 EDT 2005 The following information is taken from these URLs: @@ -577,10 +577,10 @@ make I don't have any formal beta-testers for the releases of x11vnc, so I'd appreciate any additional testing very much! - I'd like to release version 0.7.2 in Jun/Jul/2005 sometime, here is + I'd like to release version 0.7.2 in Jun-Jul/2005 sometime, here is the current tarball: - RC-6 lastmod: 2005-07-07 [51]x11vnc-0.7.2beta.tar.gz + RC-6 lastmod: 2005-07-08 [51]x11vnc-0.7.2beta.tar.gz There are also some Linux, Solaris, and other OS test binaries [52]here. Please kick the tires and report bugs, performance @@ -4548,7 +4548,7 @@ x11vnc: a VNC server for real X displays Here are all of x11vnc command line options: % x11vnc -opts (see below for -help long descriptions) -x11vnc: allow VNC connections to real X11 displays. 0.7.2 lastmod: 2005-07-06 +x11vnc: allow VNC connections to real X11 displays. 0.7.2 lastmod: 2005-07-09 x11vnc options: -display disp -auth file @@ -4602,7 +4602,8 @@ x11vnc options: -scr_keys list -scr_term list -scr_keyrepeat lo-hi -scr_parms string -fixscreen string -debug_scroll - -noxrecord -debug_grabs + -noxrecord -grab_buster + -nograb_buster -debug_grabs -pointer_mode n -input_skip n -speeds rd,bw,lat -wmdt string -debug_pointer -debug_keyboard @@ -4648,7 +4649,7 @@ libvncserver options: % x11vnc -help -x11vnc: allow VNC connections to real X11 displays. 0.7.2 lastmod: 2005-07-06 +x11vnc: allow VNC connections to real X11 displays. 0.7.2 lastmod: 2005-07-09 Typical usage is: @@ -5730,6 +5731,21 @@ Options: currently used by the -scrollcopyrect scheme and to monitor X server grabs. +-grab_buster Some of the use of the RECORD extension can leave a +-nograb_buster tiny window for XGrabServer deadlock. This is only if + the whole-server grabbing application expects mouse + or keyboard input before releasing the grab. It is + usually a window manager that does this. x11vnc takes + care to avoid the the problem, but if caught x11vnc + will freeze. Without -grab_buster, the only solution + is to go the physical display and give it some input + to satisfy the grabbing app. Or manually kill and + restart the window manager. With -grab_buster, x11vnc + will fork a helper thread and if x11vnc appears to be + stuck in a grab after a period of time (20-30 sec) + then it will inject some user input: button clicks, + Escape, mouse motion, etc to try to break the grab. + -debug_grabs Turn on debugging info printout with respect to XGrabServer() deadlock for -scrollcopyrect mode. diff --git a/x11vnc/tkx11vnc b/x11vnc/tkx11vnc index 91a63cb..f758cfe 100755 --- a/x11vnc/tkx11vnc +++ b/x11vnc/tkx11vnc @@ -403,7 +403,7 @@ After selecting a VNC client from the \"Clients -> current\" menu, you will be presented with a dialog that shows the information about the VNC client. -You can chose to disconnect the client by clicking on the +You can choose to disconnect the client by clicking on the \"Disconnect\" checkbox and pressing \"OK\". There will be a confirmation dialog to doublecheck. @@ -500,8 +500,8 @@ to apply the changes, or press \"Cancel\" to skip applying them. remote-control commands. - \"Ask for Confirmation\" toggles whether a popup menu will be presented - at the X display when a new VNC viewer attempts to connects. The person - sitting at the X display can chose to accept or reject the connection + at the X display when a new VNC viewer attempts to connect. The person + sitting at the X display can choose to accept or reject the connection or accept the connection in View-Only mode. It corresponds to the \"-R accept:popup\" and \"-R accept:\" remote-control commands. @@ -540,7 +540,7 @@ the ViewOnly log in aspect: \"Password\" is still required to log in. set helptext(Misc-Tuning:) " x11vnc has what seems like hundreds of tuning parameters. In this -sub-menu we place some lesser used ones. Mostly likely you'll want to +sub-menu we place some lesser used ones. Most likely you'll want to leave them at their default values, but you can try them out quickly with the gui to see if they improve things. " @@ -625,7 +625,7 @@ A brief Background on pixels, color, and visuals: mappings and so provide less variation in kinds of colors. A visual's \"depth\" is how many of the pixels are used in the - actual recipe. This may sound wasteful (i.t. not using some of the + actual recipe. This may sound wasteful (i.e. not using some of the bits), but for 32bpp (4 billion colors) that is too much and nearly always only 24 for them are used. The most common Visual seems to be depth 24 TrueColor at 32bpp. This provides 16 million colors @@ -645,7 +645,7 @@ a speedup is achieved because writing graphics data to, say, the 8bit visual does not destroy the image data in the 24bit visual. Evidently popup menus can be done very quickly this way: they use the 8bit visual and when the popup goes away the graphics data in the 24bit visual is -immediately reexposed without having the application redrawing it. +immediately reexposed without having the application redraw it. Also, some legacy applications can only use 8bpp visuals. But in these days of high color graphics and web browsers one would like the rest @@ -681,7 +681,7 @@ is shared. Note if the application pops up multiple windows they are not tracked and shared. So this is not application sharing. The application has to be very simple (e.g. a simple terminal or the image window on a webcam) -for this mode to be useable. +for this mode to be usable. " set helptext(ResizeRotate:) " This sub-menu has some options regarding screens that support the X @@ -722,7 +722,7 @@ the screen are still slow today). This sub-menu has some options for the x11vnc Scroll detection and CopyRect speedup scheme. -For this mode, x11vnc \"spies\" on communication be the X server and +For this mode, x11vnc \"spies\" on communication between the X server and applications using the RECORD extension. It looks for various patterns to detect a scrolled window. This only works for some applications, fortunately some important ones. @@ -2037,9 +2037,11 @@ proc update_menu_vars {{query ""}} { } foreach piece $query_result_list { +#puts stderr "UMV: $piece" if {[regexp {^([^:][^:]*):(.*)$} $piece m0 item val]} { if {[info exists menu_var($item)]} { set old $menu_var($item) +#puts stderr " $old" if {$val == "N/A"} { continue } @@ -2885,6 +2887,11 @@ proc make_menu_items {} { global bfont ffont beginner_mode simple_gui_created global helptext helpremote helplabel + # some tweaks... + if {![info exists menu_var(deny)]} { + set menu_var(deny) 0 + } + set case ""; set L_casc "" set L_casc_count 0 @@ -3234,7 +3241,14 @@ proc do_props {{msg ""}} { global have_labelframes ffont bfont global props_buttons icon_noadvanced - if ![info exists props_accept] { + if [info exists menu_var(deny)] { + if {$menu_var(deny) == $unset_str || $menu_var(deny) == 0} { + set props_accept 1 + } else { + set props_accept 0 + } + } else { + set menu_var(deny) 0 set props_accept 1 } set prop0_accept $props_accept @@ -3729,15 +3743,18 @@ proc make_icon {} { global tray_embed tray_running env global x11vnc_client_file client_tail client_str saved_clients_str global client_balloon_id - global bfont sfont ffont + global bfont sfont snfont ffont global icon_minimal gui_start_mode global window_view_posted menu_var x11vnc_gui_geom set min_x 24 set min_y 24 set font $bfont + set mfont $font + if {$tray_embed} { set font $sfont + set mfont $snfont } if {[info exists env(X11VNC_ICON_FONT)]} { set font $env(X11VNC_ICON_FONT) @@ -3768,15 +3785,16 @@ proc make_icon {} { label $l -text $lab -borderwidth $bw -font $font icon_win_cfg 0 + set window_view_posted 0 pack $l -fill both -expand 1 set menu "$l.menu" menu $menu -tearoff 0 -postcommand {set window_view_posted 0} - $menu add command -font $font -label "Properties" -command do_props - $menu add command -font $font -label "Help" -command "menu_help Tray" + $menu add command -font $mfont -label "Properties" -command do_props + $menu add command -font $mfont -label "Help" -command "menu_help Tray" $menu add separator - $menu add command -font $font -label "New Client" -command do_new_client - $menu add command -font $font -label "Disconnect All" -command do_disconnect_all + $menu add command -font $mfont -label "New Client" -command do_new_client + $menu add command -font $mfont -label "Disconnect All" -command do_disconnect_all $menu add separator set wv "$menu.casc1" @@ -3789,9 +3807,9 @@ proc make_icon {} { -command "do_var WindowView" \ -variable menu_var(WindowView) } - $menu add cascade -font $font -label "Window View" -menu $wv + $menu add cascade -font $mfont -label "Window View" -menu $wv - $menu add command -font $font -label "Stop x11vnc" -command clean_icon_exit + $menu add command -font $mfont -label "Stop x11vnc" -command clean_icon_exit bind $icon_win "pmenu $menu %X %Y" bind $icon_win "pmenu $menu %X %Y" @@ -4698,7 +4716,7 @@ global helpall helptext helpremote helplabel hostname osname global all_settings reply_xdisplay always_update global max_text_height max_text_width global menu_var unset_str menus_disabled -global bfont ffont old_labels have_labelframes +global bfont ffont sfont snfont old_labels have_labelframes global connected_to_x11vnc global delay_sleep extra_sleep extra_sleep_split global cache_all_query_vars diff --git a/x11vnc/tkx11vnc.h b/x11vnc/tkx11vnc.h index ddff298..5c84314 100644 --- a/x11vnc/tkx11vnc.h +++ b/x11vnc/tkx11vnc.h @@ -409,7 +409,7 @@ "you will be presented with a dialog that shows the information\n" "about the VNC client.\n" "\n" -"You can chose to disconnect the client by clicking on the \n" +"You can choose to disconnect the client by clicking on the \n" "\\\"Disconnect\\\" checkbox and pressing \\\"OK\\\". There will be a\n" "confirmation dialog to doublecheck.\n" "\n" @@ -506,8 +506,8 @@ " remote-control commands.\n" " \n" " - \\\"Ask for Confirmation\\\" toggles whether a popup menu will be presented\n" -" at the X display when a new VNC viewer attempts to connects. The person\n" -" sitting at the X display can chose to accept or reject the connection\n" +" at the X display when a new VNC viewer attempts to connect. The person\n" +" sitting at the X display can choose to accept or reject the connection\n" " or accept the connection in View-Only mode. It corresponds to the \n" " \\\"-R accept:popup\\\" and \\\"-R accept:\\\" remote-control commands.\n" " \n" @@ -546,7 +546,7 @@ "\n" " set helptext(Misc-Tuning:) \"\n" "x11vnc has what seems like hundreds of tuning parameters. In this\n" -"sub-menu we place some lesser used ones. Mostly likely you'll want to\n" +"sub-menu we place some lesser used ones. Most likely you'll want to\n" "leave them at their default values, but you can try them out quickly\n" "with the gui to see if they improve things.\n" "\"\n" @@ -631,7 +631,7 @@ " mappings and so provide less variation in kinds of colors.\n" "\n" " A visual's \\\"depth\\\" is how many of the pixels are used in the\n" -" actual recipe. This may sound wasteful (i.t. not using some of the\n" +" actual recipe. This may sound wasteful (i.e. not using some of the\n" " bits), but for 32bpp (4 billion colors) that is too much and nearly\n" " always only 24 for them are used. The most common Visual seems to\n" " be depth 24 TrueColor at 32bpp. This provides 16 million colors\n" @@ -651,7 +651,7 @@ "visual does not destroy the image data in the 24bit visual. Evidently\n" "popup menus can be done very quickly this way: they use the 8bit visual\n" "and when the popup goes away the graphics data in the 24bit visual is\n" -"immediately reexposed without having the application redrawing it.\n" +"immediately reexposed without having the application redraw it.\n" "\n" "Also, some legacy applications can only use 8bpp visuals. But in these\n" "days of high color graphics and web browsers one would like the rest\n" @@ -687,7 +687,7 @@ "Note if the application pops up multiple windows they are not tracked\n" "and shared. So this is not application sharing. The application has to\n" "be very simple (e.g. a simple terminal or the image window on a webcam)\n" -"for this mode to be useable.\n" +"for this mode to be usable.\n" "\"\n" " set helptext(ResizeRotate:) \"\n" "This sub-menu has some options regarding screens that support the X\n" @@ -728,7 +728,7 @@ "This sub-menu has some options for the x11vnc Scroll detection and\n" "CopyRect speedup scheme.\n" "\n" -"For this mode, x11vnc \\\"spies\\\" on communication be the X server and\n" +"For this mode, x11vnc \\\"spies\\\" on communication between the X server and\n" "applications using the RECORD extension. It looks for various patterns\n" "to detect a scrolled window. This only works for some applications,\n" "fortunately some important ones.\n" @@ -2043,9 +2043,11 @@ " }\n" "\n" " foreach piece $query_result_list {\n" +"#puts stderr \"UMV: $piece\"\n" " if {[regexp {^([^:][^:]*):(.*)$} $piece m0 item val]} {\n" " if {[info exists menu_var($item)]} {\n" " set old $menu_var($item)\n" +"#puts stderr \" $old\"\n" " if {$val == \"N/A\"} {\n" " continue\n" " }\n" @@ -2891,6 +2893,11 @@ " global bfont ffont beginner_mode simple_gui_created\n" " global helptext helpremote helplabel\n" "\n" +" # some tweaks...\n" +" if {![info exists menu_var(deny)]} {\n" +" set menu_var(deny) 0\n" +" }\n" +"\n" " set case \"\";\n" " set L_casc \"\"\n" " set L_casc_count 0\n" @@ -3240,7 +3247,14 @@ " global have_labelframes ffont bfont\n" " global props_buttons icon_noadvanced\n" "\n" -" if ![info exists props_accept] {\n" +" if [info exists menu_var(deny)] {\n" +" if {$menu_var(deny) == $unset_str || $menu_var(deny) == 0} {\n" +" set props_accept 1\n" +" } else {\n" +" set props_accept 0\n" +" }\n" +" } else {\n" +" set menu_var(deny) 0\n" " set props_accept 1\n" " }\n" " set prop0_accept $props_accept\n" @@ -3735,15 +3749,18 @@ " global tray_embed tray_running env\n" " global x11vnc_client_file client_tail client_str saved_clients_str\n" " global client_balloon_id\n" -" global bfont sfont ffont\n" +" global bfont sfont snfont ffont\n" " global icon_minimal gui_start_mode\n" " global window_view_posted menu_var x11vnc_gui_geom\n" " set min_x 24\n" " set min_y 24\n" " \n" " set font $bfont\n" +" set mfont $font\n" +"\n" " if {$tray_embed} {\n" " set font $sfont\n" +" set mfont $snfont\n" " }\n" " if {[info exists env(X11VNC_ICON_FONT)]} {\n" " set font $env(X11VNC_ICON_FONT)\n" @@ -3774,15 +3791,16 @@ " label $l -text $lab -borderwidth $bw -font $font\n" " icon_win_cfg 0\n" "\n" +"\n" " set window_view_posted 0\n" " pack $l -fill both -expand 1\n" " set menu \"$l.menu\"\n" " menu $menu -tearoff 0 -postcommand {set window_view_posted 0}\n" -" $menu add command -font $font -label \"Properties\" -command do_props\n" -" $menu add command -font $font -label \"Help\" -command \"menu_help Tray\"\n" +" $menu add command -font $mfont -label \"Properties\" -command do_props\n" +" $menu add command -font $mfont -label \"Help\" -command \"menu_help Tray\"\n" " $menu add separator\n" -" $menu add command -font $font -label \"New Client\" -command do_new_client\n" -" $menu add command -font $font -label \"Disconnect All\" -command do_disconnect_all\n" +" $menu add command -font $mfont -label \"New Client\" -command do_new_client\n" +" $menu add command -font $mfont -label \"Disconnect All\" -command do_disconnect_all\n" " $menu add separator\n" "\n" " set wv \"$menu.casc1\"\n" @@ -3795,9 +3813,9 @@ " -command \"do_var WindowView\" \\\n" " -variable menu_var(WindowView)\n" " }\n" -" $menu add cascade -font $font -label \"Window View\" -menu $wv\n" +" $menu add cascade -font $mfont -label \"Window View\" -menu $wv\n" "\n" -" $menu add command -font $font -label \"Stop x11vnc\" -command clean_icon_exit\n" +" $menu add command -font $mfont -label \"Stop x11vnc\" -command clean_icon_exit\n" "\n" " bind $icon_win \"pmenu $menu %X %Y\"\n" " bind $icon_win \"pmenu $menu %X %Y\"\n" @@ -4704,7 +4722,7 @@ "global all_settings reply_xdisplay always_update\n" "global max_text_height max_text_width\n" "global menu_var unset_str menus_disabled\n" -"global bfont ffont old_labels have_labelframes\n" +"global bfont ffont sfont snfont old_labels have_labelframes\n" "global connected_to_x11vnc\n" "global delay_sleep extra_sleep extra_sleep_split\n" "global cache_all_query_vars\n" diff --git a/x11vnc/x11vnc.1 b/x11vnc/x11vnc.1 index ee330b1..9f2242c 100644 --- a/x11vnc/x11vnc.1 +++ b/x11vnc/x11vnc.1 @@ -2,7 +2,7 @@ .TH X11VNC "1" "July 2005" "x11vnc " "User Commands" .SH NAME x11vnc - allow VNC connections to real X11 displays - version: 0.7.2, lastmod: 2005-07-06 + version: 0.7.2, lastmod: 2005-07-09 .SH SYNOPSIS .B x11vnc [OPTION]... @@ -1378,6 +1378,23 @@ Disable any use of the RECORD extension. This is currently used by the \fB-scrollcopyrect\fR scheme and to monitor X server grabs. .PP +\fB-grab_buster,\fR \fB-nograb_buster\fR +.IP +Some of the use of the RECORD extension can leave a +tiny window for XGrabServer deadlock. This is only if +the whole-server grabbing application expects mouse +or keyboard input before releasing the grab. It is +usually a window manager that does this. x11vnc takes +care to avoid the the problem, but if caught x11vnc +will freeze. Without \fB-grab_buster,\fR the only solution +is to go the physical display and give it some input +to satisfy the grabbing app. Or manually kill and +restart the window manager. With \fB-grab_buster,\fR x11vnc +will fork a helper thread and if x11vnc appears to be +stuck in a grab after a period of time (20-30 sec) +then it will inject some user input: button clicks, +Escape, mouse motion, etc to try to break the grab. +.PP \fB-debug_grabs\fR .IP Turn on debugging info printout with respect to 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