diff --git a/CHANGES b/CHANGES index b58f6d9..0f49c74 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,4 @@ + Better x11vnc from Karl J. Runge! fixed severe bug (Const Kaplinsky) got patch from Const Kaplisnky with CursorPosUpdate encoding and some Docs sync'ed with newest RealVNC (ZRLE encoding) diff --git a/Makefile b/Makefile index 8d28dfb..67370d3 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ VNCSERVERLIB=-L. -lvncserver -L/usr/local/lib -lz -ljpeg CXX=g++ CC=gcc +LINK=gcc # for Solaris #EXTRALIBS=-lsocket -lnsl -L/usr/X/lib @@ -36,6 +37,7 @@ ZRLE_SRCS=zrle.cc rdr/FdInStream.cxx rdr/FdOutStream.cxx rdr/InStream.cxx \ ZRLE_OBJS=zrle.o rdr/FdInStream.o rdr/FdOutStream.o rdr/InStream.o \ rdr/NullOutStream.o rdr/ZlibInStream.o rdr/ZlibOutStream.o ZRLE_DEF=-DHAVE_ZRLE +LINK=$(CXX) %.o: %.cxx $(CXX) $(CXXFLAGS) -c -o $@ $< @@ -75,52 +77,53 @@ libvncserver.a: $(OBJS) translate.o: translate.c tableinit24.c tableinitcmtemplate.c tableinittctemplate.c tabletrans24template.c tabletranstemplate.c example: example.o libvncserver.a - $(CC) -o example example.o $(LIBS) + $(LINK) -o example example.o $(LIBS) pnmshow: pnmshow.o libvncserver.a - $(CC) -o pnmshow pnmshow.o $(LIBS) + $(LINK) -o pnmshow pnmshow.o $(LIBS) mac.o: mac.c 1instance.c OSXvnc-server: mac.o libvncserver.a - $(CC) -o OSXvnc-server mac.o $(LIBS) $(OSX_LIBS) + $(LINK) -o OSXvnc-server mac.o $(LIBS) $(OSX_LIBS) -x11vnc.o: x11vnc.c 1instance.c +x11vnc.o: contrib/x11vnc.c rfb.h 1instance.c Makefile + $(CC) $(CFLAGS) -I. -c -o x11vnc.o contrib/x11vnc.c x11vnc: x11vnc.o libvncserver.a - $(CC) -g -o x11vnc x11vnc.o $(LIBS) $(XLIBS) + $(LINK) -g -o x11vnc x11vnc.o $(LIBS) $(XLIBS) x11vnc_static: x11vnc.o libvncserver.a - $(CC) -o x11vnc_static x11vnc.o libvncserver.a /usr/lib/libz.a /usr/lib/libjpeg.a $(XLIBS) + $(LINK) -o x11vnc_static x11vnc.o libvncserver.a /usr/lib/libz.a /usr/lib/libjpeg.a $(XLIBS) #$(LIBS) $(XLIBS) storepasswd: storepasswd.o d3des.o vncauth.o - $(CC) -o storepasswd storepasswd.o d3des.o vncauth.o + $(LINK) -o storepasswd storepasswd.o d3des.o vncauth.o sratest: sratest.o - $(CC) -o sratest sratest.o + $(LINK) -o sratest sratest.o sratest.o: sraRegion.c $(CC) $(CFLAGS) -DSRA_TEST -c -o sratest.o sraRegion.c blooptest: blooptest.o libvncserver.a - $(CC) -o blooptest blooptest.o $(LIBS) + $(LINK) -o blooptest blooptest.o $(LIBS) blooptest.o: example.c rfb.h $(CC) $(CFLAGS) -DBACKGROUND_LOOP_TEST -c -o blooptest.o example.c pnmshow24: pnmshow24.o libvncserver.a - $(CC) -o pnmshow24 pnmshow24.o $(LIBS) + $(LINK) -o pnmshow24 pnmshow24.o $(LIBS) fontsel: fontsel.o libvncserver.a - $(CC) -o fontsel fontsel.o -L. -lvncserver -lz -ljpeg + $(LINK) -o fontsel fontsel.o -L. -lvncserver -lz -ljpeg vncev: vncev.o libvncserver.a - $(CC) -o vncev vncev.o -L. -lvncserver -lz -ljpeg + $(LINK) -o vncev vncev.o -L. -lvncserver -lz -ljpeg # Example from Justin -zippy: zippy.o libvncserver.a - $(CC) -o zippy zippy.o -L. -lvncserver -lz -ljpeg +zippy: contrib/zippy.o libvncserver.a + $(LINK) -o zippy contrib/zippy.o -L. -lvncserver -lz -ljpeg clean: rm -f $(OBJS) *~ core "#"* *.bak *.orig storepasswd.o \ diff --git a/contrib/x11vnc.c b/contrib/x11vnc.c new file mode 100644 index 0000000..85366b7 --- /dev/null +++ b/contrib/x11vnc.c @@ -0,0 +1,1352 @@ +/* + * x0vnc.c: a VNC server for X displays. + * + * Copyright (c) 2002 Karl J. Runge All rights reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * + * This program is based heavily on the following programs: + * + * x11vnc.c of the libvncserver project (Johannes E. Schindelin) + * krfb, the KDE desktopsharing project (Tim Jansen) + * x0rfbserver, the original native X vnc server (Jens Wagner) + * + * The primary goal of this program is to create a portable and simple + * command-line server utility that allows a VNC viewer to connect to an + * actual X display (as x11vnc, krfb, and x0rfbserver do). The only + * non-standard dependency of this program is the static library + * libvncserver.a (although in some environments libjpeg.so may not be + * readily available). To increase portability it is written in plain C. + * + * The next goal is to improve performance and interactive response. + * The algorithm currently used here to achieve this is that of krfb + * (based on x0rfbserver algorithm). Additional heuristics are also + * applied. Currently there are a bit too many of these... + * + * To build: + * Obtain the libvncserver package (http://libvncserver.sourceforge.net) + * and ensure that "make" and "make x11vnc" work correctly. Defining + * HAVE_PTHREADS in the Makefile is recommended. One then could move the + * x11vnc.c file aside, copy this file in its place into the source tree, + * and issue: + * + * make x11vnc + * mv x11vnc x0vnc + * + * to create the x0vnc binary. Otherwise, with x0vnc.c copied to + * libvncserver source directory, something like this should work: + * + * gcc -Wall -DALLOW24BPP -I. -DBACKCHANNEL -c x0vnc.c + * gcc -o x0vnc x0vnc.o -L. -lvncserver -L/usr/local/lib -lz -ljpeg \ + * -L/usr/X11R6/lib -lX11 -lXext -lXtst + * + * include -DHAVE_PTHREADS ... -lpthread to use threads. + * + * On Solaris the 2nd command might look something like: + * + * gcc -o x0vnc x0vnc.o -L. -lvncserver -L/usr/local/lib -lz -ljpeg \ + * -lsocket -lnsl -L/usr/X/lib -R/usr/X/lib -lX11 -lXext -lXtst + */ + +#define KeySym RFBKeySym +#include "rfb.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +Display *dpy = 0; +int dpy_x, dpy_y; +int bpp; +int window; +int button_mask = 0; + +XImage *tile; +XImage *scanline; +XImage *fullscreen; +int fs_factor = 0; + +XShmSegmentInfo tile_shm; +XShmSegmentInfo scanline_shm; +XShmSegmentInfo fullscreen_shm; + +rfbScreenInfoPtr screen; +rfbCursorPtr cursor; +int bytes_per_line; + +/* size of the basic tile unit that is polled for changes: */ +int tile_x = 32; +int tile_y = 32; +int ntiles, ntiles_x, ntiles_y; + +/* arrays that indicate changed or checked tiles. */ +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; +} region_t; + +/* array to hold the tiles region_t-s. */ +region_t *tile_region; + +typedef struct hint { + /* location x, y, height, and width of a change-rectangle */ + /* (grows as adjacent horizontal tiles are glued together) */ + int x, y, w, h; +} hint_t; + +/* array to hold the hints: */ +hint_t *hint_list; + +int shared = 0; /* share vnc display. */ +int view_only = 0; /* client can only watch. */ +int connect_once = 1; /* allow only one client connection. */ + +/* + * 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. + */ +int waitms = 30; +int defer_update = 30; /* rfbDeferUpdateTime ms to wait before sends. */ +int use_threads = 1; /* but only if compiled with HAVE_PTHREADS */ + +/* tile heuristics: */ +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.5; /* threshold tile fraction to do fullscreen updates. */ + +#define NSCAN 32 +int scanlines[NSCAN] = { /* scan pattern jitter from x0rfbserver */ + 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 got_user_input; +int count = 0; +int shut_down = 0; + +/* + * 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? + */ +MUTEX(x11Mutex); +#define X_LOCK LOCK(x11Mutex); +#define X_UNLOCK UNLOCK(x11Mutex); + +void mark_hint(hint_t); + +void clean_up_exit (void) { + + X_LOCK + + XTestDiscard(dpy); + + /* remove the shm areas: */ + + XShmDetach(dpy, &tile_shm); + XDestroyImage(tile); + shmdt(tile_shm.shmaddr); + shmctl(tile_shm.shmid, IPC_RMID, 0); + + XShmDetach(dpy, &scanline_shm); + XDestroyImage(scanline); + shmdt(scanline_shm.shmaddr); + shmctl(scanline_shm.shmid, IPC_RMID, 0); + + XShmDetach(dpy, &fullscreen_shm); + XDestroyImage(fullscreen); + shmdt(fullscreen_shm.shmaddr); + shmctl(fullscreen_shm.shmid, IPC_RMID, 0); + + /* more cleanup? */ + + X_UNLOCK + exit(0); +} + +void client_gone(rfbClientPtr client) { + if (connect_once) { + printf("only one connection allowed.\n"); + clean_up_exit(); + } +} + +enum rfbNewClientAction new_client(rfbClientPtr client) { + if (connect_once) { + client->clientGoneHook = client_gone; + } + if (view_only) { + client->clientData = (void *) -1; + } else { + client->clientData = (void *) 0; + } + return(RFB_CLIENT_ACCEPT); +} + +/* key event handler */ +static void keyboard(Bool down, KeySym keysym, rfbClientPtr client) { + KeyCode k; + + if (view_only) { + return; + } + + X_LOCK + + k = XKeysymToKeycode(dpy, keysym); + + if ( k != NoSymbol ) { + XTestFakeKeyEvent(dpy, k, down, CurrentTime); + XFlush(dpy); + + got_user_input++; + } + X_UNLOCK +} + +/* mouse event handler */ +static void pointer(int mask, int x, int y, rfbClientPtr client) { + int i; + + if (view_only) { + return; + } + + X_LOCK + + XTestFakeMotionEvent(dpy, 0, x, y, CurrentTime); + + got_user_input++; + + for (i=0; i < 5; i++) { + if ( (button_mask & (1<width, fb->height, + fb->bits_per_pixel, 8, fb->bits_per_pixel/8); + + screen->paddedWidthInBytes = fb->bytes_per_line; + screen->rfbServerFormat.bitsPerPixel = fb->bits_per_pixel; + screen->rfbServerFormat.depth = fb->depth; + screen->rfbServerFormat.trueColour = (CARD8) TRUE; + + if ( screen->rfbServerFormat.bitsPerPixel == 8 ) { + /* 8 bpp */ + screen->rfbServerFormat.redShift = 0; + screen->rfbServerFormat.greenShift = 2; + screen->rfbServerFormat.blueShift = 5; + screen->rfbServerFormat.redMax = 3; + screen->rfbServerFormat.greenMax = 7; + screen->rfbServerFormat.blueMax = 3; + } else { + /* general case ... */ + screen->rfbServerFormat.redShift = 0; + if ( fb->red_mask ) { + while ( ! (fb->red_mask + & (1 << screen->rfbServerFormat.redShift) ) ) { + screen->rfbServerFormat.redShift++; + } + } + screen->rfbServerFormat.greenShift = 0; + if ( fb->green_mask ) { + while ( ! (fb->green_mask + & (1 << screen->rfbServerFormat.greenShift) ) ) { + screen->rfbServerFormat.greenShift++; + } + } + screen->rfbServerFormat.blueShift = 0; + if ( fb->blue_mask ) { + while ( ! (fb->blue_mask + & (1 << screen->rfbServerFormat.blueShift) ) ) { + screen->rfbServerFormat.blueShift++; + } + } + screen->rfbServerFormat.redMax + = fb->red_mask >> screen->rfbServerFormat.redShift; + screen->rfbServerFormat.greenMax + = fb->green_mask >> screen->rfbServerFormat.greenShift; + screen->rfbServerFormat.blueMax + = fb->blue_mask >> screen->rfbServerFormat.blueShift; + } + + screen->frameBuffer = fb->data; + + /* XXX the following 3 settings are based on libvncserver defaults. */ + if (screen->rfbPort == 5900) { + screen->autoPort = TRUE; + } + if (screen->rfbDeferUpdateTime == 5) { + screen->rfbDeferUpdateTime = defer_update; + } + if (shared && ! screen->rfbNeverShared) { + screen->rfbAlwaysShared = TRUE; + } + + /* event callbacks: */ + screen->newClientHook = new_client; + screen->kbdAddEvent = keyboard; + screen->ptrAddEvent = pointer; + + cursor = rfbMakeXCursor(cursor_x, cursor_y, cur_data, cur_mask); + screen->cursor = cursor; + + rfbInitServer(screen); + + bytes_per_line = screen->paddedWidthInBytes; + bpp = screen->rfbServerFormat.bitsPerPixel; +} + +/* + * setup tile numbers and allocate the tile and hint arrays: + */ +void initialize_tiles() { + + ntiles_x = (dpy_x - 1)/tile_x + 1; + ntiles_y = (dpy_y - 1)/tile_y + 1; + ntiles = ntiles_x * ntiles_y; + + tile_has_diff = (char *) malloc((size_t) (ntiles * sizeof(char))); + tile_tried = (char *) malloc((size_t) (ntiles * sizeof(char))); + tile_region = (region_t *) malloc((size_t) (ntiles * sizeof(region_t))); + + /* there will never be more hints than tiles: */ + hint_list = (hint_t *) malloc((size_t) (ntiles * sizeof(hint_t))); +} + +/* + * silly function to factor dpy_y until fullscreen shm is not bigger than max. + * should always work unless dpy_y is a large prime or something... under + * failure fs_factor remains 0 and no fullscreen updates will be tried. + */ +void set_fs_factor(int max) { + int f, fac = 1, n = dpy_y; + + if ( (bpp/8) * dpy_x * dpy_y <= max ) { + fs_factor = 1; + return; + } + for (f=2; f <= 101; f++) { + while (n % f == 0) { + n = n / f; + fac = fac * f; + if ( (bpp/8) * dpy_x * (dpy_y/fac) <= max ) { + fs_factor = fac; + return; + } + } + } +} + +void initialize_shm() { + + /* the tile (e.g. 32x32) shared memory area image: */ + + tile = XShmCreateImage(dpy, DefaultVisual(dpy, 0), bpp, ZPixmap, + NULL, &tile_shm, tile_x, tile_y); + + tile_shm.shmid = shmget(IPC_PRIVATE, + tile->bytes_per_line * tile->height, IPC_CREAT | 0777); + + tile_shm.shmaddr = tile->data = (char *) shmat(tile_shm.shmid, 0, 0); + tile_shm.readOnly = False; + + XShmAttach(dpy, &tile_shm); + + + /* the scanline (e.g. 1280x1) shared memory area image: */ + + scanline = XShmCreateImage(dpy, DefaultVisual(dpy, 0), bpp, ZPixmap, + NULL, &scanline_shm, dpy_x, 1); + + scanline_shm.shmid = shmget(IPC_PRIVATE, + scanline->bytes_per_line * scanline->height, IPC_CREAT | 0777); + + scanline_shm.shmaddr = scanline->data + = (char *) shmat(scanline_shm.shmid, 0, 0); + scanline_shm.readOnly = False; + + XShmAttach(dpy, &scanline_shm); + + /* + * the fullscreen (e.g. 1280x1024/fs_factor) shared memory area image: + * (we cut down the size of the shm area to try avoid and shm segment + * limits, e.g. the default 1MB on Solaris) + */ + set_fs_factor(1024 * 1024); + if (! fs_factor) { + printf("warning: fullscreen updates are disabled.\n"); + 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); + + 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 + * to libvncserver rather than sending each tile separately. + */ +void create_tile_hint(int x, int y, int th, hint_t *hint) { + int w = dpy_x - x; + int h = dpy_y - y; + + if (w > tile_x) { + w = tile_x; + } + if (h > th) { + h = th; + } + + hint->x = x; + hint->y = y; + hint->w = w; + hint->h = h; +} + +void extend_tile_hint(int x, int y, int th, hint_t *hint) { + int w = dpy_x - x; + int h = dpy_y - y; + + if (w > tile_x) { + w = tile_x; + } + if (h > th) { + h = th; + } + + if (hint->x > x) { /* extend to the left */ + hint->w += hint->x - x; + hint->x = x; + } + if (hint->y > y) { /* extend upward */ + hint->h += hint->y - y; + hint->y = y; + } + + if (hint->x + hint->w < x + w) { /* extend to the right */ + hint->w = x + w - hint->x; + } + if (hint->y + hint->h < y + h) { /* extend downward */ + hint->h = y + h - hint->y; + } +} + +void save_hint(hint_t hint, int loc) { + /* copy it to the global array: */ + + hint_list[loc].x = hint.x; + hint_list[loc].y = hint.y; + hint_list[loc].w = hint.w; + hint_list[loc].h = hint.h; +} + + +/* + * Glue together horizontal "runs" of adjacent changed tiles into one big + * rectangle change "hint" to be passed to the vnc machinery. + */ +void hint_updates() { + hint_t hint; + int x, y, i, n, ty, th; + int hint_count = 0, in_run = 0; + + for (y=0; y < ntiles_y; y++) { + for (x=0; x < ntiles_x; x++) { + n = x + y * ntiles_x; + + if (tile_has_diff[n]) { + ty = tile_region[n].first_line; + th = tile_region[n].last_line - ty + 1; + if (! in_run) { + create_tile_hint( x * tile_x, + y * tile_y + ty, th, &hint); + in_run = 1; + } else { + extend_tile_hint( x * tile_x, + y * tile_y + ty, th, &hint); + } + } else { + if (in_run) { + /* end of a row run of altered tiles: */ + save_hint(hint, hint_count++); + in_run = 0; + } + } + } + if (in_run) { /* save the last row run */ + save_hint(hint, hint_count++); + in_run = 0; + } + } + + for (i=0; i < hint_count; i++) { + /* pass update info to vnc: */ + mark_hint(hint_list[i]); + } +} + +/* + * Notifies libvncserver of a changed hint rectangle. + */ +void mark_hint(hint_t hint) { + int x = hint.x; + int y = hint.y; + int w = hint.w; + int h = hint.h; + + rfbMarkRectAsModified(screen, x, y, x + w, y + h); +} + +/* + * Notifies libvncserver of a changed tile rectangle. + */ +void mark_tile(int x, int y, int height) { + int w = dpy_x - x; + int h = dpy_y - y; + + if (w > tile_x) { + w = tile_x; + } + + /* height is the height of the changed portion of the tile */ + if (h > height) { + h = height; + } + + rfbMarkRectAsModified(screen, x, y, x + w, y + h); +} + +/* + * Simply send each modified tile separately to the vnc machinery: + * (i.e. no hints) + */ +void tile_updates() { + int x, y, n, ty, th; + + for (y=0; y < ntiles_y; y++) { + for (x=0; x < ntiles_x; x++) { + n = x + y * ntiles_x; + + if (tile_has_diff[n]) { + ty = tile_region[n].first_line; + th = tile_region[n].last_line - ty + 1; + + mark_tile(x * tile_x, y * tile_y + ty, th); + } + } + } +} + +/* + * copy_tile() is called on a tile with a known change (from a scanline + * 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 + * 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; + + char *src, *dst, *s_src, *s_dst, *m_src, *m_dst; + char *h_src, *h_dst; + + x = tx * tile_x; + y = ty * tile_y; + + size_x = dpy_x - x; + if ( size_x > tile_x ) { + size_x = tile_x; + } + size_y = dpy_y - y; + if ( size_y > tile_y ) { + size_y = tile_y; + } + n = tx + ty * ntiles_x; /* number of the tile */ + + X_LOCK + if ( size_x == tile_x && size_y == tile_y ) { + /* general case: */ + XShmGetImage(dpy, window, tile, x, y, AllPlanes); + } else { + /* + * near bottom or 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, 0, 0); + } + X_UNLOCK + + src = (unsigned char*) tile->data; + dst = screen->frameBuffer + y * bytes_per_line + x * pixelsize; + + s_src = src; + s_dst = dst; + + first_line = -1; + + /* find the first line with difference: */ + for (line = 0; line < size_y; line++) { + if ( memcmp(s_dst, s_src, size_x * pixelsize) ) { + first_line = line; + break; + } + s_src += tile->bytes_per_line; + s_dst += bytes_per_line; + } + + tile_tried[n] = 1; + if (first_line == -1) { + /* tile has no difference, note it and get out: */ + tile_has_diff[n] = 0; + return; + } else { + /* + * make sure it is recorded (e.g. sometimes we guess tiles + * and they came in with tile_has_diff 0) + */ + tile_has_diff[n] = 1; + } + + m_src = src + (tile->bytes_per_line * size_y); + m_dst = dst + (bytes_per_line * size_y); + last_line = first_line; + + /* find the last line with difference: */ + for (line = size_y - 1; line > first_line; line--) { + m_src -= tile->bytes_per_line; + m_dst -= bytes_per_line; + if ( memcmp(m_dst, m_src, size_x * pixelsize) ) { + last_line = line; + break; + } + } + + /* look for differences on left and right hand edges: */ + dx = (size_x - tile_fuzz) * pixelsize; + dw = tile_fuzz * pixelsize; + + h_src = src; + h_dst = dst; + for (line = 0; line < size_y; line++) { + if (! l_diff && memcmp(h_dst, h_src, dw) ) { + l_diff = 1; + } + if (! r_diff && memcmp(h_dst + dx, h_src + dx, dw) ) { + r_diff = 1; + } + if (l_diff && r_diff) { + break; + } + h_src += tile->bytes_per_line; + h_dst += bytes_per_line; + } + + /* now copy the difference to the vnc 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; + } + + /* 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; + tile_region[n].left_diff = l_diff; + tile_region[n].right_diff = r_diff; + + tile_region[n].top_diff = 0; + tile_region[n].bot_diff = 0; + if ( first_line < tile_fuzz ) { + tile_region[n].top_diff = 1; + } + if ( last_line > (size_y - 1) - tile_fuzz ) { + tile_region[n].bot_diff = 1; + } +} + +/* + * The copy_tile() call in the loop below copies the changed tile into + * the vnc framebuffer. Note that copy_tile() sets the tile_region + * struct to have info about the y-range of the changed region and also + * whether the tile edges contain diffs (within distance tile_fuzz). + * + * We use this tile_region info to try to guess if the downward and right + * tiles will have diffs. These tiles will be checked later in the loop + * (since y+1 > y and x+1 > x). + * + * See copy_tiles_backward_pass() for analogous checking upward and + * left tiles. + */ +void copy_all_tiles() { + int x, y, n, m; + + for (y=0; y < ntiles_y; y++) { + for (x=0; x < ntiles_x; x++) { + n = x + y * ntiles_x; + + if (tile_has_diff[n]) { + copy_tile(x, y); + } + if (! tile_has_diff[n]) { + /* + * n.b. copy_tile() may have detected + * no change and reset tile_has_diff to 0. + */ + continue; + } + + /* 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; + } + } + /* 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; + } + } + } + } +} + +/* + * 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 x, y, n, m; + + for (y = ntiles_y - 1; y >= 0; y--) { + for (x = ntiles_x - 1; x >= 0; x--) { + n = x + y * ntiles_x; /* number of this tile */ + + if (! tile_has_diff[n]) { + continue; + } + + m = x + (y-1) * ntiles_x; /* neighboring tile upward */ + + if (y >= 1 && ! tile_has_diff[m] && tile_region[n].top_diff) { + if (! tile_tried[m]) { + copy_tile(x, y-1); + } + } + + m = (x-1) + y * ntiles_x; /* neighboring tile to left */ + + if (x >= 1 && ! tile_has_diff[m] && tile_region[n].left_diff) { + if (! tile_tried[m]) { + copy_tile(x-1, y); + } + } + } + } +} + +void gap_try(int x, int y, int *run, int *saw, int along_x) { + int n, m, i, xt, yt; + + n = x + y * ntiles_x; + + if (! tile_has_diff[n]) { + if (*saw) { + (*run)++; /* extend the gap run. */ + } + return; + } + if (! *saw || *run == 0 || *run > gaps_fill) { + *run = 0; /* unacceptable run. */ + *saw = 1; + return; + } + + for (i=1; i <= *run; i++) { /* iterate thru the run. */ + if (along_x) { + xt = x - i; + yt = y; + } else { + xt = x; + yt = y - i; + } + + m = xt + yt * ntiles_x; + if (tile_tried[m]) { /* do not repeat tiles */ + continue; + } + + copy_tile(xt, yt); + } + *run = 0; + *saw = 1; +} + +/* + * Look for small gaps of unchanged tiles that may actually contain changes. + * E.g. when paging up and down in a web broswer or terminal there can + * be a distracting delayed filling in of such gaps. gaps_fill is the + * tweak parameter that sets the width of the gaps that are checked. + * + * btw, grow_islands() is actually pretty successful at doing this too. + */ +void fill_tile_gaps() { + int x, y, run, saw; + + /* horizontal: */ + for (y=0; y < ntiles_y; y++) { + run = 0; + saw = 0; + for (x=0; x < ntiles_x; x++) { + gap_try(x, y, &run, &saw, 1); + } + } + + /* vertical: */ + for (x=0; x < ntiles_x; x++) { + run = 0; + saw = 0; + for (y=0; y < ntiles_y; y++) { + gap_try(x, y, &run, &saw, 0); + } + } +} + +void island_try(int x, int y, int u, int v, int *run) { + int n, m; + + n = x + y * ntiles_x; + m = u + v * ntiles_x; + + if (tile_has_diff[n]) { + (*run)++; + } else { + *run = 0; + } + + if (tile_has_diff[n] && ! tile_has_diff[m]) { + /* found discontinuity */ + + if (tile_tried[m]) { + return; + } else if (*run < grow_fill) { + return; + } + + copy_tile(u, v); + } +} + +/* + * Scan looking for discontinuities in tile_has_diff[]. Try to extend + * the boundary of the discontinuity (i.e. make the island larger). + * Vertical scans are skipped since they do not seem to yield much... + */ +void grow_islands() { + int x, y, run; + + /* + * n.b. the way we scan here should keep an extension going, + * and so also fill in gaps effectively... + */ + + /* left to right: */ + for (y=0; y < ntiles_y; y++) { + run = 0; + for (x=0; x <= ntiles_x - 2; x++) { + island_try(x, y, x+1, y, &run); + } + } + /* right to left: */ + for (y=0; y < ntiles_y; y++) { + run = 0; + for (x = ntiles_x - 1; x >= 1; x--) { + island_try(x, y, x-1, y, &run); + } + } +} + +/* + * copy the whole X screen to the vnc 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. + */ +void copy_screen() { + int pixelsize = bpp >> 3; + char *vnc_fb; + int i, y, block_size, xi; + + block_size = (dpy_x * (dpy_y/fs_factor) * pixelsize); + + vnc_fb = screen->frameBuffer; + y = 0; + + for (i=0; i < fs_factor; i++) { + xi = XShmGetImage(dpy, window, fullscreen, 0, y, AllPlanes); + memcpy(vnc_fb, fullscreen->data, (size_t) block_size); + + y += dpy_y / fs_factor; + vnc_fb += block_size; + } + + rfbMarkRectAsModified(screen, 0, 0, dpy_x, dpy_y); +} + +/* + * Loop over 1-pixel tall horizontal scanlines looking for changes. + * Record the changes in tile_has_diff[]. Scanlines in the loop are + * equally spaced along y by NSCAN pixels, but have a slightly random + * starting offset ystart ( < NSCAN ) from scanlines[]. + */ +int scan_display(int ystart, int rescan) { + int x, y, w, n; + int tile_count = 0; + int pixelsize = bpp >> 3; + char *src, *dst; + + y = ystart; + + while (y < dpy_y) { + + /* grab the horizontal scanline from the display: */ + X_LOCK + XShmGetImage(dpy, window, scanline, 0, y, AllPlanes); + X_UNLOCK + + x = 0; + while (x < dpy_x) { + n = (x/tile_x) + (y/tile_y) * ntiles_x; + + if (rescan && tile_has_diff[n]) { + tile_count++; + x += NSCAN; + continue; + } + + /* set ptrs to correspond to the x offset: */ + src = (unsigned char*) scanline->data + x * pixelsize; + dst = screen->frameBuffer + y * bytes_per_line + + x * pixelsize; + + /* compute the width of data to be compared: */ + if ( x + NSCAN > dpy_x ) { + w = dpy_x - x; + } else { + w = NSCAN; + } + + if (memcmp(dst, src, w * pixelsize) ) { + /* found a difference, record it: */ + tile_has_diff[n] = 1; + tile_count++; + } + x += NSCAN; + } + y += NSCAN; + } + return tile_count; +} + +/* + * toplevel for the scanning, rescanning, and applying the heuristics. + */ +void scan_for_updates() { + int i, tile_count; + double frac1 = 0.1; /* tweak parameter to try a 2nd scan_display() */ + + for (i=0; i < ntiles; i++) { + tile_has_diff[i] = 0; + tile_tried[i] = 0; + } + + /* + * n.b. this program has only been tested so far with + * tile_x = tile_y = NSCAN = 32! + */ + + count++; + count %= NSCAN; + + /* scan with the initial y to the jitter value from scanlines: */ + tile_count = scan_display( scanlines[count], 0 ); + + if (fs_factor && frac1 >= fs_frac) { + /* make frac1 < fs_frac if fullscreen updates are enabled */ + frac1 = fs_frac/2.0; + } + + if ( tile_count > frac1 * ntiles) { + /* + * many tiles have changed, so try a rescan (since it should + * be short compared to the many upcoming copy_tile() calls) + */ + + /* this check is done to skip the extra scan_display() call */ + if (! fs_factor || tile_count <= fs_frac * ntiles) { + int cp; + + /* choose a different y shift for the 2nd scan: */ + cp = (NSCAN - count) % NSCAN; + + tile_count = scan_display( scanlines[cp], 1 ); + } + + /* + * 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. + * + * 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. + */ + if (fs_factor && tile_count > fs_frac * ntiles) { + copy_screen(); + return; + } + } + + /* copy all tiles with differences from display to vnc framebuffer: */ + 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(); + + if (grow_fill) { + grow_islands(); + } + + if (gaps_fill) { + fill_tile_gaps(); + } + + if (use_hints) { + hint_updates(); /* use krfb/x0rfbserver hints algorithm */ + } else { + tile_updates(); /* send each tile change individually */ + } +} + +void watch_loop(void) { + int cnt = 0; + +#if !defined(HAVE_PTHREADS) + use_threads = 0; +#endif + + if (use_threads) { + rfbRunEventLoop(screen, -1, TRUE); + } + + while (1) { + got_user_input = 0; + + if (! use_threads) { + rfbProcessEvents(screen, -1); + + if (got_user_input && cnt % 10 != 0) { + /* every 10-th drops thru to code below... */ + XFlush(dpy); + continue; + } + } + + if (shut_down) { + clean_up_exit(); + } + + if (! screen->rfbClientHead) { /* waiting for a client */ + usleep(200 * 1000); + continue; + } + + rfbUndrawCursor(screen); + scan_for_updates(); + + usleep(waitms * 1000); + + cnt++; + } +} + +void print_help() { + char help[] = " +x0vnc options: + +-defer time time in ms to wait for updates before sending to + client [rfbDeferUpdateTime] (default %d) +-wait time time in ms to pause between screen polls. used + to cut down on load (default %d) + +-gaps n heuristic to fill in gaps in rows or cols of n or less + tiles. used to improve text paging (default %d). +-grow n heuristic to grow islands of changed tiles n or wider + by checking the tile near the boundary (default %d). +-fs f if the fraction of changed tiles in a poll is greater + than f, the whole screen is updated (default %.2f) +-fuzz n tolerance in pixels to mark a tiles edges as changed. + (default %d). +-hints use krfb/x0rfbserver hints (glue changed adjacent + horizontal tiles into one big rectangle) (default %s). +-nohints do not use hints; send each tile separately. + +-threads use threaded algorithm [rfbRunEventLoop] if compiled + with threads (default %s). +-nothreads do not use [rfbRunEventLoop]. +-viewonly clients can only watch (default %s). +-shared VNC display is shared (default %s) + +These options are passed to libvncserver: + +"; + fprintf(stderr, help, defer_update, waitms, gaps_fill, grow_fill, + fs_frac, tile_fuzz, + use_hints ? "on":"off", use_threads ? "on":"off", + view_only ? "on":"off", shared ? "on":"off"); + rfbUsage(); + exit(1); +} + +int main(int argc, char** argv) { + + XImage *fb; + int i, scr, ev, er, maj, min; + char *use_dpy = NULL; + + + /* 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], "-defer")) { + defer_update = atoi(argv[++i]); + } else if (!strcmp(argv[i], "-wait")) { + waitms = atoi(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], "-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 { + /* otherwise copy it for use below. */ + printf("passing arg to libvncserver: %s\n", argv[i]); + if (argc2 < 100) { + argv2[argc2++] = argv[i]; + } + } + } + if (tile_fuzz < 1) { + tile_fuzz = 1; + } + if (waitms < 0) { + waitms = 0; + } + printf("defer: %d\n", defer_update); + printf("waitms: %d\n", waitms); + printf("tile_fuzz: %d\n", tile_fuzz); + printf("gaps_fill: %d\n", gaps_fill); + printf("grow_fill: %d\n", grow_fill); + printf("fs_frac: %.2f\n", fs_frac); + printf("use_hints: %d\n", use_hints); + printf("viewonly: %d\n", view_only); + printf("shared: %d\n", shared); + + if (use_dpy) { + dpy = XOpenDisplay(use_dpy); + } else if ( (use_dpy = getenv("DISPLAY")) ) { + dpy = XOpenDisplay(use_dpy); + } else { + dpy = XOpenDisplay(""); + } + if (! dpy) { + printf("XOpenDisplay failed (%s)\n", use_dpy); + exit(1); + } else if (use_dpy) { + printf("Using display %s\n", use_dpy); + } else { + printf("Using default display.\n"); + } + + if (! XTestQueryExtension(dpy, &ev, &er, &maj, &min)) { + printf("Display does not support the XTest extension.\n"); + exit(1); + } + if (! XShmQueryExtension(dpy)) { + printf("Display does not support XShm extension" + " (must be local).\n"); + exit(1); + } + + /* + * Window managers will often grab the display during resize, etc. + * To avoid deadlock (our user resize input is not processed) + * we tell the server to process our requests during all grabs: + */ + XTestGrabControl(dpy, True); + + scr = DefaultScreen(dpy); + window = RootWindow(dpy, scr); + + dpy_x = DisplayWidth(dpy, scr); + dpy_y = DisplayHeight(dpy, scr); + + fb = XGetImage(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, ZPixmap); + printf("Read initial data from display into framebuffer.\n"); + + if (fb->bits_per_pixel == 24) { + printf("warning: 24 bpp may have poor performance.\n"); + } + + /* + * n.b. we do not have to X_LOCK X11 calls until watch_loop() + * is called since we are single-threaded until then. + */ + + initialize_screen(&argc2, argv2, fb); + + initialize_tiles(); + + initialize_shm(); + + printf("screen setup finished.\n"); + + watch_loop(); + + return(0); +} diff --git a/zippy.c b/contrib/zippy.c similarity index 100% rename from zippy.c rename to contrib/zippy.c diff --git a/httpd.c b/httpd.c index 5207c72..328f416 100644 --- a/httpd.c +++ b/httpd.c @@ -266,7 +266,7 @@ httpProcessInput(rfbScreenInfoPtr rfbScreen) httpCloseSock(rfbScreen); return; } - // proxy connection + /* proxy connection */ rfbLog("httpd: client asked for CONNECT\n"); WriteExact(&cl,PROXY_OK_STR,strlen(PROXY_OK_STR)); rfbNewClientConnection(rfbScreen,rfbScreen->httpSock); @@ -274,7 +274,7 @@ httpProcessInput(rfbScreenInfoPtr rfbScreen) return; } if (!strncmp(buf, "GET ",4) && !strncmp(strchr(buf,'/'),"/proxied.connection HTTP/1.", 27)) { - // proxy connection + /* proxy connection */ rfbLog("httpd: client asked for /proxied.connection\n"); WriteExact(&cl,PROXY_OK_STR,strlen(PROXY_OK_STR)); rfbNewClientConnection(rfbScreen,rfbScreen->httpSock); diff --git a/main.c b/main.c index ab4c149..b975481 100644 --- a/main.c +++ b/main.c @@ -128,7 +128,7 @@ void rfbScheduleCopyRegion(rfbScreenInfoPtr rfbScreen,sraRegionPtr copyRegion,in sraRgnDestroy(modifiedRegionBackup); #if 0 -//TODO: is this needed? Or does it mess up deferring? + /* TODO: is this needed? Or does it mess up deferring? */ /* while(!sraRgnEmpty(cl->copyRegion)) */ { #ifdef HAVE_PTHREADS if(!cl->screen->backgroundLoop) diff --git a/rfb.h b/rfb.h index 856dd9b..37aac35 100644 --- a/rfb.h +++ b/rfb.h @@ -164,9 +164,17 @@ typedef int socklen_t; the library and your application (at least the parts including rfb.h) with the same support for pthreads. */ #ifdef HAVE_PTHREADS -#define rfbInitServer rfbInitServerWithPthreads +#ifdef HAVE_ZRLE +#define rfbInitServer rfbInitServerWithPthreadsAndZRLE +#else +#define rfbInitServer rfbInitServerWithPthreadsButWithoutZRLE +#endif +#else +#ifdef HAVE_ZRLE +#define rfbInitServer rfbInitServerWithoutPthreadsButWithZRLE #else -#define rfbInitServer rfbInitServerWithoutPthreads +#define rfbInitServer rfbInitServerWithoutPthreadsAndZRLE +#endif #endif #define MAX_ENCODINGS 10 @@ -409,9 +417,6 @@ typedef struct _rfbClientRec { Bool useCopyRect; int preferredEncoding; int correMaxWidth, correMaxHeight; -#ifdef HAVE_ZRLE - void* zrleData; -#endif /* The following member is only used during VNC authentication */ CARD8 authChallenge[CHALLENGESIZE]; @@ -538,6 +543,10 @@ typedef struct _rfbClientRec { COND(updateCond); #endif +#ifdef HAVE_ZRLE + void* zrleData; +#endif + } rfbClientRec, *rfbClientPtr; /* @@ -725,9 +734,10 @@ extern void rfbSetCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr c,Bool freeOld) extern void defaultPtrAddEvent(int buttonMask,int x,int y,rfbClientPtr cl); /* zrle.c */ - +#ifdef HAVE_ZRLE extern Bool rfbSendRectEncodingZRLE(rfbClientPtr cl, int x, int y, int w,int h); extern void FreeZrleData(rfbClientPtr cl); +#endif /* stats.c */ diff --git a/x11vnc.c b/x11vnc.c deleted file mode 100644 index ee21d03..0000000 --- a/x11vnc.c +++ /dev/null @@ -1,583 +0,0 @@ -/* This file (x11vnc.c) is part of LibVNCServer. - It is a small clone of x0rfbserver by HexoNet, demonstrating the - capabilities of LibVNCServer. -*/ - -#include -#include -#include -#include -#ifndef NO_SHM -#include -#include -#endif -#define KEYSYM_H -#undef Bool -#define KeySym RFBKeySym -#include "rfb.h" - -Display *dpy = 0; -int window; -int c=0,blockLength = 32; -int tileX=0,tileY=0,tileWidth=32,tileHeight=32*2,dontTile=TRUE; -Bool gotInput = FALSE; -Bool viewOnly = FALSE; -Bool sharedMode = FALSE; - -Bool disconnectAfterFirstClient = TRUE; - -/* keyboard handling */ -#define KBDDEBUG - -char modifiers[0x100]; -KeyCode keycodes[0x100],leftShiftCode,rightShiftCode,altGrCode; - -void init_keycodes() -{ - KeySym key,*keymap; - int i,j,minkey,maxkey,syms_per_keycode; - - memset(modifiers,-1,sizeof(modifiers)); - - XDisplayKeycodes(dpy,&minkey,&maxkey); - keymap=XGetKeyboardMapping(dpy,minkey,(maxkey - minkey + 1),&syms_per_keycode); - -#ifdef KBDDEBUG - fprintf(stderr,"minkey=%d, maxkey=%d, syms_per_keycode=%d\n", - minkey,maxkey,syms_per_keycode); -#endif - for (i = minkey; i <= maxkey; i++) - for(j=0;j=' ' && key<0x100 && i==XKeysymToKeycode(dpy,key)) { - keycodes[key]=i; - modifiers[key]=j; -#ifdef KBDDEBUG - fprintf(stderr,"key 0x%lx (%c): keycode=0x%x, modifier=%d\n", - key,(char)key,i,j); -#endif - } - } - - leftShiftCode=XKeysymToKeycode(dpy,XK_Shift_L); - rightShiftCode=XKeysymToKeycode(dpy,XK_Shift_R); - altGrCode=XKeysymToKeycode(dpy,XK_Mode_switch); - -#ifdef KBDDEBUG - fprintf(stderr,"leftShift=0x%x, rightShift=0x%x, altGr=0x%x\n", - leftShiftCode,rightShiftCode,altGrCode); -#endif - - XFree ((char *) keymap); -} - -static Bool shutDownServer=0; - -/* the hooks */ - -void clientGone(rfbClientPtr cl) -{ - shutDownServer=-1; -} - -enum rfbNewClientAction newClient(rfbClientPtr cl) -{ - if(disconnectAfterFirstClient) - cl->clientGoneHook = clientGone; - if(viewOnly) - cl->clientData = (void*)-1; - else - cl->clientData = (void*)0; - return(RFB_CLIENT_ACCEPT); -} - -#define LEFTSHIFT 1 -#define RIGHTSHIFT 2 -#define ALTGR 4 -char ModifierState = 0; - -/* this function adjusts the modifiers according to mod (as from modifiers) and ModifierState */ - -void tweakModifiers(signed char mod,Bool down) -{ - Bool isShift=ModifierState&(LEFTSHIFT|RIGHTSHIFT); -#ifdef KBDDEBUG - fprintf(stderr,"tweakModifiers: 0x%x %s\n", - mod,down?"down":"up"); -#endif - if(mod<0) return; - if(isShift && mod!=1) { - if(ModifierState&LEFTSHIFT) - XTestFakeKeyEvent(dpy,leftShiftCode,!down,CurrentTime); - if(ModifierState&RIGHTSHIFT) - XTestFakeKeyEvent(dpy,rightShiftCode,!down,CurrentTime); - } - if(!isShift && mod==1) - XTestFakeKeyEvent(dpy,leftShiftCode,down,CurrentTime); - - if(ModifierState&ALTGR && mod!=2) - XTestFakeKeyEvent(dpy,altGrCode,!down,CurrentTime); - if(!(ModifierState&ALTGR) && mod==2) - XTestFakeKeyEvent(dpy,altGrCode,down,CurrentTime); -} - -void keyboard(Bool down,KeySym keySym,rfbClientPtr cl) -{ - if(((int)cl->clientData)==-1) return; /* viewOnly */ - -#define ADJUSTMOD(sym,state) \ - if(keySym==sym) { if(down) ModifierState|=state; else ModifierState&=~state; } - - ADJUSTMOD(XK_Shift_L,LEFTSHIFT) - ADJUSTMOD(XK_Shift_R,RIGHTSHIFT) - ADJUSTMOD(XK_Mode_switch,ALTGR) - -#ifdef KBDDEBUG - fprintf(stderr,"keyboard: down=%s, keySym=0x%lx (%s), ModState=0x%x\n", - down?"down":"up",keySym,XKeysymToString(keySym),ModifierState); -#endif - - if(keySym>=' ' && keySym<0x100) { - KeyCode k; - if(down) - tweakModifiers(modifiers[keySym],True); - //tweakModifiers(modifiers[keySym],down); - //k = XKeysymToKeycode( dpy,keySym ); - k = keycodes[keySym]; - if(k!=NoSymbol) { - XTestFakeKeyEvent(dpy,k,down,CurrentTime); - gotInput = TRUE; - } - if(down) - tweakModifiers(modifiers[keySym],False); - gotInput = TRUE; - } else { - KeyCode k = XKeysymToKeycode( dpy,keySym ); - if(k!=NoSymbol) { - XTestFakeKeyEvent(dpy,k,down,CurrentTime); - gotInput = TRUE; - } - } -} - -int oldButtonMask = 0; - -void mouse(int buttonMask,int x,int y,rfbClientPtr cl) -{ - int i=0; - - if(((int)cl->clientData)==-1) return; /* viewOnly */ - - XTestFakeMotionEvent(dpy,0,x,y,CurrentTime ); - while(i<5) { - if ((oldButtonMask&(1<0) { - static Bool firstTime = TRUE; - if(firstTime) { - firstTime = FALSE; - *i = XShmCreateImage(dpy, - DefaultVisual( dpy, xscreen ), - bpp, - ZPixmap, - NULL, - &shminfo, - width,height); - - if(*i == 0) { - useSHM = FALSE; - getImage(bpp,dpy,xscreen,i,x,y,width,height); - return; - } - - shminfo.shmid = shmget( IPC_PRIVATE, - (*i)->bytes_per_line * (*i)->height, - IPC_CREAT | 0777 ); - shminfo.shmaddr = (*i)->data = (char *) shmat( shminfo.shmid, 0, 0 ); - shminfo.readOnly = False; - XShmAttach( dpy, &shminfo ); - } - - if(x==0 && y==0 && width==DisplayWidth(dpy,xscreen) && height==DisplayHeight(dpy,xscreen)) - XShmGetImage(dpy,window,*i,0,0,AllPlanes); - else - XGetSubImage(dpy,window,x,y,width,height,AllPlanes,ZPixmap,*i,0,0); - } else { - *i = XGetImage(dpy,window,x,y,width,height,AllPlanes,ZPixmap ); - } -} - -void checkForImageUpdates(rfbScreenInfoPtr s,char *b,int rowstride,int x,int y,int width,int height) -{ - Bool changed; - int i,j,k,l1,l2,x1,y1; - int bpp=s->bitsPerPixel/8; - - for(j=0;jheight) y1=height; - x1=i+blockLength; if(x1>width) x1=width; - y1*=rowstride; - x1*=bpp; - changed=FALSE; - for(l1=j*rowstride,l2=(j+y)*s->paddedWidthInBytes+x*bpp;l1paddedWidthInBytes) - for(k=i*bpp;kframeBuffer[l2+k]!=b[l1+k]) { - // fprintf(stderr,"changed: %d, %d\n",k,l); - changed=TRUE; - goto changed_p; - } - if(changed) { - changed_p: - for(l1+=i*bpp,l2+=i*bpp;l1paddedWidthInBytes) - memcpy(/*b+l,*/s->frameBuffer+l2,b+l1,x1-i*bpp); - rfbMarkRectAsModified(s,x+i,y+j,x+i+blockLength,y+j+blockLength); - } - } -} - -int probeX=0,probeY=0; - -void probeScreen(rfbScreenInfoPtr s,int xscreen) -{ - int i,j,/*pixel,i1,*/j1, - bpp=s->rfbServerFormat.bitsPerPixel/8,/*mask=(1<paddedWidthInBytes; - XImage* im; - //fprintf(stderr,"/%d,%d",probeX,probeY); -#if 0 - probeX++; - if(probeX>=tileWidth) { - probeX=0; - probeY++; - if(probeY>=tileHeight) - probeY=0; - } -#else - probeX=(rand()%tileWidth); - probeY=(rand()%tileHeight); -#endif - - for(j=probeY;jheight;j+=tileHeight) - for(i=0/*probeX*/;iwidth;i+=tileWidth) { - im=XGetImage(dpy,window,i,j,tileWidth/*1*/,1,AllPlanes,ZPixmap); - /* for(i1=0;i1data[i1]==(s->frameBuffer+i*bpp+j*rstride)[i1];i1++); - if(i1data,s->frameBuffer+i*bpp+j*rstride,tileWidth*bpp)) { - /* do update */ - int x=i/*-probeX*/,w=(x+tileWidth>s->width)?s->width-x:tileWidth, - y=j-probeY,h=(y+tileHeight>s->height)?s->height-y:tileHeight; - - XDestroyImage(im); - //getImage(bpp,dpy,xscreen,&im,x,y,w,h); - //fprintf(stderr,"GetImage(%d,%d,%d,%d)",x,y,w,h); - im = XGetImage(dpy,window,x,y,w,h,AllPlanes,ZPixmap ); - for(j1=0;j1frameBuffer+x*bpp+(y+j1)*rstride, - im->data+j1*im->bytes_per_line,bpp*w); - //checkForImageUpdates(s,im->data,rstride,x,y,w,h); - //if(0 && !useSHM) - XDestroyImage(im); - //memcpy(s->frameBuffer+i*bpp+j*rstride,&pixel,bpp); - rfbMarkRectAsModified(s,x,y,x+w,y+h); - //fprintf(stderr,"%d:%d:%x\n",i,j,pixel); - //fprintf(stderr,"*"); - } else - XDestroyImage(im); - } -} - -#define LOCAL_CONTROL - -#ifdef LOCAL_CONTROL -#include "1instance.c" -#endif - -/* the main program */ - -int main(int argc,char** argv) -{ - //Screen *sc; - //Colormap cm; - XImage *framebufferImage; - char *backupImage; - int xscreen,i; - rfbScreenInfoPtr screen; - int maxMsecsToConnect = 5000; /* a maximum of 5 seconds to connect */ - int updateCounter; /* about every 50 ms a screen update should be made. */ - -#ifdef LOCAL_CONTROL - char message[1024]; - single_instance_struct single_instance = { "/tmp/x11vnc_control" }; - - open_control_file(&single_instance); -#endif - - for(i=argc-1;i>0;i--) -#ifdef LOCAL_CONTROL - if(i1022) - argv[i+1][1022]=0; - sprintf(message,"t%s",argv[i+1]); - send_message(&single_instance,message); - exit(0); - } else if(!strcmp(argv[i],"-listclients")) { - fprintf(stderr,"list clients\n"); - send_message(&single_instance,"l"); - exit(0); - } else -#ifdef BACKCHANNEL - if(i1022) - argv[i+1][1022]=0; - sprintf(message,"b%s",argv[i+1]); - send_message(&single_instance,message); - exit(0); - } else -#endif -#endif - if(iwidth, - framebufferImage->height, - framebufferImage->bits_per_pixel, - 8, - framebufferImage->bits_per_pixel/8); - - screen->paddedWidthInBytes = framebufferImage->bytes_per_line; - - screen->rfbServerFormat.bitsPerPixel = framebufferImage->bits_per_pixel; - screen->rfbServerFormat.depth = framebufferImage->depth; - //rfbEndianTest = framebufferImage->bitmap_bit_order != MSBFirst; - screen->rfbServerFormat.trueColour = TRUE; - - if ( screen->rfbServerFormat.bitsPerPixel == 8 ) { - if(CellsOfScreen(ScreenOfDisplay(dpy,xscreen))) { - 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,xscreen),color,256); - screen->colourMap.data.shorts = (unsigned short*)malloc(3*sizeof(short)*screen->colourMap.count); - for(i=0;icolourMap.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 { - screen->rfbServerFormat.redShift = 0; - screen->rfbServerFormat.greenShift = 2; - screen->rfbServerFormat.blueShift = 5; - screen->rfbServerFormat.redMax = 3; - screen->rfbServerFormat.greenMax = 7; - screen->rfbServerFormat.blueMax = 3; - } - } else { - screen->rfbServerFormat.redShift = 0; - if ( framebufferImage->red_mask ) - while ( ! ( framebufferImage->red_mask & (1 << screen->rfbServerFormat.redShift) ) ) - screen->rfbServerFormat.redShift++; - screen->rfbServerFormat.greenShift = 0; - if ( framebufferImage->green_mask ) - while ( ! ( framebufferImage->green_mask & (1 << screen->rfbServerFormat.greenShift) ) ) - screen->rfbServerFormat.greenShift++; - screen->rfbServerFormat.blueShift = 0; - if ( framebufferImage->blue_mask ) - while ( ! ( framebufferImage->blue_mask & (1 << screen->rfbServerFormat.blueShift) ) ) - screen->rfbServerFormat.blueShift++; - screen->rfbServerFormat.redMax = framebufferImage->red_mask >> screen->rfbServerFormat.redShift; - screen->rfbServerFormat.greenMax = framebufferImage->green_mask >> screen->rfbServerFormat.greenShift; - screen->rfbServerFormat.blueMax = framebufferImage->blue_mask >> screen->rfbServerFormat.blueShift; - } - - backupImage = malloc(screen->height*screen->paddedWidthInBytes); - memcpy(backupImage,framebufferImage->data,screen->height*screen->paddedWidthInBytes); - - screen->frameBuffer = backupImage; - screen->cursor = 0; - screen->newClientHook = newClient; - - screen->kbdAddEvent = keyboard; - screen->ptrAddEvent = mouse; - - if(sharedMode) { - screen->rfbAlwaysShared = TRUE; - } - - screen->rfbDeferUpdateTime = 1; - updateCounter /= screen->rfbDeferUpdateTime; - - rfbInitServer(screen); - - c=0; - while(1) { - if(screen->rfbClientHead) - maxMsecsToConnect = 1<<16; - else { - maxMsecsToConnect -= screen->rfbDeferUpdateTime; - if(maxMsecsToConnect<0) { - fprintf(stderr,"Maximum time to connect reached. Exiting.\n"); - XTestDiscard(dpy); - exit(2); - } - } - -#ifdef LOCAL_CONTROL - if(get_next_message(message,1024,&single_instance,50)) { - if(message[0]=='l' && message[1]==0) { - rfbClientPtr cl; - int i; - for(i=0,cl=screen->rfbClientHead;cl;cl=cl->next,i++) - fprintf(stderr,"%02d: %s\n",i,cl->host); - } else if(message[0]=='t') { - rfbClientPtr cl; - for(cl=screen->rfbClientHead;cl;cl=cl->next) - if(!strcmp(message+1,cl->host)) { - cl->clientData=(void*)((cl->clientData==0)?-1:0); - break; - } - } -#ifdef BACKCHANNEL - else if(message[0]=='b') - rfbSendBackChannel(screen,message+1,strlen(message+1)); -#endif - } -#endif - - rfbProcessEvents(screen,-1); - if(shutDownServer) { - free(backupImage); - rfbScreenCleanup(screen); - XFree(dpy); -#ifndef NO_SHM - XShmDetach(dpy,&shminfo); -#endif - exit(0); - } - - if(dontTile) { - if(gotInput) { - gotInput = FALSE; - c=updateCounter; - } else if(screen->rfbClientHead && c++>updateCounter) { - c=0; - //fprintf(stderr,"*"); - if(!useSHM) - framebufferImage->f.destroy_image(framebufferImage); - if(dontTile) { - getImage(screen->rfbServerFormat.bitsPerPixel,dpy,xscreen,&framebufferImage,0,0,screen->width,screen->height); - checkForImageUpdates(screen,framebufferImage->data,framebufferImage->bytes_per_line, - 0,0,screen->width,screen->height); - } else { - /* old tile code. Eventually to be removed (TODO) */ - char isRightEdge = tileX+tileWidth>=screen->width; - char isLowerEdge = tileY+tileHeight>=screen->height; - getImage(screen->rfbServerFormat.bitsPerPixel,dpy,xscreen,&framebufferImage,tileX,tileY, - isRightEdge?screen->width-tileX:tileWidth, - isLowerEdge?screen->height-tileY:tileHeight); - checkForImageUpdates(screen,framebufferImage->data,framebufferImage->bytes_per_line, - tileX,tileY, - isRightEdge?screen->width-tileX:tileWidth, - isLowerEdge?screen->height-tileY:tileHeight); - if(isRightEdge) { - tileX=0; - if(isLowerEdge) - tileY=0; - else - tileY+=tileHeight; - } else - tileX+=tileWidth; - } - } - } else if(c++>updateCounter) { - c=0; - probeScreen(screen,xscreen); - } - -#ifdef WRITE_SNAPS - { - int i,j,r,g,b; - FILE* f=fopen("test.pnm","wb"); - fprintf(f,"P6\n%d %d\n255\n",screen->width,screen->height); - for(j=0;jheight;j++) - for(i=0;iwidth;i++) { - //r=screen->frameBuffer[j*screen->paddedWidthInBytes+i*2]; - r=framebufferImage->data[j*screen->paddedWidthInBytes+i*2]; - fputc(((r>>screen->rfbServerFormat.redShift)&screen->rfbServerFormat.redMax)*255/screen->rfbServerFormat.redMax,f); - fputc(((r>>screen->rfbServerFormat.greenShift)&screen->rfbServerFormat.greenMax)*255/screen->rfbServerFormat.greenMax,f); - fputc(((r>>screen->rfbServerFormat.blueShift)&screen->rfbServerFormat.blueMax)*255/screen->rfbServerFormat.blueMax,f); - } - fclose(f); - } -#endif - } -#ifndef NO_SHM - //XShmDetach(dpy,framebufferImage); -#endif - - return(0); -}