Commit c984f2b1 authored by dscho's avatar dscho

new version from Karl!

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