diff options
author | dscho <dscho> | 2004-05-25 09:05:09 +0000 |
---|---|---|
committer | dscho <dscho> | 2004-05-25 09:05:09 +0000 |
commit | 876868553da8f69ed1a368688b6d01a8a7bc1a39 (patch) | |
tree | 0bf872ebeadb746eec0f4695230c8777680ece9f /contrib | |
parent | 0130bdb9d6c61e0446ab514816041f1b9283410a (diff) | |
download | libtdevnc-876868553da8f69ed1a368688b6d01a8a7bc1a39.tar.gz libtdevnc-876868553da8f69ed1a368688b6d01a8a7bc1a39.zip |
move the library into libvncserver/, x11vnc into x11vnc/
Diffstat (limited to 'contrib')
-rw-r--r-- | contrib/ChangeLog | 143 | ||||
-rw-r--r-- | contrib/Makefile.am | 13 | ||||
-rw-r--r-- | contrib/x11vnc.c | 6742 |
3 files changed, 1 insertions, 6897 deletions
diff --git a/contrib/ChangeLog b/contrib/ChangeLog deleted file mode 100644 index 9e468f6..0000000 --- a/contrib/ChangeLog +++ /dev/null @@ -1,143 +0,0 @@ -2004-05-21 Karl Runge <runge@karlrunge.com> - * -accept: add view-only decision and other improvements. - * add -gone command option for when a client leaves. - Thanks to Jesus Alvarez for these ideas. - * -passwdfile to keep passwd off of cmd line. - * -o logfile send stderr to a logfile. - -2004-05-14 Karl Runge <runge@karlrunge.com> - * improvements to -accept popup: yes/no buttons and timeout. - * less fprintf under -q so '-q -inetd' has no stderr output. - -2004-05-08 Karl Runge <runge@karlrunge.com> - * add -accept some-command/xmessage/popup to prompt local X11 user - or otherwise decide to accept an incoming client. - * clean up -Wall warnings. - -2004-05-05 Karl Runge <runge@karlrunge.com> - * enable mouse button -> keystrokes mapping in -buttonmap (mousewheel) - * enable keystroke -> mouse button mapping in -remap (touchpad paste) - (-remap incompat ':' -> '-', sorry...) - * shm OS blacklist (i.e. <= SunOS 5.8) -> -onetile - * revert to check_user_input() under -nofb - * cleanup: lastmod, remove tile_shm and update_client_pointer, - debug output, rfbPort failure. - * user friendly last line: 'The VNC desktop is hostname:0' - -2004-04-28 Karl Runge <runge@karlrunge.com> - * -auth cmdline option for xauthority. - * decrease default deferupdate under -nofb. - * update_client_pointer() from Edoardo Tirtarahardja. - * remove some assumptions about libvncserver defaults. - -2004-04-19 Karl Runge <runge@karlrunge.com> - * support for cursor positions updates -cursorpos - * option for SIGPIPE handling -sigpipe - -2004-04-13 Karl Runge <runge@karlrunge.com> - * solve problem with sending selection when client initializing - (not yet in RFB_NORMAL state). Increase delay to 15s as well. - * when threaded: limit rfbMaxClientWait to >= 20 secs and - increase it to a huge value unless -rfbwait is supplied. - -2004-04-08 Karl Runge <runge@karlrunge.com> - * added support for blacking out regions of the screen, primarily - for Xinerama usage, options: -blackout -xinerama - * Xinerama workaround mouse problem on 'embedded' system, - option -xwarppointer (XWarpPointer instead of XTEST) - * let -remap option take key remappings on cmdline as well as file. - * use cargs fix to test for invalid cmdline options. Add --option. - * remove copy_tile, use copy_tiles(..., 1) instead. - -2004-03-10 Karl Runge <runge@karlrunge.com> - * added reverse connection for vncconnect(1) and other means - -vncconnect, -connect host:port, and -connect watchfile - * added first pass at user keysym remapping feature via - -remap file. Ignores modifier state, need to generalize. - * debugging options for users -debug_pointer and -debug_keyboard - * clear -passwd from argv for privacy (if OS allows). - -2004-02-19 Karl Runge <runge@karlrunge.com> - * added handling of clipboard/selection exchange to/from clients, - even holds PRIMARY which Xvnc does not do. disable with -nosel. - use -noprimary to disable polling of PRIMARY selection. - * added -visual option to force framebuffer visual. not really - of general use, more for testing and workarounds (e.g. win2vnc - fails under 8bpp index color) - * improve cleanup and error handling WRT shm and other failures. - -2004-01-19 Karl Runge <runge@karlrunge.com> - * improvements to pointer event handling primarily during window - dragging. check_user_input() for non-threaded and pointer() - for threaded. Revert to old way via -old_pointer option. - * some memory I/O improvement by using copy_tiles() instead - of copy_tile(). New one does rows of tiles at same time. - Revert to old way via -old_copytile. - * handle case of more mouse buttons on client than on X server. - * added -buttonmap option for finer control over button differences. - -2004-01-09 Karl Runge <runge@karlrunge.com> - * options -allow / -localhost for simple IP based access screening - * option -nodragging to skip all screen updates during mouse drags - (thanks to Michal Sabala) - * option -input_skip to allow users to tune watch_loop dropthru rate - * try to avoid wasting RAM for framebuffer under -nofb - * cleanup wrt bpp vs. depth - -2003-12-08 Karl Runge <runge@karlrunge.com> - * add Xbell support using XKEYBOARD extension (disable: -nobell) - * add "-nofb" to disable framebuffer, i.e. mouse + keyboard only (!) - * add "-notruecolor" to force indexed 8bpp color (when 8bpp) - * make alias "-forever" for "-many" - -From Karl (x11vnc's father) on Apr 2, 2003: - -New option -nocursor to not display the vncviewer local cursor if user -does not want it (also caused some problems with older vncviewers) - -New option -mouse to show the position of the X server mouse (i.e. lagged -from the user's vnc cursor position). Also: -mouseX will try to show -the a different cursor (X) when on the root background. - -New option -many to wait for more connections rather than exiting when -the first client(s) disconnect. - -New option -flashcmap to try to follow installed colormaps under 8bpp -indexed color as pointer is moved. - -New option -nap to watch for low activity and throttle down the polling -rate. Useful on shared machines to keep the load down. - -Experimental option -id <windowid> to show just that window and not -the whole display. Some remaining bugs and inconvenient behavior... -(e.g. new toplevels can be unseen) - -Fixed bug on multi-headed machines where the screen number was being -ignored in a number of places. - -Fixed bug wrt connect_once mode. Now just refuses new clients unless -shared rather than terminating all clients. - -Try to follow changing default colormap under 8bpp indexed color -as color cells are added. - -Needed to pick up HAVE_LIBPTHREAD from autoconf. - -defined a select() macro for usleep() since usleep is not always thread -safe. - -Catch and exit on errors in the shm setup work (XShmCreateImage, shmget,...) -and moved the creation and removal work to separate utility functions. - -Added signal and X error handlers to try to clean out the shm objects -before exiting on interrupt, etc. - -Improved performance a bit on the memcmp() in scan_display() by checking -the whole line first. - -Added a workaround when threaded where libvncserver may disconnect too -early if it does not hear from a client (a small heartbeat is sent). -This may not be needed any longer. - -If -desktop has not been prescribed, try to choose a title based on DISPLAY -and the hostname (and window name under -id). diff --git a/contrib/Makefile.am b/contrib/Makefile.am index 848b9a2..568d8b8 100644 --- a/contrib/Makefile.am +++ b/contrib/Makefile.am @@ -1,18 +1,7 @@ CFLAGS = -I .. -LDADD = ../libvncserver.a +LDADD = ../libvncserver/libvncserver.a noinst_PROGRAMS=zippy -if CYGIPC -LD_CYGIPC=-lcygipc -endif - -if HAVE_X -bin_PROGRAMS=x11vnc -x11vnc_SOURCES=x11vnc.c -INCLUDES=@X_CFLAGS@ -x11vnc_LDADD=@X_LIBS@ $(LD_CYGIPC) $(LDADD) -endif - zippy_SOURCES=zippy.c diff --git a/contrib/x11vnc.c b/contrib/x11vnc.c deleted file mode 100644 index 144b280..0000000 --- a/contrib/x11vnc.c +++ /dev/null @@ -1,6742 +0,0 @@ -/* - * x11vnc.c: a VNC server for X displays. - * - * 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 - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - * - * - * This program is based heavily on the following programs: - * - * the originial x11vnc.c in libvncserver (Johannes E. Schindelin) - * krfb, the KDE desktopsharing project (Tim Jansen) - * x0rfbserver, the original native X vnc server (Jens Wagner) - * - * The primary goal of this program is to create a portable and simple - * command-line server utility that allows a VNC viewer to connect to an - * actual X display (as the above do). The only non-standard dependency - * of this program is the static library libvncserver.a (although in - * some environments libjpeg.so may not be readily available and needs - * to be installed, it may be found at ftp://ftp.uu.net/graphics/jpeg/). - * To increase portability it is written in plain C. - * - * The next goal is to improve performance and interactive response. - * The algorithm currently used here to achieve this is that of krfb - * (based on x0rfbserver algorithm). Additional heuristics are also - * 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 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 the libvncserver 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. - * - * General audio at the remote display is lost unless one separately - * sets up some audio side-channel such as esd. - * - * 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. - * - * 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 depth 8 and - * 24 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. - * - * 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/utsname.h> - -#include <sys/ipc.h> -#include <sys/shm.h> - -#include <X11/Xlib.h> -#include <X11/Xutil.h> -#include <X11/extensions/XShm.h> -#include <X11/extensions/XTest.h> -#include <X11/keysym.h> -#include <X11/Xatom.h> - -#include <sys/stat.h> -#include <fcntl.h> -#include <ctype.h> - -#include <rfb/rfb.h> -#include <rfb/rfbregion.h> - -#ifdef LIBVNCSERVER_HAVE_XKEYBOARD -#include <X11/XKBlib.h> -#endif - -/* - * Temporary kludge: to run with -xinerama define the following macro - * and be sure to link with * -lXinerama (e.g. LDFLAGS=-lXinerama before - * configure). Support for this is being added to libvncserver 'configure.ac' - * so it will all be done automatically. - -#define LIBVNCSERVER_HAVE_LIBXINERAMA - */ -#ifdef LIBVNCSERVER_HAVE_LIBXINERAMA -#include <X11/extensions/Xinerama.h> -#endif - -/* date +'"lastmod: %Y-%m-%d";' */ -char lastmod[] = "lastmod: 2004-05-21"; - - -/* X and rfb framebuffer */ -Display *dpy = 0; -Visual *visual; -Window window, rootwin; -int scr; -int bpp, depth; -int button_mask = 0; -int dpy_x, dpy_y; -int off_x, off_y; -int subwin = 0; -int indexed_colour = 0; - -XImage *tile; -XImage **tile_row; /* for all possible row runs */ -XImage *scanline; -XImage *fullscreen; -int fs_factor = 0; - -XShmSegmentInfo *tile_row_shm; /* for all possible row runs */ -XShmSegmentInfo scanline_shm; -XShmSegmentInfo fullscreen_shm; - -rfbScreenInfoPtr screen; -rfbCursorPtr cursor; -int bytes_per_line; - -/* size of the basic tile unit that is polled for changes: */ -int tile_x = 32; -int tile_y = 32; -int ntiles, ntiles_x, ntiles_y; - -/* arrays that indicate changed or checked tiles. */ -unsigned char *tile_has_diff, *tile_tried; - -/* blacked-out region things */ -typedef struct bout { - int x1, y1, x2, y2; -} blackout_t; -typedef struct tbout { - blackout_t bo[10]; /* hardwired max rectangles. */ - int cover; - int count; -} tile_blackout_t; -blackout_t black[100]; /* hardwired max blackouts */ -int blackouts = 0; -tile_blackout_t *tile_blackout; - - -typedef struct tile_change_region { - /* 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. */ -region_t *tile_region; - -typedef struct hint { - /* location x, y, height, and width of a change-rectangle */ - /* (grows as adjacent horizontal tiles are glued together) */ - int x, y, w, h; -} hint_t; - -/* array to hold the hints: */ -hint_t *hint_list; - -/* various command line options */ - -int shared = 0; /* share vnc display. */ -char *allow_list = NULL; /* for -allow and -localhost */ -char *accept_cmd = NULL; /* for -accept */ -char *gone_cmd = NULL; /* for -gone */ -int view_only = 0; /* clients can only watch. */ -int inetd = 0; /* spawned from inetd(1) */ -int connect_once = 1; /* disconnect after first connection session. */ -int flash_cmap = 0; /* follow installed colormaps */ -int force_indexed_color = 0; /* whether to force indexed color for 8bpp */ - -int use_modifier_tweak = 0; /* use the altgr_keyboard modifier tweak */ -char *remap_file = NULL; /* user supplied remapping file or list */ -int nofb = 0; /* do not send any fb updates */ - -char *blackout_string = NULL; /* -blackout */ -int xinerama = 0; /* -xinerama */ - -char *client_connect = NULL; /* strings for -connect option */ -char *client_connect_file = NULL; -int vnc_connect = 0; /* -vncconnect option */ - -int local_cursor = 1; /* whether the viewer draws a local cursor */ -int cursor_pos = 0; /* cursor position updates -cursorpos */ -int show_mouse = 0; /* display a cursor for the real mouse */ -int use_xwarppointer = 0; /* use XWarpPointer instead of XTestFake... */ -int show_root_cursor = 0; /* show X when on root background */ -int show_dragging = 1; /* process mouse movement events */ -int watch_bell = 1; /* watch for the bell using XKEYBOARD */ - -int old_pointer = 0; /* use the old way of updating the pointer */ -int single_copytile = 0; /* use the old way copy_tiles() */ - -int using_shm = 1; /* whether mit-shm is used */ -int flip_byte_order = 0; /* sometimes needed when using_shm = 0 */ -/* - * waitms is the msec to wait between screen polls. Not too old h/w shows - * 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 defer_update_nofb = 6; /* defer a shorter time under -nofb */ - -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 ui_skip = 10; /* see watchloop. negative means ignore input */ - -/* for -visual override */ -VisualID visual_id = (VisualID) 0; -int visual_depth = 0; - -int nap_ok = 0, nap_diff_count = 0; -time_t last_event, last_input, last_client = 0; - -/* tile heuristics: */ -double fs_frac = 0.75; /* threshold tile fraction to do fullscreen updates. */ -int use_hints = 1; /* use the krfb scheme of gluing tiles together. */ -int tile_fuzz = 2; /* tolerance for suspecting changed tiles touching */ - /* a known changed tile. */ -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. */ - -/* scan pattern jitter from x0rfbserver */ -#define NSCAN 32 -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 cursor_x, cursor_y; /* x and y from the viewer(s) */ -int got_user_input = 0; -int got_pointer_input = 0; -int got_keyboard_input = 0; -int scan_in_progress = 0; -int fb_copy_in_progress = 0; -int client_count = 0; -int shut_down = 0; -int sigpipe = 1; /* 0=skip, 1=ignore, 2=exit */ - -int debug_pointer = 0; -int debug_keyboard = 0; - -int quiet = 0; -double dtime(double *); -int all_clients_initialized(void); - -void zero_fb(int, int, int, int); - -#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; -#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. - */ -MUTEX(x11Mutex); -#define X_LOCK LOCK(x11Mutex) -#define X_UNLOCK UNLOCK(x11Mutex) -#define X_INIT INIT_MUTEX(x11Mutex) - -/* - * Exiting and error handling: - */ -void shm_clean(XShmSegmentInfo *, XImage *); -void shm_delete(XShmSegmentInfo *); - -int exit_flag = 0; -void clean_up_exit (int ret) { - int i; - exit_flag = 1; - - /* remove the shm areas: */ - shm_clean(&scanline_shm, scanline); - shm_clean(&fullscreen_shm, fullscreen); - - for(i=1; i<=ntiles_x; i++) { - shm_clean(&tile_row_shm[i], tile_row[i]); - if (single_copytile && i >= single_copytile) { - break; - } - } - - X_LOCK; - XTestDiscard(dpy); - X_UNLOCK; - - fflush(stderr); - exit(ret); -} - -/* - * General problem handler - */ -void interrupted (int sig) { - int i; - if (exit_flag) { - exit_flag++; - if (use_threads) { - usleep2(250 * 1000); - } else if (exit_flag <= 2) { - return; - } - exit(4); - } - exit_flag++; - if (sig == 0) { - fprintf(stderr, "caught X11 error:\n"); - } else { - fprintf(stderr, "caught signal: %d\n", sig); - } - /* - * to avoid deadlock, etc, just delete the shm areas and - * leave the X stuff hanging. - */ - shm_delete(&scanline_shm); - shm_delete(&fullscreen_shm); - - /* - * Here we have to clean up quite a few shm areas for all - * the possible tile row runs (40 for 1280), not as robust - * as one might like... sometimes need to run ipcrm(1). - */ - for(i=1; i<=ntiles_x; i++) { - shm_delete(&tile_row_shm[i]); - if (single_copytile && i >= single_copytile) { - break; - } - } - if (sig) { - exit(2); - } -} - -XErrorHandler Xerror_def; -XIOErrorHandler XIOerr_def; -int Xerror(Display *d, XErrorEvent *error) { - X_UNLOCK; - interrupted(0); - return (*Xerror_def)(d, error); -} -int XIOerr(Display *d) { - X_UNLOCK; - interrupted(0); - return (*XIOerr_def)(d); -} - -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); - - if (sigpipe == 1) { -#ifdef SIG_IGN - signal(SIGPIPE, SIG_IGN); -#endif - } else if (sigpipe == 2) { - rfbLog("set_signals: will exit on SIGPIPE\n"); - signal(SIGPIPE, interrupted); - } - - X_LOCK; - Xerror_def = XSetErrorHandler(Xerror); - XIOerr_def = XSetIOErrorHandler(XIOerr); - X_UNLOCK; -} - -int run_user_command(char *, rfbClientPtr); - -int accepted_client = 0; -void client_gone(rfbClientPtr client) { - - client_count--; - rfbLog("client_count: %d\n", client_count); - - if (gone_cmd) { - rfbLog("client_gone: using cmd for: %s\n", client->host); - run_user_command(gone_cmd, client); - } - - if (inetd) { - rfbLog("viewer exited.\n"); - clean_up_exit(0); - } - if (connect_once) { - /* - * This non-exit is done for a bad passwd to be consistent - * with our RFB_CLIENT_REFUSE behavior in new_client() (i.e. - * we disconnect after 1 successful connection). - */ - if (client->state == RFB_PROTOCOL_VERSION || - client->state == RFB_AUTHENTICATION && accepted_client) { - rfbLog("connect_once: bad password or early " - "disconnect.\n"); - rfbLog("connect_once: waiting for next connection.\n"); - accepted_client = 0; - return; - } - - rfbLog("viewer exited.\n"); - clean_up_exit(0); - } -} - -/* - * Simple routine to limit access via string compare. A power user will - * want to compile libvncserver with libwrap support and use /etc/hosts.allow. - */ -int check_access(char *addr) { - int allowed = 0; - char *p, *list; - - if (allow_list == NULL || *allow_list == '\0') { - return 1; - } - if (addr == NULL || *addr == '\0') { - rfbLog("check_access: denying empty host IP address string.\n"); - return 0; - } - - list = strdup(allow_list); - p = strtok(list, ","); - while (p) { - char *q = strstr(addr, p); - if (q == addr) { - rfbLog("check_access: client %s matches pattern %s\n", - addr, p); - allowed = 1; - - } else if(!strcmp(p,"localhost") && !strcmp(addr,"127.0.0.1")) { - allowed = 1; - } - p = strtok(NULL, ","); - } - free(list); - return allowed; -} - -/* - * x11vnc's first (and only) visible widget: accept/reject dialog window. - * We go through this pain to avoid dependency on libXt. - */ - -int ugly_accept_window(char *addr, int X, int Y, int timeout, char *mode) { - -#define t2x2_width 16 -#define t2x2_height 16 -static char t2x2_bits[] = { - 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, - 0x33, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33, - 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33}; - - Window awin; - GC gc; - XSizeHints hints; - XGCValues values; - static XFontStruct *font_info = NULL; - static Pixmap ico = 0; - unsigned long valuemask = 0; - static char dash_list[] = {20, 40}; - int list_length = sizeof(dash_list); - - Atom wm_protocols; - Atom wm_delete_window; - - XEvent ev; - long evmask = ExposureMask | KeyPressMask | ButtonPressMask - | StructureNotifyMask; - double waited = 0.0; - - /* strings and geometries y/n */ - KeyCode key_y, key_n, key_v; - char strh[100]; - char str1_b[] = "To accept: press \"y\" or click the \"Yes\" button"; - char str2_b[] = "To reject: press \"n\" or click the \"No\" button"; - char str3_b[] = "View only: press \"v\" or click the \"View\" button"; - char str1_m[] = "To accept: click the \"Yes\" button"; - char str2_m[] = "To reject: click the \"No\" button"; - char str3_m[] = "View only: click the \"View\" button"; - char str1_k[] = "To accept: press \"y\""; - char str2_k[] = "To reject: press \"n\""; - char str3_k[] = "View only: press \"v\""; - char *str1, *str2, *str3; - char str_y[] = "Yes"; - char str_n[] = "No"; - char str_v[] = "View"; - int x, y, w = 345, h = 150, ret = 0; - int X_sh = 20, Y_sh = 30, dY = 20; - int Ye_x = 20, Ye_y = 0, Ye_w = 45, Ye_h = 20; - int No_x = 75, No_y = 0, No_w = 45, No_h = 20; - int Vi_x = 130, Vi_y = 0, Vi_w = 45, Vi_h = 20; - - if (!strcmp(mode, "mouse_only")) { - str1 = str1_m; - str2 = str2_m; - str3 = str3_m; - } else if (!strcmp(mode, "key_only")) { - str1 = str1_k; - str2 = str2_k; - str3 = str3_k; - h -= dY; - } else { - str1 = str1_b; - str2 = str2_b; - str3 = str3_b; - } - if (view_only) { - h -= dY; - } - - if (X < -dpy_x) { - x = (dpy_x - w)/2; /* large negative: center */ - if (x < 0) x = 0; - } else if (X < 0) { - x = dpy_x + X - w; /* from lower right */ - } else { - x = X; /* from upper left */ - } - - if (Y < -dpy_y) { - y = (dpy_y - h)/2; - if (y < 0) y = 0; - } else if (Y < 0) { - y = dpy_y + Y - h; - } else { - y = Y; - } - - X_LOCK; - - awin = XCreateSimpleWindow(dpy, window, x, y, w, h, 4, - BlackPixel(dpy, scr), WhitePixel(dpy, scr)); - - wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False); - wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False); - XSetWMProtocols(dpy, awin, &wm_delete_window, 1); - - if (! ico) { - ico = XCreateBitmapFromData(dpy, awin, t2x2_bits, t2x2_width, - t2x2_height); - } - - hints.flags = PPosition | PSize | PMinSize; - hints.x = x; - hints.y = y; - hints.width = w; - hints.height = h; - hints.min_width = w; - hints.min_height = h; - - XSetStandardProperties(dpy, awin, "new x11vnc client", "x11vnc query", - ico, NULL, 0, &hints); - - XSelectInput(dpy, awin, evmask); - - if (! font_info && (font_info = XLoadQueryFont(dpy, "fixed")) == NULL) { - rfbLog("ugly_accept_window: cannot locate font fixed.\n"); - X_UNLOCK; - clean_up_exit(1); - } - - gc = XCreateGC(dpy, awin, valuemask, &values); - XSetFont(dpy, gc, font_info->fid); - XSetForeground(dpy, gc, BlackPixel(dpy, scr)); - XSetLineAttributes(dpy, gc, 1, LineSolid, CapButt, JoinMiter); - XSetDashes(dpy, gc, 0, dash_list, list_length); - - XMapWindow(dpy, awin); - XFlush(dpy); - - sprintf(strh, "x11vnc: accept connection from %s?", addr); - key_y = XKeysymToKeycode(dpy, XStringToKeysym("y")); - key_n = XKeysymToKeycode(dpy, XStringToKeysym("n")); - key_v = XKeysymToKeycode(dpy, XStringToKeysym("v")); - - while (1) { - int out = -1, x, y, tw, k; - - if (XCheckWindowEvent(dpy, awin, evmask, &ev)) { - ; /* proceed to handling */ - } else if (XCheckTypedEvent(dpy, ClientMessage, &ev)) { - ; /* proceed to handling */ - } else { - int ms = 100; /* sleep a bit */ - usleep(ms * 1000); - waited += ((double) ms)/1000.; - if (timeout && (int) waited >= timeout) { - rfbLog("accept_client: popup timed out after " - "%d seconds.\n", timeout); - out = 0; - ev.type = 0; - } else { - continue; - } - } - - switch(ev.type) { - case Expose: - while (XCheckTypedEvent(dpy, Expose, &ev)) { - ; - } - k=0; - - /* instructions */ - XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, - strh, strlen(strh)); - XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, - str1, strlen(str1)); - XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, - str2, strlen(str2)); - if (! view_only) { - XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, - str3, strlen(str3)); - } - - if (!strcmp(mode, "key_only")) { - break; - } - - /* buttons */ - Ye_y = Y_sh+k*dY; - No_y = Y_sh+k*dY; - Vi_y = Y_sh+k*dY; - XDrawRectangle(dpy, awin, gc, Ye_x, Ye_y, Ye_w, Ye_h); - XDrawRectangle(dpy, awin, gc, No_x, No_y, No_w, No_h); - if (! view_only) { - XDrawRectangle(dpy, awin, gc, Vi_x, Vi_y, - Vi_w, Vi_h); - } - - tw = XTextWidth(font_info, str_y, strlen(str_y)); - tw = (Ye_w - tw)/2; - if (tw < 0) tw = 1; - XDrawString(dpy, awin, gc, Ye_x+tw, Ye_y+Ye_h-5, - str_y, strlen(str_y)); - - tw = XTextWidth(font_info, str_n, strlen(str_n)); - tw = (No_w - tw)/2; - if (tw < 0) tw = 1; - XDrawString(dpy, awin, gc, No_x+tw, No_y+No_h-5, - str_n, strlen(str_n)); - - if (! view_only) { - tw = XTextWidth(font_info, str_v, - strlen(str_v)); - tw = (Vi_w - tw)/2; - if (tw < 0) tw = 1; - XDrawString(dpy, awin, gc, Vi_x+tw, Vi_y+Vi_h-5, - str_v, strlen(str_v)); - } - - break; - - case ClientMessage: - if (ev.xclient.message_type == wm_protocols && - ev.xclient.data.l[0] == wm_delete_window) { - out = 0; - } - break; - - case ButtonPress: - x = ev.xbutton.x; - y = ev.xbutton.y; - if (!strcmp(mode, "key_only")) { - ; - } else if (x > No_x && x < No_x+No_w && y > No_y - && y < No_y+No_h) { - out = 0; - } else if (x > Ye_x && x < Ye_x+Ye_w && y > Ye_y - && y < Ye_y+Ye_h) { - out = 1; - } else if (! view_only && x > Vi_x && x < Vi_x+Vi_w - && y > Vi_y && y < Vi_y+Ye_h) { - out = 2; - } - break; - - case KeyPress: - if (!strcmp(mode, "mouse_only")) { - ; - } else if (ev.xkey.keycode == key_y) { - out = 1; - } else if (ev.xkey.keycode == key_n) { - out = 0; - } else if (! view_only && ev.xkey.keycode == key_v) { - out = 2; - } - break; - default: - break; - } - if (out != -1) { - ret = out; - XUnmapWindow(dpy, awin); - XFreeGC(dpy, gc); - XDestroyWindow(dpy, awin); - XFlush(dpy); - break; - } - } - X_UNLOCK; - - return ret; -} - -/* - * utility to run a user supplied command setting some RFB_ env vars. - * used by, e.g., accept_client() and client_gone() - */ -int run_user_command(char *cmd, rfbClientPtr client) { - char *dpystr = DisplayString(dpy); - static char *display_env = NULL; - static char env_rfb_client_id[100]; - static char env_rfb_client_ip[100]; - static char env_rfb_client_port[100]; - static char env_rfb_x11vnc_pid[100]; - char *addr = client->host; - int rc, fromlen, fromport; - struct sockaddr_in from; - - if (addr == NULL || addr[0] == '\0') { - addr = "unknown-host"; - } - - /* set RFB_CLIENT_ID to semi unique id for command to use */ - sprintf(env_rfb_client_id, "RFB_CLIENT_ID=%d", (int) client); - putenv(env_rfb_client_id); - - /* set RFB_CLIENT_IP to IP addr for command to use */ - sprintf(env_rfb_client_ip, "RFB_CLIENT_IP=%s", addr); - putenv(env_rfb_client_ip); - - /* set RFB_X11VNC_PID to our pid for command to use */ - sprintf(env_rfb_x11vnc_pid, "RFB_X11VNC_PID=%d", (int) getpid()); - putenv(env_rfb_x11vnc_pid); - - /* set RFB_CLIENT_PORT to peer port for command to use */ - fromlen = sizeof(from); - memset(&from, 0, sizeof(from)); - fromport = -1; - if (!getpeername(client->sock, (struct sockaddr *)&from, &fromlen)) { - fromport = ntohs(from.sin_port); - } - sprintf(env_rfb_client_port, "RFB_CLIENT_PORT=%d", fromport); - putenv(env_rfb_client_port); - - /* - * Better set DISPLAY to the one we are polling, if they - * want something trickier, they can handle on their own - * via environment, etc. XXX really should save/restore old. - */ - if (display_env == NULL) { - display_env = (char *) malloc(strlen(dpystr)+10); - } - sprintf(display_env, "DISPLAY=%s", dpystr); - putenv(display_env); - - rfbLog("running command:\n"); - rfbLog(" %s\n", cmd); - - rc = system(cmd); - - if (rc >= 256) { - rc = rc/256; - } - rfbLog("command returned: %d\n", rc); - - sprintf(env_rfb_client_id, "RFB_CLIENT_ID="); - putenv(env_rfb_client_id); - - sprintf(env_rfb_client_ip, "RFB_CLIENT_IP="); - putenv(env_rfb_client_ip); - - sprintf(env_rfb_client_port, "RFB_CLIENT_PORT="); - putenv(env_rfb_client_port); - - sprintf(env_rfb_x11vnc_pid, "RFB_X11VNC_PID="); - putenv(env_rfb_x11vnc_pid); - - return rc; -} - -/* - * process a "yes:0,no:*,view:3" type action list comparing to command - * return code rc. * means the default action with no other match. - */ -int action_match(char *action, int rc) { - char *p, *q, *s = strdup(action); - int cases[4], i, result; - char *labels[4]; - - labels[1] = "yes"; - labels[2] = "no"; - labels[3] = "view"; - - rfbLog("accept_client: process action line: %s\n", - action); - - for (i=1; i <= 3; i++) { - cases[i] = -2; - } - - p = strtok(s, ","); - while (p) { - if ((q = strchr(p, ':')) != NULL) { - int in, k; - *q = '\0'; - q++; - if (strstr(p, "yes") == p) { - k = 1; - } else if (strstr(p, "no") == p) { - k = 2; - } else if (strstr(p, "view") == p) { - k = 3; - } else { - rfbLog("bad action line: %s\n", action); - clean_up_exit(1); - } - if (*q == '*') { - cases[k] = -1; - } else if (sscanf(q, "%d", &in) == 1) { - if (in < 0) { - rfbLog("bad action line: %s\n", action); - clean_up_exit(1); - } - cases[k] = in; - } else { - rfbLog("bad action line: %s\n", action); - clean_up_exit(1); - } - } else { - rfbLog("bad action line: %s\n", action); - clean_up_exit(1); - } - p = strtok(NULL, ","); - } - free(s); - - result = -1; - for (i=1; i <= 3; i++) { - if (cases[i] == -1) { - rfbLog("accept_client: default action is case=%d %s\n", - i, labels[i]); - result = i; - break; - } - } - if (result == -1) { - rfbLog("accept_client: no default action\n"); - } - for (i=1; i <= 3; i++) { - if (cases[i] >= 0 && cases[i] == rc) { - rfbLog("accept_client: matched action is case=%d %s\n", - i, labels[i]); - result = i; - break; - } - } - if (result < 0) { - rfbLog("no action match: %s rc=%d set to no\n", action, rc); - result = 2; - } - return result; -} - -/* - * Simple routine to prompt the user on the X display whether an incoming - * client should be allowed to connect or not. If a gui is involved it - * will be running in the environment/context of the X11 DISPLAY. - * - * The command supplied via -accept is run as is (i.e. no string - * substitution) with the RFB_CLIENT_IP environment variable set to the - * incoming client's numerical IP address. - * - * If the external command exits with 0 the client is accepted, otherwise - * the client is rejected. - * - * Some builtins are provided: - * - * xmessage: use homebrew xmessage(1) for the external command. - * popup: use internal X widgets for prompting. - * - */ -int accept_client(rfbClientPtr client) { - - char xmessage[200], *cmd = NULL; - char *addr = client->host; - char *action = NULL; - - if (accept_cmd == NULL) { - return 1; /* no command specified, so we accept */ - } - - if (addr == NULL || addr[0] == '\0') { - addr = "unknown-host"; - } - - if (strstr(accept_cmd, "popup") == accept_cmd) { - /* use our builtin popup button */ - - /* (popup|popupkey|popupmouse)[+-X+-Y][:timeout] */ - - int ret, timeout = 120; - int x = -64000, y = -64000; - char *p, *mode; - - /* extract timeout */ - if ((p = strchr(accept_cmd, ':')) != NULL) { - int in; - if (sscanf(p+1, "%d", &in) == 1) { - timeout = in; - } - } - /* extract geometry */ - if ((p = strpbrk(accept_cmd, "+-")) != NULL) { - int x1, y1; - if (sscanf(p, "+%d+%d", &x1, &y1) == 2) { - x = x1; - y = y1; - } else if (sscanf(p, "+%d-%d", &x1, &y1) == 2) { - x = x1; - y = -y1; - } else if (sscanf(p, "-%d+%d", &x1, &y1) == 2) { - x = -x1; - y = y1; - } else if (sscanf(p, "-%d-%d", &x1, &y1) == 2) { - x = -x1; - y = -y1; - } - } - - /* find mode: mouse, key, or both */ - if (strstr(accept_cmd, "popupmouse") == accept_cmd) { - mode = "mouse_only"; - } else if (strstr(accept_cmd, "popupkey") == accept_cmd) { - mode = "key_only"; - } else { - mode = "both"; - } - - rfbLog("accept_client: using builtin popup for: %s\n", addr); - if ((ret = ugly_accept_window(addr, x, y, timeout, mode))) { - if (ret == 2) { - rfbLog("accept_client: viewonly: %s\n", addr); - client->viewOnly = TRUE; - } - rfbLog("accept_client: popup accepted: %s\n", addr); - return 1; - } else { - rfbLog("accept_client: popup rejected: %s\n", addr); - return 0; - } - - } else if (!strcmp(accept_cmd, "xmessage")) { - /* make our own command using xmessage(1) */ - - if (view_only) { - sprintf(xmessage, "xmessage -buttons yes:0,no:2 -center" - " 'x11vnc: accept connection from %s?'", addr); - } else { - sprintf(xmessage, "xmessage -buttons yes:0,no:2," - "view-only:3 -center" " 'x11vnc: accept connection" - " from %s?'", addr); - action = "yes:0,no:*,view:3"; - } - cmd = xmessage; - - } else { - /* use the user supplied command: */ - - cmd = accept_cmd; - - /* extract any action prefix: yes:N,no:M,view:K */ - if (strstr(accept_cmd, "yes:") == accept_cmd) { - char *p; - if ((p = strpbrk(accept_cmd, " \t")) != NULL) { - int i; - cmd = p; - p = accept_cmd; - for (i=0; i<200; i++) { - if (*p == ' ' || *p == '\t') { - xmessage[i] = '\0'; - break; - } - xmessage[i] = *p; - p++; - } - xmessage[200-1] = '\0'; - action = xmessage; - } - } - } - - if (cmd) { - int rc; - - rfbLog("accept_client: using cmd for: %s\n", addr); - rc = run_user_command(cmd, client); - - if (action) { - int result; - - if (rc < 0) { - rfbLog("accept_client: cannot use negative " - "rc: %d, action %s\n", rc, action); - result = 2; - } else { - result = action_match(action, rc); - } - - if (result == 1) { - rc = 0; - } else if (result == 2) { - rc = 1; - } else if (result == 3) { - rc = 0; - rfbLog("accept_client: viewonly: %s\n", addr); - client->viewOnly = TRUE; - } else { - rc = 1; /* NOTREACHED */ - } - } - - if (rc == 0) { - rfbLog("accept_client: accepted: %s\n", addr); - return 1; - } else { - rfbLog("accept_client: rejected: %s\n", addr); - return 0; - } - } else { - rfbLog("accept_client: no command, rejecting %s\n", addr); - return 0; - } - - return 0; /* NOTREACHED */ -} - -/* - * For the -connect <file> option: periodically read the file looking for - * a connect string. If one is found set client_connect to it. - */ -void check_connect_file(char *file) { - FILE *in; - char line[512], host[512]; - static int first_warn = 1, truncate_ok = 1; - static time_t last_time = 0; - time_t now = time(0); - - if (now - last_time < 1) { - /* check only once a second */ - return; - } - last_time = now; - - if (! truncate_ok) { - /* check if permissions changed */ - if (access(file, W_OK) == 0) { - truncate_ok = 1; - } else { - return; - } - } - - in = fopen(file, "r"); - if (in == NULL) { - if (first_warn) { - rfbLog("check_connect_file: fopen failure: %s\n", file); - perror("fopen"); - first_warn = 0; - } - return; - } - - if (fgets(line, 512, in) != NULL) { - if (sscanf(line, "%s", host) == 1) { - if (strlen(host) > 0) { - client_connect = strdup(host); - rfbLog("read connect file: %s\n", host); - } - } - } - fclose(in); - - /* truncate file */ - in = fopen(file, "w"); - if (in != NULL) { - fclose(in); - } else { - /* disable if we cannot truncate */ - rfbLog("check_connect_file: could not truncate %s, " - "disabling checking.\n", file); - truncate_ok = 0; - } -} - -/* Do a reverse connect for a single "host" or "host:port" */ -int do_reverse_connect(char *str) { - rfbClientPtr cl; - char *host, *p; - int port = 5500, len = strlen(str); - - if (len < 1) { - return 0; - } - if (len > 512) { - rfbLog("reverse_connect: string too long: %d bytes\n", len); - return 0; - } - - /* copy in to host */ - host = (char *) malloc((size_t) len+1); - if (! host) { - rfbLog("reverse_connect: could not malloc string %d\n", len); - return 0; - } - strncpy(host, str, len); - host[len] = '\0'; - - /* extract port, if any */ - if ((p = strchr(host, ':')) != NULL) { - port = atoi(p+1); - *p = '\0'; - } - - cl = rfbReverseConnection(screen, host, port); - free(host); - - if (cl == NULL) { - rfbLog("reverse_connect: %s failed\n", str); - return 0; - } else { - rfbLog("reverse_connect: %s/%s OK\n", str, cl->host); - return 1; - } -} - -void rfbPE(rfbScreenInfoPtr scr, long us) { - if (! use_threads) { - return rfbProcessEvents(scr, us); - } -} - -/* break up comma separated list of hosts and call do_reverse_connect() */ - -void reverse_connect(char *str) { - char *p, *tmp = strdup(str); - int sleep_between_host = 300; - int sleep_min = 1500, sleep_max = 4500, n_max = 5; - int n, tot, t, dt = 100, cnt = 0; - - p = strtok(tmp, ","); - while (p) { - if ((n = do_reverse_connect(p)) != 0) { - rfbPE(screen, -1); - } - cnt += n; - - p = strtok(NULL, ","); - if (p) { - t = 0; - while (t < sleep_between_host) { - usleep(dt * 1000); - rfbPE(screen, -1); - t += dt; - } - } - } - free(tmp); - - if (cnt == 0) { - return; - } - - /* - * XXX: we need to process some of the initial handshaking - * events, otherwise the client can get messed up (why??) - * so we send rfbProcessEvents() all over the place. - */ - - n = cnt; - if (n >= n_max) { - n = n_max; - } - t = sleep_max - sleep_min; - tot = sleep_min + ((n-1) * t) / (n_max-1); - - t = 0; - while (t < tot) { - rfbPE(screen, -1); - usleep(dt * 1000); - t += dt; - } -} - -/* check if client_connect has been set, if so make the reverse connections. */ - -void send_client_connect() { - if (client_connect != NULL) { - reverse_connect(client_connect); - free(client_connect); - client_connect = NULL; - } -} - -/* string for the VNC_CONNECT property */ -#define VNC_CONNECT_MAX 512 -char vnc_connect_str[VNC_CONNECT_MAX+1]; - -/* monitor the various input methods */ -void check_connect_inputs() { - - /* flush any already set: */ - send_client_connect(); - - /* connect file: */ - if (client_connect_file != NULL) { - check_connect_file(client_connect_file); - } - send_client_connect(); - - /* VNC_CONNECT property (vncconnect program) */ - if (vnc_connect && *vnc_connect_str != '\0') { - client_connect = strdup(vnc_connect_str); - vnc_connect_str[0] = '\0'; - } - send_client_connect(); -} - -/* - * libvncserver callback for when a new client connects - */ -enum rfbNewClientAction new_client(rfbClientPtr client) { - last_event = last_input = time(0); - - if (inetd) { - /* - * Set this so we exit as soon as connection closes, - * otherwise client_gone is only called after RFB_CLIENT_ACCEPT - */ - client->clientGoneHook = client_gone; - } - - if (connect_once) { - if (screen->rfbDontDisconnect && screen->rfbNeverShared) { - if (! shared && accepted_client) { - rfbLog("denying additional client: %s\n", - client->host); - return(RFB_CLIENT_REFUSE); - } - } - } - if (! check_access(client->host)) { - rfbLog("denying client: %s does not match %s\n", client->host, - allow_list ? allow_list : "(null)" ); - return(RFB_CLIENT_REFUSE); - } - if (! accept_client(client)) { - rfbLog("denying client: %s local user rejected connection.\n", - client->host); - rfbLog("denying client: accept_cmd=\"%s\"\n", - accept_cmd ? accept_cmd : "(null)" ); - return(RFB_CLIENT_REFUSE); - } - - if (view_only) { - client->clientData = (void *) -1; - client->viewOnly = TRUE; - } else { - client->clientData = (void *) 0; - } - - client->clientGoneHook = client_gone; - client_count++; - - accepted_client = 1; - last_client = time(0); - - return(RFB_CLIENT_ACCEPT); -} - -/* - * For tweaking modifiers wrt the Alt-Graph key, etc. - */ -#define LEFTSHIFT 1 -#define RIGHTSHIFT 2 -#define ALTGR 4 -char mod_state = 0; - -char modifiers[0x100]; -KeyCode keycodes[0x100], left_shift_code, right_shift_code, altgr_code; - -void initialize_modtweak() { - KeySym key, *keymap; - int i, j, minkey, maxkey, syms_per_keycode; - - memset(modifiers, -1, sizeof(modifiers)); - - XDisplayKeycodes(dpy, &minkey, &maxkey); - - keymap = XGetKeyboardMapping(dpy, minkey, (maxkey - minkey + 1), - &syms_per_keycode); - - /* handle alphabetic char with only one keysym (no upper + lower) */ - for (i = minkey; i <= maxkey; i++) { - KeySym lower, upper; - /* 2nd one */ - key = keymap[(i - minkey) * syms_per_keycode + 1]; - if (key != NoSymbol) { - continue; - } - /* 1st one */ - key = keymap[(i - minkey) * syms_per_keycode + 0]; - if (key == NoSymbol) { - continue; - } - XConvertCase(key, &lower, &upper); - if (lower != upper) { - keymap[(i - minkey) * syms_per_keycode + 0] = lower; - keymap[(i - minkey) * syms_per_keycode + 1] = upper; - } - } - for (i = minkey; i <= maxkey; i++) { - for (j = 0; j < syms_per_keycode; j++) { - key = keymap[ (i - minkey) * syms_per_keycode + j ]; - if ( key >= ' ' && key < 0x100 - && i == XKeysymToKeycode(dpy, key) ) { - keycodes[key] = i; - modifiers[key] = j; - } - } - } - - left_shift_code = XKeysymToKeycode(dpy, XK_Shift_L); - right_shift_code = XKeysymToKeycode(dpy, XK_Shift_R); - altgr_code = XKeysymToKeycode(dpy, XK_Mode_switch); - - XFree ((void *) keymap); -} - -/* - * The following is for an experimental -remap option to allow the user - * to remap keystrokes. It is currently confusing wrt modifiers... - */ -typedef struct keyremap { - KeySym before; - KeySym after; - int isbutton; - struct keyremap *next; -} keyremap_t; - -keyremap_t *keyremaps = NULL; - -void initialize_remap(char *infile) { - FILE *in; - char *p, *q, line[256], str1[256], str2[256]; - int i; - KeySym ksym1, ksym2; - keyremap_t *remap, *current; - - in = fopen(infile, "r"); - if (in == NULL) { - /* assume cmd line key1-key2,key3-key4 */ - if (! strchr(infile, '-') || (in = tmpfile()) == NULL) { - rfbLog("remap: cannot open: %s\n", infile); - perror("fopen"); - clean_up_exit(1); - } - p = infile; - while (*p) { - if (*p == '-') { - fprintf(in, " "); - } else if (*p == ',') { - fprintf(in, "\n"); - } else { - fprintf(in, "%c", *p); - } - p++; - } - fprintf(in, "\n"); - fflush(in); - rewind(in); - } - while (fgets(line, 256, in) != NULL) { - int blank = 1, isbtn = 0; - p = line; - while (*p) { - if (! isspace(*p)) { - blank = 0; - break; - } - p++; - } - if (blank) { - continue; - } - if (strchr(line, '#')) { - continue; - } - if ( (q = strchr(line, '-')) != NULL) { - /* allow Keysym1-Keysym2 notation */ - *q = ' '; - } - - if (sscanf(line, "%s %s", str1, str2) != 2) { - rfbLog("remap: bad line: %s\n", line); - fclose(in); - clean_up_exit(1); - } - if (sscanf(str1, "0x%x", &i) == 1) { - ksym1 = (KeySym) i; - } else { - ksym1 = XStringToKeysym(str1); - } - if (sscanf(str2, "0x%x", &i) == 1) { - ksym2 = (KeySym) i; - } else { - ksym2 = XStringToKeysym(str2); - } - if (ksym2 == NoSymbol) { - int i; - if (sscanf(str2, "Button%d", &i) == 1) { - ksym2 = (KeySym) i; - isbtn = 1; - } - } - if (ksym1 == NoSymbol || ksym2 == NoSymbol) { - rfbLog("warning: skipping bad remap line: %s", line); - continue; - } - remap = (keyremap_t *) malloc((size_t) sizeof(keyremap_t)); - remap->before = ksym1; - remap->after = ksym2; - remap->isbutton = isbtn; - remap->next = NULL; - rfbLog("remapping: (%s, 0x%x) -> (%s, 0x%x) isbtn=%d\n", str1, - ksym1, str2, ksym2, isbtn); - if (keyremaps == NULL) { - keyremaps = remap; - } else { - current->next = remap; - - } - current = remap; - } - fclose(in); -} - -void DebugXTestFakeKeyEvent(Display* dpy, KeyCode key, Bool down, time_t cur_time) -{ - if (debug_keyboard) { - rfbLog("XTestFakeKeyEvent(dpy, keycode=0x%x \"%s\", %s)\n", - key, XKeysymToString(XKeycodeToKeysym(dpy, key, 0)), - down ? "down":"up"); - } - XTestFakeKeyEvent(dpy, key, down, cur_time); -} - -/* - * This is to allow debug_keyboard option trap everything: - */ -#define XTestFakeKeyEvent DebugXTestFakeKeyEvent - -void tweak_mod(signed char mod, rfbBool down) { - rfbBool is_shift = mod_state & (LEFTSHIFT|RIGHTSHIFT); - Bool dn = (Bool) down; - - if (mod < 0) { - if (debug_keyboard) { - rfbLog("tweak_mod: Skip: down=%d mod=0x%x\n", down, - (int) mod); - } - return; - } - if (debug_keyboard) { - rfbLog("tweak_mod: Start: down=%d mod=0x%x mod_state=0x%x" - " is_shift=%d\n", down, (int) mod, (int) mod_state, - is_shift); - } - - X_LOCK; - if (is_shift && mod != 1) { - if (mod_state & LEFTSHIFT) { - XTestFakeKeyEvent(dpy, left_shift_code, !dn, CurrentTime); - } - if (mod_state & RIGHTSHIFT) { - XTestFakeKeyEvent(dpy, right_shift_code, !dn, CurrentTime); - } - } - if ( ! is_shift && mod == 1 ) { - XTestFakeKeyEvent(dpy, left_shift_code, dn, CurrentTime); - } - if ( altgr_code && (mod_state & ALTGR) && mod != 2 ) { - XTestFakeKeyEvent(dpy, altgr_code, !dn, CurrentTime); - } - if ( altgr_code && ! (mod_state & ALTGR) && mod == 2 ) { - XTestFakeKeyEvent(dpy, altgr_code, dn, CurrentTime); - } - X_UNLOCK; - if (debug_keyboard) { - rfbLog("tweak_mod: Finish: down=%d mod=0x%x mod_state=0x%x" - " is_shift=%d\n", down, (int) mod, (int) mod_state, - is_shift); - } -} - -static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, - rfbClientPtr client) { - KeyCode k; - int tweak = 0; - if (debug_keyboard) { - rfbLog("modifier_tweak_keyboard: %s keysym=0x%x\n", - down ? "down" : "up", (int) keysym); - } - - if (view_only) { - return; - } - if (client->viewOnly) { - return; - } - -#define ADJUSTMOD(sym, state) \ - if (keysym == sym) { \ - if (down) { \ - mod_state |= state; \ - } else { \ - mod_state &= ~state; \ - } \ - } - - ADJUSTMOD(XK_Shift_L, LEFTSHIFT) - ADJUSTMOD(XK_Shift_R, RIGHTSHIFT) - ADJUSTMOD(XK_Mode_switch, ALTGR) - - if ( down && keysym >= ' ' && keysym < 0x100 ) { - tweak = 1; - tweak_mod(modifiers[keysym], True); - k = keycodes[keysym]; - } else { - X_LOCK; - k = XKeysymToKeycode(dpy, (KeySym) keysym); - X_UNLOCK; - } - if (debug_keyboard) { - rfbLog("modifier_tweak_keyboard: KeySym 0x%x \"%s\" -> " - "KeyCode 0x%x%s\n", (int) keysym, XKeysymToString(keysym), - (int) k, k ? "" : " *ignored*"); - } - if ( k != NoSymbol ) { - X_LOCK; - XTestFakeKeyEvent(dpy, k, (Bool) down, CurrentTime); - X_UNLOCK; - } - - if ( tweak ) { - tweak_mod(modifiers[keysym], False); - } -} - -/* - * key event handler. See the above functions for contortions for - * running under -modtweak. - */ -rfbClientPtr last_keyboard_client = NULL; -int num_buttons = -1; - -static void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { - KeyCode k; - int isbutton = 0; - - if (debug_keyboard) { - X_LOCK; - rfbLog("keyboard(%s, 0x%x \"%s\")\n", down ? "down":"up", - (int) keysym, XKeysymToString(keysym)); - X_UNLOCK; - } - - if (view_only) { - return; - } - if (client->viewOnly) { - return; - } - last_keyboard_client = client; - - if (keyremaps) { - keyremap_t *remap = keyremaps; - while (remap != NULL) { - if (remap->before == keysym) { - keysym = remap->after; - isbutton = remap->isbutton; - if (debug_keyboard) { - X_LOCK; - rfbLog("keyboard(): remapping keysym: " - "0x%x \"%s\" -> 0x%x \"%s\"\n", - (int) remap->before, - XKeysymToString(remap->before), - (int) remap->after, - remap->isbutton ? "button" : - XKeysymToString(remap->after)); - X_UNLOCK; - } - break; - } - remap = remap->next; - } - } - - if (isbutton) { - int button = (int) keysym; - if (! down) { - return; /* nothing to send */ - } - if (debug_keyboard) { - rfbLog("keyboard(): remapping keystroke to button %d" - " click\n", button); - } - if (button < 1 || button > num_buttons) { - rfbLog("keyboard(): ignoring mouse button out of " - "bounds: %d\n", button); - return; - } - X_LOCK; - XTestFakeButtonEvent(dpy, button, True, CurrentTime); - XTestFakeButtonEvent(dpy, button, False, CurrentTime); - XFlush(dpy); - X_UNLOCK; - return; - } - - if (use_modifier_tweak) { - modifier_tweak_keyboard(down, keysym, client); - X_LOCK; - XFlush(dpy); - X_UNLOCK; - return; - } - - X_LOCK; - - k = XKeysymToKeycode(dpy, (KeySym) keysym); - - if (debug_keyboard) { - rfbLog("keyboard(): KeySym 0x%x \"%s\" -> KeyCode 0x%x%s\n", - (int) keysym, XKeysymToString(keysym), (int) k, - k ? "" : " *ignored*"); - } - - if ( k != NoSymbol ) { - XTestFakeKeyEvent(dpy, k, (Bool) down, CurrentTime); - XFlush(dpy); - - last_event = last_input = time(0); - got_user_input++; - got_keyboard_input++; - } - - X_UNLOCK; -} - -/* - * pointer event handling routines. - */ -typedef struct ptrremap { - KeySym keysym; - KeyCode keycode; - int end; - int button; - int down; - int up; -} prtremap_t; - -MUTEX(pointerMutex); -#define MAX_BUTTONS 5 -#define MAX_BUTTON_EVENTS 50 -prtremap_t pointer_map[MAX_BUTTONS+1][MAX_BUTTON_EVENTS]; -char *pointer_remap = NULL; -void update_pointer(int, int, int); - - -/* based on IsModifierKey in Xutil.h */ -#define ismodkey(keysym) \ - ((((KeySym)(keysym) >= XK_Shift_L) && ((KeySym)(keysym) <= XK_Hyper_R))) - - -/* - * For parsing the -buttonmap sections, e.g. "4" or ":Up+Up+Up:" - */ -void buttonparse(int from, char **s) { - char *q; - int to, i; - int modisdown[256]; - - q = *s; - - for (i=0; i<256; i++) { - modisdown[i] = 0; - } - - if (*q == ':') { - /* :sym1+sym2+...+symN: format */ - int l = 0, n = 0; - char list[1000]; - char *t, *kp = q + 1; - KeyCode kcode; - - while (*(kp+l) != ':' && *(kp+l) != '\0') { - /* loop to the matching ':' */ - l++; - if (l >= 1000) { - rfbLog("buttonparse: keysym list too long: " - "%s\n", q); - break; - } - } - *(kp+l) = '\0'; - strncpy(list, kp, l); - list[l] = '\0'; - rfbLog("remap button %d using \"%s\"\n", from, list); - - /* loop over tokens separated by '+' */ - t = strtok(list, "+"); - while (t) { - KeySym ksym; - int i; - if (n >= MAX_BUTTON_EVENTS - 20) { - rfbLog("buttonparse: too many button map " - "events: %s\n", list); - break; - } - if (sscanf(t, "0x%x", &i) == 1) { - ksym = (KeySym) i; /* hex value */ - } else { - ksym = XStringToKeysym(t); /* string value */ - } - if (ksym == NoSymbol) { - /* see if Button<N> "keysym" was used: */ - if (sscanf(t, "Button%d", &i) == 1) { - rfbLog(" event %d: button %d\n", - from, n+1, i); - if (i == 0) i = -1; /* bah */ - pointer_map[from][n].keysym = NoSymbol; - pointer_map[from][n].keycode = NoSymbol; - pointer_map[from][n].button = i; - pointer_map[from][n].end = 0; - pointer_map[from][n].down = 0; - pointer_map[from][n].up = 0; - } else { - rfbLog("buttonparse: ignoring unknown " - "keysym: %s\n", t); - n--; - } - } else { - kcode = XKeysymToKeycode(dpy, ksym); - - pointer_map[from][n].keysym = ksym; - pointer_map[from][n].keycode = kcode; - pointer_map[from][n].button = 0; - pointer_map[from][n].end = 0; - if (! ismodkey(ksym) ) { - /* do both down then up */ - pointer_map[from][n].down = 1; - pointer_map[from][n].up = 1; - } else { - if (modisdown[kcode]) { - pointer_map[from][n].down = 0; - pointer_map[from][n].up = 1; - modisdown[kcode] = 0; - } else { - pointer_map[from][n].down = 1; - pointer_map[from][n].up = 0; - modisdown[kcode] = 1; - } - } - rfbLog(" event %d: keysym %s (0x%x) -> " - "keycode 0x%x down=%d up=%d\n", n+1, - XKeysymToString(ksym), ksym, kcode, - pointer_map[from][n].down, - pointer_map[from][n].up); - } - t = strtok(NULL, "+"); - n++; - } - - /* we must release any modifiers that are still down: */ - for (i=0; i<256; i++) { - kcode = (KeyCode) i; - if (n >= MAX_BUTTON_EVENTS) { - rfbLog("buttonparse: too many button map " - "events: %s\n", list); - break; - } - if (modisdown[kcode]) { - pointer_map[from][n].keysym = NoSymbol; - pointer_map[from][n].keycode = kcode; - pointer_map[from][n].button = 0; - pointer_map[from][n].end = 0; - pointer_map[from][n].down = 0; - pointer_map[from][n].up = 1; - modisdown[kcode] = 0; - n++; - } - } - - /* advance the source pointer position */ - (*s) += l+2; - } else { - /* single digit format */ - char str[2]; - str[0] = *q; - str[1] = '\0'; - - to = atoi(str); - if (to < 1) { - rfbLog("skipping invalid remap button \"%d\" for button" - " %d from string \"%s\"\n", - to, from, str); - } else { - rfbLog("remap button %d using \"%s\"\n", from, str); - rfbLog(" button: %d -> %d\n", from, to); - pointer_map[from][0].keysym = NoSymbol; - pointer_map[from][0].keycode = NoSymbol; - pointer_map[from][0].button = to; - pointer_map[from][0].end = 0; - pointer_map[from][0].down = 0; - pointer_map[from][0].up = 0; - } - /* advance the source pointer position */ - (*s)++; - } -} - -void initialize_pointer_map(void) { - unsigned char map[MAX_BUTTONS]; - int i, k; - /* - * This routine counts the number of pointer buttons on the X - * server (to avoid problems, even crashes, if a client has more - * buttons). And also initializes any pointer button remapping - * from -buttonmap option. - */ - - X_LOCK; - num_buttons = XGetPointerMapping(dpy, map, MAX_BUTTONS); - - if (num_buttons < 0) { - num_buttons = 0; - } - - /* FIXME: should use info in map[] */ - for (i=1; i<= MAX_BUTTONS; i++) { - for (k=0; k < MAX_BUTTON_EVENTS; k++) { - pointer_map[i][k].end = 1; - } - pointer_map[i][0].keysym = NoSymbol; - pointer_map[i][0].keycode = NoSymbol; - pointer_map[i][0].button = i; - pointer_map[i][0].end = 0; - pointer_map[i][0].down = 0; - pointer_map[i][0].up = 0; - } - - if (pointer_remap) { - /* -buttonmap, format is like: 12-21=2 */ - char *p, *q, *remap = pointer_remap; - int n; - - if ((p = strchr(remap, '=')) != NULL) { - /* undocumented max button number */ - n = atoi(p+1); - *p = '\0'; - if (n < num_buttons || num_buttons == 0) { - num_buttons = n; - } else { - rfbLog("warning: increasing number of mouse " - "buttons from %d to %d\n", num_buttons, n); - num_buttons = n; - } - } - if ((q = strchr(remap, '-')) != NULL) { - /* - * The '-' separates the 'from' and 'to' lists, - * then it is kind of like tr(1). - */ - char str[2]; - int from; - - rfbLog("remapping pointer buttons using string:\n"); - rfbLog(" \"%s\"\n", remap); - - p = remap; - q++; - i = 0; - str[1] = '\0'; - while (*p != '-') { - str[0] = *p; - from = atoi(str); - buttonparse(from, &q); - p++; - } - } - } - X_UNLOCK; -} - -/* - * Actual callback from libvncserver when it gets a pointer event. - */ -rfbClientPtr last_pointer_client = NULL; - -static void pointer(int mask, int x, int y, rfbClientPtr client) { - - if (debug_pointer && mask >= 0) { - rfbLog("pointer(mask: 0x%x, x:%4d, y:%4d)\n", mask, x, y); - } - - if (view_only) { - return; - } - if (client->viewOnly) { - return; - } - - if (mask >= 0) { - /* - * mask = -1 is a special case call from scan_for_updates() - * to flush the event queue; there is no real pointer event. - */ - got_user_input++; - got_pointer_input++; - last_pointer_client = client; - } - - /* - * The following is hopefully an improvement wrt response during - * pointer user input (window drags) for the threaded case. - * See check_user_input() for the more complicated things we do - * in the non-threaded case. - */ - if (use_threads && ! old_pointer) { -# define NEV 32 - /* storage for the event queue */ - static int mutex_init = 0; - static int nevents = 0; - static int ev[NEV][3]; - int i; - /* timer things */ - static double dt = 0.0, tmr = 0.0, maxwait = 0.4; - - if (! mutex_init) { - INIT_MUTEX(pointerMutex); - mutex_init = 1; - } - - LOCK(pointerMutex); - - /* - * If the framebuffer is being copied in another thread - * (scan_for_updates()), we will queue up to 32 pointer - * events for later. The idea is by delaying these input - * events, the screen is less likely to change during the - * copying period, and so will give rise to less window - * "tearing". - * - * Tearing is not completely eliminated because we do - * not suspend work in the other libvncserver threads. - * Maybe that is a possibility with a mutex... - */ - if (fb_copy_in_progress && mask >= 0) { - /* - * mask = -1 is an all-clear signal from - * scan_for_updates(). - * - * dt is a timer in seconds; we only queue for so long. - */ - dt += dtime(&tmr); - - if (nevents < NEV && dt < maxwait) { - i = nevents++; - ev[i][0] = mask; - ev[i][1] = x; - ev[i][2] = y; - UNLOCK(pointerMutex); - if (debug_pointer) { - rfbLog("pointer(): deferring event " - "%d\n", i); - } - return; - } - } - - /* time to send the queue */ - for (i=0; i<nevents; i++) { - if (debug_pointer) { - rfbLog("pointer(): sending event %d\n", i+1); - } - update_pointer(ev[i][0], ev[i][1], ev[i][2]); - } - if (nevents && dt > maxwait) { - X_LOCK; - XFlush(dpy); - X_UNLOCK; - } - nevents = 0; /* reset everything */ - tmr = 0.0; - dt = 0.0; - dtime(&tmr); - - UNLOCK(pointerMutex); - } - if (mask < 0) { /* -1 just means flush the event queue */ - if (debug_pointer > 1) { - rfbLog("pointer(): flush only.\n"); - } - return; - } - - /* update the X display with the event: */ - update_pointer(mask, x, y); -} - -/* - * Send a pointer event to the X server. - */ - -void update_pointer(int mask, int x, int y) { - int i, mb; - - X_LOCK; - - if (! use_xwarppointer) { - XTestFakeMotionEvent(dpy, scr, x+off_x, y+off_y, CurrentTime); - } else { - XWarpPointer(dpy, None, window, 0, 0, 0, 0, x+off_x, y+off_y); - } - - cursor_x = x; - cursor_y = y; - - last_event = last_input = time(0); - - for (i=0; i < MAX_BUTTONS; i++) { - /* look for buttons that have be clicked or released: */ - if ( (button_mask & (1<<i)) != (mask & (1<<i)) ) { - int k; - if (debug_pointer) { - rfbLog("pointer(): mask change: mask: 0x%x -> " - "0x%x button: %d\n", button_mask, mask,i+1); - } - for (k=0; k < MAX_BUTTON_EVENTS; k++) { - int bmask = (mask & (1<<i)); - - if (pointer_map[i+1][k].end) { - break; - } - - if (pointer_map[i+1][k].button) { - /* sent button up or down */ - mb = pointer_map[i+1][k].button; - if ((num_buttons && mb > num_buttons) - || mb < 1) { - rfbLog("ignoring mouse button out of " - "bounds: %d>%d mask: 0x%x -> 0x%x\n", - mb, num_buttons, button_mask, mask); - continue; - } - if (debug_pointer) { - rfbLog("pointer(): sending button %d" - " %s (event %d)\n", mb, bmask - ? "down" : "up", k+1); - } - XTestFakeButtonEvent(dpy, mb, (mask & (1<<i)) - ? True : False, CurrentTime); - } else { - /* sent keysym up or down */ - KeyCode key = pointer_map[i+1][k].keycode; - int up = pointer_map[i+1][k].up; - int down = pointer_map[i+1][k].down; - - if (! bmask) { - /* do not send keysym on button up */ - continue; - } - if (debug_pointer) { - rfbLog("pointer(): sending button %d " - "down as keycode 0x%x (event %d)\n", - i+1, key, k+1); - rfbLog(" down=%d up=%d " - "keysym: %s\n", down, up, - XKeysymToString(XKeycodeToKeysym( - dpy, key, 0))); - } - if (down) { - XTestFakeKeyEvent(dpy, key, True, - CurrentTime); - } - if (up) { - XTestFakeKeyEvent(dpy, key, False, - CurrentTime); - } - } - } - } - } - - if (nofb) { - /* - * nofb is for, e.g. Win2VNC, where fastest pointer - * updates are desired. - */ - XFlush(dpy); - } - - X_UNLOCK; - - /* - * Remember the button state for next time and also for the - * -nodragging case: - */ - button_mask = mask; -} - -/* - * Bell event handling. Requires XKEYBOARD extension. - */ -#ifdef LIBVNCSERVER_HAVE_XKEYBOARD - -int xkb_base_event_type; - -void initialize_watch_bell() { - int ir, reason; - if (! XkbSelectEvents(dpy, XkbUseCoreKbd, XkbBellNotifyMask, - XkbBellNotifyMask) ) { - if (! quiet) { - fprintf(stderr, "warning: disabling bell.\n"); - } - watch_bell = 0; - return; - } - if (! XkbOpenDisplay(DisplayString(dpy), &xkb_base_event_type, &ir, - NULL, NULL, &reason) ) { - if (! quiet) { - fprintf(stderr, "warning: disabling bell.\n"); - } - watch_bell = 0; - } -} - -/* - * We call this periodically to process any bell events that have - * taken place. - */ -void watch_bell_event() { - XEvent xev; - XkbAnyEvent *xkb_ev; - int got_bell = 0; - - if (! watch_bell) { - return; - } - - X_LOCK; - if (! XCheckTypedEvent(dpy, xkb_base_event_type , &xev)) { - X_UNLOCK; - return; - } - xkb_ev = (XkbAnyEvent *) &xev; - if (xkb_ev->xkb_type == XkbBellNotify) { - got_bell = 1; - } - X_UNLOCK; - - if (got_bell) { - if (! all_clients_initialized()) { - rfbLog("watch_bell_event: not sending bell: " - "uninitialized clients\n"); - } else { - rfbSendBell(screen); - } - } -} -#else -void watch_bell_event() {} -#endif - - -/* - * Selection/Cutbuffer/Clipboard handlers. - */ - -int watch_selection = 1; /* normal selection/cutbuffer maintenance */ -int watch_primary = 1; /* more dicey, poll for changes in PRIMARY */ -int own_selection = 0; /* whether we currently own PRIMARY or not */ -int set_cutbuffer = 0; /* to avoid bouncing the CutText right back */ -int sel_waittime = 15; /* some seconds to skip before first send */ -Window selwin; /* special window for our selection */ - -/* - * This is where we keep our selection: the string sent TO us from VNC - * clients, and the string sent BY us to requesting X11 clients. - */ -char *xcut_string = NULL; - -/* - * Our callbacks instruct us to check for changes in the cutbuffer - * and PRIMARY selection on the local X11 display. - * - * We store the new cutbuffer and/or PRIMARY selection data in this - * constant sized array selection_str[]. - * TODO: check if malloc does not cause performance issues (esp. WRT - * SelectionNotify handling). - */ -#define PROP_MAX (131072L) -char selection_str[PROP_MAX+1]; - -/* - * An X11 (not VNC) client on the local display has requested the selection - * from us (because we are the current owner). - * - * n.b.: our caller already has the X_LOCK. - */ -void selection_request(XEvent *ev) { - XSelectionEvent notify_event; - XSelectionRequestEvent *req_event; - unsigned int length; - unsigned char *data; -#ifndef XA_LENGTH - unsigned long XA_LENGTH = XInternAtom(dpy, "LENGTH", True); -#endif - - req_event = &(ev->xselectionrequest); - notify_event.type = SelectionNotify; - notify_event.display = req_event->display; - notify_event.requestor = req_event->requestor; - notify_event.selection = req_event->selection; - notify_event.target = req_event->target; - notify_event.time = req_event->time; - - if (req_event->property == None) { - notify_event.property = req_event->target; - } else { - notify_event.property = req_event->property; - } - if (xcut_string) { - length = strlen(xcut_string); - } else { - length = 0; - } - - - if (ev->xselectionrequest.target == XA_LENGTH) { - /* length request */ - - XChangeProperty(ev->xselectionrequest.display, - ev->xselectionrequest.requestor, - ev->xselectionrequest.property, - ev->xselectionrequest.target, 32, PropModeReplace, - (unsigned char *) &length, sizeof(unsigned int)); - - } else { - /* data request */ - - data = (unsigned char *)xcut_string; - - XChangeProperty(ev->xselectionrequest.display, - ev->xselectionrequest.requestor, - ev->xselectionrequest.property, - ev->xselectionrequest.target, 8, PropModeReplace, - data, length); - } - - XSendEvent(req_event->display, req_event->requestor, False, 0, - (XEvent *)¬ify_event); - - XFlush(dpy); -} - -int all_clients_initialized() { - rfbClientIteratorPtr iter; - rfbClientPtr cl; - int ok = 1; - - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) { - if (cl->state != RFB_NORMAL) { - ok = 0; - break; - } - } - rfbReleaseClientIterator(iter); - - return ok; -} - -/* - * CUT_BUFFER0 property on the local display has changed, we read and - * store it and send it out to any connected VNC clients. - * - * n.b.: our caller already has the X_LOCK. - */ -void cutbuffer_send() { - Atom type; - int format, slen, dlen; - unsigned long nitems = 0, bytes_after = 0; - unsigned char* data = NULL; - - selection_str[0] = '\0'; - slen = 0; - - /* read the property value into selection_str: */ - do { - if (XGetWindowProperty(dpy, DefaultRootWindow(dpy), - XA_CUT_BUFFER0, nitems/4, PROP_MAX/16, False, - AnyPropertyType, &type, &format, &nitems, &bytes_after, - &data) == Success) { - - dlen = nitems * (format/8); - if (slen + dlen > PROP_MAX) { - /* too big */ - rfbLog("warning: truncating large CUT_BUFFER0" - " selection > %d bytes.\n", PROP_MAX); - XFree(data); - break; - } - memcpy(selection_str+slen, data, dlen); - slen += dlen; - selection_str[slen] = '\0'; - XFree(data); - } - } while (bytes_after > 0); - - selection_str[PROP_MAX] = '\0'; - - if (! all_clients_initialized()) { - rfbLog("cutbuffer_send: no send: uninitialized clients\n"); - return; /* some clients initializing, cannot send */ - } - - /* now send it to any connected VNC clients (rfbServerCutText) */ - rfbSendServerCutText(screen, selection_str, strlen(selection_str)); -} - -/* - * "callback" for our SelectionNotify polling. We try to determine if - * the PRIMARY selection has changed (checking length and first CHKSZ bytes) - * and if it has we store it and send it off to any connected VNC clients. - * - * n.b.: our caller already has the X_LOCK. - * - * TODO: if we were willing to use libXt, we could perhaps get selection - * timestamps to speed up the checking... XtGetSelectionValue(). - */ -#define CHKSZ 32 -void selection_send(XEvent *ev) { - Atom type; - int format, slen, dlen, oldlen, newlen, toobig = 0; - static int err = 0, sent_one = 0; - char before[CHKSZ], after[CHKSZ]; - unsigned long nitems = 0, bytes_after = 0; - unsigned char* data = NULL; - - /* - * remember info about our last value of PRIMARY (or CUT_BUFFER0) - * so we can check for any changes below. - */ - oldlen = strlen(selection_str); - strncpy(before, selection_str, CHKSZ); - - selection_str[0] = '\0'; - slen = 0; - - /* read in the current value of PRIMARY: */ - do { - if (XGetWindowProperty(dpy, ev->xselection.requestor, - ev->xselection.property, nitems/4, PROP_MAX/16, True, - AnyPropertyType, &type, &format, &nitems, &bytes_after, - &data) == Success) { - - dlen = nitems * (format/8); - if (slen + dlen > PROP_MAX) { - /* too big */ - toobig = 1; - XFree(data); - if (err) { /* cut down on messages */ - break; - } else { - err = 5; - } - rfbLog("warning: truncating large PRIMARY" - " selection > %d bytes.\n", PROP_MAX); - break; - } - memcpy(selection_str+slen, data, dlen); - slen += dlen; - selection_str[slen] = '\0'; - XFree(data); - } - } while (bytes_after > 0); - - if (! toobig) { - err = 0; - } else if (err) { - err--; - } - - if (! sent_one) { - /* try to force a send first time in */ - oldlen = -1; - sent_one = 1; - } - - /* look for changes in the new value */ - newlen = strlen(selection_str); - strncpy(after, selection_str, CHKSZ); - - if (oldlen == newlen && strncmp(before, after, CHKSZ) == 0) { - /* evidently no change */ - return; - } - if (newlen == 0) { - /* do not bother sending a null string out */ - return; - } - - if (! all_clients_initialized()) { - rfbLog("selection_send: no send: uninitialized clients\n"); - return; /* some clients initializing, cannot send */ - } - - /* now send it to any connected VNC clients (rfbServerCutText) */ - rfbSendServerCutText(screen, selection_str, newlen); -} - - -/* - * Routines for monitoring the VNC_CONNECT property for changes. - * The vncconnect(1) will set it on our X display. - */ - -Atom vnc_connect_prop = None; - -void read_vnc_connect_prop() { - Atom type; - int format, slen, dlen; - unsigned long nitems = 0, bytes_after = 0; - unsigned char* data = NULL; - - vnc_connect_str[0] = '\0'; - slen = 0; - - if (! vnc_connect || vnc_connect_prop == None) { - /* not active or problem with VNC_CONNECT atom */ - return; - } - - /* read the property value into vnc_connect_str: */ - do { - if (XGetWindowProperty(dpy, DefaultRootWindow(dpy), - vnc_connect_prop, nitems/4, VNC_CONNECT_MAX/16, False, - AnyPropertyType, &type, &format, &nitems, &bytes_after, - &data) == Success) { - - dlen = nitems * (format/8); - if (slen + dlen > VNC_CONNECT_MAX) { - /* too big */ - rfbLog("warning: truncating large VNC_CONNECT" - " string > %d bytes.\n", VNC_CONNECT_MAX); - XFree(data); - break; - } - memcpy(vnc_connect_str+slen, data, dlen); - slen += dlen; - vnc_connect_str[slen] = '\0'; - XFree(data); - } - } while (bytes_after > 0); - - vnc_connect_str[VNC_CONNECT_MAX] = '\0'; - rfbLog("read property VNC_CONNECT: %s\n", vnc_connect_str); -} - -/* - * This routine is periodically called to check for selection related - * and other X11 events and respond to them as needed. - */ -void watch_xevents() { - XEvent xev; - static int first = 1, sent_sel = 0; - int have_clients = screen->rfbClientHead ? 1 : 0; - time_t last_request = 0, now = time(0); - - X_LOCK; - if (first && (watch_selection || vnc_connect)) { - /* - * register desired event(s) for notification. - * PropertyChangeMask is for CUT_BUFFER0 changes. - * TODO: does this cause a flood of other stuff? - */ - XSelectInput(dpy, rootwin, PropertyChangeMask); - } - if (first && watch_selection) { - /* create fake window for our selection ownership, etc */ - selwin = XCreateSimpleWindow(dpy, rootwin, 0, 0, 1, 1, 0, 0, 0); - } - if (first && vnc_connect) { - vnc_connect_str[0] = '\0'; - vnc_connect_prop = XInternAtom(dpy, "VNC_CONNECT", False); - } - first = 0; - - /* - * There is a bug where we have to wait before sending text to - * the client... so instead of sending right away we wait a - * the few seconds. - */ - if (have_clients && watch_selection && ! sent_sel - && now > last_client + sel_waittime) { - if (XGetSelectionOwner(dpy, XA_PRIMARY) == None) { - cutbuffer_send(); - } - sent_sel = 1; - } - - /* check for CUT_BUFFER0 and VNC_CONNECT changes: */ - if (XCheckTypedEvent(dpy, PropertyNotify, &xev)) { - if (xev.type == PropertyNotify) { - if (xev.xproperty.atom == XA_CUT_BUFFER0) { - /* - * Go retrieve CUT_BUFFER0 and send it. - * - * set_cutbuffer is a flag to try to avoid - * processing our own cutbuffer changes. - */ - if (have_clients && watch_selection - && ! set_cutbuffer) { - cutbuffer_send(); - sent_sel = 1; - } - set_cutbuffer = 0; - } else if (vnc_connect && vnc_connect_prop != None - && xev.xproperty.atom == vnc_connect_prop) { - - /* - * Go retrieve VNC_CONNECT string. - */ - read_vnc_connect_prop(); - } - } - } - - if (! have_clients || ! watch_selection) { - /* - * no need to monitor selections if no current clients - * or -nosel. - */ - X_UNLOCK; - return; - } - - /* check for our PRIMARY request notification: */ - if (watch_primary) { - if (XCheckTypedEvent(dpy, SelectionNotify, &xev)) { - if (xev.type == SelectionNotify && - xev.xselection.requestor == selwin && - xev.xselection.selection == XA_PRIMARY && - xev.xselection.property != None && - xev.xselection.target == XA_STRING) { - - /* go retrieve PRIMARY and check it */ - if (now > last_client + sel_waittime - || sent_sel) { - selection_send(&xev); - } - } - } - if (now > last_request + 1) { - /* - * Every second or two, request PRIMARY, unless we - * already own it or there is no owner. - * TODO: even at this low rate we should look into - * and performance problems in odds cases, etc. - */ - last_request = now; - if (! own_selection && - XGetSelectionOwner(dpy, XA_PRIMARY) != None) { - XConvertSelection(dpy, XA_PRIMARY, XA_STRING, - XA_STRING, selwin, CurrentTime); - } - } - } - - if (! own_selection) { - /* - * no need to do the PRIMARY maintenance tasks below if - * no we do not own it (right?). - */ - X_UNLOCK; - return; - } - - /* we own PRIMARY, see if someone requested it: */ - if (XCheckTypedEvent(dpy, SelectionRequest, &xev)) { - if (xev.type == SelectionRequest && - xev.xselectionrequest.selection == XA_PRIMARY) { - selection_request(&xev); - } - } - - /* we own PRIMARY, see if we no longer own it: */ - if (XCheckTypedEvent(dpy, SelectionClear, &xev)) { - if (xev.type == SelectionClear && - xev.xselectionclear.selection == XA_PRIMARY) { - - own_selection = 0; - if (xcut_string) { - free(xcut_string); - xcut_string = NULL; - } - } - } - X_UNLOCK; -} - -/* - * hook called when a VNC client sends us some "XCut" text (rfbClientCutText). - */ -void xcut_receive(char *text, int len, rfbClientPtr cl) { - - if (cl->viewOnly) { - return; - } - if (text == NULL || len == 0) { - return; - } - - X_LOCK; - - /* associate this text with PRIMARY (and SECONDARY...) */ - if (! own_selection) { - own_selection = 1; - /* we need to grab the PRIMARY selection */ - XSetSelectionOwner(dpy, XA_PRIMARY, selwin, CurrentTime); - XFlush(dpy); - } - - /* duplicate the text string for our own use. */ - if (xcut_string != NULL) { - free(xcut_string); - } - xcut_string = (unsigned char *) - malloc((size_t) (len+1) * sizeof(unsigned char)); - strncpy(xcut_string, text, len); - xcut_string[len] = '\0'; /* make sure null terminated */ - - /* copy this text to CUT_BUFFER0 as well: */ - XChangeProperty(dpy, rootwin, XA_CUT_BUFFER0, XA_STRING, 8, - PropModeReplace, text, len); - XFlush(dpy); - - X_UNLOCK; - - set_cutbuffer = 1; -} - -void mark_hint(hint_t); - -/* - * 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 " -" xxxxx " -" xxxxxx " -" xxxxxxx " -" xxxxxxxx " -" xxxxx " -" xx xx " -" x xx " -" xx " -" xx " -" xx " -" " -" " -" "; - -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 non-cursor 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 window tree at pointer until the window cursor matches the current - * cursor. So far only used to detect if mouse is on root background or not. - * (returns 0 in that case, 1 otherwise). - * - * 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_descend_cursor(void) { - Window r, c; - int rx, ry, wx, wy; - unsigned int mask; - int descend = 0, tries = 0, maxtries = 1; - - X_LOCK; - c = window; - while (c) { - if (++tries > maxtries) { - descend = maxtries; - break; - } - if ( XTestCompareCurrentCursorWithWindow(dpy, c) ) { - break; - } - XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &mask); - descend++; - } - X_UNLOCK; - return descend; -} - -void blackout_nearby_tiles(x, y, dt) { - int sx, sy, n, b; - int tx = x/tile_x; - int ty = y/tile_y; - - if (! blackouts) { - return; - } - if (dt < 1) { - dt = 1; - } - /* loop over a range of tiles, blacking out as needed */ - - for (sx = tx - dt; sx <= tx + dt; sx++) { - if (sx < 0 || sx >= tile_x) { - continue; - } - for (sy = ty - dt; sy <= ty + dt; sy++) { - if (sy < 0 || sy >= tile_y) { - continue; - } - n = sx + sy * ntiles_x; - if (tile_blackout[n].cover == 0) { - continue; - } - for (b=0; b <= tile_blackout[n].count; b++) { - int x1, y1, x2, y2; - x1 = tile_blackout[n].bo[b].x1; - y1 = tile_blackout[n].bo[b].y1; - x2 = tile_blackout[n].bo[b].x2; - y2 = tile_blackout[n].bo[b].y2; - zero_fb(x1, y1, x2, y2); - } - } - } -} - -/* - * Send rfbCursorPosUpdates back to clients that understand them. This - * seems to be TightVNC specific. - */ -void cursor_pos_updates(int x, int y) { - rfbClientIteratorPtr iter; - rfbClientPtr cl; - int cnt = 0; - - if (! cursor_pos) { - return; - } - /* x and y are current positions of X11 pointer on the X11 display */ - if (x == screen->cursorX && y == screen->cursorY) { - return; - } - - if (screen->cursorIsDrawn) { - rfbUndrawCursor(screen); - } - LOCK(screen->cursorMutex); - if (! screen->cursorIsDrawn) { - screen->cursorX = x; - screen->cursorY = y; - } - UNLOCK(screen->cursorMutex); - - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) { - if (! cl->enableCursorPosUpdates) { - continue; - } - if (cl == last_pointer_client) { - /* - * special case if this client was the last one to - * send a pointer position. - */ - if (x == cursor_x && y == cursor_y) { - cl->cursorWasMoved = FALSE; - } else { - /* an X11 app evidently warped the pointer */ - if (debug_pointer) { - rfbLog("cursor_pos_updates: warp " - "detected dx=%3d dy=%3d\n", - cursor_x - x, cursor_y - y); - } - cl->cursorWasMoved = TRUE; - cnt++; - } - } else { - cl->cursorWasMoved = TRUE; - cnt++; - } - } - rfbReleaseClientIterator(iter); - - if (debug_pointer && cnt) { - rfbLog("cursor_pos_updates: sent position x=%3d y=%3d to %d" - " clients\n", x, y, cnt); - } -} - -/* - * 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 (! show_mouse) { - return; - } - 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 (indexed_colour) { - black = BlackPixel(dpy, scr) % 256; - white = WhitePixel(dpy, scr) % 256; - } - if (reverse) { - int tmp = black; - black = white; - white = tmp; - } - - /* - * 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 (blackouts) { - /* - * loop over a range of tiles, blacking out as needed - * note we currently disable mouse drawing under blackouts. - */ - static int mx = -1, my = -1; - int skip = 0; - if (mx < 0) { - mx = x; - my = y; - } else if (mx == x && my == y) { - skip = 1; - } - mx = x; - my = y; - - if (! skip) { - blackout_nearby_tiles(x, y, 2); - } - } - - 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; - rfbBool 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 descend; - if ( (descend = tree_descend_cursor()) ) { - which = 0; - } else { - which = 1; - } - } - - cursor_pos_updates(root_x - off_x, root_y - off_y); - draw_mouse(root_x - off_x, root_y - off_y, which, 1); -} - -/* - * For the subwin case follows the window if it is moved. - */ -void set_offset(void) { - Window w; - if (! subwin) { - return; - } - X_LOCK; - XTranslateCoordinates(dpy, window, rootwin, 0, 0, &off_x, &off_y, &w); - X_UNLOCK; -} - -/* - * Some handling of 8bpp PseudoColor colormaps. Called for initializing - * the clients and dynamically if -flashcmap is specified. - */ -#define NCOLOR 256 -void set_colormap(void) { - static int first = 1; - static XColor color[NCOLOR], prev[NCOLOR]; - Colormap cmap; - Visual *vis; - int i, ncells, 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++) { - prev[i].red = color[i].red; - prev[i].green = color[i].green; - prev[i].blue = color[i].blue; - } - - 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) { - if (! quiet) { - 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; - Window r, c; - int rx, ry, wx, wy, tries = 0; - unsigned int m; - - c = window; - while (c && tries++ < 16) { - /* XXX XQueryTree somehow? */ - XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &m); - if (c && XGetWindowAttributes(dpy, c, &attr)) { - if (attr.colormap && attr.map_installed) { - cmap = attr.colormap; - vis = attr.visual; - ncells = vis->map_entries; - break; - } - } else { - break; - } - } - } - if (ncells > NCOLOR && ! quiet) { - 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, ncells); - - X_UNLOCK; - - 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; - - if (prev[i].red != color[i].red || - prev[i].green != color[i].green || - prev[i].blue != color[i].blue ) { - diffs++; - } - } - - if (diffs && ! first) { - if (! all_clients_initialized()) { - rfbLog("set_colormap: warning: sending cmap " - "with uninitialized clients.\n"); - } - rfbSetClientColourMaps(screen, 0, ncells); - } - - first = 0; -} - -/* - * Experimental mode to force the visual of the window instead of querying - * it. Currently just used for testing or overriding some rare cases. - * Input string can be a decimal or 0x hex or something like TrueColor - * or TrueColor:24 to force a depth as well. - */ -void set_visual(char *vstring) { - int vis, defdepth = DefaultDepth(dpy, scr); - XVisualInfo vinfo; - char *p; - - if (! quiet) { - fprintf(stderr, "set_visual: %s\n", vstring); - } - - if ((p = strchr(vstring, ':')) != NULL) { - visual_depth = atoi(p+1); - *p = '\0'; - } else { - visual_depth = defdepth; - } - if (strcmp(vstring, "StaticGray") == 0) { - vis = StaticGray; - } else if (strcmp(vstring, "GrayScale") == 0) { - vis = GrayScale; - } else if (strcmp(vstring, "StaticColor") == 0) { - vis = StaticColor; - } else if (strcmp(vstring, "PseudoColor") == 0) { - vis = PseudoColor; - } else if (strcmp(vstring, "TrueColor") == 0) { - vis = TrueColor; - } else if (strcmp(vstring, "DirectColor") == 0) { - vis = DirectColor; - } else { - int v_in; - if (sscanf(vstring, "0x%x", &v_in) != 1) { - if (sscanf(vstring, "%d", &v_in) == 1) { - visual_id = (VisualID) v_in; - return; - } - fprintf(stderr, "bad -visual arg: %s\n", vstring); - exit(1); - } - visual_id = (VisualID) v_in; - return; - } - if (XMatchVisualInfo(dpy, scr, visual_depth, vis, &vinfo)) { - ; - } else if (XMatchVisualInfo(dpy, scr, defdepth, vis, &vinfo)) { - ; - } else { - fprintf(stderr, "could not find visual: %s\n", vstring); - exit(1); - } - visual_id = vinfo.visualid; -} - -/* - * Presumably under -nofb the clients will never request the framebuffer. - * But we have gotten such a request... so let's just give them - * the current view on the display. n.b. x2vnc and perhaps win2vnc - * requests a 1x1 pixel for some workaround so sadly this evidently - * nearly always happens. - */ -void nofb_hook(rfbClientPtr cl) { - static int loaded_fb = 0; - XImage *fb; - if (loaded_fb) { - return; - } - rfbLog("framebuffer requested in -nofb mode by client %s\n", cl->host); - fb = XGetImage(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, ZPixmap); - screen->frameBuffer = fb->data; - loaded_fb = 1; - screen->displayHook = NULL; -} - -int got_rfbport = 0; -int got_alwaysshared = 0; -int got_nevershared = 0; -/* - * 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); - - if (! quiet) { - fprintf(stderr, "\n"); - } - - if (! screen) { - int i; - rfbLog("\n"); - rfbLog("failed to create rfb screen.\n"); - for (i=0; i< *argc; i++) { - rfbLog("\t[%d] %s\n", i, argv[i]); - } - clean_up_exit(1); - } - -/* - * This ifdef is a transient for source compatibility for people who download - * the x11vnc.c file by itself and plop it down into their libvncserver tree. - * Remove at some point. BTW, this assumes no usage of earlier "0.7pre". - */ -#ifdef LIBVNCSERVER_VERSION -if (strcmp(LIBVNCSERVER_VERSION, "0.5") && strcmp(LIBVNCSERVER_VERSION, "0.6")) { - if (*argc != 1) { - int i; - rfbLog("*** unrecognized option(s) ***\n"); - for (i=1; i< *argc; i++) { - rfbLog("\t[%d] %s\n", i, argv[i]); - } - rfbLog("for a list of options run: x11vnc -help\n"); - clean_up_exit(1); - } -} -#endif - - screen->paddedWidthInBytes = fb->bytes_per_line; - 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 (force_indexed_color) { - have_masks = 0; - } - - if ( ! have_masks && screen->rfbServerFormat.bitsPerPixel == 8 - && CellsOfScreen(ScreenOfDisplay(dpy,scr)) ) { - /* indexed colour */ - if (!quiet) rfbLog("Using X display with 8bpp indexed color\n"); - indexed_colour = 1; - set_colormap(); - } else { - /* general case ... */ - if (! quiet) { - rfbLog("Using X display with %dbpp depth=%d true " - "color\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 - & (1 << screen->rfbServerFormat.redShift) ) ) { - screen->rfbServerFormat.redShift++; - } - } - screen->rfbServerFormat.greenShift = 0; - if ( fb->green_mask ) { - while ( ! (fb->green_mask - & (1 << screen->rfbServerFormat.greenShift) ) ) { - screen->rfbServerFormat.greenShift++; - } - } - screen->rfbServerFormat.blueShift = 0; - if ( fb->blue_mask ) { - while ( ! (fb->blue_mask - & (1 << screen->rfbServerFormat.blueShift) ) ) { - screen->rfbServerFormat.blueShift++; - } - } - screen->rfbServerFormat.redMax - = fb->red_mask >> screen->rfbServerFormat.redShift; - screen->rfbServerFormat.greenMax - = fb->green_mask >> screen->rfbServerFormat.greenShift; - screen->rfbServerFormat.blueMax - = fb->blue_mask >> screen->rfbServerFormat.blueShift; - } - - /* nofb is for pointer/keyboard only handling. */ - if (nofb) { - screen->frameBuffer = NULL; - screen->displayHook = nofb_hook; - } else { - screen->frameBuffer = fb->data; - } - - /* called from inetd, we need to treat stdio as our socket */ - if (inetd) { - int fd = dup(0); - if (fd < 3) { - rfbErr("dup(0) = %d failed.\n", fd); - perror("dup"); - clean_up_exit(1); - } - fclose(stdin); - fclose(stdout); - /* we keep stderr for logging */ - screen->inetdSock = fd; - screen->rfbPort = 0; - - } else if (! got_rfbport) { - screen->autoPort = TRUE; - } - - if (! got_nevershared && ! got_alwaysshared) { - if (shared) { - screen->rfbAlwaysShared = TRUE; - } else { - screen->rfbDontDisconnect = TRUE; - screen->rfbNeverShared = TRUE; - } - } - /* XXX the following is based on libvncserver defaults. */ - if (screen->rfbDeferUpdateTime == 5) { - /* XXX will be fixed someday */ - screen->rfbDeferUpdateTime = defer_update; - } - - /* event callbacks: */ - screen->newClientHook = new_client; - screen->kbdAddEvent = keyboard; - screen->ptrAddEvent = pointer; - if (watch_selection) { - screen->setXCutText = xcut_receive; - } - - if (local_cursor) { - cursor = rfbMakeXCursor(CUR_SIZE, CUR_SIZE, CUR_DATA, CUR_MASK); - screen->cursor = cursor; - } else { - screen->cursor = NULL; - } - - rfbInitServer(screen); - - bytes_per_line = screen->paddedWidthInBytes; - bpp = screen->rfbServerFormat.bitsPerPixel; - depth = screen->rfbServerFormat.depth; -} - -/* - * Take a comma separated list of geometries: WxH+X+Y and register them as - * rectangles to black out from the screen. - */ -void initialize_blackout (char *list) { - char *p, *blist = strdup(list); - int x, y, X, Y, h, w; - - p = strtok(blist, ","); - while (p) { - /* handle +/-x and +/-y */ - if (sscanf(p, "%dx%d+%d+%d", &w, &h, &x, &y) == 4) { - ; - } else if (sscanf(p, "%dx%d-%d+%d", &w, &h, &x, &y) == 4) { - x = dpy_x - x - w; - } else if (sscanf(p, "%dx%d+%d-%d", &w, &h, &x, &y) == 4) { - y = dpy_y - y - h; - } else if (sscanf(p, "%dx%d-%d-%d", &w, &h, &x, &y) == 4) { - x = dpy_x - x - w; - y = dpy_y - y - h; - } else { - if (*p != '\0') { - rfbLog("skipping invalid geometry: %s\n", p); - } - p = strtok(NULL, ","); - continue; - } - X = x + w; - Y = y + h; - if (x < 0 || x > dpy_x || y < 0 || y > dpy_y || - X < 0 || X > dpy_x || Y < 0 || Y > dpy_y) { - rfbLog("skipping invalid blackout geometry: %s x=" - "%d-%d,y=%d-%d,w=%d,h=%d\n", p, x, X, y, Y, w, h); - } else { - rfbLog("blackout rect: %s: x=%d-%d y=%d-%d\n", p, - x, X, y, Y); - - /* - * note that the black out is x1 <= x but x < x2 - * for the region. i.e. the x2, y2 are outside - * by 1 pixel. - */ - black[blackouts].x1 = x; - black[blackouts].y1 = y; - black[blackouts].x2 = X; - black[blackouts].y2 = Y; - blackouts++; - if (blackouts >= 100) { - rfbLog("too many blackouts: %d\n", blackouts); - break; - } - } - p = strtok(NULL, ","); - } - free(blist); -} - -/* - * Now that all blackout rectangles have been constructed, see what overlap - * they have with the tiles in the system. If a tile is touched by a - * blackout, record information. - */ -void blackout_tiles() { - int tx, ty; - if (! blackouts) { - return; - } - - /* - * to simplify things drop down to single copy mode, no vcr, etc... - */ - single_copytile = 1; - if (show_mouse) { - rfbLog("disabling remote mouse drawing due to blackouts\n"); - show_mouse = 0; - } - - /* loop over all tiles. */ - for (ty=0; ty < ntiles_y; ty++) { - for (tx=0; tx < ntiles_x; tx++) { - sraRegionPtr tile_reg, black_reg; - sraRect rect; - sraRectangleIterator *iter; - int n, b, x1, y1, x2, y2, cnt; - - /* tile number and coordinates: */ - n = tx + ty * ntiles_x; - x1 = tx * tile_x; - y1 = ty * tile_y; - x2 = x1 + tile_x; - y2 = y1 + tile_y; - if (x2 > dpy_x) { - x2 = dpy_x; - } - if (y2 > dpy_y) { - y2 = dpy_y; - } - - /* make regions for the tile and the blackouts: */ - black_reg = (sraRegionPtr) sraRgnCreate(); - tile_reg = (sraRegionPtr) sraRgnCreateRect(x1, y1, - x2, y2); - - tile_blackout[n].cover = 0; - tile_blackout[n].count = 0; - - /* union of blackouts */ - for (b=0; b < blackouts; b++) { - sraRegionPtr tmp_reg = (sraRegionPtr) - sraRgnCreateRect(black[b].x1, black[b].y1, - black[b].x2, black[b].y2); - - sraRgnOr(black_reg, tmp_reg); - sraRgnDestroy(tmp_reg); - } - - if (! sraRgnAnd(black_reg, tile_reg)) { - /* - * no intersection for this tile, so we - * are done. - */ - sraRgnDestroy(black_reg); - sraRgnDestroy(tile_reg); - continue; - } - - /* - * loop over rectangles that make up the blackout - * region: - */ - cnt = 0; - iter = sraRgnGetIterator(black_reg); - while (sraRgnIteratorNext(iter, &rect)) { - - /* make sure x1 < x2 and y1 < y2 */ - if (rect.x1 > rect.x2) { - int tmp = rect.x2; - rect.x2 = rect.x1; - rect.x1 = tmp; - } - if (rect.y1 > rect.y2) { - int tmp = rect.y2; - rect.y2 = rect.y1; - rect.y1 = tmp; - } - - /* store coordinates */ - tile_blackout[n].bo[cnt].x1 = rect.x1; - tile_blackout[n].bo[cnt].y1 = rect.y1; - tile_blackout[n].bo[cnt].x2 = rect.x2; - tile_blackout[n].bo[cnt].y2 = rect.y2; - - /* note if the tile is completely obscured */ - if (rect.x1 == x1 && rect.y1 == y1 && - rect.x2 == x2 && rect.y2 == y2) { - tile_blackout[n].cover = 2; - } else { - tile_blackout[n].cover = 1; - } - - if (++cnt >= 10) { - rfbLog("too many blackout rectangles " - "for tile %d=%d,%d.\n", n, tx, ty); - break; - } - } - - sraRgnReleaseIterator(iter); - sraRgnDestroy(black_reg); - sraRgnDestroy(tile_reg); - - tile_blackout[n].count = cnt; - } - } -} - -void initialize_xinerama () { -#ifndef LIBVNCSERVER_HAVE_LIBXINERAMA - rfbLog("Xinerama: Library libXinerama is not available to determine\n"); - rfbLog("Xinerama: the head geometries, consider using -blackout\n"); - rfbLog("Xinerama: if the screen is non-rectangular.\n"); -#else - XineramaScreenInfo *sc, *xineramas; - sraRegionPtr black_region, tmp_region; - sraRectangleIterator *iter; - sraRect rect; - char *bstr, *tstr; - int ev, er, i, n, rcnt; - - if (! XineramaQueryExtension(dpy, &ev, &er)) { - rfbLog("Xinerama: disabling: display does not support it.\n"); - xinerama = 0; - return; - } - if (! XineramaIsActive(dpy)) { - /* n.b. change to XineramaActive(dpy, window) someday */ - rfbLog("Xinerama: disabling: not active on display.\n"); - xinerama = 0; - return; - } - - /* n.b. change to XineramaGetData() someday */ - xineramas = XineramaQueryScreens(dpy, &n); - rfbLog("Xinerama: number of sub-screens: %d\n", n); - - if (n == 1) { - rfbLog("Xinerama: no blackouts needed (only one" - " sub-screen)\n"); - XFree(xineramas); - return; /* must be OK w/o change */ - } - - black_region = sraRgnCreateRect(0, 0, dpy_x, dpy_y); - - sc = xineramas; - for (i=0; i<n; i++) { - int x, y, w, h; - - x = sc->x_org; - y = sc->y_org; - w = sc->width; - h = sc->height; - - tmp_region = sraRgnCreateRect(x, y, x + w, y + h); - - sraRgnSubtract(black_region, tmp_region); - sraRgnDestroy(tmp_region); - sc++; - } - XFree(xineramas); - - if (sraRgnEmpty(black_region)) { - rfbLog("Xinerama: no blackouts needed (screen fills" - " rectangle)\n"); - sraRgnDestroy(black_region); - return; - } - - /* max len is 10000x10000+10000+10000 (23 chars) per geometry */ - rcnt = (int) sraRgnCountRects(black_region); - bstr = (char *) malloc(30 * rcnt * sizeof(char)); - tstr = (char *) malloc(30 * sizeof(char)); - bstr[0] = '\0'; - - iter = sraRgnGetIterator(black_region); - while (sraRgnIteratorNext(iter, &rect)) { - int x, y, w, h; - - /* make sure x1 < x2 and y1 < y2 */ - if (rect.x1 > rect.x2) { - int tmp = rect.x2; - rect.x2 = rect.x1; - rect.x1 = tmp; - } - if (rect.y1 > rect.y2) { - int tmp = rect.y2; - rect.y2 = rect.y1; - rect.y1 = tmp; - } - x = rect.x1; - y = rect.y1; - w = rect.x2 - x; - h = rect.y2 - y; - sprintf(tstr, "%dx%d+%d+%d,", w, h, x, y); - strcat(bstr, tstr); - } - initialize_blackout(bstr); - - free(bstr); - free(tstr); -#endif -} - -/* - * Fill the framebuffer with zero for the prescribed rectangle - */ -void zero_fb(x1, y1, x2, y2) { - int pixelsize = bpp >> 3; - int line, fill = 0; - char *dst; - - if (x1 < 0 || x2 <= x1 || x2 > dpy_x) { - return; - } - if (y1 < 0 || y2 <= y1 || y2 > dpy_y) { - return; - } - - dst = screen->frameBuffer + y1 * bytes_per_line + x1 * pixelsize; - line = y1; - while (line++ < y2) { - memset(dst, fill, (size_t) (x2 - x1) * pixelsize); - dst += bytes_per_line; - } -} - -/* - * Fill the framebuffer with zeros for each blackout region - */ -void blackout_regions() { - int i; - for (i=0; i < blackouts; i++) { - zero_fb(black[i].x1, black[i].y1, black[i].x2, black[i].y2); - } -} - -/* - * setup tile numbers and allocate the tile and hint arrays: - */ -void initialize_tiles() { - - ntiles_x = (dpy_x - 1)/tile_x + 1; - ntiles_y = (dpy_y - 1)/tile_y + 1; - ntiles = ntiles_x * ntiles_y; - - tile_has_diff = (unsigned char *) - malloc((size_t) (ntiles * sizeof(unsigned char))); - tile_tried = (unsigned char *) - malloc((size_t) (ntiles * sizeof(unsigned char))); - tile_blackout = (tile_blackout_t *) - malloc((size_t) (ntiles * sizeof(tile_blackout_t))); - tile_region = (region_t *) malloc((size_t) (ntiles * sizeof(region_t))); - - tile_row = (XImage **) - malloc((size_t) ((ntiles_x + 1) * sizeof(XImage *))); - tile_row_shm = (XShmSegmentInfo *) - malloc((size_t) ((ntiles_x + 1) * sizeof(XShmSegmentInfo))); - - /* there will never be more hints than tiles: */ - hint_list = (hint_t *) malloc((size_t) (ntiles * sizeof(hint_t))); -} - -/* - * silly function to factor dpy_y until fullscreen shm is not bigger than max. - * should always work unless dpy_y is a large prime or something... under - * failure fs_factor remains 0 and no fullscreen updates will be tried. - */ -void set_fs_factor(int max) { - int f, fac = 1, n = dpy_y; - - if ( (bpp/8) * dpy_x * dpy_y <= max ) { - fs_factor = 1; - return; - } - for (f=2; f <= 101; f++) { - while (n % f == 0) { - n = n / f; - fac = fac * f; - if ( (bpp/8) * dpy_x * (dpy_y/fac) <= max ) { - fs_factor = fac; - return; - } - } - } -} - -/* - * set up an XShm image - */ -int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h, - char *name) { - - XImage *xim; - - shm->shmid = -1; - shm->shmaddr = (char *) -1; - *ximg_ptr = NULL; - - if (nofb) { - return 1; - } - - X_LOCK; - - if (! using_shm) { - /* we only need the XImage created */ - xim = XCreateImage(dpy, visual, depth, ZPixmap, 0, NULL, w, h, - BitmapPad(dpy), 0); - - X_UNLOCK; - - if (xim == NULL) { - rfbErr("XCreateImage(%s) failed.\n", name); - return 0; - } - xim->data = (char *) malloc(xim->bytes_per_line * xim->height); - if (xim->data == NULL) { - rfbErr("XCreateImage(%s) data malloc failed.\n", name); - return 0; - } - if (flip_byte_order) { - static int reported = 0; - char *bo; - if (xim->byte_order == LSBFirst) { - bo = "MSBFirst"; - xim->byte_order = MSBFirst; - xim->bitmap_bit_order = MSBFirst; - } else { - bo = "LSBFirst"; - xim->byte_order = LSBFirst; - xim->bitmap_bit_order = LSBFirst; - } - if (! reported && ! quiet) { - rfbLog("changing XImage byte order" - " to %s\n", bo); - reported = 1; - } - } - - *ximg_ptr = xim; - return 1; - } - - xim = XShmCreateImage(dpy, visual, depth, ZPixmap, NULL, shm, w, h); - - if (xim == NULL) { - rfbErr("XShmCreateImage(%s) failed.\n", name); - X_UNLOCK; - return 0; - } - - *ximg_ptr = xim; - - shm->shmid = shmget(IPC_PRIVATE, - xim->bytes_per_line * xim->height, IPC_CREAT | 0777); - - if (shm->shmid == -1) { - rfbErr("shmget(%s) failed.\n", name); - perror("shmget"); - - XDestroyImage(xim); - *ximg_ptr = NULL; - - X_UNLOCK; - return 0; - } - - shm->shmaddr = xim->data = (char *) shmat(shm->shmid, 0, 0); - - if (shm->shmaddr == (char *)-1) { - rfbErr("shmat(%s) failed.\n", name); - perror("shmat"); - - XDestroyImage(xim); - *ximg_ptr = NULL; - - shmctl(shm->shmid, IPC_RMID, 0); - shm->shmid = -1; - - X_UNLOCK; - return 0; - } - - shm->readOnly = False; - - if (! XShmAttach(dpy, shm)) { - rfbErr("XShmAttach(%s) failed.\n", name); - XDestroyImage(xim); - *ximg_ptr = NULL; - - shmdt(shm->shmaddr); - shm->shmaddr = (char *) -1; - - shmctl(shm->shmid, IPC_RMID, 0); - shm->shmid = -1; - - X_UNLOCK; - return 0; - } - - X_UNLOCK; - return 1; -} - -void shm_delete(XShmSegmentInfo *shm) { - if (! using_shm) { - return; - } - if (shm->shmaddr != (char *) -1) { - shmdt(shm->shmaddr); - } - if (shm->shmid != -1) { - shmctl(shm->shmid, IPC_RMID, 0); - } -} - -void shm_clean(XShmSegmentInfo *shm, XImage *xim) { - if (! using_shm || nofb) { - return; - } - X_LOCK; - if (shm->shmid != -1) { - XShmDetach(dpy, shm); - } - if (xim != NULL) { - XDestroyImage(xim); - } - X_UNLOCK; - - shm_delete(shm); -} - -void initialize_shm() { - int i; - - /* set all shm areas to "none" before trying to create any */ - scanline_shm.shmid = -1; - scanline_shm.shmaddr = (char *) -1; - scanline = NULL; - fullscreen_shm.shmid = -1; - fullscreen_shm.shmaddr = (char *) -1; - fullscreen = NULL; - for (i=1; i<=ntiles_x; i++) { - tile_row_shm[i].shmid = -1; - tile_row_shm[i].shmaddr = (char *) -1; - tile_row[i] = NULL; - } - - /* the scanline (e.g. 1280x1) shared memory area image: */ - - if (! shm_create(&scanline_shm, &scanline, dpy_x, 1, "scanline")) { - clean_up_exit(1); - } - - /* - * the fullscreen (e.g. 1280x1024/fs_factor) shared memory area image: - * (we cut down the size of the shm area to try avoid and shm segment - * limits, e.g. the default 1MB on Solaris) - */ - set_fs_factor(1024 * 1024); - if (fs_frac >= 1.0) { - fs_frac = 1.1; - fs_factor = 0; - } - if (! fs_factor) { - rfbLog("warning: fullscreen updates are disabled.\n"); - } else { - if (! shm_create(&fullscreen_shm, &fullscreen, dpy_x, - dpy_y/fs_factor, "fullscreen")) { - clean_up_exit(1); - } - } - - /* - * for copy_tiles we need a lot of shared memory areas, one for - * each possible run length of changed tiles. 32 for 1024x768 - * and 40 for 1280x1024, etc. - */ - - for (i=1; i<=ntiles_x; i++) { - if (! shm_create(&tile_row_shm[i], &tile_row[i], tile_x * i, - tile_y, "tile_row")) { - if (i == 1) { - clean_up_exit(1); - } - rfbLog("shm: Error creating shared memory tile-row for" - " len=%d,\n", i); - rfbLog("shm: reverting to -onetile mode. If this" - " problem persists\n"); - rfbLog("shm: try using the -onetile or -noshm options" - " to limit\n"); - rfbLog("shm: shared memory usage, or run ipcrm(1)" - " to manually\n"); - rfbLog("shm: delete unattached shm segments.\n"); - /* n.b.: "i" not "1", a kludge for cleanup */ - single_copytile = i; - } - if (single_copytile && i >= 1) { - /* only need 1x1 tiles */ - break; - } - } -} - - -/* - * A hint is a rectangular region built from 1 or more adjacent tiles - * glued together. Ultimately, this information in a single hint is sent - * to libvncserver rather than sending each tile separately. - */ -void create_tile_hint(int x, int y, int th, hint_t *hint) { - int w = dpy_x - x; - int h = dpy_y - y; - - if (w > tile_x) { - w = tile_x; - } - if (h > th) { - h = th; - } - - hint->x = x; - hint->y = y; - hint->w = w; - hint->h = h; -} - -void extend_tile_hint(int x, int y, int th, hint_t *hint) { - int w = dpy_x - x; - int h = dpy_y - y; - - if (w > tile_x) { - w = tile_x; - } - if (h > th) { - h = th; - } - - if (hint->x > x) { /* extend to the left */ - hint->w += hint->x - x; - hint->x = x; - } - if (hint->y > y) { /* extend upward */ - hint->h += hint->y - y; - hint->y = y; - } - - if (hint->x + hint->w < x + w) { /* extend to the right */ - hint->w = x + w - hint->x; - } - if (hint->y + hint->h < y + h) { /* extend downward */ - hint->h = y + h - hint->y; - } -} - -void save_hint(hint_t hint, int loc) { - /* simply copy it to the global array for later use. */ - hint_list[loc].x = hint.x; - hint_list[loc].y = hint.y; - hint_list[loc].w = hint.w; - hint_list[loc].h = hint.h; -} - -/* - * Glue together horizontal "runs" of adjacent changed tiles into one big - * rectangle change "hint" to be passed to the vnc machinery. - */ -void hint_updates() { - hint_t hint; - int x, y, i, n, ty, th; - int hint_count = 0, in_run = 0; - - for (y=0; y < ntiles_y; y++) { - for (x=0; x < ntiles_x; x++) { - n = x + y * ntiles_x; - - if (tile_has_diff[n]) { - ty = tile_region[n].first_line; - th = tile_region[n].last_line - ty + 1; - if (! in_run) { - create_tile_hint( x * tile_x, - y * tile_y + ty, th, &hint); - in_run = 1; - } else { - extend_tile_hint( x * tile_x, - y * tile_y + ty, th, &hint); - } - } else { - if (in_run) { - /* end of a row run of altered tiles: */ - save_hint(hint, hint_count++); - in_run = 0; - } - } - } - if (in_run) { /* save the last row run */ - save_hint(hint, hint_count++); - in_run = 0; - } - } - - - for (i=0; i < hint_count; i++) { - /* pass update info to vnc: */ - mark_hint(hint_list[i]); - } -} - -/* - * Notifies libvncserver of a changed hint rectangle. - */ -void mark_hint(hint_t hint) { - int x = hint.x; - int y = hint.y; - int w = hint.w; - int h = hint.h; - - rfbMarkRectAsModified(screen, x, y, x + w, y + h); -} - -/* - * Notifies libvncserver of a changed tile rectangle. - */ -void mark_tile(int x, int y, int height) { - int w = dpy_x - x; - int h = dpy_y - y; - - if (w > tile_x) { - w = tile_x; - } - - /* height is the height of the changed portion of the tile */ - if (h > height) { - h = height; - } - - rfbMarkRectAsModified(screen, x, y, x + w, y + h); -} - -/* - * Simply send each modified tile separately to the vnc machinery: - * (i.e. no hints) - */ -void tile_updates() { - int x, y, n, ty, th; - - for (y=0; y < ntiles_y; y++) { - for (x=0; x < ntiles_x; x++) { - n = x + y * ntiles_x; - - if (tile_has_diff[n]) { - ty = tile_region[n].first_line; - th = tile_region[n].last_line - ty + 1; - - mark_tile(x * tile_x, y * tile_y + ty, th); - } - } - } -} - - -/* - * copy_tiles() gives a slight improvement over copy_tile() since - * adjacent runs of tiles are done all at once there is some savings - * due to contiguous memory access. Not a great speedup, but in - * some cases it can be up to 2X. Even more on a SunRay where no - * graphics hardware is involved in the read. Generally, graphics - * devices are optimized for write, not read, so we are limited by - * the read bandwidth, sometimes only 5 MB/sec on otherwise fast - * hardware. - */ - -int *first_line = NULL, *last_line; -unsigned short *left_diff, *right_diff; - -void copy_tiles(int tx, int ty, int nt) { - int x, y, line; - int size_x, size_y, width1, width2; - int off, len, n, dw, dx, t; - int w1, w2, dx1, dx2; /* tmps for normal and short tiles */ - int pixelsize = bpp >> 3; - int first_min, last_max; - - int restored_patch = 0; /* for show_mouse */ - - char *src, *dst, *s_src, *s_dst, *m_src, *m_dst; - char *h_src, *h_dst; - if (! first_line) { - /* allocate arrays first time in. */ - int n = ntiles_x + 1; - first_line = (int *) malloc((size_t) (n * sizeof(int))); - last_line = (int *) malloc((size_t) (n * sizeof(int))); - left_diff = (unsigned short *) - malloc((size_t) (n * sizeof(unsigned short))); - right_diff = (unsigned short *) - malloc((size_t) (n * sizeof(unsigned short))); - } - - x = tx * tile_x; - y = ty * tile_y; - - size_x = dpy_x - x; - if ( size_x > tile_x * nt ) { - size_x = tile_x * nt; - width1 = tile_x; - width2 = tile_x; - } else { - /* short tile */ - width1 = tile_x; /* internal tile */ - width2 = size_x - (nt - 1) * tile_x; /* right hand tile */ - } - - size_y = dpy_y - y; - if ( size_y > tile_y ) { - size_y = tile_y; - } - - n = tx + ty * ntiles_x; /* number of the first tile */ - - if (blackouts && tile_blackout[n].cover == 2) { - /* - * If there are blackouts and this tile is completely covered - * no need to poll screen or do anything else.. - * n.b. we are int single copy_tile mode: nt=1 - */ - tile_has_diff[n] = 0; - return; - } - - X_LOCK; - /* read in the whole tile run at once: */ - if ( using_shm && size_x == tile_x * nt && size_y == tile_y ) { - /* general case: */ - XShmGetImage(dpy, window, tile_row[nt], x, y, AllPlanes); - } else { - /* - * No shm or near bottom/rhs edge case: - * (but only if tile size does not divide screen size) - */ - XGetSubImage(dpy, window, x, y, size_x, size_y, AllPlanes, - ZPixmap, tile_row[nt], 0, 0); - } - X_UNLOCK; - - if (blackouts && tile_blackout[n].cover == 1) { - /* - * If there are blackouts and this tile is partially covered - * we should re-black-out the portion. - * n.b. we are int single copy_tile mode: nt=1 - */ - int x1, x2, y1, y2, b; - int w, s, fill = 0; - - for (b=0; b < tile_blackout[n].count; b++) { - char *b_dst = tile_row[nt]->data; - - x1 = tile_blackout[n].bo[b].x1 - x; - y1 = tile_blackout[n].bo[b].y1 - y; - x2 = tile_blackout[n].bo[b].x2 - x; - y2 = tile_blackout[n].bo[b].y2 - y; - - w = (x2 - x1) * pixelsize; - s = x1 * pixelsize; - - for (line = 0; line < size_y; line++) { - if (y1 <= line && line < y2) { - memset(b_dst + s, fill, (size_t) w); - } - b_dst += tile_row[nt]->bytes_per_line; - } - } - } - - /* - * Some awkwardness wrt the little remote mouse patch we display. - * When threaded we want to have as small a window of time - * as possible when the mouse image is not in the fb, otherwise - * a libvncserver thread may send the uncorrected patch to the - * clients. - */ - if (show_mouse && use_threads && cur_saved) { - /* check for overlap */ - if (cur_save_x + cur_save_w > x && x + size_x > cur_save_x && - cur_save_y + cur_save_h > y && y + size_y > cur_save_y) { - - /* restore the real data to the rfb fb */ - restore_mouse_patch(); - restored_patch = 1; - } - } - - src = tile_row[nt]->data; - dst = screen->frameBuffer + y * bytes_per_line + x * pixelsize; - - s_src = src; - s_dst = dst; - - for (t=1; t <= nt; t++) { - first_line[t] = -1; - } - /* find the first line with difference: */ - w1 = width1 * pixelsize; - w2 = width2 * pixelsize; - - - /* foreach line: */ - for (line = 0; line < size_y; line++) { - /* foreach horizontal tile: */ - for (t=1; t <= nt; t++) { - if (first_line[t] != -1) { - continue; - } - - off = (t-1) * w1; - if (t == nt) { - len = w2; /* possible short tile */ - } else { - len = w1; - } - - if (memcmp(s_dst + off, s_src + off, len)) { - first_line[t] = line; - } - } - s_src += tile_row[nt]->bytes_per_line; - s_dst += bytes_per_line; - } - - /* see if there were any differences for any tile: */ - first_min = -1; - for (t=1; t <= nt; t++) { - tile_tried[n+(t-1)] = 1; - if (first_line[t] != -1) { - if (first_min == -1 || first_line[t] < first_min) { - first_min = first_line[t]; - } - } - } - if (first_min == -1) { - /* no tile has a difference, note this and get out: */ - for (t=1; t <= nt; t++) { - tile_has_diff[n+(t-1)] = 0; - } - if (restored_patch) { - redraw_mouse(); - } - return; - } else { - /* - * at least one tile has a difference. make sure info - * is recorded (e.g. sometimes we guess tiles and they - * came in with tile_has_diff 0) - */ - for (t=1; t <= nt; t++) { - if (first_line[t] == -1) { - tile_has_diff[n+(t-1)] = 0; - } else { - tile_has_diff[n+(t-1)] = 1; - } - } - } - - m_src = src + (tile_row[nt]->bytes_per_line * size_y); - m_dst = dst + (bytes_per_line * size_y); - - for (t=1; t <= nt; t++) { - last_line[t] = first_line[t]; - } - - /* find the last line with difference: */ - w1 = width1 * pixelsize; - w2 = width2 * pixelsize; - - /* foreach line: */ - for (line = size_y - 1; line > first_min; line--) { - - m_src -= tile_row[nt]->bytes_per_line; - m_dst -= bytes_per_line; - - /* foreach tile: */ - for (t=1; t <= nt; t++) { - if (first_line[t] == -1 - || last_line[t] != first_line[t]) { - /* tile has no changes or already done */ - continue; - } - - off = (t-1) * w1; - if (t == nt) { - len = w2; /* possible short tile */ - } else { - len = w1; - } - if (memcmp(m_dst + off, m_src + off, len)) { - last_line[t] = line; - } - } - } - - /* - * determine the farthest down last changed line - * will be used below to limit our memcpy() to the framebuffer. - */ - last_max = -1; - for (t=1; t <= nt; t++) { - if (first_line[t] == -1) { - continue; - } - if (last_max == -1 || last_line[t] > last_max) { - last_max = last_line[t]; - } - } - - /* look for differences on left and right hand edges: */ - for (t=1; t <= nt; t++) { - left_diff[t] = 0; - right_diff[t] = 0; - } - - h_src = src; - h_dst = dst; - - w1 = width1 * pixelsize; - w2 = width2 * pixelsize; - - dx1 = (width1 - tile_fuzz) * pixelsize; - dx2 = (width2 - tile_fuzz) * pixelsize; - dw = tile_fuzz * pixelsize; - - /* foreach line: */ - for (line = 0; line < size_y; line++) { - /* foreach tile: */ - for (t=1; t <= nt; t++) { - if (first_line[t] == -1) { - /* tile has no changes at all */ - continue; - } - - off = (t-1) * w1; - if (t == nt) { - dx = dx2; /* possible short tile */ - if (dx <= 0) { - break; - } - } else { - dx = dx1; - } - - if (! left_diff[t] && memcmp(h_dst + off, - h_src + off, dw)) { - left_diff[t] = 1; - } - if (! right_diff[t] && memcmp(h_dst + off + dx, - h_src + off + dx, dw) ) { - right_diff[t] = 1; - } - } - h_src += tile_row[nt]->bytes_per_line; - h_dst += bytes_per_line; - } - - /* now finally copy the difference to the rfb framebuffer: */ - s_src = src + tile_row[nt]->bytes_per_line * first_min; - s_dst = dst + bytes_per_line * first_min; - - for (line = first_min; line <= last_max; line++) { - /* for I/O speed we do not do this tile by tile */ - memcpy(s_dst, s_src, size_x * pixelsize); - s_src += tile_row[nt]->bytes_per_line; - s_dst += bytes_per_line; - } - - if (restored_patch) { - redraw_mouse(); - } - - /* record all the info in the region array for this tile: */ - for (t=1; t <= nt; t++) { - int s = t - 1; - - if (first_line[t] == -1) { - /* tile unchanged */ - continue; - } - tile_region[n+s].first_line = first_line[t]; - tile_region[n+s].last_line = last_line[t]; - - tile_region[n+s].top_diff = 0; - tile_region[n+s].bot_diff = 0; - if ( first_line[t] < tile_fuzz ) { - tile_region[n+s].top_diff = 1; - } - if ( last_line[t] > (size_y - 1) - tile_fuzz ) { - tile_region[n+s].bot_diff = 1; - } - - tile_region[n+s].left_diff = left_diff[t]; - tile_region[n+s].right_diff = right_diff[t]; - } - -} - -/* - * The copy_tile() call in the loop below copies the changed tile into - * the rfb framebuffer. Note that copy_tile() sets the tile_region - * struct to have info about the y-range of the changed region and also - * whether the tile edges contain diffs (within distance tile_fuzz). - * - * We use this tile_region info to try to guess if the downward and right - * tiles will have diffs. These tiles will be checked later in the loop - * (since y+1 > y and x+1 > x). - * - * See copy_tiles_backward_pass() for analogous checking upward and - * left 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++) { - n = x + y * ntiles_x; - - if (tile_has_diff[n]) { - copy_tiles(x, y, 1); - } - if (! tile_has_diff[n]) { - /* - * n.b. copy_tiles() may have detected - * no change and reset tile_has_diff to 0. - */ - 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] = 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] = 2; - } - } - } - } - return diffs; -} - -/* - * Routine analogous to copy_all_tiles() above, but for horizontal runs - * of adjacent changed tiles. - */ -int copy_all_tile_runs() { - int x, y, n, m, i; - int diffs = 0; - int in_run = 0, run = 0; - int ntave = 0, ntcnt = 0; - - for (y=0; y < ntiles_y; y++) { - for (x=0; x < ntiles_x + 1; x++) { - n = x + y * ntiles_x; - - if (x != ntiles_x && tile_has_diff[n]) { - in_run = 1; - run++; - } else { - if (! in_run) { - in_run = 0; - run = 0; - continue; - } - copy_tiles(x - run, y, run); - - ntcnt++; - ntave += run; - diffs += run; - - /* neighboring tile downward: */ - for (i=1; i <= run; i++) { - if ((y+1) < ntiles_y - && tile_region[n-i].bot_diff) { - m = (x-i) + (y+1) * ntiles_x; - if (! tile_has_diff[m]) { - tile_has_diff[m] = 2; - } - } - } - - /* neighboring tile to right: */ - if (((x-1)+1) < ntiles_x - && tile_region[n-1].right_diff) { - m = ((x-1)+1) + y * ntiles_x; - if (! tile_has_diff[m]) { - tile_has_diff[m] = 2; - } - - /* note that this starts a new run */ - in_run = 1; - run = 1; - } else { - in_run = 0; - run = 0; - } - } - } - } - return diffs; -} - -/* - * Here starts a bunch of heuristics to guess/detect changed tiles. - * They are: - * copy_tiles_backward_pass, fill_tile_gaps/gap_try, grow_islands/island_try - */ - -/* - * Try to predict whether the upward and/or leftward tile has been modified. - * copy_all_tiles() has already done downward and rightward tiles. - */ -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--) { - n = x + y * ntiles_x; /* number of this tile */ - - if (! tile_has_diff[n]) { - continue; - } - - m = x + (y-1) * ntiles_x; /* neighboring tile upward */ - - if (y >= 1 && ! tile_has_diff[m] && tile_region[n].top_diff) { - if (! tile_tried[m]) { - tile_has_diff[m] = 2; - copy_tiles(x, y-1, 1); - } - } - - m = (x-1) + y * ntiles_x; /* neighboring tile to left */ - - if (x >= 1 && ! tile_has_diff[m] && tile_region[n].left_diff) { - if (! tile_tried[m]) { - tile_has_diff[m] = 2; - copy_tiles(x-1, y, 1); - } - } - } - } - 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) { - int n, m, i, xt, yt; - - n = x + y * ntiles_x; - - if (! tile_has_diff[n]) { - if (*saw) { - (*run)++; /* extend the gap run. */ - } - return; - } - if (! *saw || *run == 0 || *run > gaps_fill) { - *run = 0; /* unacceptable run. */ - *saw = 1; - return; - } - - for (i=1; i <= *run; i++) { /* iterate thru the run. */ - if (along_x) { - xt = x - i; - yt = y; - } else { - xt = x; - yt = y - i; - } - - m = xt + yt * ntiles_x; - if (tile_tried[m]) { /* do not repeat tiles */ - continue; - } - - copy_tiles(xt, yt, 1); - } - *run = 0; - *saw = 1; -} - -/* - * Look for small gaps of unchanged tiles that may actually contain changes. - * E.g. when paging up and down in a web broswer or terminal there can - * 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... - */ -int fill_tile_gaps() { - int x, y, run, saw; - int n, diffs = 0; - - /* horizontal: */ - for (y=0; y < ntiles_y; y++) { - run = 0; - saw = 0; - for (x=0; x < ntiles_x; x++) { - gap_try(x, y, &run, &saw, 1); - } - } - - /* vertical: */ - for (x=0; x < ntiles_x; x++) { - run = 0; - saw = 0; - for (y=0; y < ntiles_y; y++) { - 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) { - int n, m; - - n = x + y * ntiles_x; - m = u + v * ntiles_x; - - if (tile_has_diff[n]) { - (*run)++; - } else { - *run = 0; - } - - if (tile_has_diff[n] && ! tile_has_diff[m]) { - /* found a discontinuity */ - - if (tile_tried[m]) { - return; - } else if (*run < grow_fill) { - return; - } - - copy_tiles(u, v, 1); - } -} - -/* - * Scan looking for discontinuities in tile_has_diff[]. Try to extend - * the boundary of the discontinuity (i.e. make the island larger). - * Vertical scans are skipped since they do not seem to yield much... - */ -int grow_islands() { - int x, y, n, run; - int diffs = 0; - - /* - * n.b. the way we scan here should keep an extension going, - * and so also fill in gaps effectively... - */ - - /* left to right: */ - for (y=0; y < ntiles_y; y++) { - run = 0; - for (x=0; x <= ntiles_x - 2; x++) { - island_try(x, y, x+1, y, &run); - } - } - /* right to left: */ - for (y=0; y < ntiles_y; y++) { - run = 0; - for (x = ntiles_x - 1; x >= 1; x--) { - 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 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 and compression time - * are other issues... use -fs 1.0 to disable. - */ -void copy_screen() { - int pixelsize = bpp >> 3; - char *rfb_fb; - int i, y, block_size; - - block_size = (dpy_x * (dpy_y/fs_factor) * pixelsize); - - rfb_fb = screen->frameBuffer; - y = 0; - - X_LOCK; - - /* screen may be too big for 1 shm area, so broken into fs_factor */ - for (i=0; i < fs_factor; i++) { - if (using_shm) { - XShmGetImage(dpy, window, fullscreen, 0, y, AllPlanes); - } else { - XGetSubImage(dpy, window, 0, y, fullscreen->width, - fullscreen->height, AllPlanes, ZPixmap, fullscreen, - 0, 0); - } - memcpy(rfb_fb, fullscreen->data, (size_t) block_size); - - y += dpy_y / fs_factor; - rfb_fb += block_size; - } - - X_UNLOCK; - - if (blackouts) { - blackout_regions(); - } - - rfbMarkRectAsModified(screen, 0, 0, dpy_x, dpy_y); -} - -/* profiling routines */ - -double dtime(double *t_old) { - /* - * usage: call with 0.0 to initialize, subsequent calls give - * the time differences. - */ - double t_now, dt; - struct timeval now; - - gettimeofday(&now, NULL); - t_now = now.tv_sec + ( (double) now.tv_usec/1000000. ); - if (*t_old == 0) { - *t_old = t_now; - return t_now; - } - dt = t_now - *t_old; - *t_old = t_now; - return(dt); -} - - -/* - * Utilities for managing the "naps" to cut down on amount of polling. - */ -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 up a long nap to improve the wakeup time */ - for (i=0; i<split; i++) { - usleep(ms * 1000 / split); - if (! use_threads && i != split - 1) { - 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); - } - } -} - -/* - * This is called to avoid a ~20 second timeout in libvncserver. - * May no longer be needed. - */ -void ping_clients(int tile_cnt) { - static time_t last_send = 0; - time_t now = time(0); - - if (rfbMaxClientWait < 20000) { - rfbMaxClientWait = 20000; - rfbLog("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; - } -} - -/* - * scan_display() wants to know if this tile can be skipped due to - * blackout regions: (no data compare is done, just a quick geometric test) - */ -int blackout_line_skip(int n, int x, int y, int rescan, int *tile_count) { - - if (tile_blackout[n].cover == 2) { - tile_has_diff[n] = 0; - return 1; /* skip it */ - - } else if (tile_blackout[n].cover == 1) { - int w, x1, y1, x2, y2, b, hit = 0; - if (x + NSCAN > dpy_x) { - w = dpy_x - x; - } else { - w = NSCAN; - } - - for (b=0; b < tile_blackout[n].count; b++) { - - /* n.b. these coords are in full display space: */ - x1 = tile_blackout[n].bo[b].x1; - x2 = tile_blackout[n].bo[b].x2; - y1 = tile_blackout[n].bo[b].y1; - y2 = tile_blackout[n].bo[b].y2; - - if (x2 - x1 < w) { - /* need to cover full width */ - continue; - } - if (y1 <= y && y < y2) { - hit = 1; - break; - } - } - if (hit) { - if (! rescan) { - tile_has_diff[n] = 0; - } else { - *tile_count += tile_has_diff[n]; - } - return 1; /* skip */ - } - } - return 0; /* do not skip */ -} - -/* - * scan_display() wants to know if this changed tile can be skipped due - * to blackout regions (we do an actual compare to find the changed region). - */ -int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src, - int w, int pixelsize) { - - int i, x1, y1, x2, y2, b, hit = 0; - int beg = -1, end = -1; - - if (tile_blackout[n].cover == 0) { - return 0; /* 0 means do not skip it. */ - } else if (tile_blackout[n].cover == 2) { - return 1; /* 1 means skip it. */ - } - - /* tile has partial coverage: */ - - for (i=0; i < w * pixelsize; i++) { - if (*(dst+i) != *(src+i)) { - beg = i/pixelsize; /* beginning difference */ - break; - } - } - for (i = w * pixelsize - 1; i >= 0; i--) { - if (*(dst+i) != *(src+i)) { - end = i/pixelsize; /* ending difference */ - break; - } - } - if (beg < 0 || end < 0) { - /* problem finding range... */ - return 0; - } - - /* loop over blackout rectangles: */ - for (b=0; b < tile_blackout[n].count; b++) { - - /* y in full display space: */ - y1 = tile_blackout[n].bo[b].y1; - y2 = tile_blackout[n].bo[b].y2; - - /* x relative to tile origin: */ - x1 = tile_blackout[n].bo[b].x1 - x; - x2 = tile_blackout[n].bo[b].x2 - x; - - if (y1 > y || y >= y2) { - continue; - } - if (x1 <= beg && end <= x2) { - hit = 1; - break; - } - } - if (hit) { - return 1; - } else { - return 0; - } -} - -/* - * Loop over 1-pixel tall horizontal scanlines looking for changes. - * Record the changes in tile_has_diff[]. Scanlines in the loop are - * equally spaced along y by NSCAN pixels, but have a slightly random - * 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 whole_line = 1, nodiffs = 0; - - y = ystart; - - while (y < dpy_y) { - - /* grab the horizontal scanline from the display: */ - X_LOCK; - if (using_shm) { - XShmGetImage(dpy, window, scanline, 0, y, AllPlanes); - } else { - XGetSubImage(dpy, window, 0, y, scanline->width, - scanline->height, AllPlanes, ZPixmap, scanline, - 0, 0); - } - X_UNLOCK; - - /* for better memory i/o try the whole line at once */ - src = scanline->data; - dst = screen->frameBuffer + y * bytes_per_line; - - 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 (blackouts) { - if (blackout_line_skip(n, x, y, rescan, - &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: */ - src = scanline->data + x * pixelsize; - dst = screen->frameBuffer + y * bytes_per_line - + x * pixelsize; - - /* compute the width of data to be compared: */ - if (x + NSCAN > dpy_x) { - w = dpy_x - x; - } else { - w = NSCAN; - } - - if (memcmp(dst, src, w * pixelsize)) { - /* found a difference, record it: */ - if (! blackouts) { - tile_has_diff[n] = 1; - tile_count++; - } else { - if (blackout_line_cmpskip(n, x, y, - dst, src, w, pixelsize)) { - tile_has_diff[n] = 0; - } else { - tile_has_diff[n] = 1; - tile_count++; - } - } - } - x += NSCAN; - } - y += NSCAN; - } - return tile_count; -} - -/* - * toplevel for the scanning, rescanning, and applying the heuristics. - */ -void scan_for_updates() { - int i, tile_count, tile_diffs; - double frac1 = 0.1; /* tweak parameter to try a 2nd scan_display() */ - double frac2 = 0.35; /* or 3rd */ - for (i=0; i < ntiles; i++) { - tile_has_diff[i] = 0; - tile_tried[i] = 0; - } - - /* - * n.b. this program has only been tested so far with - * tile_x = tile_y = NSCAN = 32! - */ - - count++; - count %= NSCAN; - - if (count % (NSCAN/4) == 0) { - /* some periodic maintenance */ - - 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: */ - scan_in_progress = 1; - 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; - } - - if (tile_count > frac1 * ntiles) { - /* - * many tiles have changed, so try a rescan (since it should - * be short compared to the many upcoming copy_tiles() calls) - */ - - /* this check is done to skip the extra scan_display() call */ - if (! fs_factor || tile_count <= fs_frac * ntiles) { - int cp, tile_count_old = tile_count; - - /* choose a different y shift for the 2nd scan: */ - cp = (NSCAN - count) % NSCAN; - - tile_count = scan_display(scanlines[cp], 1); - - if (tile_count >= (1 + frac2) * tile_count_old) { - /* on a roll... do a 3rd scan */ - cp = (NSCAN - count + 7) % NSCAN; - tile_count = scan_display(scanlines[cp], 1); - } - } - scan_in_progress = 0; - - /* - * At some number of changed tiles it is better to just - * copy the full screen at once. I.e. time = c1 + m * r1 - * where m is number of tiles, r1 is the copy_tiles() - * 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... - * - * Use -fs 1.0 to disable on slow links. - */ - if (fs_factor && tile_count > fs_frac * ntiles) { - fb_copy_in_progress = 1; - copy_screen(); - if (show_mouse || cursor_pos) { - if (show_mouse && ! use_threads) { - redraw_mouse(); - } - update_mouse(); - } - fb_copy_in_progress = 0; - if (use_threads && ! old_pointer) { - pointer(-1, 0, 0, NULL); - } - nap_check(tile_count); - return; - } - } - scan_in_progress = 0; - - /* copy all tiles with differences from display to rfb framebuffer: */ - fb_copy_in_progress = 1; - - if (single_copytile) { - /* - * Old way, copy I/O one tile at a time. - */ - tile_diffs = copy_all_tiles(); - } else { - /* - * New way, does runs of horizontal tiles at once. - * Note that below, for simplicity, the extra tile finding - * (e.g. copy_tiles_backward_pass) is done the old way. - */ - tile_diffs = copy_all_tile_runs(); - } - - /* - * This backward pass for upward and left tiles complements what - * was done in copy_all_tiles() for downward and right tiles. - */ - tile_diffs = copy_tiles_backward_pass(); - - /* Given enough tile diffs, try the islands: */ - if (grow_fill && tile_diffs > 4) { - tile_diffs = grow_islands(); - } - - /* Given enough tile diffs, try the gaps: */ - if (gaps_fill && tile_diffs > 4) { - tile_diffs = fill_tile_gaps(); - } - - fb_copy_in_progress = 0; - if (use_threads && ! old_pointer) { - /* - * tell the pointer handler it can process any queued - * pointer events: - */ - pointer(-1, 0, 0, NULL); - } - - if (blackouts) { - /* ignore any diffs in completely covered tiles */ - int x, y, n; - for (y=0; y < ntiles_y; y++) { - for (x=0; x < ntiles_x; x++) { - n = x + y * ntiles_x; - if (tile_blackout[n].cover == 2) { - tile_has_diff[n] = 0; - } - } - } - } - - if (use_hints) { - hint_updates(); /* use krfb/x0rfbserver hints algorithm */ - } 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 || cursor_pos) { - if (show_mouse && ! use_threads) { - redraw_mouse(); - } - update_mouse(); - } - - nap_check(tile_diffs); -} - -int check_user_input(double, int *); - -void watch_loop(void) { - int cnt = 0; - double dt = 0.0; - - if (use_threads) { - rfbRunEventLoop(screen, -1, TRUE); - } - - while (1) { - - got_user_input = 0; - got_pointer_input = 0; - got_keyboard_input = 0; - - if (! use_threads) { - rfbProcessEvents(screen, -1); - if (check_user_input(dt, &cnt)) { - /* true means loop back for more input */ - continue; - } - } - - if (shut_down) { - clean_up_exit(0); - } - - watch_xevents(); - check_connect_inputs(); - - if (! screen->rfbClientHead) { /* waiting for a client */ - usleep(200 * 1000); - continue; - } - - if (nofb) { /* no framebuffer polling needed */ - if (cursor_pos) { - update_mouse(); - } - continue; - } - - if (watch_bell) { - /* - * check for any bell events. - * n.b. assumes -nofb folks do not want bell... - */ - watch_bell_event(); - } - if (! show_dragging && button_mask) { - /* if any button is pressed do not update screen */ - /* XXX consider: use_threads || got_pointer_input */ - X_LOCK; - XFlush(dpy); - X_UNLOCK; - } else { - /* for timing the scan to try to detect thrashing */ - double tm = 0.0; - dtime(&tm); - - rfbUndrawCursor(screen); - scan_for_updates(); - - dt = dtime(&tm); - } - - /* sleep a bit to lessen load */ - usleep(waitms * 1000); - cnt++; - } -} - -/* - * We need to handle user input, particularly pointer input, carefully. - * This function is only called when non-threaded. Note that - * rfbProcessEvents() only processes *one* pointer event per call, - * so if we interlace it with scan_for_updates(), we can get swamped - * with queued up pointer inputs. And if the pointer inputs are inducing - * large changes on the screen (e.g. window drags), the whole thing - * bogs down miserably and only comes back to life at some time after - * one stops moving the mouse. So, to first approximation, we are trying - * to eat as much user input here as we can using some hints from the - * duration of the previous scan_for_updates() call (in dt). - * - * note: we do this even under -nofb - * - * return of 1 means watch_loop should short-circuit and reloop, - * return of 0 means watch_loop should proceed to scan_for_updates(). - */ - -int check_user_input(double dt, int *cnt) { - - if (old_pointer) { - /* every n-th drops thru to scan */ - if ((got_user_input || ui_skip < 0) && *cnt % ui_skip != 0) { - *cnt++; - XFlush(dpy); - return 1; /* short circuit watch_loop */ - } else { - return 0; - } - } - - if (got_keyboard_input) { - if (*cnt % ui_skip != 0) { - *cnt++; - return 1; /* short circuit watch_loop */ - } - /* otherwise continue with pointer input */ - } - - if (got_pointer_input) { - int eaten = 0, miss = 0, max_eat = 50; - int g, g_in; - double spin = 0.0, tm = 0.0; - double quick_spin_fac = 0.40; - double grind_spin_time = 0.175; - - - dtime(&tm); - g = g_in = got_pointer_input; - - /* - * Try for some "quick" pointer input processing. - * - * About as fast as we can, we try to process user input - * calling rfbProcessEvents or rfbCheckFds. We do this - * for a time on order of the last scan_for_updates() time, - * dt, but if we stop getting user input we break out. - * We will also break out if we have processed max_eat - * inputs. - * - * Note that rfbCheckFds() does not send any framebuffer - * updates, so is more what we want here, although it is - * likely they have all be sent already. - */ - while (1) { - rfbCheckFds(screen, 1000); - XFlush(dpy); - - spin += dtime(&tm); - - if (spin > quick_spin_fac * dt) { - /* get out if spin time comparable to last scan time */ - break; - } - if (got_pointer_input > g) { - g = got_pointer_input; - if (eaten++ < max_eat) { - continue; - } - } else { - miss++; - } - if (miss > 1) { /* 1 means out on 2nd miss */ - break; - } - } - - - /* - * Probably grinding with a lot of fb I/O if dt is - * this large. (need to do this more elegantly) - * - * Current idea is to spin our wheels here *not* processing - * any fb I/O, but still processing the user input. - * This user input goes to the X display and changes it, - * but we don't poll it while we "rest" here for a time - * on order of dt, the previous scan_for_updates() time. - * We also break out if we miss enough user input. - */ - if (dt > grind_spin_time) { - int i, ms, split = 30; - double shim; - - /* - * Break up our pause into 'split' steps. - * We get at most one input per step. - */ - shim = 0.75 * dt / split; - - ms = (int) (1000 * shim); - - /* cutoff how long the pause can be */ - if (split * ms > 300) { - ms = 300 / split; - } - - spin = 0.0; - tm = 0.0; - dtime(&tm); - - g = got_pointer_input; - miss = 0; - for (i=0; i<split; i++) { - usleep(ms * 1000); - rfbCheckFds(screen, 1000); - spin += dtime(&tm); - if (got_pointer_input > g) { - XFlush(dpy); - miss = 0; - } else { - miss++; - } - g = got_pointer_input; - if (miss > 2) { - break; - } - if (1000 * spin > ms * split) { - break; - } - } - } - } - return 0; -} - -void print_help() { - char help[] = -"\n" -"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" -"By default x11vnc will not allow the screen to be shared and it will\n" -"exit as soon as a client disconnects. See -shared and -forever below\n" -"to override these protections.\n" -"\n" -"For additional info see: http://www.karlrunge.com/x11vnc/\n" -" and http://www.karlrunge.com/x11vnc/#faq\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" -" Equivalent to setting the DISPLAY env. variable.\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" -"-notruecolor Force 8bpp indexed color even if it looks like TrueColor.\n" -"\n" -"-visual n Experimental option: probably does not do what you\n" -" think. It simply *forces* the visual used for the\n" -" framebuffer; this may be a bad thing... It is useful for\n" -" testing and for some workarounds. n may be a decimal\n" -" number, or 0x hex. Run xdpyinfo(1) for the values.\n" -" One may also use \"TrueColor\", etc. see <X11/X.h>\n" -" for a list. If the string ends in \":m\" for better\n" -" or for worse the visual depth is forced to be m.\n" -"\n" -"-viewonly All clients can only watch (default %s).\n" -"-shared VNC display is shared (default %s).\n" -"-forever Keep listening for more connections rather than exiting\n" -" as soon as the first client(s) disconnect. Same as -many\n" -"-connect string For use with \"vncviewer -listen\" reverse connections.\n" -" If string has the form \"host\" or \"host:port\"\n" -" the connection is made once at startup. Use commas\n" -" for a list. If string contains \"/\" it is a file to\n" -" periodically check for new hosts. The first line is\n" -" read and then file is truncated.\n" -"-vncconnect Monitor the VNC_CONNECT X property set by vncconnect(1).\n" -"-auth file Set the X authority file to be \"file\", equivalent to\n" -" setting the XAUTHORITY env. var to \"file\" before startup.\n" -"-allow addr1[,addr2..] Only allow client connections from IP addresses matching\n" -" the comma separated list of numerical addresses.\n" -" Can be a prefix, e.g. \"192.168.100.\" to match a\n" -" simple subnet, for more control build libvncserver with\n" -" libwrap support.\n" -"-localhost Same as -allow 127.0.0.1\n" -"-passwdfile filename Specify libvncserver -passwd via the first line of file\n" -" \"filename\" instead of via command line. Note: this\n" -" is a simple plaintext passwd, see also -rfbauth below.\n" -"-accept string Run a command (possibly to prompt the user at the\n" -" X11 display) to decide whether an incoming client\n" -" should be allowed to connect or not. \"string\" is\n" -" an external command run via system(3) (see below for\n" -" special cases). Be sure to quote \"string\" if it\n" -" contains spaces, etc. The RFB_CLIENT_IP environment\n" -" variable will be set to the incoming client IP number\n" -" and the port in RFB_CLIENT_PORT (or -1 if unavailable).\n" -" The x11vnc process id will be in RFB_X11VNC_PID and a\n" -" client id number in RFB_CLIENT_ID. If the external\n" -" command returns 0 the client is accepted, otherwise\n" -" the client is rejected. See below for an extension to\n" -" accept a client view-only.\n" -"\n" -" If \"string\" is \"popup\" then a builtin popup window\n" -" is used. The popup will time out after 120 seconds,\n" -" use \"popup:N\" to modify the timeout to N seconds\n" -" (use 0 for no timeout)\n" -"\n" -" If \"string\" is \"xmessage\" then an xmessage(1)\n" -" invocation is used for the command.\n" -"\n" -" Both \"popup\" and \"xmessage\" will present an option\n" -" for accepting the client \"View-Only\" (the client\n" -" can only watch). This option will not be presented if\n" -" -viewonly has been specified, in which case the entire\n" -" display is view only.\n" -"\n" -" If the user supplied command is prefixed with something\n" -" like \"yes:0,no:*,view:3 mycommand ...\" then this\n" -" associates the numerical command return code with\n" -" the actions: accept, reject, and accept-view-only,\n" -" respectively. Use \"*\" instead of a number to indicate\n" -" the default action (in case the command returns an\n" -" unexpected value). E.g. \"no:*\" is a good choice.\n" -"\n" -" Note that x11vnc blocks while the external command or\n" -" or popup is running (other clients may see no updates\n" -" during this period).\n" -"\n" -" More -accept tricks: use \"popupmouse\" to only allow\n" -" mouse clicks in the builtin popup to be recognized.\n" -" Similarly use \"popupkey\" to only recognize keystroke\n" -" responses. All 3 of the popup keywords can be followed\n" -" by +N+M to supply a position for the popup window.\n" -" The default is to center the popup window.\n" -"\n" -"-gone string As -accept string, except to run a user supplied command\n" -" when a client goes away (disconnects). Unlike -accept,\n" -" the command return code is not interpreted by x11vnc.\n" -"\n" -"-inetd Launched by inetd(1): stdio instead of listening socket.\n" -" Note: if you are not redirecting stderr to a log file\n" -" you must also specify the -q option.\n" -"\n" -"-noshm Do not use the MIT-SHM extension for the polling.\n" -" Remote displays can be polled this way: be careful this\n" -" can use large amounts of network bandwidth. This is\n" -" also of use if the local machine has a limited number\n" -" of shm segments and -onetile is not sufficient.\n" -"-flipbyteorder Sometimes needed if remotely polled host has different\n" -" endianness. Ignored unless -noshm is set.\n" -"-blackout string Black out rectangles on the screen. string is a comma\n" -" separated list of WxH+X+Y type geometries for each rect.\n" -"-xinerama If your screen is composed of multiple monitors\n" -" glued together via XINERAMA, and that screen is\n" -" non-rectangular this option will try to guess the areas\n" -" to black out (if your system has libXinerama).\n" -"\n" -"-o logfile Write stderr messages to file \"logfile\" instead of\n" -" to the terminal. Same as -logfile.\n" -"-q Be quiet by printing less informational output to\n" -" stderr. Same as -quiet.\n" -"-bg Go into the background after screen setup. Messages to\n" -" stderr are lost unless -o logfile is used. Something\n" -" 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" -"-remap string Read keysym remappings from file \"string\". Format is\n" -" one pair of keysyms per line (can be name or hex value).\n" -" \"string\" can also be of form: key1-key2,key3-key4...\n" -" To map a key to a button click, use the fake keysyms\n" -" \"Button1\", ..., etc. E.g. -remap Super_R-Button2\n" -"-nobell Do not watch for XBell events.\n" -"-nofb Ignore framebuffer: only process keyboard and pointer.\n" -"-nosel Do not manage exchange of X selection/cutbuffer.\n" -"-noprimary Do not poll the PRIMARY selection for changes and send\n" -" back to clients. PRIMARY is still set on received\n" -" changes, however.\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" -"-xwarppointer Move the pointer with XWarpPointer instead of XTEST\n" -" (try as a workaround if pointer behaves poorly, e.g.\n" -" on touchscreens or other non-standard setups).\n" -"-cursorpos Send the X cursor position back to all vnc clients that\n" -" support the TightVNC CursorPosUpdates extension.\n" -"-buttonmap string String to remap mouse buttons. Format: IJK-LMN, this\n" -" maps buttons I -> L, etc., e.g. -buttonmap 13-31\n" -"\n" -" Button presses can also be mapped to keystrokes: replace\n" -" a button digit on the right of the dash with :<sym>:\n" -" or :<sym1>+<sym2>: etc. for multiple keys. For example,\n" -" if the viewing machine has a mouse-wheel (buttons 4 5)\n" -" but the x11vnc side does not, these will do scrolls:\n" -" -buttonmap 12345-123:Prior::Next:\n" -" -buttonmap 12345-123:Up+Up+Up::Down+Down+Down:\n" -"\n" -" If you include a modifier like \"Shift_L\" the\n" -" modifier's up/down state is toggled, e.g. to send\n" -" \"The\" use :Shift_L+t+Shift_L+h+e: (the 1st one is\n" -" shift down and the 2nd one is shift up). (note: the\n" -" initial state of the modifier is ignored and not reset)\n" -" To include button events use \"Button1\", ... etc.\n" -"\n" -"-nodragging Do not update the display during mouse dragging events\n" -" (mouse motion with a button held down). Greatly\n" -" improves response on slow setups, but you lose all\n" -" visual feedback for drags, text selection, and some\n" -" menu traversals.\n" -"-old_pointer Do not use the new pointer input handling mechanisms.\n" -" See check_input() and pointer() for details.\n" -"-input_skip n For the old pointer handling when non-threaded: try to\n" -" read n user input events before scanning display. n < 0\n" -" means to act as though there is always user input.\n" -"\n" -"-debug_pointer Print debugging output for every pointer event.\n" -"-debug_keyboard Print debugging output for every keyboard event.\n" -"\n" -"-defer time Time in ms to wait for updates before sending to client\n" -" [rfbDeferUpdateTime] (default %d).\n" -"-wait time Time in ms to pause between screen polls. Used to cut\n" -" 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" -"-sigpipe string Broken pipe (SIGPIPE) handling. \"string\" can be\n" -" \"ignore\" or \"exit\". For \"ignore\" libvncserver\n" -" will handle the abrupt loss of a client and continue,\n" -" for \"exit\" x11vnc will cleanup and exit at the 1st\n" -" broken connection. Default is \"ignore\".\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" -"-onetile Do not use the new copy_tiles() framebuffer mechanism,\n" -" just use 1 shm tile for polling. Same as -old_copytile.\n" -" Limits shm segments used to 3.\n" -"-gaps n Heuristic to fill in gaps in rows or cols of n or\n" -" less 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" -"-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" -"%s\n" -"\n" -"These options are passed to libvncserver:\n" -"\n" -; - fprintf(stderr, help, - view_only ? "on":"off", - shared ? "on":"off", - use_modifier_tweak ? "on":"off", - defer_update, - waitms, - take_naps ? "on":"off", -#ifdef LIBVNCSERVER_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 *this_host() { - char host[MAXN]; -#ifdef LIBVNCSERVER_HAVE_GETHOSTNAME - if (gethostname(host, MAXN) == 0) { - return strdup(host); - } -#endif - return NULL; -} - -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] == ':') { - if (this_host() != NULL) { - strncpy(title, this_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; -} - -/* - * check blacklist for OSs with tight shm limits. - */ -int limit_shm(void) { - struct utsname ut; - int limit = 0; - - if (uname(&ut) == -1) { - return 0; - } - if (!strcmp(ut.sysname, "SunOS")) { - char *r = ut.release; - if (*r == '5' && *(r+1) == '.') { - if (strchr("2345678", *(r+2)) != NULL) { - limit = 1; - } - } - } - if (limit && ! quiet) { - fprintf(stderr, "reducing shm usage on %s %s (adding " - "-onetile)\n", ut.sysname, ut.release); - } - return limit; -} - -int main(int argc, char** argv) { - - XImage *fb; - int i, op, ev, er, maj, min; - char *use_dpy = NULL; - char *auth_file = NULL; - char *arg, *visual_str = NULL; - char *logfile = NULL; - char *passwdfile = NULL; - int pw_loc = -1; - int dt = 0; - int bg = 0; - int got_rfbwait = 0; - int got_deferupdate = 0, got_defer = 0; - - /* used to pass args we do not know about to rfbGetScreen(): */ - int argc2 = 1; char *argv2[128]; - - argv2[0] = strdup(argv[0]); - - for (i=1; i < argc; i++) { - /* quick-n-dirty --option handling. */ - arg = argv[i]; - if (strstr(arg, "--") == arg) { - arg++; - } - - if (!strcmp(arg, "-display")) { - use_dpy = argv[++i]; - } else if (!strcmp(arg, "-id")) { - if (sscanf(argv[++i], "0x%x", &subwin) != 1) { - if (sscanf(argv[i], "%d", &subwin) != 1) { - fprintf(stderr, "bad -id arg: %s\n", - argv[i]); - exit(1); - } - } - } else if (!strcmp(arg, "-visual")) { - visual_str = argv[++i]; - } else if (!strcmp(arg, "-flashcmap")) { - flash_cmap = 1; - } else if (!strcmp(arg, "-notruecolor")) { - force_indexed_color = 1; - } else if (!strcmp(arg, "-viewonly")) { - view_only = 1; - } else if (!strcmp(arg, "-passwdfile")) { - passwdfile = argv[++i]; - } else if (!strcmp(arg, "-shared")) { - shared = 1; - } else if (!strcmp(arg, "-auth")) { - auth_file = argv[++i]; - } else if (!strcmp(arg, "-allow")) { - allow_list = argv[++i]; - } else if (!strcmp(arg, "-localhost")) { - allow_list = "127.0.0.1"; - } else if (!strcmp(arg, "-accept")) { - accept_cmd = argv[++i]; - } else if (!strcmp(arg, "-gone")) { - gone_cmd = argv[++i]; - } else if (!strcmp(arg, "-many") - || !strcmp(arg, "-forever")) { - connect_once = 0; - } else if (!strcmp(arg, "-connect")) { - i++; - if (strchr(arg, '/')) { - client_connect_file = argv[i]; - } else { - client_connect = strdup(argv[i]); - } - } else if (!strcmp(arg, "-vncconnect")) { - vnc_connect = 1; - } else if (!strcmp(arg, "-inetd")) { - inetd = 1; - } else if (!strcmp(arg, "-noshm")) { - using_shm = 0; - } else if (!strcmp(arg, "-flipbyteorder")) { - flip_byte_order = 1; - } else if (!strcmp(arg, "-modtweak")) { - use_modifier_tweak = 1; - } else if (!strcmp(arg, "-nomodtweak")) { - use_modifier_tweak = 0; - } else if (!strcmp(arg, "-remap")) { - remap_file = argv[++i]; - } else if (!strcmp(arg, "-blackout")) { - blackout_string = argv[++i]; - } else if (!strcmp(arg, "-xinerama")) { - xinerama = 1; - } else if (!strcmp(arg, "-nobell")) { - watch_bell = 0; - } else if (!strcmp(arg, "-nofb")) { - nofb = 1; - } else if (!strcmp(arg, "-nosel")) { - watch_selection = 0; - } else if (!strcmp(arg, "-noprimary")) { - watch_primary = 0; - } else if (!strcmp(arg, "-nocursor")) { - local_cursor = 0; - } else if (!strcmp(arg, "-mouse")) { - show_mouse = 1; - } else if (!strcmp(arg, "-mouseX")) { - show_mouse = 1; - show_root_cursor = 1; - } else if (!strcmp(arg, "-X")) { - show_mouse = 1; - show_root_cursor = 1; - local_cursor = 0; - } else if (!strcmp(arg, "-xwarppointer")) { - use_xwarppointer = 1; - } else if (!strcmp(arg, "-cursorpos")) { - cursor_pos = 1; - } else if (!strcmp(arg, "-buttonmap")) { - pointer_remap = argv[++i]; - } else if (!strcmp(arg, "-nodragging")) { - show_dragging = 0; - } else if (!strcmp(arg, "-input_skip")) { - ui_skip = atoi(argv[++i]); - if (! ui_skip) ui_skip = 1; - } else if (!strcmp(arg, "-old_pointer")) { - old_pointer = 1; - } else if (!strcmp(arg, "-onetile") - || !strcmp(arg, "-old_copytile")) { - single_copytile = 1; - } else if (!strcmp(arg, "-debug_pointer")) { - debug_pointer++; - } else if (!strcmp(arg, "-debug_keyboard")) { - debug_keyboard++; - } else if (!strcmp(arg, "-defer")) { - defer_update = atoi(argv[++i]); - got_defer = 1; - } else if (!strcmp(arg, "-wait")) { - waitms = atoi(argv[++i]); - } else if (!strcmp(arg, "-nap")) { - take_naps = 1; -#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD - } else if (!strcmp(arg, "-threads")) { - use_threads = 1; - } else if (!strcmp(arg, "-nothreads")) { - use_threads = 0; -#endif - } else if (!strcmp(arg, "-sigpipe")) { - if (!strcmp(argv[++i], "ignore")) { - sigpipe = 1; - } else if (!strcmp(argv[i], "exit")) { - sigpipe = 2; - } else if (!strcmp(argv[i], "skip")) { - sigpipe = 0; - } else { - fprintf(stderr, "bad -sigpipe arg: %s, must " - "be \"ignore\" or \"exit\"\n", argv[i]); - exit(1); - } - } else if (!strcmp(arg, "-fs")) { - fs_frac = atof(argv[++i]); - } else if (!strcmp(arg, "-gaps")) { - gaps_fill = atoi(argv[++i]); - } else if (!strcmp(arg, "-grow")) { - grow_fill = atoi(argv[++i]); - } else if (!strcmp(arg, "-fuzz")) { - tile_fuzz = atoi(argv[++i]); - } else if (!strcmp(arg, "-hints")) { - use_hints = 1; - } else if (!strcmp(arg, "-nohints")) { - use_hints = 0; - } else if (!strcmp(arg, "-h") || !strcmp(arg, "-help") - || !strcmp(arg, "-?")) { - print_help(); - } else if (!strcmp(arg, "-o") || !strcmp(arg, "-logfile")) { - logfile = argv[++i]; - } else if (!strcmp(arg, "-q") || !strcmp(arg, "-quiet")) { - quiet = 1; -#ifdef LIBVNCSERVER_HAVE_SETSID - } else if (!strcmp(arg, "-bg") || !strcmp(arg, "-background")) { - bg = 1; -#endif - } else { - if (!strcmp(arg, "-desktop")) { - dt = 1; - } - if (!strcmp(arg, "-passwd")) { - pw_loc = i; - } - if (!strcmp(arg, "-rfbwait")) { - got_rfbwait = 1; - } - if (!strcmp(arg, "-deferupdate")) { - got_deferupdate = 1; - } - if (!strcmp(arg, "-rfbport")) { - got_rfbport = 1; - } - if (!strcmp(arg, "-alwaysshared ")) { - got_alwaysshared = 1; - } - if (!strcmp(arg, "-nevershared")) { - got_nevershared = 1; - } - /* otherwise copy it for libvncserver use below. */ - if (argc2 < 100) { - argv2[argc2++] = strdup(arg); - } - } - } - if (logfile) { - int n; - if ((n = open(logfile, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) { - fprintf(stderr, "error opening logfile: %s\n", logfile); - perror("open"); - exit(1); - } - if (dup2(n, 2) < 0) { - fprintf(stderr, "dup2 failed\n"); - perror("dup2"); - exit(1); - } - if (n > 2) { - close(n); - } - } - if (! quiet && ! inetd) { - int i; - for (i=1; i < argc2; i++) { - fprintf(stderr, "passing arg to libvncserver: %s\n", - argv2[i]); - if (!strcmp(argv2[i], "-passwd")) { - i++; - } - } - } - - - /* - * If -passwd was used, clear it out of argv. This does not - * work on all UNIX, have to use execvp() in general... - */ - if (pw_loc > 0) { - char *p = argv[pw_loc]; - while (*p != '\0') { - *p++ = '\0'; - } - if (pw_loc+1 < argc) { - p = argv[pw_loc+1]; - while (*p != '\0') { - *p++ = '\0'; - } - } - } else if (passwdfile) { - char line[512]; - FILE *in; - in = fopen(passwdfile, "r"); - if (in == NULL) { - fprintf(stderr, "cannot open passwdfile: %s\n", - passwdfile); - perror("fopen"); - exit(1); - } - if (fgets(line, 512, in) != NULL) { - line[strlen(line)-1] = '\0'; - argv2[argc2++] = "-passwd"; - argv2[argc2++] = strdup(line); - } else { - fprintf(stderr, "cannot read passwdfile: %s\n", - passwdfile); - perror("fgets"); - exit(1); - } - } - - /* fixup settings that do not make sense */ - - if (use_threads && nofb && cursor_pos) { - if (! quiet) { - fprintf(stderr, "disabling -threads under -nofb " - "-cursorpos\n"); - } - use_threads = 0; - } - if (tile_fuzz < 1) { - tile_fuzz = 1; - } - if (waitms < 0) { - waitms = 0; - } - if (inetd) { - shared = 0; - connect_once = 1; - bg = 0; - } - - /* increase rfbwait if threaded */ - if (use_threads && ! got_rfbwait) { - argv2[argc2++] = "-rfbwait"; - argv2[argc2++] = "604800000"; /* one week... */ - } - - /* check for OS with small shm limits */ - if (using_shm && ! single_copytile) { - if (limit_shm()) { - single_copytile = 1; - } - } - - if (nofb && ! got_deferupdate && ! got_defer) { - /* reduce defer time under -nofb */ - defer_update = defer_update_nofb; - } - if (! got_deferupdate) { - char tmp[40]; - /* XXX not working yet in libvncserver */ - sprintf(tmp, "%d", defer_update); - argv2[argc2++] = "-deferupdate"; - argv2[argc2++] = strdup(tmp); - } - if (debug_pointer || debug_keyboard) { - if (bg || quiet) { - fprintf(stderr, "disabling -bg/-q under -debug_pointer" - "/-debug_keyboard\n"); - bg = 0; - quiet = 0; - } - } - - if (! quiet) { - fprintf(stderr, "\n"); - fprintf(stderr, "display: %s\n", use_dpy ? use_dpy - : "null"); - fprintf(stderr, "subwin: 0x%x\n", subwin); - fprintf(stderr, "visual: %s\n", visual_str ? visual_str - : "null"); - fprintf(stderr, "flashcmap: %d\n", flash_cmap); - fprintf(stderr, "force_idx: %d\n", force_indexed_color); - fprintf(stderr, "viewonly: %d\n", view_only); - fprintf(stderr, "shared: %d\n", shared); - fprintf(stderr, "authfile: %s\n", auth_file ? auth_file - : "null"); - fprintf(stderr, "passfile: %s\n", passwdfile ? passwdfile - : "null"); - fprintf(stderr, "logfile: %s\n", logfile ? logfile - : "null"); - fprintf(stderr, "allow: %s\n", allow_list ? allow_list - : "null"); - fprintf(stderr, "accept: %s\n", accept_cmd ? accept_cmd - : "null"); - fprintf(stderr, "gone: %s\n", gone_cmd ? gone_cmd - : "null"); - fprintf(stderr, "conn_once: %d\n", connect_once); - fprintf(stderr, "connect: %s\n", client_connect - ? client_connect : "null"); - fprintf(stderr, "connectfile %s\n", client_connect_file - ? client_connect_file : "null"); - fprintf(stderr, "vnc_conn: %d\n", vnc_connect); - fprintf(stderr, "inetd: %d\n", inetd); - fprintf(stderr, "using_shm: %d\n", using_shm); - fprintf(stderr, "flipbytes: %d\n", flip_byte_order); - fprintf(stderr, "mod_tweak: %d\n", use_modifier_tweak); - fprintf(stderr, "remap: %s\n", remap_file ? remap_file - : "null"); - fprintf(stderr, "blackout: %s\n", blackout_string - ? blackout_string : "null"); - fprintf(stderr, "xinerama: %d\n", xinerama); - fprintf(stderr, "watchbell: %d\n", watch_bell); - fprintf(stderr, "nofb: %d\n", nofb); - fprintf(stderr, "watchsel: %d\n", watch_selection); - fprintf(stderr, "watchprim: %d\n", watch_primary); - 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, "xwarpptr: %d\n", use_xwarppointer); - fprintf(stderr, "cursorpos: %d\n", cursor_pos); - fprintf(stderr, "buttonmap: %s\n", pointer_remap - ? pointer_remap : "null"); - fprintf(stderr, "dragging: %d\n", show_dragging); - fprintf(stderr, "inputskip: %d\n", ui_skip); - fprintf(stderr, "old_ptr: %d\n", old_pointer); - fprintf(stderr, "onetile: %d\n", single_copytile); - fprintf(stderr, "debug_ptr: %d\n", debug_pointer); - fprintf(stderr, "debug_key: %d\n", debug_keyboard); - fprintf(stderr, "defer: %d\n", defer_update); - fprintf(stderr, "waitms: %d\n", waitms); - fprintf(stderr, "take_naps: %d\n", take_naps); - fprintf(stderr, "sigpipe: %d\n", sigpipe); - 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); - fprintf(stderr, "bg: %d\n", bg); - fprintf(stderr, "%s\n", lastmod); - } else { - rfbLogEnable(0); - } - - /* open the X display: */ - X_INIT; - if (auth_file) { - char *tmp; - int len = strlen("XAUTHORITY=") + strlen(auth_file) + 1; - tmp = (char *) malloc((size_t) len); - sprintf(tmp, "XAUTHORITY=%s", auth_file); - putenv(tmp); - } - if (use_dpy) { - dpy = XOpenDisplay(use_dpy); - } else if ( (use_dpy = getenv("DISPLAY")) ) { - dpy = XOpenDisplay(use_dpy); - } else { - dpy = XOpenDisplay(""); - } - - if (! dpy) { - fprintf(stderr, "XOpenDisplay failed (%s)\n", - use_dpy ? use_dpy:"null"); - exit(1); - } else if (use_dpy) { - if (! quiet) fprintf(stderr, "Using X display %s\n", use_dpy); - } else { - if (! quiet) fprintf(stderr, "Using default X display.\n"); - } - - /* check for XTEST */ - if (! XTestQueryExtension(dpy, &ev, &er, &maj, &min)) { - fprintf(stderr, "Display does not support XTest extension.\n"); - exit(1); - } - - /* check for MIT-SHM */ - if (! nofb && ! XShmQueryExtension(dpy)) { - if (! using_shm) { - if (! quiet) { - fprintf(stderr, "warning: display does not " - "support XShm.\n"); - } - } else { - fprintf(stderr, "Display does not support XShm " - "extension (must be local).\n"); - exit(1); - } - } - - if (visual_str != NULL) { - set_visual(visual_str); - } -#ifdef LIBVNCSERVER_HAVE_XKEYBOARD - /* check for XKEYBOARD */ - if (watch_bell) { - if (! XkbQueryExtension(dpy, &op, &ev, &er, &maj, &min)) { - if (! quiet) { - fprintf(stderr, "warning: disabling bell.\n"); - } - watch_bell = 0; - } else { - initialize_watch_bell(); - } - } -#endif - - /* - * Window managers will often grab the display during resize, etc. - * To avoid deadlock (our user resize input is not processed) - * we tell the server to process our requests during all grabs: - */ - XTestGrabControl(dpy, True); - - scr = DefaultScreen(dpy); - rootwin = RootWindow(dpy, scr); - - /* set up parameters for subwin or non-subwin cases: */ - if (! subwin) { - window = rootwin; - dpy_x = DisplayWidth(dpy, scr); - 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) ) { - fprintf(stderr, "bad window: 0x%lx\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; - if (! quiet) { - fprintf(stderr, "disabling root cursor drawing" - " for subwindow\n"); - } - } - - set_offset(); - } - - /* initialize depth to reasonable value */ - depth = DefaultDepth(dpy, scr); - - /* - * User asked for non-default visual, this is not working well but it - * does some useful things... What should it do in general? - */ - if (visual_id) { - XVisualInfo vinfo_tmpl, *vinfo; - int n; - - vinfo_tmpl.visualid = visual_id; - vinfo = XGetVisualInfo(dpy, VisualIDMask, &vinfo_tmpl, &n); - if (vinfo == NULL || n == 0) { - fprintf(stderr, "could not match visual_id: 0x%x\n", - (int) visual_id); - exit(1); - } - visual = vinfo->visual; - depth = vinfo->depth; - if (visual_depth) { - depth = visual_depth; /* force it */ - } - if (! quiet) { - fprintf(stderr, "vis id: 0x%x\n", - (int) vinfo->visualid); - fprintf(stderr, "vis scr: %d\n", vinfo->screen); - fprintf(stderr, "vis depth %d\n", vinfo->depth); - fprintf(stderr, "vis class %d\n", vinfo->class); - fprintf(stderr, "vis rmask 0x%lx\n", vinfo->red_mask); - fprintf(stderr, "vis gmask 0x%lx\n", vinfo->green_mask); - fprintf(stderr, "vis bmask 0x%lx\n", vinfo->blue_mask); - fprintf(stderr, "vis cmap_sz %d\n", vinfo->colormap_size); - fprintf(stderr, "vis b/rgb %d\n", vinfo->bits_per_rgb); - } - - XFree(vinfo); - } - - - if (nofb || visual_id) { - fb = XCreateImage(dpy, visual, depth, ZPixmap, 0, NULL, - dpy_x, dpy_y, BitmapPad(dpy), 0); - /* - * For -nofb we do not allocate the framebuffer, so we - * can save a few MB of memory. - */ - if (! nofb) { - fb->data = (char *) malloc(fb->bytes_per_line * - fb->height); - } - } else { - fb = XGetImage(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, - ZPixmap); - if (! quiet) { - fprintf(stderr, "Read initial data from X display into" - " framebuffer.\n"); - } - } - if (fb->bits_per_pixel == 24 && ! quiet) { - fprintf(stderr, "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 any X11 calls until watch_loop() - * is called since we are single-threaded until then. - */ - - initialize_screen(&argc2, argv2, fb); - - initialize_tiles(); - - /* rectangular blackout regions */ - if (blackout_string != NULL) { - initialize_blackout(blackout_string); - } - if (xinerama) { - initialize_xinerama(); - } - if (blackouts) { - blackout_tiles(); - } - - initialize_shm(); /* also creates XImages when using_shm = 0 */ - - set_signals(); - - if (blackouts) { /* blackout fb as needed. */ - copy_screen(); - } - - if (use_modifier_tweak) { - initialize_modtweak(); - } - if (remap_file != NULL) { - initialize_remap(remap_file); - } - initialize_pointer_map(); - - if (! inetd) { - if (! screen->rfbPort || screen->rfbListenSock < 0) { - rfbLog("Error: could not obtain listening port.\n"); - clean_up_exit(1); - } - } - if (! quiet) { - rfbLog("screen setup finished.\n"); - } - if (screen->rfbPort) { - char *host = this_host(); - int port = screen->rfbPort; - if (host != NULL) { - /* note that vncviewer special cases 5900-5999 */ - if (inetd) { - ; /* should not occur */ - } else if (quiet) { - if (port >= 5900) { - fprintf(stderr, "The VNC desktop is " - "%s:%d\n", host, port - 5900); - } else { - fprintf(stderr, "The VNC desktop is " - "%s:%d\n", host, port); - } - } else if (port >= 5900) { - rfbLog("The VNC desktop is %s:%d\n", host, - port - 5900); - if (port >= 6000) { - rfbLog("possible aliases: %s:%d, " - "%s::%d\n", host, port, host, port); - } - } else { - rfbLog("The VNC desktop is %s:%d\n", host, - port); - rfbLog("possible alias: %s::%d\n", - host, port); - } - } - fflush(stderr); - fprintf(stdout, "PORT=%d\n", screen->rfbPort); - fflush(stdout); - } - -#if defined(LIBVNCSERVER_HAVE_FORK) && defined(LIBVNCSERVER_HAVE_SETSID) - if (bg) { - /* fork into the background now */ - int p, n; - if ((p = fork()) > 0) { - exit(0); - } else if (p == -1) { - fprintf(stderr, "could not fork\n"); - perror("fork"); - clean_up_exit(1); - } - if (setsid() == -1) { - fprintf(stderr, "setsid failed\n"); - perror("setsid"); - clean_up_exit(1); - } - /* adjust our stdio */ - n = open("/dev/null", O_RDONLY); - dup2(n, 0); - dup2(n, 1); - if (! logfile) { - dup2(n, 2); - } - if (n > 2) { - close(n); - } - } -#endif - - watch_loop(); - - return(0); -} |