From ae59284aec473bada56b55b3177ad0b4998971dd Mon Sep 17 00:00:00 2001
From: runge <runge>
Date: Wed, 25 May 2005 03:19:23 +0000
Subject:  x11vnc: scrolling: grabserver, autorepeat throttling, mouse wheel,
 fix onetile

---
 ChangeLog              |    4 +
 prepare_x11vnc_dist.sh |    1 +
 x11vnc/ChangeLog       |    5 +
 x11vnc/README          |   76 ++--
 x11vnc/tkx11vnc        |    2 +
 x11vnc/tkx11vnc.h      |    2 +
 x11vnc/x11vnc.1        |   57 ++-
 x11vnc/x11vnc.c        | 1069 +++++++++++++++++++++++++++++++++++++++---------
 8 files changed, 983 insertions(+), 233 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 7bc75cd..0e0f0a4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2005-05-24  Karl Runge <runge@karlrunge.com>
+	* x11vnc: scrollcopyrect: GrabServer detection, autorepeat throttling..
+	* prepare_x11vnc_dist.sh: grep out new libvncserver-config line.
+
 2005-05-23  Karl Runge <runge@karlrunge.com>
 	* configure.ac: malloc(0) is never used, so we don't need the check
 
diff --git a/prepare_x11vnc_dist.sh b/prepare_x11vnc_dist.sh
index ca78be6..9af70cf 100644
--- a/prepare_x11vnc_dist.sh
+++ b/prepare_x11vnc_dist.sh
@@ -7,6 +7,7 @@ cd "$(dirname "$0")"
 mv configure.ac configure.ac.LibVNCServer
 
 cat configure.ac.LibVNCServer | \
+egrep -v 'AC_CONFIG_COMMANDS.*libvncserver-config' | \
 sed -e "s/LibVNCServer, [^,)]*\([(,]\)*/x11vnc, $VERSION\1/g" \
     -e "s/\(contrib\|examples\|vncterm\|libvncclient\|test\|client_examples\)\/Makefile//g" \
     -e "s/LibVNCServer.spec/x11vnc.spec/g" \
diff --git a/x11vnc/ChangeLog b/x11vnc/ChangeLog
index ef2312e..964f754 100644
--- a/x11vnc/ChangeLog
+++ b/x11vnc/ChangeLog
@@ -1,3 +1,8 @@
+2005-05-24  Karl Runge <runge@karlrunge.com>
+	* more -scrollcopyrect: GrabServer detection, autorepeat throttling,
+	  hack to clean screen 3,4,5 Alt_L in a row, mouse wheel detect.
+	* fix bug wrt switching to single_copytile, add Darwin to shm limit.
+
 2005-05-17  Karl Runge <runge@karlrunge.com>
 	* more -scrollcopyrect, -scr_term hacks for terminals.
 	* -wait_ui, -nowait_bog tunables.  push cursor sooner.
diff --git a/x11vnc/README b/x11vnc/README
index 822c343..8b9f1c1 100644
--- a/x11vnc/README
+++ b/x11vnc/README
@@ -1,5 +1,5 @@
 
-x11vnc README file                         Date: Tue May 17 14:39:38 EDT 2005
+x11vnc README file                         Date: Tue May 24 23:49:07 EDT 2005
 
 The following information is taken from these URLs:
 
@@ -4238,7 +4238,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-05-17
+x11vnc: allow VNC connections to real X11 displays. 0.7.2 lastmod: 2005-05-24
 
 x11vnc options:
   -display disp            -auth file             
@@ -4289,7 +4289,8 @@ x11vnc options:
   -noscrollcopyrect        -scr_area n            
   -scr_skip list           -scr_inc list          
   -scr_keys list           -scr_term list         
-  -scr_parms string        -debug_scroll          
+  -scr_keyrepeat lo-hi     -scr_parms string      
+  -debug_scroll            -noxrecord             
   -pointer_mode n          -input_skip n          
   -speeds rd,bw,lat        -debug_pointer         
   -debug_keyboard          -defer time            
@@ -4335,7 +4336,7 @@ libvncserver options:
 
 % x11vnc -help
 
-x11vnc: allow VNC connections to real X11 displays. 0.7.2 lastmod: 2005-05-17
+x11vnc: allow VNC connections to real X11 displays. 0.7.2 lastmod: 2005-05-24
 
 Typical usage is:
 
@@ -4357,8 +4358,12 @@ the name of the machine running x11vnc and N is XXXX - 5900, i.e. usually
 
 By default x11vnc will not allow the screen to be shared and it will exit
 as soon as the client disconnects.  See -shared and -forever below to override
-these protections.  See the FAQ on how to tunnel the VNC connection through
-an encrypted channel such as ssh(1).
+these protections.  See the FAQ for details how to tunnel the VNC connection
+through an encrypted channel such as ssh(1).  In brief:
+
+       ssh -L 5900:localhost:5900 far-host 'x11vnc -localhost -display :0'
+
+       vncviewer -encodings 'copyrect tight zrle hextile' localhost:0
 
 For additional info see: http://www.karlrunge.com/x11vnc/
                     and  http://www.karlrunge.com/x11vnc/#faq
@@ -5215,7 +5220,7 @@ Options:
                        to Keystroke generated scrolls (e.g. Up arrow).  If it
                        is prefixed with "MOUSE:" it only applies to Mouse
                        induced scrolls (e.g. dragging on a scrollbar).
-                       Default: ##Soffice.bin
+                       Default: ##Soffice.bin,##StarOffice
 
 -scr_inc list          Opposite of -scr_skip: this list is consulted first
                        and if there is a match the window will be monitored
@@ -5246,14 +5251,14 @@ Options:
                        never induce scrolling by themselves.
 
 -scr_term list         Yet another cosmetic kludge.  Apply shell/terminal
