From 872405e6988eb4bc8897cd7d776b7250291e3ed5 Mon Sep 17 00:00:00 2001 From: dscho Date: Mon, 18 Aug 2003 09:25:09 +0000 Subject: [PATCH] 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 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 #include +#include +#include + #include /* 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();