From c984f2b126dbadd99bbb4882fba15e32331cabcd Mon Sep 17 00:00:00 2001
From: dscho <dscho>
Date: Thu, 3 Apr 2003 10:49:34 +0000
Subject: new version from Karl!

---
 contrib/x11vnc.c | 1452 ++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 1187 insertions(+), 265 deletions(-)

diff --git a/contrib/x11vnc.c b/contrib/x11vnc.c
index 3967b9a..2395702 100644
--- a/contrib/x11vnc.c
+++ b/contrib/x11vnc.c
@@ -1,7 +1,8 @@
 /*
  * x11vnc.c: a VNC server for X displays.
  *
- * Copyright (c) 2002 Karl J. Runge <runge@karlrunge.com>  All rights reserved.
+ * Copyright (c) 2002-2003 Karl J. Runge <runge@karlrunge.com>
+ * All rights reserved.
  *
  *  This is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -39,15 +40,57 @@
  * applied (currently there are a bit too many of these...)
  *
  * To build:
+ *
  * Obtain the libvncserver package (http://libvncserver.sourceforge.net).
  * As of 12/2002 this version of x11vnc.c is contained in the libvncserver
- * CVS tree.  Parameters in the Makefile should be adjusted to the build
- * system and then "make x11vnc" should create it.  For earlier releases
- * (say libvncserver-0.4) this file may be inserted in place of the
- * original x11vnc.c file.
+ * CVS tree and released in version 0.5.  For earlier releases (say
+ * libvncserver-0.4) this file may be inserted in place of the original
+ * x11vnc.c file.
+ *
+ * gcc should be used on all platforms.  To build a threaded version put
+ * "-D_REENTRANT -DX11VNC_THREADED" in the environment variable CFLAGS
+ * or CPPFLAGS (e.g. before running configure).  The threaded mode is a 
+ * bit more responsive, but can be unstable.
+ *
+ * Known shortcomings:
+ *
+ * The screen updates are good, but of course not perfect since the X
+ * display must be continuously polled and read for changes (as opposed to
+ * receiving a change callback from the X server, if that were generally
+ * possible...).  So, e.g., opaque moves and similar window activity
+ * can be very painful; one has to modify one's behavior a bit.
+ *
+ * It currently cannot capture XBell beeps (impossible?)  And, of course,
+ * general audio at the remote display is lost as well unless one separately
+ * sets up some audio side-channel.
+ *
+ * Windows using visuals other than the default X visual may have their
+ * colors messed up.  When using 8bpp indexed color, the colormap may
+ * become out of date (as the colormap is added to) or incorrect.
+ *
+ * It does not appear possible to query the X server for the current
+ * cursor shape.  We can use XTest to compare cursor to current window's
+ * cursor, but we cannot extract what the cursor is...  
+ * 
+ * Nevertheless, the current *position* of the remote X mouse pointer
+ * is shown with the -mouse option.  Further, if -mouseX or -X is used, a
+ * trick is done to at least show the root window cursor vs non-root cursor.
+ * (perhaps some heuristic can be done to further distinguish cases...)
+ *
+ * With -mouse there are occasionally some repainting errors involving
+ * big areas near the cursor.  The mouse painting is in general a bit
+ * ragged and not very pleasant.
+ *
+ * Occasionally, a few tile updates can be missed leaving a patch of
+ * color that needs to be refreshed.
+ *
+ * There seems to be a serious bug with simultaneous clients when
+ * threaded, currently the only workaround in this case is -nothreads.
+ *
  */
 
 #include <unistd.h>
+#include <signal.h>
 #include <sys/ipc.h>
 #include <sys/shm.h>
 #include <X11/Xlib.h>
@@ -76,12 +119,17 @@ typedef KeySym X_KeySym;
 
 #include <rfb/rfb.h>
 
+/* X and rfb framebuffer */
 Display *dpy = 0;
+Visual *visual;
+Window window, rootwin;
+int subwin = 0;
 int scr;
-int dpy_x, dpy_y;
 int bpp;
-int window;
 int button_mask = 0;
+int dpy_x, dpy_y;
+int off_x, off_y;
+int indexed_colour = 0;
 
 XImage *tile;
 XImage *scanline;
@@ -102,13 +150,14 @@ int tile_y = 32;
 int ntiles, ntiles_x, ntiles_y;
 
 /* arrays that indicate changed or checked tiles. */
-char *tile_has_diff, *tile_tried;
+unsigned char *tile_has_diff, *tile_tried;
 
 typedef struct tile_change_region {
-	short first_line, last_line;  /* start and end lines, along y,      */
-				      /* of the changed area inside a tile. */
-	short left_diff, right_diff;  /* info about differences along edges. */
-	short top_diff,  bot_diff;
+	/* start and end lines, along y, of the changed area inside a tile. */
+	unsigned short first_line, last_line;
+	/* info about differences along edges. */
+	unsigned short left_diff, right_diff;
+	unsigned short top_diff,  bot_diff;
 } region_t;
 
 /* array to hold the tiles region_t-s. */
