From dede7749226de8d24e6bb775582f3e3243d2e729 Mon Sep 17 00:00:00 2001
From: runge <runge>
Date: Mon, 19 Jan 2004 21:02:32 +0000
Subject:  handle mouse button number mismatch  improved pointer input handling
 during drags, etc.  somewhat faster copy_tiles() -> copy_tiles()  x11vnc
 options -buttonmap -old_pointer -old_copytile

---
 ChangeLog         |   6 +
 contrib/ChangeLog |  10 +
 contrib/x11vnc.c  | 995 ++++++++++++++++++++++++++++++++++++++++++++++++++----
 3 files changed, 938 insertions(+), 73 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 36994e9..a3a12e4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2004-01-19  Karl Runge <runge@karlrunge.com>
+	* handle mouse button number mismatch
+	* improved pointer input handling during drags, etc.
+	* somewhat faster copy_tiles() -> copy_tiles()
+	* x11vnc options -buttonmap -old_pointer -old_copytile
+
 2004-01-16  Johannes E. Schindelin  <Johannes.Schindelin@gmx.de>
 	* compile fix for cygwin
 
diff --git a/contrib/ChangeLog b/contrib/ChangeLog
index 137be5c..1ac58ad 100644
--- a/contrib/ChangeLog
+++ b/contrib/ChangeLog
@@ -1,3 +1,13 @@
+2004-01-19  Karl Runge <runge@karlrunge.com>
+	* improvements to pointer event handling primarily during window
+	  dragging.  check_user_input() for non-threaded and pointer()
+	  for threaded.  Revert to old way via -old_pointer option.
+	* some memory I/O improvement by using copy_tiles() instead
+	  of copy_tile().  New one does rows of tiles at same time.
+	  Revert to old way via -old_copytile.
+	* handle case of more mouse buttons on client than on X server.
+	* added -buttonmap option for finer control over button differences.
+
 2004-01-09  Karl Runge <runge@karlrunge.com>
 	* options -allow / -localhost for simple IP based access screening
 	* option -nodragging to skip all screen updates during mouse drags
diff --git a/contrib/x11vnc.c b/contrib/x11vnc.c
index 15bbf6c..0e6a7b7 100644
--- a/contrib/x11vnc.c
+++ b/contrib/x11vnc.c
@@ -125,11 +125,13 @@ int subwin = 0;
 int indexed_colour = 0;
 
 XImage *tile;
+XImage **tile_row;		/* for all possible row runs */
 XImage *scanline;
 XImage *fullscreen;
 int fs_factor = 0;
 
 XShmSegmentInfo tile_shm;
+XShmSegmentInfo *tile_row_shm;	/* for all possible row runs */
 XShmSegmentInfo scanline_shm;
 XShmSegmentInfo fullscreen_shm;
 
@@ -182,10 +184,11 @@ int local_cursor = 1;		/* whether the viewer draws a local cursor */
 int show_mouse = 0;		/* display a cursor for the real mouse */
 int show_root_cursor = 0;	/* show X when on root background */
 int show_dragging = 1;		/* process mouse movement events */
-int delay_refresh = 0;		/* when buttons are pressed, delay refreshes */
-int ui_dropthru = 10;		/* see watchloop.  negative means ignore input */
 int watch_bell = 1;		/* watch for the bell using XKEYBOARD */
 
+int old_pointer = 0;		/* use the old way of updating the pointer */
+int old_copytile = 0;		/* use the old way copy_tile() */
+
 int using_shm = 1;		/* whether mit-shm is used */
 int flip_byte_order = 0;	/* sometimes needed when using_shm = 0 */
 /*
@@ -201,12 +204,13 @@ int take_naps = 0;
 int naptile = 3;	/* tile change threshold per poll to take a nap */
 int napfac = 4;		/* time = napfac*waitms, cut load with extra waits */
 int napmax = 1500;	/* longest nap in ms. */
+int ui_skip = 10;	/* see watchloop.  negative means ignore input */
 
 int nap_ok = 0, nap_diff_count = 0;
 time_t last_event, last_input;
 
 /* tile heuristics: */
-double fs_frac = 0.6;	/* threshold tile fraction to do fullscreen updates. */
+double fs_frac = 0.75;	/* threshold tile fraction to do fullscreen updates. */
 int use_hints = 1;	/* use the krfb scheme of gluing tiles together. */
 int tile_fuzz = 2;	/* tolerance for suspecting changed tiles touching */
 			/* a known changed tile. */
@@ -225,9 +229,15 @@ int count = 0;			/* indicates which scan pattern we are on  */
 
 int cursor_x, cursor_y;		/* x and y from the viewer(s) */
 int got_user_input = 0;
+int got_pointer_input = 0;
+int got_keyboard_input = 0;
+int scan_in_progress = 0;	
+int fb_copy_in_progress = 0;	
 int shut_down = 0;	
 
 int quiet = 0;
+double dtime(double *);
+
 #if defined(LIBVNCSERVER_X11VNC_THREADED) && ! defined(X11VNC_THREADED)
 #define X11VNC_THREADED
 #endif
