From 872405e6988eb4bc8897cd7d776b7250291e3ed5 Mon Sep 17 00:00:00 2001
From: dscho <dscho>
Date: Mon, 18 Aug 2003 09:25:09 +0000
Subject: Karl Runge: 8bpp handling now much better, single window also, many
 improvements

---
 contrib/x11vnc.c | 262 ++++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 199 insertions(+), 63 deletions(-)

diff --git a/contrib/x11vnc.c b/contrib/x11vnc.c
index d37d51a..051bafd 100644
--- a/contrib/x11vnc.c
+++ b/contrib/x11vnc.c
@@ -43,14 +43,12 @@
  *
  * Obtain the libvncserver package (http://libvncserver.sourceforge.net).
  * As of 12/2002 this version of x11vnc.c is contained in the libvncserver
- * 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.
+ * CVS tree and released in version 0.5.
  *
  * 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.
+ * or CPPFLAGS (e.g. before running the libvncserver configure).  The
+ * threaded mode is a bit more responsive, but can be unstable.
  *
  * Known shortcomings:
  *
@@ -64,10 +62,6 @@
  * 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...  
@@ -81,6 +75,17 @@
  * big areas near the cursor.  The mouse painting is in general a bit
  * ragged and not very pleasant.
  *
+ * Windows using visuals other than the default X visual may have
+ * their colors messed up.  When using 8bpp indexed color, the colormap
+ * is attempted to be followed, but may become out of date.  Use the
+ * -flashcmap option to have colormap flashing as the pointer moves
+ * windows with private colormaps (slow).  Displays with mixed 8bpp and
+ * 24bpp visuals will incorrect display the non-default one.
+ *
+ * Feature -id <windowid> can be picky: it can crash for things like the
+ * window not sufficiently mapped into server memory, use of -mouse, etc.
+ * SaveUnders menus, popups, etc will not be seen.
+ *
  * Occasionally, a few tile updates can be missed leaving a patch of
  * color that needs to be refreshed.
  *
@@ -99,18 +104,21 @@
 #include <X11/extensions/XTest.h>
 #include <X11/keysym.h>
 
+#include <sys/stat.h>
+#include <fcntl.h>
+
 #include <rfb/rfb.h>
 
 /* X and rfb framebuffer */
 Display *dpy = 0;
 Visual *visual;
 Window window, rootwin;
-int subwin = 0;
 int scr;
 int bpp;
 int button_mask = 0;
 int dpy_x, dpy_y;
 int off_x, off_y;
+int subwin = 0;
 int indexed_colour = 0;
 
 XImage *tile;
@@ -206,7 +214,12 @@ int cursor_x, cursor_y;		/* x and y from the viewer(s) */
 int got_user_input = 0;
 int shut_down = 0;	
 
-#if defined(LIBVNCSERVER_HAVE_LIBPTHREAD) && defined(LIBVNCSERVER_X11VNC_THREADED)
+int quiet = 0;
+#if defined(LIBVNCSERVER_X11VNC_THREADED) && ! defined(X11VNC_THREADED)
+#define X11VNC_THREADED
+#endif
+
+#if defined(LIBVNCSERVER_HAVE_LIBPTHREAD) && defined(X11VNC_THREADED)
 	int use_threads = 1;
 #else
 	int use_threads = 0;
@@ -263,9 +276,9 @@ void interrupted (int sig) {
 	}
 	exit_flag++;
 	if (sig == 0) {
-		printf("caught X11 error:\n");
+		fprintf(stderr, "caught X11 error:\n");
 	} else {
-		printf("caught signal: %d\n", sig);
+		fprintf(stderr, "caught signal: %d\n", sig);
 	}
 	/*
 	 * to avoid deadlock, etc, just delete the shm areas and
@@ -308,7 +321,7 @@ void set_signals(void) {
 
 void client_gone(rfbClientPtr client) {
 	if (connect_once) {
-		printf("viewer exited.\n");
+		fprintf(stderr, "viewer exited.\n");
 		clean_up_exit(0);
 	}
 }
@@ -319,8 +332,8 @@ enum rfbNewClientAction new_client(rfbClientPtr client) {
 	if (connect_once) {
 		if (screen->rfbDontDisconnect && screen->rfbNeverShared) {
 			if (! shared && client_count) {
-				printf("denying additional client: %s\n",
-				    client->host);
+				fprintf(stderr, "denying additional client:"
+				     " %s\n", client->host);
 				return(RFB_CLIENT_REFUSE);
 			}
 		}
@@ -771,9 +784,14 @@ void draw_mouse(int x, int y, int which, int update) {
 	cur_sy	= cursors[which]->sy;
 	reverse	= cursors[which]->reverse;	/* reverse video */
 
+	if (indexed_colour) {
+		black = BlackPixel(dpy, scr) % 256;
+		white = WhitePixel(dpy, scr) % 256;
+	}
 	if (reverse) {
+		int tmp = black;
 		black = white;
-		white = 0;
+		white = tmp;
 	}
 
 	/*
@@ -924,7 +942,8 @@ void set_colormap(void) {
 	static int first = 1;
 	static XColor color[NCOLOR], prev[NCOLOR];
 	Colormap cmap;
-	int i, diffs = 0;
+	Visual *vis;
+	int i, ncells, diffs = 0;
 
 	if (first) {
 		screen->colourMap.count = NCOLOR;
@@ -935,7 +954,6 @@ void set_colormap(void) {
 	}
 
 	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;
@@ -944,6 +962,24 @@ void set_colormap(void) {
 	X_LOCK;
 
 	cmap = DefaultColormap(dpy, scr);
+	ncells = CellsOfScreen(ScreenOfDisplay(dpy, scr));
+	vis = visual;
+
+	if (subwin) {
+		XWindowAttributes attr;
+
+		if (XGetWindowAttributes(dpy, window, &attr)) {
+			cmap = attr.colormap;
+			vis = attr.visual;
+			ncells = vis->map_entries;
+		}
+	}
+
+	if (first && ncells != NCOLOR) {
+		fprintf(stderr, "set_colormap: number of cells is %d"
+		    " instead of %d.\n", ncells, NCOLOR);
+		screen->colourMap.count = ncells;
+	}
 
 	if (flash_cmap && ! first) {
 		XWindowAttributes attr;
@@ -953,11 +989,13 @@ void set_colormap(void) {
 
 		c = window;
 		while (c && tries++ < 16) {
-			/* XXX this is a hack, XQueryTree probably better. */
+			/* XQueryTree somehow? */
 			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;
+					vis = attr.visual;
+					ncells = vis->map_entries;
 					break;
 				}
 			} else {
@@ -965,12 +1003,32 @@ void set_colormap(void) {
 			}
 		}
 	}