@@ -123,90 +172,176 @@ typedef struct hint {
 /* array to hold the hints: */
 hint_t *hint_list;
 
-int shared = 0;		/* share vnc display. */
-int view_only = 0;	/* client can only watch. */
-int connect_once = 1;	/* allow only one client connection. */
+/* various command line options */
+
+int shared = 0;			/* share vnc display. */
+int view_only = 0;		/* clients can only watch. */
+int connect_once = 1;		/* disconnect after first connection session. */
+int flash_cmap = 0;		/* follow installed colormaps */
 
 int use_modifier_tweak = 0;	/* use the altgr_keyboard modifier tweak */
 
+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 */
+
 /*
  * waitms is the msec to wait between screen polls.  Not too old h/w shows
- * poll times of 15-35ms, so maybe this value cuts the rest load by 2 or so.
+ * poll times of 10-35ms, so maybe this value cuts the idle load by 2 or so.
  */
 int waitms = 30;
 int defer_update = 30;	/* rfbDeferUpdateTime ms to wait before sends. */
-int use_threads = 1;	/* but only if compiled with HAVE_PTHREADS */
+
+int screen_blank = 60;	/* number of seconds of no activity to throttle */
+			/* down the screen polls.  zero to disable. */
+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 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. */
 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. */
 int grow_fill = 3;	/* do the grow islands heuristic with this width. */
 int gaps_fill = 4;	/* do a final pass to try to fill gaps between tiles. */
-double fs_frac = 0.6;	/* threshold tile fraction to do fullscreen updates. */
 
+/* scan pattern jitter from x0rfbserver */
 #define NSCAN 32
-int scanlines[NSCAN] = {	 /* scan pattern jitter from x0rfbserver */
+int scanlines[NSCAN] = {
 	 0, 16,  8, 24,  4, 20, 12, 28,
 	10, 26, 18,  2, 22,  6, 30, 14,
 	 1, 17,  9, 25,  7, 23, 15, 31,
 	19,  3, 27, 11, 29, 13,  5, 21
 };
+int count = 0;			/* indicates which scan pattern we are on  */
 
-int got_user_input;
-int count = 0;
+int cursor_x, cursor_y;		/* x and y from the viewer(s) */
+int got_user_input = 0;
 int shut_down = 0;	
 
+#if defined(HAVE_LIBPTHREAD) && defined(X11VNC_THREADED)
+	int use_threads = 1;
+#else
+	int use_threads = 0;
+#endif
+
+/* XXX usleep(3) is not thread safe on some older systems... */
+struct timeval _mysleep;
+#define usleep2(x) \
+	_mysleep.tv_sec  = (x) / 1000000; \
+	_mysleep.tv_usec = (x) % 1000000; \
+	select(0, NULL, NULL, NULL, &_mysleep); 
+#if !defined(X11VNC_USLEEP)
+#undef usleep
+#define usleep usleep2
+#endif
+
 /*
  * Not sure why... but when threaded we have to mutex our X11 calls to
- * avoid XIO crashes.  This should not be too bad since keyboard and pointer
- * updates are infrequent compared to the scanning. (note: these lines are
- * noops unless HAVE_PTHREADS)  XXX: what is going on?
+ * avoid XIO crashes.
  */
 MUTEX(x11Mutex);
-#define X_LOCK     LOCK(x11Mutex);
-#define X_UNLOCK UNLOCK(x11Mutex);
-
-void mark_hint(hint_t);
-
-void clean_up_exit (void) {
+#define X_LOCK       LOCK(x11Mutex)
+#define X_UNLOCK   UNLOCK(x11Mutex)
+#define X_INIT INIT_MUTEX(x11Mutex)
 
-	X_LOCK
+/*
+ * Exiting and error handling:
+ */
+void shm_clean(XShmSegmentInfo *, XImage *);
+void shm_delete(XShmSegmentInfo *);
 
-	XTestDiscard(dpy);
+int exit_flag = 0;
+void clean_up_exit (int ret) {
+	exit_flag = 1;
 
 	/* remove the shm areas: */
+	shm_clean(&tile_shm, tile);
+	shm_clean(&scanline_shm, scanline);
+	shm_clean(&fullscreen_shm, fullscreen);
 
-	XShmDetach(dpy, &tile_shm);
-	XDestroyImage(tile);
-	shmdt(tile_shm.shmaddr);
-	shmctl(tile_shm.shmid, IPC_RMID, 0);
+	X_LOCK;
+	XTestDiscard(dpy);
+	X_UNLOCK;
 
-	XShmDetach(dpy, &scanline_shm);
-	XDestroyImage(scanline);
-	shmdt(scanline_shm.shmaddr);
-	shmctl(scanline_shm.shmid, IPC_RMID, 0);
+	exit(ret);
+}
 
-	XShmDetach(dpy, &fullscreen_shm);
-	XDestroyImage(fullscreen);
-	shmdt(fullscreen_shm.shmaddr);
-	shmctl(fullscreen_shm.shmid, IPC_RMID, 0);
+void interrupted (int sig) {
+	if (exit_flag) {
+		if (use_threads) {
+			usleep2(250 * 1000);
+		}
+		exit(4);
+	}
+	exit_flag++;
+	if (sig == 0) {
+		printf("caught X11 error:\n");
+	} else {
+		printf("caught signal: %d\n", sig);
+	}
+	/*
+	 * to avoid deadlock, etc, just delete the shm areas and
+	 * leave the X stuff hanging.
+	 */
+	shm_delete(&tile_shm);
+	shm_delete(&scanline_shm);
+	shm_delete(&fullscreen_shm);
+	if (sig) {
+		exit(2);
+	}
+}
 
-	/* more cleanup? */
+XErrorHandler   Xerror_def;
+XIOErrorHandler XIOerr_def;
+int Xerror(Display *d, XErrorEvent *error) {
+	interrupted(0);
+	return (*Xerror_def)(d, error);
+}
+int XIOerr(Display *d) {
+	interrupted(0);
+	return (*XIOerr_def)(d);
+}
 
-	X_UNLOCK
-	exit(0);
+void set_signals(void) {
+	signal(SIGHUP,  interrupted);
+	signal(SIGINT,  interrupted);
+	signal(SIGQUIT, interrupted);
+	signal(SIGABRT, interrupted);
+	signal(SIGTERM, interrupted);
+	signal(SIGBUS,  interrupted);
+	signal(SIGSEGV, interrupted);
+	signal(SIGFPE,  interrupted);
+
+	X_LOCK;
+	Xerror_def = XSetErrorHandler(Xerror);
+	XIOerr_def = XSetIOErrorHandler(XIOerr);
+	X_UNLOCK;
 }
 
 void client_gone(rfbClientPtr client) {
 	if (connect_once) {
-		printf("only one connection allowed.\n");
-		clean_up_exit();
+		printf("viewer exited.\n");
+		clean_up_exit(0);
 	}
 }
 
 enum rfbNewClientAction new_client(rfbClientPtr client) {
+	static client_count = 0;
+	last_event = last_input = time(0);
 	if (connect_once) {
+		if (screen->rfbDontDisconnect && screen->rfbNeverShared) {
+			if (! shared && client_count) {
+				printf("denying additional client: %s\n",
+				    client->host);
+				return(RFB_CLIENT_REFUSE);
+			}
+		}
 		client->clientGoneHook = client_gone;
 	}
 	if (view_only)  {
@@ -214,11 +349,13 @@ enum rfbNewClientAction new_client(rfbClientPtr client) {
 	} else {
 		client->clientData = (void *) 0;
 	}
+	client_count++;
 	return(RFB_CLIENT_ACCEPT);
 }
 
-/* For tweaking modifiers wrt the Alt-Graph key */
-
+/*
+ * For tweaking modifiers wrt the Alt-Graph key, etc.
+ */
 #define LEFTSHIFT 1
 #define RIGHTSHIFT 2
 #define ALTGR 4
@@ -277,9 +414,10 @@ void initialize_keycodes() {
 
 void DebugXTestFakeKeyEvent(Display* dpy, KeyCode keysym, Bool down, time_t cur_time)
 {
-    fprintf(stderr,"XTestFakeKeyEvent(dpy,%s(0x%x),%s,CurrentTime)\n",
-	    XKeysymToString(XKeycodeToKeysym(dpy,keysym,0)),keysym,down?"down":"up");
-    XTestFakeKeyEvent(dpy,keysym,down,cur_time);
+	fprintf(stderr,"XTestFakeKeyEvent(dpy,%s(0x%x),%s,CurrentTime)\n",
+	    XKeysymToString(XKeycodeToKeysym(dpy,keysym,0)),keysym,
+	    down?"down":"up");
+	XTestFakeKeyEvent(dpy,keysym,down,cur_time);
 }
 
 /* #define XTestFakeKeyEvent DebugXTestFakeKeyEvent */
@@ -292,7 +430,7 @@ void tweak_mod(signed char mod, Bool down) {
 		return;
 	}
 
-	X_LOCK
+	X_LOCK;
 	if (is_shift && mod != 1) {
 	    if (mod_state & LEFTSHIFT) {
 		XTestFakeKeyEvent(dpy, left_shift_code, !dn, CurrentTime);
@@ -310,7 +448,7 @@ void tweak_mod(signed char mod, Bool down) {
 	if ( altgr_code && ! (mod_state & ALTGR) && mod == 2 ) {
 	    XTestFakeKeyEvent(dpy, altgr_code, dn, CurrentTime);
 	}
-	X_UNLOCK
+	X_UNLOCK;
 }
 
 static void modifier_tweak_keyboard(Bool down, KeySym keysym, rfbClientPtr client) {
@@ -339,14 +477,14 @@ static void modifier_tweak_keyboard(Bool down, KeySym keysym, rfbClientPtr clien
 		tweak_mod(modifiers[keysym], True);
 		k = keycodes[keysym];
 	} else {
-		X_LOCK
+		X_LOCK;
 		k = XKeysymToKeycode(dpy, (X_KeySym) keysym);
-		X_UNLOCK
+		X_UNLOCK;
 	}
 	if ( k != NoSymbol ) {
-		X_LOCK
+		X_LOCK;
 		XTestFakeKeyEvent(dpy, k, (X_Bool) down, CurrentTime);
-		X_UNLOCK
+		X_UNLOCK;
 	}
 
 	if ( tweak ) {
@@ -354,12 +492,18 @@ static void modifier_tweak_keyboard(Bool down, KeySym keysym, rfbClientPtr clien
 	}
 }
 
-/* key event handler */
+/*
+ * key event handler
+ */
 static void keyboard(Bool down, KeySym keysym, rfbClientPtr client) {
 	KeyCode k;
 
-	/* fprintf(stderr,"keyboard(%s,%s(0x%x),client)\n",
-	   down?"down":"up",XKeysymToString(keysym),(int)keysym); */
+	if (0) {
+		X_LOCK;
+		fprintf(stderr,"keyboard(%s,%s(0x%x),client)\n",
+		    down?"down":"up",XKeysymToString(keysym),(int)keysym);
+		X_UNLOCK;
+	}
 
 	if (view_only) {
 		return;
@@ -370,7 +514,7 @@ static void keyboard(Bool down, KeySym keysym, rfbClientPtr client) {
 		return;
 	}
 
-	X_LOCK
+	X_LOCK;
 
 	k = XKeysymToKeycode(dpy, (X_KeySym) keysym);
 
@@ -378,13 +522,16 @@ static void keyboard(Bool down, KeySym keysym, rfbClientPtr client) {
 		XTestFakeKeyEvent(dpy, k, (X_Bool) down, CurrentTime);
 		XFlush(dpy);
 
+		last_event = last_input = time(0);
 		got_user_input++;
 	}
 
-	X_UNLOCK
+	X_UNLOCK;
 }
 
-/* mouse event handler */
+/*
+ * pointer event handler
+ */
 static void pointer(int mask, int x, int y, rfbClientPtr client) {
 	int i;
 
@@ -392,10 +539,14 @@ static void pointer(int mask, int x, int y, rfbClientPtr client) {
 		return;
 	}
 
-	X_LOCK
+	X_LOCK;
+
+	XTestFakeMotionEvent(dpy, scr, x + off_x, y + off_y, CurrentTime);
 
-	XTestFakeMotionEvent(dpy, 0, x, y, CurrentTime);
+	cursor_x = x;
+	cursor_y = y;
 
+	last_event = last_input = time(0);
 	got_user_input++;
 
 	for (i=0; i < 5; i++) {
@@ -404,35 +555,30 @@ static void pointer(int mask, int x, int y, rfbClientPtr client) {
 			    (mask & (1<<i)) ? True : False, CurrentTime);
 		}
 	}
-	X_UNLOCK
+
+	X_UNLOCK;
 
 	/* remember the button state for next time: */
 	button_mask = mask;
 }
 
-/* simple fixed cursor */
-static char* cur_data =
-"                  "
-"                  "
-"  x               "
-"  xx              "
-"  xxx             "
-"  xxxx            "
-"  xxxxx           "
-"  xxxxxx          "
-"  xxxxxxx         "
-"  xxxxxxxx        "
-"  xxxxx           "
-"  xx xx           "
-"  x   xx          "
-"      xx          "
-"       xx         "
-"       xx         "
-"                  "
-"                  ";
+void mark_hint(hint_t);
 
-static char* cur_mask =
+/*
+ * here begins a bit of a mess to experiment with multiple cursors ...
+ */
+typedef struct cursor_info {
+	char *data;	/* data and mask pointers */
+	char *mask;
+	int wx, wy;	/* size of cursor */
+	int sx, sy;	/* shift to its centering point */
+	int reverse;	/* swap black and white */
+} cursor_info_t;
+
+/* main cursor */
+static char* cur_data =
 "                  "
+" x                "
 " xx               "
 " xxx              "
 " xxxx             "
@@ -440,19 +586,430 @@ static char* cur_mask =
 " xxxxxx           "
 " xxxxxxx          "
 " xxxxxxxx         "
-" xxxxxxxxx        "
-" xxxxxxxxxx       "
-" xxxxxxxxxx       "
-" xxxxxxx          "
-" xxx xxxx         "
-" xx  xxxx         "
-"      xxxx        "
-"      xxxx        "
-"       xx         "
+" xxxxx            "
+" xx xx            "
+" x   xx           "
+"     xx           "
+"      xx          "
+"      xx          "
+"                  "
+"                  "
 "                  ";
-int cursor_x = 18;
-int cursor_y = 18;
 
+static char* cur_mask =
+"xx                "
+"xxx               "
+"xxxx              "
+"xxxxx             "
+"xxxxxx            "
+"xxxxxxx           "
+"xxxxxxxx          "
+"xxxxxxxxx         "
+"xxxxxxxxxx        "
+"xxxxxxxxxx        "
+"xxxxxxx           "
+"xxx xxxx          "
+"xx  xxxx          "
+"     xxxx         "
+"     xxxx         "
+"      xx          "
+"                  "
+"                  ";
+#define CUR_SIZE 18
+#define CUR_DATA cur_data
+#define CUR_MASK cur_mask
+cursor_info_t cur0 = {NULL, NULL, CUR_SIZE, CUR_SIZE, 0, 0, 0};
+
+/*
+ * It turns out we can at least detect mouse is on the root window so 
+ * show it (under -mouseX or -X) with this familiar cursor... 
+ */
+static char* root_data =
+"                  "
+"                  "
+"  xxx        xxx  "
+"  xxxx      xxxx  "
+"  xxxxx    xxxxx  "
+"   xxxxx  xxxxx   "
+"    xxxxxxxxxx    "
+"     xxxxxxxx     "
+"      xxxxxx      "
+"      xxxxxx      "
+"     xxxxxxxx     "
+"    xxxxxxxxxx    "
+"   xxxxx  xxxxx   "
+"  xxxxx    xxxxx  "
+"  xxxx      xxxx  "
+"  xxx        xxx  "
+"                  "
+"                  ";
+
+static char* root_mask =
+"                  "
+" xxxx        xxxx "
+" xxxxx      xxxxx "
+" xxxxxx    xxxxxx "
+" xxxxxxx  xxxxxxx "
+"  xxxxxxxxxxxxxx  "
+"   xxxxxxxxxxxx   "
+"    xxxxxxxxxx    "
+"     xxxxxxxx     "
+"     xxxxxxxx     "
+"    xxxxxxxxxx    "
+"   xxxxxxxxxxxx   "
+"  xxxxxxxxxxxxxx  "
+" xxxxxxx  xxxxxxx "
+" xxxxxx    xxxxxx "
+" xxxxx      xxxxx "
+" xxxx        xxxx "
+"                  ";
+cursor_info_t cur1 = {NULL, NULL, 18, 18, 8, 8, 1};
+
+cursor_info_t *cursors[2];
+void setup_cursors(void) {
+	/* TODO clean this up if we ever do more cursors... */
+
+	cur0.data = cur_data;
+	cur0.mask = cur_mask;
+
+	cur1.data = root_data;
+	cur1.mask = root_mask;
+
+	cursors[0] = &cur0;
+	cursors[1] = &cur1;
+}
+
+/*
+ * data and functions for -mouse real pointer position updates
+ */
+char cur_save[(4 * CUR_SIZE * CUR_SIZE)];
+int cur_save_x, cur_save_y, cur_save_w, cur_save_h;
+int cur_save_cx, cur_save_cy, cur_save_which, cur_saved = 0;
+
+/*
+ * save current cursor info and the patch of data it covers
+ */
+void save_mouse_patch(int x, int y, int w, int h, int cx, int cy, int which) {
+	int pixelsize = bpp >> 3;
+	char *rfb_fb = screen->frameBuffer;
+	int ly, i = 0;
+
+	for (ly = y; ly < y + h; ly++) {
+		memcpy(cur_save+i, rfb_fb + ly * bytes_per_line
+		    + x * pixelsize, w * pixelsize);
+
+		i += w * pixelsize;
+	}
+	cur_save_x = x;		/* patch geometry */
+	cur_save_y = y;
+	cur_save_w = w;
+	cur_save_h = h;
+
+	cur_save_which = which;	/* which cursor and its position  */
+	cur_save_cx = cx;
+	cur_save_cy = cy;
+
+	cur_saved = 1;
+}
+
+/*
+ * put the non-cursor patch back in the rfb fb
+ */
+void restore_mouse_patch() {
+	int pixelsize = bpp >> 3;
+	char *rfb_fb = screen->frameBuffer;
+	int ly, i = 0;
+
+	if (! cur_saved) {
+		return;		/* not yet saved */
+	}
+
+	for (ly = cur_save_y; ly < cur_save_y + cur_save_h; ly++) {
+		memcpy(rfb_fb + ly * bytes_per_line + cur_save_x * pixelsize,
+		    cur_save+i, cur_save_w * pixelsize);
+		i += cur_save_w * pixelsize;
+	}
+}
+
+/*
+ * Descends windows 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).
+ *
+ * It seems impossible to do, but if the actual cursor could ever be
+ * determined we might want to hash that info on window ID or something...
+ */
+int tree_depth_cursor(void) {
+	Window r, c;
+	int rx, ry, wx, wy;
+	unsigned int mask;
+	int depth = 0, tries = 0, maxtries = 1;
+
+	X_LOCK;
+	c = window;
+	while (c) {
+		if (++tries > maxtries) {
+			depth = maxtries;
+			break;
+		}
+		if ( XTestCompareCurrentCursorWithWindow(dpy, c) ) {
+			break;
+		}
+		XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &mask);
+		depth++;
+	}
+	X_UNLOCK;
+	return depth;
+}
+
+/*
+ * draw one of the mouse cursors into the rfb fb
+ */
+void draw_mouse(int x, int y, int which, int update) {
+	int px, py, i, offset;
+	int pixelsize = bpp >> 3;
+	char *rfb_fb = screen->frameBuffer;
+	char cdata, cmask;
+	char *data, *mask;
+	int white = 255, black = 0, shade;
+	int x0, x1, x2, y0, y1, y2;
+	int cur_x, cur_y, cur_sx, cur_sy, reverse;
+	static int first = 1;
+
+	if (first) {
+		first = 0;
+		setup_cursors();
+	}
+
+	data	= cursors[which]->data;		/* pattern data */
+	mask	= cursors[which]->mask;
+	cur_x	= cursors[which]->wx;		/* widths */
+	cur_y	= cursors[which]->wy;
+	cur_sx	= cursors[which]->sx;		/* shifts */
+	cur_sy	= cursors[which]->sy;
+	reverse	= cursors[which]->reverse;	/* reverse video */
+
+	if (reverse) {
+		black = white;
+		white = 0;
+	}
+
+	/*
+	 * notation:
+	 *   x0, y0: position after cursor shift (no edge corrections)
+	 *   x1, y1: corrected for lower boundary < 0
+	 *   x2, y2: position + cursor width and corrected for upper boundary
+	 */
+
+	x0 = x1 = x - cur_sx;		/* apply shift */
+	if (x1 < 0) x1 = 0;
+
+	y0 = y1 = y - cur_sy;
+	if (y1 < 0) y1 = 0;
+
+	x2 = x0 + cur_x;		/* apply width for upper endpoints */
+	if (x2 >= dpy_x) x2 = dpy_x - 1;
+
+	y2 = y0 + cur_y;
+	if (y2 >= dpy_y) y2 = dpy_y - 1;
+
+	/* save the patch and info about which cursor will overwrite it */
+	save_mouse_patch(x1, y1, x2 - x1, y2 - y1, x, y, which);
+
+	for (py = 0; py < cur_y; py++) {
+		if (y0 + py < 0 || y0 + py >= dpy_y) {
+			continue;		/* off screen */
+		}
+		for (px = 0; px < cur_x; px++) {
+			if (x0 + px < 0 || x0 + px >= dpy_x){
+				continue;	/* off screen */
+			}
+			cdata = data[px + py * cur_x];
+			cmask = mask[px + py * cur_x];
+
+			if (cmask != 'x') {
+				continue;	/* transparent */
+			}
+
+			shade = white;
+			if (cdata != cmask)  {
+				shade = black;
+			}
+
+			offset = (y0 + py)*bytes_per_line + (x0 + px)*pixelsize;
+
+			/* fill in each color byte in the fb */
+			for (i=0; i < pixelsize; i++) {
+				rfb_fb[offset+i] = (char) shade;
+			}
+		}
+	}
+
+	if (update) {
+		/* x and y of the real (X server) mouse */
+		static int mouse_x = -1;
+		static int mouse_y = -1;
+
+		if (x != mouse_x || y != mouse_y) { 
+			hint_t hint;
+
+			hint.x = x1;
+			hint.y = y2;
+			hint.w = x2 - x1;
+			hint.h = y2 - y1;
+
+			mark_hint(hint);
+			
+			if (mouse_x < 0) {
+				mouse_x = 0;
+			}
+			if (mouse_y < 0) {
+				mouse_y = 0;
+			}
+
+			/* XXX this ignores change of shift... */
+			x1 = mouse_x - cur_sx;
+			if (x1 < 0) x1 = 0;
+
+			y1 = mouse_y - cur_sy;
+			if (y1 < 0) y1 = 0;
+
+			x2 = mouse_x - cur_sx + cur_x;
+			if (x2 >= dpy_x) x2 = dpy_x - 1;
+
+			y2 = mouse_y - cur_sy + cur_y;
+			if (y2 >= dpy_y) y2 = dpy_y - 1;
+
+			hint.x = x1;
+			hint.y = y2;
+			hint.w = x2 - x1;
+			hint.h = y2 - y1;
+
+			mark_hint(hint);
+
+			mouse_x = x;
+			mouse_y = y;
+		}
+	}
+}
+
+void redraw_mouse(void) {
+	if (cur_saved) {
+		/* redraw saved mouse from info (save_mouse_patch) */
+		draw_mouse(cur_save_cx, cur_save_cy, cur_save_which, 0);
+	}
+}
+
+void update_mouse(void) {
+	Window root_w, child_w;
+	Bool ret;
+	int root_x, root_y, win_x, win_y, which = 0;
+	unsigned int mask;
+
+	X_LOCK;
+	ret = XQueryPointer(dpy, rootwin, &root_w, &child_w, &root_x, &root_y,
+	    &win_x, &win_y, &mask);
+	X_UNLOCK;
+
+	if (! ret) {
+		return;
+	}
+
+	if (show_root_cursor) {
+		int depth;
+		if ( (depth = tree_depth_cursor()) ) {
+			which = 0;
+		} else {
+			which = 1;
+		}
+	}
+
+	draw_mouse(root_x - off_x, root_y - off_y, which, 1);
+}
+
+void set_offset(void) {
+	Window w;
+	if (! subwin) {
+		return;
+	}
+	X_LOCK;
+	XTranslateCoordinates(dpy, window, rootwin, 0, 0, &off_x, &off_y, &w);
+	X_UNLOCK;
+}
+
+#define NCOLOR 256
+void set_colormap(void) {
+	static int first = 1;
+	static XColor color[NCOLOR], prev[NCOLOR];
+	Colormap cmap;
+	int i, diffs = 0;
+
+	if (first) {
+		screen->colourMap.count = NCOLOR;
+		screen->rfbServerFormat.trueColour = FALSE;
+		screen->colourMap.is16 = TRUE;
+		screen->colourMap.data.shorts = (unsigned short*)
+			malloc(3*sizeof(short) * NCOLOR);
+	}
+
+	for (i=0; i < NCOLOR; i++) {
+		color[i].pixel = i;
+		prev[i].red   = color[i].red;
+		prev[i].green = color[i].green;
+		prev[i].blue  = color[i].blue;
+	}
+
+	X_LOCK;
+
+	cmap = DefaultColormap(dpy, scr);
+
+	if (flash_cmap && ! first) {
+		XWindowAttributes attr;
+		Window r, c;
+		int rx, ry, wx, wy, tries = 0;
+		unsigned int m;
+
+		c = window;
+		while (c && tries++ < 16) {
+			/* XXX this is a hack, XQueryTree probably better. */
+			XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &m);
+			if (c && XGetWindowAttributes(dpy, c, &attr)) {
+				if (attr.colormap && attr.map_installed) {
+					cmap = attr.colormap;
+					break;
+				}
+			} else {
+				break;
+			}
+		}
+	}
+
+	XQueryColors(dpy, cmap, color, NCOLOR);
+
+	X_UNLOCK;
+
+	for(i=0; i < NCOLOR; i++) {
+		screen->colourMap.data.shorts[i*3+0] = color[i].red;
+		screen->colourMap.data.shorts[i*3+1] = color[i].green;
+		screen->colourMap.data.shorts[i*3+2] = color[i].blue;
+
+		if (prev[i].red   != color[i].red ||
+		    prev[i].green != color[i].green || 
+		    prev[i].blue  != color[i].blue ) {
+			diffs++;
+		}
+	}
+
+	if (diffs && ! first) {
+		rfbSetClientColourMaps(screen, 0, NCOLOR);
+	}
+
+	first = 0;
+}
+
+/*
+ * initialize the rfb framebuffer/screen
+ */
 void initialize_screen(int *argc, char **argv, XImage *fb) {
 
 	screen = rfbGetScreen(argc, argv, fb->width, fb->height,
@@ -463,35 +1020,18 @@ void initialize_screen(int *argc, char **argv, XImage *fb) {
 	screen->rfbServerFormat.depth = fb->depth;
 	screen->rfbServerFormat.trueColour = (uint8_t) TRUE;
 
-	if ( screen->rfbServerFormat.bitsPerPixel == 8 ) {
-		/* 8 bpp */
-		if(CellsOfScreen(ScreenOfDisplay(dpy,scr))) {
-			/* indexed colour */
-			XColor color[256];
-			int i;
-			screen->colourMap.count = 256;
-			screen->rfbServerFormat.trueColour = FALSE;
-			screen->colourMap.is16 = TRUE;
-			for(i=0;i<256;i++)
-				color[i].pixel=i;
-			XQueryColors(dpy,DefaultColormap(dpy,scr),color,256);
-			screen->colourMap.data.shorts = (unsigned short*)malloc(3*sizeof(short)*screen->colourMap.count);
-			for(i=0;i<screen->colourMap.count;i++) {
-				screen->colourMap.data.shorts[i*3+0] = color[i].red;
-				screen->colourMap.data.shorts[i*3+1] = color[i].green;
-				screen->colourMap.data.shorts[i*3+2] = color[i].blue;
-			}
-		} else {
-			/* true colour */
-			screen->rfbServerFormat.redShift   = 0;
-			screen->rfbServerFormat.greenShift = 2;
-			screen->rfbServerFormat.blueShift  = 5;
-			screen->rfbServerFormat.redMax     = 3;
-			screen->rfbServerFormat.greenMax   = 7;
-			screen->rfbServerFormat.blueMax    = 3;
-		}	
+	if ( screen->rfbServerFormat.bitsPerPixel == 8
+	    && CellsOfScreen(ScreenOfDisplay(dpy,scr)) ) {
+		/* indexed colour */
+		printf("using 8bpp indexed colour\n");
+		indexed_colour = 1;
+		set_colormap();
 	} else {
 		/* general case ... */
+		printf("using %dbpp depth=%d true colour\n", fb->bits_per_pixel,
+		    fb->depth);
+
+		/* convert masks to bit shifts and max # colors */
 		screen->rfbServerFormat.redShift = 0;
 		if ( fb->red_mask ) {
 			while ( ! (fb->red_mask
@@ -530,8 +1070,13 @@ void initialize_screen(int *argc, char **argv, XImage *fb) {
 	if (screen->rfbDeferUpdateTime == 5) {
 		screen->rfbDeferUpdateTime = defer_update;
 	}
-	if (shared && ! screen->rfbNeverShared) {
-		screen->rfbAlwaysShared = TRUE;
+	if (! screen->rfbNeverShared && ! screen->rfbAlwaysShared) {
+		if (shared) {
+			screen->rfbAlwaysShared = TRUE;
+		} else {
+			screen->rfbDontDisconnect = TRUE;
+			screen->rfbNeverShared = TRUE;
+		}
 	}
 
 	/* event callbacks: */
@@ -539,8 +1084,12 @@ void initialize_screen(int *argc, char **argv, XImage *fb) {
 	screen->kbdAddEvent = keyboard;
 	screen->ptrAddEvent = pointer;
 
-	cursor = rfbMakeXCursor(cursor_x, cursor_y, cur_data, cur_mask);
-	screen->cursor = cursor;
+	if (local_cursor) {
+		cursor = rfbMakeXCursor(CUR_SIZE, CUR_SIZE, CUR_DATA, CUR_MASK);
+		screen->cursor = cursor;
+	} else {
+		screen->cursor = NULL;
+	}
 
 	rfbInitServer(screen);
 
@@ -557,8 +1106,10 @@ void initialize_tiles() {
 	ntiles_y = (dpy_y - 1)/tile_y + 1;
 	ntiles = ntiles_x * ntiles_y;
 
-	tile_has_diff = (char *) malloc((size_t) (ntiles * sizeof(char)));
-	tile_tried    = (char *) malloc((size_t) (ntiles * sizeof(char)));
+	tile_has_diff = (unsigned char *)
+		malloc((size_t) (ntiles * sizeof(unsigned char)));
+	tile_tried    = (unsigned char *)
+		malloc((size_t) (ntiles * sizeof(unsigned char)));
 	tile_region = (region_t *) malloc((size_t) (ntiles * sizeof(region_t)));
 
 	/* there will never be more hints than tiles: */
@@ -589,35 +1140,75 @@ void set_fs_factor(int max) {
 	}
 }
 
-void initialize_shm() {
+/*
+ * set up an XShm image
+ */
+void shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
+    char *name) {
 
-	/* the tile (e.g. 32x32) shared memory area image: */
+	XImage *xim;
 
-	tile = XShmCreateImage(dpy, DefaultVisual(dpy, 0), bpp, ZPixmap,
-	    NULL, &tile_shm, tile_x, tile_y);
+	X_LOCK;
 
-	tile_shm.shmid = shmget(IPC_PRIVATE,
-	    tile->bytes_per_line * tile->height, IPC_CREAT | 0777);
+	xim = XShmCreateImage(dpy, visual, bpp, ZPixmap, NULL, shm, w, h);
 
-	tile_shm.shmaddr = tile->data = (char *) shmat(tile_shm.shmid, 0, 0);
-	tile_shm.readOnly = False;
+	if (xim == NULL) {
+		fprintf(stderr, "XShmCreateImage(%s) failed.\n", name);
+		exit(1);
+	}
 
-	XShmAttach(dpy, &tile_shm);
+	*ximg_ptr = xim;
 
+	shm->shmid = shmget(IPC_PRIVATE,
+	    xim->bytes_per_line * xim->height, IPC_CREAT | 0777);
 
-	/* the scanline (e.g. 1280x1) shared memory area image: */
+	if (shm->shmid == -1) {
+		fprintf(stderr, "shmget(%s) failed.\n", name);
+		perror("shmget");
+		exit(1);
+	}
 
-	scanline = XShmCreateImage(dpy, DefaultVisual(dpy, 0), bpp, ZPixmap,
-	    NULL, &scanline_shm, dpy_x, 1);
+	shm->shmaddr = xim->data = (char *) shmat(shm->shmid, 0, 0);
 
-	scanline_shm.shmid = shmget(IPC_PRIVATE,
-	    scanline->bytes_per_line * scanline->height, IPC_CREAT | 0777);
+	if (shm->shmaddr == (char *)-1) {
+		fprintf(stderr, "shmat(%s) failed.\n", name);
+		perror("shmat");
+		exit(1);
+	}
 
-	scanline_shm.shmaddr = scanline->data
-	    = (char *) shmat(scanline_shm.shmid, 0, 0);
-	scanline_shm.readOnly = False;
+	shm->readOnly = False;
+
+	if (! XShmAttach(dpy, shm)) {
+		fprintf(stderr, "XShmAttach(%s) failed.\n", name);
+		exit(1);
+	}
 
-	XShmAttach(dpy, &scanline_shm);
+	X_UNLOCK;
+}
+
+void shm_delete(XShmSegmentInfo *shm) {
+	shmdt(shm->shmaddr);
+	shmctl(shm->shmid, IPC_RMID, 0);
+}
+
+void shm_clean(XShmSegmentInfo *shm, XImage *xim) {
+	X_LOCK;
+	XShmDetach(dpy, shm);
+	XDestroyImage(xim);
+	X_UNLOCK;
+
+	shm_delete(shm);
+}
+
+void initialize_shm() {
+
+	/* the tile (e.g. 32x32) shared memory area image: */
+
+	shm_create(&tile_shm, &tile, tile_x, tile_y, "tile"); 
+
+	/* the scanline (e.g. 1280x1) shared memory area image: */
+
+	shm_create(&scanline_shm, &scanline, dpy_x, 1, "scanline"); 
 
 	/*
 	 * the fullscreen (e.g. 1280x1024/fs_factor) shared memory area image:
@@ -630,19 +1221,12 @@ void initialize_shm() {
 		return;
 	}
 
-	fullscreen = XShmCreateImage(dpy, DefaultVisual(dpy, 0), bpp, ZPixmap,
-	    NULL, &fullscreen_shm, dpy_x, dpy_y/fs_factor);
-
-	fullscreen_shm.shmid = shmget(IPC_PRIVATE,
-	    fullscreen->bytes_per_line * fullscreen->height, IPC_CREAT | 0777);
-
-	fullscreen_shm.shmaddr = fullscreen->data
-	    = (char *) shmat(fullscreen_shm.shmid, 0, 0);
-	fullscreen_shm.readOnly = False;
+	shm_create(&fullscreen_shm, &fullscreen, dpy_x, dpy_y/fs_factor,
+	    "fullscreen"); 
 
-	XShmAttach(dpy, &fullscreen_shm);
 }
 
+
 /*
  * A hint is a rectangular region built from 1 or more adjacent tiles
  * glued together.  Ultimately, this information in a single hint is sent
@@ -694,9 +1278,7 @@ void extend_tile_hint(int x, int y, int th, hint_t *hint) {
 }
 
 void save_hint(hint_t hint, int loc) {
-	/* copy it to the global array: */
-
-	hint_list[loc].x = hint.x;
+	hint_list[loc].x = hint.x;		/* copy to the global array */
 	hint_list[loc].y = hint.y;
 	hint_list[loc].w = hint.w;
 	hint_list[loc].h = hint.h;
@@ -804,17 +1386,20 @@ void tile_updates() {
  * diff) or a suspected change (from our various heuristics).
  *
  * Examine the whole tile for the y-range of difference, copy that
- * image difference to the vnc framebuffer, and do bookkeepping wrt
+ * image difference to the rfb framebuffer, and do bookkeepping wrt
  * the y-range and edge differences.
  *
  * This call is somewhat costly, maybe 1-2 ms.  Primarily the XShmGetImage
  * and then the memcpy/memcmp.
  */
+
 void copy_tile(int tx, int ty) {
 	int x, y, line, first_line, last_line;
 	int size_x, size_y, n, dw, dx;
 	int pixelsize = bpp >> 3;
-	short l_diff = 0, r_diff = 0;
+	unsigned short l_diff = 0, r_diff = 0;
+
+	int restored_patch = 0; /* for show_mouse */
 
 	char *src, *dst, *s_src, *s_dst, *m_src, *m_dst;
 	char *h_src, *h_dst;
@@ -823,16 +1408,14 @@ void copy_tile(int tx, int ty) {
 	y = ty * tile_y;
 
 	size_x = dpy_x - x;
-	if ( size_x > tile_x ) {
-		size_x = tile_x;
-	}
+	if ( size_x > tile_x ) size_x = tile_x;
+
 	size_y = dpy_y - y;
-	if ( size_y > tile_y ) {
-		size_y = tile_y;
-	}
+	if ( size_y > tile_y ) size_y = tile_y;
+
 	n = tx + ty * ntiles_x;		/* number of the tile */
 
-	X_LOCK
+	X_LOCK;
 	if ( size_x == tile_x && size_y == tile_y ) {
 		/* general case: */
 		XShmGetImage(dpy, window, tile, x, y, AllPlanes);
@@ -844,7 +1427,25 @@ void copy_tile(int tx, int ty) {
 		XGetSubImage(dpy, window, x, y, size_x, size_y, AllPlanes,
 		    ZPixmap, tile, 0, 0);
 	}
-	X_UNLOCK
+	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->data;
 	dst = screen->frameBuffer + y * bytes_per_line + x * pixelsize;
@@ -868,6 +1469,9 @@ void copy_tile(int tx, int ty) {
 	if (first_line == -1) {
 		/* tile has no difference, note it and get out: */
 		tile_has_diff[n] = 0;
+		if (restored_patch) {
+			redraw_mouse(); 
+		}
 		return;
 	} else {
 		/*
@@ -911,13 +1515,17 @@ void copy_tile(int tx, int ty) {
 		h_dst += bytes_per_line;
 	}
 
-	/* now copy the difference to the vnc framebuffer: */
+	/* now copy the difference to the rfb framebuffer: */
 	for (line = first_line; line <= last_line; line++) {
 		memcpy(s_dst, s_src, size_x * pixelsize);
 		s_src += tile->bytes_per_line;
 		s_dst += bytes_per_line;
 	}
 
+	if (restored_patch) {
+		redraw_mouse(); 
+	}
+
 	/* record all the info in the region array for this tile: */
 	tile_region[n].first_line = first_line;
 	tile_region[n].last_line  = last_line;
@@ -936,7 +1544,7 @@ void copy_tile(int tx, int ty) {
 
 /*
  * The copy_tile() call in the loop below copies the changed tile into
- * the vnc framebuffer.  Note that copy_tile() sets the tile_region
+ * the rfb framebuffer.  Note that copy_tile() sets the tile_region
  * struct to have info about the y-range of the changed region and also
  * whether the tile edges contain diffs (within distance tile_fuzz).
  *
@@ -947,8 +1555,9 @@ void copy_tile(int tx, int ty) {
  * See copy_tiles_backward_pass() for analogous checking upward and
  * left tiles.
  */
-void copy_all_tiles() {
+int copy_all_tiles() {
 	int x, y, n, m;
+	int diffs = 0;
 
 	for (y=0; y < ntiles_y; y++) {
 		for (x=0; x < ntiles_x; x++) {
@@ -964,38 +1573,40 @@ void copy_all_tiles() {
 				 */
 				continue;
 			}
+			diffs++;
 
 			/* neighboring tile downward: */
 			if ( (y+1) < ntiles_y && tile_region[n].bot_diff) {
 				m = x + (y+1) * ntiles_x;
 				if (! tile_has_diff[m]) {
-					tile_has_diff[m] = 1;
+					tile_has_diff[m] = 2;
 				}
 			}
 			/* neighboring tile to right: */
 			if ( (x+1) < ntiles_x && tile_region[n].right_diff) {
 				m = (x+1) + y * ntiles_x;
 				if (! tile_has_diff[m]) {
-					tile_has_diff[m] = 1;
+					tile_has_diff[m] = 2;
 				}
 			}
 		}
 	}
+	return diffs;
 }
 
 /*
  * Here starts a bunch of heuristics to guess/detect changed tiles.
  * They are:
  *   copy_tiles_backward_pass, fill_tile_gaps/gap_try, grow_islands/island_try
- * They are of varying utility... and perhaps some should be dropped.
  */
 
 /*
  * Try to predict whether the upward and/or leftward tile has been modified.
  * copy_all_tiles() has already done downward and rightward tiles.
  */
-void copy_tiles_backward_pass() {
+int copy_tiles_backward_pass() {
 	int x, y, n, m;
+	int diffs = 0;
 
 	for (y = ntiles_y - 1; y >= 0; y--) {
 	    for (x = ntiles_x - 1; x >= 0; x--) {
@@ -1009,6 +1620,7 @@ void copy_tiles_backward_pass() {
 
 		if (y >= 1 && ! tile_has_diff[m] && tile_region[n].top_diff) {
 			if (! tile_tried[m]) {
+				tile_has_diff[m] = 2;
 				copy_tile(x, y-1);
 			}
 		}
@@ -1017,11 +1629,18 @@ void copy_tiles_backward_pass() {
 
 		if (x >= 1 && ! tile_has_diff[m] && tile_region[n].left_diff) {
 			if (! tile_tried[m]) {
+				tile_has_diff[m] = 2;
 				copy_tile(x-1, y);
 			}
 		}
 	    }
 	}
+	for (n=0; n < ntiles; n++) {
+		if (tile_has_diff[n]) {
+			diffs++;
+		}
+	}
+	return diffs;
 }
 
 void gap_try(int x, int y, int *run, int *saw, int along_x) {
@@ -1067,10 +1686,11 @@ 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.
  */
-void fill_tile_gaps() {
+int fill_tile_gaps() {
 	int x, y, run, saw;
+	int n, diffs = 0;
 
 	/* horizontal: */
 	for (y=0; y < ntiles_y; y++) {
@@ -1089,6 +1709,13 @@ void fill_tile_gaps() {
 			gap_try(x, y, &run, &saw, 0);
 		}
 	}
+
+	for (n=0; n < ntiles; n++) {
+		if (tile_has_diff[n]) {
+			diffs++;
+		}
+	}
+	return diffs;
 }
 
 void island_try(int x, int y, int u, int v, int *run) {
@@ -1104,7 +1731,7 @@ void island_try(int x, int y, int u, int v, int *run) {
 	}
 
 	if (tile_has_diff[n] && ! tile_has_diff[m]) {
-		/* found discontinuity */
+		/* found a discontinuity */
 
 		if (tile_tried[m]) {
 			return;
@@ -1121,8 +1748,9 @@ void island_try(int x, int y, int u, int v, int *run) {
  * the boundary of the discontinuity (i.e. make the island larger).
  * Vertical scans are skipped since they do not seem to yield much...
  */
-void grow_islands() {
-	int x, y, run;
+int grow_islands() {
+	int x, y, n, run;
+	int diffs = 0;
 
 	/*
 	 * n.b. the way we scan here should keep an extension going,
@@ -1143,35 +1771,134 @@ void grow_islands() {
 			island_try(x, y, x-1, y, &run);
 		}
 	}
+	for (n=0; n < ntiles; n++) {
+		if (tile_has_diff[n]) {
+			diffs++;
+		}
+	}
+	return diffs;
 }
 
 /*
- * copy the whole X screen to the vnc framebuffer.  For a large enough
+ * copy the whole X screen to the rfb framebuffer.  For a large enough
  * number of changed tiles, this is faster than tiles scheme at retrieving
- * the info from the X server.  Bandwidth to client is another issue...
- * use -fs 1.0 to disable.
+ * the info from the X server.  Bandwidth to client and compression time
+ * are other issues...  use -fs 1.0 to disable.
  */
 void copy_screen() {
 	int pixelsize = bpp >> 3;
-	char *vnc_fb;
+	char *rfb_fb;
 	int i, y, block_size, xi;
 
 	block_size = (dpy_x * (dpy_y/fs_factor) * pixelsize);
 
-	vnc_fb = screen->frameBuffer;
+	rfb_fb = screen->frameBuffer;
 	y = 0;
 
+	X_LOCK;
+
 	for (i=0; i < fs_factor; i++) {
 		xi = XShmGetImage(dpy, window, fullscreen, 0, y, AllPlanes);
-		memcpy(vnc_fb, fullscreen->data, (size_t) block_size);
+		memcpy(rfb_fb, fullscreen->data, (size_t) block_size);
 
 		y += dpy_y / fs_factor;
-		vnc_fb += block_size;
+		rfb_fb += block_size;
 	}
 
+	X_UNLOCK;
+
 	rfbMarkRectAsModified(screen, 0, 0, dpy_x, dpy_y);
 }
 
+/*
+ * Utilities for managing the "naps" to cut down on amount of polling
+ */
+void nap_set(int tile_cnt) {
+
+	if (count == 0) {
+		/* roll up check for all NSCAN scans */
+		nap_ok = 0;
+		if (naptile && nap_diff_count < 2 * NSCAN * naptile) {
+			/* "2" is a fudge to permit a bit of bg drawing */
+			nap_ok = 1;
+		}
+		nap_diff_count = 0;
+	}
+
+	if (show_mouse) {
+		/* kludge for the up to 4 tiles the mouse patch could occupy */
+		if ( tile_cnt > 4) {
+			last_event = time(0);
+		}
+	} else if (tile_cnt != 0) {
+		last_event = time(0);
+	}
+}
+
+void nap_sleep(int ms, int split) {
+	int i, input = got_user_input;
+
+	/* split it up to improve the wakeup time */
+	for (i=0; i<split; i++) {
+		usleep(ms * 1000 / split);
+		if (! use_threads && i != split - 1) {
+			rfbProcessEvents(screen, -1);
+		}
+		if (input != got_user_input) {
+			break;
+		}
+	}
+}
+
+void nap_check(int tile_cnt) {
+	time_t now;
+
+	nap_diff_count += tile_cnt;
+
+	if (! take_naps) {
+		return;
+	}
+
+	now = time(0);
+
+	if (screen_blank > 0) {
+		int dt = (int) (now - last_event);
+		int ms = 1500;
+
+		/* if no activity, pause here for a second or so. */
+		if (dt > screen_blank) {
+			nap_sleep(ms, 8);
+			return;
+		}
+	}
+	if (naptile && nap_ok && tile_cnt < naptile) {
+		int ms = napfac * waitms;
+		ms = ms > napmax ? napmax : ms;
+		if (now - last_input <= 2) {
+			nap_ok = 0;
+		} else {
+			nap_sleep(ms, 1);
+		}
+	}
+}
+
+void ping_clients(int tile_cnt) {
+	static time_t last_send = 0;
+	time_t now = time(0);
+
+	if (rfbMaxClientWait <= 3000) {
+		rfbMaxClientWait = 3000;
+		printf("reset rfbMaxClientWait to %d ms.\n", rfbMaxClientWait);
+	}
+	if (tile_cnt) {
+		last_send = now;
+	} else if (now - last_send > 1) {
+		/* Send small heartbeat to client */
+		rfbMarkRectAsModified(screen, 0, 0, 1, 1);
+		last_send = now;
+	}
+}
+
 /*
  * Loop over 1-pixel tall horizontal scanlines looking for changes.  
  * Record the changes in tile_has_diff[].  Scanlines in the loop are
@@ -1179,28 +1906,45 @@ void copy_screen() {
  * starting offset ystart ( < NSCAN ) from scanlines[].
  */
 int scan_display(int ystart, int rescan) {
+	char *src, *dst;
+	int pixelsize = bpp >> 3;
 	int x, y, w, n;
 	int tile_count = 0;
-	int pixelsize = bpp >> 3;
-	char *src, *dst;
+	int whole_line = 1, nodiffs;
 
 	y = ystart;
 
 	while (y < dpy_y) {
 
 		/* grab the horizontal scanline from the display: */
-		X_LOCK
+		X_LOCK;
 		XShmGetImage(dpy, window, scanline, 0, y, AllPlanes);
-		X_UNLOCK
+		X_UNLOCK;
+
+		/* for better memory i/o try the whole line at once */
+		src = scanline->data;
+		dst = screen->frameBuffer + y * bytes_per_line;
+
+		nodiffs = 0;
+		if (whole_line && ! memcmp(dst, src, bytes_per_line)) {
+			/* no changes anywhere in scan line */
+			nodiffs = 1;
+			if (! rescan) {
+				y += NSCAN;
+				continue;
+			}
+		}
 
 		x = 0;
 		while (x < dpy_x) {
 			n = (x/tile_x) + (y/tile_y) * ntiles_x;
 
-			if (rescan && tile_has_diff[n]) {
-				tile_count++;
-				x += NSCAN;
-				continue;
+			if (rescan) {
+				if (nodiffs || tile_has_diff[n]) {
+					tile_count += tile_has_diff[n];
+					x += NSCAN;
+					continue;
+				}
 			}
 
 			/* set ptrs to correspond to the x offset: */
@@ -1209,13 +1953,13 @@ int scan_display(int ystart, int rescan) {
 			    + x * pixelsize;
 
 			/* compute the width of data to be compared: */
-			if ( x + NSCAN > dpy_x ) {
+			if (x + NSCAN > dpy_x) {
 				w = dpy_x - x;
 			} else {
 				w = NSCAN;
 			}
 
-			if (memcmp(dst, src, w * pixelsize) ) {
+			if (memcmp(dst, src, w * pixelsize)) {
 				/* found a difference, record it: */
 				tile_has_diff[n] = 1;
 				tile_count++;		
@@ -1231,7 +1975,7 @@ int scan_display(int ystart, int rescan) {
  * toplevel for the scanning, rescanning, and applying the heuristics.
  */
 void scan_for_updates() {
-	int i, tile_count;
+	int i, tile_count, tile_diffs;
 	double frac1 = 0.1;   /* tweak parameter to try a 2nd scan_display() */
 
 	for (i=0; i < ntiles; i++) {
@@ -1247,9 +1991,25 @@ void scan_for_updates() {
 	count++;
 	count %= NSCAN;
 
+	if (count % (NSCAN/4) == 0)  {
+		if (subwin) {
+			set_offset();	/* follow the subwindow */
+		}
+		if (indexed_colour) {	/* check for changed colormap */
+			set_colormap();
+		}
+	}
+
+	if (show_mouse && ! use_threads) {
+		/* single-thread is safe to do it here for all scanning */
+		restore_mouse_patch();
+	}
+
 	/* scan with the initial y to the jitter value from scanlines: */
 	tile_count = scan_display( scanlines[count], 0 );
 
+	nap_set(tile_count);
+
 	if (fs_factor && frac1 >= fs_frac) {
 		/* make frac1 < fs_frac if fullscreen updates are enabled */
 		frac1 = fs_frac/2.0;
@@ -1274,35 +2034,45 @@ void scan_for_updates() {
 		/*
 		 * At some number of changed tiles it is better to just
 		 * copy the full screen at once.  I.e. time = c1 + m * r1
-		 * where m is number of tiles and c1 is the scan_display()
-		 * time: for some m it crosses the full screen update time.
+		 * where m is number of tiles, r1 is the copy_tile()
+		 * time, and c1 is the scan_display() time: for some m
+		 * it crosses the full screen update time.
+		 *
+		 * We try to predict that crossover with the fs_frac
+		 * fudge factor... seems to be about 1/2 the total number
+		 * of tiles.  n.b. this ignores network bandwidth,
+		 * compression time etc...
 		 *
-		 * We try to predict that crossover with the fs_frac fudge
-		 * factor... seems to be about 1/2 the total number
-		 * of tiles.  n.b. this ignores network bandwidth, etc.
-		 * use -fs 1.0 to disable on slow links.
+		 * Use -fs 1.0 to disable on slow links.
 		 */
 		if (fs_factor && tile_count > fs_frac * ntiles) {
 			copy_screen();
+			if (show_mouse) {
+				if (! use_threads) {
+					redraw_mouse();
+				}
+				update_mouse();
+			}
+			nap_check(tile_count);
 			return;
 		}
 	}
 
-	/* copy all tiles with differences from display to vnc framebuffer: */
-	copy_all_tiles();
+	/* copy all tiles with differences from display to rfb framebuffer: */
+	tile_diffs = copy_all_tiles();
 
 	/*
 	 * This backward pass for upward and left tiles complements what
 	 * was done in copy_all_tiles() for downward and right tiles.
 	 */
-	copy_tiles_backward_pass();
+	tile_diffs = copy_tiles_backward_pass();
 
-	if (grow_fill) {
-		grow_islands();
+	if (grow_fill && tile_diffs > 4) {
+		tile_diffs = grow_islands();
 	}
 
-	if (gaps_fill) {
-		fill_tile_gaps();
+	if (gaps_fill && tile_diffs > 4) {
+		tile_diffs = fill_tile_gaps();
 	}
 
 	if (use_hints) {
@@ -1310,15 +2080,26 @@ void scan_for_updates() {
 	} else {
 		tile_updates();	/* send each tile change individually */
 	}
+
+	/* Work around threaded rfbProcessClientMessage() calls timeouts */
+	if (use_threads) {
+		ping_clients(tile_diffs);
+	}
+
+	/* Handle the remote mouse pointer */
+	if (show_mouse) {
+		if (! use_threads) {
+			redraw_mouse();
+		}
+		update_mouse();
+	}
+
+	nap_check(tile_diffs);
 }
 
 void watch_loop(void) {
 	int cnt = 0;
 
-#if !defined(HAVE_PTHREADS)
-	use_threads = 0;
-#endif
-
 	if (use_threads) {
 		rfbRunEventLoop(screen, -1, TRUE);
 	}
@@ -1337,7 +2118,7 @@ void watch_loop(void) {
 		}
 
 		if (shut_down) {
-			clean_up_exit();
+			clean_up_exit(0);
 		}
 
 		if (! screen->rfbClientHead) {	/* waiting for a client */
@@ -1346,6 +2127,7 @@ void watch_loop(void) {
 		}
 
 		rfbUndrawCursor(screen);
+
 		scan_for_updates();
 
 		usleep(waitms * 1000);
@@ -1356,92 +2138,188 @@ void watch_loop(void) {
 
 void print_help() {
 	char help[] = 
+"\n"
 "x11vnc options:\n"
 "\n"
+"-display disp          X11 server display to connect to, the X server process\n"
+"                       must be running on same machine and support MIT-SHM.\n"
+"-id windowid           show the window corresponding to <windowid> not the\n"
+"                       entire display. Warning: bugs! new toplevels missed!...\n"
+"-flashcmap             in 8bpp indexed color, let the installed colormap flash\n"
+"                       as the pointer moves from window to window (slow).\n"
+"\n"
+"-viewonly              clients can only watch (default %s).\n"
+"-shared                VNC display is shared (default %s).\n"
+"-many                  keep listening for more connections rather than exiting\n"
+"                       as soon as the first clients disconnect.\n"
+"\n"
+"-modtweak              handle AltGr/Shift modifiers for differing languages\n"
+"                       between client and host (default %s).\n"
+"-nomodtweak            send the keysym directly to the X server.\n"
+"\n"
+"-nocursor              do not have the viewer show a local cursor.\n"
+"-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"
+"\n"
 "-defer time            time in ms to wait for updates before sending to\n"
-"                       client [rfbDeferUpdateTime]  (default %d)\n"
+"                       client [rfbDeferUpdateTime]  (default %d).\n"
 "-wait time             time in ms to pause between screen polls.  used\n"
-"                       to cut down on load (default %d)\n"
+"                       to cut down on load (default %d).\n"
+"-nap                   monitor activity and if low take longer naps between\n" 
+"                       polls to really cut down load when idle (default %s).\n"
+#ifdef HAVE_LIBPTHREAD
+"-threads               whether or not to use the threaded libvncserver\n"
+"-nothreads             algorithm [rfbRunEventLoop] (default %s).\n"
+#endif
 "\n"
+"-fs f                  if the fraction of changed tiles in a poll is greater\n"
+"                       than f, the whole screen is updated (default %.2f).\n"
 "-gaps n                heuristic to fill in gaps in rows or cols of n or less\n"
 "                       tiles.  used to improve text paging (default %d).\n"
 "-grow n                heuristic to grow islands of changed tiles n or wider\n"
 "                       by checking the tile near the boundary (default %d).\n"
-"-fs f                  if the fraction of changed tiles in a poll is greater\n"
-"                       than f, the whole screen is updated (default %.2f)\n"
-"-fuzz n                tolerance in pixels to mark a tiles edges as changed.\n"
+"-fuzz n                tolerance in pixels to mark a tiles edges as changed\n"
 "                       (default %d).\n"
 "-hints                 use krfb/x0rfbserver hints (glue changed adjacent\n"
 "                       horizontal tiles into one big rectangle)  (default %s).\n"
 "-nohints               do not use hints; send each tile separately.\n"
-"\n"
-"-modtweak              handle AltGr/Shift modifiers for differing languages\n"
-"                       between client and host (default %d).\n"
-"-nomodtweak            send the keysym directly to the X server.\n"
-"-threads               use threaded algorithm [rfbRunEventLoop] if compiled\n"
-"                       with threads (default %s).\n"
-"-nothreads             do not use [rfbRunEventLoop].\n"
-"-viewonly              clients can only watch (default %s).\n"
-"-shared                VNC display is shared (default %s)\n"
+"%s\n"
 "\n"
 "These options are passed to libvncserver:\n"
 "\n"
 ;
-	fprintf(stderr, help, defer_update, waitms, gaps_fill, grow_fill,
-	    fs_frac, tile_fuzz,
-	    use_hints ? "on":"off", use_modifier_tweak ? "on":"off",
-	    use_threads ? "on":"off", view_only ? "on":"off",
-	    shared ? "on":"off");
+	fprintf(stderr, help,
+		view_only ? "on":"off",
+		shared ? "on":"off",
+		use_modifier_tweak ? "on":"off",
+		defer_update,
+		waitms,
+		take_naps ? "on":"off",
+#ifdef HAVE_LIBPTHREAD
+		use_threads ? "on":"off",
+#endif
+		fs_frac,
+		gaps_fill,
+		grow_fill,
+		tile_fuzz,
+		use_hints ? "on":"off",
+		""
+	);
+
 	rfbUsage();
 	exit(1);
 }
 
+/*
+ * choose a desktop name
+ */
+#define MAXN 256
+char *choose_title(char *display) {
+	static char title[(MAXN+10)];	
+	strcpy(title, "x11vnc");
+
+	if (display == NULL) {
+		display = getenv("DISPLAY");
+	}
+	if (display == NULL) {
+		return title;
+	}
+	title[0] = '\0';
+	if (display[0] == ':') {
+		char host[MAXN];
+		if (gethostname(host, MAXN) == 0) {
+			strncpy(title, host, MAXN - strlen(title));
+		}
+	}
+	strncat(title, display, MAXN - strlen(title));
+	if (subwin) {
+		char *name;
+		if (XFetchName(dpy, window, &name)) {
+			strncat(title, " ",  MAXN - strlen(title));
+			strncat(title, name, MAXN - strlen(title));
+		}
+	}
+	return title;
+}
+
 int main(int argc, char** argv) {
 
 	XImage *fb;
 	int i, ev, er, maj, min;
 	char *use_dpy = NULL;
-
+	int dt = 0;
 
 	/* used to pass args we do not know about to rfbGetScreen(): */
 	int argc2 = 1; char *argv2[100];
+
 	argv2[0] = argv[0];
 	
 	for (i=1; i < argc; i++) {
 		if (!strcmp(argv[i], "-display")) {
 			use_dpy = argv[++i];
+		} else if (!strcmp(argv[i], "-id")) {
+			/* expt to just show one window. XXX not finished. */
+			if (sscanf(argv[++i], "0x%x", &subwin) != 1) {
+				if (sscanf(argv[i], "%d", &subwin) != 1) {
+					printf("bad -id arg: %s\n", argv[i]);
+					exit(1);
+				}
+			}
+		} else if (!strcmp(argv[i], "-flashcmap")) {
+			flash_cmap = 1;
+		} else if (!strcmp(argv[i], "-viewonly")) {
+			view_only = 1;
+		} else if (!strcmp(argv[i], "-shared")) {
+			shared = 1;
+		} else if (!strcmp(argv[i], "-many")) {
+			connect_once = 0;
+		} else if (!strcmp(argv[i], "-modtweak")) {
+			use_modifier_tweak = 1;
+		} else if (!strcmp(argv[i], "-nomodtweak")) {
+			use_modifier_tweak = 0;
+		} else if (!strcmp(argv[i], "-nocursor")) {
+			local_cursor = 0;
+		} else if (!strcmp(argv[i], "-mouse")) {
+			show_mouse = 1;
+		} else if (!strcmp(argv[i], "-mouseX")) {
+			show_mouse = 1;
+			show_root_cursor = 1;
+		} else if (!strcmp(argv[i], "-X")) {
+			show_mouse = 1;
+			show_root_cursor = 1;
+			local_cursor = 0;
 		} else if (!strcmp(argv[i], "-defer")) {
 			defer_update = atoi(argv[++i]);
 		} else if (!strcmp(argv[i], "-wait")) {
 			waitms = atoi(argv[++i]);
+		} else if (!strcmp(argv[i], "-nap")) {
+			take_naps = 1;
+#ifdef HAVE_LIBPTHREAD
+		} else if (!strcmp(argv[i], "-threads")) {
+			use_threads = 1;
+		} else if (!strcmp(argv[i], "-nothreads")) {
+			use_threads = 0;
+#endif
+		} else if (!strcmp(argv[i], "-fs")) {
+			fs_frac = atof(argv[++i]);
 		} else if (!strcmp(argv[i], "-gaps")) {
 			gaps_fill = atoi(argv[++i]);
 		} else if (!strcmp(argv[i], "-grow")) {
 			grow_fill = atoi(argv[++i]);
-		} else if (!strcmp(argv[i], "-fs")) {
-			fs_frac = atof(argv[++i]);
 		} else if (!strcmp(argv[i], "-fuzz")) {
 			tile_fuzz = atoi(argv[++i]);
 		} else if (!strcmp(argv[i], "-hints")) {
 			use_hints = 1;
 		} else if (!strcmp(argv[i], "-nohints")) {
 			use_hints = 0;
-		} else if (!strcmp(argv[i], "-threads")) {
-			use_threads = 1;
-		} else if (!strcmp(argv[i], "-nothreads")) {
-			use_threads = 0;
-		} else if (!strcmp(argv[i], "-modtweak")) {
-			use_modifier_tweak = 1;
-		} else if (!strcmp(argv[i], "-nomodtweak")) {
-			use_modifier_tweak = 0;
-		} else if (!strcmp(argv[i], "-viewonly")) {
-			view_only = 1;
-		} else if (!strcmp(argv[i], "-shared")) {
-			shared = 1;
 		} else if (!strcmp(argv[i], "-h")
 		    || !strcmp(argv[i], "-help")) {
 			print_help();
 		} else {
+			if (!strcmp(argv[i], "-desktop")) {
+				dt = 1;
+			}
 			/* otherwise copy it for use below. */
 			printf("passing arg to libvncserver: %s\n", argv[i]);
 			if (argc2 < 100) {
@@ -1449,22 +2327,31 @@ int main(int argc, char** argv) {
 			}
 		}
 	}
+		
 	if (tile_fuzz < 1) {
 		tile_fuzz = 1;
 	}
 	if (waitms < 0) {
 		waitms = 0;
 	}
+	printf("viewonly:   %d\n", view_only);
+	printf("shared:     %d\n", shared);
+	printf("conn_once:  %d\n", connect_once);
+	printf("mod_tweak:  %d\n", use_modifier_tweak);
+	printf("loc_curs:   %d\n", local_cursor);
+	printf("mouse:      %d\n", show_mouse);
+	printf("root_curs:  %d\n", show_root_cursor);
 	printf("defer:      %d\n", defer_update);
 	printf("waitms:     %d\n", waitms);
-	printf("tile_fuzz:  %d\n", tile_fuzz);
+	printf("take_naps:  %d\n", take_naps);
+	printf("threads:    %d\n", use_threads);
+	printf("fs_frac:    %.2f\n", fs_frac);
 	printf("gaps_fill:  %d\n", gaps_fill);
 	printf("grow_fill:  %d\n", grow_fill);
-	printf("fs_frac:    %.2f\n", fs_frac);
+	printf("tile_fuzz:  %d\n", tile_fuzz);
 	printf("use_hints:  %d\n", use_hints);
-	printf("viewonly:   %d\n", view_only);
-	printf("shared:     %d\n", shared);
 
+	X_INIT;
 	if (use_dpy) {
 		dpy = XOpenDisplay(use_dpy);
 	} else if ( (use_dpy = getenv("DISPLAY")) ) {
@@ -1472,6 +2359,7 @@ int main(int argc, char** argv) {
 	} else {
 		dpy = XOpenDisplay("");
 	}
+
 	if (! dpy) {
 		printf("XOpenDisplay failed (%s)\n", use_dpy);
 		exit(1);
@@ -1499,10 +2387,36 @@ int main(int argc, char** argv) {
 	XTestGrabControl(dpy, True);
 
 	scr = DefaultScreen(dpy);
-	window = RootWindow(dpy, scr);
+	rootwin = RootWindow(dpy, scr);
+
+	if (! subwin) {
+		window = rootwin;
+		dpy_x = DisplayWidth(dpy, scr);
+		dpy_y = DisplayHeight(dpy, scr);
+		off_x = 0;
+		off_y = 0;
+		visual = DefaultVisual(dpy, scr);
+	} else {
+		/* experiment to share just one window */
+		XWindowAttributes attr;
+
+		window = (Window) subwin;
+		if ( ! XGetWindowAttributes(dpy, window, &attr) ) {
+			printf("bad window: 0x%x\n", window);
+			exit(1);
+		}
+		dpy_x = attr.width;
+		dpy_y = attr.height;
+		visual = attr.visual;
+
+		/* show_mouse has some segv crashes as well */
+		if (show_root_cursor) {
+			show_root_cursor = 0;
+			printf("disabling root cursor drawing for subwindow\n");
+		}
 
-	dpy_x = DisplayWidth(dpy, scr);
-	dpy_y = DisplayHeight(dpy, scr);
+		set_offset();
+	}
 
 	fb = XGetImage(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, ZPixmap);
 	printf("Read initial data from display into framebuffer.\n");
@@ -1511,8 +2425,14 @@ int main(int argc, char** argv) {
 		printf("warning: 24 bpp may have poor performance.\n");
 	}
 
+	if (! dt) {
+		static char str[] = "-desktop";
+		argv2[argc2++] = str;
+		argv2[argc2++] = choose_title(use_dpy);
+	}
+
 	/*
-	 * n.b. we do not have to X_LOCK X11 calls until watch_loop()
+	 * n.b. we do not have to X_LOCK any X11 calls until watch_loop()
 	 * is called since we are single-threaded until then.
 	 */
 
@@ -1522,6 +2442,8 @@ int main(int argc, char** argv) {
 
 	initialize_shm();
 
+	set_signals();
+
 	if (use_modifier_tweak) {
 		initialize_keycodes();
 	}
-- 
cgit v1.2.3