@@ -266,6 +276,7 @@ void shm_delete(XShmSegmentInfo *);
 
 int exit_flag = 0;
 void clean_up_exit (int ret) {
+	int i;
 	exit_flag = 1;
 
 	/* remove the shm areas: */
@@ -273,6 +284,13 @@ void clean_up_exit (int ret) {
 	shm_clean(&scanline_shm, scanline);
 	shm_clean(&fullscreen_shm, fullscreen);
 
+	for(i=1; i<=ntiles_x; i++) {
+		shm_clean(&tile_row_shm[i], tile_row[i]);
+		if (old_copytile && i == 1) {
+			break;
+		}
+	}
+
 	X_LOCK;
 	XTestDiscard(dpy);
 	X_UNLOCK;
@@ -280,10 +298,17 @@ void clean_up_exit (int ret) {
 	exit(ret);
 }
 
+/*
+ * General problem handler
+ */
 void interrupted (int sig) {
+	int i;
 	if (exit_flag) {
+		exit_flag++;
 		if (use_threads) {
 			usleep2(250 * 1000);
+		} else if (exit_flag <= 2) {
+			return;
 		}
 		exit(4);
 	}
@@ -300,6 +325,18 @@ void interrupted (int sig) {
 	shm_delete(&tile_shm);
 	shm_delete(&scanline_shm);
 	shm_delete(&fullscreen_shm);
+
+	/* 
+	 * Here we have to clean up quite a few shm areas for all
+	 * the possible tile row runs (40 for 1280), not as robust
+	 * as one might like... sometimes need to run ipcrm(1). 
+	 */
+	for(i=1; i<=ntiles_x; i++) {
+		shm_clean(&tile_row_shm[i], tile_row[i]);
+		if (old_copytile && i == 1) {
+			break;
+		}
+	}
 	if (sig) {
 		exit(2);
 	}
@@ -308,10 +345,12 @@ void interrupted (int sig) {
 XErrorHandler   Xerror_def;
 XIOErrorHandler XIOerr_def;
 int Xerror(Display *d, XErrorEvent *error) {
+	X_UNLOCK;
 	interrupted(0);
 	return (*Xerror_def)(d, error);
 }
 int XIOerr(Display *d) {
+	X_UNLOCK;
 	interrupted(0);
 	return (*XIOerr_def)(d);
 }
@@ -339,6 +378,10 @@ void client_gone(rfbClientPtr client) {
 	}
 }
 
+/*
+ * Simple routine to limit access via string compare.  A power user will
+ * want to compile libvncserver with libwrap support and use /etc/hosts.allow.
+ */
 int check_access(char *addr) {
 	int allowed = 0;
 	char *p, *list;
@@ -361,7 +404,6 @@ int check_access(char *addr) {
 			allowed = 1;
 
 		} else if(!strcmp(p,"localhost") && !strcmp(addr,"127.0.0.1")) {
-			/* also do host lookup someday... */
 			allowed = 1;
 		}
 		p = strtok(NULL, ",");
@@ -370,6 +412,9 @@ int check_access(char *addr) {
 	return allowed;
 }
 
+/*
+ * libvncserver callback for when a new client connects
+ */
 enum rfbNewClientAction new_client(rfbClientPtr client) {
 	static accepted_client = 0;
 	last_event = last_input = time(0);
@@ -465,6 +510,10 @@ void DebugXTestFakeKeyEvent(Display* dpy, KeyCode keysym, Bool down, time_t cur_
 	XTestFakeKeyEvent(dpy,keysym,down,cur_time);
 }
 
+/*
+ * Uncomment the next line to aid in debugging keymapping problems.
+ */
+
 /* #define XTestFakeKeyEvent DebugXTestFakeKeyEvent */
 
 void tweak_mod(signed char mod, rfbBool down) {
@@ -538,7 +587,8 @@ static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr
 }
 
 /*
- * key event handler
+ * key event handler.  See the above functions for contortions for
+ * running under -modtweak.
  */
 static void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
 	KeyCode k;
@@ -572,32 +622,188 @@ static void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
 
 		last_event = last_input = time(0);
 		got_user_input++;
+		got_keyboard_input++;
 	}
 
 	X_UNLOCK;
 }
 
 /*
- * pointer event handler
+ * pointer event handling routines.
  */
-static void pointer(int mask, int x, int y, rfbClientPtr client) {
+MUTEX(pointerMutex);
+#define MAX_BUTTONS 5
+int pointer_map[MAX_BUTTONS+1];
+int num_buttons = -1;
+char *pointer_remap = NULL;
+void update_pointer(int, int, int);
+
+void init_pointer(void) {
+	unsigned char map[MAX_BUTTONS];
 	int i;
+	/*
+	 * This routine counts the number of pointer buttons on the X
+	 * server (to avoid problems, even crashes, if a client has more
+	 * buttons).  And also initializes any pointer button remapping
+	 * from -buttonmap option.
+	 */
+	
+	X_LOCK;
+	num_buttons = XGetPointerMapping(dpy, map, MAX_BUTTONS);
+	X_UNLOCK;
+	if (num_buttons < 0) {
+		num_buttons = 0;
+	}
+
+	/* FIXME: should use info in map[] */
+	for (i=1; i<= MAX_BUTTONS; i++) {
+		pointer_map[i] = i;
+	}
+
+	if (pointer_remap) {
+		/* -buttonmap, format is like: 12-21:2 */
+		char *p, *q;	
+		int n;
+		if ((p = strchr(pointer_remap, ':')) != NULL) {
+			/* undocumented max button number */
+			n = atoi(p+1);	
+			if (n < num_buttons || num_buttons == 0) {
+				num_buttons = n;
+			}
+		}
+		if ((q = strchr(pointer_remap, '-')) != NULL) {
+			/*
+			 * The '-' separates the 'from' and 'to' lists,
+			 * then it is kind of like tr(1).  
+			 */
+			char str[2];
+			int from, to;
+			p = pointer_remap;
+			q++;
+			i = 0;
+			str[1] = '\0';
+			while (*p != '-') {
+				str[0] = *p;
+				from = atoi(str);
+				str[0] = *(q+i);
+				to   = atoi(str);
+				rfbLog("button remap: %d -> %d using: "
+				    "%s\n", from, to, pointer_remap);
+				pointer_map[from] = to;
+				p++;
+				i++;
+			}
+		}
+	}
+}
+
+/*
+ * Actual callback from libvncserver when it gets a pointer event.
+ */
+static void pointer(int mask, int x, int y, rfbClientPtr client) {
 
 	if (view_only) {
 		return;
 	}
 
-	got_user_input++;
+	if (num_buttons < 0) {
+		init_pointer();
+	}
 
-	/* if any button is pressed, then do not refresh the screen */
-	if (! show_dragging) {
-		if (mask) {
-			delay_refresh = 1;
-		} else {
-			delay_refresh = 0;
+	if (mask >= 0) {
+		/*
+		 * mask = -1 is a special case call from scan_for_updates()
+		 * to flush the event queue; there is no real pointer event.
+		 */
+		got_user_input++;
+		got_pointer_input++;
+	}
+
+	/*
+	 * The following is hopefully an improvement wrt response during
+	 * pointer user input (window drags) for the threaded case.
+	 * See check_user_input() for the more complicated things we do
+	 * in the non-threaded case.
+	 */
+	if (use_threads && ! old_pointer) {
+#		define NEV 32
+		/* storage for the event queue */
+		static int mutex_init = 0;
+		static int nevents = 0;
+		static int ev[NEV][3];
+		int i;
+		/* timer things */
+		static double dt = 0.0, tmr = 0.0, maxwait = 0.4;
+
+		if (! mutex_init) {
+			INIT_MUTEX(pointerMutex);
+			mutex_init = 1;
+		}
+
+		LOCK(pointerMutex);
+
+		/* 
+		 * If the framebuffer is being copied in another thread
+		 * (scan_for_updates()), we will queue up to 32 pointer
+		 * events for later.  The idea is by delaying these input
+		 * events, the screen is less likely to change during the
+		 * copying period, and so will give rise to less window
+		 * "tearing".
+		 *
+		 * Tearing is not completely eliminated because we do
+		 * not suspend work in the other libvncserver threads.
+		 * Maybe that is a possibility with a mutex...
+		 */
+		if (fb_copy_in_progress && mask >= 0) {
+			/* 
+			 * mask = -1 is an all-clear signal from
+			 * scan_for_updates().
+			 *
+			 * dt is a timer in seconds; we only queue for so long.
+			 */
+			dt += dtime(&tmr);
+
+			if (nevents < NEV && dt < maxwait) {
+				i = nevents++;
+				ev[i][0] = mask;
+				ev[i][1] = x;
+				ev[i][2] = y;
+				UNLOCK(pointerMutex);
+				return;
+			}
+		}
+
+		/* time to send the queue */
+		for (i=0; i<nevents; i++) {
+			update_pointer(ev[i][0], ev[i][1], ev[i][2]);
 		}
+		if (nevents && dt > maxwait) {
+			X_LOCK;
+			XFlush(dpy);	
+			X_UNLOCK;
+		}
+		nevents = 0;	/* reset everything */
+		tmr = 0.0;
+		dt = 0.0;
+		dtime(&tmr);
+
+		UNLOCK(pointerMutex);
+	}
+	if (mask < 0) {		/* -1 just means flush the event queue */
+		return;
 	}
 
+	/* update the X display with the event: */
+	update_pointer(mask, x, y);
+}
+
+/*
+ * Send a pointer event to the X server.
+ */
+
+void update_pointer(int mask, int x, int y) {
+	int i, mb;
+
 	X_LOCK;
 
 	XTestFakeMotionEvent(dpy, scr, x + off_x, y + off_y, CurrentTime);
@@ -607,25 +813,40 @@ static void pointer(int mask, int x, int y, rfbClientPtr client) {
 
 	last_event = last_input = time(0);
 
-	for (i=0; i < 5; i++) {
+	for (i=0; i < MAX_BUTTONS; i++) {
+		/* look for buttons that have be clicked or released: */
 		if ( (button_mask & (1<<i)) != (mask & (1<<i)) ) {
-			XTestFakeButtonEvent(dpy, i+1, 
+			mb = pointer_map[i+1];
+			if (num_buttons && mb > num_buttons) {
+				rfbLog("ignoring mouse button out of bounds: %d"
+				    ">%d mask: 0x%x -> 0x%x\n", mb, num_buttons,
+				    button_mask, mask);
+				continue;
+			}
+			XTestFakeButtonEvent(dpy, mb, 
 			    (mask & (1<<i)) ? True : False, CurrentTime);
 		}
 	}
 
 	if (nofb) {
+		/* 
+		 * nofb is for, e.g. Win2VNC, where fastest pointer
+		 * updates are desired.
+		 */
 		XFlush(dpy);
 	}
 
 	X_UNLOCK;
 
-	/* remember the button state for next time: */
+	/*
+	 * Remember the button state for next time and also for the
+	 * -nodragging case:
+	 */
 	button_mask = mask;
 }
 
 /*
- * bell event handling
+ * Bell event handling.  Requires XKEYBOARD extension.
  */
 #ifdef LIBVNCSERVER_HAVE_XKEYBOARD
 
@@ -646,6 +867,10 @@ void initialize_watch_bell() {
 	}
 }
 
+/*
+ * We call this periodically to process any bell events that have 
+ * taken place.
+ */
 void watch_bell_event() {
 	XEvent xev;
 	XkbAnyEvent *xkb_ev;
@@ -677,7 +902,7 @@ void watch_bell_event() {}
 void mark_hint(hint_t);
 
 /*
- * here begins a bit of a mess to experiment with multiple cursors ...
+ * Here begins a bit of a mess to experiment with multiple cursors ...
  */
 typedef struct cursor_info {
 	char *data;	/* data and mask pointers */
@@ -844,7 +1069,7 @@ void restore_mouse_patch() {
 }
 
 /*
- * Descends windows at pointer until the window cursor matches the current 
+ * Descends window tree at pointer until the window cursor matches the current 
  * cursor.  So far only used to detect if mouse is on root background or not.
  * (returns 0 in that case, 1 otherwise).
  *
@@ -1044,6 +1269,9 @@ void update_mouse(void) {
 	draw_mouse(root_x - off_x, root_y - off_y, which, 1);
 }
 
+/*
+ * For the subwin case follows the window if it is moved.
+ */
 void set_offset(void) {
 	Window w;
 	if (! subwin) {
@@ -1054,6 +1282,10 @@ void set_offset(void) {
 	X_UNLOCK;
 }
 
+/*
+ * Some handling of 8bpp PseudoColor colormaps.  Called for initializing
+ * the clients and dynamically if -flashcmap is specified.
+ */
 #define NCOLOR 256
 void set_colormap(void) {
 	static int first = 1;
@@ -1106,7 +1338,7 @@ void set_colormap(void) {
 
 		c = window;
 		while (c && tries++ < 16) {
-			/* XQueryTree somehow? */
+			/* XXX XQueryTree somehow? */
 			XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &m);
 			if (c && XGetWindowAttributes(dpy, c, &attr)) {
 				if (attr.colormap && attr.map_installed) {
@@ -1166,8 +1398,9 @@ void set_colormap(void) {
 
 /*
  * Presumably under -nofb the clients will never request the framebuffer.
- * But we have gotten such a request... so let's just give them the current
- * view on the display.  n.b. x2vnc requests a 1x1 pixel for some workaround. 
+ * But we have gotten such a request... so let's just give them the
+ * current view on the display.  n.b. x2vnc and perhaps win2vnc requests
+ * a 1x1 pixel for some workaround so sadly this evidently always happens.
  */
 void nofb_hook(rfbClientPtr cl) {
 	static int loaded_fb = 0;
@@ -1179,6 +1412,7 @@ void nofb_hook(rfbClientPtr cl) {
 	fb = XGetImage(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, ZPixmap);
 	screen->frameBuffer = fb->data;
 	loaded_fb = 1;
+	screen->displayHook = NULL;
 }
 
 /*
@@ -1242,6 +1476,7 @@ void initialize_screen(int *argc, char **argv, XImage *fb) {
 		    = fb->blue_mask >> screen->rfbServerFormat.blueShift;
 	}
 
+	/* nofb is for pointer/keyboard only handling.  */
 	if (nofb) {
 		screen->frameBuffer = NULL;
 		screen->displayHook = nofb_hook;
@@ -1249,6 +1484,7 @@ void initialize_screen(int *argc, char **argv, XImage *fb) {
 		screen->frameBuffer = fb->data; 
 	}
 
+	/* called from inetd, we need to treat stdio as our socket */
 	if (inetd) {
 		int fd = dup(0);
 		if (fd < 3) {
@@ -1313,6 +1549,11 @@ void initialize_tiles() {
 		malloc((size_t) (ntiles * sizeof(unsigned char)));
 	tile_region = (region_t *) malloc((size_t) (ntiles * sizeof(region_t)));
 
+	tile_row = (XImage **)
+		malloc((size_t) ((ntiles_x + 1) * sizeof(XImage *)));
+	tile_row_shm = (XShmSegmentInfo *)
+		malloc((size_t) ((ntiles_x + 1) * sizeof(XShmSegmentInfo)));
+
 	/* there will never be more hints than tiles: */
 	hint_list = (hint_t *) malloc((size_t) (ntiles * sizeof(hint_t)));
 }
@@ -1452,11 +1693,27 @@ void shm_clean(XShmSegmentInfo *shm, XImage *xim) {
 }
 
 void initialize_shm() {
+	int i;
 
 	/* the tile (e.g. 32x32) shared memory area image: */
 
 	shm_create(&tile_shm, &tile, tile_x, tile_y, "tile"); 
 
+	/*
+	 * for copy_tiles we need a lot of shared memory areas, one for
+	 * each possible run length of changed tiles.  32 for 1024x768
+	 * and 40 for 1280x1024, etc. 
+	 */
+
+	for (i=1; i<=ntiles_x; i++) {
+		shm_create(&tile_row_shm[i], &tile_row[i], tile_x * i, tile_y,
+		    "tile_row"); 
+		if (old_copytile && i == 1) {
+			/* only need 1x1 tiles */
+			break;
+		}
+	}
+
 	/* the scanline (e.g. 1280x1) shared memory area image: */
 
 	shm_create(&scanline_shm, &scanline, dpy_x, 1, "scanline"); 
@@ -1474,7 +1731,6 @@ void initialize_shm() {
 
 	shm_create(&fullscreen_shm, &fullscreen, dpy_x, dpy_y/fs_factor,
 	    "fullscreen"); 
-
 }
 
 
@@ -1529,13 +1785,13 @@ void extend_tile_hint(int x, int y, int th, hint_t *hint) {
 }
 
 void save_hint(hint_t hint, int loc) {
-	hint_list[loc].x = hint.x;		/* copy to the global array */
+	/* simply copy it to the global array for later use. */
+	hint_list[loc].x = hint.x;
 	hint_list[loc].y = hint.y;
 	hint_list[loc].w = hint.w;
 	hint_list[loc].h = hint.h;
 }
 
-
 /*
  * Glue together horizontal "runs" of adjacent changed tiles into one big
  * rectangle change "hint" to be passed to the vnc machinery.
@@ -1794,6 +2050,308 @@ void copy_tile(int tx, int ty) {
 	}
 }
 
+
+/*
+ * copy_tiles() gives a slight improvement over copy_tile() since
+ * adjacent runs of tiles are done all at once there is some savings
+ * due to contiguous memory access.  Not a great speedup, but in
+ * some cases it can be up to 2X.  Even more on a SunRay where no
+ * graphics hardware is involved in the read.  Generally, graphics
+ * devices are optimized for write, not read, so we are limited by
+ * the read bandwidth, sometimes only 5 MB/sec on otherwise fast
+ * hardware.
+ */
+
+int *first_line = NULL, *last_line;
+unsigned short *left_diff, *right_diff;
+
+void copy_tiles(int tx, int ty, int nt) {
+	int x, y, line;
+	int size_x, size_y, width, width1, width2;
+	int off, len, n, dw, dx, i, t;
+	int w1, w2, dx1, dx2;	/* tmps for normal and short tiles */
+	int pixelsize = bpp >> 3;
+	int first_min, last_max;
+
+	int restored_patch = 0; /* for show_mouse */
+
+	char *src, *dst, *s_src, *s_dst, *m_src, *m_dst;
+	char *h_src, *h_dst;
+	if (! first_line) {
+		/* allocate arrays first time in. */
+		int n = ntiles_x + 1;
+		first_line = (int *) malloc((size_t) (n * sizeof(int)));
+		last_line  = (int *) malloc((size_t) (n * sizeof(int)));
+		left_diff  = (unsigned short *)
+			malloc((size_t) (n * sizeof(unsigned short)));
+		right_diff = (unsigned short *)
+			malloc((size_t) (n * sizeof(unsigned short)));
+	}
+
+	x = tx * tile_x;
+	y = ty * tile_y;
+
+	size_x = dpy_x - x;
+	if ( size_x > tile_x * nt ) {
+		size_x = tile_x * nt;
+		width1 = tile_x;
+		width2 = tile_x;
+	} else {
+		/* short tile */
+		width1 = tile_x;	/* internal tile */
+		width2 = size_x - (nt - 1) * tile_x;	/* right hand tile */
+	}
+
+	size_y = dpy_y - y;
+	if ( size_y > tile_y ) {
+		size_y = tile_y;
+	}
+
+	n = tx + ty * ntiles_x;		/* number of the first tile */
+
+	X_LOCK;
+	/* read in the whole tile run at once: */
+	if ( using_shm && size_x == tile_x * nt && size_y == tile_y ) {
+		/* general case: */
+		XShmGetImage(dpy, window, tile_row[nt], x, y, AllPlanes);
+	} else {
+		/*
+		 * No shm or near bottom/rhs edge case:
+		 * (but only if tile size does not divide screen size)
+		 */
+		XGetSubImage(dpy, window, x, y, size_x, size_y, AllPlanes,
+		    ZPixmap, tile_row[nt], 0, 0);
+	}
+	X_UNLOCK;
+
+	/*
+	 * Some awkwardness wrt the little remote mouse patch we display.
+	 * When threaded we want to have as small a window of time
+	 * as possible when the mouse image is not in the fb, otherwise
+	 * a libvncserver thread may send the uncorrected patch to the
+	 * clients.
+	 */
+	if (show_mouse && use_threads && cur_saved) {
+		/* check for overlap */
+		if (cur_save_x + cur_save_w > x && x + size_x > cur_save_x &&
+		    cur_save_y + cur_save_h > y && y + size_y > cur_save_y) {
+
+			/* restore the real data to the rfb fb */
+			restore_mouse_patch();
+			restored_patch = 1;
+		}
+	}
+
+	src = tile_row[nt]->data;
+	dst = screen->frameBuffer + y * bytes_per_line + x * pixelsize;
+
+	s_src = src;
+	s_dst = dst;
+
+	for (t=1; t <= nt; t++) {
+		first_line[t] = -1;
+	}
+	/* find the first line with difference: */
+	w1 = width1 * pixelsize;
+	w2 = width2 * pixelsize;
+
+	/* foreach line: */
+	for (line = 0; line < size_y; line++) {
+		/* foreach horizontal tile: */
+		for (t=1; t <= nt; t++) {
+			if (first_line[t] != -1) {
+				continue;
+			}
+
+			off = (t-1) * w1;
+			if (t == nt) {
+				len = w2;	/* possible short tile */
+			} else {
+				len = w1;
+			}
+			
+			if (memcmp(s_dst + off, s_src + off, len)) {
+				first_line[t] = line;
+			}
+		}
+		s_src += tile_row[nt]->bytes_per_line;
+		s_dst += bytes_per_line;
+	}
+
+	/* see if there were any differences for any tile: */
+	first_min = -1;
+	for (t=1; t <= nt; t++) {
+		tile_tried[n+(t-1)] = 1;
+		if (first_line[t] != -1) {
+			if (first_min == -1 || first_line[t] < first_min) {
+				first_min = first_line[t];
+			}
+		}
+	}
+	if (first_min == -1) {
+		/* no tile has a difference, note this and get out: */
+		for (t=1; t <= nt; t++) {
+			tile_has_diff[n+(t-1)] = 0;
+		}
+		if (restored_patch) {
+			redraw_mouse(); 
+		}
+		return;
+	} else {
+		/*
+		 * at least one tile has a difference.  make sure info
+		 * is recorded (e.g. sometimes we guess tiles and they
+		 * came in with tile_has_diff 0)
+		 */
+		for (t=1; t <= nt; t++) {
+			if (first_line[t] == -1) {
+				tile_has_diff[n+(t-1)] = 0;
+			} else {
+				tile_has_diff[n+(t-1)] = 1;
+			}
+		}
+	}
+
+	m_src = src + (tile_row[nt]->bytes_per_line * size_y);
+	m_dst = dst + (bytes_per_line * size_y);
+
+	for (t=1; t <= nt; t++) {
+		last_line[t] = first_line[t];
+	}
+
+	/* find the last line with difference: */
+	w1 = width1 * pixelsize;
+	w2 = width2 * pixelsize;
+
+	/* foreach line: */
+	for (line = size_y - 1; line > first_min; line--) {
+
+		m_src -= tile_row[nt]->bytes_per_line;
+		m_dst -= bytes_per_line;
+
+		/* foreach tile: */
+		for (t=1; t <= nt; t++) {
+			if (first_line[t] == -1
+			    || last_line[t] != first_line[t]) {
+				/* tile has no changes or already done */
+				continue;
+			}
+
+			off = (t-1) * w1;
+			if (t == nt) {
+				len = w2;	/* possible short tile */
+			} else {
+				len = w1;
+			}
+			if (memcmp(m_dst + off, m_src + off, len)) {
+				last_line[t] = line;
+			}
+		}
+	}
+	
+	/*
+	 * determine the farthest down last changed line
+	 * will be used below to limit our memcpy() to the framebuffer.
+	 */
+	last_max = -1;
+	for (t=1; t <= nt; t++) {
+		if (first_line[t] == -1) {
+			continue;
+		}
+		if (last_max == -1 || last_line[t] > last_max) {
+			last_max = last_line[t];
+		}
+	}
+
+	/* look for differences on left and right hand edges: */
+	for (t=1; t <= nt; t++) {
+		left_diff[t] = 0;
+		right_diff[t] = 0;
+	}
+
+	h_src = src;
+	h_dst = dst;
+
+	w1 = width1 * pixelsize;
+	w2 = width2 * pixelsize;
+
+	dx1 = (width1 - tile_fuzz) * pixelsize;
+	dx2 = (width2 - tile_fuzz) * pixelsize;
+	dw = tile_fuzz * pixelsize; 
+
+	/* foreach line: */
+	for (line = 0; line < size_y; line++) {
+		/* foreach tile: */
+		for (t=1; t <= nt; t++) {
+			if (first_line[t] == -1) {
+				/* tile has no changes at all */
+				continue;
+			}
+
+			off = (t-1) * w1;
+			if (t == nt) {
+				dx = dx2;	/* possible short tile */
+				if (dx <= 0) {
+					break;
+				}
+			} else {
+				dx = dx1;
+			}
+
+			if (! left_diff[t] && memcmp(h_dst + off,
+			    h_src + off, dw)) {
+				left_diff[t] = 1;
+			}
+			if (! right_diff[t] && memcmp(h_dst + off + dx,
+			    h_src + off + dx, dw) ) {
+				right_diff[t] = 1;
+			}
+		}
+		h_src += tile_row[nt]->bytes_per_line;
+		h_dst += bytes_per_line;
+	}
+
+	/* now finally copy the difference to the rfb framebuffer: */
+	s_src = src + tile_row[nt]->bytes_per_line * first_min;
+	s_dst = dst + bytes_per_line * first_min;
+
+	for (line = first_min; line <= last_max; line++) {
+		/* for I/O speed we do not do this tile by tile */
+		memcpy(s_dst, s_src, size_x * pixelsize);
+		s_src += tile_row[nt]->bytes_per_line;
+		s_dst += bytes_per_line;
+	}
+
+	if (restored_patch) {
+		redraw_mouse(); 
+	}
+
+	/* record all the info in the region array for this tile: */
+	for (t=1; t <= nt; t++) {
+		int s = t - 1;
+
+		if (first_line[t] == -1) {
+			/* tile unchanged */
+			continue;
+		}
+		tile_region[n+s].first_line = first_line[t];
+		tile_region[n+s].last_line  = last_line[t];
+
+		tile_region[n+s].top_diff = 0;
+		tile_region[n+s].bot_diff = 0;
+		if ( first_line[t] < tile_fuzz ) {
+			tile_region[n+s].top_diff = 1;
+		}
+		if ( last_line[t] > (size_y - 1) - tile_fuzz ) {
+			tile_region[n+s].bot_diff = 1;
+		}
+
+		tile_region[n+s].left_diff  = left_diff[t];
+		tile_region[n+s].right_diff = right_diff[t];
+	}
+
+}
+
 /*
  * The copy_tile() call in the loop below copies the changed tile into
  * the rfb framebuffer.  Note that copy_tile() sets the tile_region
@@ -1817,6 +2375,7 @@ int copy_all_tiles() {
 
 			if (tile_has_diff[n]) {
 				copy_tile(x, y);
+				/* later: copy_tiles(x, y, 1); */
 			}
 			if (! tile_has_diff[n]) {
 				/*
@@ -1846,6 +2405,67 @@ int copy_all_tiles() {
 	return diffs;
 }
 
+/*
+ * Routine analogous to copy_all_tiles() above, but for horizontal runs
+ * of adjacent changed tiles.
+ */
+int copy_all_tile_runs() {
+	int x, y, n, m, i;
+	int diffs = 0;
+	int in_run = 0, run = 0;
+	int ntave = 0, ntcnt = 0;
+
+	for (y=0; y < ntiles_y; y++) {
+		for (x=0; x < ntiles_x + 1; x++) {
+			n = x + y * ntiles_x;
+
+			if (x != ntiles_x && tile_has_diff[n]) {
+				in_run = 1;
+				run++;
+			} else {
+				if (! in_run) {
+					in_run = 0;
+					run = 0;
+					continue;
+				}
+				copy_tiles(x - run, y, run);
+
+				ntcnt++;
+				ntave += run;
+				diffs += run;
+
+				/* neighboring tile downward: */
+				for (i=1; i <= run; i++) {
+					if ((y+1) < ntiles_y
+					    && tile_region[n-i].bot_diff) {
+						m = (x-i) + (y+1) * ntiles_x;
+						if (! tile_has_diff[m]) {
+							tile_has_diff[m] = 2;
+						}
+					}
+				}
+
+				/* neighboring tile to right: */
+				if (((x-1)+1) < ntiles_x
+				    && tile_region[n-1].right_diff) {
+					m = ((x-1)+1) + y * ntiles_x;
+					if (! tile_has_diff[m]) {
+						tile_has_diff[m] = 2;
+					}
+					
+					/* note that this starts a new run */
+					in_run = 1;
+					run = 1;
+				} else {
+					in_run = 0;
+					run = 0;
+				}
+			}
+		}
+	}
+	return diffs;
+}
+
 /*
  * Here starts a bunch of heuristics to guess/detect changed tiles.
  * They are:
@@ -1938,7 +2558,7 @@ void gap_try(int x, int y, int *run, int *saw, int along_x) {
  * be a distracting delayed filling in of such gaps.  gaps_fill is the
  * tweak parameter that sets the width of the gaps that are checked.
  *
- * BTW, grow_islands() is actually pretty successful at doing this too.
+ * BTW, grow_islands() is actually pretty successful at doing this too...
  */
 int fill_tile_gaps() {
 	int x, y, run, saw;
@@ -2049,6 +2669,7 @@ void copy_screen() {
 
 	X_LOCK;
 
+	/* screen may be too big for 1 shm area, so broken into fs_factor */
 	for (i=0; i < fs_factor; i++) {
 		if (using_shm) {
 			XShmGetImage(dpy, window, fullscreen, 0, y, AllPlanes);
@@ -2068,8 +2689,30 @@ void copy_screen() {
 	rfbMarkRectAsModified(screen, 0, 0, dpy_x, dpy_y);
 }
 
+/* profiling routines */
+
+double dtime(double *t_old) {
+	/* 
+	 * usage: call with 0.0 to initialize, subsequent calls give
+	 * the time differences.
+	 */
+	double t_now, dt;
+	struct timeval now;
+
+	gettimeofday(&now, NULL);
+	t_now = now.tv_sec + ( (double) now.tv_usec/1000000. );
+	if (*t_old == 0) {
+		*t_old = t_now;
+		return t_now;
+	}
+	dt = t_now - *t_old;
+	*t_old = t_now;
+	return(dt);
+}
+
+
 /*
- * Utilities for managing the "naps" to cut down on amount of polling
+ * Utilities for managing the "naps" to cut down on amount of polling.
  */
 void nap_set(int tile_cnt) {
 
@@ -2096,7 +2739,7 @@ void nap_set(int tile_cnt) {
 void nap_sleep(int ms, int split) {
 	int i, input = got_user_input;
 
-	/* split it up to improve the wakeup time */
+	/* split up a long nap to improve the wakeup time */
 	for (i=0; i<split; i++) {
 		usleep(ms * 1000 / split);
 		if (! use_threads && i != split - 1) {
@@ -2140,6 +2783,10 @@ void nap_check(int tile_cnt) {
 	}
 }
 
+/*
+ * This is called to avoid a ~20 second timeout in libvncserver.
+ * May no longer be needed.
+ */
 void ping_clients(int tile_cnt) {
 	static time_t last_send = 0;
 	time_t now = time(0);
@@ -2242,6 +2889,7 @@ int scan_display(int ystart, int rescan) {
 void scan_for_updates() {
 	int i, tile_count, tile_diffs;
 	double frac1 = 0.1;   /* tweak parameter to try a 2nd scan_display() */
+	double frac2 = 0.35;  /* or 3rd */
 	for (i=0; i < ntiles; i++) {
 		tile_has_diff[i] = 0;
 		tile_tried[i] = 0;
@@ -2256,6 +2904,8 @@ void scan_for_updates() {
 	count %= NSCAN;
 
 	if (count % (NSCAN/4) == 0)  {
+		/* some periodic maintenance */
+
 		if (subwin) {
 			set_offset();	/* follow the subwindow */
 		}
@@ -2270,7 +2920,8 @@ void scan_for_updates() {
 	}
 
 	/* scan with the initial y to the jitter value from scanlines: */
-	tile_count = scan_display( scanlines[count], 0 );
+	scan_in_progress = 1;
+	tile_count = scan_display(scanlines[count], 0);
 
 	nap_set(tile_count);
 
@@ -2279,7 +2930,7 @@ void scan_for_updates() {
 		frac1 = fs_frac/2.0;
 	}
 
-	if ( tile_count > frac1 * ntiles) {
+	if (tile_count > frac1 * ntiles) {
 		/*
 		 * many tiles have changed, so try a rescan (since it should
 		 * be short compared to the many upcoming copy_tile() calls)
@@ -2287,13 +2938,20 @@ void scan_for_updates() {
 
 		/* this check is done to skip the extra scan_display() call */
 		if (! fs_factor || tile_count <= fs_frac * ntiles) {
-			int cp;
+			int cp, tile_count_old = tile_count;
 			
 			/* choose a different y shift for the 2nd scan: */
 			cp = (NSCAN - count) % NSCAN;
 
-			tile_count = scan_display( scanlines[cp], 1 );
+			tile_count = scan_display(scanlines[cp], 1);
+
+			if (tile_count >= (1 + frac2) * tile_count_old) {
+				/* on a roll... do a 3rd scan */
+				cp = (NSCAN - count + 7) % NSCAN;
+				tile_count = scan_display(scanlines[cp], 1);
+			}
 		}
+		scan_in_progress = 0;
 
 		/*
 		 * At some number of changed tiles it is better to just
@@ -2310,6 +2968,7 @@ void scan_for_updates() {
 		 * Use -fs 1.0 to disable on slow links.
 		 */
 		if (fs_factor && tile_count > fs_frac * ntiles) {
+			fb_copy_in_progress = 1;
 			copy_screen();
 			if (show_mouse) {
 				if (! use_threads) {
@@ -2317,13 +2976,32 @@ void scan_for_updates() {
 				}
 				update_mouse();
 			}
+			fb_copy_in_progress = 0;
+			if (use_threads && ! old_pointer) {
+				pointer(-1, 0, 0, NULL);
+			}
 			nap_check(tile_count);
 			return;
 		}
 	}
+	scan_in_progress = 0;
 
 	/* copy all tiles with differences from display to rfb framebuffer: */
-	tile_diffs = copy_all_tiles();
+	fb_copy_in_progress = 1;
+
+	if (old_copytile) {
+		/*
+		 * Old way, copy I/O one tile at a time.
+		 */
+		tile_diffs = copy_all_tiles();
+	} else {
+		/* 
+		 * New way, does runs of horizontal tiles at once.
+		 * Note that below, for simplicity, the extra tile finding
+		 * (e.g. copy_tiles_backward_pass) is done the old way.
+		 */
+		tile_diffs = copy_all_tile_runs();
+	}
 
 	/*
 	 * This backward pass for upward and left tiles complements what
@@ -2331,14 +3009,25 @@ void scan_for_updates() {
 	 */
 	tile_diffs = copy_tiles_backward_pass();
 
+	/* Given enough tile diffs, try the islands: */
 	if (grow_fill && tile_diffs > 4) {
 		tile_diffs = grow_islands();
 	}
 
+	/* Given enough tile diffs, try the gaps: */
 	if (gaps_fill && tile_diffs > 4) {
 		tile_diffs = fill_tile_gaps();
 	}
 
+	fb_copy_in_progress = 0;
+	if (use_threads && ! old_pointer) {
+		/*
+		 * tell the pointer handler it can process any queued
+		 * pointer events:
+		 */
+		pointer(-1, 0, 0, NULL);
+	}
+
 	if (use_hints) {
 		hint_updates();	/* use krfb/x0rfbserver hints algorithm */
 	} else {
@@ -2363,22 +3052,22 @@ void scan_for_updates() {
 
 void watch_loop(void) {
 	int cnt = 0;
+	double dt = 0.0;
 
 	if (use_threads) {
 		rfbRunEventLoop(screen, -1, TRUE);
 	}
 
 	while (1) {
+
 		got_user_input = 0;
+		got_pointer_input = 0;
+		got_keyboard_input = 0;
 
 		if (! use_threads) {
 			rfbProcessEvents(screen, -1);
-
-			if ((got_user_input || ui_dropthru < 0) &&
-			    cnt % ui_dropthru != 0) {
-				/* every n-th drops thru to code below... */
-				cnt++;
-				XFlush(dpy);
+			if (check_user_input(dt, &cnt)) {
+				/* true means loop back for more input */
 				continue;
 			}
 		}
@@ -2392,45 +3081,186 @@ void watch_loop(void) {
 			continue;
 		}
 
-		if (nofb) {
+		if (nofb) {	/* no framebuffer polling needed */
 			continue;
 		}
 
 		if (watch_bell) {
-			/* n.b. assumes -nofb folks do not want bell... */
+			/*
+			 * check for any bell events.
+			 * n.b. assumes -nofb folks do not want bell...
+			 */
 			watch_bell_event();
 		}
 
-		/*
-		 * delay_refresh amounts to delaying rfb screen refreshes.
-		 * During that time, XQueryPointer needs to be called for
-		 * the events to take place on the x server.
-		 */
-		if (delay_refresh) {
-			Window root_w, child_w;
-			rfbBool ret;
-			int root_x, root_y, win_x, win_y, which = 0;
-			unsigned int mask;
-
+		if (! show_dragging && button_mask) {
+			/* if any button is pressed do not update screen */
+			/* XXX consider: use_threads || got_pointer_input */
 			X_LOCK;
-			if (0) {
-				XFlush(dpy);
-			} else {
-				ret = XQueryPointer(dpy, rootwin, &root_w,
-				    &child_w, &root_x, &root_y, &win_x, &win_y,
-				    &mask);
-			}
+			XFlush(dpy);
 			X_UNLOCK;
 		} else {
+			/* for timing the scan to try to detect thrashing */
+			double tm = 0.0;
+			dtime(&tm);
+
 			rfbUndrawCursor(screen);
 			scan_for_updates();
+
+			dt = dtime(&tm);
 		}
 
+		/* sleep a bit to lessen load */
 		usleep(waitms * 1000);
 		cnt++;
 	}
 }
 
+/*
+ * We need to handle user input, particularly pointer input, carefully.
+ * This function is only called when non-threaded.  Note that
+ * rfbProcessEvents() only processes *one* pointer event per call,
+ * so if we interlace it with scan_for_updates(), we can get swamped
+ * with queued up pointer inputs.  And if the pointer inputs are inducing
+ * large changes on the screen (e.g. window drags), the whole thing
+ * bogs down miserably and only comes back to life at some time after
+ * one stops moving the mouse.  So, to first approximation, we are trying
+ * to eat as much user input here as we can using some hints from the
+ * duration of the previous scan_for_updates() call (in dt).
+ *
+ * return of 1 means watch_loop should short-circuit and reloop,
+ * return of 0 means watch_loop should proceed to scan_for_updates().
+ */
+
+int check_user_input(double dt, int *cnt) {
+	
+	if (old_pointer) {
+		/* every n-th drops thru to scan */
+		if ((got_user_input || ui_skip < 0) && *cnt % ui_skip != 0) {
+			*cnt++;
+			XFlush(dpy);
+			return 1;	/* short circuit watch_loop */
+		} else {
+			return 0;
+		}
+	}
+
+	if (got_keyboard_input) {
+		if (*cnt % ui_skip != 0) {
+			*cnt++;
+			return 1;	/* short circuit watch_loop */
+		}
+		/* otherwise continue with pointer input */
+	}
+
+	if (got_pointer_input) {
+		int eaten = 0, miss = 0, max_eat = 50;
+		int g, g_in;
+		double spin = 0.0, tm = 0.0;
+		double quick_spin_fac  = 0.40;
+		double grind_spin_time = 0.175;
+
+
+		dtime(&tm);
+		g = g_in = got_pointer_input;
+
+		/*
+		 * Try for some "quick" pointer input processing.
+		 *
+		 * About as fast as we can, we try to process user input
+		 * calling rfbProcessEvents or rfbCheckFds.  We do this
+		 * for a time on order of the last scan_for_updates() time,
+		 * dt, but if we stop getting user input we break out.
+		 * We will also break out if we have processed max_eat
+		 * inputs.
+		 *
+		 * Note that rfbCheckFds() does not send any framebuffer
+		 * updates, so is more what we want here, although it is
+		 * likely they have all be sent already.
+		 */
+		while (1) {
+			//rfbProcessEvents(screen, 1000);
+			rfbCheckFds(screen, 1000);
+			XFlush(dpy);
+
+			spin += dtime(&tm);
+
+			if (spin > quick_spin_fac * dt) {
+				/* get out if spin time comparable to last scan time */
+				break;
+			}
+			if (got_pointer_input > g) {
+				g = got_pointer_input;
+				if (eaten++ < max_eat) {
+					continue;
+				}
+			} else {
+				miss++;
+			}
+			if (miss > 1) {	/* 1 means out on 2nd miss */
+				break;
+			}
+		}
+
+
+		/*
+		 * Probably grinding with a lot of fb I/O if dt is
+		 * this large.  (need to do this more elegantly) 
+		 *
+		 * Current idea is to spin our wheels here *not* processing
+		 * any fb I/O, but still processing the user input.
+		 * This user input goes to the X display and changes it,
+		 * but we don't poll it while we "rest" here for a time
+		 * on order of dt, the previous scan_for_updates() time.
+		 * We also break out if we miss enough user input.
+		 */
+		if (dt > grind_spin_time) {
+			int i, ms, split = 30;
+			double shim;
+
+			/*
+			 * Break up our pause into 'split' steps.
+			 * We get at most one input per step.
+			 */
+			shim = 0.75 * dt / split;
+
+			ms = (int) (1000 * shim);
+
+			/* cutoff how long the pause can be */
+			if (split * ms > 300) {
+				ms = 300 / split;
+			}
+
+			spin = 0.0;
+			tm = 0.0;
+			dtime(&tm);
+
+			g = got_pointer_input;
+			miss = 0;
+			for (i=0; i<split; i++) {
+				usleep(ms * 1000);
+				//rfbProcessEvents(screen, 1000);
+				rfbCheckFds(screen, 1000);
+				spin += dtime(&tm);
+				if (got_pointer_input > g) {
+					XFlush(dpy);
+					miss = 0;
+				} else {
+					miss++;
+				}
+				g = got_pointer_input;
+				if (miss > 2) {
+					break;
+				}
+				if (1000 * spin > ms * split)  {
+					break;
+				}
+			}
+		}
+	}
+	return 0;
+}
+
 void print_help() {
 	char help[] = 
 "\n"
@@ -2499,15 +3329,17 @@ void print_help() {
 "-mouse                 Draw a 2nd cursor at the current X pointer position.\n"
 "-mouseX                As -mouse, but also draw an X on root background.\n"
 "-X                     Shorthand for -mouseX -nocursor.\n"
+"-buttonmap str         String to remap mouse buttons.  Format: IJK-LMN, this\n"
+"                       maps buttons I -> L, etc., e.g.  -buttonmap 13-31\n"
 "-nodragging            Do not update the display during mouse dragging events.\n"
-"                       Greatly improves response on slow links, but you lose\n"
+"                       Greatly improves response on slow setups, but you lose\n"
 "                       all visual feedback for drags and some menu traversals.\n"
-"-input_skip n          Experimental option difficult to explain! Only active in\n"
-"                       non-threaded mode. See watch_loop() function for info.\n"
-"                       Loop is: rfbProcessEvents, scan display, sleep. If\n"
-"                       there is mouse or keyboard input the scan is skipped\n"
-"                       except every n-th time thru the loop. Default n is 10,\n"
-"                       n<0 means skip even with no user input. Need to improve!\n"
+"-old_pointer           Do not use the new pointer input handling mechanisms.\n"
+"                       See check_input() and pointer() for details.\n"
+"-input_skip n          For the old pointer handling when non-threaded: try to\n"
+"                       read n user input events before scanning display. n < 0\n"
+"                       means to act as though there is always user input.\n"
+"-old_copytile          Do not use the new copy_tiles() framebuffer mechanism.\n"
 "\n"
 "-defer time            Time in ms to wait for updates before sending to\n"
 "                       client [rfbDeferUpdateTime]  (default %d).\n"
@@ -2651,15 +3483,21 @@ int main(int argc, char** argv) {
 		} else if (!strcmp(argv[i], "-mouseX")) {
 			show_mouse = 1;
 			show_root_cursor = 1;
-		} else if (!strcmp(argv[i], "-nodragging")) {
-			show_dragging = 0;
-		} else if (!strcmp(argv[i], "-input_skip")) {
-			ui_dropthru = atoi(argv[++i]);
-			if (! ui_dropthru) ui_dropthru = 1;
 		} else if (!strcmp(argv[i], "-X")) {
 			show_mouse = 1;
 			show_root_cursor = 1;
 			local_cursor = 0;
+		} else if (!strcmp(argv[i], "-buttonmap")) {
+			pointer_remap = argv[++i];
+		} else if (!strcmp(argv[i], "-nodragging")) {
+			show_dragging = 0;
+		} else if (!strcmp(argv[i], "-input_skip")) {
+			ui_skip = atoi(argv[++i]);
+			if (! ui_skip) ui_skip = 1;
+		} else if (!strcmp(argv[i], "-old_pointer")) {
+			old_pointer = 1;
+		} else if (!strcmp(argv[i], "-old_copytile")) {
+			old_copytile = 1;
 		} else if (!strcmp(argv[i], "-defer")) {
 			defer_update = atoi(argv[++i]);
 		} else if (!strcmp(argv[i], "-wait")) {
@@ -2708,6 +3546,8 @@ int main(int argc, char** argv) {
 			}
 		}
 	}
+
+	/* fixup settings that do not make sense */
 		
 	if (tile_fuzz < 1) {
 		tile_fuzz = 1;
@@ -2724,6 +3564,7 @@ int main(int argc, char** argv) {
 		connect_once = 1;
 		bg = 0;
 	}
+
 	if (! quiet) {
 		fprintf(stderr, "viewonly:   %d\n", view_only);
 		fprintf(stderr, "shared:     %d\n", shared);
@@ -2741,7 +3582,7 @@ int main(int argc, char** argv) {
 		fprintf(stderr, "loc_curs:   %d\n", local_cursor);
 		fprintf(stderr, "mouse:      %d\n", show_mouse);
 		fprintf(stderr, "dragging:   %d\n", show_dragging);
-		fprintf(stderr, "inputskip:  %d\n", ui_dropthru);
+		fprintf(stderr, "inputskip:  %d\n", ui_skip);
 		fprintf(stderr, "root_curs:  %d\n", show_root_cursor);
 		fprintf(stderr, "defer:      %d\n", defer_update);
 		fprintf(stderr, "waitms:     %d\n", waitms);
@@ -2756,6 +3597,7 @@ int main(int argc, char** argv) {
 		rfbLogEnable(0);
 	}
 
+	/* open the X display: */
 	X_INIT;
 	if (use_dpy) {
 		dpy = XOpenDisplay(use_dpy);
@@ -2775,10 +3617,13 @@ int main(int argc, char** argv) {
 		if (! quiet) fprintf(stderr, "Using default display.\n");
 	}
 
+	/* check for XTEST */
 	if (! XTestQueryExtension(dpy, &ev, &er, &maj, &min)) {
 		fprintf(stderr, "Display does not support XTest extension.\n");
 		exit(1);
 	}
+
+	/* check for MIT-SHM */
 	if (! nofb && ! XShmQueryExtension(dpy)) {
 		if (! using_shm) {
 			fprintf(stderr, "warning: display does not support "
@@ -2790,6 +3635,7 @@ int main(int argc, char** argv) {
 		}
 	}
 #ifdef LIBVNCSERVER_HAVE_XKEYBOARD
+	/* check for XKEYBOARD */
 	if (watch_bell) {
 		if (! XkbQueryExtension(dpy, &op, &ev, &er, &maj, &min)) {
 			fprintf(stderr, "warning: disabling bell.\n");
@@ -2810,6 +3656,7 @@ int main(int argc, char** argv) {
 	scr = DefaultScreen(dpy);
 	rootwin = RootWindow(dpy, scr);
 
+	/* set up parameters for subwin or non-subwin cases: */
 	if (! subwin) {
 		window = rootwin;
 		dpy_x = DisplayWidth(dpy, scr);
@@ -2895,6 +3742,7 @@ int main(int argc, char** argv) {
 
 #if defined(LIBVNCSERVER_HAVE_FORK) && defined(LIBVNCSERVER_HAVE_SETSID)
 	if (bg) {
+		/* fork into the background now */
 		int p, n;
 		if ((p = fork()) > 0)  {
 			exit(0);
@@ -2908,6 +3756,7 @@ int main(int argc, char** argv) {
 			perror("setsid");
 			exit(1);
 		}
+		/* adjust our stdio */
 		n = open("/dev/null", O_RDONLY);
 		dup2(n, 0);
 		dup2(n, 1);
-- 
cgit v1.2.3