+	if (ncells > NCOLOR) {
+		fprintf(stderr, "set_colormap: big problem: ncells=%d > %d\n",
+		    ncells, NCOLOR);
+	}
+
+	if (vis->class == TrueColor || vis->class == DirectColor) {
+		/*
+		 * Kludge to make 8bpp TrueColor & DirectColor be like
+		 * the StaticColor map.  The ncells = 8 is "8 per subfield"
+		 * mentioned in xdpyinfo.  Looks OK... likely fortuitously.
+		 */
+		if (ncells == 8) {
+			ncells = NCOLOR;
+		}
+	}
+
+	for (i=0; i < ncells; i++) {
+		color[i].pixel = i;
+		color[i].pad = 0;
+	}
 
-	XQueryColors(dpy, cmap, color, NCOLOR);
+	XQueryColors(dpy, cmap, color, ncells);
 
 	X_UNLOCK;
 
-	for(i=0; i < NCOLOR; i++) {
+	for(i=0; i < ncells; 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;
@@ -983,7 +1041,7 @@ void set_colormap(void) {
 	}
 
 	if (diffs && ! first) {
-		rfbSetClientColourMaps(screen, 0, NCOLOR);
+		rfbSetClientColourMaps(screen, 0, ncells);
 	}
 
 	first = 0;
@@ -993,6 +1051,7 @@ void set_colormap(void) {
  * initialize the rfb framebuffer/screen
  */
 void initialize_screen(int *argc, char **argv, XImage *fb) {
+	int have_masks = 0;
 
 	screen = rfbGetScreen(argc, argv, fb->width, fb->height,
 	    fb->bits_per_pixel, 8, fb->bits_per_pixel/8);
@@ -1001,17 +1060,20 @@ void initialize_screen(int *argc, char **argv, XImage *fb) {
 	screen->rfbServerFormat.bitsPerPixel = fb->bits_per_pixel;
 	screen->rfbServerFormat.depth = fb->depth;
 	screen->rfbServerFormat.trueColour = (uint8_t) TRUE;
+	have_masks = (fb->red_mask|fb->green_mask|fb->blue_mask != 0);
 
-	if ( screen->rfbServerFormat.bitsPerPixel == 8
+	if ( ! have_masks && screen->rfbServerFormat.bitsPerPixel == 8
 	    && CellsOfScreen(ScreenOfDisplay(dpy,scr)) ) {
 		/* indexed colour */
-		printf("using 8bpp indexed colour\n");
+		if (! quiet) fprintf(stderr, "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);
+		if (! quiet) {
+			fprintf(stderr, "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;
@@ -1135,7 +1197,7 @@ void shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h,
 	xim = XShmCreateImage(dpy, visual, bpp, ZPixmap, NULL, shm, w, h);
 
 	if (xim == NULL) {
-		rfbErr( "XShmCreateImage(%s) failed.\n", name);
+		rfbErr("XShmCreateImage(%s) failed.\n", name);
 		exit(1);
 	}
 
@@ -1199,7 +1261,7 @@ void initialize_shm() {
 	 */
 	set_fs_factor(1024 * 1024);
 	if (! fs_factor) {
-		printf("warning: fullscreen updates are disabled.\n");
+		fprintf(stderr, "warning: fullscreen updates are disabled.\n");
 		return;
 	}
 
@@ -1870,7 +1932,8 @@ void ping_clients(int tile_cnt) {
 
 	if (rfbMaxClientWait <= 3000) {
 		rfbMaxClientWait = 3000;
-		printf("reset rfbMaxClientWait to %d ms.\n", rfbMaxClientWait);
+		fprintf(stderr, "reset rfbMaxClientWait to %d ms.\n",
+		    rfbMaxClientWait);
 	}
 	if (tile_cnt) {
 		last_send = now;
@@ -2094,6 +2157,7 @@ void watch_loop(void) {
 
 			if (got_user_input && cnt % 10 != 0) {
 				/* every 10-th drops thru to code below... */
+				cnt++;
 				XFlush(dpy);
 				continue;
 			}
@@ -2121,7 +2185,24 @@ void watch_loop(void) {
 void print_help() {
 	char help[] = 
 "\n"
-"x11vnc options:\n"
+"x11vnc: allow VNC connections to real X11 displays.\n"
+"\n"
+"Typical usage is:\n"
+"\n"
+"   Run this command in a shell on the remote machine \"far-host\":\n"
+"\n"
+"       x11vnc -display :0\n"
+"\n"
+"   Then run this in another window on the machine you are sitting at:\n"
+"\n"
+"       vncviewer far-host:0\n"
+"\n"
+"Once x11vnc establishes connections with the X11 server and starts\n"
+"listening as a VNC server it will print out a string: PORT=XXXX where\n"
+"XXXX is typically 5900 (the default VNC port).  One would next run something\n"
+"like this on the local machine: \"vncviewer host:N\" where N is XXXX - 5900.\n" 
+"\n"
+"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"
@@ -2135,6 +2216,14 @@ void print_help() {
 "-many                  keep listening for more connections rather than exiting\n"
 "                       as soon as the first clients disconnect.\n"
 "\n"
+"-q                     be quiet by printing less informational output.\n" 
+"-bg                    go into the background after screen setup.\n" 
+"                       something like this could be useful in a script:\n"
+"                         port=`ssh $host \"x11vnc -display :0 -bg\" | grep PORT`\n"
+"                         port=`echo \"$port\" | sed -e 's/PORT=//'`\n"
+"                         port=`expr $port - 5900`\n"
+"                         vncviewer $host:$port\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"
@@ -2150,10 +2239,8 @@ void print_help() {
 "                       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 LIBVNCSERVER_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"
@@ -2210,9 +2297,11 @@ char *choose_title(char *display) {
 	title[0] = '\0';
 	if (display[0] == ':') {
 		char host[MAXN];
+#ifdef LIBVNCSERVER_HAVE_GETHOSTNAME
 		if (gethostname(host, MAXN) == 0) {
 			strncpy(title, host, MAXN - strlen(title));
 		}
+#endif
 	}
 	strncat(title, display, MAXN - strlen(title));
 	if (subwin) {
@@ -2231,6 +2320,7 @@ int main(int argc, char** argv) {
 	int i, ev, er, maj, min;
 	char *use_dpy = NULL;
 	int dt = 0;
+	int bg = 0;
 
 	/* used to pass args we do not know about to rfbGetScreen(): */
 	int argc2 = 1; char *argv2[100];
@@ -2241,10 +2331,10 @@ int main(int argc, char** argv) {
 		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]);
+					fprintf(stderr, "bad -id arg: %s\n",
+					    argv[i]);
 					exit(1);
 				}
 			}
@@ -2298,12 +2388,21 @@ int main(int argc, char** argv) {
 		} else if (!strcmp(argv[i], "-h")
 		    || !strcmp(argv[i], "-help")) {
 			print_help();
+		} else if (!strcmp(argv[i], "-q")) {
+			quiet = 1;
+#ifdef LIBVNCSERVER_HAVE_SETSID
+		} else if (!strcmp(argv[i], "-bg")) {
+			bg = 1;
+#endif
 		} else {
 			if (!strcmp(argv[i], "-desktop")) {
 				dt = 1;
 			}
 			/* otherwise copy it for use below. */
-			printf("passing arg to libvncserver: %s\n", argv[i]);
+			if (! quiet) {
+			    fprintf(stderr, "passing arg to libvncserver: %s\n",
+				argv[i]);
+			}
 			if (argc2 < 100) {
 				argv2[argc2++] = argv[i];
 			}
@@ -2316,22 +2415,26 @@ int main(int argc, char** argv) {
 	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("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("tile_fuzz:  %d\n", tile_fuzz);
-	printf("use_hints:  %d\n", use_hints);
+	if (! quiet) {
+		fprintf(stderr, "viewonly:   %d\n", view_only);
+		fprintf(stderr, "shared:     %d\n", shared);
+		fprintf(stderr, "conn_once:  %d\n", connect_once);
+		fprintf(stderr, "mod_tweak:  %d\n", use_modifier_tweak);
+		fprintf(stderr, "loc_curs:   %d\n", local_cursor);
+		fprintf(stderr, "mouse:      %d\n", show_mouse);
+		fprintf(stderr, "root_curs:  %d\n", show_root_cursor);
+		fprintf(stderr, "defer:      %d\n", defer_update);
+		fprintf(stderr, "waitms:     %d\n", waitms);
+		fprintf(stderr, "take_naps:  %d\n", take_naps);
+		fprintf(stderr, "threads:    %d\n", use_threads);
+		fprintf(stderr, "fs_frac:    %.2f\n", fs_frac);
+		fprintf(stderr, "gaps_fill:  %d\n", gaps_fill);
+		fprintf(stderr, "grow_fill:  %d\n", grow_fill);
+		fprintf(stderr, "tile_fuzz:  %d\n", tile_fuzz);
+		fprintf(stderr, "use_hints:  %d\n", use_hints);
+	} else {
+		rfbLogEnable(0);
+	}
 
 	X_INIT;
 	if (use_dpy) {
@@ -2343,20 +2446,21 @@ int main(int argc, char** argv) {
 	}
 
 	if (! dpy) {
-		printf("XOpenDisplay failed (%s)\n", use_dpy);
+		fprintf(stderr, "XOpenDisplay failed (%s)\n",
+		    use_dpy ? use_dpy:"null");
 		exit(1);
 	} else if (use_dpy) {
-		printf("Using display %s\n", use_dpy);
+		if (! quiet) fprintf(stderr, "Using display %s\n", use_dpy);
 	} else {
-		printf("Using default display.\n");
+		if (! quiet) fprintf(stderr, "Using default display.\n");
 	}
 
 	if (! XTestQueryExtension(dpy, &ev, &er, &maj, &min)) {
-		printf("Display does not support the XTest extension.\n");
+		fprintf(stderr, "Display does not support XTest extension.\n");
 		exit(1);
 	}
 	if (! XShmQueryExtension(dpy)) {
-		printf("Display does not support XShm extension"
+		fprintf(stderr, "Display does not support XShm extension"
 		    " (must be local).\n");
 		exit(1);
 	}
@@ -2384,7 +2488,7 @@ int main(int argc, char** argv) {
 
 		window = (Window) subwin;
 		if ( ! XGetWindowAttributes(dpy, window, &attr) ) {
-			printf("bad window: 0x%x\n", window);
+			fprintf(stderr, "bad window: 0x%x\n", window);
 			exit(1);
 		}
 		dpy_x = attr.width;
@@ -2394,17 +2498,18 @@ int main(int argc, char** argv) {
 		/* show_mouse has some segv crashes as well */
 		if (show_root_cursor) {
 			show_root_cursor = 0;
-			printf("disabling root cursor drawing for subwindow\n");
+			fprintf(stderr, "disabling root cursor drawing for "
+			    "subwindow\n");
 		}
 
 		set_offset();
 	}
 
 	fb = XGetImage(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, ZPixmap);
-	printf("Read initial data from display into framebuffer.\n");
+	if (! quiet) fprintf(stderr, "Read initial data from display into framebuffer.\n");
 
-	if (fb->bits_per_pixel == 24) {
-		printf("warning: 24 bpp may have poor performance.\n");
+	if (fb->bits_per_pixel == 24 && ! quiet) {
+		fprintf(stderr, "warning: 24 bpp may have poor performance.\n");
 	}
 
 	if (! dt) {
@@ -2430,7 +2535,38 @@ int main(int argc, char** argv) {
 		initialize_keycodes();
 	}
 
-	printf("screen setup finished.\n");
+	if (screen->rfbPort) {
+		fprintf(stdout, "PORT=%d\n", screen->rfbPort);
+		fflush(stdout);	
+	}
+	if (! quiet) {
+		fprintf(stderr, "screen setup finished.\n");
+	}
+
+#if defined(LIBVNCSERVER_HAVE_FORK) && defined(LIBVNCSERVER_HAVE_SETSID)
+	if (bg) {
+		int p, n;
+		if ((p = fork()) > 0)  {
+			exit(0);
+		} else if (p == -1) {
+			fprintf(stderr, "could not fork\n");
+			perror("fork");
+			exit(1);
+		}
+		if (setsid() == -1) {
+			fprintf(stderr, "setsid failed\n");
+			perror("setsid");
+			exit(1);
+		}
+		n = open("/dev/null", O_RDONLY);
+		dup2(n, 0);
+		dup2(n, 1);
+		dup2(n, 2);
+		if (n > 2) {
+			close(n);
+		}
+	}
+#endif
 
 	watch_loop();
 
-- 
cgit v1.2.3

