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