-                       heuristics to applications matching comma separated list
-                       (same as -scr_skip/-scr_inc).  For example an annoying
-                       transient under scroll detection is if you hit Enter in
-                       a terminal shell with full text window, the solid text
-                       cursor block will be scrolled up.  So for a short time
-                       there are two (or more) block cursors on the screen.
-                       There are similar scenarios, (e.g. an output line is
-                       duplicated).
+                       heuristics to applications matching comma separated
+                       list (same as for -scr_skip/-scr_inc).  For example an
+                       annoying transient under scroll detection is if you
+                       hit Enter in a terminal shell with full text window,
+                       the solid text cursor block will be scrolled up.
+                       So for a short time there are two (or more) block
+                       cursors on the screen.  There are similar scenarios,
+                       (e.g. an output line is duplicated).
 
                        These transients are induced by the approximation of
                        scroll detection (e.g. it detects the scroll, but not
@@ -5268,6 +5273,19 @@ Options:
                        the annoying artifacts.  Use "none" to disable.
                        Default: "term"
 
+-scr_keyrepeat lo-hi   If a key is held down (or otherwise repeats rapidly) and
+                       this induces a rapid sequence of scrolls (e.g. holding
+                       down an Arrow key) the "scrollcopyrect" detection
+                       and overhead may not be able to keep up.  A time per
+                       single scroll estimate is performed and if that estimate
+                       predicts a sustainable scrollrate of keys per second
+                       between "lo" and "hi" then repeated keys will be
+                       DISCARDED to maintain the scrollrate. For example your
+                       key autorepeat may be 25 keys/sec, but for a large
+                       window or slow link only 8 scrolls per second can be
+                       sustained, then roughly 2 out of every 3 repeated keys
+                       will be discarded during this period. Default: "4-20"
+
 -scr_parms string      Set various parameters for the scrollcopyrect mode.
                        The format is similar to that for -wireframe and packed
                        with lots of parameters:
@@ -5314,6 +5332,10 @@ Options:
                        heuristics.  "-ds" is an alias.  Specify it multiple
                        times for more output.
 
+-noxrecord             Disable any use of the RECORD extension.  This is
+                       currently used by the -scrollcopyrect scheme and to
+                       monitor X server grabs.
+
 -pointer_mode n        Various pointer motion update schemes. "-pm" is
                        an alias.  The problem is pointer motion can cause
                        rapid changes on the screen: consider the rapid changes
@@ -5529,8 +5551,8 @@ Options:
                        usage should be very rare, i.e. doing something strange
                        with /dev/fb0.
 
--pipeinput cmd         Another experimental option: it lets you supply
-                       an extern command in "cmd" that x11vnc will pipe
+-pipeinput cmd         Another experimental option: it lets you supply an
+                       external command in "cmd" that x11vnc will pipe
                        all of the user input events to in a simple format.
                        In -pipeinput mode by default x11vnc will not process
                        any of the user input events.  If you prefix "cmd"
@@ -5789,7 +5811,10 @@ Options:
                        scr_inc:list    set -scr_inc to "list"
                        scr_keys:list   set -scr_keys to "list"
                        scr_term:list   set -scr_term to "list"
+                       scr_keyrepeat:str set -scr_keyrepeat to "str"
                        scr_parms:str   set -scr_parms parameters.
+                       noxrecord       disable all use of RECORD extension.
+                       xrecord         enable  use of RECORD extension.
                        pointer_mode:n  set -pointer_mode to n. same as "pm"
                        input_skip:n    set -input_skip to n.
                        speeds:str      set -speeds to str.
@@ -5905,14 +5930,15 @@ Options:
                        noclear_mods clear_keys noclear_keys remap repeat
                        norepeat fb nofb bell nobell sel nosel primary noprimary
                        cursorshape nocursorshape cursorpos nocursorpos cursor
-                       show_cursor noshow_cursor nocursor arrow xfixes noxfixes
-                       xdamage noxdamage xd_area xd_mem alphacut alphafrac
-                       alpharemove noalpharemove alphablend noalphablend
-                       xwarppointer xwarp noxwarppointer noxwarp buttonmap
-                       dragging nodragging wireframe_mode wireframe wf
-                       nowireframe nowf wirecopyrect wcr nowirecopyrect nowcr
-                       scr_area scr_skip scr_inc scr_keys scr_term scr_parms
-                       scrollcopyrect scr noscrollcopyrect noscr pointer_mode
+                       show_cursor noshow_cursor nocursor arrow xfixes
+                       noxfixes xdamage noxdamage xd_area xd_mem alphacut
+                       alphafrac alpharemove noalpharemove alphablend
+                       noalphablend xwarppointer xwarp noxwarppointer
+                       noxwarp buttonmap dragging nodragging wireframe_mode
+                       wireframe wf nowireframe nowf wirecopyrect wcr
+                       nowirecopyrect nowcr scr_area scr_skip scr_inc scr_keys
+                       scr_term scr_keyrepeat scr_parms scrollcopyrect scr
+                       noscrollcopyrect noscr noxrecord xrecord pointer_mode
                        pm input_skip input client_input speeds debug_pointer dp
                        nodebug_pointer nodp debug_keyboard dk nodebug_keyboard
                        nodk deferupdate defer wait_ui wait_bog nowait_bog wait
diff --git a/x11vnc/tkx11vnc b/x11vnc/tkx11vnc
index 5ef148b..d1e27c1 100755
--- a/x11vnc/tkx11vnc
+++ b/x11vnc/tkx11vnc
@@ -172,6 +172,7 @@ Misc
 	nolookup
 	--
 	xtrap
+	xrecord
 	--
 	bg
 	=-C:ignore,exit sigpipe:
@@ -266,6 +267,7 @@ Tuning
 	scr_inc:
 	scr_keys:
 	scr_term:
+	scr_keyrepeat:
 	scr_parms:
 	-- D
 	speeds:
diff --git a/x11vnc/tkx11vnc.h b/x11vnc/tkx11vnc.h
index 7864574..1e0d9d5 100644
--- a/x11vnc/tkx11vnc.h
+++ b/x11vnc/tkx11vnc.h
@@ -178,6 +178,7 @@
 "	nolookup\n"
 "	--\n"
 "	xtrap\n"
+"	xrecord\n"
 "	--\n"
 "	bg\n"
 "	=-C:ignore,exit sigpipe:\n"
@@ -272,6 +273,7 @@
 "	scr_inc:\n"
 "	scr_keys:\n"
 "	scr_term:\n"
+"	scr_keyrepeat:\n"
 "	scr_parms:\n"
 "	-- D\n"
 "	speeds:\n"
diff --git a/x11vnc/x11vnc.1 b/x11vnc/x11vnc.1
index c7757ac..acf8a70 100644
--- a/x11vnc/x11vnc.1
+++ b/x11vnc/x11vnc.1
@@ -2,7 +2,7 @@
 .TH X11VNC "1" "May 2005" "x11vnc " "User Commands"
 .SH NAME
 x11vnc - allow VNC connections to real X11 displays
-         version: 0.7.2, lastmod: 2005-05-17
+         version: 0.7.2, lastmod: 2005-05-24
 .SH SYNOPSIS
 .B x11vnc
 [OPTION]...
@@ -28,9 +28,14 @@ the name of the machine running x11vnc and N is XXXX - 5900, i.e. usually
 .PP
 By default x11vnc will not allow the screen to be shared and it will exit
 as soon as the client disconnects.  See \fB-shared\fR and \fB-forever\fR below to override
-these protections.  See the FAQ on how to tunnel the VNC connection through
-an encrypted channel such as 
+these protections.  See the FAQ for details how to tunnel the VNC connection
+through an encrypted channel such as 
 .IR ssh (1).
+In brief:
+.IP
+ssh -L 5900:localhost:5900 far-host 'x11vnc -localhost -display :0'
+.IP
+vncviewer -encodings 'copyrect tight zrle hextile' localhost:0
 .PP
 For additional info see: http://www.karlrunge.com/x11vnc/
 and  http://www.karlrunge.com/x11vnc/#faq
@@ -1152,7 +1157,7 @@ If a pattern is prefixed with "KEY:" it only applies
 to Keystroke generated scrolls (e.g. Up arrow).  If it
 is prefixed with "MOUSE:" it only applies to Mouse
 induced scrolls (e.g. dragging on a scrollbar).
-Default: ##Soffice.bin
+Default: ##Soffice.bin,##StarOffice
 .PP
 \fB-scr_inc\fR \fIlist\fR
 .IP
@@ -1212,6 +1217,21 @@ from the actual X framebuffer.  This usually reduces
 the annoying artifacts.  Use "none" to disable.
 Default: "term"
 .PP
+\fB-scr_keyrepeat\fR \fIlo-hi\fR
+.IP
+If a key is held down (or otherwise repeats rapidly) and
+this induces a rapid sequence of scrolls (e.g. holding
+down an Arrow key) the "scrollcopyrect" detection
+and overhead may not be able to keep up.  A time per
+single scroll estimate is performed and if that estimate
+predicts a sustainable scrollrate of keys per second
+between "lo" and "hi" then repeated keys will be
+DISCARDED to maintain the scrollrate. For example your
+key autorepeat may be 25 keys/sec, but for a large
+window or slow link only 8 scrolls per second can be
+sustained, then roughly 2 out of every 3 repeated keys
+will be discarded during this period. Default: "4-20"
+.PP
 \fB-scr_parms\fR \fIstring\fR
 .IP
 Set various parameters for the scrollcopyrect mode.
@@ -1262,6 +1282,12 @@ Turn on debugging info printout for the scroll
 heuristics.  "\fB-ds\fR" is an alias.  Specify it multiple
 times for more output.
 .PP
+\fB-noxrecord\fR
+.IP
+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-pointer_mode\fR \fIn\fR
 .IP
 Various pointer motion update schemes. "\fB-pm\fR" is
@@ -1942,8 +1968,14 @@ scr_keys:list   set \fB-scr_keys\fR to "list"
 .IP
 scr_term:list   set \fB-scr_term\fR to "list"
 .IP
+scr_keyrepeat:str set \fB-scr_keyrepeat\fR to "str"
+.IP
 scr_parms:str   set \fB-scr_parms\fR parameters.
 .IP
+noxrecord       disable all use of RECORD extension.
+.IP
+xrecord         enable  use of RECORD extension.
+.IP
 pointer_mode:n  set \fB-pointer_mode\fR to n. same as "pm"
 .IP
 input_skip:n    set \fB-input_skip\fR to n.
@@ -2128,14 +2160,15 @@ noskip_dups add_keysyms noadd_keysyms clear_mods
 noclear_mods clear_keys noclear_keys remap repeat
 norepeat fb nofb bell nobell sel nosel primary noprimary
 cursorshape nocursorshape cursorpos nocursorpos cursor
-show_cursor noshow_cursor nocursor arrow xfixes noxfixes
-xdamage noxdamage xd_area xd_mem alphacut alphafrac
-alpharemove noalpharemove alphablend noalphablend
-xwarppointer xwarp noxwarppointer noxwarp buttonmap
-dragging nodragging wireframe_mode wireframe wf
-nowireframe nowf wirecopyrect wcr nowirecopyrect nowcr
-scr_area scr_skip scr_inc scr_keys scr_term scr_parms
-scrollcopyrect scr noscrollcopyrect noscr pointer_mode
+show_cursor noshow_cursor nocursor arrow xfixes
+noxfixes xdamage noxdamage xd_area xd_mem alphacut
+alphafrac alpharemove noalpharemove alphablend
+noalphablend xwarppointer xwarp noxwarppointer
+noxwarp buttonmap dragging nodragging wireframe_mode
+wireframe wf nowireframe nowf wirecopyrect wcr
+nowirecopyrect nowcr scr_area scr_skip scr_inc scr_keys
+scr_term scr_keyrepeat scr_parms scrollcopyrect scr
+noscrollcopyrect noscr noxrecord xrecord pointer_mode
 pm input_skip input client_input speeds debug_pointer dp
 nodebug_pointer nodp debug_keyboard dk nodebug_keyboard
 nodk deferupdate defer wait_ui wait_bog nowait_bog wait
diff --git a/x11vnc/x11vnc.c b/x11vnc/x11vnc.c
index a05b911..be428f8 100644
--- a/x11vnc/x11vnc.c
+++ b/x11vnc/x11vnc.c
@@ -372,12 +372,12 @@ double xdamage_scheduled_mark = 0.0;
 sraRegionPtr xdamage_scheduled_mark_region = NULL;
 
 /*               date +'lastmod: %Y-%m-%d' */
-char lastmod[] = "0.7.2 lastmod: 2005-05-17";
+char lastmod[] = "0.7.2 lastmod: 2005-05-24";
 int hack_val = 0;
 
 /* X display info */
 
-Display *dpy = 0;		/* the single display screen we connect to */
+Display *dpy = NULL;		/* the single display screen we connect to */
 int scr;
 Window window, rootwin;		/* polled window, root window (usu. same) */
 Visual *default_visual;		/* the default visual (unless -visual) */
@@ -494,6 +494,10 @@ unsigned char *tile_has_xdamage_diff, *tile_row_has_xdamage_diff;
 time_t last_event, last_input = 0, last_client = 0;
 time_t last_keyboard_input = 0, last_pointer_input = 0; 
 double last_keyboard_time = 0.0;
+double last_pointer_time = 0.0;
+double last_pointer_click_time = 0.0;
+double last_pointer_motion_time = 0.0;
+double last_key_to_button_remap_time = 0.0;
 double servertime_diff = 0.0;
 double x11vnc_start = 0.0;
 
@@ -512,7 +516,10 @@ int got_pointer_input = 0;
 int got_keyboard_input = 0;
 int urgent_update = 0;
 int last_keyboard_keycode = 0;
-rfbKeySym last_keysym = 0;
+rfbBool last_rfb_down = FALSE;
+rfbBool last_rfb_key_accepted = FALSE;
+rfbKeySym last_rfb_keysym = 0;
+double last_rfb_keytime = 0.0;
 int fb_copy_in_progress = 0;	
 int drag_in_progress = 0;	
 int shut_down = 0;	
@@ -593,10 +600,12 @@ void initialize_xinerama(void);
 void initialize_xfixes(void);
 void initialize_xdamage(void);
 int valid_window(Window, XWindowAttributes *, int);
+int xtranslate(Window, Window, int, int, int*, int*, Window*, int);
 void create_xdamage_if_needed(void);
 void destroy_xdamage_if_needed(void);
 void mark_for_xdamage(int, int, int, int);
 void mark_region_for_xdamage(sraRegionPtr);
+void set_xdamage_mark(int, int, int, int);
 void initialize_xrandr(void);
 XImage *initialize_xdisplay_fb(void);
 
@@ -638,6 +647,7 @@ void set_wirecopyrect_mode(char *);
 void set_scrollcopyrect_mode(char *);
 void initialize_scroll_matches(void);
 void initialize_scroll_term(void);
+void initialize_max_keyrepeat(void);
 void initialize_scroll_keys(void);
 int try_copyrect(Window, int, int, int, int, int, int, int *, sraRegionPtr,
     double);
@@ -721,7 +731,7 @@ int scale_round(int, double);
 void zero_fb(int, int, int, int);
 void push_black_screen(int);
 void push_sleep(int);
-void refresh_screen(void);
+void refresh_screen(int);
 
 
 /* -- options.h -- */
@@ -842,9 +852,14 @@ char *pad_geometry = NULL;
 time_t pad_geometry_time;
 int use_snapfb = 0;
 
-Display *rdpy_data = 0;		/* Data connection for RECORD */
-Display *rdpy_ctrl = 0;		/* Control connection for RECORD */
+Display *rdpy_data = NULL;		/* Data connection for RECORD */
+Display *rdpy_ctrl = NULL;		/* Control connection for RECORD */
 int use_xrecord = 0;
+int noxrecord = 0;
+
+Display *gdpy_data = NULL;		/* Ditto for GrabServer watcher */
+Display *gdpy_ctrl = NULL;
+int xserver_grabbed = 0;
 
 char *client_connect = NULL;	/* strings for -connect option */
 char *client_connect_file = NULL;
@@ -924,6 +939,10 @@ int debug_scroll = 0;
 double pointer_flush_delay = 0.0;
 double last_scroll_event = 0.0;
 int max_scroll_keyrate = 0;
+double max_keyrepeat_time = 0.0;
+char *max_keyrepeat_str = NULL;
+char *max_keyrepeat_str0 = "4-20";
+int max_keyrepeat_lo = 1, max_keyrepeat_hi = 40;
 enum scroll_types {
 	SCR_NONE = 0,
 	SCR_MOUSE,
@@ -950,7 +969,8 @@ char **scroll_skip_mouse = NULL;
 char *scroll_skip_str = NULL;
 char *scroll_skip_str0 =
 /*	"##Konsole,"	 * no problems, known heuristics do not work */
-	"##Soffice.bin" /* big problems, no clips, scrolls outside area */
+	"##Soffice.bin," /* big problems, no clips, scrolls outside area */
+	"##StarOffice"
 ;
 
 char **scroll_term = NULL;
@@ -1924,7 +1944,7 @@ int try_user_and_display(uid_t uid, char *dpystr) {
 		return 0;
 	} else {
 		/* child */
-		Display *dpy2 = 0;
+		Display *dpy2 = NULL;
 		int rc;
 
 		rc = switch_user_env(uid, name, home, 0); 
@@ -2811,13 +2831,15 @@ int XTRAP_GrabControl_wr(Display *dpy, Bool impervious) {
 	return 0;
 }
 
-void disable_grabserver(Display *in_dpy) {
+void disable_grabserver(Display *in_dpy, int change) {
 	int ok = 0;
 	static int didmsg = 0;
 
 	if (! xtrap_input) {
 		if (XTestGrabControl_wr(in_dpy, True)) {
-			XTRAP_GrabControl_wr(in_dpy, False);
+			if (change) {
+				XTRAP_GrabControl_wr(in_dpy, False);
+			}
 			if (! didmsg) {
 				rfbLog("GrabServer control via XTEST.\n"); 
 				didmsg = 1;
@@ -2835,7 +2857,9 @@ void disable_grabserver(Display *in_dpy) {
 		}
 	} else {
 		if (XTRAP_GrabControl_wr(in_dpy, True)) {
-			XTestGrabControl_wr(in_dpy, False);
+			if (change) {
+				XTestGrabControl_wr(in_dpy, False);
+			}
 			if (! didmsg) {
 				rfbLog("GrabServer control via DEC-XTRAP.\n"); 
 				didmsg = 1;
@@ -2868,11 +2892,17 @@ Bool XRecordQueryVersion_wr(Display *dpy, int *maj, int *min) {
 }
 
 #if LIBVNCSERVER_HAVE_RECORD
-XRecordRange *rr_CA;
-XRecordRange *rr_CW;
+XRecordRange *rr_CA = NULL;
+XRecordRange *rr_CW = NULL;
+XRecordRange *rr_GS = NULL;
 XRecordRange *rr_scroll[10];
 XRecordContext rc_scroll;
 XRecordClientSpec rcs_scroll;
+XRecordRange *rr_grab[10];
+XRecordContext rc_grab;
+XRecordClientSpec rcs_grab;
+
+void record_grab(XPointer, XRecordInterceptData *);
 #endif
 
 int xrecording = 0;
@@ -2885,6 +2915,63 @@ KeySym xrecord_keysym = NoSymbol;
 #define NAMEINFO 2048
 char xrecord_name_info[NAMEINFO];
 
+char *xerror_string(XErrorEvent *error);
+int trap_record_xerror(Display *, XErrorEvent *);
+int trapped_record_xerror;
+XErrorEvent *trapped_record_xerror_event;
+
+void xrecord_grabserver(int start) {
+	XErrorHandler old_handler = NULL;
+	int rc;
+
+	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(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(gdpy_data);
+#endif
+}
+
 void initialize_xrecord(void) {
 	use_xrecord = 0;
 	if (! xrecord_present) {
@@ -2893,10 +2980,19 @@ void initialize_xrecord(void) {
 	if (nofb) {
 		return;
 	}
+	if (noxrecord) {
+		return;
+	}
 #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();
-	if (! rr_CA || ! rr_CW) {
+	rr_GS = XRecordAllocRange();
+	if (!rr_CA || !rr_CW || !rr_GS) {
 		return;
 	}
 	/* protocol request ranges: */
@@ -2906,19 +3002,107 @@ void initialize_xrecord(void) {
 	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);
-	disable_grabserver(rdpy_data);
+	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 (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;
 }
 
 int xrecord_skip_keysym(rfbKeySym keysym) {
@@ -3047,7 +3231,7 @@ winattr_t scr_attr_cache[SCR_ATTR_CACHE];
 double attr_cache_max_age = 1.5;
 
 int lookup_attr_cache(Window win, int *cache_index, int *next_index) {
-	double dnow, t, oldest;
+	double now, t, oldest;
 	int i, old_index = -1, count = 0;
 	Window cwin;
 
@@ -3061,13 +3245,13 @@ int lookup_attr_cache(Window win, int *cache_index, int *next_index) {
 		return 0;
 	}
 
-	dtime0(&dnow);
+	dtime0(&now);
 	for (i=0; i < SCR_ATTR_CACHE; i++) {
 
 		cwin = scr_attr_cache[i].win;
 		t = scr_attr_cache[i].time;
 
-		if (dnow > t + attr_cache_max_age) {
+		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;
@@ -3110,6 +3294,7 @@ if (0) fprintf(stderr, "lookup_attr_cache count: %d\n", count);
 	}
 }
 
+
 typedef struct scroll_event {
 	Window win, frame;
 	int dx, dy;
@@ -3253,8 +3438,9 @@ if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
 		valid = valid_window(src, &attr, 1);
 
 		if (valid) {
-			XTranslateCoordinates(dpy, src, rootwin, 0, 0,
-			    &rx, &ry, &c); 
+			if (!xtranslate(src, rootwin, 0, 0, &rx, &ry, &c, 1)) {
+				valid = 0;
+			}
 		}
 		if (next_index >= 0) {
 			i = next_index;
@@ -3289,7 +3475,7 @@ if (db > 1) fprintf(stderr, "record_CA-%d\n", k++);
 	}
 
 
-if (dba || db) {
+if (0 || dba || db) {
 	double st, dt;
 	st = (double) rec_data->server_time/1000.0;
 	dt = (dnow() - servertime_diff) - st;
@@ -3744,8 +3930,9 @@ if (0) fprintf(stderr, "lookup_attr_cache MISS: %2d %2d 0x%lx %d\n",
     cache_index, next_index, win, valid);
 
 		if (valid) {
-			XTranslateCoordinates(dpy, win, rootwin, 0, 0,
-			    &rx, &ry, &c); 
+			if (!xtranslate(win, rootwin, 0, 0, &rx, &ry, &c, 1)) {
+				valid = 0;
+			}
 		}
 		if (next_index >= 0) {
 			i = next_index;
@@ -3780,7 +3967,7 @@ if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
 	}
 if (db > 1) fprintf(stderr, "record_CW-%d\n", k++);
 
-if (dba || db) {
+if (0 || dba || db) {
 	double st, dt;
 	st = (double) rec_data->server_time/1000.0;
 	dt = (dnow() - servertime_diff) - st;
@@ -3885,18 +4072,101 @@ void record_switch(XPointer ptr, XRecordInterceptData *rec_data) {
 	}
 	XRecordFreeData(rec_data);
 }
+
+void record_grab(XPointer ptr, XRecordInterceptData *rec_data) {
+	xReq *req;
+
+	/* 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 (0) 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 (0) rfbLog("X server Un-Grabbed: %d %.5f\n", xserver_grabbed, now);
+	} else {
+		;
+	}
+	XRecordFreeData(rec_data);
+}
+#endif
+
+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 (0)	dtime0(&d);
+	XFlush(gdpy_ctrl);
+	for (i=0; i<max; i++) {
+		last_val = xserver_grabbed;
+		XRecordProcessReplies(gdpy_data);
+		if (xserver_grabbed != last_val) {
+			cnt++;
+		} else if (i > 2) {
+			break;
+		}
+	}
+	if (cnt) {
+		XFlush(gdpy_ctrl);
+	}
+if (0) {
+	d = dtime(&d);
+fprintf(stderr, "check_xrecord_grabserver: cnt=%d i=%d %.4f\n", cnt, i, d);
+}
 #endif
+}
 
 #if LIBVNCSERVER_HAVE_RECORD
 void shutdown_record_context(XRecordContext rc, int bequiet, int reopen) {
 	int ret1, ret2;
+	int verb = (!bequiet && !quiet);
+
+	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 && !bequiet && !quiet) {
+	if (!ret1 && verb) {
 		rfbLog("XRecordDisableContext(0x%lx) failed.\n", rc);	
 	}
-	ret2 = XRecordFreeContext(rdpy_ctrl, rc_scroll);
-	if (!ret2 && !bequiet && !quiet) {
+	ret2 = XRecordFreeContext(rdpy_ctrl, rc);
+	if (!ret2 && verb) {
 		rfbLog("XRecordFreeContext(0x%lx) failed.\n", rc);	
 	}
 	XFlush(rdpy_ctrl);
@@ -3904,44 +4174,139 @@ void shutdown_record_context(XRecordContext rc, int bequiet, int reopen) {
 	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 = XOpenDisplay(dpystr);
+		rdpy_data = NULL;
 
-		if (! rdpy_data) {
-			rfbLog("Failed to reopen RECORD data connection:"
+		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);
 
-		XCloseDisplay(rdpy_ctrl);
-		rdpy_ctrl = XOpenDisplay(dpystr);
+		disable_grabserver(rdpy_ctrl, 0);
+		XSync(rdpy_ctrl, True);
 
-		if (! rdpy_ctrl) {
-			rfbLog("Failed to reopen RECORD control connection:"
+		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;
 		}
-		if (! bequiet && reopen == 2) {
+		disable_grabserver(rdpy_data, 0);
+
+		if (debug_scroll || (! bequiet && reopen == 2)) {
 			rfbLog("reopened RECORD data and control display"
 			    " connections: %s\n", dpystr);
 		}
-		disable_grabserver(rdpy_ctrl);
-		disable_grabserver(rdpy_data);
 	}
 }
 #endif
 
-char *xerror_string(XErrorEvent *error);
-int trap_record_xerror(Display *, XErrorEvent *);
-int trapped_record_xerror;
-XErrorEvent *trapped_record_xerror_event;
+void check_xrecord_reset(void) {
+	static double last_reset = 0.0;
+	int reset_time  = 120, reset_idle  = 15;
+	int reset_time2 = 600, reset_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;
+		reset_idle = reset_idle2;
+	}
+
+	if (!use_xrecord) {
+		return;
+	}
+	if (xrecording) {
+		return;
+	}
+	if (button_mask) {
+		return;
+	}
+	if (xserver_grabbed) {
+		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 (now < last_reset + reset_time) {
+		return;
+	}
+	if (now < last_pointer_click_time + reset_idle)  {
+		return;
+	}
+	if (now < last_keyboard_time + reset_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) { \
@@ -3956,7 +4321,7 @@ XErrorEvent *trapped_record_xerror_event;
 void xrecord_watch(int start, int setby) {
 	Window focus, wm, c, clast;
 	static double create_time = 0.0;
-	double dnow;
+	double now;
 	static double last_error = 0.0;
 	int rc, db = debug_scroll;
 	int do_shutdown = 0;
@@ -3964,6 +4329,8 @@ void xrecord_watch(int start, int setby) {
 	XErrorHandler old_handler = NULL;
 	static Window last_win = None, last_result = None;
 
+if (0) db = 1;
+
 	if (nofb) {
 		xrecording = 0;
 		return;
@@ -3975,15 +4342,25 @@ void xrecord_watch(int start, int setby) {
 		return;
 	}
 
-	dtime0(&dnow);
-	if (dnow < last_error + 0.5) {
+	dtime0(&now);
+	if (now < last_error + 0.5) {
 		return;
 	}
 
+	if (gdpy_ctrl) {
+		X_LOCK;
+		check_xrecord_grabserver();
+		X_UNLOCK;
+		if (xserver_grabbed) {
+if (db) 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) fprintf(stderr, "XRECORD OFF: %d/%d  %.4f\n", xrecording, setby, dnow - x11vnc_start);
+if (db) fprintf(stderr, "XRECORD OFF: %d/%d  %.4f\n", xrecording, setby, now - x11vnc_start);
 		xrecording = 0;
 		if (! rc_scroll) {
 			xrecord_focus_window = None;
@@ -3994,10 +4371,9 @@ if (db) fprintf(stderr, "XRECORD OFF: %d/%d  %.4f\n", xrecording, setby, dnow -
 			return;
 		}
 
-		if (! do_shutdown && dnow > create_time + shut_time) {
+		if (! do_shutdown && now > create_time + shut_time) {
 			/* XXX unstable if we keep a RECORD going forever */
 			do_shutdown = 1;
-			shut_reopen = 1;
 		}
 
 		SCR_LOCK;
@@ -4009,18 +4385,36 @@ if (db > 1) fprintf(stderr, "=== shutdown-scroll 0x%lx\n", rc_scroll);
 			old_handler = XSetErrorHandler(trap_record_xerror);
 
 			shutdown_record_context(rc_scroll, 0, shut_reopen);
-			if (! use_xrecord) return;
+			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 = dnow;
+				last_error = now;
 			}
+
 			XSetErrorHandler(old_handler);
 			X_UNLOCK;
-
-			rc_scroll = 0;
+			SCR_UNLOCK;
 
 		} else {
 			if (rcs_scroll) {
@@ -4042,11 +4436,16 @@ if (db > 1) fprintf(stderr, "=== disab-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scr
 
 					shutdown_record_context(rc_scroll,
 					    0, reopen_dpys);
-
-					last_error = dnow;
 					rc_scroll = 0;
 
-					if (! use_xrecord) return;
+					last_error = now;
+
+					if (! use_xrecord) {
+						XSetErrorHandler(old_handler);
+						X_UNLOCK;
+						SCR_UNLOCK;
+						return;
+					}
 				}
 				XSetErrorHandler(old_handler);
 				X_UNLOCK;
@@ -4076,7 +4475,7 @@ if (db > 1) fprintf(stderr, "=== disab-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scr
 		rcs_scroll = 0;
 		return;
 	}
-if (db) fprintf(stderr, "XRECORD ON:  %d/%d  %.4f\n", xrecording, setby, dnow - x11vnc_start);
+if (db) fprintf(stderr, "XRECORD ON:  %d/%d  %.4f\n", xrecording, setby, now - x11vnc_start);
 
 	if (xrecording) {
 		return;
@@ -4085,7 +4484,7 @@ if (db) fprintf(stderr, "XRECORD ON:  %d/%d  %.4f\n", xrecording, setby, dnow -
 	if (do_shutdown && rc_scroll) {
 		static int didmsg = 0;
 		/* should not happen... */
-		if (1 || !didmsg) {
+		if (0 || !didmsg) {
 			rfbLog("warning: do_shutdown && rc_scroll\n");
 			didmsg = 1;
 		}
@@ -4192,13 +4591,31 @@ if (db) fprintf(stderr, "--- xrecord_watch: SKIP.\n");
 	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 {
@@ -4224,11 +4641,11 @@ if (db > 1) fprintf(stderr, "=-=   reg-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scr
 
 		if (!XRecordRegisterClients(rdpy_ctrl, rc_scroll, 0,
 		    &rcs_scroll, 1, rr_scroll, 2)) {
-			if (1 || dnow > last_error + 60) {
+			if (1 || now > last_error + 60) {
 				rfbLog("failed to register client 0x%lx with"
 				    " X RECORD context rc_scroll.\n", clast);
 			}
-			last_error = dnow;
+			last_error = now;
 			rcs_scroll = 0;
 			/* continue on for now... */
 		}
@@ -4252,14 +4669,12 @@ if (db) fprintf(stderr, "rc_scroll: 0x%lx\n", rc_scroll);
 	} else if (! rcs_scroll || trapped_record_xerror) {
 		/* try again later */
 		shutdown_record_context(rc_scroll, 0, reopen_dpys);
-		if (! use_xrecord) return;
 		rc_scroll = 0;
-		last_error = dnow;
+		last_error = now;
 
 		XSetErrorHandler(old_handler);
 		X_UNLOCK;
 		SCR_UNLOCK;
-
 		return;
 	}
 
@@ -4285,24 +4700,25 @@ if (db) fprintf(stderr, "rc_scroll: 0x%lx\n", rc_scroll);
 	    (XPointer) xrecord_seq);
 
 	if (!rc || trapped_record_xerror) {
-		if (1 || dnow > last_error + 60) {
+		if (1 || now > last_error + 60) {
 			rfbLog("failed to enable RECORD context "
-			    "rc_scroll: 0x%lx\n", rc_scroll);
+			    "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);
-		if (! use_xrecord) return;
 		rc_scroll = 0;
-		last_error = dnow;
+		last_error = now;
 		xrecording = 0;
 		/* continue on for now... */
 	}
 	XSetErrorHandler(old_handler);
 
 	/* XXX this may cause more problems than it solves... */
-	XFlush(rdpy_data);
+	if (use_xrecord) {
+		XFlush(rdpy_data);
+	}
 
 	X_UNLOCK;
 	SCR_UNLOCK;
@@ -4398,14 +4814,7 @@ void clean_up_exit (int ret) {
 		XEFreeTC(trap_ctx);
 	}
 #endif
-#if LIBVNCSERVER_HAVE_RECORD
-	/* XXX currently blocks: */
-#if 0
-	if (rdpy_ctrl && rc_scroll) XRecordDisableContext(rdpy_ctrl, rc_scroll);
-	if (rdpy_data) XCloseDisplay(rdpy_data);
-	if (rdpy_ctrl) XCloseDisplay(rdpy_ctrl);
-#endif
-#endif
+	/* XXX rdpy_ctrl, etc. cannot close w/o blocking */
 	XCloseDisplay(dpy);
 	X_UNLOCK;
 
@@ -4673,6 +5082,30 @@ int valid_window(Window win, XWindowAttributes *attr_ret, int bequiet) {
 	return ok;
 }
 
+Bool xtranslate(Window src, Window dst, int src_x, int src_y, int *dst_x,
+    int *dst_y, Window *child, int bequiet) {
+	XErrorHandler old_handler;
+	Bool ok = False;
+
+	trapped_xerror = 0;
+	old_handler = XSetErrorHandler(trap_xerror);
+	if (XTranslateCoordinates(dpy, src, dst, src_x, src_y, dst_x,
+	    dst_y, child)) {
+		ok = True;
+	}
+	if (trapped_xerror && trapped_xerror_event) {
+		if (! quiet && ! bequiet) {
+			rfbLog("xtranslate: trapped XError: %s (0x%lx)\n",
+			    xerror_string(trapped_xerror_event), src);
+		}
+		ok = False;
+	}
+	XSetErrorHandler(old_handler);
+	trapped_xerror = 0;
+	
+	return ok;
+}
+
 int wait_until_mapped(Window win) {
 	int ms = 50, waittime = 30;
 	time_t start = time(0);
@@ -8470,7 +8903,7 @@ keyevent_t key_history[KEY_HIST];
 double typing_rate(double time_window, int *repeating) {
 	double dt = 1.0, now = dnow();
 	KeySym key = NoSymbol;
-	int i, idx, cnt = 0, diff_keys = 0;
+	int i, idx, cnt = 0, repeat_keys = 0;
 
 	if (key_history_idx == -1) {
 		if (repeating) {
@@ -8493,15 +8926,17 @@ double typing_rate(double time_window, int *repeating) {
 			break;
 		}
 		cnt++;
-		if (key != NoSymbol && key != key_history[idx].sym) {
-			diff_keys++;
+		if (key == NoSymbol) {
+			key = key_history[idx].sym;
+			repeat_keys = 1;
+		} else if (key == key_history[idx].sym) {
+			repeat_keys++;
 		}
-		key = key_history[idx].sym;
 	}
 
 	if (repeating) {
-		if (! diff_keys && cnt > 4) {
-			*repeating = 1;
+		if (repeat_keys >= 2) {
+			*repeating = repeat_keys;
 		} else {
 			*repeating = 0;
 		}
@@ -8525,18 +8960,21 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
 	int idx, isbutton = 0;
 	allowed_input_t input;
 	time_t now = time(0);
-	double dnow;
+	double tnow;
+	static int skipped_last_down;
 	static rfbBool last_down;
-	static rfbKeySym last_keysym;
+	static rfbKeySym last_keysym = NoSymbol;
+	static rfbKeySym max_keyrepeat_last_keysym = NoSymbol;
+	static double max_keyrepeat_last_time = 0.0;
 
-	dtime0(&dnow);
+	dtime0(&tnow);
 
 	if (debug_keyboard) {
 		char *str;
 		X_LOCK;
 		str = XKeysymToString(keysym);
 		rfbLog("keyboard(%s, 0x%x \"%s\")  %.4f\n", down ? "down":"up",
-		    (int) keysym, str ? str : "null", dnow - x11vnc_start);
+		    (int) keysym, str ? str : "null", tnow - x11vnc_start);
 		X_UNLOCK;
 	}
 
@@ -8552,7 +8990,12 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
 
 	last_down = down;
 	last_keysym = keysym;
-	last_keyboard_time = dnow;
+	last_keyboard_time = tnow;
+
+	last_rfb_down = down;
+	last_rfb_keysym = keysym;
+	last_rfb_keytime = tnow;
+	last_rfb_key_accepted = FALSE;
 
 	if (key_history_idx == -1) {
 		for (idx=0; idx<KEY_HIST; idx++) {
@@ -8568,73 +9011,80 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
 	}
 	key_history[idx].sym = keysym;
 	key_history[idx].down = down;
-	key_history[idx].time = dnow;
-
-	if (0 && max_scroll_keyrate) {
-		/* XXX not working... */
-		static int hlen = 256, hidx = 0;
-		static keyevent_t history[256];
-		static rfbKeySym last_down_skip_keysym = None;
-		double key_dt, keytimes[256];
-		int idx, i, nrep = 0, skip = 0;
-
-		if (!down) {
-			if (last_down_skip_keysym != None) {
-				if (keysym == last_down_skip_keysym) {
-					skip = 1;
-				}
+	key_history[idx].time = tnow;
+
+	if (use_xdamage && down && skip_duplicate_key_events &&
+	    (keysym == XK_Alt_L || keysym == XK_Super_L)) {
+		int i, k, run = 0;
+		double delay = 1.0;
+		for (i=0; i<16; i++) {
+			k = idx - i;
+			if (k < 0) k += KEY_HIST;
+			if (!key_history[k].down) {
+				continue;
 			}
-		} else {
-			if (last_scroll_type == SCR_KEY &&
-			    dnow < last_scroll_event + 1.0) {
-				key_dt = 1.0/max_scroll_keyrate;
-if (0) fprintf(stderr, "key_dt: %.4f\n", key_dt);
-				for (i=0; i<hlen; i++) {
-					idx = hidx - i - 1;
-					if (idx < 0) idx += hlen;
-
-					if (history[idx].sym != keysym) {
-						break;
-					}
-					if (dnow > history[idx].time + 1.5) {
-						break;
-					}
-					if (history[idx].down == down) {
+			if (key_history[k].time < tnow - delay) {
+				break;
+			} else if (key_history[k].sym == XK_Alt_L) {
+				run++;
+			} else if (key_history[k].sym == XK_Super_L) {
+				run++;
+			} else {
+				break;
+			}
+		}
+		if (run == 3) {
+			rfbLog("3*Alt_L, calling: set_xdamage_mark()\n");
+			set_xdamage_mark(0, 0, dpy_x, dpy_y);
+		} else if (run == 4) {
+			rfbLog("4*Alt_L, calling: refresh_screen(0)\n");
+			refresh_screen(0);
+		} else if (run == 5) {
+			rfbLog("5*Alt_L, setting: do_copy_screen\n");
+			do_copy_screen = 1;
+		}
+	}
 
-if (0) fprintf(stderr, "key_dt: %.4f  %d %d %.4f\n", key_dt, history[idx].sym,
-	history[idx].down, history[idx].time - x11vnc_start);
+	if (!down && skipped_last_down) {
+		int db = debug_scroll;
+		if (keysym == max_keyrepeat_last_keysym) {
+			skipped_last_down = 0;
+			if (db) rfbLog("--- scroll keyrate skipping 0x%lx %s "
+			    "%.4f  %.4f\n", keysym, down ? "down":"up  ",
+			    tnow - x11vnc_start, tnow - max_keyrepeat_last_time); 
+			return;
+		}
+	}
+	if (down && max_keyrepeat_time > 0.0) {
+		int skip = 0;
+		int db = debug_scroll;
 
-						keytimes[nrep++] =
-						    history[idx].time;
-					}
-				}
-				if (nrep > 0) {
-					idx = hidx - 1;
-					if (idx < 0) idx += hlen;
-					if (dnow < keytimes[0] + key_dt) {
-						skip = 1;
-					}
-				}
+		if (max_keyrepeat_last_keysym != NoSymbol &&
+		    max_keyrepeat_last_keysym != keysym) {
+			;
+		} else {
+			if (tnow < max_keyrepeat_last_time+max_keyrepeat_time) {
+				skip = 1;
 			}
 		}
+		max_keyrepeat_time = 0.0;
 		if (skip) {
-			rfbLog("--- scroll keyrate skipping 0x%lx %s rep:%d  "
-			    "%.4f\n", keysym, down ? "down":"up", nrep,
-			    down ? dnow - keytimes[0] : dnow - x11vnc_start); 
-			if (down) {
-				last_down_skip_keysym = keysym;
-			}
+			if (db) rfbLog("--- scroll keyrate skipping 0x%lx %s "
+			    "%.4f  %.4f\n", keysym, down ? "down":"up  ",
+			    tnow - x11vnc_start, tnow - max_keyrepeat_last_time); 
+			max_keyrepeat_last_keysym = keysym;
+			skipped_last_down = 1;
 			return;
-		}
-		last_down_skip_keysym = None;
-
-		history[hidx].sym = keysym;
-		history[hidx].time = dnow;
-		history[hidx].down = down;
-		if (++hidx >= hlen) {
-			hidx = 0;
+		} else {
+			if (db) rfbLog("--- scroll keyrate KEEPING  0x%lx %s "
+			    "%.4f  %.4f\n", keysym, down ? "down":"up  ",
+			    tnow - x11vnc_start, tnow - max_keyrepeat_last_time); 
 		}
 	}
+	max_keyrepeat_last_keysym = keysym;
+	max_keyrepeat_last_time = tnow;
+	skipped_last_down = 0;
+	last_rfb_key_accepted = TRUE;
 
 	if (pipeinput_fh != NULL) {
 		pipe_keyboard(down, keysym, client);
@@ -8645,6 +9095,11 @@ if (0) fprintf(stderr, "key_dt: %.4f  %d %d %.4f\n", key_dt, history[idx].sym,
 				last_keyboard_input = now;
 		
 				last_keysym = keysym;
+
+				last_rfb_down = down;
+				last_rfb_keysym = keysym;
+				last_rfb_keytime = tnow;
+
 				got_user_input++;
 				got_keyboard_input++;
 			}
@@ -8665,6 +9120,11 @@ if (0) fprintf(stderr, "key_dt: %.4f  %d %d %.4f\n", key_dt, history[idx].sym,
 	last_keyboard_input = now;
 
 	last_keysym = keysym;
+
+	last_rfb_down = down;
+	last_rfb_keysym = keysym;
+	last_rfb_keytime = tnow;
+
 	got_user_input++;
 	got_keyboard_input++;
 	
@@ -8723,6 +9183,7 @@ if (0) fprintf(stderr, "key_dt: %.4f  %d %d %.4f\n", key_dt, history[idx].sym,
 			rfbLog("keyboard(): remapping keystroke to button %d"
 			    " click\n", button);
 		}
+		dtime0(&last_key_to_button_remap_time);
 
 		X_LOCK;
 		/*
@@ -9145,6 +9606,10 @@ static void update_x11_pointer_position(int x, int y) {
 	}
 	X_UNLOCK;
 
+	if (cursor_x != x || cursor_y != y) {
+		last_pointer_motion_time = dnow();
+	}
+
 	cursor_x = x;
 	cursor_y = y;
 
@@ -9230,6 +9695,10 @@ static void update_x11_pointer_mask(int mask) {
 
 	if (raw_fb && ! dpy) return;	/* raw_fb hack */
 
+	if (mask != button_mask) {
+		last_pointer_click_time = dnow();
+	}
+
 	if (scaling && ! got_scrollcopyrect) {
 		xr_mouse = 0;
 	} else if (nofb) {
@@ -9268,10 +9737,18 @@ if (debug_scroll > 1) fprintf(stderr, "wm_win: 0x%lx\n", mwin);
 				skip = 1;
 			} else {
 				int ok = 0;
+				int btn4 = (1<<3);
+				int btn5 = (1<<4);
+
 				if (near_scrollbar_edge(x, y, w, h, px, py)) {
 					ok = 1;
 				}
-				if (! ok && mwin != None) {
+				if (mask & (btn4|btn5)) {
+					/* scroll wheel mouse */
+					ok = 1;
+				}
+				if (mwin != None) {
+					/* skinny internal window */
 					int w = attr.width;
 					int h = attr.height;
 					if (h > 10 * w || w > 10 * h) {
@@ -9401,6 +9878,7 @@ void pipe_pointer(int mask, int x, int y, rfbClientPtr client) {
 void pointer(int mask, int x, int y, rfbClientPtr client) {
 	allowed_input_t input;
 	int sent = 0, buffer_it = 0;
+	double now;
 
 	if (debug_pointer && mask >= 0) {
 		static int show_motion = -1;
@@ -9455,6 +9933,9 @@ void pointer(int mask, int x, int y, rfbClientPtr client) {
 	if (view_only) {
 		return;
 	}
+
+	now = dnow();
+
 	if (mask >= 0) {
 		/*
 		 * mask = -1 is a special case call from scan_for_updates()
@@ -9468,6 +9949,8 @@ void pointer(int mask, int x, int y, rfbClientPtr client) {
 		got_user_input++;
 		got_pointer_input++;
 		last_pointer_client = client;
+
+		last_pointer_time = now;
 	}
 
 	/*
@@ -10863,8 +11346,9 @@ void check_xevents(void) {
 	if (now > last_sync + 1200) {
 		/* kludge for any remaining event leaks */
 		int bugout = use_xdamage ? 500 : 50;
+		int qlen, i;
 		if (last_sync != 0) {
-			int qlen = XEventsQueued(dpy, QueuedAlready);
+			qlen = XEventsQueued(dpy, QueuedAlready);
 			if (qlen >= bugout) {
 				rfbLog("event leak: %d queued, "
 				    " calling XSync(dpy, True)\n", qlen);  
@@ -10874,6 +11358,20 @@ void check_xevents(void) {
 			}
 		}
 		last_sync = now;
+
+		/* clear these, we don't want any events on them */
+		if (rdpy_ctrl) {
+			qlen = XEventsQueued(rdpy_ctrl, QueuedAlready);
+			for (i=0; i<qlen; i++) {
+				XNextEvent(rdpy_ctrl, &xev);
+			}
+		}
+		if (gdpy_ctrl) {
+			qlen = XEventsQueued(gdpy_ctrl, QueuedAlready);
+			for (i=0; i<qlen; i++) {
+				XNextEvent(gdpy_ctrl, &xev);
+			}
+		}
 	}
 	X_UNLOCK;
 
@@ -11574,7 +12072,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
 		push_black_screen(4);
 	} else if (!strcmp(p, "refresh")) {
 		NOTAPP
-		refresh_screen();
+		refresh_screen(1);
 	} else if (!strcmp(p, "reset")) {
 		NOTAPP
 		do_new_fb(1);
@@ -12459,7 +12957,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
 		    "(if applicable).\n");
 		if (! xtrap_input) {
 			xtrap_input = 1;
-			disable_grabserver(dpy);
+			disable_grabserver(dpy, 1);
 		}
 
 	} else if (!strcmp(p, "noxtrap")) {
@@ -12471,7 +12969,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
 		    "(if applicable).\n");
 		if (xtrap_input) {
 			xtrap_input = 0;
-			disable_grabserver(dpy);
+			disable_grabserver(dpy, 1);
 		}
 
 	} else if (!strcmp(p, "xrandr")) {
@@ -13401,6 +13899,24 @@ char *process_remote_cmd(char *cmd, int stringonly) {
 		    scroll_term_str);
 		initialize_scroll_term();
 
+	} else if (strstr(p, "scr_keyrepeat") == p) {
+		char *s = max_keyrepeat_str;
+		if (!s || *s == '\0') s = max_keyrepeat_str0;
+		COLON_CHECK("scr_keyrepeat:")
+		if (query) {
+			snprintf(buf, bufn, "ans=%s%s%s", p, co, NONUL(s));
+			goto qry;
+		}
+		p += strlen("scr_keyrepeat:");
+		if (max_keyrepeat_str) {
+			free(max_keyrepeat_str);
+		}
+
+		max_keyrepeat_str = strdup(p);
+		rfbLog("remote_cmd: changed -scr_keyrepeat to: %s\n",
+		    max_keyrepeat_str);
+		initialize_max_keyrepeat();
+
 	} else if (strstr(p, "scr_parms") == p) {
 		COLON_CHECK("scr_parms:")
 		if (query) {
@@ -13460,6 +13976,29 @@ char *process_remote_cmd(char *cmd, int stringonly) {
 		rfbLog("remote_cmd: changed -scrollcopyrect mode "
 		    "to: %s\n", NONUL(scroll_copyrect));
 
+	} else if (!strcmp(p, "noxrecord")) {
+		int orig = noxrecord;
+		if (query) {
+			snprintf(buf, bufn, "ans=%s:%d", p, noxrecord);
+			goto qry;
+		}
+		noxrecord = 1;
+		rfbLog("set noxrecord to: %d\n", noxrecord);
+		if (orig != noxrecord) {
+			shutdown_xrecord();
+		}
+	} else if (!strcmp(p, "xrecord")) {
+		int orig = noxrecord;
+		if (query) {
+			snprintf(buf, bufn, "ans=%s:%d", p, !noxrecord);
+			goto qry;
+		}
+		noxrecord = 0;
+		rfbLog("set noxrecord to: %d\n", noxrecord);
+		if (orig != noxrecord) {
+			initialize_xrecord();
+		}
+
 	} else if (strstr(p, "pointer_mode") == p) {
 		int pm;
 		COLON_CHECK("pointer_mode:")
@@ -14476,6 +15015,7 @@ void add_region_xdamage(sraRegionPtr new_region) {
 
 	reg = xdamage_regions[prev_tick];  
 	if (reg != NULL) {
+if (0) fprintf(stderr, "add_region_xdamage: prev_tick: %d reg %p\n", prev_tick, reg);
 		sraRgnOr(reg, new_region);
 	}
 }
@@ -17161,7 +17701,7 @@ XImage *initialize_raw_fb(void) {
 			    DisplayString(dpy));
 			XCloseDisplay(dpy);	/* yow! */
 		}
-		dpy = 0;
+		dpy = NULL;
 	}
 #endif
 
@@ -17580,8 +18120,11 @@ XImage *initialize_xdisplay_fb(void) {
 		int disp_y = DisplayHeight(dpy, scr);
 		Window twin;
 		/* subwins can be a dicey if they are changing size... */
+		trapped_xerror = 0;
+		old_handler = XSetErrorHandler(trap_xerror);
 		XTranslateCoordinates(dpy, window, rootwin, 0, 0, &subwin_x,
 		    &subwin_y, &twin);
+
 		if (subwin_x + wdpy_x > disp_x) {
 			shift = 1;
 			subwin_x = disp_x - wdpy_x - 3;
@@ -17599,8 +18142,6 @@ XImage *initialize_xdisplay_fb(void) {
 			subwin_y = 1;
 		}
 
-		trapped_xerror = 0;
-		old_handler = XSetErrorHandler(trap_xerror);
 		if (shift) {
 			XMoveWindow(dpy, window, subwin_x, subwin_y);
 		}
@@ -18982,10 +19523,9 @@ void blackout_tiles(void) {
 	}
 
 	/* 
-	 * to simplify things drop down to single copy mode, no vcr, etc...
+	 * to simplify things drop down to single copy mode, etc...
 	 */
 	single_copytile = 1;
-
 	/* loop over all tiles. */
 	for (ty=0; ty < ntiles_y; ty++) {
 		for (tx=0; tx < ntiles_x; tx++) {
@@ -19237,12 +19777,15 @@ void push_black_screen(int n) {
 	push_sleep(n);
 }
 
-void refresh_screen(void) {
+void refresh_screen(int push) {
+	int i;
 	if (!screen) {
 		return;
 	}
 	mark_rect_as_modified(0, 0, dpy_x, dpy_y, 1);
-	rfbPE(-1);
+	for (i=0; i<push; i++) {
+		rfbPE(-1);
+	}
 }
 
 /*
@@ -19635,6 +20178,7 @@ void initialize_polling_images(void) {
 			    " to manually\n");
 			rfbLog("shm: delete unattached shm segments.\n");
 			single_copytile_count = i;
+			single_copytile = 1;
 		}
 		tile_shm_count++;
 		if (single_copytile && i >= 1) {
@@ -19757,7 +20301,6 @@ static void hint_updates(void) {
 		}
 	}
 
-
 	for (i=0; i < hint_count; i++) {
 		/* pass update info to vnc: */
 		mark_hint(hint_list[i]);
@@ -21432,7 +21975,7 @@ void set_offset(void) {
 		return;
 	}
 	X_LOCK;
-	XTranslateCoordinates(dpy, window, rootwin, 0, 0, &off_x, &off_y, &w);
+	xtranslate(window, rootwin, 0, 0, &off_x, &off_y, &w, 0);
 	X_UNLOCK;
 }
 
@@ -21880,6 +22423,7 @@ void run_gui(char *gui_xdisplay, int connect_to_x11vnc, int simple_gui,
 		}
 		if (dpy)  {
 			XCloseDisplay(dpy);
+			dpy = NULL;
 		}
 		if (old_xauth) {
 			if (*old_xauth == '\0') {
@@ -22792,6 +23336,30 @@ void initialize_scroll_term(void) {
 	}
 }
 
+void initialize_max_keyrepeat(void) {
+	char *str;
+	int lo, hi;
+
+	if (max_keyrepeat_str != NULL && *max_keyrepeat_str != '\0') {
+		str = max_keyrepeat_str;
+	} else {
+		str = max_keyrepeat_str0;
+	}
+
+	if (sscanf(str, "%d-%d", &lo, &hi) != 2) {
+		rfbLog("skipping invalid -scr_keyrepeat string: %s\n", str);
+		sscanf(max_keyrepeat_str0, "%d-%d", &lo, &hi);
+	}
+	max_keyrepeat_lo = lo;
+	max_keyrepeat_hi = hi;
+	if (max_keyrepeat_lo < 1) {
+		max_keyrepeat_lo = 1;
+	}
+	if (max_keyrepeat_hi > 40) {
+		max_keyrepeat_hi = 40;
+	}
+}
+
 typedef struct saveline {
 	int x0, y0, x1, y1;
 	int shift;
@@ -23382,7 +23950,7 @@ if (n) { \
 	fprintf(stderr, "---PUSH\n"); \
 }
 
-int push_scr_ev(double max_age, int type, int bdpush, int bdx, int bdy,
+int push_scr_ev(double *age, int type, int bdpush, int bdx, int bdy,
     int bdskinny) {
 	Window frame, win, win0;
 	int x, y, w, h, wx, wy, ww, wh, dx, dy;
@@ -23390,12 +23958,16 @@ int push_scr_ev(double max_age, int type, int bdpush, int bdx, int bdy,
 	int nx, ny, nw, nh;
 	int dret = 1, do_fb_push = 0, obscured;
 	int ev, ev_tot = scr_ev_cnt;
-	double tm, dt, st, dnow, waittime = 0.125;
+	double tm, dt, st, waittime = 0.125;
+	double max_age = *age;
 	int db = debug_scroll, rrate = get_read_rate();
 	sraRegionPtr backfill, whole, tmpregion, tmpregion2;
 	int link, latency, netrate;
 	int ypad = 0;
 
+	/* we return the oldest one. */
+	*age = 0.0;
+
 	if (ev_tot == 0) {
 		return dret;
 	}
@@ -23410,8 +23982,6 @@ int push_scr_ev(double max_age, int type, int bdpush, int bdx, int bdy,
 		waittime *= 3;
 	}
 
-	dtime0(&dnow);
-
 	backfill = sraRgnCreate();
 	whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
 
@@ -23426,6 +23996,7 @@ int push_scr_ev(double max_age, int type, int bdpush, int bdx, int bdy,
 if (db) fprintf(stderr, "ypad: %d  dy[0]: %d\n", ypad, scr_ev[0].dy);
 
 	for (ev=0; ev < ev_tot; ev++) {
+		double ag;
 	
 		x   = scr_ev[ev].x;
 		y   = scr_ev[ev].y;
@@ -23444,12 +24015,18 @@ if (db) fprintf(stderr, "ypad: %d  dy[0]: %d\n", ypad, scr_ev[0].dy);
 		nh  = scr_ev[ev].new_h;
 		st  = scr_ev[ev].t;
 
-		if (dabs((dnow - servertime_diff) - st) > max_age) {
-if (db) fprintf(stderr, "push_scr_ev: TOO OLD: %.4f :: (%.4f - %.4f) - %.4f \n", (dnow - servertime_diff) - st, dnow, servertime_diff, st);				
+		ag = (dnow() - servertime_diff) - st;
+		if (ag > *age) {
+			*age = ag;
+		}
+
+		if (dabs(ag) > max_age) {
+if (db) fprintf(stderr, "push_scr_ev: TOO OLD: %.4f :: (%.4f - %.4f) "
+    "- %.4f \n", ag, dnow(), servertime_diff, st);				
 			dret = 0;
 			break;
 		} else {
-if (db) fprintf(stderr, "push_scr_ev: AGE:     %.4f\n", (dnow - servertime_diff) - st);				
+if (db) fprintf(stderr, "push_scr_ev: AGE:     %.4f\n", ag);
 		}
 		if (win != win0) {
 if (db) fprintf(stderr, "push_scr_ev: DIFF WIN: 0x%lx != 0x%lx\n", win, win0);
@@ -23659,7 +24236,7 @@ PUSH_TEST(0);
 			}
 			sraRgnReleaseIterator(iter);
 			dt = dtime(&tm);
-if (0) fprintf(stderr, "  dfc---- dt: %.4f", dt);
+if (db) fprintf(stderr, "  dfc---- dt: %.4f", dt);
 
 		}
 if (db &&  dret) fprintf(stderr, " **** dret=%d", dret);
@@ -23992,6 +24569,9 @@ void mark_region_for_xdamage(sraRegionPtr region) {
 void set_xdamage_mark(int x, int y, int w, int h) {
 	sraRegionPtr region;
 
+	if (! use_xdamage) {
+		return;
+	}
 	mark_for_xdamage(x, y, w, h);
 
 	if (xdamage_scheduled_mark == 0.0) {
@@ -24021,8 +24601,12 @@ int check_xrecord_keys(void) {
 	static int persist_count = 0;
 	double last_scroll, scroll_persist = scr_key_persist;
 	double spin_fac = 1.0, scroll_fac = 2.0;
-	double max_spin;
-	double max_long_spin = 0.3;
+	double max_spin, max_long_spin = 0.3;
+	double set_repeat_in;
+	static double set_repeat = 0.0;
+
+	set_repeat_in = set_repeat;
+	set_repeat = 0.0;
 
 	get_out = 1;
 	if (got_keyboard_input) {
@@ -24034,6 +24618,10 @@ int check_xrecord_keys(void) {
 		get_out = 0;
 	}
 
+	if (set_repeat_in > 0.0 && tnow < last_key_scroll + set_repeat_in) {
+		get_out = 0;
+	}
+
 	if (get_out) {
 		persist_start = 0.0;
 		persist_count = 0;
@@ -24048,9 +24636,14 @@ int check_xrecord_keys(void) {
 
 	max_spin = scr_key_time;
 
-	if (tnow < last_key_scroll + scroll_persist) {
+	if (set_repeat_in > 0.0 && tnow < last_key_scroll + 2*set_repeat_in) {
+		max_spin = 2 * set_repeat_in;
+	} else if (tnow < last_key_scroll + scroll_persist) {
 		max_spin = 1.25*(tnow - last_key_scroll);
-	} else if (xrecord_scroll_keysym(last_keysym)) {
+	} else if (tnow < last_key_to_button_remap_time + scroll_persist) {
+		/* mostly a hack I use for testing -remap key -> btn4/btn5 */
+		max_spin = scroll_persist;
+	} else if (xrecord_scroll_keysym(last_rfb_keysym)) {
 		spin_fac = scroll_fac;
 	}
 	if (max_spin > max_long_spin) {
@@ -24058,13 +24651,13 @@ int check_xrecord_keys(void) {
 	}
 
 	/* XXX use this somehow  */
-	link = link_rate(&latency, &netrate);
+if (0)	link = link_rate(&latency, &netrate);
 
 	gk = gk0 = got_keyboard_input;
 	dtime0(&tm);
 
-if (db) fprintf(stderr, "check_xrecord_keys: BEGIN LOOP: scr_ev_cnt: %d max: %.3f\n",
-    scr_ev_cnt, max_spin);
+if (db) fprintf(stderr, "check_xrecord_keys: BEGIN LOOP: scr_ev_cnt: "
+    "%d max: %.3f  %.4f\n", scr_ev_cnt, max_spin, tm - x11vnc_start);
 
 	while (1) {
 
@@ -24085,6 +24678,10 @@ if (db) fprintf(stderr, "check_xrecord_keys: BEGIN LOOP: scr_ev_cnt: %d max: %.3
 		XFlush(dpy);
 		X_UNLOCK;
 
+		if (set_repeat_in > 0.0) {
+			max_keyrepeat_time = set_repeat_in;
+		}
+
 		if (use_threads) {
 			usleep(1000);
 		} else {
@@ -24096,10 +24693,14 @@ if (db) fprintf(stderr, "check_xrecord_keys: BEGIN LOOP: scr_ev_cnt: %d max: %.3
 		if (got_keyboard_input > gk) {
 			gk = got_keyboard_input;
 			input++;
-			if (xrecord_scroll_keysym(last_keysym)) {
+			if (set_repeat_in) {
+				;
+			} else if (xrecord_scroll_keysym(last_rfb_keysym)) {
 				spin_fac = scroll_fac;
 			}
-if (db) fprintf(stderr, "check_xrecord: more keys: %.3f\n", spin);
+if (0 || db) fprintf(stderr, "check_xrecord: more keys: %.3f  0x%x "
+    " %.4f  %s  %s\n", spin, last_rfb_keysym, last_rfb_keytime - x11vnc_start,
+    last_rfb_down ? "down":"up  ", last_rfb_key_accepted ? "accept":"skip");
 			flush2 = 1;
 			XFlush(dpy);
 		}
@@ -24109,16 +24710,20 @@ if (db) fprintf(stderr, "check_xrecord: more keys: %.3f\n", spin);
 		X_UNLOCK;
 
 		if (spin >= max_spin * spin_fac) {
-if (db) fprintf(stderr, "check_xrecord: SPIN-OUT: %.3f/%.3f\n", spin,
+if (0 || db) fprintf(stderr, "check_xrecord: SPIN-OUT: %.3f/%.3f\n", spin,
     max_spin * spin_fac);
 			fail = 1;
 			break;
 		}
 	}
 
+	max_keyrepeat_time = 0.0;
+
 	if (scr_ev_cnt) {
 		int dret, ev = scr_ev_cnt - 1;
 		int bdx, bdy, bdskinny, bdpush = 0;
+		double max_age = 0.25, age, tm, dt;
+		static double last_scr_ev = 0.0;
 
 		last_wx = scr_ev[ev].win_x;
 		last_wy = scr_ev[ev].win_y;
@@ -24136,10 +24741,34 @@ if (db) fprintf(stderr, "check_xrecord: SPIN-OUT: %.3f/%.3f\n", spin,
 			set_bdpush(SCR_KEY, &last_bdpush, &bdpush);
 		}
 
-		dret = push_scr_ev(0.25, SCR_KEY, bdpush, bdx, bdy, bdskinny);
+		dtime0(&tm);
+		age = max_age;
+		dret = push_scr_ev(&age, SCR_KEY, bdpush, bdx, bdy, bdskinny);
+		dt = dtime(&tm);
 
 		ret = 1 + dret;
 		scr_ev_cnt = 0;
+
+		if (ret == 2 && xrecord_scroll_keysym(last_rfb_keysym)) {
+			int repeating;
+			double time_lo = 1.0/max_keyrepeat_lo;
+			double time_hi = 1.0/max_keyrepeat_hi;
+			double rate = typing_rate(0.0, &repeating);
+if (0 || db) fprintf(stderr, "Typing: dt: %.4f rate: %.1f\n", dt, rate);
+			if (repeating) {
+				/* n.b. the "quantum" is about 1/30 sec. */
+				max_keyrepeat_time = 1.0*dt;
+				if (max_keyrepeat_time > time_lo ||
+				    max_keyrepeat_time < time_hi) {
+					max_keyrepeat_time = 0.0;
+				} else {
+					set_repeat = max_keyrepeat_time;
+if (0 || db) fprintf(stderr, "set max_keyrepeat_time: %.2f\n", max_keyrepeat_time);
+				}
+			}
+		}
+
+		last_scr_ev = dnow();
 	}
 
 	if ((got_one && ret < 2) || persist_count) {
@@ -24197,6 +24826,10 @@ int check_xrecord_mouse(void) {
 	static int want_back_in = 0;
 	int came_back_in;
 
+	int scroll_wheel = 0;
+	int btn4 = (1<<3);
+	int btn5 = (1<<4);
+
 	get_out = 1;
 	if (button_mask) {
 		get_out = 0;
@@ -24226,6 +24859,10 @@ if (0) fprintf(stderr, "check_xrecord_mouse: IN xrecording: %d\n", xrecording);
 	}
 	want_back_in = 0;
 
+	if (button_mask & (btn4|btn5)) {
+		scroll_wheel = 1;
+	}
+
 	/*
 	 * set up times for the various "reputations"
 	 *
@@ -24278,15 +24915,15 @@ if (0) fprintf(stderr, "check_xrecord_mouse: IN xrecording: %d\n", xrecording);
 	last_x = start_x = cursor_x;
 	last_y = start_y = cursor_y;
 
-if (db) fprintf(stderr, "check_xrecord_mouse: BEGIN LOOP: scr_ev_cnt: %d max: %.3f\n",
-    scr_ev_cnt, max_spin[scroll_rep]);
+if (db) fprintf(stderr, "check_xrecord_mouse: BEGIN LOOP: scr_ev_cnt: "
+    "%d max: %.3f  %.4f\n", scr_ev_cnt, max_spin[scroll_rep], tm - x11vnc_start);
 
 	while (1) {
 		double spin_check;
 		if (scr_ev_cnt) {
 			int dret, ev = scr_ev_cnt - 1;
 			int bdpush = 0, bdx, bdy, bdskinny;
-			double tm, dt;
+			double tm, dt, age = 0.35;
 
 			got_one = 1;
 			scrollability(xrecord_ptr_window, SCR_SUCCESS);
@@ -24309,7 +24946,7 @@ if (db) fprintf(stderr, "check_xrecord_mouse: BEGIN LOOP: scr_ev_cnt: %d max: %.
 
 			dtime0(&tm);
 
-			dret = push_scr_ev(0.35, SCR_MOUSE, bdpush, bdx,
+			dret = push_scr_ev(&age, SCR_MOUSE, bdpush, bdx,
 			    bdy, bdskinny);
 			ret = 1 + dret;
 
@@ -24406,6 +25043,10 @@ if (db) fprintf(stderr, "check_xrecord: SPIN-OUT-3: %.3f/%.3f\n", spin, max_long
 			} else if (came_back_in) {
 				dtime0(&button_up_time);
 				doflush = 1;
+			} else if (scroll_wheel) {
+if (db) fprintf(stderr, "check_xrecord: SCROLL-WHEEL-BUTTON-UP-KEEP-GOING:  %.3f/%.3f %d/%d %d/%d\n", spin, max_long[scroll_rep], last_x, last_y, cursor_x, cursor_y);
+				doflush = 1;
+				dtime0(&button_up_time);
 			} else if (last_x == cursor_x && last_y == cursor_y) {
 if (db) fprintf(stderr, "check_xrecord: BUTTON-UP:  %.3f/%.3f %d/%d %d/%d\n", spin, max_long[scroll_rep], last_x, last_y, cursor_x, cursor_y);
 				break;
@@ -26701,6 +27342,7 @@ if (0 && dt > 0.0) fprintf(stderr, "dt: %.5f %.4f\n", dt, dnow() - x11vnc_start)
 		msec = (int) (1000 * 1.75 * bave);
 		if (dts[ndt - nave - 1] > 0.75 * bave) {
 			msec = 1.5 * msec;
+			set_xdamage_mark(0, 0, dpy_x, dpy_y);
 		}
 		if (msec > 1500) {
 			msec = 1500;
@@ -26828,6 +27470,7 @@ if (debug_scroll) fprintf(stderr, "watch_loop: LOOP-BACK: %d\n", ret);
 			check_connect_inputs();		
 			check_padded_fb();		
 			check_xdamage_state();
+			check_xrecord_reset();
 			check_add_keysyms();
 			if (started_as_root) {
 				check_switched_user();
@@ -26952,8 +27595,12 @@ static void print_help(int mode) {
 "\n"
 "By default x11vnc will not allow the screen to be shared and it will exit\n"
 "as soon as the client disconnects.  See -shared and -forever below to override\n"
-"these protections.  See the FAQ on how to tunnel the VNC connection through\n"
-"an encrypted channel such as ssh(1).\n"
+"these protections.  See the FAQ for details how to tunnel the VNC connection\n"
+"through an encrypted channel such as ssh(1).  In brief:\n"
+"\n"
+"       ssh -L 5900:localhost:5900 far-host 'x11vnc -localhost -display :0'\n"
+"\n"
+"       vncviewer -encodings 'copyrect tight zrle hextile' localhost:0\n"
 "\n"
 "For additional info see: http://www.karlrunge.com/x11vnc/\n"
 "                    and  http://www.karlrunge.com/x11vnc/#faq\n"
@@ -27875,6 +28522,19 @@ static void print_help(int mode) {
 "                       the annoying artifacts.  Use \"none\" to disable.\n"
 "                       Default: \"%s\"\n"
 "\n"
+"-scr_keyrepeat lo-hi   If a key is held down (or otherwise repeats rapidly) and\n"
+"                       this induces a rapid sequence of scrolls (e.g. holding\n"
+"                       down an Arrow key) the \"scrollcopyrect\" detection\n"
+"                       and overhead may not be able to keep up.  A time per\n"
+"                       single scroll estimate is performed and if that estimate\n"
+"                       predicts a sustainable scrollrate of keys per second\n"
+"                       between \"lo\" and \"hi\" then repeated keys will be\n"
+"                       DISCARDED to maintain the scrollrate. For example your\n"
+"                       key autorepeat may be 25 keys/sec, but for a large\n"
+"                       window or slow link only 8 scrolls per second can be\n"
+"                       sustained, then roughly 2 out of every 3 repeated keys\n"
+"                       will be discarded during this period. Default: \"%s\"\n"
+"\n"
 "-scr_parms string      Set various parameters for the scrollcopyrect mode.\n"
 "                       The format is similar to that for -wireframe and packed\n"
 "                       with lots of parameters:\n"
@@ -27921,6 +28581,10 @@ static void print_help(int mode) {
 "                       heuristics.  \"-ds\" is an alias.  Specify it multiple\n"
 "                       times for more output.\n"
 "\n"
+"-noxrecord             Disable any use of the RECORD extension.  This is\n"
+"                       currently used by the -scrollcopyrect scheme and to\n"
+"                       monitor X server grabs.\n"
+"\n"
 "-pointer_mode n        Various pointer motion update schemes. \"-pm\" is\n"
 "                       an alias.  The problem is pointer motion can cause\n"
 "                       rapid changes on the screen: consider the rapid changes\n"
@@ -28403,7 +29067,10 @@ static void print_help(int mode) {
 "                       scr_inc:list    set -scr_inc to \"list\"\n"
 "                       scr_keys:list   set -scr_keys to \"list\"\n"
 "                       scr_term:list   set -scr_term to \"list\"\n"
+"                       scr_keyrepeat:str set -scr_keyrepeat to \"str\"\n"
 "                       scr_parms:str   set -scr_parms parameters.\n"
+"                       noxrecord       disable all use of RECORD extension.\n"
+"                       xrecord         enable  use of RECORD extension.\n"
 "                       pointer_mode:n  set -pointer_mode to n. same as \"pm\"\n"
 "                       input_skip:n    set -input_skip to n.\n"
 "                       speeds:str      set -speeds to str.\n"
@@ -28519,14 +29186,15 @@ static void print_help(int mode) {
 "                       noclear_mods clear_keys noclear_keys remap repeat\n"
 "                       norepeat fb nofb bell nobell sel nosel primary noprimary\n"
 "                       cursorshape nocursorshape cursorpos nocursorpos cursor\n"
-"                       show_cursor noshow_cursor nocursor arrow xfixes noxfixes\n"
-"                       xdamage noxdamage xd_area xd_mem alphacut alphafrac\n"
-"                       alpharemove noalpharemove alphablend noalphablend\n"
-"                       xwarppointer xwarp noxwarppointer noxwarp buttonmap\n"
-"                       dragging nodragging wireframe_mode wireframe wf\n"
-"                       nowireframe nowf wirecopyrect wcr nowirecopyrect nowcr\n"
-"                       scr_area scr_skip scr_inc scr_keys scr_term scr_parms\n"
-"                       scrollcopyrect scr noscrollcopyrect noscr pointer_mode\n"
+"                       show_cursor noshow_cursor nocursor arrow xfixes\n"
+"                       noxfixes xdamage noxdamage xd_area xd_mem alphacut\n"
+"                       alphafrac alpharemove noalpharemove alphablend\n"
+"                       noalphablend xwarppointer xwarp noxwarppointer\n"
+"                       noxwarp buttonmap dragging nodragging wireframe_mode\n"
+"                       wireframe wf nowireframe nowf wirecopyrect wcr\n"
+"                       nowirecopyrect nowcr scr_area scr_skip scr_inc scr_keys\n"
+"                       scr_term scr_keyrepeat scr_parms scrollcopyrect scr\n"
+"                       noscrollcopyrect noscr noxrecord xrecord pointer_mode\n"
 "                       pm input_skip input client_input speeds debug_pointer dp\n"
 "                       nodebug_pointer nodp debug_keyboard dk nodebug_keyboard\n"
 "                       nodk deferupdate defer wait_ui wait_bog nowait_bog wait\n"
@@ -28667,6 +29335,7 @@ static void print_help(int mode) {
 		scrollcopyrect_min_area,
 		scroll_skip_str0 ? scroll_skip_str0 : "(empty)",
 		scroll_term_str0,
+		max_keyrepeat_str0,
 		SCROLL_COPYRECT_PARMS,
 		pointer_mode_max, pointer_mode,
 		ui_skip,
@@ -28825,6 +29494,8 @@ static int limit_shm(void) {
 				limit = 1;
 			}
 		}
+	} else if (!strcmp(UT.sysname, "Darwin")) {
+		limit = 1;
 	}
 	if (limit && ! quiet) {
 		fprintf(stderr, "reducing shm usage on %s %s (adding "
@@ -29455,12 +30126,20 @@ int main(int argc, char* argv[]) {
 		} else if (!strcmp(arg, "-scr_keys")) {
 			CHECK_ARGC
 			scroll_key_list_str = strdup(argv[++i]);
+		} else if (!strcmp(arg, "-scr_term")) {
+			CHECK_ARGC
+			scroll_term_str = strdup(argv[++i]);
+		} else if (!strcmp(arg, "-scr_keyrepeat")) {
+			CHECK_ARGC
+			max_keyrepeat_str = strdup(argv[++i]);
 		} else if (!strcmp(arg, "-scr_parms")) {
 			CHECK_ARGC
 			scroll_copyrect_str = strdup(argv[++i]);
 		} else if (!strcmp(arg, "-debug_scroll")
 		    || !strcmp(arg, "-ds")) {
 			debug_scroll++;
+		} else if (!strcmp(arg, "-noxrecord")) {
+			noxrecord = 1;
 		} else if (!strcmp(arg, "-pointer_mode")
 		    || !strcmp(arg, "-pm")) {
 			char *p, *s;
@@ -29868,6 +30547,7 @@ int main(int argc, char* argv[]) {
 	}
 	initialize_scroll_matches();
 	initialize_scroll_term();
+	initialize_max_keyrepeat();
 
 	/* increase rfbwait if threaded */
 	if (use_threads && ! got_rfbwait) {
@@ -30384,7 +31064,7 @@ int main(int argc, char* argv[]) {
 	 * input is not processed) we tell the server to process our
 	 * requests during all grabs:
 	 */
-	disable_grabserver(dpy);
+	disable_grabserver(dpy, 0);
 
 	/* check for RECORD */
 	if (! XRecordQueryVersion_wr(dpy, &maj, &min)) {
@@ -30508,7 +31188,6 @@ int main(int argc, char* argv[]) {
 
 	initialize_signals();
 
-
 	initialize_speeds();
 
 	initialize_keyboard_and_pointer();
@@ -30562,5 +31241,3 @@ int main(int argc, char* argv[]) {
 #undef argc
 #undef argv
 }
-
-
-- 
cgit v1.2.3

