From 876868553da8f69ed1a368688b6d01a8a7bc1a39 Mon Sep 17 00:00:00 2001 From: dscho Date: Tue, 25 May 2004 09:05:09 +0000 Subject: move the library into libvncserver/, x11vnc into x11vnc/ --- ChangeLog | 4 + Makefile.am | 28 +- auth.c | 104 - cargs.c | 169 - configure.ac | 2 + contrib/ChangeLog | 143 - contrib/Makefile.am | 13 +- contrib/x11vnc.c | 6742 ----------------------------------- corre.c | 352 -- cursor.c | 527 --- cutpaste.c | 38 - d3des.c | 442 --- d3des.h | 56 - draw.c | 61 - examples/Makefile.am | 2 +- examples/regiontest.c | 2 +- font.c | 192 - hextile.c | 346 -- httpd.c | 579 --- libvncclient/rfbproto.c | 4 +- libvncserver/Makefile.am | 42 + libvncserver/auth.c | 104 + libvncserver/cargs.c | 169 + libvncserver/config.h | 240 ++ libvncserver/corre.c | 352 ++ libvncserver/cursor.c | 527 +++ libvncserver/cutpaste.c | 38 + libvncserver/d3des.c | 442 +++ libvncserver/d3des.h | 56 + libvncserver/draw.c | 61 + libvncserver/font.c | 192 + libvncserver/hextile.c | 346 ++ libvncserver/httpd.c | 579 +++ libvncserver/main.c | 851 +++++ libvncserver/rfbconfig.h | 243 ++ libvncserver/rfbregion.c | 862 +++++ libvncserver/rfbserver.c | 1808 ++++++++++ libvncserver/rre.c | 321 ++ libvncserver/selbox.c | 301 ++ libvncserver/sockets.c | 619 ++++ libvncserver/stats.c | 119 + libvncserver/tableinit24.c | 157 + libvncserver/tableinitcmtemplate.c | 84 + libvncserver/tableinittctemplate.c | 142 + libvncserver/tabletrans24template.c | 281 ++ libvncserver/tabletranstemplate.c | 117 + libvncserver/tight.c | 1820 ++++++++++ libvncserver/translate.c | 484 +++ libvncserver/vncauth.c | 185 + libvncserver/zlib.c | 302 ++ libvncserver/zrle.c | 183 + libvncserver/zrleencodetemplate.c | 272 ++ libvncserver/zrleoutstream.c | 276 ++ libvncserver/zrleoutstream.h | 62 + libvncserver/zrlepalettehelper.c | 62 + libvncserver/zrlepalettehelper.h | 46 + libvncserver/zrletypes.h | 30 + main.c | 851 ----- rfbregion.c | 862 ----- rfbserver.c | 1808 ---------- rre.c | 321 -- selbox.c | 301 -- sockets.c | 619 ---- stats.c | 119 - tableinit24.c | 157 - tableinitcmtemplate.c | 84 - tableinittctemplate.c | 142 - tabletrans24template.c | 281 -- tabletranstemplate.c | 117 - test/Makefile.am | 2 +- tight.c | 1820 ---------- translate.c | 484 --- vncauth.c | 185 - vncterm/Makefile.am | 4 +- x11vnc/ChangeLog | 143 + x11vnc/Makefile.am | 16 + x11vnc/x11vnc.c | 6742 +++++++++++++++++++++++++++++++++++ zlib.c | 302 -- zrle.c | 183 - zrleencodetemplate.c | 272 -- zrleoutstream.c | 276 -- zrleoutstream.h | 62 - zrlepalettehelper.c | 62 - zrlepalettehelper.h | 46 - zrletypes.h | 30 - 85 files changed, 19692 insertions(+), 19180 deletions(-) delete mode 100644 auth.c delete mode 100644 cargs.c delete mode 100644 contrib/ChangeLog delete mode 100644 contrib/x11vnc.c delete mode 100644 corre.c delete mode 100644 cursor.c delete mode 100644 cutpaste.c delete mode 100644 d3des.c delete mode 100644 d3des.h delete mode 100644 draw.c delete mode 100644 font.c delete mode 100644 hextile.c delete mode 100644 httpd.c create mode 100644 libvncserver/Makefile.am create mode 100755 libvncserver/auth.c create mode 100644 libvncserver/cargs.c create mode 100644 libvncserver/config.h create mode 100755 libvncserver/corre.c create mode 100644 libvncserver/cursor.c create mode 100755 libvncserver/cutpaste.c create mode 100755 libvncserver/d3des.c create mode 100755 libvncserver/d3des.h create mode 100755 libvncserver/draw.c create mode 100755 libvncserver/font.c create mode 100755 libvncserver/hextile.c create mode 100755 libvncserver/httpd.c create mode 100644 libvncserver/main.c create mode 100644 libvncserver/rfbconfig.h create mode 100755 libvncserver/rfbregion.c create mode 100644 libvncserver/rfbserver.c create mode 100755 libvncserver/rre.c create mode 100755 libvncserver/selbox.c create mode 100755 libvncserver/sockets.c create mode 100755 libvncserver/stats.c create mode 100755 libvncserver/tableinit24.c create mode 100755 libvncserver/tableinitcmtemplate.c create mode 100755 libvncserver/tableinittctemplate.c create mode 100755 libvncserver/tabletrans24template.c create mode 100755 libvncserver/tabletranstemplate.c create mode 100644 libvncserver/tight.c create mode 100755 libvncserver/translate.c create mode 100644 libvncserver/vncauth.c create mode 100644 libvncserver/zlib.c create mode 100644 libvncserver/zrle.c create mode 100644 libvncserver/zrleencodetemplate.c create mode 100644 libvncserver/zrleoutstream.c create mode 100644 libvncserver/zrleoutstream.h create mode 100644 libvncserver/zrlepalettehelper.c create mode 100644 libvncserver/zrlepalettehelper.h create mode 100755 libvncserver/zrletypes.h delete mode 100644 main.c delete mode 100755 rfbregion.c delete mode 100644 rfbserver.c delete mode 100644 rre.c delete mode 100644 selbox.c delete mode 100644 sockets.c delete mode 100644 stats.c delete mode 100644 tableinit24.c delete mode 100644 tableinitcmtemplate.c delete mode 100644 tableinittctemplate.c delete mode 100644 tabletrans24template.c delete mode 100644 tabletranstemplate.c delete mode 100644 tight.c delete mode 100644 translate.c delete mode 100644 vncauth.c create mode 100644 x11vnc/ChangeLog create mode 100644 x11vnc/Makefile.am create mode 100644 x11vnc/x11vnc.c delete mode 100644 zlib.c delete mode 100644 zrle.c delete mode 100644 zrleencodetemplate.c delete mode 100644 zrleoutstream.c delete mode 100644 zrleoutstream.h delete mode 100644 zrlepalettehelper.c delete mode 100644 zrlepalettehelper.h delete mode 100644 zrletypes.h diff --git a/ChangeLog b/ChangeLog index 59fe24b..ef9d490 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2004-05-25 Johannes E. Schindelin + * moved the library into libvncserver/ + * moved x11vnc into x11vnc/ + 2004-05-21 Karl Runge * x11vnc: -gone, -passwdfile, -o logfile; add view-only to -accept diff --git a/Makefile.am b/Makefile.am index 359012d..d61ae3c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,7 @@ CFLAGS=-g -Wall -SUBDIRS=. examples contrib vncterm classes libvncclient test -DIST_SUBDIRS=examples contrib vncterm classes libvncclient test +SUBDIRS=libvncserver examples contrib x11vnc vncterm classes libvncclient test +DIST_SUBDIRS=libvncserver examples contrib x11vnc vncterm classes libvncclient test bin_SCRIPTS = libvncserver-config @@ -11,30 +11,6 @@ includedir=$(prefix)/include/rfb include_HEADERS=rfb/rfb.h rfb/rfbconfig.h rfb/rfbint.h rfb/rfbproto.h \ rfb/keysym.h rfb/rfbregion.h rfb/rfbclient.h -noinst_HEADERS=d3des.h rfb/default8x16.h zrleoutstream.h \ - zrlepalettehelper.h zrletypes.h - -EXTRA_DIST=tableinit24.c tableinittctemplate.c tabletranstemplate.c \ - tableinitcmtemplate.c tabletrans24template.c \ - zrleencodetemplate.c - -if HAVE_LIBZ -ZLIBSRCS = zlib.c zrle.c zrleoutstream.c zrlepalettehelper.c -if HAVE_LIBJPEG -JPEGSRCS = tight.c -endif -endif - -LIB_SRCS = main.c rfbserver.c rfbregion.c auth.c sockets.c \ - stats.c corre.c hextile.c rre.c translate.c cutpaste.c \ - httpd.c cursor.c font.c \ - draw.c selbox.c d3des.c vncauth.c cargs.c \ - $(ZLIBSRCS) $(JPEGSRCS) - -libvncserver_a_SOURCES=$(LIB_SRCS) - -lib_LIBRARIES=libvncserver.a - if HAVE_RPM $(PACKAGE)-$(VERSION).tar.gz: dist diff --git a/auth.c b/auth.c deleted file mode 100644 index ec253dd..0000000 --- a/auth.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * auth.c - deal with authentication. - * - * This file implements the VNC authentication protocol when setting up an RFB - * connection. - */ - -/* - * OSXvnc Copyright (C) 2001 Dan McGuirk . - * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. - * 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. - */ - -#include - -/* - * rfbAuthNewClient is called when we reach the point of authenticating - * a new client. If authentication isn't being used then we simply send - * rfbNoAuth. Otherwise we send rfbVncAuth plus the challenge. - */ - -void -rfbAuthNewClient(cl) - rfbClientPtr cl; -{ - char buf[4 + CHALLENGESIZE]; - int len; - - cl->state = RFB_AUTHENTICATION; - - if (cl->screen->rfbAuthPasswdData && !cl->reverseConnection) { - *(uint32_t *)buf = Swap32IfLE(rfbVncAuth); - vncRandomBytes(cl->authChallenge); - memcpy(&buf[4], (char *)cl->authChallenge, CHALLENGESIZE); - len = 4 + CHALLENGESIZE; - } else { - *(uint32_t *)buf = Swap32IfLE(rfbNoAuth); - len = 4; - cl->state = RFB_INITIALISATION; - } - - if (WriteExact(cl, buf, len) < 0) { - rfbLogPerror("rfbAuthNewClient: write"); - rfbCloseClient(cl); - return; - } -} - - -/* - * rfbAuthProcessClientMessage is called when the client sends its - * authentication response. - */ - -void -rfbAuthProcessClientMessage(cl) - rfbClientPtr cl; -{ - int n; - uint8_t response[CHALLENGESIZE]; - uint32_t authResult; - - if ((n = ReadExact(cl, (char *)response, CHALLENGESIZE)) <= 0) { - if (n != 0) - rfbLogPerror("rfbAuthProcessClientMessage: read"); - rfbCloseClient(cl); - return; - } - - if(!cl->screen->passwordCheck(cl,(const char*)response,CHALLENGESIZE)) { - rfbErr("rfbAuthProcessClientMessage: password check failed\n"); - authResult = Swap32IfLE(rfbVncAuthFailed); - if (WriteExact(cl, (char *)&authResult, 4) < 0) { - rfbLogPerror("rfbAuthProcessClientMessage: write"); - } - rfbCloseClient(cl); - return; - } - - authResult = Swap32IfLE(rfbVncAuthOK); - - if (WriteExact(cl, (char *)&authResult, 4) < 0) { - rfbLogPerror("rfbAuthProcessClientMessage: write"); - rfbCloseClient(cl); - return; - } - - cl->state = RFB_INITIALISATION; -} diff --git a/cargs.c b/cargs.c deleted file mode 100644 index c26050e..0000000 --- a/cargs.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * This parses the command line arguments. It was seperated from main.c by - * Justin Dearing . - */ - -/* - * LibVNCServer (C) 2001 Johannes E. Schindelin - * Original OSXvnc (C) 2001 Dan McGuirk . - * Original Xvnc (C) 1999 AT&T Laboratories Cambridge. - * All Rights Reserved. - * - * see GPL (latest version) for full details - */ - -#include - -void -rfbUsage(void) -{ - fprintf(stderr, "-rfbport port TCP port for RFB protocol\n"); - fprintf(stderr, "-rfbwait time max time in ms to wait for RFB client\n"); - fprintf(stderr, "-rfbauth passwd-file use authentication on RFB protocol\n" - " (use 'storepasswd' to create a password file)\n"); - fprintf(stderr, "-passwd plain-password use authentication \n" - " (use plain-password as password, USE AT YOUR RISK)\n"); - fprintf(stderr, "-deferupdate time time in ms to defer updates " - "(default 40)\n"); - fprintf(stderr, "-desktop name VNC desktop name (default \"LibVNCServer\")\n"); - fprintf(stderr, "-alwaysshared always treat new clients as shared\n"); - fprintf(stderr, "-nevershared never treat new clients as shared\n"); - fprintf(stderr, "-dontdisconnect don't disconnect existing clients when a " - "new non-shared\n" - " connection comes in (refuse new connection " - "instead)\n"); - fprintf(stderr, "-httpdir dir-path enable http server using dir-path home\n"); - fprintf(stderr, "-httpport portnum use portnum for http connection\n"); - fprintf(stderr, "-enablehttpproxy enable http proxy support\n"); - fprintf(stderr, "-progressive height enable progressive updating for slow links\n"); -} - -/* purges COUNT arguments from ARGV at POSITION and decrements ARGC. - POSITION points to the first non purged argument afterwards. */ -void rfbPurgeArguments(int* argc,int* position,int count,char *argv[]) -{ - int amount=(*argc)-(*position)-count; - if(amount) - memmove(argv+(*position),argv+(*position)+count,sizeof(char*)*amount); - (*argc)-=count; -} - -rfbBool -rfbProcessArguments(rfbScreenInfoPtr rfbScreen,int* argc, char *argv[]) -{ - int i,i1; - - if(!argc) return TRUE; - - for (i = i1 = 1; i < *argc;) { - if (strcmp(argv[i], "-help") == 0) { - rfbUsage(); - return FALSE; - } else if (strcmp(argv[i], "-rfbport") == 0) { /* -rfbport port */ - if (i + 1 >= *argc) { - rfbUsage(); - return FALSE; - } - rfbScreen->rfbPort = atoi(argv[++i]); - } else if (strcmp(argv[i], "-rfbwait") == 0) { /* -rfbwait ms */ - if (i + 1 >= *argc) { - rfbUsage(); - return FALSE; - } - rfbScreen->rfbMaxClientWait = atoi(argv[++i]); - } else if (strcmp(argv[i], "-rfbauth") == 0) { /* -rfbauth passwd-file */ - if (i + 1 >= *argc) { - rfbUsage(); - return FALSE; - } - rfbScreen->rfbAuthPasswdData = argv[++i]; - } else if (strcmp(argv[i], "-passwd") == 0) { /* -passwd password */ - char **passwds = malloc(sizeof(char**)*2); - if (i + 1 >= *argc) { - rfbUsage(); - return FALSE; - } - passwds[0] = argv[++i]; - passwds[1] = 0; - rfbScreen->rfbAuthPasswdData = (void*)passwds; - rfbScreen->passwordCheck = rfbCheckPasswordByList; - } else if (strcmp(argv[i], "-deferupdate") == 0) { /* -deferupdate milliseconds */ - if (i + 1 >= *argc) { - rfbUsage(); - return FALSE; - } - rfbScreen->rfbDeferUpdateTime = atoi(argv[++i]); - } else if (strcmp(argv[i], "-desktop") == 0) { /* -desktop desktop-name */ - if (i + 1 >= *argc) { - rfbUsage(); - return FALSE; - } - rfbScreen->desktopName = argv[++i]; - } else if (strcmp(argv[i], "-alwaysshared") == 0) { - rfbScreen->rfbAlwaysShared = TRUE; - } else if (strcmp(argv[i], "-nevershared") == 0) { - rfbScreen->rfbNeverShared = TRUE; - } else if (strcmp(argv[i], "-dontdisconnect") == 0) { - rfbScreen->rfbDontDisconnect = TRUE; - } else if (strcmp(argv[i], "-httpdir") == 0) { /* -httpdir directory-path */ - if (i + 1 >= *argc) { - rfbUsage(); - return FALSE; - } - rfbScreen->httpDir = argv[++i]; - } else if (strcmp(argv[i], "-httpport") == 0) { /* -httpport portnum */ - if (i + 1 >= *argc) { - rfbUsage(); - return FALSE; - } - rfbScreen->httpPort = atoi(argv[++i]); - } else if (strcmp(argv[i], "-enablehttpproxy") == 0) { - rfbScreen->httpEnableProxyConnect = TRUE; - } else if (strcmp(argv[i], "-progressive") == 0) { /* -httpport portnum */ - if (i + 1 >= *argc) { - rfbUsage(); - return FALSE; - } - rfbScreen->progressiveSliceHeight = atoi(argv[++i]); - } else { - i++; - i1=i; - continue; - } - /* we just remove the processed arguments from the list */ - rfbPurgeArguments(argc,&i1,i-i1+1,argv); - i=i1; - } - return TRUE; -} - -void rfbSizeUsage() -{ - fprintf(stderr, "-width sets the width of the framebuffer\n"); - fprintf(stderr, "-height sets the height of the framebuffer\n"); -} - -rfbBool -rfbProcessSizeArguments(int* width,int* height,int* bpp,int* argc, char *argv[]) -{ - int i,i1; - - if(!argc) return TRUE; - for (i = i1 = 1; i < *argc-1;) { - if (strcmp(argv[i], "-bpp") == 0) { - *bpp = atoi(argv[++i]); - } else if (strcmp(argv[i], "-width") == 0) { - *width = atoi(argv[++i]); - } else if (strcmp(argv[i], "-height") == 0) { - *height = atoi(argv[++i]); - } else { - i++; - i1=i; - continue; - } - rfbPurgeArguments(argc,&i1,i-i1,argv); - i=i1; - } - return TRUE; -} - diff --git a/configure.ac b/configure.ac index 0761996..888a529 100644 --- a/configure.ac +++ b/configure.ac @@ -136,7 +136,9 @@ AC_SUBST(RPMSOURCEDIR) LDADD="-L.. -lvncserver" AC_CONFIG_FILES([Makefile + libvncserver/Makefile contrib/Makefile + x11vnc/Makefile examples/Makefile vncterm/Makefile classes/Makefile diff --git a/contrib/ChangeLog b/contrib/ChangeLog deleted file mode 100644 index 9e468f6..0000000 --- a/contrib/ChangeLog +++ /dev/null @@ -1,143 +0,0 @@ -2004-05-21 Karl Runge - * -accept: add view-only decision and other improvements. - * add -gone command option for when a client leaves. - Thanks to Jesus Alvarez for these ideas. - * -passwdfile to keep passwd off of cmd line. - * -o logfile send stderr to a logfile. - -2004-05-14 Karl Runge - * improvements to -accept popup: yes/no buttons and timeout. - * less fprintf under -q so '-q -inetd' has no stderr output. - -2004-05-08 Karl Runge - * add -accept some-command/xmessage/popup to prompt local X11 user - or otherwise decide to accept an incoming client. - * clean up -Wall warnings. - -2004-05-05 Karl Runge - * enable mouse button -> keystrokes mapping in -buttonmap (mousewheel) - * enable keystroke -> mouse button mapping in -remap (touchpad paste) - (-remap incompat ':' -> '-', sorry...) - * shm OS blacklist (i.e. <= SunOS 5.8) -> -onetile - * revert to check_user_input() under -nofb - * cleanup: lastmod, remove tile_shm and update_client_pointer, - debug output, rfbPort failure. - * user friendly last line: 'The VNC desktop is hostname:0' - -2004-04-28 Karl Runge - * -auth cmdline option for xauthority. - * decrease default deferupdate under -nofb. - * update_client_pointer() from Edoardo Tirtarahardja. - * remove some assumptions about libvncserver defaults. - -2004-04-19 Karl Runge - * support for cursor positions updates -cursorpos - * option for SIGPIPE handling -sigpipe - -2004-04-13 Karl Runge - * solve problem with sending selection when client initializing - (not yet in RFB_NORMAL state). Increase delay to 15s as well. - * when threaded: limit rfbMaxClientWait to >= 20 secs and - increase it to a huge value unless -rfbwait is supplied. - -2004-04-08 Karl Runge - * added support for blacking out regions of the screen, primarily - for Xinerama usage, options: -blackout -xinerama - * Xinerama workaround mouse problem on 'embedded' system, - option -xwarppointer (XWarpPointer instead of XTEST) - * let -remap option take key remappings on cmdline as well as file. - * use cargs fix to test for invalid cmdline options. Add --option. - * remove copy_tile, use copy_tiles(..., 1) instead. - -2004-03-10 Karl Runge - * added reverse connection for vncconnect(1) and other means - -vncconnect, -connect host:port, and -connect watchfile - * added first pass at user keysym remapping feature via - -remap file. Ignores modifier state, need to generalize. - * debugging options for users -debug_pointer and -debug_keyboard - * clear -passwd from argv for privacy (if OS allows). - -2004-02-19 Karl Runge - * added handling of clipboard/selection exchange to/from clients, - even holds PRIMARY which Xvnc does not do. disable with -nosel. - use -noprimary to disable polling of PRIMARY selection. - * added -visual option to force framebuffer visual. not really - of general use, more for testing and workarounds (e.g. win2vnc - fails under 8bpp index color) - * improve cleanup and error handling WRT shm and other failures. - -2004-01-19 Karl Runge - * improvements to pointer event handling primarily during window - dragging. check_user_input() for non-threaded and pointer() - for threaded. Revert to old way via -old_pointer option. - * some memory I/O improvement by using copy_tiles() instead - of copy_tile(). New one does rows of tiles at same time. - Revert to old way via -old_copytile. - * handle case of more mouse buttons on client than on X server. - * added -buttonmap option for finer control over button differences. - -2004-01-09 Karl Runge - * options -allow / -localhost for simple IP based access screening - * option -nodragging to skip all screen updates during mouse drags - (thanks to Michal Sabala) - * option -input_skip to allow users to tune watch_loop dropthru rate - * try to avoid wasting RAM for framebuffer under -nofb - * cleanup wrt bpp vs. depth - -2003-12-08 Karl Runge - * add Xbell support using XKEYBOARD extension (disable: -nobell) - * add "-nofb" to disable framebuffer, i.e. mouse + keyboard only (!) - * add "-notruecolor" to force indexed 8bpp color (when 8bpp) - * make alias "-forever" for "-many" - -From Karl (x11vnc's father) on Apr 2, 2003: - -New option -nocursor to not display the vncviewer local cursor if user -does not want it (also caused some problems with older vncviewers) - -New option -mouse to show the position of the X server mouse (i.e. lagged -from the user's vnc cursor position). Also: -mouseX will try to show -the a different cursor (X) when on the root background. - -New option -many to wait for more connections rather than exiting when -the first client(s) disconnect. - -New option -flashcmap to try to follow installed colormaps under 8bpp -indexed color as pointer is moved. - -New option -nap to watch for low activity and throttle down the polling -rate. Useful on shared machines to keep the load down. - -Experimental option -id to show just that window and not -the whole display. Some remaining bugs and inconvenient behavior... -(e.g. new toplevels can be unseen) - -Fixed bug on multi-headed machines where the screen number was being -ignored in a number of places. - -Fixed bug wrt connect_once mode. Now just refuses new clients unless -shared rather than terminating all clients. - -Try to follow changing default colormap under 8bpp indexed color -as color cells are added. - -Needed to pick up HAVE_LIBPTHREAD from autoconf. - -defined a select() macro for usleep() since usleep is not always thread -safe. - -Catch and exit on errors in the shm setup work (XShmCreateImage, shmget,...) -and moved the creation and removal work to separate utility functions. - -Added signal and X error handlers to try to clean out the shm objects -before exiting on interrupt, etc. - -Improved performance a bit on the memcmp() in scan_display() by checking -the whole line first. - -Added a workaround when threaded where libvncserver may disconnect too -early if it does not hear from a client (a small heartbeat is sent). -This may not be needed any longer. - -If -desktop has not been prescribed, try to choose a title based on DISPLAY -and the hostname (and window name under -id). diff --git a/contrib/Makefile.am b/contrib/Makefile.am index 848b9a2..568d8b8 100644 --- a/contrib/Makefile.am +++ b/contrib/Makefile.am @@ -1,18 +1,7 @@ CFLAGS = -I .. -LDADD = ../libvncserver.a +LDADD = ../libvncserver/libvncserver.a noinst_PROGRAMS=zippy -if CYGIPC -LD_CYGIPC=-lcygipc -endif - -if HAVE_X -bin_PROGRAMS=x11vnc -x11vnc_SOURCES=x11vnc.c -INCLUDES=@X_CFLAGS@ -x11vnc_LDADD=@X_LIBS@ $(LD_CYGIPC) $(LDADD) -endif - zippy_SOURCES=zippy.c diff --git a/contrib/x11vnc.c b/contrib/x11vnc.c deleted file mode 100644 index 144b280..0000000 --- a/contrib/x11vnc.c +++ /dev/null @@ -1,6742 +0,0 @@ -/* - * x11vnc.c: a VNC server for X displays. - * - * Copyright (c) 2002-2003 Karl J. Runge - * All rights reserved. - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - * - * - * This program is based heavily on the following programs: - * - * the originial x11vnc.c in libvncserver (Johannes E. Schindelin) - * krfb, the KDE desktopsharing project (Tim Jansen) - * x0rfbserver, the original native X vnc server (Jens Wagner) - * - * The primary goal of this program is to create a portable and simple - * command-line server utility that allows a VNC viewer to connect to an - * actual X display (as the above do). The only non-standard dependency - * of this program is the static library libvncserver.a (although in - * some environments libjpeg.so may not be readily available and needs - * to be installed, it may be found at ftp://ftp.uu.net/graphics/jpeg/). - * To increase portability it is written in plain C. - * - * The next goal is to improve performance and interactive response. - * The algorithm currently used here to achieve this is that of krfb - * (based on x0rfbserver algorithm). Additional heuristics are also - * applied (currently there are a bit too many of these...) - * - * To build: - * - * Obtain the libvncserver package (http://libvncserver.sourceforge.net). - * As of 12/2002 this version of x11vnc.c is contained in the libvncserver - * CVS tree and released in version 0.5. - * - * gcc should be used on all platforms. To build a threaded version put - * "-D_REENTRANT -DX11VNC_THREADED" in the environment variable CFLAGS - * or CPPFLAGS (e.g. before running the libvncserver configure). The - * threaded mode is a bit more responsive, but can be unstable. - * - * Known shortcomings: - * - * The screen updates are good, but of course not perfect since the X - * display must be continuously polled and read for changes (as opposed to - * receiving a change callback from the X server, if that were generally - * possible...). So, e.g., opaque moves and similar window activity - * can be very painful; one has to modify one's behavior a bit. - * - * General audio at the remote display is lost unless one separately - * sets up some audio side-channel such as esd. - * - * It does not appear possible to query the X server for the current - * cursor shape. We can use XTest to compare cursor to current window's - * cursor, but we cannot extract what the cursor is... - * - * Nevertheless, the current *position* of the remote X mouse pointer - * is shown with the -mouse option. Further, if -mouseX or -X is used, a - * trick is done to at least show the root window cursor vs non-root cursor. - * (perhaps some heuristic can be done to further distinguish cases...) - * - * With -mouse there are occasionally some repainting errors involving - * big areas near the cursor. The mouse painting is in general a bit - * ragged and not very pleasant. - * - * Windows using visuals other than the default X visual may have - * their colors messed up. When using 8bpp indexed color, the colormap - * is attempted to be followed, but may become out of date. Use the - * -flashcmap option to have colormap flashing as the pointer moves - * windows with private colormaps (slow). Displays with mixed depth 8 and - * 24 visuals will incorrect display the non-default one. - * - * Feature -id can be picky: it can crash for things like the - * window not sufficiently mapped into server memory, use of -mouse, etc. - * SaveUnders menus, popups, etc will not be seen. - * - * Occasionally, a few tile updates can be missed leaving a patch of - * color that needs to be refreshed. - * - * There seems to be a serious bug with simultaneous clients when - * threaded, currently the only workaround in this case is -nothreads. - * - */ - -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#ifdef LIBVNCSERVER_HAVE_XKEYBOARD -#include -#endif - -/* - * Temporary kludge: to run with -xinerama define the following macro - * and be sure to link with * -lXinerama (e.g. LDFLAGS=-lXinerama before - * configure). Support for this is being added to libvncserver 'configure.ac' - * so it will all be done automatically. - -#define LIBVNCSERVER_HAVE_LIBXINERAMA - */ -#ifdef LIBVNCSERVER_HAVE_LIBXINERAMA -#include -#endif - -/* date +'"lastmod: %Y-%m-%d";' */ -char lastmod[] = "lastmod: 2004-05-21"; - - -/* X and rfb framebuffer */ -Display *dpy = 0; -Visual *visual; -Window window, rootwin; -int scr; -int bpp, depth; -int button_mask = 0; -int dpy_x, dpy_y; -int off_x, off_y; -int subwin = 0; -int indexed_colour = 0; - -XImage *tile; -XImage **tile_row; /* for all possible row runs */ -XImage *scanline; -XImage *fullscreen; -int fs_factor = 0; - -XShmSegmentInfo *tile_row_shm; /* for all possible row runs */ -XShmSegmentInfo scanline_shm; -XShmSegmentInfo fullscreen_shm; - -rfbScreenInfoPtr screen; -rfbCursorPtr cursor; -int bytes_per_line; - -/* size of the basic tile unit that is polled for changes: */ -int tile_x = 32; -int tile_y = 32; -int ntiles, ntiles_x, ntiles_y; - -/* arrays that indicate changed or checked tiles. */ -unsigned char *tile_has_diff, *tile_tried; - -/* blacked-out region things */ -typedef struct bout { - int x1, y1, x2, y2; -} blackout_t; -typedef struct tbout { - blackout_t bo[10]; /* hardwired max rectangles. */ - int cover; - int count; -} tile_blackout_t; -blackout_t black[100]; /* hardwired max blackouts */ -int blackouts = 0; -tile_blackout_t *tile_blackout; - - -typedef struct tile_change_region { - /* start and end lines, along y, of the changed area inside a tile. */ - unsigned short first_line, last_line; - /* info about differences along edges. */ - unsigned short left_diff, right_diff; - unsigned short top_diff, bot_diff; -} region_t; - -/* array to hold the tiles region_t-s. */ -region_t *tile_region; - -typedef struct hint { - /* location x, y, height, and width of a change-rectangle */ - /* (grows as adjacent horizontal tiles are glued together) */ - int x, y, w, h; -} hint_t; - -/* array to hold the hints: */ -hint_t *hint_list; - -/* various command line options */ - -int shared = 0; /* share vnc display. */ -char *allow_list = NULL; /* for -allow and -localhost */ -char *accept_cmd = NULL; /* for -accept */ -char *gone_cmd = NULL; /* for -gone */ -int view_only = 0; /* clients can only watch. */ -int inetd = 0; /* spawned from inetd(1) */ -int connect_once = 1; /* disconnect after first connection session. */ -int flash_cmap = 0; /* follow installed colormaps */ -int force_indexed_color = 0; /* whether to force indexed color for 8bpp */ - -int use_modifier_tweak = 0; /* use the altgr_keyboard modifier tweak */ -char *remap_file = NULL; /* user supplied remapping file or list */ -int nofb = 0; /* do not send any fb updates */ - -char *blackout_string = NULL; /* -blackout */ -int xinerama = 0; /* -xinerama */ - -char *client_connect = NULL; /* strings for -connect option */ -char *client_connect_file = NULL; -int vnc_connect = 0; /* -vncconnect option */ - -int local_cursor = 1; /* whether the viewer draws a local cursor */ -int cursor_pos = 0; /* cursor position updates -cursorpos */ -int show_mouse = 0; /* display a cursor for the real mouse */ -int use_xwarppointer = 0; /* use XWarpPointer instead of XTestFake... */ -int show_root_cursor = 0; /* show X when on root background */ -int show_dragging = 1; /* process mouse movement events */ -int watch_bell = 1; /* watch for the bell using XKEYBOARD */ - -int old_pointer = 0; /* use the old way of updating the pointer */ -int single_copytile = 0; /* use the old way copy_tiles() */ - -int using_shm = 1; /* whether mit-shm is used */ -int flip_byte_order = 0; /* sometimes needed when using_shm = 0 */ -/* - * waitms is the msec to wait between screen polls. Not too old h/w shows - * poll times of 10-35ms, so maybe this value cuts the idle load by 2 or so. - */ -int waitms = 30; -int defer_update = 30; /* rfbDeferUpdateTime ms to wait before sends. */ -int defer_update_nofb = 6; /* defer a shorter time under -nofb */ - -int screen_blank = 60; /* number of seconds of no activity to throttle */ - /* down the screen polls. zero to disable. */ -int take_naps = 0; -int naptile = 3; /* tile change threshold per poll to take a nap */ -int napfac = 4; /* time = napfac*waitms, cut load with extra waits */ -int napmax = 1500; /* longest nap in ms. */ -int ui_skip = 10; /* see watchloop. negative means ignore input */ - -/* for -visual override */ -VisualID visual_id = (VisualID) 0; -int visual_depth = 0; - -int nap_ok = 0, nap_diff_count = 0; -time_t last_event, last_input, last_client = 0; - -/* tile heuristics: */ -double fs_frac = 0.75; /* threshold tile fraction to do fullscreen updates. */ -int use_hints = 1; /* use the krfb scheme of gluing tiles together. */ -int tile_fuzz = 2; /* tolerance for suspecting changed tiles touching */ - /* a known changed tile. */ -int grow_fill = 3; /* do the grow islands heuristic with this width. */ -int gaps_fill = 4; /* do a final pass to try to fill gaps between tiles. */ - -/* scan pattern jitter from x0rfbserver */ -#define NSCAN 32 -int scanlines[NSCAN] = { - 0, 16, 8, 24, 4, 20, 12, 28, - 10, 26, 18, 2, 22, 6, 30, 14, - 1, 17, 9, 25, 7, 23, 15, 31, - 19, 3, 27, 11, 29, 13, 5, 21 -}; -int count = 0; /* indicates which scan pattern we are on */ - -int cursor_x, cursor_y; /* x and y from the viewer(s) */ -int got_user_input = 0; -int got_pointer_input = 0; -int got_keyboard_input = 0; -int scan_in_progress = 0; -int fb_copy_in_progress = 0; -int client_count = 0; -int shut_down = 0; -int sigpipe = 1; /* 0=skip, 1=ignore, 2=exit */ - -int debug_pointer = 0; -int debug_keyboard = 0; - -int quiet = 0; -double dtime(double *); -int all_clients_initialized(void); - -void zero_fb(int, int, int, int); - -#if defined(LIBVNCSERVER_X11VNC_THREADED) && ! defined(X11VNC_THREADED) -#define X11VNC_THREADED -#endif - -#if defined(LIBVNCSERVER_HAVE_LIBPTHREAD) && defined(X11VNC_THREADED) - int use_threads = 1; -#else - int use_threads = 0; -#endif - -/* XXX usleep(3) is not thread safe on some older systems... */ -struct timeval _mysleep; -#define usleep2(x) \ - _mysleep.tv_sec = (x) / 1000000; \ - _mysleep.tv_usec = (x) % 1000000; \ - select(0, NULL, NULL, NULL, &_mysleep); -#if !defined(X11VNC_USLEEP) -#undef usleep -#define usleep usleep2 -#endif - -/* - * Not sure why... but when threaded we have to mutex our X11 calls to - * avoid XIO crashes. - */ -MUTEX(x11Mutex); -#define X_LOCK LOCK(x11Mutex) -#define X_UNLOCK UNLOCK(x11Mutex) -#define X_INIT INIT_MUTEX(x11Mutex) - -/* - * Exiting and error handling: - */ -void shm_clean(XShmSegmentInfo *, XImage *); -void shm_delete(XShmSegmentInfo *); - -int exit_flag = 0; -void clean_up_exit (int ret) { - int i; - exit_flag = 1; - - /* remove the shm areas: */ - shm_clean(&scanline_shm, scanline); - shm_clean(&fullscreen_shm, fullscreen); - - for(i=1; i<=ntiles_x; i++) { - shm_clean(&tile_row_shm[i], tile_row[i]); - if (single_copytile && i >= single_copytile) { - break; - } - } - - X_LOCK; - XTestDiscard(dpy); - X_UNLOCK; - - fflush(stderr); - exit(ret); -} - -/* - * General problem handler - */ -void interrupted (int sig) { - int i; - if (exit_flag) { - exit_flag++; - if (use_threads) { - usleep2(250 * 1000); - } else if (exit_flag <= 2) { - return; - } - exit(4); - } - exit_flag++; - if (sig == 0) { - fprintf(stderr, "caught X11 error:\n"); - } else { - fprintf(stderr, "caught signal: %d\n", sig); - } - /* - * to avoid deadlock, etc, just delete the shm areas and - * leave the X stuff hanging. - */ - shm_delete(&scanline_shm); - shm_delete(&fullscreen_shm); - - /* - * Here we have to clean up quite a few shm areas for all - * the possible tile row runs (40 for 1280), not as robust - * as one might like... sometimes need to run ipcrm(1). - */ - for(i=1; i<=ntiles_x; i++) { - shm_delete(&tile_row_shm[i]); - if (single_copytile && i >= single_copytile) { - break; - } - } - if (sig) { - exit(2); - } -} - -XErrorHandler Xerror_def; -XIOErrorHandler XIOerr_def; -int Xerror(Display *d, XErrorEvent *error) { - X_UNLOCK; - interrupted(0); - return (*Xerror_def)(d, error); -} -int XIOerr(Display *d) { - X_UNLOCK; - interrupted(0); - return (*XIOerr_def)(d); -} - -void set_signals(void) { - signal(SIGHUP, interrupted); - signal(SIGINT, interrupted); - signal(SIGQUIT, interrupted); - signal(SIGABRT, interrupted); - signal(SIGTERM, interrupted); - signal(SIGBUS, interrupted); - signal(SIGSEGV, interrupted); - signal(SIGFPE, interrupted); - - if (sigpipe == 1) { -#ifdef SIG_IGN - signal(SIGPIPE, SIG_IGN); -#endif - } else if (sigpipe == 2) { - rfbLog("set_signals: will exit on SIGPIPE\n"); - signal(SIGPIPE, interrupted); - } - - X_LOCK; - Xerror_def = XSetErrorHandler(Xerror); - XIOerr_def = XSetIOErrorHandler(XIOerr); - X_UNLOCK; -} - -int run_user_command(char *, rfbClientPtr); - -int accepted_client = 0; -void client_gone(rfbClientPtr client) { - - client_count--; - rfbLog("client_count: %d\n", client_count); - - if (gone_cmd) { - rfbLog("client_gone: using cmd for: %s\n", client->host); - run_user_command(gone_cmd, client); - } - - if (inetd) { - rfbLog("viewer exited.\n"); - clean_up_exit(0); - } - if (connect_once) { - /* - * This non-exit is done for a bad passwd to be consistent - * with our RFB_CLIENT_REFUSE behavior in new_client() (i.e. - * we disconnect after 1 successful connection). - */ - if (client->state == RFB_PROTOCOL_VERSION || - client->state == RFB_AUTHENTICATION && accepted_client) { - rfbLog("connect_once: bad password or early " - "disconnect.\n"); - rfbLog("connect_once: waiting for next connection.\n"); - accepted_client = 0; - return; - } - - rfbLog("viewer exited.\n"); - clean_up_exit(0); - } -} - -/* - * Simple routine to limit access via string compare. A power user will - * want to compile libvncserver with libwrap support and use /etc/hosts.allow. - */ -int check_access(char *addr) { - int allowed = 0; - char *p, *list; - - if (allow_list == NULL || *allow_list == '\0') { - return 1; - } - if (addr == NULL || *addr == '\0') { - rfbLog("check_access: denying empty host IP address string.\n"); - return 0; - } - - list = strdup(allow_list); - p = strtok(list, ","); - while (p) { - char *q = strstr(addr, p); - if (q == addr) { - rfbLog("check_access: client %s matches pattern %s\n", - addr, p); - allowed = 1; - - } else if(!strcmp(p,"localhost") && !strcmp(addr,"127.0.0.1")) { - allowed = 1; - } - p = strtok(NULL, ","); - } - free(list); - return allowed; -} - -/* - * x11vnc's first (and only) visible widget: accept/reject dialog window. - * We go through this pain to avoid dependency on libXt. - */ - -int ugly_accept_window(char *addr, int X, int Y, int timeout, char *mode) { - -#define t2x2_width 16 -#define t2x2_height 16 -static char t2x2_bits[] = { - 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, - 0x33, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33, - 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33}; - - Window awin; - GC gc; - XSizeHints hints; - XGCValues values; - static XFontStruct *font_info = NULL; - static Pixmap ico = 0; - unsigned long valuemask = 0; - static char dash_list[] = {20, 40}; - int list_length = sizeof(dash_list); - - Atom wm_protocols; - Atom wm_delete_window; - - XEvent ev; - long evmask = ExposureMask | KeyPressMask | ButtonPressMask - | StructureNotifyMask; - double waited = 0.0; - - /* strings and geometries y/n */ - KeyCode key_y, key_n, key_v; - char strh[100]; - char str1_b[] = "To accept: press \"y\" or click the \"Yes\" button"; - char str2_b[] = "To reject: press \"n\" or click the \"No\" button"; - char str3_b[] = "View only: press \"v\" or click the \"View\" button"; - char str1_m[] = "To accept: click the \"Yes\" button"; - char str2_m[] = "To reject: click the \"No\" button"; - char str3_m[] = "View only: click the \"View\" button"; - char str1_k[] = "To accept: press \"y\""; - char str2_k[] = "To reject: press \"n\""; - char str3_k[] = "View only: press \"v\""; - char *str1, *str2, *str3; - char str_y[] = "Yes"; - char str_n[] = "No"; - char str_v[] = "View"; - int x, y, w = 345, h = 150, ret = 0; - int X_sh = 20, Y_sh = 30, dY = 20; - int Ye_x = 20, Ye_y = 0, Ye_w = 45, Ye_h = 20; - int No_x = 75, No_y = 0, No_w = 45, No_h = 20; - int Vi_x = 130, Vi_y = 0, Vi_w = 45, Vi_h = 20; - - if (!strcmp(mode, "mouse_only")) { - str1 = str1_m; - str2 = str2_m; - str3 = str3_m; - } else if (!strcmp(mode, "key_only")) { - str1 = str1_k; - str2 = str2_k; - str3 = str3_k; - h -= dY; - } else { - str1 = str1_b; - str2 = str2_b; - str3 = str3_b; - } - if (view_only) { - h -= dY; - } - - if (X < -dpy_x) { - x = (dpy_x - w)/2; /* large negative: center */ - if (x < 0) x = 0; - } else if (X < 0) { - x = dpy_x + X - w; /* from lower right */ - } else { - x = X; /* from upper left */ - } - - if (Y < -dpy_y) { - y = (dpy_y - h)/2; - if (y < 0) y = 0; - } else if (Y < 0) { - y = dpy_y + Y - h; - } else { - y = Y; - } - - X_LOCK; - - awin = XCreateSimpleWindow(dpy, window, x, y, w, h, 4, - BlackPixel(dpy, scr), WhitePixel(dpy, scr)); - - wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False); - wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False); - XSetWMProtocols(dpy, awin, &wm_delete_window, 1); - - if (! ico) { - ico = XCreateBitmapFromData(dpy, awin, t2x2_bits, t2x2_width, - t2x2_height); - } - - hints.flags = PPosition | PSize | PMinSize; - hints.x = x; - hints.y = y; - hints.width = w; - hints.height = h; - hints.min_width = w; - hints.min_height = h; - - XSetStandardProperties(dpy, awin, "new x11vnc client", "x11vnc query", - ico, NULL, 0, &hints); - - XSelectInput(dpy, awin, evmask); - - if (! font_info && (font_info = XLoadQueryFont(dpy, "fixed")) == NULL) { - rfbLog("ugly_accept_window: cannot locate font fixed.\n"); - X_UNLOCK; - clean_up_exit(1); - } - - gc = XCreateGC(dpy, awin, valuemask, &values); - XSetFont(dpy, gc, font_info->fid); - XSetForeground(dpy, gc, BlackPixel(dpy, scr)); - XSetLineAttributes(dpy, gc, 1, LineSolid, CapButt, JoinMiter); - XSetDashes(dpy, gc, 0, dash_list, list_length); - - XMapWindow(dpy, awin); - XFlush(dpy); - - sprintf(strh, "x11vnc: accept connection from %s?", addr); - key_y = XKeysymToKeycode(dpy, XStringToKeysym("y")); - key_n = XKeysymToKeycode(dpy, XStringToKeysym("n")); - key_v = XKeysymToKeycode(dpy, XStringToKeysym("v")); - - while (1) { - int out = -1, x, y, tw, k; - - if (XCheckWindowEvent(dpy, awin, evmask, &ev)) { - ; /* proceed to handling */ - } else if (XCheckTypedEvent(dpy, ClientMessage, &ev)) { - ; /* proceed to handling */ - } else { - int ms = 100; /* sleep a bit */ - usleep(ms * 1000); - waited += ((double) ms)/1000.; - if (timeout && (int) waited >= timeout) { - rfbLog("accept_client: popup timed out after " - "%d seconds.\n", timeout); - out = 0; - ev.type = 0; - } else { - continue; - } - } - - switch(ev.type) { - case Expose: - while (XCheckTypedEvent(dpy, Expose, &ev)) { - ; - } - k=0; - - /* instructions */ - XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, - strh, strlen(strh)); - XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, - str1, strlen(str1)); - XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, - str2, strlen(str2)); - if (! view_only) { - XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, - str3, strlen(str3)); - } - - if (!strcmp(mode, "key_only")) { - break; - } - - /* buttons */ - Ye_y = Y_sh+k*dY; - No_y = Y_sh+k*dY; - Vi_y = Y_sh+k*dY; - XDrawRectangle(dpy, awin, gc, Ye_x, Ye_y, Ye_w, Ye_h); - XDrawRectangle(dpy, awin, gc, No_x, No_y, No_w, No_h); - if (! view_only) { - XDrawRectangle(dpy, awin, gc, Vi_x, Vi_y, - Vi_w, Vi_h); - } - - tw = XTextWidth(font_info, str_y, strlen(str_y)); - tw = (Ye_w - tw)/2; - if (tw < 0) tw = 1; - XDrawString(dpy, awin, gc, Ye_x+tw, Ye_y+Ye_h-5, - str_y, strlen(str_y)); - - tw = XTextWidth(font_info, str_n, strlen(str_n)); - tw = (No_w - tw)/2; - if (tw < 0) tw = 1; - XDrawString(dpy, awin, gc, No_x+tw, No_y+No_h-5, - str_n, strlen(str_n)); - - if (! view_only) { - tw = XTextWidth(font_info, str_v, - strlen(str_v)); - tw = (Vi_w - tw)/2; - if (tw < 0) tw = 1; - XDrawString(dpy, awin, gc, Vi_x+tw, Vi_y+Vi_h-5, - str_v, strlen(str_v)); - } - - break; - - case ClientMessage: - if (ev.xclient.message_type == wm_protocols && - ev.xclient.data.l[0] == wm_delete_window) { - out = 0; - } - break; - - case ButtonPress: - x = ev.xbutton.x; - y = ev.xbutton.y; - if (!strcmp(mode, "key_only")) { - ; - } else if (x > No_x && x < No_x+No_w && y > No_y - && y < No_y+No_h) { - out = 0; - } else if (x > Ye_x && x < Ye_x+Ye_w && y > Ye_y - && y < Ye_y+Ye_h) { - out = 1; - } else if (! view_only && x > Vi_x && x < Vi_x+Vi_w - && y > Vi_y && y < Vi_y+Ye_h) { - out = 2; - } - break; - - case KeyPress: - if (!strcmp(mode, "mouse_only")) { - ; - } else if (ev.xkey.keycode == key_y) { - out = 1; - } else if (ev.xkey.keycode == key_n) { - out = 0; - } else if (! view_only && ev.xkey.keycode == key_v) { - out = 2; - } - break; - default: - break; - } - if (out != -1) { - ret = out; - XUnmapWindow(dpy, awin); - XFreeGC(dpy, gc); - XDestroyWindow(dpy, awin); - XFlush(dpy); - break; - } - } - X_UNLOCK; - - return ret; -} - -/* - * utility to run a user supplied command setting some RFB_ env vars. - * used by, e.g., accept_client() and client_gone() - */ -int run_user_command(char *cmd, rfbClientPtr client) { - char *dpystr = DisplayString(dpy); - static char *display_env = NULL; - static char env_rfb_client_id[100]; - static char env_rfb_client_ip[100]; - static char env_rfb_client_port[100]; - static char env_rfb_x11vnc_pid[100]; - char *addr = client->host; - int rc, fromlen, fromport; - struct sockaddr_in from; - - if (addr == NULL || addr[0] == '\0') { - addr = "unknown-host"; - } - - /* set RFB_CLIENT_ID to semi unique id for command to use */ - sprintf(env_rfb_client_id, "RFB_CLIENT_ID=%d", (int) client); - putenv(env_rfb_client_id); - - /* set RFB_CLIENT_IP to IP addr for command to use */ - sprintf(env_rfb_client_ip, "RFB_CLIENT_IP=%s", addr); - putenv(env_rfb_client_ip); - - /* set RFB_X11VNC_PID to our pid for command to use */ - sprintf(env_rfb_x11vnc_pid, "RFB_X11VNC_PID=%d", (int) getpid()); - putenv(env_rfb_x11vnc_pid); - - /* set RFB_CLIENT_PORT to peer port for command to use */ - fromlen = sizeof(from); - memset(&from, 0, sizeof(from)); - fromport = -1; - if (!getpeername(client->sock, (struct sockaddr *)&from, &fromlen)) { - fromport = ntohs(from.sin_port); - } - sprintf(env_rfb_client_port, "RFB_CLIENT_PORT=%d", fromport); - putenv(env_rfb_client_port); - - /* - * Better set DISPLAY to the one we are polling, if they - * want something trickier, they can handle on their own - * via environment, etc. XXX really should save/restore old. - */ - if (display_env == NULL) { - display_env = (char *) malloc(strlen(dpystr)+10); - } - sprintf(display_env, "DISPLAY=%s", dpystr); - putenv(display_env); - - rfbLog("running command:\n"); - rfbLog(" %s\n", cmd); - - rc = system(cmd); - - if (rc >= 256) { - rc = rc/256; - } - rfbLog("command returned: %d\n", rc); - - sprintf(env_rfb_client_id, "RFB_CLIENT_ID="); - putenv(env_rfb_client_id); - - sprintf(env_rfb_client_ip, "RFB_CLIENT_IP="); - putenv(env_rfb_client_ip); - - sprintf(env_rfb_client_port, "RFB_CLIENT_PORT="); - putenv(env_rfb_client_port); - - sprintf(env_rfb_x11vnc_pid, "RFB_X11VNC_PID="); - putenv(env_rfb_x11vnc_pid); - - return rc; -} - -/* - * process a "yes:0,no:*,view:3" type action list comparing to command - * return code rc. * means the default action with no other match. - */ -int action_match(char *action, int rc) { - char *p, *q, *s = strdup(action); - int cases[4], i, result; - char *labels[4]; - - labels[1] = "yes"; - labels[2] = "no"; - labels[3] = "view"; - - rfbLog("accept_client: process action line: %s\n", - action); - - for (i=1; i <= 3; i++) { - cases[i] = -2; - } - - p = strtok(s, ","); - while (p) { - if ((q = strchr(p, ':')) != NULL) { - int in, k; - *q = '\0'; - q++; - if (strstr(p, "yes") == p) { - k = 1; - } else if (strstr(p, "no") == p) { - k = 2; - } else if (strstr(p, "view") == p) { - k = 3; - } else { - rfbLog("bad action line: %s\n", action); - clean_up_exit(1); - } - if (*q == '*') { - cases[k] = -1; - } else if (sscanf(q, "%d", &in) == 1) { - if (in < 0) { - rfbLog("bad action line: %s\n", action); - clean_up_exit(1); - } - cases[k] = in; - } else { - rfbLog("bad action line: %s\n", action); - clean_up_exit(1); - } - } else { - rfbLog("bad action line: %s\n", action); - clean_up_exit(1); - } - p = strtok(NULL, ","); - } - free(s); - - result = -1; - for (i=1; i <= 3; i++) { - if (cases[i] == -1) { - rfbLog("accept_client: default action is case=%d %s\n", - i, labels[i]); - result = i; - break; - } - } - if (result == -1) { - rfbLog("accept_client: no default action\n"); - } - for (i=1; i <= 3; i++) { - if (cases[i] >= 0 && cases[i] == rc) { - rfbLog("accept_client: matched action is case=%d %s\n", - i, labels[i]); - result = i; - break; - } - } - if (result < 0) { - rfbLog("no action match: %s rc=%d set to no\n", action, rc); - result = 2; - } - return result; -} - -/* - * Simple routine to prompt the user on the X display whether an incoming - * client should be allowed to connect or not. If a gui is involved it - * will be running in the environment/context of the X11 DISPLAY. - * - * The command supplied via -accept is run as is (i.e. no string - * substitution) with the RFB_CLIENT_IP environment variable set to the - * incoming client's numerical IP address. - * - * If the external command exits with 0 the client is accepted, otherwise - * the client is rejected. - * - * Some builtins are provided: - * - * xmessage: use homebrew xmessage(1) for the external command. - * popup: use internal X widgets for prompting. - * - */ -int accept_client(rfbClientPtr client) { - - char xmessage[200], *cmd = NULL; - char *addr = client->host; - char *action = NULL; - - if (accept_cmd == NULL) { - return 1; /* no command specified, so we accept */ - } - - if (addr == NULL || addr[0] == '\0') { - addr = "unknown-host"; - } - - if (strstr(accept_cmd, "popup") == accept_cmd) { - /* use our builtin popup button */ - - /* (popup|popupkey|popupmouse)[+-X+-Y][:timeout] */ - - int ret, timeout = 120; - int x = -64000, y = -64000; - char *p, *mode; - - /* extract timeout */ - if ((p = strchr(accept_cmd, ':')) != NULL) { - int in; - if (sscanf(p+1, "%d", &in) == 1) { - timeout = in; - } - } - /* extract geometry */ - if ((p = strpbrk(accept_cmd, "+-")) != NULL) { - int x1, y1; - if (sscanf(p, "+%d+%d", &x1, &y1) == 2) { - x = x1; - y = y1; - } else if (sscanf(p, "+%d-%d", &x1, &y1) == 2) { - x = x1; - y = -y1; - } else if (sscanf(p, "-%d+%d", &x1, &y1) == 2) { - x = -x1; - y = y1; - } else if (sscanf(p, "-%d-%d", &x1, &y1) == 2) { - x = -x1; - y = -y1; - } - } - - /* find mode: mouse, key, or both */ - if (strstr(accept_cmd, "popupmouse") == accept_cmd) { - mode = "mouse_only"; - } else if (strstr(accept_cmd, "popupkey") == accept_cmd) { - mode = "key_only"; - } else { - mode = "both"; - } - - rfbLog("accept_client: using builtin popup for: %s\n", addr); - if ((ret = ugly_accept_window(addr, x, y, timeout, mode))) { - if (ret == 2) { - rfbLog("accept_client: viewonly: %s\n", addr); - client->viewOnly = TRUE; - } - rfbLog("accept_client: popup accepted: %s\n", addr); - return 1; - } else { - rfbLog("accept_client: popup rejected: %s\n", addr); - return 0; - } - - } else if (!strcmp(accept_cmd, "xmessage")) { - /* make our own command using xmessage(1) */ - - if (view_only) { - sprintf(xmessage, "xmessage -buttons yes:0,no:2 -center" - " 'x11vnc: accept connection from %s?'", addr); - } else { - sprintf(xmessage, "xmessage -buttons yes:0,no:2," - "view-only:3 -center" " 'x11vnc: accept connection" - " from %s?'", addr); - action = "yes:0,no:*,view:3"; - } - cmd = xmessage; - - } else { - /* use the user supplied command: */ - - cmd = accept_cmd; - - /* extract any action prefix: yes:N,no:M,view:K */ - if (strstr(accept_cmd, "yes:") == accept_cmd) { - char *p; - if ((p = strpbrk(accept_cmd, " \t")) != NULL) { - int i; - cmd = p; - p = accept_cmd; - for (i=0; i<200; i++) { - if (*p == ' ' || *p == '\t') { - xmessage[i] = '\0'; - break; - } - xmessage[i] = *p; - p++; - } - xmessage[200-1] = '\0'; - action = xmessage; - } - } - } - - if (cmd) { - int rc; - - rfbLog("accept_client: using cmd for: %s\n", addr); - rc = run_user_command(cmd, client); - - if (action) { - int result; - - if (rc < 0) { - rfbLog("accept_client: cannot use negative " - "rc: %d, action %s\n", rc, action); - result = 2; - } else { - result = action_match(action, rc); - } - - if (result == 1) { - rc = 0; - } else if (result == 2) { - rc = 1; - } else if (result == 3) { - rc = 0; - rfbLog("accept_client: viewonly: %s\n", addr); - client->viewOnly = TRUE; - } else { - rc = 1; /* NOTREACHED */ - } - } - - if (rc == 0) { - rfbLog("accept_client: accepted: %s\n", addr); - return 1; - } else { - rfbLog("accept_client: rejected: %s\n", addr); - return 0; - } - } else { - rfbLog("accept_client: no command, rejecting %s\n", addr); - return 0; - } - - return 0; /* NOTREACHED */ -} - -/* - * For the -connect option: periodically read the file looking for - * a connect string. If one is found set client_connect to it. - */ -void check_connect_file(char *file) { - FILE *in; - char line[512], host[512]; - static int first_warn = 1, truncate_ok = 1; - static time_t last_time = 0; - time_t now = time(0); - - if (now - last_time < 1) { - /* check only once a second */ - return; - } - last_time = now; - - if (! truncate_ok) { - /* check if permissions changed */ - if (access(file, W_OK) == 0) { - truncate_ok = 1; - } else { - return; - } - } - - in = fopen(file, "r"); - if (in == NULL) { - if (first_warn) { - rfbLog("check_connect_file: fopen failure: %s\n", file); - perror("fopen"); - first_warn = 0; - } - return; - } - - if (fgets(line, 512, in) != NULL) { - if (sscanf(line, "%s", host) == 1) { - if (strlen(host) > 0) { - client_connect = strdup(host); - rfbLog("read connect file: %s\n", host); - } - } - } - fclose(in); - - /* truncate file */ - in = fopen(file, "w"); - if (in != NULL) { - fclose(in); - } else { - /* disable if we cannot truncate */ - rfbLog("check_connect_file: could not truncate %s, " - "disabling checking.\n", file); - truncate_ok = 0; - } -} - -/* Do a reverse connect for a single "host" or "host:port" */ -int do_reverse_connect(char *str) { - rfbClientPtr cl; - char *host, *p; - int port = 5500, len = strlen(str); - - if (len < 1) { - return 0; - } - if (len > 512) { - rfbLog("reverse_connect: string too long: %d bytes\n", len); - return 0; - } - - /* copy in to host */ - host = (char *) malloc((size_t) len+1); - if (! host) { - rfbLog("reverse_connect: could not malloc string %d\n", len); - return 0; - } - strncpy(host, str, len); - host[len] = '\0'; - - /* extract port, if any */ - if ((p = strchr(host, ':')) != NULL) { - port = atoi(p+1); - *p = '\0'; - } - - cl = rfbReverseConnection(screen, host, port); - free(host); - - if (cl == NULL) { - rfbLog("reverse_connect: %s failed\n", str); - return 0; - } else { - rfbLog("reverse_connect: %s/%s OK\n", str, cl->host); - return 1; - } -} - -void rfbPE(rfbScreenInfoPtr scr, long us) { - if (! use_threads) { - return rfbProcessEvents(scr, us); - } -} - -/* break up comma separated list of hosts and call do_reverse_connect() */ - -void reverse_connect(char *str) { - char *p, *tmp = strdup(str); - int sleep_between_host = 300; - int sleep_min = 1500, sleep_max = 4500, n_max = 5; - int n, tot, t, dt = 100, cnt = 0; - - p = strtok(tmp, ","); - while (p) { - if ((n = do_reverse_connect(p)) != 0) { - rfbPE(screen, -1); - } - cnt += n; - - p = strtok(NULL, ","); - if (p) { - t = 0; - while (t < sleep_between_host) { - usleep(dt * 1000); - rfbPE(screen, -1); - t += dt; - } - } - } - free(tmp); - - if (cnt == 0) { - return; - } - - /* - * XXX: we need to process some of the initial handshaking - * events, otherwise the client can get messed up (why??) - * so we send rfbProcessEvents() all over the place. - */ - - n = cnt; - if (n >= n_max) { - n = n_max; - } - t = sleep_max - sleep_min; - tot = sleep_min + ((n-1) * t) / (n_max-1); - - t = 0; - while (t < tot) { - rfbPE(screen, -1); - usleep(dt * 1000); - t += dt; - } -} - -/* check if client_connect has been set, if so make the reverse connections. */ - -void send_client_connect() { - if (client_connect != NULL) { - reverse_connect(client_connect); - free(client_connect); - client_connect = NULL; - } -} - -/* string for the VNC_CONNECT property */ -#define VNC_CONNECT_MAX 512 -char vnc_connect_str[VNC_CONNECT_MAX+1]; - -/* monitor the various input methods */ -void check_connect_inputs() { - - /* flush any already set: */ - send_client_connect(); - - /* connect file: */ - if (client_connect_file != NULL) { - check_connect_file(client_connect_file); - } - send_client_connect(); - - /* VNC_CONNECT property (vncconnect program) */ - if (vnc_connect && *vnc_connect_str != '\0') { - client_connect = strdup(vnc_connect_str); - vnc_connect_str[0] = '\0'; - } - send_client_connect(); -} - -/* - * libvncserver callback for when a new client connects - */ -enum rfbNewClientAction new_client(rfbClientPtr client) { - last_event = last_input = time(0); - - if (inetd) { - /* - * Set this so we exit as soon as connection closes, - * otherwise client_gone is only called after RFB_CLIENT_ACCEPT - */ - client->clientGoneHook = client_gone; - } - - if (connect_once) { - if (screen->rfbDontDisconnect && screen->rfbNeverShared) { - if (! shared && accepted_client) { - rfbLog("denying additional client: %s\n", - client->host); - return(RFB_CLIENT_REFUSE); - } - } - } - if (! check_access(client->host)) { - rfbLog("denying client: %s does not match %s\n", client->host, - allow_list ? allow_list : "(null)" ); - return(RFB_CLIENT_REFUSE); - } - if (! accept_client(client)) { - rfbLog("denying client: %s local user rejected connection.\n", - client->host); - rfbLog("denying client: accept_cmd=\"%s\"\n", - accept_cmd ? accept_cmd : "(null)" ); - return(RFB_CLIENT_REFUSE); - } - - if (view_only) { - client->clientData = (void *) -1; - client->viewOnly = TRUE; - } else { - client->clientData = (void *) 0; - } - - client->clientGoneHook = client_gone; - client_count++; - - accepted_client = 1; - last_client = time(0); - - return(RFB_CLIENT_ACCEPT); -} - -/* - * For tweaking modifiers wrt the Alt-Graph key, etc. - */ -#define LEFTSHIFT 1 -#define RIGHTSHIFT 2 -#define ALTGR 4 -char mod_state = 0; - -char modifiers[0x100]; -KeyCode keycodes[0x100], left_shift_code, right_shift_code, altgr_code; - -void initialize_modtweak() { - KeySym key, *keymap; - int i, j, minkey, maxkey, syms_per_keycode; - - memset(modifiers, -1, sizeof(modifiers)); - - XDisplayKeycodes(dpy, &minkey, &maxkey); - - keymap = XGetKeyboardMapping(dpy, minkey, (maxkey - minkey + 1), - &syms_per_keycode); - - /* handle alphabetic char with only one keysym (no upper + lower) */ - for (i = minkey; i <= maxkey; i++) { - KeySym lower, upper; - /* 2nd one */ - key = keymap[(i - minkey) * syms_per_keycode + 1]; - if (key != NoSymbol) { - continue; - } - /* 1st one */ - key = keymap[(i - minkey) * syms_per_keycode + 0]; - if (key == NoSymbol) { - continue; - } - XConvertCase(key, &lower, &upper); - if (lower != upper) { - keymap[(i - minkey) * syms_per_keycode + 0] = lower; - keymap[(i - minkey) * syms_per_keycode + 1] = upper; - } - } - for (i = minkey; i <= maxkey; i++) { - for (j = 0; j < syms_per_keycode; j++) { - key = keymap[ (i - minkey) * syms_per_keycode + j ]; - if ( key >= ' ' && key < 0x100 - && i == XKeysymToKeycode(dpy, key) ) { - keycodes[key] = i; - modifiers[key] = j; - } - } - } - - left_shift_code = XKeysymToKeycode(dpy, XK_Shift_L); - right_shift_code = XKeysymToKeycode(dpy, XK_Shift_R); - altgr_code = XKeysymToKeycode(dpy, XK_Mode_switch); - - XFree ((void *) keymap); -} - -/* - * The following is for an experimental -remap option to allow the user - * to remap keystrokes. It is currently confusing wrt modifiers... - */ -typedef struct keyremap { - KeySym before; - KeySym after; - int isbutton; - struct keyremap *next; -} keyremap_t; - -keyremap_t *keyremaps = NULL; - -void initialize_remap(char *infile) { - FILE *in; - char *p, *q, line[256], str1[256], str2[256]; - int i; - KeySym ksym1, ksym2; - keyremap_t *remap, *current; - - in = fopen(infile, "r"); - if (in == NULL) { - /* assume cmd line key1-key2,key3-key4 */ - if (! strchr(infile, '-') || (in = tmpfile()) == NULL) { - rfbLog("remap: cannot open: %s\n", infile); - perror("fopen"); - clean_up_exit(1); - } - p = infile; - while (*p) { - if (*p == '-') { - fprintf(in, " "); - } else if (*p == ',') { - fprintf(in, "\n"); - } else { - fprintf(in, "%c", *p); - } - p++; - } - fprintf(in, "\n"); - fflush(in); - rewind(in); - } - while (fgets(line, 256, in) != NULL) { - int blank = 1, isbtn = 0; - p = line; - while (*p) { - if (! isspace(*p)) { - blank = 0; - break; - } - p++; - } - if (blank) { - continue; - } - if (strchr(line, '#')) { - continue; - } - if ( (q = strchr(line, '-')) != NULL) { - /* allow Keysym1-Keysym2 notation */ - *q = ' '; - } - - if (sscanf(line, "%s %s", str1, str2) != 2) { - rfbLog("remap: bad line: %s\n", line); - fclose(in); - clean_up_exit(1); - } - if (sscanf(str1, "0x%x", &i) == 1) { - ksym1 = (KeySym) i; - } else { - ksym1 = XStringToKeysym(str1); - } - if (sscanf(str2, "0x%x", &i) == 1) { - ksym2 = (KeySym) i; - } else { - ksym2 = XStringToKeysym(str2); - } - if (ksym2 == NoSymbol) { - int i; - if (sscanf(str2, "Button%d", &i) == 1) { - ksym2 = (KeySym) i; - isbtn = 1; - } - } - if (ksym1 == NoSymbol || ksym2 == NoSymbol) { - rfbLog("warning: skipping bad remap line: %s", line); - continue; - } - remap = (keyremap_t *) malloc((size_t) sizeof(keyremap_t)); - remap->before = ksym1; - remap->after = ksym2; - remap->isbutton = isbtn; - remap->next = NULL; - rfbLog("remapping: (%s, 0x%x) -> (%s, 0x%x) isbtn=%d\n", str1, - ksym1, str2, ksym2, isbtn); - if (keyremaps == NULL) { - keyremaps = remap; - } else { - current->next = remap; - - } - current = remap; - } - fclose(in); -} - -void DebugXTestFakeKeyEvent(Display* dpy, KeyCode key, Bool down, time_t cur_time) -{ - if (debug_keyboard) { - rfbLog("XTestFakeKeyEvent(dpy, keycode=0x%x \"%s\", %s)\n", - key, XKeysymToString(XKeycodeToKeysym(dpy, key, 0)), - down ? "down":"up"); - } - XTestFakeKeyEvent(dpy, key, down, cur_time); -} - -/* - * This is to allow debug_keyboard option trap everything: - */ -#define XTestFakeKeyEvent DebugXTestFakeKeyEvent - -void tweak_mod(signed char mod, rfbBool down) { - rfbBool is_shift = mod_state & (LEFTSHIFT|RIGHTSHIFT); - Bool dn = (Bool) down; - - if (mod < 0) { - if (debug_keyboard) { - rfbLog("tweak_mod: Skip: down=%d mod=0x%x\n", down, - (int) mod); - } - return; - } - if (debug_keyboard) { - rfbLog("tweak_mod: Start: down=%d mod=0x%x mod_state=0x%x" - " is_shift=%d\n", down, (int) mod, (int) mod_state, - is_shift); - } - - X_LOCK; - if (is_shift && mod != 1) { - if (mod_state & LEFTSHIFT) { - XTestFakeKeyEvent(dpy, left_shift_code, !dn, CurrentTime); - } - if (mod_state & RIGHTSHIFT) { - XTestFakeKeyEvent(dpy, right_shift_code, !dn, CurrentTime); - } - } - if ( ! is_shift && mod == 1 ) { - XTestFakeKeyEvent(dpy, left_shift_code, dn, CurrentTime); - } - if ( altgr_code && (mod_state & ALTGR) && mod != 2 ) { - XTestFakeKeyEvent(dpy, altgr_code, !dn, CurrentTime); - } - if ( altgr_code && ! (mod_state & ALTGR) && mod == 2 ) { - XTestFakeKeyEvent(dpy, altgr_code, dn, CurrentTime); - } - X_UNLOCK; - if (debug_keyboard) { - rfbLog("tweak_mod: Finish: down=%d mod=0x%x mod_state=0x%x" - " is_shift=%d\n", down, (int) mod, (int) mod_state, - is_shift); - } -} - -static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, - rfbClientPtr client) { - KeyCode k; - int tweak = 0; - if (debug_keyboard) { - rfbLog("modifier_tweak_keyboard: %s keysym=0x%x\n", - down ? "down" : "up", (int) keysym); - } - - if (view_only) { - return; - } - if (client->viewOnly) { - return; - } - -#define ADJUSTMOD(sym, state) \ - if (keysym == sym) { \ - if (down) { \ - mod_state |= state; \ - } else { \ - mod_state &= ~state; \ - } \ - } - - ADJUSTMOD(XK_Shift_L, LEFTSHIFT) - ADJUSTMOD(XK_Shift_R, RIGHTSHIFT) - ADJUSTMOD(XK_Mode_switch, ALTGR) - - if ( down && keysym >= ' ' && keysym < 0x100 ) { - tweak = 1; - tweak_mod(modifiers[keysym], True); - k = keycodes[keysym]; - } else { - X_LOCK; - k = XKeysymToKeycode(dpy, (KeySym) keysym); - X_UNLOCK; - } - if (debug_keyboard) { - rfbLog("modifier_tweak_keyboard: KeySym 0x%x \"%s\" -> " - "KeyCode 0x%x%s\n", (int) keysym, XKeysymToString(keysym), - (int) k, k ? "" : " *ignored*"); - } - if ( k != NoSymbol ) { - X_LOCK; - XTestFakeKeyEvent(dpy, k, (Bool) down, CurrentTime); - X_UNLOCK; - } - - if ( tweak ) { - tweak_mod(modifiers[keysym], False); - } -} - -/* - * key event handler. See the above functions for contortions for - * running under -modtweak. - */ -rfbClientPtr last_keyboard_client = NULL; -int num_buttons = -1; - -static void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { - KeyCode k; - int isbutton = 0; - - if (debug_keyboard) { - X_LOCK; - rfbLog("keyboard(%s, 0x%x \"%s\")\n", down ? "down":"up", - (int) keysym, XKeysymToString(keysym)); - X_UNLOCK; - } - - if (view_only) { - return; - } - if (client->viewOnly) { - return; - } - last_keyboard_client = client; - - if (keyremaps) { - keyremap_t *remap = keyremaps; - while (remap != NULL) { - if (remap->before == keysym) { - keysym = remap->after; - isbutton = remap->isbutton; - if (debug_keyboard) { - X_LOCK; - rfbLog("keyboard(): remapping keysym: " - "0x%x \"%s\" -> 0x%x \"%s\"\n", - (int) remap->before, - XKeysymToString(remap->before), - (int) remap->after, - remap->isbutton ? "button" : - XKeysymToString(remap->after)); - X_UNLOCK; - } - break; - } - remap = remap->next; - } - } - - if (isbutton) { - int button = (int) keysym; - if (! down) { - return; /* nothing to send */ - } - if (debug_keyboard) { - rfbLog("keyboard(): remapping keystroke to button %d" - " click\n", button); - } - if (button < 1 || button > num_buttons) { - rfbLog("keyboard(): ignoring mouse button out of " - "bounds: %d\n", button); - return; - } - X_LOCK; - XTestFakeButtonEvent(dpy, button, True, CurrentTime); - XTestFakeButtonEvent(dpy, button, False, CurrentTime); - XFlush(dpy); - X_UNLOCK; - return; - } - - if (use_modifier_tweak) { - modifier_tweak_keyboard(down, keysym, client); - X_LOCK; - XFlush(dpy); - X_UNLOCK; - return; - } - - X_LOCK; - - k = XKeysymToKeycode(dpy, (KeySym) keysym); - - if (debug_keyboard) { - rfbLog("keyboard(): KeySym 0x%x \"%s\" -> KeyCode 0x%x%s\n", - (int) keysym, XKeysymToString(keysym), (int) k, - k ? "" : " *ignored*"); - } - - if ( k != NoSymbol ) { - XTestFakeKeyEvent(dpy, k, (Bool) down, CurrentTime); - XFlush(dpy); - - last_event = last_input = time(0); - got_user_input++; - got_keyboard_input++; - } - - X_UNLOCK; -} - -/* - * pointer event handling routines. - */ -typedef struct ptrremap { - KeySym keysym; - KeyCode keycode; - int end; - int button; - int down; - int up; -} prtremap_t; - -MUTEX(pointerMutex); -#define MAX_BUTTONS 5 -#define MAX_BUTTON_EVENTS 50 -prtremap_t pointer_map[MAX_BUTTONS+1][MAX_BUTTON_EVENTS]; -char *pointer_remap = NULL; -void update_pointer(int, int, int); - - -/* based on IsModifierKey in Xutil.h */ -#define ismodkey(keysym) \ - ((((KeySym)(keysym) >= XK_Shift_L) && ((KeySym)(keysym) <= XK_Hyper_R))) - - -/* - * For parsing the -buttonmap sections, e.g. "4" or ":Up+Up+Up:" - */ -void buttonparse(int from, char **s) { - char *q; - int to, i; - int modisdown[256]; - - q = *s; - - for (i=0; i<256; i++) { - modisdown[i] = 0; - } - - if (*q == ':') { - /* :sym1+sym2+...+symN: format */ - int l = 0, n = 0; - char list[1000]; - char *t, *kp = q + 1; - KeyCode kcode; - - while (*(kp+l) != ':' && *(kp+l) != '\0') { - /* loop to the matching ':' */ - l++; - if (l >= 1000) { - rfbLog("buttonparse: keysym list too long: " - "%s\n", q); - break; - } - } - *(kp+l) = '\0'; - strncpy(list, kp, l); - list[l] = '\0'; - rfbLog("remap button %d using \"%s\"\n", from, list); - - /* loop over tokens separated by '+' */ - t = strtok(list, "+"); - while (t) { - KeySym ksym; - int i; - if (n >= MAX_BUTTON_EVENTS - 20) { - rfbLog("buttonparse: too many button map " - "events: %s\n", list); - break; - } - if (sscanf(t, "0x%x", &i) == 1) { - ksym = (KeySym) i; /* hex value */ - } else { - ksym = XStringToKeysym(t); /* string value */ - } - if (ksym == NoSymbol) { - /* see if Button "keysym" was used: */ - if (sscanf(t, "Button%d", &i) == 1) { - rfbLog(" event %d: button %d\n", - from, n+1, i); - if (i == 0) i = -1; /* bah */ - pointer_map[from][n].keysym = NoSymbol; - pointer_map[from][n].keycode = NoSymbol; - pointer_map[from][n].button = i; - pointer_map[from][n].end = 0; - pointer_map[from][n].down = 0; - pointer_map[from][n].up = 0; - } else { - rfbLog("buttonparse: ignoring unknown " - "keysym: %s\n", t); - n--; - } - } else { - kcode = XKeysymToKeycode(dpy, ksym); - - pointer_map[from][n].keysym = ksym; - pointer_map[from][n].keycode = kcode; - pointer_map[from][n].button = 0; - pointer_map[from][n].end = 0; - if (! ismodkey(ksym) ) { - /* do both down then up */ - pointer_map[from][n].down = 1; - pointer_map[from][n].up = 1; - } else { - if (modisdown[kcode]) { - pointer_map[from][n].down = 0; - pointer_map[from][n].up = 1; - modisdown[kcode] = 0; - } else { - pointer_map[from][n].down = 1; - pointer_map[from][n].up = 0; - modisdown[kcode] = 1; - } - } - rfbLog(" event %d: keysym %s (0x%x) -> " - "keycode 0x%x down=%d up=%d\n", n+1, - XKeysymToString(ksym), ksym, kcode, - pointer_map[from][n].down, - pointer_map[from][n].up); - } - t = strtok(NULL, "+"); - n++; - } - - /* we must release any modifiers that are still down: */ - for (i=0; i<256; i++) { - kcode = (KeyCode) i; - if (n >= MAX_BUTTON_EVENTS) { - rfbLog("buttonparse: too many button map " - "events: %s\n", list); - break; - } - if (modisdown[kcode]) { - pointer_map[from][n].keysym = NoSymbol; - pointer_map[from][n].keycode = kcode; - pointer_map[from][n].button = 0; - pointer_map[from][n].end = 0; - pointer_map[from][n].down = 0; - pointer_map[from][n].up = 1; - modisdown[kcode] = 0; - n++; - } - } - - /* advance the source pointer position */ - (*s) += l+2; - } else { - /* single digit format */ - char str[2]; - str[0] = *q; - str[1] = '\0'; - - to = atoi(str); - if (to < 1) { - rfbLog("skipping invalid remap button \"%d\" for button" - " %d from string \"%s\"\n", - to, from, str); - } else { - rfbLog("remap button %d using \"%s\"\n", from, str); - rfbLog(" button: %d -> %d\n", from, to); - pointer_map[from][0].keysym = NoSymbol; - pointer_map[from][0].keycode = NoSymbol; - pointer_map[from][0].button = to; - pointer_map[from][0].end = 0; - pointer_map[from][0].down = 0; - pointer_map[from][0].up = 0; - } - /* advance the source pointer position */ - (*s)++; - } -} - -void initialize_pointer_map(void) { - unsigned char map[MAX_BUTTONS]; - int i, k; - /* - * This routine counts the number of pointer buttons on the X - * server (to avoid problems, even crashes, if a client has more - * buttons). And also initializes any pointer button remapping - * from -buttonmap option. - */ - - X_LOCK; - num_buttons = XGetPointerMapping(dpy, map, MAX_BUTTONS); - - if (num_buttons < 0) { - num_buttons = 0; - } - - /* FIXME: should use info in map[] */ - for (i=1; i<= MAX_BUTTONS; i++) { - for (k=0; k < MAX_BUTTON_EVENTS; k++) { - pointer_map[i][k].end = 1; - } - pointer_map[i][0].keysym = NoSymbol; - pointer_map[i][0].keycode = NoSymbol; - pointer_map[i][0].button = i; - pointer_map[i][0].end = 0; - pointer_map[i][0].down = 0; - pointer_map[i][0].up = 0; - } - - if (pointer_remap) { - /* -buttonmap, format is like: 12-21=2 */ - char *p, *q, *remap = pointer_remap; - int n; - - if ((p = strchr(remap, '=')) != NULL) { - /* undocumented max button number */ - n = atoi(p+1); - *p = '\0'; - if (n < num_buttons || num_buttons == 0) { - num_buttons = n; - } else { - rfbLog("warning: increasing number of mouse " - "buttons from %d to %d\n", num_buttons, n); - num_buttons = n; - } - } - if ((q = strchr(remap, '-')) != NULL) { - /* - * The '-' separates the 'from' and 'to' lists, - * then it is kind of like tr(1). - */ - char str[2]; - int from; - - rfbLog("remapping pointer buttons using string:\n"); - rfbLog(" \"%s\"\n", remap); - - p = remap; - q++; - i = 0; - str[1] = '\0'; - while (*p != '-') { - str[0] = *p; - from = atoi(str); - buttonparse(from, &q); - p++; - } - } - } - X_UNLOCK; -} - -/* - * Actual callback from libvncserver when it gets a pointer event. - */ -rfbClientPtr last_pointer_client = NULL; - -static void pointer(int mask, int x, int y, rfbClientPtr client) { - - if (debug_pointer && mask >= 0) { - rfbLog("pointer(mask: 0x%x, x:%4d, y:%4d)\n", mask, x, y); - } - - if (view_only) { - return; - } - if (client->viewOnly) { - return; - } - - if (mask >= 0) { - /* - * mask = -1 is a special case call from scan_for_updates() - * to flush the event queue; there is no real pointer event. - */ - got_user_input++; - got_pointer_input++; - last_pointer_client = client; - } - - /* - * The following is hopefully an improvement wrt response during - * pointer user input (window drags) for the threaded case. - * See check_user_input() for the more complicated things we do - * in the non-threaded case. - */ - if (use_threads && ! old_pointer) { -# define NEV 32 - /* storage for the event queue */ - static int mutex_init = 0; - static int nevents = 0; - static int ev[NEV][3]; - int i; - /* timer things */ - static double dt = 0.0, tmr = 0.0, maxwait = 0.4; - - if (! mutex_init) { - INIT_MUTEX(pointerMutex); - mutex_init = 1; - } - - LOCK(pointerMutex); - - /* - * If the framebuffer is being copied in another thread - * (scan_for_updates()), we will queue up to 32 pointer - * events for later. The idea is by delaying these input - * events, the screen is less likely to change during the - * copying period, and so will give rise to less window - * "tearing". - * - * Tearing is not completely eliminated because we do - * not suspend work in the other libvncserver threads. - * Maybe that is a possibility with a mutex... - */ - if (fb_copy_in_progress && mask >= 0) { - /* - * mask = -1 is an all-clear signal from - * scan_for_updates(). - * - * dt is a timer in seconds; we only queue for so long. - */ - dt += dtime(&tmr); - - if (nevents < NEV && dt < maxwait) { - i = nevents++; - ev[i][0] = mask; - ev[i][1] = x; - ev[i][2] = y; - UNLOCK(pointerMutex); - if (debug_pointer) { - rfbLog("pointer(): deferring event " - "%d\n", i); - } - return; - } - } - - /* time to send the queue */ - for (i=0; i maxwait) { - X_LOCK; - XFlush(dpy); - X_UNLOCK; - } - nevents = 0; /* reset everything */ - tmr = 0.0; - dt = 0.0; - dtime(&tmr); - - UNLOCK(pointerMutex); - } - if (mask < 0) { /* -1 just means flush the event queue */ - if (debug_pointer > 1) { - rfbLog("pointer(): flush only.\n"); - } - return; - } - - /* update the X display with the event: */ - update_pointer(mask, x, y); -} - -/* - * Send a pointer event to the X server. - */ - -void update_pointer(int mask, int x, int y) { - int i, mb; - - X_LOCK; - - if (! use_xwarppointer) { - XTestFakeMotionEvent(dpy, scr, x+off_x, y+off_y, CurrentTime); - } else { - XWarpPointer(dpy, None, window, 0, 0, 0, 0, x+off_x, y+off_y); - } - - cursor_x = x; - cursor_y = y; - - last_event = last_input = time(0); - - for (i=0; i < MAX_BUTTONS; i++) { - /* look for buttons that have be clicked or released: */ - if ( (button_mask & (1< " - "0x%x button: %d\n", button_mask, mask,i+1); - } - for (k=0; k < MAX_BUTTON_EVENTS; k++) { - int bmask = (mask & (1< num_buttons) - || mb < 1) { - rfbLog("ignoring mouse button out of " - "bounds: %d>%d mask: 0x%x -> 0x%x\n", - mb, num_buttons, button_mask, mask); - continue; - } - if (debug_pointer) { - rfbLog("pointer(): sending button %d" - " %s (event %d)\n", mb, bmask - ? "down" : "up", k+1); - } - XTestFakeButtonEvent(dpy, mb, (mask & (1<xkb_type == XkbBellNotify) { - got_bell = 1; - } - X_UNLOCK; - - if (got_bell) { - if (! all_clients_initialized()) { - rfbLog("watch_bell_event: not sending bell: " - "uninitialized clients\n"); - } else { - rfbSendBell(screen); - } - } -} -#else -void watch_bell_event() {} -#endif - - -/* - * Selection/Cutbuffer/Clipboard handlers. - */ - -int watch_selection = 1; /* normal selection/cutbuffer maintenance */ -int watch_primary = 1; /* more dicey, poll for changes in PRIMARY */ -int own_selection = 0; /* whether we currently own PRIMARY or not */ -int set_cutbuffer = 0; /* to avoid bouncing the CutText right back */ -int sel_waittime = 15; /* some seconds to skip before first send */ -Window selwin; /* special window for our selection */ - -/* - * This is where we keep our selection: the string sent TO us from VNC - * clients, and the string sent BY us to requesting X11 clients. - */ -char *xcut_string = NULL; - -/* - * Our callbacks instruct us to check for changes in the cutbuffer - * and PRIMARY selection on the local X11 display. - * - * We store the new cutbuffer and/or PRIMARY selection data in this - * constant sized array selection_str[]. - * TODO: check if malloc does not cause performance issues (esp. WRT - * SelectionNotify handling). - */ -#define PROP_MAX (131072L) -char selection_str[PROP_MAX+1]; - -/* - * An X11 (not VNC) client on the local display has requested the selection - * from us (because we are the current owner). - * - * n.b.: our caller already has the X_LOCK. - */ -void selection_request(XEvent *ev) { - XSelectionEvent notify_event; - XSelectionRequestEvent *req_event; - unsigned int length; - unsigned char *data; -#ifndef XA_LENGTH - unsigned long XA_LENGTH = XInternAtom(dpy, "LENGTH", True); -#endif - - req_event = &(ev->xselectionrequest); - notify_event.type = SelectionNotify; - notify_event.display = req_event->display; - notify_event.requestor = req_event->requestor; - notify_event.selection = req_event->selection; - notify_event.target = req_event->target; - notify_event.time = req_event->time; - - if (req_event->property == None) { - notify_event.property = req_event->target; - } else { - notify_event.property = req_event->property; - } - if (xcut_string) { - length = strlen(xcut_string); - } else { - length = 0; - } - - - if (ev->xselectionrequest.target == XA_LENGTH) { - /* length request */ - - XChangeProperty(ev->xselectionrequest.display, - ev->xselectionrequest.requestor, - ev->xselectionrequest.property, - ev->xselectionrequest.target, 32, PropModeReplace, - (unsigned char *) &length, sizeof(unsigned int)); - - } else { - /* data request */ - - data = (unsigned char *)xcut_string; - - XChangeProperty(ev->xselectionrequest.display, - ev->xselectionrequest.requestor, - ev->xselectionrequest.property, - ev->xselectionrequest.target, 8, PropModeReplace, - data, length); - } - - XSendEvent(req_event->display, req_event->requestor, False, 0, - (XEvent *)¬ify_event); - - XFlush(dpy); -} - -int all_clients_initialized() { - rfbClientIteratorPtr iter; - rfbClientPtr cl; - int ok = 1; - - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) { - if (cl->state != RFB_NORMAL) { - ok = 0; - break; - } - } - rfbReleaseClientIterator(iter); - - return ok; -} - -/* - * CUT_BUFFER0 property on the local display has changed, we read and - * store it and send it out to any connected VNC clients. - * - * n.b.: our caller already has the X_LOCK. - */ -void cutbuffer_send() { - Atom type; - int format, slen, dlen; - unsigned long nitems = 0, bytes_after = 0; - unsigned char* data = NULL; - - selection_str[0] = '\0'; - slen = 0; - - /* read the property value into selection_str: */ - do { - if (XGetWindowProperty(dpy, DefaultRootWindow(dpy), - XA_CUT_BUFFER0, nitems/4, PROP_MAX/16, False, - AnyPropertyType, &type, &format, &nitems, &bytes_after, - &data) == Success) { - - dlen = nitems * (format/8); - if (slen + dlen > PROP_MAX) { - /* too big */ - rfbLog("warning: truncating large CUT_BUFFER0" - " selection > %d bytes.\n", PROP_MAX); - XFree(data); - break; - } - memcpy(selection_str+slen, data, dlen); - slen += dlen; - selection_str[slen] = '\0'; - XFree(data); - } - } while (bytes_after > 0); - - selection_str[PROP_MAX] = '\0'; - - if (! all_clients_initialized()) { - rfbLog("cutbuffer_send: no send: uninitialized clients\n"); - return; /* some clients initializing, cannot send */ - } - - /* now send it to any connected VNC clients (rfbServerCutText) */ - rfbSendServerCutText(screen, selection_str, strlen(selection_str)); -} - -/* - * "callback" for our SelectionNotify polling. We try to determine if - * the PRIMARY selection has changed (checking length and first CHKSZ bytes) - * and if it has we store it and send it off to any connected VNC clients. - * - * n.b.: our caller already has the X_LOCK. - * - * TODO: if we were willing to use libXt, we could perhaps get selection - * timestamps to speed up the checking... XtGetSelectionValue(). - */ -#define CHKSZ 32 -void selection_send(XEvent *ev) { - Atom type; - int format, slen, dlen, oldlen, newlen, toobig = 0; - static int err = 0, sent_one = 0; - char before[CHKSZ], after[CHKSZ]; - unsigned long nitems = 0, bytes_after = 0; - unsigned char* data = NULL; - - /* - * remember info about our last value of PRIMARY (or CUT_BUFFER0) - * so we can check for any changes below. - */ - oldlen = strlen(selection_str); - strncpy(before, selection_str, CHKSZ); - - selection_str[0] = '\0'; - slen = 0; - - /* read in the current value of PRIMARY: */ - do { - if (XGetWindowProperty(dpy, ev->xselection.requestor, - ev->xselection.property, nitems/4, PROP_MAX/16, True, - AnyPropertyType, &type, &format, &nitems, &bytes_after, - &data) == Success) { - - dlen = nitems * (format/8); - if (slen + dlen > PROP_MAX) { - /* too big */ - toobig = 1; - XFree(data); - if (err) { /* cut down on messages */ - break; - } else { - err = 5; - } - rfbLog("warning: truncating large PRIMARY" - " selection > %d bytes.\n", PROP_MAX); - break; - } - memcpy(selection_str+slen, data, dlen); - slen += dlen; - selection_str[slen] = '\0'; - XFree(data); - } - } while (bytes_after > 0); - - if (! toobig) { - err = 0; - } else if (err) { - err--; - } - - if (! sent_one) { - /* try to force a send first time in */ - oldlen = -1; - sent_one = 1; - } - - /* look for changes in the new value */ - newlen = strlen(selection_str); - strncpy(after, selection_str, CHKSZ); - - if (oldlen == newlen && strncmp(before, after, CHKSZ) == 0) { - /* evidently no change */ - return; - } - if (newlen == 0) { - /* do not bother sending a null string out */ - return; - } - - if (! all_clients_initialized()) { - rfbLog("selection_send: no send: uninitialized clients\n"); - return; /* some clients initializing, cannot send */ - } - - /* now send it to any connected VNC clients (rfbServerCutText) */ - rfbSendServerCutText(screen, selection_str, newlen); -} - - -/* - * Routines for monitoring the VNC_CONNECT property for changes. - * The vncconnect(1) will set it on our X display. - */ - -Atom vnc_connect_prop = None; - -void read_vnc_connect_prop() { - Atom type; - int format, slen, dlen; - unsigned long nitems = 0, bytes_after = 0; - unsigned char* data = NULL; - - vnc_connect_str[0] = '\0'; - slen = 0; - - if (! vnc_connect || vnc_connect_prop == None) { - /* not active or problem with VNC_CONNECT atom */ - return; - } - - /* read the property value into vnc_connect_str: */ - do { - if (XGetWindowProperty(dpy, DefaultRootWindow(dpy), - vnc_connect_prop, nitems/4, VNC_CONNECT_MAX/16, False, - AnyPropertyType, &type, &format, &nitems, &bytes_after, - &data) == Success) { - - dlen = nitems * (format/8); - if (slen + dlen > VNC_CONNECT_MAX) { - /* too big */ - rfbLog("warning: truncating large VNC_CONNECT" - " string > %d bytes.\n", VNC_CONNECT_MAX); - XFree(data); - break; - } - memcpy(vnc_connect_str+slen, data, dlen); - slen += dlen; - vnc_connect_str[slen] = '\0'; - XFree(data); - } - } while (bytes_after > 0); - - vnc_connect_str[VNC_CONNECT_MAX] = '\0'; - rfbLog("read property VNC_CONNECT: %s\n", vnc_connect_str); -} - -/* - * This routine is periodically called to check for selection related - * and other X11 events and respond to them as needed. - */ -void watch_xevents() { - XEvent xev; - static int first = 1, sent_sel = 0; - int have_clients = screen->rfbClientHead ? 1 : 0; - time_t last_request = 0, now = time(0); - - X_LOCK; - if (first && (watch_selection || vnc_connect)) { - /* - * register desired event(s) for notification. - * PropertyChangeMask is for CUT_BUFFER0 changes. - * TODO: does this cause a flood of other stuff? - */ - XSelectInput(dpy, rootwin, PropertyChangeMask); - } - if (first && watch_selection) { - /* create fake window for our selection ownership, etc */ - selwin = XCreateSimpleWindow(dpy, rootwin, 0, 0, 1, 1, 0, 0, 0); - } - if (first && vnc_connect) { - vnc_connect_str[0] = '\0'; - vnc_connect_prop = XInternAtom(dpy, "VNC_CONNECT", False); - } - first = 0; - - /* - * There is a bug where we have to wait before sending text to - * the client... so instead of sending right away we wait a - * the few seconds. - */ - if (have_clients && watch_selection && ! sent_sel - && now > last_client + sel_waittime) { - if (XGetSelectionOwner(dpy, XA_PRIMARY) == None) { - cutbuffer_send(); - } - sent_sel = 1; - } - - /* check for CUT_BUFFER0 and VNC_CONNECT changes: */ - if (XCheckTypedEvent(dpy, PropertyNotify, &xev)) { - if (xev.type == PropertyNotify) { - if (xev.xproperty.atom == XA_CUT_BUFFER0) { - /* - * Go retrieve CUT_BUFFER0 and send it. - * - * set_cutbuffer is a flag to try to avoid - * processing our own cutbuffer changes. - */ - if (have_clients && watch_selection - && ! set_cutbuffer) { - cutbuffer_send(); - sent_sel = 1; - } - set_cutbuffer = 0; - } else if (vnc_connect && vnc_connect_prop != None - && xev.xproperty.atom == vnc_connect_prop) { - - /* - * Go retrieve VNC_CONNECT string. - */ - read_vnc_connect_prop(); - } - } - } - - if (! have_clients || ! watch_selection) { - /* - * no need to monitor selections if no current clients - * or -nosel. - */ - X_UNLOCK; - return; - } - - /* check for our PRIMARY request notification: */ - if (watch_primary) { - if (XCheckTypedEvent(dpy, SelectionNotify, &xev)) { - if (xev.type == SelectionNotify && - xev.xselection.requestor == selwin && - xev.xselection.selection == XA_PRIMARY && - xev.xselection.property != None && - xev.xselection.target == XA_STRING) { - - /* go retrieve PRIMARY and check it */ - if (now > last_client + sel_waittime - || sent_sel) { - selection_send(&xev); - } - } - } - if (now > last_request + 1) { - /* - * Every second or two, request PRIMARY, unless we - * already own it or there is no owner. - * TODO: even at this low rate we should look into - * and performance problems in odds cases, etc. - */ - last_request = now; - if (! own_selection && - XGetSelectionOwner(dpy, XA_PRIMARY) != None) { - XConvertSelection(dpy, XA_PRIMARY, XA_STRING, - XA_STRING, selwin, CurrentTime); - } - } - } - - if (! own_selection) { - /* - * no need to do the PRIMARY maintenance tasks below if - * no we do not own it (right?). - */ - X_UNLOCK; - return; - } - - /* we own PRIMARY, see if someone requested it: */ - if (XCheckTypedEvent(dpy, SelectionRequest, &xev)) { - if (xev.type == SelectionRequest && - xev.xselectionrequest.selection == XA_PRIMARY) { - selection_request(&xev); - } - } - - /* we own PRIMARY, see if we no longer own it: */ - if (XCheckTypedEvent(dpy, SelectionClear, &xev)) { - if (xev.type == SelectionClear && - xev.xselectionclear.selection == XA_PRIMARY) { - - own_selection = 0; - if (xcut_string) { - free(xcut_string); - xcut_string = NULL; - } - } - } - X_UNLOCK; -} - -/* - * hook called when a VNC client sends us some "XCut" text (rfbClientCutText). - */ -void xcut_receive(char *text, int len, rfbClientPtr cl) { - - if (cl->viewOnly) { - return; - } - if (text == NULL || len == 0) { - return; - } - - X_LOCK; - - /* associate this text with PRIMARY (and SECONDARY...) */ - if (! own_selection) { - own_selection = 1; - /* we need to grab the PRIMARY selection */ - XSetSelectionOwner(dpy, XA_PRIMARY, selwin, CurrentTime); - XFlush(dpy); - } - - /* duplicate the text string for our own use. */ - if (xcut_string != NULL) { - free(xcut_string); - } - xcut_string = (unsigned char *) - malloc((size_t) (len+1) * sizeof(unsigned char)); - strncpy(xcut_string, text, len); - xcut_string[len] = '\0'; /* make sure null terminated */ - - /* copy this text to CUT_BUFFER0 as well: */ - XChangeProperty(dpy, rootwin, XA_CUT_BUFFER0, XA_STRING, 8, - PropModeReplace, text, len); - XFlush(dpy); - - X_UNLOCK; - - set_cutbuffer = 1; -} - -void mark_hint(hint_t); - -/* - * Here begins a bit of a mess to experiment with multiple cursors ... - */ -typedef struct cursor_info { - char *data; /* data and mask pointers */ - char *mask; - int wx, wy; /* size of cursor */ - int sx, sy; /* shift to its centering point */ - int reverse; /* swap black and white */ -} cursor_info_t; - -/* main cursor */ -static char* cur_data = -" " -" x " -" xx " -" xxx " -" xxxx " -" xxxxx " -" xxxxxx " -" xxxxxxx " -" xxxxxxxx " -" xxxxx " -" xx xx " -" x xx " -" xx " -" xx " -" xx " -" " -" " -" "; - -static char* cur_mask = -"xx " -"xxx " -"xxxx " -"xxxxx " -"xxxxxx " -"xxxxxxx " -"xxxxxxxx " -"xxxxxxxxx " -"xxxxxxxxxx " -"xxxxxxxxxx " -"xxxxxxx " -"xxx xxxx " -"xx xxxx " -" xxxx " -" xxxx " -" xx " -" " -" "; -#define CUR_SIZE 18 -#define CUR_DATA cur_data -#define CUR_MASK cur_mask -cursor_info_t cur0 = {NULL, NULL, CUR_SIZE, CUR_SIZE, 0, 0, 0}; - -/* - * It turns out we can at least detect mouse is on the root window so - * show it (under -mouseX or -X) with this familiar cursor... - */ -static char* root_data = -" " -" " -" xxx xxx " -" xxxx xxxx " -" xxxxx xxxxx " -" xxxxx xxxxx " -" xxxxxxxxxx " -" xxxxxxxx " -" xxxxxx " -" xxxxxx " -" xxxxxxxx " -" xxxxxxxxxx " -" xxxxx xxxxx " -" xxxxx xxxxx " -" xxxx xxxx " -" xxx xxx " -" " -" "; - -static char* root_mask = -" " -" xxxx xxxx " -" xxxxx xxxxx " -" xxxxxx xxxxxx " -" xxxxxxx xxxxxxx " -" xxxxxxxxxxxxxx " -" xxxxxxxxxxxx " -" xxxxxxxxxx " -" xxxxxxxx " -" xxxxxxxx " -" xxxxxxxxxx " -" xxxxxxxxxxxx " -" xxxxxxxxxxxxxx " -" xxxxxxx xxxxxxx " -" xxxxxx xxxxxx " -" xxxxx xxxxx " -" xxxx xxxx " -" "; -cursor_info_t cur1 = {NULL, NULL, 18, 18, 8, 8, 1}; - -cursor_info_t *cursors[2]; -void setup_cursors(void) { - /* TODO clean this up if we ever do more cursors... */ - - cur0.data = cur_data; - cur0.mask = cur_mask; - - cur1.data = root_data; - cur1.mask = root_mask; - - cursors[0] = &cur0; - cursors[1] = &cur1; -} - -/* - * data and functions for -mouse real pointer position updates - */ -char cur_save[(4 * CUR_SIZE * CUR_SIZE)]; -int cur_save_x, cur_save_y, cur_save_w, cur_save_h; -int cur_save_cx, cur_save_cy, cur_save_which, cur_saved = 0; - -/* - * save current cursor info and the patch of non-cursor data it covers - */ -void save_mouse_patch(int x, int y, int w, int h, int cx, int cy, int which) { - int pixelsize = bpp >> 3; - char *rfb_fb = screen->frameBuffer; - int ly, i = 0; - - for (ly = y; ly < y + h; ly++) { - memcpy(cur_save+i, rfb_fb + ly * bytes_per_line - + x * pixelsize, w * pixelsize); - - i += w * pixelsize; - } - cur_save_x = x; /* patch geometry */ - cur_save_y = y; - cur_save_w = w; - cur_save_h = h; - - cur_save_which = which; /* which cursor and its position */ - cur_save_cx = cx; - cur_save_cy = cy; - - cur_saved = 1; -} - -/* - * put the non-cursor patch back in the rfb fb - */ -void restore_mouse_patch() { - int pixelsize = bpp >> 3; - char *rfb_fb = screen->frameBuffer; - int ly, i = 0; - - if (! cur_saved) { - return; /* not yet saved */ - } - - for (ly = cur_save_y; ly < cur_save_y + cur_save_h; ly++) { - memcpy(rfb_fb + ly * bytes_per_line + cur_save_x * pixelsize, - cur_save+i, cur_save_w * pixelsize); - i += cur_save_w * pixelsize; - } -} - -/* - * Descends window tree at pointer until the window cursor matches the current - * cursor. So far only used to detect if mouse is on root background or not. - * (returns 0 in that case, 1 otherwise). - * - * It seems impossible to do, but if the actual cursor could ever be - * determined we might want to hash that info on window ID or something... - */ -int tree_descend_cursor(void) { - Window r, c; - int rx, ry, wx, wy; - unsigned int mask; - int descend = 0, tries = 0, maxtries = 1; - - X_LOCK; - c = window; - while (c) { - if (++tries > maxtries) { - descend = maxtries; - break; - } - if ( XTestCompareCurrentCursorWithWindow(dpy, c) ) { - break; - } - XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &mask); - descend++; - } - X_UNLOCK; - return descend; -} - -void blackout_nearby_tiles(x, y, dt) { - int sx, sy, n, b; - int tx = x/tile_x; - int ty = y/tile_y; - - if (! blackouts) { - return; - } - if (dt < 1) { - dt = 1; - } - /* loop over a range of tiles, blacking out as needed */ - - for (sx = tx - dt; sx <= tx + dt; sx++) { - if (sx < 0 || sx >= tile_x) { - continue; - } - for (sy = ty - dt; sy <= ty + dt; sy++) { - if (sy < 0 || sy >= tile_y) { - continue; - } - n = sx + sy * ntiles_x; - if (tile_blackout[n].cover == 0) { - continue; - } - for (b=0; b <= tile_blackout[n].count; b++) { - int x1, y1, x2, y2; - x1 = tile_blackout[n].bo[b].x1; - y1 = tile_blackout[n].bo[b].y1; - x2 = tile_blackout[n].bo[b].x2; - y2 = tile_blackout[n].bo[b].y2; - zero_fb(x1, y1, x2, y2); - } - } - } -} - -/* - * Send rfbCursorPosUpdates back to clients that understand them. This - * seems to be TightVNC specific. - */ -void cursor_pos_updates(int x, int y) { - rfbClientIteratorPtr iter; - rfbClientPtr cl; - int cnt = 0; - - if (! cursor_pos) { - return; - } - /* x and y are current positions of X11 pointer on the X11 display */ - if (x == screen->cursorX && y == screen->cursorY) { - return; - } - - if (screen->cursorIsDrawn) { - rfbUndrawCursor(screen); - } - LOCK(screen->cursorMutex); - if (! screen->cursorIsDrawn) { - screen->cursorX = x; - screen->cursorY = y; - } - UNLOCK(screen->cursorMutex); - - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) { - if (! cl->enableCursorPosUpdates) { - continue; - } - if (cl == last_pointer_client) { - /* - * special case if this client was the last one to - * send a pointer position. - */ - if (x == cursor_x && y == cursor_y) { - cl->cursorWasMoved = FALSE; - } else { - /* an X11 app evidently warped the pointer */ - if (debug_pointer) { - rfbLog("cursor_pos_updates: warp " - "detected dx=%3d dy=%3d\n", - cursor_x - x, cursor_y - y); - } - cl->cursorWasMoved = TRUE; - cnt++; - } - } else { - cl->cursorWasMoved = TRUE; - cnt++; - } - } - rfbReleaseClientIterator(iter); - - if (debug_pointer && cnt) { - rfbLog("cursor_pos_updates: sent position x=%3d y=%3d to %d" - " clients\n", x, y, cnt); - } -} - -/* - * draw one of the mouse cursors into the rfb fb - */ -void draw_mouse(int x, int y, int which, int update) { - int px, py, i, offset; - int pixelsize = bpp >> 3; - char *rfb_fb = screen->frameBuffer; - char cdata, cmask; - char *data, *mask; - int white = 255, black = 0, shade; - int x0, x1, x2, y0, y1, y2; - int cur_x, cur_y, cur_sx, cur_sy, reverse; - static int first = 1; - - if (! show_mouse) { - return; - } - if (first) { - first = 0; - setup_cursors(); - } - - data = cursors[which]->data; /* pattern data */ - mask = cursors[which]->mask; - cur_x = cursors[which]->wx; /* widths */ - cur_y = cursors[which]->wy; - cur_sx = cursors[which]->sx; /* shifts */ - cur_sy = cursors[which]->sy; - reverse = cursors[which]->reverse; /* reverse video */ - - if (indexed_colour) { - black = BlackPixel(dpy, scr) % 256; - white = WhitePixel(dpy, scr) % 256; - } - if (reverse) { - int tmp = black; - black = white; - white = tmp; - } - - /* - * notation: - * x0, y0: position after cursor shift (no edge corrections) - * x1, y1: corrected for lower boundary < 0 - * x2, y2: position + cursor width and corrected for upper boundary - */ - - x0 = x1 = x - cur_sx; /* apply shift */ - if (x1 < 0) x1 = 0; - - y0 = y1 = y - cur_sy; - if (y1 < 0) y1 = 0; - - x2 = x0 + cur_x; /* apply width for upper endpoints */ - if (x2 >= dpy_x) x2 = dpy_x - 1; - - y2 = y0 + cur_y; - if (y2 >= dpy_y) y2 = dpy_y - 1; - - /* save the patch and info about which cursor will overwrite it */ - save_mouse_patch(x1, y1, x2 - x1, y2 - y1, x, y, which); - - for (py = 0; py < cur_y; py++) { - if (y0 + py < 0 || y0 + py >= dpy_y) { - continue; /* off screen */ - } - for (px = 0; px < cur_x; px++) { - if (x0 + px < 0 || x0 + px >= dpy_x){ - continue; /* off screen */ - } - cdata = data[px + py * cur_x]; - cmask = mask[px + py * cur_x]; - - if (cmask != 'x') { - continue; /* transparent */ - } - - shade = white; - if (cdata != cmask) { - shade = black; - } - - offset = (y0 + py)*bytes_per_line + (x0 + px)*pixelsize; - - /* fill in each color byte in the fb */ - for (i=0; i < pixelsize; i++) { - rfb_fb[offset+i] = (char) shade; - } - } - } - - if (blackouts) { - /* - * loop over a range of tiles, blacking out as needed - * note we currently disable mouse drawing under blackouts. - */ - static int mx = -1, my = -1; - int skip = 0; - if (mx < 0) { - mx = x; - my = y; - } else if (mx == x && my == y) { - skip = 1; - } - mx = x; - my = y; - - if (! skip) { - blackout_nearby_tiles(x, y, 2); - } - } - - if (update) { - /* x and y of the real (X server) mouse */ - static int mouse_x = -1; - static int mouse_y = -1; - - if (x != mouse_x || y != mouse_y) { - hint_t hint; - - hint.x = x1; - hint.y = y2; - hint.w = x2 - x1; - hint.h = y2 - y1; - - mark_hint(hint); - - if (mouse_x < 0) { - mouse_x = 0; - } - if (mouse_y < 0) { - mouse_y = 0; - } - - /* XXX this ignores change of shift... */ - x1 = mouse_x - cur_sx; - if (x1 < 0) x1 = 0; - - y1 = mouse_y - cur_sy; - if (y1 < 0) y1 = 0; - - x2 = mouse_x - cur_sx + cur_x; - if (x2 >= dpy_x) x2 = dpy_x - 1; - - y2 = mouse_y - cur_sy + cur_y; - if (y2 >= dpy_y) y2 = dpy_y - 1; - - hint.x = x1; - hint.y = y2; - hint.w = x2 - x1; - hint.h = y2 - y1; - - mark_hint(hint); - - mouse_x = x; - mouse_y = y; - } - } -} - -void redraw_mouse(void) { - if (cur_saved) { - /* redraw saved mouse from info (save_mouse_patch) */ - draw_mouse(cur_save_cx, cur_save_cy, cur_save_which, 0); - } -} - -void update_mouse(void) { - Window root_w, child_w; - rfbBool ret; - int root_x, root_y, win_x, win_y, which = 0; - unsigned int mask; - - X_LOCK; - ret = XQueryPointer(dpy, rootwin, &root_w, &child_w, &root_x, &root_y, - &win_x, &win_y, &mask); - X_UNLOCK; - - if (! ret) { - return; - } - - if (show_root_cursor) { - int descend; - if ( (descend = tree_descend_cursor()) ) { - which = 0; - } else { - which = 1; - } - } - - cursor_pos_updates(root_x - off_x, root_y - off_y); - draw_mouse(root_x - off_x, root_y - off_y, which, 1); -} - -/* - * For the subwin case follows the window if it is moved. - */ -void set_offset(void) { - Window w; - if (! subwin) { - return; - } - X_LOCK; - XTranslateCoordinates(dpy, window, rootwin, 0, 0, &off_x, &off_y, &w); - X_UNLOCK; -} - -/* - * Some handling of 8bpp PseudoColor colormaps. Called for initializing - * the clients and dynamically if -flashcmap is specified. - */ -#define NCOLOR 256 -void set_colormap(void) { - static int first = 1; - static XColor color[NCOLOR], prev[NCOLOR]; - Colormap cmap; - Visual *vis; - int i, ncells, diffs = 0; - - if (first) { - screen->colourMap.count = NCOLOR; - screen->rfbServerFormat.trueColour = FALSE; - screen->colourMap.is16 = TRUE; - screen->colourMap.data.shorts = (unsigned short*) - malloc(3*sizeof(short) * NCOLOR); - } - - for (i=0; i < NCOLOR; i++) { - prev[i].red = color[i].red; - prev[i].green = color[i].green; - prev[i].blue = color[i].blue; - } - - X_LOCK; - - cmap = DefaultColormap(dpy, scr); - ncells = CellsOfScreen(ScreenOfDisplay(dpy, scr)); - vis = visual; - - if (subwin) { - XWindowAttributes attr; - - if (XGetWindowAttributes(dpy, window, &attr)) { - cmap = attr.colormap; - vis = attr.visual; - ncells = vis->map_entries; - } - } - - if (first && ncells != NCOLOR) { - if (! quiet) { - fprintf(stderr, "set_colormap: number of cells is %d " - "instead of %d.\n", ncells, NCOLOR); - } - screen->colourMap.count = ncells; - } - - if (flash_cmap && ! first) { - XWindowAttributes attr; - Window r, c; - int rx, ry, wx, wy, tries = 0; - unsigned int m; - - c = window; - while (c && tries++ < 16) { - /* XXX XQueryTree somehow? */ - XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &m); - if (c && XGetWindowAttributes(dpy, c, &attr)) { - if (attr.colormap && attr.map_installed) { - cmap = attr.colormap; - vis = attr.visual; - ncells = vis->map_entries; - break; - } - } else { - break; - } - } - } - if (ncells > NCOLOR && ! quiet) { - fprintf(stderr, "set_colormap: big problem: ncells=%d > %d\n", - ncells, NCOLOR); - } - - if (vis->class == TrueColor || vis->class == DirectColor) { - /* - * Kludge to make 8bpp TrueColor & DirectColor be like - * the StaticColor map. The ncells = 8 is "8 per subfield" - * mentioned in xdpyinfo. Looks OK... likely fortuitously. - */ - if (ncells == 8) { - ncells = NCOLOR; - } - } - - for (i=0; i < ncells; i++) { - color[i].pixel = i; - color[i].pad = 0; - } - - XQueryColors(dpy, cmap, color, ncells); - - X_UNLOCK; - - for(i=0; i < ncells; i++) { - screen->colourMap.data.shorts[i*3+0] = color[i].red; - screen->colourMap.data.shorts[i*3+1] = color[i].green; - screen->colourMap.data.shorts[i*3+2] = color[i].blue; - - if (prev[i].red != color[i].red || - prev[i].green != color[i].green || - prev[i].blue != color[i].blue ) { - diffs++; - } - } - - if (diffs && ! first) { - if (! all_clients_initialized()) { - rfbLog("set_colormap: warning: sending cmap " - "with uninitialized clients.\n"); - } - rfbSetClientColourMaps(screen, 0, ncells); - } - - first = 0; -} - -/* - * Experimental mode to force the visual of the window instead of querying - * it. Currently just used for testing or overriding some rare cases. - * Input string can be a decimal or 0x hex or something like TrueColor - * or TrueColor:24 to force a depth as well. - */ -void set_visual(char *vstring) { - int vis, defdepth = DefaultDepth(dpy, scr); - XVisualInfo vinfo; - char *p; - - if (! quiet) { - fprintf(stderr, "set_visual: %s\n", vstring); - } - - if ((p = strchr(vstring, ':')) != NULL) { - visual_depth = atoi(p+1); - *p = '\0'; - } else { - visual_depth = defdepth; - } - if (strcmp(vstring, "StaticGray") == 0) { - vis = StaticGray; - } else if (strcmp(vstring, "GrayScale") == 0) { - vis = GrayScale; - } else if (strcmp(vstring, "StaticColor") == 0) { - vis = StaticColor; - } else if (strcmp(vstring, "PseudoColor") == 0) { - vis = PseudoColor; - } else if (strcmp(vstring, "TrueColor") == 0) { - vis = TrueColor; - } else if (strcmp(vstring, "DirectColor") == 0) { - vis = DirectColor; - } else { - int v_in; - if (sscanf(vstring, "0x%x", &v_in) != 1) { - if (sscanf(vstring, "%d", &v_in) == 1) { - visual_id = (VisualID) v_in; - return; - } - fprintf(stderr, "bad -visual arg: %s\n", vstring); - exit(1); - } - visual_id = (VisualID) v_in; - return; - } - if (XMatchVisualInfo(dpy, scr, visual_depth, vis, &vinfo)) { - ; - } else if (XMatchVisualInfo(dpy, scr, defdepth, vis, &vinfo)) { - ; - } else { - fprintf(stderr, "could not find visual: %s\n", vstring); - exit(1); - } - visual_id = vinfo.visualid; -} - -/* - * Presumably under -nofb the clients will never request the framebuffer. - * But we have gotten such a request... so let's just give them - * the current view on the display. n.b. x2vnc and perhaps win2vnc - * requests a 1x1 pixel for some workaround so sadly this evidently - * nearly always happens. - */ -void nofb_hook(rfbClientPtr cl) { - static int loaded_fb = 0; - XImage *fb; - if (loaded_fb) { - return; - } - rfbLog("framebuffer requested in -nofb mode by client %s\n", cl->host); - fb = XGetImage(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, ZPixmap); - screen->frameBuffer = fb->data; - loaded_fb = 1; - screen->displayHook = NULL; -} - -int got_rfbport = 0; -int got_alwaysshared = 0; -int got_nevershared = 0; -/* - * initialize the rfb framebuffer/screen - */ -void initialize_screen(int *argc, char **argv, XImage *fb) { - int have_masks = 0; - - screen = rfbGetScreen(argc, argv, fb->width, fb->height, - fb->bits_per_pixel, 8, fb->bits_per_pixel/8); - - if (! quiet) { - fprintf(stderr, "\n"); - } - - if (! screen) { - int i; - rfbLog("\n"); - rfbLog("failed to create rfb screen.\n"); - for (i=0; i< *argc; i++) { - rfbLog("\t[%d] %s\n", i, argv[i]); - } - clean_up_exit(1); - } - -/* - * This ifdef is a transient for source compatibility for people who download - * the x11vnc.c file by itself and plop it down into their libvncserver tree. - * Remove at some point. BTW, this assumes no usage of earlier "0.7pre". - */ -#ifdef LIBVNCSERVER_VERSION -if (strcmp(LIBVNCSERVER_VERSION, "0.5") && strcmp(LIBVNCSERVER_VERSION, "0.6")) { - if (*argc != 1) { - int i; - rfbLog("*** unrecognized option(s) ***\n"); - for (i=1; i< *argc; i++) { - rfbLog("\t[%d] %s\n", i, argv[i]); - } - rfbLog("for a list of options run: x11vnc -help\n"); - clean_up_exit(1); - } -} -#endif - - screen->paddedWidthInBytes = fb->bytes_per_line; - screen->rfbServerFormat.bitsPerPixel = fb->bits_per_pixel; - screen->rfbServerFormat.depth = fb->depth; - screen->rfbServerFormat.trueColour = (uint8_t) TRUE; - have_masks = ((fb->red_mask|fb->green_mask|fb->blue_mask) != 0); - if (force_indexed_color) { - have_masks = 0; - } - - if ( ! have_masks && screen->rfbServerFormat.bitsPerPixel == 8 - && CellsOfScreen(ScreenOfDisplay(dpy,scr)) ) { - /* indexed colour */ - if (!quiet) rfbLog("Using X display with 8bpp indexed color\n"); - indexed_colour = 1; - set_colormap(); - } else { - /* general case ... */ - if (! quiet) { - rfbLog("Using X display with %dbpp depth=%d true " - "color\n", fb->bits_per_pixel, fb->depth); - } - - /* convert masks to bit shifts and max # colors */ - screen->rfbServerFormat.redShift = 0; - if ( fb->red_mask ) { - while ( ! (fb->red_mask - & (1 << screen->rfbServerFormat.redShift) ) ) { - screen->rfbServerFormat.redShift++; - } - } - screen->rfbServerFormat.greenShift = 0; - if ( fb->green_mask ) { - while ( ! (fb->green_mask - & (1 << screen->rfbServerFormat.greenShift) ) ) { - screen->rfbServerFormat.greenShift++; - } - } - screen->rfbServerFormat.blueShift = 0; - if ( fb->blue_mask ) { - while ( ! (fb->blue_mask - & (1 << screen->rfbServerFormat.blueShift) ) ) { - screen->rfbServerFormat.blueShift++; - } - } - screen->rfbServerFormat.redMax - = fb->red_mask >> screen->rfbServerFormat.redShift; - screen->rfbServerFormat.greenMax - = fb->green_mask >> screen->rfbServerFormat.greenShift; - screen->rfbServerFormat.blueMax - = fb->blue_mask >> screen->rfbServerFormat.blueShift; - } - - /* nofb is for pointer/keyboard only handling. */ - if (nofb) { - screen->frameBuffer = NULL; - screen->displayHook = nofb_hook; - } else { - screen->frameBuffer = fb->data; - } - - /* called from inetd, we need to treat stdio as our socket */ - if (inetd) { - int fd = dup(0); - if (fd < 3) { - rfbErr("dup(0) = %d failed.\n", fd); - perror("dup"); - clean_up_exit(1); - } - fclose(stdin); - fclose(stdout); - /* we keep stderr for logging */ - screen->inetdSock = fd; - screen->rfbPort = 0; - - } else if (! got_rfbport) { - screen->autoPort = TRUE; - } - - if (! got_nevershared && ! got_alwaysshared) { - if (shared) { - screen->rfbAlwaysShared = TRUE; - } else { - screen->rfbDontDisconnect = TRUE; - screen->rfbNeverShared = TRUE; - } - } - /* XXX the following is based on libvncserver defaults. */ - if (screen->rfbDeferUpdateTime == 5) { - /* XXX will be fixed someday */ - screen->rfbDeferUpdateTime = defer_update; - } - - /* event callbacks: */ - screen->newClientHook = new_client; - screen->kbdAddEvent = keyboard; - screen->ptrAddEvent = pointer; - if (watch_selection) { - screen->setXCutText = xcut_receive; - } - - if (local_cursor) { - cursor = rfbMakeXCursor(CUR_SIZE, CUR_SIZE, CUR_DATA, CUR_MASK); - screen->cursor = cursor; - } else { - screen->cursor = NULL; - } - - rfbInitServer(screen); - - bytes_per_line = screen->paddedWidthInBytes; - bpp = screen->rfbServerFormat.bitsPerPixel; - depth = screen->rfbServerFormat.depth; -} - -/* - * Take a comma separated list of geometries: WxH+X+Y and register them as - * rectangles to black out from the screen. - */ -void initialize_blackout (char *list) { - char *p, *blist = strdup(list); - int x, y, X, Y, h, w; - - p = strtok(blist, ","); - while (p) { - /* handle +/-x and +/-y */ - if (sscanf(p, "%dx%d+%d+%d", &w, &h, &x, &y) == 4) { - ; - } else if (sscanf(p, "%dx%d-%d+%d", &w, &h, &x, &y) == 4) { - x = dpy_x - x - w; - } else if (sscanf(p, "%dx%d+%d-%d", &w, &h, &x, &y) == 4) { - y = dpy_y - y - h; - } else if (sscanf(p, "%dx%d-%d-%d", &w, &h, &x, &y) == 4) { - x = dpy_x - x - w; - y = dpy_y - y - h; - } else { - if (*p != '\0') { - rfbLog("skipping invalid geometry: %s\n", p); - } - p = strtok(NULL, ","); - continue; - } - X = x + w; - Y = y + h; - if (x < 0 || x > dpy_x || y < 0 || y > dpy_y || - X < 0 || X > dpy_x || Y < 0 || Y > dpy_y) { - rfbLog("skipping invalid blackout geometry: %s x=" - "%d-%d,y=%d-%d,w=%d,h=%d\n", p, x, X, y, Y, w, h); - } else { - rfbLog("blackout rect: %s: x=%d-%d y=%d-%d\n", p, - x, X, y, Y); - - /* - * note that the black out is x1 <= x but x < x2 - * for the region. i.e. the x2, y2 are outside - * by 1 pixel. - */ - black[blackouts].x1 = x; - black[blackouts].y1 = y; - black[blackouts].x2 = X; - black[blackouts].y2 = Y; - blackouts++; - if (blackouts >= 100) { - rfbLog("too many blackouts: %d\n", blackouts); - break; - } - } - p = strtok(NULL, ","); - } - free(blist); -} - -/* - * Now that all blackout rectangles have been constructed, see what overlap - * they have with the tiles in the system. If a tile is touched by a - * blackout, record information. - */ -void blackout_tiles() { - int tx, ty; - if (! blackouts) { - return; - } - - /* - * to simplify things drop down to single copy mode, no vcr, etc... - */ - single_copytile = 1; - if (show_mouse) { - rfbLog("disabling remote mouse drawing due to blackouts\n"); - show_mouse = 0; - } - - /* loop over all tiles. */ - for (ty=0; ty < ntiles_y; ty++) { - for (tx=0; tx < ntiles_x; tx++) { - sraRegionPtr tile_reg, black_reg; - sraRect rect; - sraRectangleIterator *iter; - int n, b, x1, y1, x2, y2, cnt; - - /* tile number and coordinates: */ - n = tx + ty * ntiles_x; - x1 = tx * tile_x; - y1 = ty * tile_y; - x2 = x1 + tile_x; - y2 = y1 + tile_y; - if (x2 > dpy_x) { - x2 = dpy_x; - } - if (y2 > dpy_y) { - y2 = dpy_y; - } - - /* make regions for the tile and the blackouts: */ - black_reg = (sraRegionPtr) sraRgnCreate(); - tile_reg = (sraRegionPtr) sraRgnCreateRect(x1, y1, - x2, y2); - - tile_blackout[n].cover = 0; - tile_blackout[n].count = 0; - - /* union of blackouts */ - for (b=0; b < blackouts; b++) { - sraRegionPtr tmp_reg = (sraRegionPtr) - sraRgnCreateRect(black[b].x1, black[b].y1, - black[b].x2, black[b].y2); - - sraRgnOr(black_reg, tmp_reg); - sraRgnDestroy(tmp_reg); - } - - if (! sraRgnAnd(black_reg, tile_reg)) { - /* - * no intersection for this tile, so we - * are done. - */ - sraRgnDestroy(black_reg); - sraRgnDestroy(tile_reg); - continue; - } - - /* - * loop over rectangles that make up the blackout - * region: - */ - cnt = 0; - iter = sraRgnGetIterator(black_reg); - while (sraRgnIteratorNext(iter, &rect)) { - - /* make sure x1 < x2 and y1 < y2 */ - if (rect.x1 > rect.x2) { - int tmp = rect.x2; - rect.x2 = rect.x1; - rect.x1 = tmp; - } - if (rect.y1 > rect.y2) { - int tmp = rect.y2; - rect.y2 = rect.y1; - rect.y1 = tmp; - } - - /* store coordinates */ - tile_blackout[n].bo[cnt].x1 = rect.x1; - tile_blackout[n].bo[cnt].y1 = rect.y1; - tile_blackout[n].bo[cnt].x2 = rect.x2; - tile_blackout[n].bo[cnt].y2 = rect.y2; - - /* note if the tile is completely obscured */ - if (rect.x1 == x1 && rect.y1 == y1 && - rect.x2 == x2 && rect.y2 == y2) { - tile_blackout[n].cover = 2; - } else { - tile_blackout[n].cover = 1; - } - - if (++cnt >= 10) { - rfbLog("too many blackout rectangles " - "for tile %d=%d,%d.\n", n, tx, ty); - break; - } - } - - sraRgnReleaseIterator(iter); - sraRgnDestroy(black_reg); - sraRgnDestroy(tile_reg); - - tile_blackout[n].count = cnt; - } - } -} - -void initialize_xinerama () { -#ifndef LIBVNCSERVER_HAVE_LIBXINERAMA - rfbLog("Xinerama: Library libXinerama is not available to determine\n"); - rfbLog("Xinerama: the head geometries, consider using -blackout\n"); - rfbLog("Xinerama: if the screen is non-rectangular.\n"); -#else - XineramaScreenInfo *sc, *xineramas; - sraRegionPtr black_region, tmp_region; - sraRectangleIterator *iter; - sraRect rect; - char *bstr, *tstr; - int ev, er, i, n, rcnt; - - if (! XineramaQueryExtension(dpy, &ev, &er)) { - rfbLog("Xinerama: disabling: display does not support it.\n"); - xinerama = 0; - return; - } - if (! XineramaIsActive(dpy)) { - /* n.b. change to XineramaActive(dpy, window) someday */ - rfbLog("Xinerama: disabling: not active on display.\n"); - xinerama = 0; - return; - } - - /* n.b. change to XineramaGetData() someday */ - xineramas = XineramaQueryScreens(dpy, &n); - rfbLog("Xinerama: number of sub-screens: %d\n", n); - - if (n == 1) { - rfbLog("Xinerama: no blackouts needed (only one" - " sub-screen)\n"); - XFree(xineramas); - return; /* must be OK w/o change */ - } - - black_region = sraRgnCreateRect(0, 0, dpy_x, dpy_y); - - sc = xineramas; - for (i=0; ix_org; - y = sc->y_org; - w = sc->width; - h = sc->height; - - tmp_region = sraRgnCreateRect(x, y, x + w, y + h); - - sraRgnSubtract(black_region, tmp_region); - sraRgnDestroy(tmp_region); - sc++; - } - XFree(xineramas); - - if (sraRgnEmpty(black_region)) { - rfbLog("Xinerama: no blackouts needed (screen fills" - " rectangle)\n"); - sraRgnDestroy(black_region); - return; - } - - /* max len is 10000x10000+10000+10000 (23 chars) per geometry */ - rcnt = (int) sraRgnCountRects(black_region); - bstr = (char *) malloc(30 * rcnt * sizeof(char)); - tstr = (char *) malloc(30 * sizeof(char)); - bstr[0] = '\0'; - - iter = sraRgnGetIterator(black_region); - while (sraRgnIteratorNext(iter, &rect)) { - int x, y, w, h; - - /* make sure x1 < x2 and y1 < y2 */ - if (rect.x1 > rect.x2) { - int tmp = rect.x2; - rect.x2 = rect.x1; - rect.x1 = tmp; - } - if (rect.y1 > rect.y2) { - int tmp = rect.y2; - rect.y2 = rect.y1; - rect.y1 = tmp; - } - x = rect.x1; - y = rect.y1; - w = rect.x2 - x; - h = rect.y2 - y; - sprintf(tstr, "%dx%d+%d+%d,", w, h, x, y); - strcat(bstr, tstr); - } - initialize_blackout(bstr); - - free(bstr); - free(tstr); -#endif -} - -/* - * Fill the framebuffer with zero for the prescribed rectangle - */ -void zero_fb(x1, y1, x2, y2) { - int pixelsize = bpp >> 3; - int line, fill = 0; - char *dst; - - if (x1 < 0 || x2 <= x1 || x2 > dpy_x) { - return; - } - if (y1 < 0 || y2 <= y1 || y2 > dpy_y) { - return; - } - - dst = screen->frameBuffer + y1 * bytes_per_line + x1 * pixelsize; - line = y1; - while (line++ < y2) { - memset(dst, fill, (size_t) (x2 - x1) * pixelsize); - dst += bytes_per_line; - } -} - -/* - * Fill the framebuffer with zeros for each blackout region - */ -void blackout_regions() { - int i; - for (i=0; i < blackouts; i++) { - zero_fb(black[i].x1, black[i].y1, black[i].x2, black[i].y2); - } -} - -/* - * setup tile numbers and allocate the tile and hint arrays: - */ -void initialize_tiles() { - - ntiles_x = (dpy_x - 1)/tile_x + 1; - ntiles_y = (dpy_y - 1)/tile_y + 1; - ntiles = ntiles_x * ntiles_y; - - tile_has_diff = (unsigned char *) - malloc((size_t) (ntiles * sizeof(unsigned char))); - tile_tried = (unsigned char *) - malloc((size_t) (ntiles * sizeof(unsigned char))); - tile_blackout = (tile_blackout_t *) - malloc((size_t) (ntiles * sizeof(tile_blackout_t))); - tile_region = (region_t *) malloc((size_t) (ntiles * sizeof(region_t))); - - tile_row = (XImage **) - malloc((size_t) ((ntiles_x + 1) * sizeof(XImage *))); - tile_row_shm = (XShmSegmentInfo *) - malloc((size_t) ((ntiles_x + 1) * sizeof(XShmSegmentInfo))); - - /* there will never be more hints than tiles: */ - hint_list = (hint_t *) malloc((size_t) (ntiles * sizeof(hint_t))); -} - -/* - * silly function to factor dpy_y until fullscreen shm is not bigger than max. - * should always work unless dpy_y is a large prime or something... under - * failure fs_factor remains 0 and no fullscreen updates will be tried. - */ -void set_fs_factor(int max) { - int f, fac = 1, n = dpy_y; - - if ( (bpp/8) * dpy_x * dpy_y <= max ) { - fs_factor = 1; - return; - } - for (f=2; f <= 101; f++) { - while (n % f == 0) { - n = n / f; - fac = fac * f; - if ( (bpp/8) * dpy_x * (dpy_y/fac) <= max ) { - fs_factor = fac; - return; - } - } - } -} - -/* - * set up an XShm image - */ -int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h, - char *name) { - - XImage *xim; - - shm->shmid = -1; - shm->shmaddr = (char *) -1; - *ximg_ptr = NULL; - - if (nofb) { - return 1; - } - - X_LOCK; - - if (! using_shm) { - /* we only need the XImage created */ - xim = XCreateImage(dpy, visual, depth, ZPixmap, 0, NULL, w, h, - BitmapPad(dpy), 0); - - X_UNLOCK; - - if (xim == NULL) { - rfbErr("XCreateImage(%s) failed.\n", name); - return 0; - } - xim->data = (char *) malloc(xim->bytes_per_line * xim->height); - if (xim->data == NULL) { - rfbErr("XCreateImage(%s) data malloc failed.\n", name); - return 0; - } - if (flip_byte_order) { - static int reported = 0; - char *bo; - if (xim->byte_order == LSBFirst) { - bo = "MSBFirst"; - xim->byte_order = MSBFirst; - xim->bitmap_bit_order = MSBFirst; - } else { - bo = "LSBFirst"; - xim->byte_order = LSBFirst; - xim->bitmap_bit_order = LSBFirst; - } - if (! reported && ! quiet) { - rfbLog("changing XImage byte order" - " to %s\n", bo); - reported = 1; - } - } - - *ximg_ptr = xim; - return 1; - } - - xim = XShmCreateImage(dpy, visual, depth, ZPixmap, NULL, shm, w, h); - - if (xim == NULL) { - rfbErr("XShmCreateImage(%s) failed.\n", name); - X_UNLOCK; - return 0; - } - - *ximg_ptr = xim; - - shm->shmid = shmget(IPC_PRIVATE, - xim->bytes_per_line * xim->height, IPC_CREAT | 0777); - - if (shm->shmid == -1) { - rfbErr("shmget(%s) failed.\n", name); - perror("shmget"); - - XDestroyImage(xim); - *ximg_ptr = NULL; - - X_UNLOCK; - return 0; - } - - shm->shmaddr = xim->data = (char *) shmat(shm->shmid, 0, 0); - - if (shm->shmaddr == (char *)-1) { - rfbErr("shmat(%s) failed.\n", name); - perror("shmat"); - - XDestroyImage(xim); - *ximg_ptr = NULL; - - shmctl(shm->shmid, IPC_RMID, 0); - shm->shmid = -1; - - X_UNLOCK; - return 0; - } - - shm->readOnly = False; - - if (! XShmAttach(dpy, shm)) { - rfbErr("XShmAttach(%s) failed.\n", name); - XDestroyImage(xim); - *ximg_ptr = NULL; - - shmdt(shm->shmaddr); - shm->shmaddr = (char *) -1; - - shmctl(shm->shmid, IPC_RMID, 0); - shm->shmid = -1; - - X_UNLOCK; - return 0; - } - - X_UNLOCK; - return 1; -} - -void shm_delete(XShmSegmentInfo *shm) { - if (! using_shm) { - return; - } - if (shm->shmaddr != (char *) -1) { - shmdt(shm->shmaddr); - } - if (shm->shmid != -1) { - shmctl(shm->shmid, IPC_RMID, 0); - } -} - -void shm_clean(XShmSegmentInfo *shm, XImage *xim) { - if (! using_shm || nofb) { - return; - } - X_LOCK; - if (shm->shmid != -1) { - XShmDetach(dpy, shm); - } - if (xim != NULL) { - XDestroyImage(xim); - } - X_UNLOCK; - - shm_delete(shm); -} - -void initialize_shm() { - int i; - - /* set all shm areas to "none" before trying to create any */ - scanline_shm.shmid = -1; - scanline_shm.shmaddr = (char *) -1; - scanline = NULL; - fullscreen_shm.shmid = -1; - fullscreen_shm.shmaddr = (char *) -1; - fullscreen = NULL; - for (i=1; i<=ntiles_x; i++) { - tile_row_shm[i].shmid = -1; - tile_row_shm[i].shmaddr = (char *) -1; - tile_row[i] = NULL; - } - - /* the scanline (e.g. 1280x1) shared memory area image: */ - - if (! shm_create(&scanline_shm, &scanline, dpy_x, 1, "scanline")) { - clean_up_exit(1); - } - - /* - * the fullscreen (e.g. 1280x1024/fs_factor) shared memory area image: - * (we cut down the size of the shm area to try avoid and shm segment - * limits, e.g. the default 1MB on Solaris) - */ - set_fs_factor(1024 * 1024); - if (fs_frac >= 1.0) { - fs_frac = 1.1; - fs_factor = 0; - } - if (! fs_factor) { - rfbLog("warning: fullscreen updates are disabled.\n"); - } else { - if (! shm_create(&fullscreen_shm, &fullscreen, dpy_x, - dpy_y/fs_factor, "fullscreen")) { - clean_up_exit(1); - } - } - - /* - * for copy_tiles we need a lot of shared memory areas, one for - * each possible run length of changed tiles. 32 for 1024x768 - * and 40 for 1280x1024, etc. - */ - - for (i=1; i<=ntiles_x; i++) { - if (! shm_create(&tile_row_shm[i], &tile_row[i], tile_x * i, - tile_y, "tile_row")) { - if (i == 1) { - clean_up_exit(1); - } - rfbLog("shm: Error creating shared memory tile-row for" - " len=%d,\n", i); - rfbLog("shm: reverting to -onetile mode. If this" - " problem persists\n"); - rfbLog("shm: try using the -onetile or -noshm options" - " to limit\n"); - rfbLog("shm: shared memory usage, or run ipcrm(1)" - " to manually\n"); - rfbLog("shm: delete unattached shm segments.\n"); - /* n.b.: "i" not "1", a kludge for cleanup */ - single_copytile = i; - } - if (single_copytile && i >= 1) { - /* only need 1x1 tiles */ - break; - } - } -} - - -/* - * A hint is a rectangular region built from 1 or more adjacent tiles - * glued together. Ultimately, this information in a single hint is sent - * to libvncserver rather than sending each tile separately. - */ -void create_tile_hint(int x, int y, int th, hint_t *hint) { - int w = dpy_x - x; - int h = dpy_y - y; - - if (w > tile_x) { - w = tile_x; - } - if (h > th) { - h = th; - } - - hint->x = x; - hint->y = y; - hint->w = w; - hint->h = h; -} - -void extend_tile_hint(int x, int y, int th, hint_t *hint) { - int w = dpy_x - x; - int h = dpy_y - y; - - if (w > tile_x) { - w = tile_x; - } - if (h > th) { - h = th; - } - - if (hint->x > x) { /* extend to the left */ - hint->w += hint->x - x; - hint->x = x; - } - if (hint->y > y) { /* extend upward */ - hint->h += hint->y - y; - hint->y = y; - } - - if (hint->x + hint->w < x + w) { /* extend to the right */ - hint->w = x + w - hint->x; - } - if (hint->y + hint->h < y + h) { /* extend downward */ - hint->h = y + h - hint->y; - } -} - -void save_hint(hint_t hint, int loc) { - /* simply copy it to the global array for later use. */ - hint_list[loc].x = hint.x; - hint_list[loc].y = hint.y; - hint_list[loc].w = hint.w; - hint_list[loc].h = hint.h; -} - -/* - * Glue together horizontal "runs" of adjacent changed tiles into one big - * rectangle change "hint" to be passed to the vnc machinery. - */ -void hint_updates() { - hint_t hint; - int x, y, i, n, ty, th; - int hint_count = 0, in_run = 0; - - for (y=0; y < ntiles_y; y++) { - for (x=0; x < ntiles_x; x++) { - n = x + y * ntiles_x; - - if (tile_has_diff[n]) { - ty = tile_region[n].first_line; - th = tile_region[n].last_line - ty + 1; - if (! in_run) { - create_tile_hint( x * tile_x, - y * tile_y + ty, th, &hint); - in_run = 1; - } else { - extend_tile_hint( x * tile_x, - y * tile_y + ty, th, &hint); - } - } else { - if (in_run) { - /* end of a row run of altered tiles: */ - save_hint(hint, hint_count++); - in_run = 0; - } - } - } - if (in_run) { /* save the last row run */ - save_hint(hint, hint_count++); - in_run = 0; - } - } - - - for (i=0; i < hint_count; i++) { - /* pass update info to vnc: */ - mark_hint(hint_list[i]); - } -} - -/* - * Notifies libvncserver of a changed hint rectangle. - */ -void mark_hint(hint_t hint) { - int x = hint.x; - int y = hint.y; - int w = hint.w; - int h = hint.h; - - rfbMarkRectAsModified(screen, x, y, x + w, y + h); -} - -/* - * Notifies libvncserver of a changed tile rectangle. - */ -void mark_tile(int x, int y, int height) { - int w = dpy_x - x; - int h = dpy_y - y; - - if (w > tile_x) { - w = tile_x; - } - - /* height is the height of the changed portion of the tile */ - if (h > height) { - h = height; - } - - rfbMarkRectAsModified(screen, x, y, x + w, y + h); -} - -/* - * Simply send each modified tile separately to the vnc machinery: - * (i.e. no hints) - */ -void tile_updates() { - int x, y, n, ty, th; - - for (y=0; y < ntiles_y; y++) { - for (x=0; x < ntiles_x; x++) { - n = x + y * ntiles_x; - - if (tile_has_diff[n]) { - ty = tile_region[n].first_line; - th = tile_region[n].last_line - ty + 1; - - mark_tile(x * tile_x, y * tile_y + ty, th); - } - } - } -} - - -/* - * copy_tiles() gives a slight improvement over copy_tile() since - * adjacent runs of tiles are done all at once there is some savings - * due to contiguous memory access. Not a great speedup, but in - * some cases it can be up to 2X. Even more on a SunRay where no - * graphics hardware is involved in the read. Generally, graphics - * devices are optimized for write, not read, so we are limited by - * the read bandwidth, sometimes only 5 MB/sec on otherwise fast - * hardware. - */ - -int *first_line = NULL, *last_line; -unsigned short *left_diff, *right_diff; - -void copy_tiles(int tx, int ty, int nt) { - int x, y, line; - int size_x, size_y, width1, width2; - int off, len, n, dw, dx, t; - int w1, w2, dx1, dx2; /* tmps for normal and short tiles */ - int pixelsize = bpp >> 3; - int first_min, last_max; - - int restored_patch = 0; /* for show_mouse */ - - char *src, *dst, *s_src, *s_dst, *m_src, *m_dst; - char *h_src, *h_dst; - if (! first_line) { - /* allocate arrays first time in. */ - int n = ntiles_x + 1; - first_line = (int *) malloc((size_t) (n * sizeof(int))); - last_line = (int *) malloc((size_t) (n * sizeof(int))); - left_diff = (unsigned short *) - malloc((size_t) (n * sizeof(unsigned short))); - right_diff = (unsigned short *) - malloc((size_t) (n * sizeof(unsigned short))); - } - - x = tx * tile_x; - y = ty * tile_y; - - size_x = dpy_x - x; - if ( size_x > tile_x * nt ) { - size_x = tile_x * nt; - width1 = tile_x; - width2 = tile_x; - } else { - /* short tile */ - width1 = tile_x; /* internal tile */ - width2 = size_x - (nt - 1) * tile_x; /* right hand tile */ - } - - size_y = dpy_y - y; - if ( size_y > tile_y ) { - size_y = tile_y; - } - - n = tx + ty * ntiles_x; /* number of the first tile */ - - if (blackouts && tile_blackout[n].cover == 2) { - /* - * If there are blackouts and this tile is completely covered - * no need to poll screen or do anything else.. - * n.b. we are int single copy_tile mode: nt=1 - */ - tile_has_diff[n] = 0; - return; - } - - X_LOCK; - /* read in the whole tile run at once: */ - if ( using_shm && size_x == tile_x * nt && size_y == tile_y ) { - /* general case: */ - XShmGetImage(dpy, window, tile_row[nt], x, y, AllPlanes); - } else { - /* - * No shm or near bottom/rhs edge case: - * (but only if tile size does not divide screen size) - */ - XGetSubImage(dpy, window, x, y, size_x, size_y, AllPlanes, - ZPixmap, tile_row[nt], 0, 0); - } - X_UNLOCK; - - if (blackouts && tile_blackout[n].cover == 1) { - /* - * If there are blackouts and this tile is partially covered - * we should re-black-out the portion. - * n.b. we are int single copy_tile mode: nt=1 - */ - int x1, x2, y1, y2, b; - int w, s, fill = 0; - - for (b=0; b < tile_blackout[n].count; b++) { - char *b_dst = tile_row[nt]->data; - - x1 = tile_blackout[n].bo[b].x1 - x; - y1 = tile_blackout[n].bo[b].y1 - y; - x2 = tile_blackout[n].bo[b].x2 - x; - y2 = tile_blackout[n].bo[b].y2 - y; - - w = (x2 - x1) * pixelsize; - s = x1 * pixelsize; - - for (line = 0; line < size_y; line++) { - if (y1 <= line && line < y2) { - memset(b_dst + s, fill, (size_t) w); - } - b_dst += tile_row[nt]->bytes_per_line; - } - } - } - - /* - * Some awkwardness wrt the little remote mouse patch we display. - * When threaded we want to have as small a window of time - * as possible when the mouse image is not in the fb, otherwise - * a libvncserver thread may send the uncorrected patch to the - * clients. - */ - if (show_mouse && use_threads && cur_saved) { - /* check for overlap */ - if (cur_save_x + cur_save_w > x && x + size_x > cur_save_x && - cur_save_y + cur_save_h > y && y + size_y > cur_save_y) { - - /* restore the real data to the rfb fb */ - restore_mouse_patch(); - restored_patch = 1; - } - } - - src = tile_row[nt]->data; - dst = screen->frameBuffer + y * bytes_per_line + x * pixelsize; - - s_src = src; - s_dst = dst; - - for (t=1; t <= nt; t++) { - first_line[t] = -1; - } - /* find the first line with difference: */ - w1 = width1 * pixelsize; - w2 = width2 * pixelsize; - - - /* foreach line: */ - for (line = 0; line < size_y; line++) { - /* foreach horizontal tile: */ - for (t=1; t <= nt; t++) { - if (first_line[t] != -1) { - continue; - } - - off = (t-1) * w1; - if (t == nt) { - len = w2; /* possible short tile */ - } else { - len = w1; - } - - if (memcmp(s_dst + off, s_src + off, len)) { - first_line[t] = line; - } - } - s_src += tile_row[nt]->bytes_per_line; - s_dst += bytes_per_line; - } - - /* see if there were any differences for any tile: */ - first_min = -1; - for (t=1; t <= nt; t++) { - tile_tried[n+(t-1)] = 1; - if (first_line[t] != -1) { - if (first_min == -1 || first_line[t] < first_min) { - first_min = first_line[t]; - } - } - } - if (first_min == -1) { - /* no tile has a difference, note this and get out: */ - for (t=1; t <= nt; t++) { - tile_has_diff[n+(t-1)] = 0; - } - if (restored_patch) { - redraw_mouse(); - } - return; - } else { - /* - * at least one tile has a difference. make sure info - * is recorded (e.g. sometimes we guess tiles and they - * came in with tile_has_diff 0) - */ - for (t=1; t <= nt; t++) { - if (first_line[t] == -1) { - tile_has_diff[n+(t-1)] = 0; - } else { - tile_has_diff[n+(t-1)] = 1; - } - } - } - - m_src = src + (tile_row[nt]->bytes_per_line * size_y); - m_dst = dst + (bytes_per_line * size_y); - - for (t=1; t <= nt; t++) { - last_line[t] = first_line[t]; - } - - /* find the last line with difference: */ - w1 = width1 * pixelsize; - w2 = width2 * pixelsize; - - /* foreach line: */ - for (line = size_y - 1; line > first_min; line--) { - - m_src -= tile_row[nt]->bytes_per_line; - m_dst -= bytes_per_line; - - /* foreach tile: */ - for (t=1; t <= nt; t++) { - if (first_line[t] == -1 - || last_line[t] != first_line[t]) { - /* tile has no changes or already done */ - continue; - } - - off = (t-1) * w1; - if (t == nt) { - len = w2; /* possible short tile */ - } else { - len = w1; - } - if (memcmp(m_dst + off, m_src + off, len)) { - last_line[t] = line; - } - } - } - - /* - * determine the farthest down last changed line - * will be used below to limit our memcpy() to the framebuffer. - */ - last_max = -1; - for (t=1; t <= nt; t++) { - if (first_line[t] == -1) { - continue; - } - if (last_max == -1 || last_line[t] > last_max) { - last_max = last_line[t]; - } - } - - /* look for differences on left and right hand edges: */ - for (t=1; t <= nt; t++) { - left_diff[t] = 0; - right_diff[t] = 0; - } - - h_src = src; - h_dst = dst; - - w1 = width1 * pixelsize; - w2 = width2 * pixelsize; - - dx1 = (width1 - tile_fuzz) * pixelsize; - dx2 = (width2 - tile_fuzz) * pixelsize; - dw = tile_fuzz * pixelsize; - - /* foreach line: */ - for (line = 0; line < size_y; line++) { - /* foreach tile: */ - for (t=1; t <= nt; t++) { - if (first_line[t] == -1) { - /* tile has no changes at all */ - continue; - } - - off = (t-1) * w1; - if (t == nt) { - dx = dx2; /* possible short tile */ - if (dx <= 0) { - break; - } - } else { - dx = dx1; - } - - if (! left_diff[t] && memcmp(h_dst + off, - h_src + off, dw)) { - left_diff[t] = 1; - } - if (! right_diff[t] && memcmp(h_dst + off + dx, - h_src + off + dx, dw) ) { - right_diff[t] = 1; - } - } - h_src += tile_row[nt]->bytes_per_line; - h_dst += bytes_per_line; - } - - /* now finally copy the difference to the rfb framebuffer: */ - s_src = src + tile_row[nt]->bytes_per_line * first_min; - s_dst = dst + bytes_per_line * first_min; - - for (line = first_min; line <= last_max; line++) { - /* for I/O speed we do not do this tile by tile */ - memcpy(s_dst, s_src, size_x * pixelsize); - s_src += tile_row[nt]->bytes_per_line; - s_dst += bytes_per_line; - } - - if (restored_patch) { - redraw_mouse(); - } - - /* record all the info in the region array for this tile: */ - for (t=1; t <= nt; t++) { - int s = t - 1; - - if (first_line[t] == -1) { - /* tile unchanged */ - continue; - } - tile_region[n+s].first_line = first_line[t]; - tile_region[n+s].last_line = last_line[t]; - - tile_region[n+s].top_diff = 0; - tile_region[n+s].bot_diff = 0; - if ( first_line[t] < tile_fuzz ) { - tile_region[n+s].top_diff = 1; - } - if ( last_line[t] > (size_y - 1) - tile_fuzz ) { - tile_region[n+s].bot_diff = 1; - } - - tile_region[n+s].left_diff = left_diff[t]; - tile_region[n+s].right_diff = right_diff[t]; - } - -} - -/* - * The copy_tile() call in the loop below copies the changed tile into - * the rfb framebuffer. Note that copy_tile() sets the tile_region - * struct to have info about the y-range of the changed region and also - * whether the tile edges contain diffs (within distance tile_fuzz). - * - * We use this tile_region info to try to guess if the downward and right - * tiles will have diffs. These tiles will be checked later in the loop - * (since y+1 > y and x+1 > x). - * - * See copy_tiles_backward_pass() for analogous checking upward and - * left tiles. - */ -int copy_all_tiles() { - int x, y, n, m; - int diffs = 0; - - for (y=0; y < ntiles_y; y++) { - for (x=0; x < ntiles_x; x++) { - n = x + y * ntiles_x; - - if (tile_has_diff[n]) { - copy_tiles(x, y, 1); - } - if (! tile_has_diff[n]) { - /* - * n.b. copy_tiles() may have detected - * no change and reset tile_has_diff to 0. - */ - continue; - } - diffs++; - - /* neighboring tile downward: */ - if ( (y+1) < ntiles_y && tile_region[n].bot_diff) { - m = x + (y+1) * ntiles_x; - if (! tile_has_diff[m]) { - tile_has_diff[m] = 2; - } - } - /* neighboring tile to right: */ - if ( (x+1) < ntiles_x && tile_region[n].right_diff) { - m = (x+1) + y * ntiles_x; - if (! tile_has_diff[m]) { - tile_has_diff[m] = 2; - } - } - } - } - return diffs; -} - -/* - * Routine analogous to copy_all_tiles() above, but for horizontal runs - * of adjacent changed tiles. - */ -int copy_all_tile_runs() { - int x, y, n, m, i; - int diffs = 0; - int in_run = 0, run = 0; - int ntave = 0, ntcnt = 0; - - for (y=0; y < ntiles_y; y++) { - for (x=0; x < ntiles_x + 1; x++) { - n = x + y * ntiles_x; - - if (x != ntiles_x && tile_has_diff[n]) { - in_run = 1; - run++; - } else { - if (! in_run) { - in_run = 0; - run = 0; - continue; - } - copy_tiles(x - run, y, run); - - ntcnt++; - ntave += run; - diffs += run; - - /* neighboring tile downward: */ - for (i=1; i <= run; i++) { - if ((y+1) < ntiles_y - && tile_region[n-i].bot_diff) { - m = (x-i) + (y+1) * ntiles_x; - if (! tile_has_diff[m]) { - tile_has_diff[m] = 2; - } - } - } - - /* neighboring tile to right: */ - if (((x-1)+1) < ntiles_x - && tile_region[n-1].right_diff) { - m = ((x-1)+1) + y * ntiles_x; - if (! tile_has_diff[m]) { - tile_has_diff[m] = 2; - } - - /* note that this starts a new run */ - in_run = 1; - run = 1; - } else { - in_run = 0; - run = 0; - } - } - } - } - return diffs; -} - -/* - * Here starts a bunch of heuristics to guess/detect changed tiles. - * They are: - * copy_tiles_backward_pass, fill_tile_gaps/gap_try, grow_islands/island_try - */ - -/* - * Try to predict whether the upward and/or leftward tile has been modified. - * copy_all_tiles() has already done downward and rightward tiles. - */ -int copy_tiles_backward_pass() { - int x, y, n, m; - int diffs = 0; - - for (y = ntiles_y - 1; y >= 0; y--) { - for (x = ntiles_x - 1; x >= 0; x--) { - n = x + y * ntiles_x; /* number of this tile */ - - if (! tile_has_diff[n]) { - continue; - } - - m = x + (y-1) * ntiles_x; /* neighboring tile upward */ - - if (y >= 1 && ! tile_has_diff[m] && tile_region[n].top_diff) { - if (! tile_tried[m]) { - tile_has_diff[m] = 2; - copy_tiles(x, y-1, 1); - } - } - - m = (x-1) + y * ntiles_x; /* neighboring tile to left */ - - if (x >= 1 && ! tile_has_diff[m] && tile_region[n].left_diff) { - if (! tile_tried[m]) { - tile_has_diff[m] = 2; - copy_tiles(x-1, y, 1); - } - } - } - } - for (n=0; n < ntiles; n++) { - if (tile_has_diff[n]) { - diffs++; - } - } - return diffs; -} - -void gap_try(int x, int y, int *run, int *saw, int along_x) { - int n, m, i, xt, yt; - - n = x + y * ntiles_x; - - if (! tile_has_diff[n]) { - if (*saw) { - (*run)++; /* extend the gap run. */ - } - return; - } - if (! *saw || *run == 0 || *run > gaps_fill) { - *run = 0; /* unacceptable run. */ - *saw = 1; - return; - } - - for (i=1; i <= *run; i++) { /* iterate thru the run. */ - if (along_x) { - xt = x - i; - yt = y; - } else { - xt = x; - yt = y - i; - } - - m = xt + yt * ntiles_x; - if (tile_tried[m]) { /* do not repeat tiles */ - continue; - } - - copy_tiles(xt, yt, 1); - } - *run = 0; - *saw = 1; -} - -/* - * Look for small gaps of unchanged tiles that may actually contain changes. - * E.g. when paging up and down in a web broswer or terminal there can - * be a distracting delayed filling in of such gaps. gaps_fill is the - * tweak parameter that sets the width of the gaps that are checked. - * - * BTW, grow_islands() is actually pretty successful at doing this too... - */ -int fill_tile_gaps() { - int x, y, run, saw; - int n, diffs = 0; - - /* horizontal: */ - for (y=0; y < ntiles_y; y++) { - run = 0; - saw = 0; - for (x=0; x < ntiles_x; x++) { - gap_try(x, y, &run, &saw, 1); - } - } - - /* vertical: */ - for (x=0; x < ntiles_x; x++) { - run = 0; - saw = 0; - for (y=0; y < ntiles_y; y++) { - gap_try(x, y, &run, &saw, 0); - } - } - - for (n=0; n < ntiles; n++) { - if (tile_has_diff[n]) { - diffs++; - } - } - return diffs; -} - -void island_try(int x, int y, int u, int v, int *run) { - int n, m; - - n = x + y * ntiles_x; - m = u + v * ntiles_x; - - if (tile_has_diff[n]) { - (*run)++; - } else { - *run = 0; - } - - if (tile_has_diff[n] && ! tile_has_diff[m]) { - /* found a discontinuity */ - - if (tile_tried[m]) { - return; - } else if (*run < grow_fill) { - return; - } - - copy_tiles(u, v, 1); - } -} - -/* - * Scan looking for discontinuities in tile_has_diff[]. Try to extend - * the boundary of the discontinuity (i.e. make the island larger). - * Vertical scans are skipped since they do not seem to yield much... - */ -int grow_islands() { - int x, y, n, run; - int diffs = 0; - - /* - * n.b. the way we scan here should keep an extension going, - * and so also fill in gaps effectively... - */ - - /* left to right: */ - for (y=0; y < ntiles_y; y++) { - run = 0; - for (x=0; x <= ntiles_x - 2; x++) { - island_try(x, y, x+1, y, &run); - } - } - /* right to left: */ - for (y=0; y < ntiles_y; y++) { - run = 0; - for (x = ntiles_x - 1; x >= 1; x--) { - island_try(x, y, x-1, y, &run); - } - } - for (n=0; n < ntiles; n++) { - if (tile_has_diff[n]) { - diffs++; - } - } - return diffs; -} - -/* - * copy the whole X screen to the rfb framebuffer. For a large enough - * number of changed tiles, this is faster than tiles scheme at retrieving - * the info from the X server. Bandwidth to client and compression time - * are other issues... use -fs 1.0 to disable. - */ -void copy_screen() { - int pixelsize = bpp >> 3; - char *rfb_fb; - int i, y, block_size; - - block_size = (dpy_x * (dpy_y/fs_factor) * pixelsize); - - rfb_fb = screen->frameBuffer; - y = 0; - - X_LOCK; - - /* screen may be too big for 1 shm area, so broken into fs_factor */ - for (i=0; i < fs_factor; i++) { - if (using_shm) { - XShmGetImage(dpy, window, fullscreen, 0, y, AllPlanes); - } else { - XGetSubImage(dpy, window, 0, y, fullscreen->width, - fullscreen->height, AllPlanes, ZPixmap, fullscreen, - 0, 0); - } - memcpy(rfb_fb, fullscreen->data, (size_t) block_size); - - y += dpy_y / fs_factor; - rfb_fb += block_size; - } - - X_UNLOCK; - - if (blackouts) { - blackout_regions(); - } - - rfbMarkRectAsModified(screen, 0, 0, dpy_x, dpy_y); -} - -/* profiling routines */ - -double dtime(double *t_old) { - /* - * usage: call with 0.0 to initialize, subsequent calls give - * the time differences. - */ - double t_now, dt; - struct timeval now; - - gettimeofday(&now, NULL); - t_now = now.tv_sec + ( (double) now.tv_usec/1000000. ); - if (*t_old == 0) { - *t_old = t_now; - return t_now; - } - dt = t_now - *t_old; - *t_old = t_now; - return(dt); -} - - -/* - * Utilities for managing the "naps" to cut down on amount of polling. - */ -void nap_set(int tile_cnt) { - - if (count == 0) { - /* roll up check for all NSCAN scans */ - nap_ok = 0; - if (naptile && nap_diff_count < 2 * NSCAN * naptile) { - /* "2" is a fudge to permit a bit of bg drawing */ - nap_ok = 1; - } - nap_diff_count = 0; - } - - if (show_mouse) { - /* kludge for the up to 4 tiles the mouse patch could occupy */ - if ( tile_cnt > 4) { - last_event = time(0); - } - } else if (tile_cnt != 0) { - last_event = time(0); - } -} - -void nap_sleep(int ms, int split) { - int i, input = got_user_input; - - /* split up a long nap to improve the wakeup time */ - for (i=0; i 0) { - int dt = (int) (now - last_event); - int ms = 1500; - - /* if no activity, pause here for a second or so. */ - if (dt > screen_blank) { - nap_sleep(ms, 8); - return; - } - } - if (naptile && nap_ok && tile_cnt < naptile) { - int ms = napfac * waitms; - ms = ms > napmax ? napmax : ms; - if (now - last_input <= 2) { - nap_ok = 0; - } else { - nap_sleep(ms, 1); - } - } -} - -/* - * This is called to avoid a ~20 second timeout in libvncserver. - * May no longer be needed. - */ -void ping_clients(int tile_cnt) { - static time_t last_send = 0; - time_t now = time(0); - - if (rfbMaxClientWait < 20000) { - rfbMaxClientWait = 20000; - rfbLog("reset rfbMaxClientWait to %d ms.\n", - rfbMaxClientWait); - } - if (tile_cnt) { - last_send = now; - } else if (now - last_send > 1) { - /* Send small heartbeat to client */ - rfbMarkRectAsModified(screen, 0, 0, 1, 1); - last_send = now; - } -} - -/* - * scan_display() wants to know if this tile can be skipped due to - * blackout regions: (no data compare is done, just a quick geometric test) - */ -int blackout_line_skip(int n, int x, int y, int rescan, int *tile_count) { - - if (tile_blackout[n].cover == 2) { - tile_has_diff[n] = 0; - return 1; /* skip it */ - - } else if (tile_blackout[n].cover == 1) { - int w, x1, y1, x2, y2, b, hit = 0; - if (x + NSCAN > dpy_x) { - w = dpy_x - x; - } else { - w = NSCAN; - } - - for (b=0; b < tile_blackout[n].count; b++) { - - /* n.b. these coords are in full display space: */ - x1 = tile_blackout[n].bo[b].x1; - x2 = tile_blackout[n].bo[b].x2; - y1 = tile_blackout[n].bo[b].y1; - y2 = tile_blackout[n].bo[b].y2; - - if (x2 - x1 < w) { - /* need to cover full width */ - continue; - } - if (y1 <= y && y < y2) { - hit = 1; - break; - } - } - if (hit) { - if (! rescan) { - tile_has_diff[n] = 0; - } else { - *tile_count += tile_has_diff[n]; - } - return 1; /* skip */ - } - } - return 0; /* do not skip */ -} - -/* - * scan_display() wants to know if this changed tile can be skipped due - * to blackout regions (we do an actual compare to find the changed region). - */ -int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src, - int w, int pixelsize) { - - int i, x1, y1, x2, y2, b, hit = 0; - int beg = -1, end = -1; - - if (tile_blackout[n].cover == 0) { - return 0; /* 0 means do not skip it. */ - } else if (tile_blackout[n].cover == 2) { - return 1; /* 1 means skip it. */ - } - - /* tile has partial coverage: */ - - for (i=0; i < w * pixelsize; i++) { - if (*(dst+i) != *(src+i)) { - beg = i/pixelsize; /* beginning difference */ - break; - } - } - for (i = w * pixelsize - 1; i >= 0; i--) { - if (*(dst+i) != *(src+i)) { - end = i/pixelsize; /* ending difference */ - break; - } - } - if (beg < 0 || end < 0) { - /* problem finding range... */ - return 0; - } - - /* loop over blackout rectangles: */ - for (b=0; b < tile_blackout[n].count; b++) { - - /* y in full display space: */ - y1 = tile_blackout[n].bo[b].y1; - y2 = tile_blackout[n].bo[b].y2; - - /* x relative to tile origin: */ - x1 = tile_blackout[n].bo[b].x1 - x; - x2 = tile_blackout[n].bo[b].x2 - x; - - if (y1 > y || y >= y2) { - continue; - } - if (x1 <= beg && end <= x2) { - hit = 1; - break; - } - } - if (hit) { - return 1; - } else { - return 0; - } -} - -/* - * Loop over 1-pixel tall horizontal scanlines looking for changes. - * Record the changes in tile_has_diff[]. Scanlines in the loop are - * equally spaced along y by NSCAN pixels, but have a slightly random - * starting offset ystart ( < NSCAN ) from scanlines[]. - */ -int scan_display(int ystart, int rescan) { - char *src, *dst; - int pixelsize = bpp >> 3; - int x, y, w, n; - int tile_count = 0; - int whole_line = 1, nodiffs = 0; - - y = ystart; - - while (y < dpy_y) { - - /* grab the horizontal scanline from the display: */ - X_LOCK; - if (using_shm) { - XShmGetImage(dpy, window, scanline, 0, y, AllPlanes); - } else { - XGetSubImage(dpy, window, 0, y, scanline->width, - scanline->height, AllPlanes, ZPixmap, scanline, - 0, 0); - } - X_UNLOCK; - - /* for better memory i/o try the whole line at once */ - src = scanline->data; - dst = screen->frameBuffer + y * bytes_per_line; - - if (whole_line && ! memcmp(dst, src, bytes_per_line)) { - /* no changes anywhere in scan line */ - nodiffs = 1; - if (! rescan) { - y += NSCAN; - continue; - } - } - - x = 0; - while (x < dpy_x) { - n = (x/tile_x) + (y/tile_y) * ntiles_x; - - if (blackouts) { - if (blackout_line_skip(n, x, y, rescan, - &tile_count)) { - x += NSCAN; - continue; - } - } - - if (rescan) { - if (nodiffs || tile_has_diff[n]) { - tile_count += tile_has_diff[n]; - x += NSCAN; - continue; - } - } - - /* set ptrs to correspond to the x offset: */ - src = scanline->data + x * pixelsize; - dst = screen->frameBuffer + y * bytes_per_line - + x * pixelsize; - - /* compute the width of data to be compared: */ - if (x + NSCAN > dpy_x) { - w = dpy_x - x; - } else { - w = NSCAN; - } - - if (memcmp(dst, src, w * pixelsize)) { - /* found a difference, record it: */ - if (! blackouts) { - tile_has_diff[n] = 1; - tile_count++; - } else { - if (blackout_line_cmpskip(n, x, y, - dst, src, w, pixelsize)) { - tile_has_diff[n] = 0; - } else { - tile_has_diff[n] = 1; - tile_count++; - } - } - } - x += NSCAN; - } - y += NSCAN; - } - return tile_count; -} - -/* - * toplevel for the scanning, rescanning, and applying the heuristics. - */ -void scan_for_updates() { - int i, tile_count, tile_diffs; - double frac1 = 0.1; /* tweak parameter to try a 2nd scan_display() */ - double frac2 = 0.35; /* or 3rd */ - for (i=0; i < ntiles; i++) { - tile_has_diff[i] = 0; - tile_tried[i] = 0; - } - - /* - * n.b. this program has only been tested so far with - * tile_x = tile_y = NSCAN = 32! - */ - - count++; - count %= NSCAN; - - if (count % (NSCAN/4) == 0) { - /* some periodic maintenance */ - - if (subwin) { - set_offset(); /* follow the subwindow */ - } - if (indexed_colour) { /* check for changed colormap */ - set_colormap(); - } - } - - if (show_mouse && ! use_threads) { - /* single-thread is safe to do it here for all scanning */ - restore_mouse_patch(); - } - - /* scan with the initial y to the jitter value from scanlines: */ - scan_in_progress = 1; - tile_count = scan_display(scanlines[count], 0); - - nap_set(tile_count); - - if (fs_factor && frac1 >= fs_frac) { - /* make frac1 < fs_frac if fullscreen updates are enabled */ - frac1 = fs_frac/2.0; - } - - if (tile_count > frac1 * ntiles) { - /* - * many tiles have changed, so try a rescan (since it should - * be short compared to the many upcoming copy_tiles() calls) - */ - - /* this check is done to skip the extra scan_display() call */ - if (! fs_factor || tile_count <= fs_frac * ntiles) { - int cp, tile_count_old = tile_count; - - /* choose a different y shift for the 2nd scan: */ - cp = (NSCAN - count) % NSCAN; - - tile_count = scan_display(scanlines[cp], 1); - - if (tile_count >= (1 + frac2) * tile_count_old) { - /* on a roll... do a 3rd scan */ - cp = (NSCAN - count + 7) % NSCAN; - tile_count = scan_display(scanlines[cp], 1); - } - } - scan_in_progress = 0; - - /* - * At some number of changed tiles it is better to just - * copy the full screen at once. I.e. time = c1 + m * r1 - * where m is number of tiles, r1 is the copy_tiles() - * time, and c1 is the scan_display() time: for some m - * it crosses the full screen update time. - * - * We try to predict that crossover with the fs_frac - * fudge factor... seems to be about 1/2 the total number - * of tiles. n.b. this ignores network bandwidth, - * compression time etc... - * - * Use -fs 1.0 to disable on slow links. - */ - if (fs_factor && tile_count > fs_frac * ntiles) { - fb_copy_in_progress = 1; - copy_screen(); - if (show_mouse || cursor_pos) { - if (show_mouse && ! use_threads) { - redraw_mouse(); - } - update_mouse(); - } - fb_copy_in_progress = 0; - if (use_threads && ! old_pointer) { - pointer(-1, 0, 0, NULL); - } - nap_check(tile_count); - return; - } - } - scan_in_progress = 0; - - /* copy all tiles with differences from display to rfb framebuffer: */ - fb_copy_in_progress = 1; - - if (single_copytile) { - /* - * Old way, copy I/O one tile at a time. - */ - tile_diffs = copy_all_tiles(); - } else { - /* - * New way, does runs of horizontal tiles at once. - * Note that below, for simplicity, the extra tile finding - * (e.g. copy_tiles_backward_pass) is done the old way. - */ - tile_diffs = copy_all_tile_runs(); - } - - /* - * This backward pass for upward and left tiles complements what - * was done in copy_all_tiles() for downward and right tiles. - */ - tile_diffs = copy_tiles_backward_pass(); - - /* Given enough tile diffs, try the islands: */ - if (grow_fill && tile_diffs > 4) { - tile_diffs = grow_islands(); - } - - /* Given enough tile diffs, try the gaps: */ - if (gaps_fill && tile_diffs > 4) { - tile_diffs = fill_tile_gaps(); - } - - fb_copy_in_progress = 0; - if (use_threads && ! old_pointer) { - /* - * tell the pointer handler it can process any queued - * pointer events: - */ - pointer(-1, 0, 0, NULL); - } - - if (blackouts) { - /* ignore any diffs in completely covered tiles */ - int x, y, n; - for (y=0; y < ntiles_y; y++) { - for (x=0; x < ntiles_x; x++) { - n = x + y * ntiles_x; - if (tile_blackout[n].cover == 2) { - tile_has_diff[n] = 0; - } - } - } - } - - if (use_hints) { - hint_updates(); /* use krfb/x0rfbserver hints algorithm */ - } else { - tile_updates(); /* send each tile change individually */ - } - - /* Work around threaded rfbProcessClientMessage() calls timeouts */ - if (use_threads) { - ping_clients(tile_diffs); - } - - /* Handle the remote mouse pointer */ - if (show_mouse || cursor_pos) { - if (show_mouse && ! use_threads) { - redraw_mouse(); - } - update_mouse(); - } - - nap_check(tile_diffs); -} - -int check_user_input(double, int *); - -void watch_loop(void) { - int cnt = 0; - double dt = 0.0; - - if (use_threads) { - rfbRunEventLoop(screen, -1, TRUE); - } - - while (1) { - - got_user_input = 0; - got_pointer_input = 0; - got_keyboard_input = 0; - - if (! use_threads) { - rfbProcessEvents(screen, -1); - if (check_user_input(dt, &cnt)) { - /* true means loop back for more input */ - continue; - } - } - - if (shut_down) { - clean_up_exit(0); - } - - watch_xevents(); - check_connect_inputs(); - - if (! screen->rfbClientHead) { /* waiting for a client */ - usleep(200 * 1000); - continue; - } - - if (nofb) { /* no framebuffer polling needed */ - if (cursor_pos) { - update_mouse(); - } - continue; - } - - if (watch_bell) { - /* - * check for any bell events. - * n.b. assumes -nofb folks do not want bell... - */ - watch_bell_event(); - } - if (! show_dragging && button_mask) { - /* if any button is pressed do not update screen */ - /* XXX consider: use_threads || got_pointer_input */ - X_LOCK; - XFlush(dpy); - X_UNLOCK; - } else { - /* for timing the scan to try to detect thrashing */ - double tm = 0.0; - dtime(&tm); - - rfbUndrawCursor(screen); - scan_for_updates(); - - dt = dtime(&tm); - } - - /* sleep a bit to lessen load */ - usleep(waitms * 1000); - cnt++; - } -} - -/* - * We need to handle user input, particularly pointer input, carefully. - * This function is only called when non-threaded. Note that - * rfbProcessEvents() only processes *one* pointer event per call, - * so if we interlace it with scan_for_updates(), we can get swamped - * with queued up pointer inputs. And if the pointer inputs are inducing - * large changes on the screen (e.g. window drags), the whole thing - * bogs down miserably and only comes back to life at some time after - * one stops moving the mouse. So, to first approximation, we are trying - * to eat as much user input here as we can using some hints from the - * duration of the previous scan_for_updates() call (in dt). - * - * note: we do this even under -nofb - * - * return of 1 means watch_loop should short-circuit and reloop, - * return of 0 means watch_loop should proceed to scan_for_updates(). - */ - -int check_user_input(double dt, int *cnt) { - - if (old_pointer) { - /* every n-th drops thru to scan */ - if ((got_user_input || ui_skip < 0) && *cnt % ui_skip != 0) { - *cnt++; - XFlush(dpy); - return 1; /* short circuit watch_loop */ - } else { - return 0; - } - } - - if (got_keyboard_input) { - if (*cnt % ui_skip != 0) { - *cnt++; - return 1; /* short circuit watch_loop */ - } - /* otherwise continue with pointer input */ - } - - if (got_pointer_input) { - int eaten = 0, miss = 0, max_eat = 50; - int g, g_in; - double spin = 0.0, tm = 0.0; - double quick_spin_fac = 0.40; - double grind_spin_time = 0.175; - - - dtime(&tm); - g = g_in = got_pointer_input; - - /* - * Try for some "quick" pointer input processing. - * - * About as fast as we can, we try to process user input - * calling rfbProcessEvents or rfbCheckFds. We do this - * for a time on order of the last scan_for_updates() time, - * dt, but if we stop getting user input we break out. - * We will also break out if we have processed max_eat - * inputs. - * - * Note that rfbCheckFds() does not send any framebuffer - * updates, so is more what we want here, although it is - * likely they have all be sent already. - */ - while (1) { - rfbCheckFds(screen, 1000); - XFlush(dpy); - - spin += dtime(&tm); - - if (spin > quick_spin_fac * dt) { - /* get out if spin time comparable to last scan time */ - break; - } - if (got_pointer_input > g) { - g = got_pointer_input; - if (eaten++ < max_eat) { - continue; - } - } else { - miss++; - } - if (miss > 1) { /* 1 means out on 2nd miss */ - break; - } - } - - - /* - * Probably grinding with a lot of fb I/O if dt is - * this large. (need to do this more elegantly) - * - * Current idea is to spin our wheels here *not* processing - * any fb I/O, but still processing the user input. - * This user input goes to the X display and changes it, - * but we don't poll it while we "rest" here for a time - * on order of dt, the previous scan_for_updates() time. - * We also break out if we miss enough user input. - */ - if (dt > grind_spin_time) { - int i, ms, split = 30; - double shim; - - /* - * Break up our pause into 'split' steps. - * We get at most one input per step. - */ - shim = 0.75 * dt / split; - - ms = (int) (1000 * shim); - - /* cutoff how long the pause can be */ - if (split * ms > 300) { - ms = 300 / split; - } - - spin = 0.0; - tm = 0.0; - dtime(&tm); - - g = got_pointer_input; - miss = 0; - for (i=0; i g) { - XFlush(dpy); - miss = 0; - } else { - miss++; - } - g = got_pointer_input; - if (miss > 2) { - break; - } - if (1000 * spin > ms * split) { - break; - } - } - } - } - return 0; -} - -void print_help() { - char help[] = -"\n" -"x11vnc: allow VNC connections to real X11 displays.\n" -"\n" -"Typical usage is:\n" -"\n" -" Run this command in a shell on the remote machine \"far-host\":\n" -"\n" -" x11vnc -display :0\n" -"\n" -" Then run this in another window on the machine you are sitting at:\n" -"\n" -" vncviewer far-host:0\n" -"\n" -"Once x11vnc establishes connections with the X11 server and starts\n" -"listening as a VNC server it will print out a string: PORT=XXXX where\n" -"XXXX is typically 5900 (the default VNC port). One would next run something\n" -"like this on the local machine: \"vncviewer host:N\" where N is XXXX - 5900.\n" -"\n" -"By default x11vnc will not allow the screen to be shared and it will\n" -"exit as soon as a client disconnects. See -shared and -forever below\n" -"to override these protections.\n" -"\n" -"For additional info see: http://www.karlrunge.com/x11vnc/\n" -" and http://www.karlrunge.com/x11vnc/#faq\n" -"\n" -"Options:\n" -"\n" -"-display disp X11 server display to connect to, the X server process\n" -" must be running on same machine and support MIT-SHM.\n" -" Equivalent to setting the DISPLAY env. variable.\n" -"-id windowid Show the window corresponding to not the\n" -" entire display. Warning: bugs! new toplevels missed!...\n" -"-flashcmap In 8bpp indexed color, let the installed colormap flash\n" -" as the pointer moves from window to window (slow).\n" -"-notruecolor Force 8bpp indexed color even if it looks like TrueColor.\n" -"\n" -"-visual n Experimental option: probably does not do what you\n" -" think. It simply *forces* the visual used for the\n" -" framebuffer; this may be a bad thing... It is useful for\n" -" testing and for some workarounds. n may be a decimal\n" -" number, or 0x hex. Run xdpyinfo(1) for the values.\n" -" One may also use \"TrueColor\", etc. see \n" -" for a list. If the string ends in \":m\" for better\n" -" or for worse the visual depth is forced to be m.\n" -"\n" -"-viewonly All clients can only watch (default %s).\n" -"-shared VNC display is shared (default %s).\n" -"-forever Keep listening for more connections rather than exiting\n" -" as soon as the first client(s) disconnect. Same as -many\n" -"-connect string For use with \"vncviewer -listen\" reverse connections.\n" -" If string has the form \"host\" or \"host:port\"\n" -" the connection is made once at startup. Use commas\n" -" for a list. If string contains \"/\" it is a file to\n" -" periodically check for new hosts. The first line is\n" -" read and then file is truncated.\n" -"-vncconnect Monitor the VNC_CONNECT X property set by vncconnect(1).\n" -"-auth file Set the X authority file to be \"file\", equivalent to\n" -" setting the XAUTHORITY env. var to \"file\" before startup.\n" -"-allow addr1[,addr2..] Only allow client connections from IP addresses matching\n" -" the comma separated list of numerical addresses.\n" -" Can be a prefix, e.g. \"192.168.100.\" to match a\n" -" simple subnet, for more control build libvncserver with\n" -" libwrap support.\n" -"-localhost Same as -allow 127.0.0.1\n" -"-passwdfile filename Specify libvncserver -passwd via the first line of file\n" -" \"filename\" instead of via command line. Note: this\n" -" is a simple plaintext passwd, see also -rfbauth below.\n" -"-accept string Run a command (possibly to prompt the user at the\n" -" X11 display) to decide whether an incoming client\n" -" should be allowed to connect or not. \"string\" is\n" -" an external command run via system(3) (see below for\n" -" special cases). Be sure to quote \"string\" if it\n" -" contains spaces, etc. The RFB_CLIENT_IP environment\n" -" variable will be set to the incoming client IP number\n" -" and the port in RFB_CLIENT_PORT (or -1 if unavailable).\n" -" The x11vnc process id will be in RFB_X11VNC_PID and a\n" -" client id number in RFB_CLIENT_ID. If the external\n" -" command returns 0 the client is accepted, otherwise\n" -" the client is rejected. See below for an extension to\n" -" accept a client view-only.\n" -"\n" -" If \"string\" is \"popup\" then a builtin popup window\n" -" is used. The popup will time out after 120 seconds,\n" -" use \"popup:N\" to modify the timeout to N seconds\n" -" (use 0 for no timeout)\n" -"\n" -" If \"string\" is \"xmessage\" then an xmessage(1)\n" -" invocation is used for the command.\n" -"\n" -" Both \"popup\" and \"xmessage\" will present an option\n" -" for accepting the client \"View-Only\" (the client\n" -" can only watch). This option will not be presented if\n" -" -viewonly has been specified, in which case the entire\n" -" display is view only.\n" -"\n" -" If the user supplied command is prefixed with something\n" -" like \"yes:0,no:*,view:3 mycommand ...\" then this\n" -" associates the numerical command return code with\n" -" the actions: accept, reject, and accept-view-only,\n" -" respectively. Use \"*\" instead of a number to indicate\n" -" the default action (in case the command returns an\n" -" unexpected value). E.g. \"no:*\" is a good choice.\n" -"\n" -" Note that x11vnc blocks while the external command or\n" -" or popup is running (other clients may see no updates\n" -" during this period).\n" -"\n" -" More -accept tricks: use \"popupmouse\" to only allow\n" -" mouse clicks in the builtin popup to be recognized.\n" -" Similarly use \"popupkey\" to only recognize keystroke\n" -" responses. All 3 of the popup keywords can be followed\n" -" by +N+M to supply a position for the popup window.\n" -" The default is to center the popup window.\n" -"\n" -"-gone string As -accept string, except to run a user supplied command\n" -" when a client goes away (disconnects). Unlike -accept,\n" -" the command return code is not interpreted by x11vnc.\n" -"\n" -"-inetd Launched by inetd(1): stdio instead of listening socket.\n" -" Note: if you are not redirecting stderr to a log file\n" -" you must also specify the -q option.\n" -"\n" -"-noshm Do not use the MIT-SHM extension for the polling.\n" -" Remote displays can be polled this way: be careful this\n" -" can use large amounts of network bandwidth. This is\n" -" also of use if the local machine has a limited number\n" -" of shm segments and -onetile is not sufficient.\n" -"-flipbyteorder Sometimes needed if remotely polled host has different\n" -" endianness. Ignored unless -noshm is set.\n" -"-blackout string Black out rectangles on the screen. string is a comma\n" -" separated list of WxH+X+Y type geometries for each rect.\n" -"-xinerama If your screen is composed of multiple monitors\n" -" glued together via XINERAMA, and that screen is\n" -" non-rectangular this option will try to guess the areas\n" -" to black out (if your system has libXinerama).\n" -"\n" -"-o logfile Write stderr messages to file \"logfile\" instead of\n" -" to the terminal. Same as -logfile.\n" -"-q Be quiet by printing less informational output to\n" -" stderr. Same as -quiet.\n" -"-bg Go into the background after screen setup. Messages to\n" -" stderr are lost unless -o logfile is used. Something\n" -" like this could be useful in a script:\n" -" port=`ssh $host \"x11vnc -display :0 -bg\" | grep PORT`\n" -" port=`echo \"$port\" | sed -e 's/PORT=//'`\n" -" port=`expr $port - 5900`\n" -" vncviewer $host:$port\n" -"\n" -"-modtweak Handle AltGr/Shift modifiers for differing languages\n" -" between client and host (default %s).\n" -"-nomodtweak Send the keysym directly to the X server.\n" -"-remap string Read keysym remappings from file \"string\". Format is\n" -" one pair of keysyms per line (can be name or hex value).\n" -" \"string\" can also be of form: key1-key2,key3-key4...\n" -" To map a key to a button click, use the fake keysyms\n" -" \"Button1\", ..., etc. E.g. -remap Super_R-Button2\n" -"-nobell Do not watch for XBell events.\n" -"-nofb Ignore framebuffer: only process keyboard and pointer.\n" -"-nosel Do not manage exchange of X selection/cutbuffer.\n" -"-noprimary Do not poll the PRIMARY selection for changes and send\n" -" back to clients. PRIMARY is still set on received\n" -" changes, however.\n" -"\n" -"-nocursor Do not have the viewer show a local cursor.\n" -"-mouse Draw a 2nd cursor at the current X pointer position.\n" -"-mouseX As -mouse, but also draw an X on root background.\n" -"-X Shorthand for -mouseX -nocursor.\n" -"-xwarppointer Move the pointer with XWarpPointer instead of XTEST\n" -" (try as a workaround if pointer behaves poorly, e.g.\n" -" on touchscreens or other non-standard setups).\n" -"-cursorpos Send the X cursor position back to all vnc clients that\n" -" support the TightVNC CursorPosUpdates extension.\n" -"-buttonmap string String to remap mouse buttons. Format: IJK-LMN, this\n" -" maps buttons I -> L, etc., e.g. -buttonmap 13-31\n" -"\n" -" Button presses can also be mapped to keystrokes: replace\n" -" a button digit on the right of the dash with ::\n" -" or :+: etc. for multiple keys. For example,\n" -" if the viewing machine has a mouse-wheel (buttons 4 5)\n" -" but the x11vnc side does not, these will do scrolls:\n" -" -buttonmap 12345-123:Prior::Next:\n" -" -buttonmap 12345-123:Up+Up+Up::Down+Down+Down:\n" -"\n" -" If you include a modifier like \"Shift_L\" the\n" -" modifier's up/down state is toggled, e.g. to send\n" -" \"The\" use :Shift_L+t+Shift_L+h+e: (the 1st one is\n" -" shift down and the 2nd one is shift up). (note: the\n" -" initial state of the modifier is ignored and not reset)\n" -" To include button events use \"Button1\", ... etc.\n" -"\n" -"-nodragging Do not update the display during mouse dragging events\n" -" (mouse motion with a button held down). Greatly\n" -" improves response on slow setups, but you lose all\n" -" visual feedback for drags, text selection, and some\n" -" menu traversals.\n" -"-old_pointer Do not use the new pointer input handling mechanisms.\n" -" See check_input() and pointer() for details.\n" -"-input_skip n For the old pointer handling when non-threaded: try to\n" -" read n user input events before scanning display. n < 0\n" -" means to act as though there is always user input.\n" -"\n" -"-debug_pointer Print debugging output for every pointer event.\n" -"-debug_keyboard Print debugging output for every keyboard event.\n" -"\n" -"-defer time Time in ms to wait for updates before sending to client\n" -" [rfbDeferUpdateTime] (default %d).\n" -"-wait time Time in ms to pause between screen polls. Used to cut\n" -" down on load (default %d).\n" -"-nap Monitor activity and if low take longer naps between\n" -" polls to really cut down load when idle (default %s).\n" -"-sigpipe string Broken pipe (SIGPIPE) handling. \"string\" can be\n" -" \"ignore\" or \"exit\". For \"ignore\" libvncserver\n" -" will handle the abrupt loss of a client and continue,\n" -" for \"exit\" x11vnc will cleanup and exit at the 1st\n" -" broken connection. Default is \"ignore\".\n" -#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD -"-threads Whether or not to use the threaded libvncserver\n" -"-nothreads algorithm [rfbRunEventLoop] (default %s).\n" -#endif -"\n" -"-fs f If the fraction of changed tiles in a poll is greater\n" -" than f, the whole screen is updated (default %.2f).\n" -"-onetile Do not use the new copy_tiles() framebuffer mechanism,\n" -" just use 1 shm tile for polling. Same as -old_copytile.\n" -" Limits shm segments used to 3.\n" -"-gaps n Heuristic to fill in gaps in rows or cols of n or\n" -" less tiles. Used to improve text paging (default %d).\n" -"-grow n Heuristic to grow islands of changed tiles n or wider\n" -" by checking the tile near the boundary (default %d).\n" -"-fuzz n Tolerance in pixels to mark a tiles edges as changed\n" -" (default %d).\n" -"-hints Use krfb/x0rfbserver hints (glue changed adjacent\n" -" horizontal tiles into one big rectangle) (default %s).\n" -"-nohints Do not use hints; send each tile separately.\n" -"%s\n" -"\n" -"These options are passed to libvncserver:\n" -"\n" -; - fprintf(stderr, help, - view_only ? "on":"off", - shared ? "on":"off", - use_modifier_tweak ? "on":"off", - defer_update, - waitms, - take_naps ? "on":"off", -#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD - use_threads ? "on":"off", -#endif - fs_frac, - gaps_fill, - grow_fill, - tile_fuzz, - use_hints ? "on":"off", - "" - ); - - rfbUsage(); - exit(1); -} - -/* - * choose a desktop name - */ -#define MAXN 256 - -char *this_host() { - char host[MAXN]; -#ifdef LIBVNCSERVER_HAVE_GETHOSTNAME - if (gethostname(host, MAXN) == 0) { - return strdup(host); - } -#endif - return NULL; -} - -char *choose_title(char *display) { - static char title[(MAXN+10)]; - strcpy(title, "x11vnc"); - - if (display == NULL) { - display = getenv("DISPLAY"); - } - if (display == NULL) { - return title; - } - title[0] = '\0'; - if (display[0] == ':') { - if (this_host() != NULL) { - strncpy(title, this_host(), MAXN - strlen(title)); - } - } - strncat(title, display, MAXN - strlen(title)); - if (subwin) { - char *name; - if (XFetchName(dpy, window, &name)) { - strncat(title, " ", MAXN - strlen(title)); - strncat(title, name, MAXN - strlen(title)); - } - } - return title; -} - -/* - * check blacklist for OSs with tight shm limits. - */ -int limit_shm(void) { - struct utsname ut; - int limit = 0; - - if (uname(&ut) == -1) { - return 0; - } - if (!strcmp(ut.sysname, "SunOS")) { - char *r = ut.release; - if (*r == '5' && *(r+1) == '.') { - if (strchr("2345678", *(r+2)) != NULL) { - limit = 1; - } - } - } - if (limit && ! quiet) { - fprintf(stderr, "reducing shm usage on %s %s (adding " - "-onetile)\n", ut.sysname, ut.release); - } - return limit; -} - -int main(int argc, char** argv) { - - XImage *fb; - int i, op, ev, er, maj, min; - char *use_dpy = NULL; - char *auth_file = NULL; - char *arg, *visual_str = NULL; - char *logfile = NULL; - char *passwdfile = NULL; - int pw_loc = -1; - int dt = 0; - int bg = 0; - int got_rfbwait = 0; - int got_deferupdate = 0, got_defer = 0; - - /* used to pass args we do not know about to rfbGetScreen(): */ - int argc2 = 1; char *argv2[128]; - - argv2[0] = strdup(argv[0]); - - for (i=1; i < argc; i++) { - /* quick-n-dirty --option handling. */ - arg = argv[i]; - if (strstr(arg, "--") == arg) { - arg++; - } - - if (!strcmp(arg, "-display")) { - use_dpy = argv[++i]; - } else if (!strcmp(arg, "-id")) { - if (sscanf(argv[++i], "0x%x", &subwin) != 1) { - if (sscanf(argv[i], "%d", &subwin) != 1) { - fprintf(stderr, "bad -id arg: %s\n", - argv[i]); - exit(1); - } - } - } else if (!strcmp(arg, "-visual")) { - visual_str = argv[++i]; - } else if (!strcmp(arg, "-flashcmap")) { - flash_cmap = 1; - } else if (!strcmp(arg, "-notruecolor")) { - force_indexed_color = 1; - } else if (!strcmp(arg, "-viewonly")) { - view_only = 1; - } else if (!strcmp(arg, "-passwdfile")) { - passwdfile = argv[++i]; - } else if (!strcmp(arg, "-shared")) { - shared = 1; - } else if (!strcmp(arg, "-auth")) { - auth_file = argv[++i]; - } else if (!strcmp(arg, "-allow")) { - allow_list = argv[++i]; - } else if (!strcmp(arg, "-localhost")) { - allow_list = "127.0.0.1"; - } else if (!strcmp(arg, "-accept")) { - accept_cmd = argv[++i]; - } else if (!strcmp(arg, "-gone")) { - gone_cmd = argv[++i]; - } else if (!strcmp(arg, "-many") - || !strcmp(arg, "-forever")) { - connect_once = 0; - } else if (!strcmp(arg, "-connect")) { - i++; - if (strchr(arg, '/')) { - client_connect_file = argv[i]; - } else { - client_connect = strdup(argv[i]); - } - } else if (!strcmp(arg, "-vncconnect")) { - vnc_connect = 1; - } else if (!strcmp(arg, "-inetd")) { - inetd = 1; - } else if (!strcmp(arg, "-noshm")) { - using_shm = 0; - } else if (!strcmp(arg, "-flipbyteorder")) { - flip_byte_order = 1; - } else if (!strcmp(arg, "-modtweak")) { - use_modifier_tweak = 1; - } else if (!strcmp(arg, "-nomodtweak")) { - use_modifier_tweak = 0; - } else if (!strcmp(arg, "-remap")) { - remap_file = argv[++i]; - } else if (!strcmp(arg, "-blackout")) { - blackout_string = argv[++i]; - } else if (!strcmp(arg, "-xinerama")) { - xinerama = 1; - } else if (!strcmp(arg, "-nobell")) { - watch_bell = 0; - } else if (!strcmp(arg, "-nofb")) { - nofb = 1; - } else if (!strcmp(arg, "-nosel")) { - watch_selection = 0; - } else if (!strcmp(arg, "-noprimary")) { - watch_primary = 0; - } else if (!strcmp(arg, "-nocursor")) { - local_cursor = 0; - } else if (!strcmp(arg, "-mouse")) { - show_mouse = 1; - } else if (!strcmp(arg, "-mouseX")) { - show_mouse = 1; - show_root_cursor = 1; - } else if (!strcmp(arg, "-X")) { - show_mouse = 1; - show_root_cursor = 1; - local_cursor = 0; - } else if (!strcmp(arg, "-xwarppointer")) { - use_xwarppointer = 1; - } else if (!strcmp(arg, "-cursorpos")) { - cursor_pos = 1; - } else if (!strcmp(arg, "-buttonmap")) { - pointer_remap = argv[++i]; - } else if (!strcmp(arg, "-nodragging")) { - show_dragging = 0; - } else if (!strcmp(arg, "-input_skip")) { - ui_skip = atoi(argv[++i]); - if (! ui_skip) ui_skip = 1; - } else if (!strcmp(arg, "-old_pointer")) { - old_pointer = 1; - } else if (!strcmp(arg, "-onetile") - || !strcmp(arg, "-old_copytile")) { - single_copytile = 1; - } else if (!strcmp(arg, "-debug_pointer")) { - debug_pointer++; - } else if (!strcmp(arg, "-debug_keyboard")) { - debug_keyboard++; - } else if (!strcmp(arg, "-defer")) { - defer_update = atoi(argv[++i]); - got_defer = 1; - } else if (!strcmp(arg, "-wait")) { - waitms = atoi(argv[++i]); - } else if (!strcmp(arg, "-nap")) { - take_naps = 1; -#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD - } else if (!strcmp(arg, "-threads")) { - use_threads = 1; - } else if (!strcmp(arg, "-nothreads")) { - use_threads = 0; -#endif - } else if (!strcmp(arg, "-sigpipe")) { - if (!strcmp(argv[++i], "ignore")) { - sigpipe = 1; - } else if (!strcmp(argv[i], "exit")) { - sigpipe = 2; - } else if (!strcmp(argv[i], "skip")) { - sigpipe = 0; - } else { - fprintf(stderr, "bad -sigpipe arg: %s, must " - "be \"ignore\" or \"exit\"\n", argv[i]); - exit(1); - } - } else if (!strcmp(arg, "-fs")) { - fs_frac = atof(argv[++i]); - } else if (!strcmp(arg, "-gaps")) { - gaps_fill = atoi(argv[++i]); - } else if (!strcmp(arg, "-grow")) { - grow_fill = atoi(argv[++i]); - } else if (!strcmp(arg, "-fuzz")) { - tile_fuzz = atoi(argv[++i]); - } else if (!strcmp(arg, "-hints")) { - use_hints = 1; - } else if (!strcmp(arg, "-nohints")) { - use_hints = 0; - } else if (!strcmp(arg, "-h") || !strcmp(arg, "-help") - || !strcmp(arg, "-?")) { - print_help(); - } else if (!strcmp(arg, "-o") || !strcmp(arg, "-logfile")) { - logfile = argv[++i]; - } else if (!strcmp(arg, "-q") || !strcmp(arg, "-quiet")) { - quiet = 1; -#ifdef LIBVNCSERVER_HAVE_SETSID - } else if (!strcmp(arg, "-bg") || !strcmp(arg, "-background")) { - bg = 1; -#endif - } else { - if (!strcmp(arg, "-desktop")) { - dt = 1; - } - if (!strcmp(arg, "-passwd")) { - pw_loc = i; - } - if (!strcmp(arg, "-rfbwait")) { - got_rfbwait = 1; - } - if (!strcmp(arg, "-deferupdate")) { - got_deferupdate = 1; - } - if (!strcmp(arg, "-rfbport")) { - got_rfbport = 1; - } - if (!strcmp(arg, "-alwaysshared ")) { - got_alwaysshared = 1; - } - if (!strcmp(arg, "-nevershared")) { - got_nevershared = 1; - } - /* otherwise copy it for libvncserver use below. */ - if (argc2 < 100) { - argv2[argc2++] = strdup(arg); - } - } - } - if (logfile) { - int n; - if ((n = open(logfile, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) { - fprintf(stderr, "error opening logfile: %s\n", logfile); - perror("open"); - exit(1); - } - if (dup2(n, 2) < 0) { - fprintf(stderr, "dup2 failed\n"); - perror("dup2"); - exit(1); - } - if (n > 2) { - close(n); - } - } - if (! quiet && ! inetd) { - int i; - for (i=1; i < argc2; i++) { - fprintf(stderr, "passing arg to libvncserver: %s\n", - argv2[i]); - if (!strcmp(argv2[i], "-passwd")) { - i++; - } - } - } - - - /* - * If -passwd was used, clear it out of argv. This does not - * work on all UNIX, have to use execvp() in general... - */ - if (pw_loc > 0) { - char *p = argv[pw_loc]; - while (*p != '\0') { - *p++ = '\0'; - } - if (pw_loc+1 < argc) { - p = argv[pw_loc+1]; - while (*p != '\0') { - *p++ = '\0'; - } - } - } else if (passwdfile) { - char line[512]; - FILE *in; - in = fopen(passwdfile, "r"); - if (in == NULL) { - fprintf(stderr, "cannot open passwdfile: %s\n", - passwdfile); - perror("fopen"); - exit(1); - } - if (fgets(line, 512, in) != NULL) { - line[strlen(line)-1] = '\0'; - argv2[argc2++] = "-passwd"; - argv2[argc2++] = strdup(line); - } else { - fprintf(stderr, "cannot read passwdfile: %s\n", - passwdfile); - perror("fgets"); - exit(1); - } - } - - /* fixup settings that do not make sense */ - - if (use_threads && nofb && cursor_pos) { - if (! quiet) { - fprintf(stderr, "disabling -threads under -nofb " - "-cursorpos\n"); - } - use_threads = 0; - } - if (tile_fuzz < 1) { - tile_fuzz = 1; - } - if (waitms < 0) { - waitms = 0; - } - if (inetd) { - shared = 0; - connect_once = 1; - bg = 0; - } - - /* increase rfbwait if threaded */ - if (use_threads && ! got_rfbwait) { - argv2[argc2++] = "-rfbwait"; - argv2[argc2++] = "604800000"; /* one week... */ - } - - /* check for OS with small shm limits */ - if (using_shm && ! single_copytile) { - if (limit_shm()) { - single_copytile = 1; - } - } - - if (nofb && ! got_deferupdate && ! got_defer) { - /* reduce defer time under -nofb */ - defer_update = defer_update_nofb; - } - if (! got_deferupdate) { - char tmp[40]; - /* XXX not working yet in libvncserver */ - sprintf(tmp, "%d", defer_update); - argv2[argc2++] = "-deferupdate"; - argv2[argc2++] = strdup(tmp); - } - if (debug_pointer || debug_keyboard) { - if (bg || quiet) { - fprintf(stderr, "disabling -bg/-q under -debug_pointer" - "/-debug_keyboard\n"); - bg = 0; - quiet = 0; - } - } - - if (! quiet) { - fprintf(stderr, "\n"); - fprintf(stderr, "display: %s\n", use_dpy ? use_dpy - : "null"); - fprintf(stderr, "subwin: 0x%x\n", subwin); - fprintf(stderr, "visual: %s\n", visual_str ? visual_str - : "null"); - fprintf(stderr, "flashcmap: %d\n", flash_cmap); - fprintf(stderr, "force_idx: %d\n", force_indexed_color); - fprintf(stderr, "viewonly: %d\n", view_only); - fprintf(stderr, "shared: %d\n", shared); - fprintf(stderr, "authfile: %s\n", auth_file ? auth_file - : "null"); - fprintf(stderr, "passfile: %s\n", passwdfile ? passwdfile - : "null"); - fprintf(stderr, "logfile: %s\n", logfile ? logfile - : "null"); - fprintf(stderr, "allow: %s\n", allow_list ? allow_list - : "null"); - fprintf(stderr, "accept: %s\n", accept_cmd ? accept_cmd - : "null"); - fprintf(stderr, "gone: %s\n", gone_cmd ? gone_cmd - : "null"); - fprintf(stderr, "conn_once: %d\n", connect_once); - fprintf(stderr, "connect: %s\n", client_connect - ? client_connect : "null"); - fprintf(stderr, "connectfile %s\n", client_connect_file - ? client_connect_file : "null"); - fprintf(stderr, "vnc_conn: %d\n", vnc_connect); - fprintf(stderr, "inetd: %d\n", inetd); - fprintf(stderr, "using_shm: %d\n", using_shm); - fprintf(stderr, "flipbytes: %d\n", flip_byte_order); - fprintf(stderr, "mod_tweak: %d\n", use_modifier_tweak); - fprintf(stderr, "remap: %s\n", remap_file ? remap_file - : "null"); - fprintf(stderr, "blackout: %s\n", blackout_string - ? blackout_string : "null"); - fprintf(stderr, "xinerama: %d\n", xinerama); - fprintf(stderr, "watchbell: %d\n", watch_bell); - fprintf(stderr, "nofb: %d\n", nofb); - fprintf(stderr, "watchsel: %d\n", watch_selection); - fprintf(stderr, "watchprim: %d\n", watch_primary); - fprintf(stderr, "loc_curs: %d\n", local_cursor); - fprintf(stderr, "mouse: %d\n", show_mouse); - fprintf(stderr, "root_curs: %d\n", show_root_cursor); - fprintf(stderr, "xwarpptr: %d\n", use_xwarppointer); - fprintf(stderr, "cursorpos: %d\n", cursor_pos); - fprintf(stderr, "buttonmap: %s\n", pointer_remap - ? pointer_remap : "null"); - fprintf(stderr, "dragging: %d\n", show_dragging); - fprintf(stderr, "inputskip: %d\n", ui_skip); - fprintf(stderr, "old_ptr: %d\n", old_pointer); - fprintf(stderr, "onetile: %d\n", single_copytile); - fprintf(stderr, "debug_ptr: %d\n", debug_pointer); - fprintf(stderr, "debug_key: %d\n", debug_keyboard); - fprintf(stderr, "defer: %d\n", defer_update); - fprintf(stderr, "waitms: %d\n", waitms); - fprintf(stderr, "take_naps: %d\n", take_naps); - fprintf(stderr, "sigpipe: %d\n", sigpipe); - fprintf(stderr, "threads: %d\n", use_threads); - fprintf(stderr, "fs_frac: %.2f\n", fs_frac); - fprintf(stderr, "gaps_fill: %d\n", gaps_fill); - fprintf(stderr, "grow_fill: %d\n", grow_fill); - fprintf(stderr, "tile_fuzz: %d\n", tile_fuzz); - fprintf(stderr, "use_hints: %d\n", use_hints); - fprintf(stderr, "bg: %d\n", bg); - fprintf(stderr, "%s\n", lastmod); - } else { - rfbLogEnable(0); - } - - /* open the X display: */ - X_INIT; - if (auth_file) { - char *tmp; - int len = strlen("XAUTHORITY=") + strlen(auth_file) + 1; - tmp = (char *) malloc((size_t) len); - sprintf(tmp, "XAUTHORITY=%s", auth_file); - putenv(tmp); - } - if (use_dpy) { - dpy = XOpenDisplay(use_dpy); - } else if ( (use_dpy = getenv("DISPLAY")) ) { - dpy = XOpenDisplay(use_dpy); - } else { - dpy = XOpenDisplay(""); - } - - if (! dpy) { - fprintf(stderr, "XOpenDisplay failed (%s)\n", - use_dpy ? use_dpy:"null"); - exit(1); - } else if (use_dpy) { - if (! quiet) fprintf(stderr, "Using X display %s\n", use_dpy); - } else { - if (! quiet) fprintf(stderr, "Using default X display.\n"); - } - - /* check for XTEST */ - if (! XTestQueryExtension(dpy, &ev, &er, &maj, &min)) { - fprintf(stderr, "Display does not support XTest extension.\n"); - exit(1); - } - - /* check for MIT-SHM */ - if (! nofb && ! XShmQueryExtension(dpy)) { - if (! using_shm) { - if (! quiet) { - fprintf(stderr, "warning: display does not " - "support XShm.\n"); - } - } else { - fprintf(stderr, "Display does not support XShm " - "extension (must be local).\n"); - exit(1); - } - } - - if (visual_str != NULL) { - set_visual(visual_str); - } -#ifdef LIBVNCSERVER_HAVE_XKEYBOARD - /* check for XKEYBOARD */ - if (watch_bell) { - if (! XkbQueryExtension(dpy, &op, &ev, &er, &maj, &min)) { - if (! quiet) { - fprintf(stderr, "warning: disabling bell.\n"); - } - watch_bell = 0; - } else { - initialize_watch_bell(); - } - } -#endif - - /* - * Window managers will often grab the display during resize, etc. - * To avoid deadlock (our user resize input is not processed) - * we tell the server to process our requests during all grabs: - */ - XTestGrabControl(dpy, True); - - scr = DefaultScreen(dpy); - rootwin = RootWindow(dpy, scr); - - /* set up parameters for subwin or non-subwin cases: */ - if (! subwin) { - window = rootwin; - dpy_x = DisplayWidth(dpy, scr); - dpy_y = DisplayHeight(dpy, scr); - off_x = 0; - off_y = 0; - visual = DefaultVisual(dpy, scr); - } else { - /* experiment to share just one window */ - XWindowAttributes attr; - - window = (Window) subwin; - if ( ! XGetWindowAttributes(dpy, window, &attr) ) { - fprintf(stderr, "bad window: 0x%lx\n", window); - exit(1); - } - dpy_x = attr.width; - dpy_y = attr.height; - visual = attr.visual; - - /* show_mouse has some segv crashes as well */ - if (show_root_cursor) { - show_root_cursor = 0; - if (! quiet) { - fprintf(stderr, "disabling root cursor drawing" - " for subwindow\n"); - } - } - - set_offset(); - } - - /* initialize depth to reasonable value */ - depth = DefaultDepth(dpy, scr); - - /* - * User asked for non-default visual, this is not working well but it - * does some useful things... What should it do in general? - */ - if (visual_id) { - XVisualInfo vinfo_tmpl, *vinfo; - int n; - - vinfo_tmpl.visualid = visual_id; - vinfo = XGetVisualInfo(dpy, VisualIDMask, &vinfo_tmpl, &n); - if (vinfo == NULL || n == 0) { - fprintf(stderr, "could not match visual_id: 0x%x\n", - (int) visual_id); - exit(1); - } - visual = vinfo->visual; - depth = vinfo->depth; - if (visual_depth) { - depth = visual_depth; /* force it */ - } - if (! quiet) { - fprintf(stderr, "vis id: 0x%x\n", - (int) vinfo->visualid); - fprintf(stderr, "vis scr: %d\n", vinfo->screen); - fprintf(stderr, "vis depth %d\n", vinfo->depth); - fprintf(stderr, "vis class %d\n", vinfo->class); - fprintf(stderr, "vis rmask 0x%lx\n", vinfo->red_mask); - fprintf(stderr, "vis gmask 0x%lx\n", vinfo->green_mask); - fprintf(stderr, "vis bmask 0x%lx\n", vinfo->blue_mask); - fprintf(stderr, "vis cmap_sz %d\n", vinfo->colormap_size); - fprintf(stderr, "vis b/rgb %d\n", vinfo->bits_per_rgb); - } - - XFree(vinfo); - } - - - if (nofb || visual_id) { - fb = XCreateImage(dpy, visual, depth, ZPixmap, 0, NULL, - dpy_x, dpy_y, BitmapPad(dpy), 0); - /* - * For -nofb we do not allocate the framebuffer, so we - * can save a few MB of memory. - */ - if (! nofb) { - fb->data = (char *) malloc(fb->bytes_per_line * - fb->height); - } - } else { - fb = XGetImage(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, - ZPixmap); - if (! quiet) { - fprintf(stderr, "Read initial data from X display into" - " framebuffer.\n"); - } - } - if (fb->bits_per_pixel == 24 && ! quiet) { - fprintf(stderr, "warning: 24 bpp may have poor" - " performance.\n"); - } - - if (! dt) { - static char str[] = "-desktop"; - argv2[argc2++] = str; - argv2[argc2++] = choose_title(use_dpy); - } - - /* - * n.b. we do not have to X_LOCK any X11 calls until watch_loop() - * is called since we are single-threaded until then. - */ - - initialize_screen(&argc2, argv2, fb); - - initialize_tiles(); - - /* rectangular blackout regions */ - if (blackout_string != NULL) { - initialize_blackout(blackout_string); - } - if (xinerama) { - initialize_xinerama(); - } - if (blackouts) { - blackout_tiles(); - } - - initialize_shm(); /* also creates XImages when using_shm = 0 */ - - set_signals(); - - if (blackouts) { /* blackout fb as needed. */ - copy_screen(); - } - - if (use_modifier_tweak) { - initialize_modtweak(); - } - if (remap_file != NULL) { - initialize_remap(remap_file); - } - initialize_pointer_map(); - - if (! inetd) { - if (! screen->rfbPort || screen->rfbListenSock < 0) { - rfbLog("Error: could not obtain listening port.\n"); - clean_up_exit(1); - } - } - if (! quiet) { - rfbLog("screen setup finished.\n"); - } - if (screen->rfbPort) { - char *host = this_host(); - int port = screen->rfbPort; - if (host != NULL) { - /* note that vncviewer special cases 5900-5999 */ - if (inetd) { - ; /* should not occur */ - } else if (quiet) { - if (port >= 5900) { - fprintf(stderr, "The VNC desktop is " - "%s:%d\n", host, port - 5900); - } else { - fprintf(stderr, "The VNC desktop is " - "%s:%d\n", host, port); - } - } else if (port >= 5900) { - rfbLog("The VNC desktop is %s:%d\n", host, - port - 5900); - if (port >= 6000) { - rfbLog("possible aliases: %s:%d, " - "%s::%d\n", host, port, host, port); - } - } else { - rfbLog("The VNC desktop is %s:%d\n", host, - port); - rfbLog("possible alias: %s::%d\n", - host, port); - } - } - fflush(stderr); - fprintf(stdout, "PORT=%d\n", screen->rfbPort); - fflush(stdout); - } - -#if defined(LIBVNCSERVER_HAVE_FORK) && defined(LIBVNCSERVER_HAVE_SETSID) - if (bg) { - /* fork into the background now */ - int p, n; - if ((p = fork()) > 0) { - exit(0); - } else if (p == -1) { - fprintf(stderr, "could not fork\n"); - perror("fork"); - clean_up_exit(1); - } - if (setsid() == -1) { - fprintf(stderr, "setsid failed\n"); - perror("setsid"); - clean_up_exit(1); - } - /* adjust our stdio */ - n = open("/dev/null", O_RDONLY); - dup2(n, 0); - dup2(n, 1); - if (! logfile) { - dup2(n, 2); - } - if (n > 2) { - close(n); - } - } -#endif - - watch_loop(); - - return(0); -} diff --git a/corre.c b/corre.c deleted file mode 100644 index 3f123b0..0000000 --- a/corre.c +++ /dev/null @@ -1,352 +0,0 @@ -/* - * corre.c - * - * Routines to implement Compact Rise-and-Run-length Encoding (CoRRE). This - * code is based on krw's original javatel rfbserver. - */ - -/* - * Copyright (C) 2002 RealVNC Ltd. - * OSXvnc Copyright (C) 2001 Dan McGuirk . - * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. - * 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. - */ - -#include - -/* - * rreBeforeBuf contains pixel data in the client's format. - * rreAfterBuf contains the RRE encoded version. If the RRE encoded version is - * larger than the raw data or if it exceeds rreAfterBufSize then - * raw encoding is used instead. - */ - -static int rreBeforeBufSize = 0; -static char *rreBeforeBuf = NULL; - -static int rreAfterBufSize = 0; -static char *rreAfterBuf = NULL; -static int rreAfterBufLen; - -static int subrectEncode8(uint8_t *data, int w, int h); -static int subrectEncode16(uint16_t *data, int w, int h); -static int subrectEncode32(uint32_t *data, int w, int h); -static uint32_t getBgColour(char *data, int size, int bpp); -static rfbBool rfbSendSmallRectEncodingCoRRE(rfbClientPtr cl, int x, int y, - int w, int h); - - -/* - * rfbSendRectEncodingCoRRE - send an arbitrary size rectangle using CoRRE - * encoding. - */ - -rfbBool -rfbSendRectEncodingCoRRE(cl, x, y, w, h) - rfbClientPtr cl; - int x, y, w, h; -{ - if (h > cl->correMaxHeight) { - return (rfbSendRectEncodingCoRRE(cl, x, y, w, cl->correMaxHeight) && - rfbSendRectEncodingCoRRE(cl, x, y + cl->correMaxHeight, w, - h - cl->correMaxHeight)); - } - - if (w > cl->correMaxWidth) { - return (rfbSendRectEncodingCoRRE(cl, x, y, cl->correMaxWidth, h) && - rfbSendRectEncodingCoRRE(cl, x + cl->correMaxWidth, y, - w - cl->correMaxWidth, h)); - } - - rfbSendSmallRectEncodingCoRRE(cl, x, y, w, h); - return TRUE; -} - - - -/* - * rfbSendSmallRectEncodingCoRRE - send a small (guaranteed < 256x256) - * rectangle using CoRRE encoding. - */ - -static rfbBool -rfbSendSmallRectEncodingCoRRE(cl, x, y, w, h) - rfbClientPtr cl; - int x, y, w, h; -{ - rfbFramebufferUpdateRectHeader rect; - rfbRREHeader hdr; - int nSubrects; - int i; - char *fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y) - + (x * (cl->screen->bitsPerPixel / 8))); - - int maxRawSize = (cl->screen->width * cl->screen->height - * (cl->format.bitsPerPixel / 8)); - - if (rreBeforeBufSize < maxRawSize) { - rreBeforeBufSize = maxRawSize; - if (rreBeforeBuf == NULL) - rreBeforeBuf = (char *)malloc(rreBeforeBufSize); - else - rreBeforeBuf = (char *)realloc(rreBeforeBuf, rreBeforeBufSize); - } - - if (rreAfterBufSize < maxRawSize) { - rreAfterBufSize = maxRawSize; - if (rreAfterBuf == NULL) - rreAfterBuf = (char *)malloc(rreAfterBufSize); - else - rreAfterBuf = (char *)realloc(rreAfterBuf, rreAfterBufSize); - } - - (*cl->translateFn)(cl->translateLookupTable,&(cl->screen->rfbServerFormat), - &cl->format, fbptr, rreBeforeBuf, - cl->screen->paddedWidthInBytes, w, h); - - switch (cl->format.bitsPerPixel) { - case 8: - nSubrects = subrectEncode8((uint8_t *)rreBeforeBuf, w, h); - break; - case 16: - nSubrects = subrectEncode16((uint16_t *)rreBeforeBuf, w, h); - break; - case 32: - nSubrects = subrectEncode32((uint32_t *)rreBeforeBuf, w, h); - break; - default: - rfbLog("getBgColour: bpp %d?\n",cl->format.bitsPerPixel); - return FALSE; - } - - if (nSubrects < 0) { - - /* RRE encoding was too large, use raw */ - - return rfbSendRectEncodingRaw(cl, x, y, w, h); - } - - cl->rfbRectanglesSent[rfbEncodingCoRRE]++; - cl->rfbBytesSent[rfbEncodingCoRRE] += (sz_rfbFramebufferUpdateRectHeader - + sz_rfbRREHeader + rreAfterBufLen); - - if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader - > UPDATE_BUF_SIZE) - { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - rect.r.x = Swap16IfLE(x); - rect.r.y = Swap16IfLE(y); - rect.r.w = Swap16IfLE(w); - rect.r.h = Swap16IfLE(h); - rect.encoding = Swap32IfLE(rfbEncodingCoRRE); - - memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, - sz_rfbFramebufferUpdateRectHeader); - cl->ublen += sz_rfbFramebufferUpdateRectHeader; - - hdr.nSubrects = Swap32IfLE(nSubrects); - - memcpy(&cl->updateBuf[cl->ublen], (char *)&hdr, sz_rfbRREHeader); - cl->ublen += sz_rfbRREHeader; - - for (i = 0; i < rreAfterBufLen;) { - - int bytesToCopy = UPDATE_BUF_SIZE - cl->ublen; - - if (i + bytesToCopy > rreAfterBufLen) { - bytesToCopy = rreAfterBufLen - i; - } - - memcpy(&cl->updateBuf[cl->ublen], &rreAfterBuf[i], bytesToCopy); - - cl->ublen += bytesToCopy; - i += bytesToCopy; - - if (cl->ublen == UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - } - - return TRUE; -} - - - -/* - * subrectEncode() encodes the given multicoloured rectangle as a background - * colour overwritten by single-coloured rectangles. It returns the number - * of subrectangles in the encoded buffer, or -1 if subrect encoding won't - * fit in the buffer. It puts the encoded rectangles in rreAfterBuf. The - * single-colour rectangle partition is not optimal, but does find the biggest - * horizontal or vertical rectangle top-left anchored to each consecutive - * coordinate position. - * - * The coding scheme is simply [...] where each - * is []. - */ - -#define DEFINE_SUBRECT_ENCODE(bpp) \ -static int \ -subrectEncode##bpp(data,w,h) \ - uint##bpp##_t *data; \ - int w; \ - int h; \ -{ \ - uint##bpp##_t cl; \ - rfbCoRRERectangle subrect; \ - int x,y; \ - int i,j; \ - int hx=0,hy,vx=0,vy; \ - int hyflag; \ - uint##bpp##_t *seg; \ - uint##bpp##_t *line; \ - int hw,hh,vw,vh; \ - int thex,they,thew,theh; \ - int numsubs = 0; \ - int newLen; \ - uint##bpp##_t bg = (uint##bpp##_t)getBgColour((char*)data,w*h,bpp); \ - \ - *((uint##bpp##_t*)rreAfterBuf) = bg; \ - \ - rreAfterBufLen = (bpp/8); \ - \ - for (y=0; y 0) && (i >= hx)) {hy += 1;} else {hyflag = 0;} \ - } \ - vy = j-1; \ - \ - /* We now have two possible subrects: (x,y,hx,hy) and (x,y,vx,vy) \ - * We'll choose the bigger of the two. \ - */ \ - hw = hx-x+1; \ - hh = hy-y+1; \ - vw = vx-x+1; \ - vh = vy-y+1; \ - \ - thex = x; \ - they = y; \ - \ - if ((hw*hh) > (vw*vh)) { \ - thew = hw; \ - theh = hh; \ - } else { \ - thew = vw; \ - theh = vh; \ - } \ - \ - subrect.x = thex; \ - subrect.y = they; \ - subrect.w = thew; \ - subrect.h = theh; \ - \ - newLen = rreAfterBufLen + (bpp/8) + sz_rfbCoRRERectangle; \ - if ((newLen > (w * h * (bpp/8))) || (newLen > rreAfterBufSize)) \ - return -1; \ - \ - numsubs += 1; \ - *((uint##bpp##_t*)(rreAfterBuf + rreAfterBufLen)) = cl; \ - rreAfterBufLen += (bpp/8); \ - memcpy(&rreAfterBuf[rreAfterBufLen],&subrect,sz_rfbCoRRERectangle); \ - rreAfterBufLen += sz_rfbCoRRERectangle; \ - \ - /* \ - * Now mark the subrect as done. \ - */ \ - for (j=they; j < (they+theh); j++) { \ - for (i=thex; i < (thex+thew); i++) { \ - data[j*w+i] = bg; \ - } \ - } \ - } \ - } \ - } \ - \ - return numsubs; \ -} - -DEFINE_SUBRECT_ENCODE(8) -DEFINE_SUBRECT_ENCODE(16) -DEFINE_SUBRECT_ENCODE(32) - - -/* - * getBgColour() gets the most prevalent colour in a byte array. - */ -static uint32_t -getBgColour(data,size,bpp) - char *data; - int size; - int bpp; -{ - -#define NUMCLRS 256 - - static int counts[NUMCLRS]; - int i,j,k; - - int maxcount = 0; - uint8_t maxclr = 0; - - if (bpp != 8) { - if (bpp == 16) { - return ((uint16_t *)data)[0]; - } else if (bpp == 32) { - return ((uint32_t *)data)[0]; - } else { - rfbLog("getBgColour: bpp %d?\n",bpp); - return 0; - } - } - - for (i=0; i= NUMCLRS) { - rfbLog("getBgColour: unusual colour = %d\n", k); - return 0; - } - counts[k] += 1; - if (counts[k] > maxcount) { - maxcount = counts[k]; - maxclr = ((uint8_t *)data)[j]; - } - } - - return maxclr; -} diff --git a/cursor.c b/cursor.c deleted file mode 100644 index 4f290da..0000000 --- a/cursor.c +++ /dev/null @@ -1,527 +0,0 @@ -/* - * cursor.c - support for cursor shape updates. - */ - -/* - * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. - * Copyright (C) 1999 AT&T Laboratories Cambridge. 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. - */ - -#include - -/* - * Send cursor shape either in X-style format or in client pixel format. - */ - -rfbBool -rfbSendCursorShape(cl) - rfbClientPtr cl; -{ - rfbCursorPtr pCursor; - rfbFramebufferUpdateRectHeader rect; - rfbXCursorColors colors; - int saved_ublen; - int bitmapRowBytes, maskBytes, dataBytes; - int i, j; - uint8_t *bitmapData; - uint8_t bitmapByte; - - pCursor = cl->screen->getCursorPtr(cl); - /*if(!pCursor) return TRUE;*/ - - if (cl->useRichCursorEncoding) { - if(pCursor && !pCursor->richSource) - MakeRichCursorFromXCursor(cl->screen,pCursor); - rect.encoding = Swap32IfLE(rfbEncodingRichCursor); - } else { - if(pCursor && !pCursor->source) - MakeXCursorFromRichCursor(cl->screen,pCursor); - rect.encoding = Swap32IfLE(rfbEncodingXCursor); - } - - /* If there is no cursor, send update with empty cursor data. */ - - if ( pCursor && pCursor->width == 1 && - pCursor->height == 1 && - pCursor->mask[0] == 0 ) { - pCursor = NULL; - } - - if (pCursor == NULL) { - if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE ) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - rect.r.x = rect.r.y = 0; - rect.r.w = rect.r.h = 0; - memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, - sz_rfbFramebufferUpdateRectHeader); - cl->ublen += sz_rfbFramebufferUpdateRectHeader; - - cl->rfbCursorShapeBytesSent += sz_rfbFramebufferUpdateRectHeader; - cl->rfbCursorShapeUpdatesSent++; - - if (!rfbSendUpdateBuf(cl)) - return FALSE; - - return TRUE; - } - - /* Calculate data sizes. */ - - bitmapRowBytes = (pCursor->width + 7) / 8; - maskBytes = bitmapRowBytes * pCursor->height; - dataBytes = (cl->useRichCursorEncoding) ? - (pCursor->width * pCursor->height * - (cl->format.bitsPerPixel / 8)) : maskBytes; - - /* Send buffer contents if needed. */ - - if ( cl->ublen + sz_rfbFramebufferUpdateRectHeader + - sz_rfbXCursorColors + maskBytes + dataBytes > UPDATE_BUF_SIZE ) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - if ( cl->ublen + sz_rfbFramebufferUpdateRectHeader + - sz_rfbXCursorColors + maskBytes + dataBytes > UPDATE_BUF_SIZE ) { - return FALSE; /* FIXME. */ - } - - saved_ublen = cl->ublen; - - /* Prepare rectangle header. */ - - rect.r.x = Swap16IfLE(pCursor->xhot); - rect.r.y = Swap16IfLE(pCursor->yhot); - rect.r.w = Swap16IfLE(pCursor->width); - rect.r.h = Swap16IfLE(pCursor->height); - - memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader); - cl->ublen += sz_rfbFramebufferUpdateRectHeader; - - /* Prepare actual cursor data (depends on encoding used). */ - - if (!cl->useRichCursorEncoding) { - /* XCursor encoding. */ - colors.foreRed = (char)(pCursor->foreRed >> 8); - colors.foreGreen = (char)(pCursor->foreGreen >> 8); - colors.foreBlue = (char)(pCursor->foreBlue >> 8); - colors.backRed = (char)(pCursor->backRed >> 8); - colors.backGreen = (char)(pCursor->backGreen >> 8); - colors.backBlue = (char)(pCursor->backBlue >> 8); - - memcpy(&cl->updateBuf[cl->ublen], (char *)&colors, sz_rfbXCursorColors); - cl->ublen += sz_rfbXCursorColors; - - bitmapData = (uint8_t *)pCursor->source; - - for (i = 0; i < pCursor->height; i++) { - for (j = 0; j < bitmapRowBytes; j++) { - bitmapByte = bitmapData[i * bitmapRowBytes + j]; - cl->updateBuf[cl->ublen++] = (char)bitmapByte; - } - } - } else { - /* RichCursor encoding. */ - int bpp1=cl->screen->rfbServerFormat.bitsPerPixel/8, - bpp2=cl->format.bitsPerPixel/8; - (*cl->translateFn)(cl->translateLookupTable, - &(cl->screen->rfbServerFormat), - &cl->format, (char*)pCursor->richSource, - &cl->updateBuf[cl->ublen], - pCursor->width*bpp1, pCursor->width, pCursor->height); - - cl->ublen += pCursor->width*bpp2*pCursor->height; - } - - /* Prepare transparency mask. */ - - bitmapData = (uint8_t *)pCursor->mask; - - for (i = 0; i < pCursor->height; i++) { - for (j = 0; j < bitmapRowBytes; j++) { - bitmapByte = bitmapData[i * bitmapRowBytes + j]; - cl->updateBuf[cl->ublen++] = (char)bitmapByte; - } - } - - /* Send everything we have prepared in the cl->updateBuf[]. */ - - cl->rfbCursorShapeBytesSent += (cl->ublen - saved_ublen); - cl->rfbCursorShapeUpdatesSent++; - - if (!rfbSendUpdateBuf(cl)) - return FALSE; - - return TRUE; -} - -/* - * Send cursor position (PointerPos pseudo-encoding). - */ - -rfbBool -rfbSendCursorPos(rfbClientPtr cl) -{ - rfbFramebufferUpdateRectHeader rect; - - if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - rect.encoding = Swap32IfLE(rfbEncodingPointerPos); - rect.r.x = Swap16IfLE(cl->screen->cursorX); - rect.r.y = Swap16IfLE(cl->screen->cursorY); - rect.r.w = 0; - rect.r.h = 0; - - memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, - sz_rfbFramebufferUpdateRectHeader); - cl->ublen += sz_rfbFramebufferUpdateRectHeader; - - cl->rfbCursorPosBytesSent += sz_rfbFramebufferUpdateRectHeader; - cl->rfbCursorPosUpdatesSent++; - - if (!rfbSendUpdateBuf(cl)) - return FALSE; - - return TRUE; -} - -/* conversion routine for predefined cursors in LSB order */ -unsigned char rfbReverseByte[0x100] = { - /* copied from Xvnc/lib/font/util/utilbitmap.c */ - 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, - 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, - 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, - 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, - 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, - 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, - 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, - 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, - 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, - 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, - 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, - 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, - 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, - 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, - 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, - 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, - 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, - 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, - 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, - 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, - 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, - 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, - 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, - 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, - 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, - 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, - 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, - 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, - 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, - 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, - 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, - 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff -}; - -void rfbConvertLSBCursorBitmapOrMask(int width,int height,unsigned char* bitmap) -{ - int i,t=(width+7)/8*height; - for(i=0;icleanup=TRUE; - cursor->width=width; - cursor->height=height; - /*cursor->backRed=cursor->backGreen=cursor->backBlue=0xffff;*/ - cursor->foreRed=cursor->foreGreen=cursor->foreBlue=0xffff; - - cursor->source = (unsigned char*)calloc(w,height); - cursor->cleanupSource = TRUE; - for(j=0,cp=cursorString;j>1,cp++) - if(*cp!=' ') cursor->source[j*w+i/8]|=bit; - - if(maskString) { - cursor->mask = (unsigned char*)calloc(w,height); - for(j=0,cp=maskString;j>1,cp++) - if(*cp!=' ') cursor->mask[j*w+i/8]|=bit; - } else - cursor->mask = (unsigned char*)rfbMakeMaskForXCursor(width,height,(char*)cursor->source); - cursor->cleanupMask = TRUE; - - return(cursor); -} - -char* rfbMakeMaskForXCursor(int width,int height,char* source) -{ - int i,j,w=(width+7)/8; - char* mask=(char*)calloc(w,height); - unsigned char c; - - for(j=0;j=0;i--) { - c=source[j*w+i]; - if(j>0) c|=source[(j-1)*w+i]; - if(j0 && (c&0x80)) - mask[j*w+i-1]|=0x01; - if(i>1); - } - - return(mask); -} - -void rfbFreeCursor(rfbCursorPtr cursor) -{ - if(cursor) { - if(cursor->cleanupRichSource && cursor->richSource) - free(cursor->richSource); - if(cursor->cleanupSource && cursor->source) - free(cursor->source); - if(cursor->cleanupMask && cursor->mask) - free(cursor->mask); - if(cursor->cleanup) - free(cursor); - else { - cursor->cleanup=cursor->cleanupSource=cursor->cleanupMask - =cursor->cleanupRichSource=FALSE; - cursor->source=cursor->mask=cursor->richSource=0; - } - } - -} - -/* background and foregroud colour have to be set beforehand */ -void MakeXCursorFromRichCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr cursor) -{ - rfbPixelFormat* format=&rfbScreen->rfbServerFormat; - int i,j,w=(cursor->width+7)/8,bpp=format->bitsPerPixel/8, - width=cursor->width*bpp; - uint32_t background; - char *back=(char*)&background; - unsigned char bit; - - if(cursor->source && cursor->cleanupSource) - free(cursor->source); - cursor->source=(unsigned char*)calloc(w,cursor->height); - cursor->cleanupSource=TRUE; - - if(format->bigEndian) - back+=4-bpp; - - background=cursor->backRed<redShift| - cursor->backGreen<greenShift|cursor->backBlue<blueShift; - - for(j=0;jheight;j++) - for(i=0,bit=0x80;iwidth;i++,bit=(bit&1)?0x80:bit>>1) - if(memcmp(cursor->richSource+j*width+i*bpp,back,bpp)) - cursor->source[j*w+i/8]|=bit; -} - -void MakeRichCursorFromXCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr cursor) -{ - rfbPixelFormat* format=&rfbScreen->rfbServerFormat; - int i,j,w=(cursor->width+7)/8,bpp=format->bitsPerPixel/8; - uint32_t background,foreground; - char *back=(char*)&background,*fore=(char*)&foreground; - unsigned char *cp; - unsigned char bit; - - if(cursor->richSource && cursor->cleanupRichSource) - free(cursor->richSource); - cp=cursor->richSource=(unsigned char*)calloc(cursor->width*bpp,cursor->height); - cursor->cleanupRichSource=TRUE; - - if(format->bigEndian) { - back+=4-bpp; - fore+=4-bpp; - } - - background=cursor->backRed<redShift| - cursor->backGreen<greenShift|cursor->backBlue<blueShift; - foreground=cursor->foreRed<redShift| - cursor->foreGreen<greenShift|cursor->foreBlue<blueShift; - - for(j=0;jheight;j++) - for(i=0,bit=0x80;iheight;i++,bit=(bit&1)?0x80:bit>>1,cp+=bpp) - if(cursor->source[j*w+i/8]&bit) memcpy(cp,fore,bpp); - else memcpy(cp,back,bpp); -} - -/* functions to draw/hide cursor directly in the frame buffer */ - -void rfbUndrawCursor(rfbScreenInfoPtr s) -{ - rfbCursorPtr c=s->cursor; - int j,x1,x2,y1,y2,bpp=s->rfbServerFormat.bitsPerPixel/8, - rowstride=s->paddedWidthInBytes; - LOCK(s->cursorMutex); - if(!s->cursorIsDrawn) { - UNLOCK(s->cursorMutex); - return; - } - - /* restore what is under the cursor */ - x1=s->cursorX-c->xhot; - x2=x1+c->width; - if(x1<0) x1=0; - if(x2>=s->width) x2=s->width-1; - x2-=x1; if(x2<=0) { - UNLOCK(s->cursorMutex); - return; - } - y1=s->cursorY-c->yhot; - y2=y1+c->height; - if(y1<0) y1=0; - if(y2>=s->height) y2=s->height-1; - y2-=y1; if(y2<=0) { - UNLOCK(s->cursorMutex); - return; - } - - /* get saved data */ - for(j=0;jframeBuffer+(y1+j)*rowstride+x1*bpp, - s->underCursorBuffer+j*x2*bpp, - x2*bpp); - - /* rfbMarkRectAsModified(s,x1,y1,x1+x2,y1+y2); */ - s->cursorIsDrawn = FALSE; - UNLOCK(s->cursorMutex); -} - -void rfbDrawCursor(rfbScreenInfoPtr s) -{ - rfbCursorPtr c=s->cursor; - int i,j,x1,x2,y1,y2,i1,j1,bpp=s->rfbServerFormat.bitsPerPixel/8, - rowstride=s->paddedWidthInBytes, - bufSize,w; - rfbBool wasChanged=FALSE; - - if(!c) return; - LOCK(s->cursorMutex); - if(s->cursorIsDrawn) { - /* is already drawn */ - UNLOCK(s->cursorMutex); - return; - } - bufSize=c->width*c->height*bpp; - w=(c->width+7)/8; - if(s->underCursorBufferLenunderCursorBuffer!=NULL) - free(s->underCursorBuffer); - s->underCursorBuffer=malloc(bufSize); - s->underCursorBufferLen=bufSize; - } - /* save what is under the cursor */ - i1=j1=0; /* offset in cursor */ - x1=s->cursorX-c->xhot; - x2=x1+c->width; - if(x1<0) { i1=-x1; x1=0; } - if(x2>=s->width) x2=s->width-1; - x2-=x1; if(x2<=0) { - UNLOCK(s->cursorMutex); - return; /* nothing to do */ - } - y1=s->cursorY-c->yhot; - y2=y1+c->height; - if(y1<0) { j1=-y1; y1=0; } - if(y2>=s->height) y2=s->height-1; - y2-=y1; if(y2<=0) { - UNLOCK(s->cursorMutex); - return; /* nothing to do */ - } - - /* save data */ - for(j=0;junderCursorBuffer+j*x2*bpp; - const char* src=s->frameBuffer+(y1+j)*rowstride+x1*bpp; - unsigned int count=x2*bpp; - if(wasChanged || memcmp(dest,src,count)) { - wasChanged=TRUE; - memcpy(dest,src,count); - } - } - - if(!c->richSource) - MakeRichCursorFromXCursor(s,c); - - /* now the cursor has to be drawn */ - for(j=0;jmask[(j+j1)*w+(i+i1)/8]<<((i+i1)&7))&0x80) - memcpy(s->frameBuffer+(j+y1)*rowstride+(i+x1)*bpp, - c->richSource+(j+j1)*c->width*bpp+(i+i1)*bpp,bpp); - - if(wasChanged) - rfbMarkRectAsModified(s,x1,y1,x1+x2,y1+y2); - s->cursorIsDrawn = TRUE; - UNLOCK(s->cursorMutex); -} - -/* for debugging */ - -void rfbPrintXCursor(rfbCursorPtr cursor) -{ - int i,i1,j,w=(cursor->width+7)/8; - unsigned char bit; - for(j=0;jheight;j++) { - for(i=0,i1=0,bit=0x80;i1width;i1++,i+=(bit&1)?1:0,bit=(bit&1)?0x80:bit>>1) - if(cursor->source[j*w+i]&bit) putchar('#'); else putchar(' '); - putchar(':'); - for(i=0,i1=0,bit=0x80;i1width;i1++,i+=(bit&1)?1:0,bit=(bit&1)?0x80:bit>>1) - if(cursor->mask[j*w+i]&bit) putchar('#'); else putchar(' '); - putchar('\n'); - } -} - -void rfbSetCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr c,rfbBool freeOld) -{ - LOCK(rfbScreen->cursorMutex); - while(rfbScreen->cursorIsDrawn) { - UNLOCK(rfbScreen->cursorMutex); - rfbUndrawCursor(rfbScreen); - LOCK(rfbScreen->cursorMutex); - } - - if(rfbScreen->cursor && (freeOld || rfbScreen->cursor->cleanup)) - rfbFreeCursor(rfbScreen->cursor); - - rfbScreen->cursor = c; - - UNLOCK(rfbScreen->cursorMutex); -} diff --git a/cutpaste.c b/cutpaste.c deleted file mode 100644 index 6a9dcb9..0000000 --- a/cutpaste.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * cutpaste.c - routines to deal with cut & paste buffers / selection. - */ - -/* - * OSXvnc Copyright (C) 2001 Dan McGuirk . - * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. - * 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. - */ - -#include - - -/* - * rfbSetXCutText sets the cut buffer to be the given string. We also clear - * the primary selection. Ideally we'd like to set it to the same thing, but I - * can't work out how to do that without some kind of helper X client. - */ - -void rfbGotXCutText(rfbScreenInfoPtr rfbScreen, char *str, int len) -{ - rfbSendServerCutText(rfbScreen, str, len); -} diff --git a/d3des.c b/d3des.c deleted file mode 100644 index 4994afb..0000000 --- a/d3des.c +++ /dev/null @@ -1,442 +0,0 @@ -/* - * This is D3DES (V5.09) by Richard Outerbridge with the double and - * triple-length support removed for use in VNC. Also the bytebit[] array - * has been reversed so that the most significant bit in each byte of the - * key is ignored, not the least significant. - * - * These changes are: - * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. - * - * 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. - */ - -/* D3DES (V5.09) - - * - * A portable, public domain, version of the Data Encryption Standard. - * - * Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge. - * Thanks to: Dan Hoey for his excellent Initial and Inverse permutation - * code; Jim Gillogly & Phil Karn for the DES key schedule code; Dennis - * Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau, - * for humouring me on. - * - * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge. - * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992. - */ - -#include "d3des.h" - -static void scrunch(unsigned char *, unsigned long *); -static void unscrun(unsigned long *, unsigned char *); -static void desfunc(unsigned long *, unsigned long *); -static void cookey(unsigned long *); - -static unsigned long KnL[32] = { 0L }; -/* -static unsigned long KnR[32] = { 0L }; -static unsigned long Kn3[32] = { 0L }; -static unsigned char Df_Key[24] = { - 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, - 0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10, - 0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67 }; -*/ - -static unsigned short bytebit[8] = { - 01, 02, 04, 010, 020, 040, 0100, 0200 }; - -static unsigned long bigbyte[24] = { - 0x800000L, 0x400000L, 0x200000L, 0x100000L, - 0x80000L, 0x40000L, 0x20000L, 0x10000L, - 0x8000L, 0x4000L, 0x2000L, 0x1000L, - 0x800L, 0x400L, 0x200L, 0x100L, - 0x80L, 0x40L, 0x20L, 0x10L, - 0x8L, 0x4L, 0x2L, 0x1L }; - -/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */ - -static unsigned char pc1[56] = { - 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, - 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, - 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, - 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 }; - -static unsigned char totrot[16] = { - 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 }; - -static unsigned char pc2[48] = { - 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, - 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, - 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, - 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 }; - -void deskey(key, edf) /* Thanks to James Gillogly & Phil Karn! */ -unsigned char *key; -int edf; -{ - register int i, j, l, m, n; - unsigned char pc1m[56], pcr[56]; - unsigned long kn[32]; - - for ( j = 0; j < 56; j++ ) { - l = pc1[j]; - m = l & 07; - pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0; - } - for( i = 0; i < 16; i++ ) { - if( edf == DE1 ) m = (15 - i) << 1; - else m = i << 1; - n = m + 1; - kn[m] = kn[n] = 0L; - for( j = 0; j < 28; j++ ) { - l = j + totrot[i]; - if( l < 28 ) pcr[j] = pc1m[l]; - else pcr[j] = pc1m[l - 28]; - } - for( j = 28; j < 56; j++ ) { - l = j + totrot[i]; - if( l < 56 ) pcr[j] = pc1m[l]; - else pcr[j] = pc1m[l - 28]; - } - for( j = 0; j < 24; j++ ) { - if( pcr[pc2[j]] ) kn[m] |= bigbyte[j]; - if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j]; - } - } - cookey(kn); - return; - } - -static void cookey(raw1) -register unsigned long *raw1; -{ - register unsigned long *cook, *raw0; - unsigned long dough[32]; - register int i; - - cook = dough; - for( i = 0; i < 16; i++, raw1++ ) { - raw0 = raw1++; - *cook = (*raw0 & 0x00fc0000L) << 6; - *cook |= (*raw0 & 0x00000fc0L) << 10; - *cook |= (*raw1 & 0x00fc0000L) >> 10; - *cook++ |= (*raw1 & 0x00000fc0L) >> 6; - *cook = (*raw0 & 0x0003f000L) << 12; - *cook |= (*raw0 & 0x0000003fL) << 16; - *cook |= (*raw1 & 0x0003f000L) >> 4; - *cook++ |= (*raw1 & 0x0000003fL); - } - usekey(dough); - return; - } - -void cpkey(into) -register unsigned long *into; -{ - register unsigned long *from, *endp; - - from = KnL, endp = &KnL[32]; - while( from < endp ) *into++ = *from++; - return; - } - -void usekey(from) -register unsigned long *from; -{ - register unsigned long *to, *endp; - - to = KnL, endp = &KnL[32]; - while( to < endp ) *to++ = *from++; - return; - } - -void des(inblock, outblock) -unsigned char *inblock, *outblock; -{ - unsigned long work[2]; - - scrunch(inblock, work); - desfunc(work, KnL); - unscrun(work, outblock); - return; - } - -static void scrunch(outof, into) -register unsigned char *outof; -register unsigned long *into; -{ - *into = (*outof++ & 0xffL) << 24; - *into |= (*outof++ & 0xffL) << 16; - *into |= (*outof++ & 0xffL) << 8; - *into++ |= (*outof++ & 0xffL); - *into = (*outof++ & 0xffL) << 24; - *into |= (*outof++ & 0xffL) << 16; - *into |= (*outof++ & 0xffL) << 8; - *into |= (*outof & 0xffL); - return; - } - -static void unscrun(outof, into) -register unsigned long *outof; -register unsigned char *into; -{ - *into++ = (unsigned char)((*outof >> 24) & 0xffL); - *into++ = (unsigned char)((*outof >> 16) & 0xffL); - *into++ = (unsigned char)((*outof >> 8) & 0xffL); - *into++ = (unsigned char)( *outof++ & 0xffL); - *into++ = (unsigned char)((*outof >> 24) & 0xffL); - *into++ = (unsigned char)((*outof >> 16) & 0xffL); - *into++ = (unsigned char)((*outof >> 8) & 0xffL); - *into = (unsigned char)( *outof & 0xffL); - return; - } - -static unsigned long SP1[64] = { - 0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L, - 0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L, - 0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L, - 0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L, - 0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L, - 0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L, - 0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L, - 0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L, - 0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L, - 0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L, - 0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L, - 0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L, - 0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L, - 0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L, - 0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L, - 0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L }; - -static unsigned long SP2[64] = { - 0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L, - 0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L, - 0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L, - 0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L, - 0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L, - 0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L, - 0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L, - 0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L, - 0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L, - 0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L, - 0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L, - 0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L, - 0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L, - 0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L, - 0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L, - 0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L }; - -static unsigned long SP3[64] = { - 0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L, - 0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L, - 0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L, - 0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L, - 0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L, - 0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L, - 0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L, - 0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L, - 0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L, - 0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L, - 0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L, - 0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L, - 0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L, - 0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L, - 0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L, - 0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L }; - -static unsigned long SP4[64] = { - 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, - 0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L, - 0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L, - 0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L, - 0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L, - 0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L, - 0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L, - 0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L, - 0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L, - 0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L, - 0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L, - 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, - 0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L, - 0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L, - 0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L, - 0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L }; - -static unsigned long SP5[64] = { - 0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L, - 0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L, - 0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L, - 0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L, - 0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L, - 0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L, - 0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L, - 0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L, - 0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L, - 0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L, - 0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L, - 0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L, - 0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L, - 0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L, - 0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L, - 0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L }; - -static unsigned long SP6[64] = { - 0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L, - 0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L, - 0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L, - 0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L, - 0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L, - 0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L, - 0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L, - 0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L, - 0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L, - 0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L, - 0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L, - 0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L, - 0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L, - 0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L, - 0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L, - 0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L }; - -static unsigned long SP7[64] = { - 0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L, - 0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L, - 0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L, - 0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L, - 0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L, - 0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L, - 0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L, - 0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L, - 0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L, - 0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L, - 0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L, - 0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L, - 0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L, - 0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L, - 0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L, - 0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L }; - -static unsigned long SP8[64] = { - 0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L, - 0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L, - 0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L, - 0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L, - 0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L, - 0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L, - 0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L, - 0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L, - 0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L, - 0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L, - 0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L, - 0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L, - 0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L, - 0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L, - 0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L, - 0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L }; - -static void desfunc(block, keys) -register unsigned long *block, *keys; -{ - register unsigned long fval, work, right, leftt; - register int round; - - leftt = block[0]; - right = block[1]; - work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL; - right ^= work; - leftt ^= (work << 4); - work = ((leftt >> 16) ^ right) & 0x0000ffffL; - right ^= work; - leftt ^= (work << 16); - work = ((right >> 2) ^ leftt) & 0x33333333L; - leftt ^= work; - right ^= (work << 2); - work = ((right >> 8) ^ leftt) & 0x00ff00ffL; - leftt ^= work; - right ^= (work << 8); - right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL; - work = (leftt ^ right) & 0xaaaaaaaaL; - leftt ^= work; - right ^= work; - leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL; - - for( round = 0; round < 8; round++ ) { - work = (right << 28) | (right >> 4); - work ^= *keys++; - fval = SP7[ work & 0x3fL]; - fval |= SP5[(work >> 8) & 0x3fL]; - fval |= SP3[(work >> 16) & 0x3fL]; - fval |= SP1[(work >> 24) & 0x3fL]; - work = right ^ *keys++; - fval |= SP8[ work & 0x3fL]; - fval |= SP6[(work >> 8) & 0x3fL]; - fval |= SP4[(work >> 16) & 0x3fL]; - fval |= SP2[(work >> 24) & 0x3fL]; - leftt ^= fval; - work = (leftt << 28) | (leftt >> 4); - work ^= *keys++; - fval = SP7[ work & 0x3fL]; - fval |= SP5[(work >> 8) & 0x3fL]; - fval |= SP3[(work >> 16) & 0x3fL]; - fval |= SP1[(work >> 24) & 0x3fL]; - work = leftt ^ *keys++; - fval |= SP8[ work & 0x3fL]; - fval |= SP6[(work >> 8) & 0x3fL]; - fval |= SP4[(work >> 16) & 0x3fL]; - fval |= SP2[(work >> 24) & 0x3fL]; - right ^= fval; - } - - right = (right << 31) | (right >> 1); - work = (leftt ^ right) & 0xaaaaaaaaL; - leftt ^= work; - right ^= work; - leftt = (leftt << 31) | (leftt >> 1); - work = ((leftt >> 8) ^ right) & 0x00ff00ffL; - right ^= work; - leftt ^= (work << 8); - work = ((leftt >> 2) ^ right) & 0x33333333L; - right ^= work; - leftt ^= (work << 2); - work = ((right >> 16) ^ leftt) & 0x0000ffffL; - leftt ^= work; - right ^= (work << 16); - work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL; - leftt ^= work; - right ^= (work << 4); - *block++ = right; - *block = leftt; - return; - } - -/* Validation sets: - * - * Single-length key, single-length plaintext - - * Key : 0123 4567 89ab cdef - * Plain : 0123 4567 89ab cde7 - * Cipher : c957 4425 6a5e d31d - * - * Double-length key, single-length plaintext - - * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 - * Plain : 0123 4567 89ab cde7 - * Cipher : 7f1d 0a77 826b 8aff - * - * Double-length key, double-length plaintext - - * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 - * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff - * Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7 - * - * Triple-length key, single-length plaintext - - * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567 - * Plain : 0123 4567 89ab cde7 - * Cipher : de0b 7c06 ae5e 0ed5 - * - * Triple-length key, double-length plaintext - - * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567 - * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff - * Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5 - * - * d3des V5.0a rwo 9208.07 18:44 Graven Imagery - **********************************************************************/ diff --git a/d3des.h b/d3des.h deleted file mode 100644 index b2f9724..0000000 --- a/d3des.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef D3DES_H -#define D3DES_H - -/* - * This is D3DES (V5.09) by Richard Outerbridge with the double and - * triple-length support removed for use in VNC. - * - * These changes are: - * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. - * - * 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. - */ - -/* d3des.h - - * - * Headers and defines for d3des.c - * Graven Imagery, 1992. - * - * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge - * (GEnie : OUTER; CIS : [71755,204]) - */ - -#define EN0 0 /* MODE == encrypt */ -#define DE1 1 /* MODE == decrypt */ - -extern void deskey(unsigned char *, int); -/* hexkey[8] MODE - * Sets the internal key register according to the hexadecimal - * key contained in the 8 bytes of hexkey, according to the DES, - * for encryption or decryption according to MODE. - */ - -extern void usekey(unsigned long *); -/* cookedkey[32] - * Loads the internal key register with the data in cookedkey. - */ - -extern void cpkey(unsigned long *); -/* cookedkey[32] - * Copies the contents of the internal key register into the storage - * located at &cookedkey[0]. - */ - -extern void des(unsigned char *, unsigned char *); -/* from[8] to[8] - * Encrypts/Decrypts (according to the key currently loaded in the - * internal key register) one block of eight bytes at address 'from' - * into the block at address 'to'. They can be the same. - */ - -/* d3des.h V5.09 rwo 9208.04 15:06 Graven Imagery - ********************************************************************/ - -#endif diff --git a/draw.c b/draw.c deleted file mode 100644 index 7e1ed49..0000000 --- a/draw.c +++ /dev/null @@ -1,61 +0,0 @@ -#include - -void rfbFillRect(rfbScreenInfoPtr s,int x1,int y1,int x2,int y2,rfbPixel col) -{ - int rowstride = s->paddedWidthInBytes, bpp = s->bitsPerPixel>>3; - int i,j; - char* colour=(char*)&col; - - if(!rfbEndianTest) - colour += 4-bpp; - for(j=y1;jframeBuffer+j*rowstride+i*bpp,colour,bpp); - rfbMarkRectAsModified(s,x1,y1,x2,y2); -} - -#define SETPIXEL(x,y) \ - memcpy(s->frameBuffer+(y)*rowstride+(x)*bpp,colour,bpp) - -void rfbDrawPixel(rfbScreenInfoPtr s,int x,int y,rfbPixel col) -{ - int rowstride = s->paddedWidthInBytes, bpp = s->bitsPerPixel>>3; - char* colour=(char*)&col; - - if(!rfbEndianTest) - colour += 4-bpp; - SETPIXEL(x,y); - rfbMarkRectAsModified(s,x,y,x+1,y+1); -} - -void rfbDrawLine(rfbScreenInfoPtr s,int x1,int y1,int x2,int y2,rfbPixel col) -{ - int rowstride = s->paddedWidthInBytes, bpp = s->bitsPerPixel>>3; - int i; - char* colour=(char*)&col; - - if(!rfbEndianTest) - colour += 4-bpp; - -#define SWAPPOINTS { i=x1; x1=x2; x2=i; i=y1; y1=y2; y2=i; } - if(abs(x1-x2)y2) - SWAPPOINTS - for(i=y1;i<=y2;i++) - SETPIXEL(x1+(i-y1)*(x2-x1)/(y2-y1),i); - /* TODO: Maybe make this more intelligently? */ - if(x2x2) - SWAPPOINTS - else if(x1==x2) { - rfbDrawPixel(s,x1,y1,col); - return; - } - for(i=x1;i<=x2;i++) - SETPIXEL(i,y1+(i-x1)*(y2-y1)/(x2-x1)); - if(y2 - -int rfbDrawChar(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font, - int x,int y,unsigned char c,rfbPixel col) -{ - int i,j,width,height; - unsigned char* data=font->data+font->metaData[c*5]; - unsigned char d=*data; - int rowstride=rfbScreen->paddedWidthInBytes; - int bpp=rfbScreen->rfbServerFormat.bitsPerPixel/8; - char *colour=(char*)&col; - - if(!rfbEndianTest) - colour += 4-bpp; - - width=font->metaData[c*5+1]; - height=font->metaData[c*5+2]; - x+=font->metaData[c*5+3]; - y+=-font->metaData[c*5+4]-height+1; - - for(j=0;jframeBuffer+(y+j)*rowstride+(x+i)*bpp,colour,bpp); - d<<=1; - } - /* if((i&7)!=0) data++; */ - } - return(width); -} - -void rfbDrawString(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font, - int x,int y,const char* string,rfbPixel colour) -{ - while(*string) { - x+=rfbDrawChar(rfbScreen,font,x,y,*string,colour); - string++; - } -} - -/* TODO: these two functions need to be more efficient */ -int rfbDrawCharWithClip(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font, - int x,int y,unsigned char c, - int x1,int y1,int x2,int y2, - rfbPixel col,rfbPixel bcol) -{ - int i,j,width,height; - unsigned char* data=font->data+font->metaData[c*5]; - unsigned char d; - int rowstride=rfbScreen->paddedWidthInBytes; - int bpp=rfbScreen->rfbServerFormat.bitsPerPixel/8,extra_bytes=0; - char* colour=(char*)&col; - char* bcolour=(char*)&bcol; - - if(!rfbEndianTest) { - colour+=4-bpp; - bcolour+=4-bpp; - } - - width=font->metaData[c*5+1]; - height=font->metaData[c*5+2]; - x+=font->metaData[c*5+3]; - y+=-font->metaData[c*5+4]-height+1; - - /* after clipping, x2 will be count of bytes between rows, - * x1 start of i, y1 start of j, width and height will be adjusted. */ - if(y1>y) { y1-=y; data+=(width+7)/8; height-=y1; y+=y1; } else y1=0; - if(x1>x) { x1-=x; data+=x1; width-=x1; x+=x1; extra_bytes+=x1/8; } else x1=0; - if(y2=x1 && x+i=y1 && y+jframeBuffer+(y+j)*rowstride+(x+i)*bpp, - colour,bpp); - } else if(bcol!=col) { - memcpy(rfbScreen->frameBuffer+(y+j)*rowstride+(x+i)*bpp, - bcolour,bpp); - } - } - d<<=1; - } - /* if((i&7)==0) data++; */ - data += extra_bytes; - } - return(width); -} - -void rfbDrawStringWithClip(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font, - int x,int y,const char* string, - int x1,int y1,int x2,int y2, - rfbPixel colour,rfbPixel backColour) -{ - while(*string) { - x+=rfbDrawCharWithClip(rfbScreen,font,x,y,*string,x1,y1,x2,y2, - colour,backColour); - string++; - } -} - -int rfbWidthOfString(rfbFontDataPtr font,const char* string) -{ - int i=0; - while(*string) { - i+=font->metaData[*string*5+1]; - string++; - } - return(i); -} - -int rfbWidthOfChar(rfbFontDataPtr font,unsigned char c) -{ - return(font->metaData[c*5+1]+font->metaData[c*5+3]); -} - -void rfbFontBBox(rfbFontDataPtr font,unsigned char c,int* x1,int* y1,int* x2,int* y2) -{ - *x1+=font->metaData[c*5+3]; - *y1+=-font->metaData[c*5+4]-font->metaData[c*5+2]+1; - *x2=*x1+font->metaData[c*5+1]; - *y2=*y1+font->metaData[c*5+2]; -} - -#ifndef INT_MAX -#define INT_MAX 0x7fffffff -#endif - -void rfbWholeFontBBox(rfbFontDataPtr font, - int *x1, int *y1, int *x2, int *y2) -{ - int i; - int* m=font->metaData; - - (*x1)=(*y1)=INT_MAX; (*x2)=(*y2)=-INT_MAX+1; - for(i=0;i<256;i++) { - if(m[i*5+1]-m[i*5+3]>(*x2)) - (*x2)=m[i*5+1]-m[i*5+3]; - if(-m[i*5+2]+m[i*5+4]<(*y1)) - (*y1)=-m[i*5+2]+m[i*5+4]; - if(m[i*5+3]<(*x1)) - (*x1)=m[i*5+3]; - if(-m[i*5+4]>(*y2)) - (*y2)=-m[i*5+4]; - } -} - -rfbFontDataPtr rfbLoadConsoleFont(char *filename) -{ - FILE *f=fopen(filename,"rb"); - rfbFontDataPtr p; - int i; - - if(!f) return(0); - - p=(rfbFontDataPtr)malloc(sizeof(rfbFontData)); - p->data=(unsigned char*)malloc(4096); - if(1!=fread(p->data,4096,1,f)) { - free(p->data); - free(p); - return(0); - } - fclose(f); - p->metaData=(int*)malloc(256*5*sizeof(int)); - for(i=0;i<256;i++) { - p->metaData[i*5+0]=i*16; /* offset */ - p->metaData[i*5+1]=8; /* width */ - p->metaData[i*5+2]=16; /* height */ - p->metaData[i*5+3]=0; /* xhot */ - p->metaData[i*5+4]=0; /* yhot */ - } - return(p); -} - -void rfbFreeFont(rfbFontDataPtr f) -{ - free(f->data); - free(f->metaData); - free(f); -} diff --git a/hextile.c b/hextile.c deleted file mode 100644 index e13021b..0000000 --- a/hextile.c +++ /dev/null @@ -1,346 +0,0 @@ -/* - * hextile.c - * - * Routines to implement Hextile Encoding - */ - -/* - * OSXvnc Copyright (C) 2001 Dan McGuirk . - * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. - * 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. - */ - -#include - -static rfbBool sendHextiles8(rfbClientPtr cl, int x, int y, int w, int h); -static rfbBool sendHextiles16(rfbClientPtr cl, int x, int y, int w, int h); -static rfbBool sendHextiles32(rfbClientPtr cl, int x, int y, int w, int h); - - -/* - * rfbSendRectEncodingHextile - send a rectangle using hextile encoding. - */ - -rfbBool -rfbSendRectEncodingHextile(cl, x, y, w, h) - rfbClientPtr cl; - int x, y, w, h; -{ - rfbFramebufferUpdateRectHeader rect; - - if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - rect.r.x = Swap16IfLE(x); - rect.r.y = Swap16IfLE(y); - rect.r.w = Swap16IfLE(w); - rect.r.h = Swap16IfLE(h); - rect.encoding = Swap32IfLE(rfbEncodingHextile); - - memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, - sz_rfbFramebufferUpdateRectHeader); - cl->ublen += sz_rfbFramebufferUpdateRectHeader; - - cl->rfbRectanglesSent[rfbEncodingHextile]++; - cl->rfbBytesSent[rfbEncodingHextile] += sz_rfbFramebufferUpdateRectHeader; - - switch (cl->format.bitsPerPixel) { - case 8: - return sendHextiles8(cl, x, y, w, h); - case 16: - return sendHextiles16(cl, x, y, w, h); - case 32: - return sendHextiles32(cl, x, y, w, h); - } - - rfbLog("rfbSendRectEncodingHextile: bpp %d?\n", cl->format.bitsPerPixel); - return FALSE; -} - - -#define PUT_PIXEL8(pix) (cl->updateBuf[cl->ublen++] = (pix)) - -#define PUT_PIXEL16(pix) (cl->updateBuf[cl->ublen++] = ((char*)&(pix))[0], \ - cl->updateBuf[cl->ublen++] = ((char*)&(pix))[1]) - -#define PUT_PIXEL32(pix) (cl->updateBuf[cl->ublen++] = ((char*)&(pix))[0], \ - cl->updateBuf[cl->ublen++] = ((char*)&(pix))[1], \ - cl->updateBuf[cl->ublen++] = ((char*)&(pix))[2], \ - cl->updateBuf[cl->ublen++] = ((char*)&(pix))[3]) - - -#define DEFINE_SEND_HEXTILES(bpp) \ - \ - \ -static rfbBool subrectEncode##bpp(rfbClientPtr cli, uint##bpp##_t *data, int w, int h, \ - uint##bpp##_t bg, uint##bpp##_t fg, rfbBool mono); \ -static void testColours##bpp(uint##bpp##_t *data, int size, rfbBool *mono, \ - rfbBool *solid, uint##bpp##_t *bg, uint##bpp##_t *fg); \ - \ - \ -/* \ - * rfbSendHextiles \ - */ \ - \ -static rfbBool \ -sendHextiles##bpp(cl, rx, ry, rw, rh) \ - rfbClientPtr cl; \ - int rx, ry, rw, rh; \ -{ \ - int x, y, w, h; \ - int startUblen; \ - char *fbptr; \ - uint##bpp##_t bg = 0, fg = 0, newBg, newFg; \ - rfbBool mono, solid; \ - rfbBool validBg = FALSE; \ - rfbBool validFg = FALSE; \ - uint##bpp##_t clientPixelData[16*16*(bpp/8)]; \ - \ - for (y = ry; y < ry+rh; y += 16) { \ - for (x = rx; x < rx+rw; x += 16) { \ - w = h = 16; \ - if (rx+rw - x < 16) \ - w = rx+rw - x; \ - if (ry+rh - y < 16) \ - h = ry+rh - y; \ - \ - if ((cl->ublen + 1 + (2 + 16 * 16) * (bpp/8)) > \ - UPDATE_BUF_SIZE) { \ - if (!rfbSendUpdateBuf(cl)) \ - return FALSE; \ - } \ - \ - fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y) \ - + (x * (cl->screen->bitsPerPixel / 8))); \ - \ - (*cl->translateFn)(cl->translateLookupTable, &(cl->screen->rfbServerFormat), \ - &cl->format, fbptr, (char *)clientPixelData, \ - cl->screen->paddedWidthInBytes, w, h); \ - \ - startUblen = cl->ublen; \ - cl->updateBuf[startUblen] = 0; \ - cl->ublen++; \ - \ - testColours##bpp(clientPixelData, w * h, \ - &mono, &solid, &newBg, &newFg); \ - \ - if (!validBg || (newBg != bg)) { \ - validBg = TRUE; \ - bg = newBg; \ - cl->updateBuf[startUblen] |= rfbHextileBackgroundSpecified; \ - PUT_PIXEL##bpp(bg); \ - } \ - \ - if (solid) { \ - cl->rfbBytesSent[rfbEncodingHextile] += cl->ublen - startUblen; \ - continue; \ - } \ - \ - cl->updateBuf[startUblen] |= rfbHextileAnySubrects; \ - \ - if (mono) { \ - if (!validFg || (newFg != fg)) { \ - validFg = TRUE; \ - fg = newFg; \ - cl->updateBuf[startUblen] |= rfbHextileForegroundSpecified; \ - PUT_PIXEL##bpp(fg); \ - } \ - } else { \ - validFg = FALSE; \ - cl->updateBuf[startUblen] |= rfbHextileSubrectsColoured; \ - } \ - \ - if (!subrectEncode##bpp(cl, clientPixelData, w, h, bg, fg, mono)) { \ - /* encoding was too large, use raw */ \ - validBg = FALSE; \ - validFg = FALSE; \ - cl->ublen = startUblen; \ - cl->updateBuf[cl->ublen++] = rfbHextileRaw; \ - (*cl->translateFn)(cl->translateLookupTable, \ - &(cl->screen->rfbServerFormat), &cl->format, fbptr, \ - (char *)clientPixelData, \ - cl->screen->paddedWidthInBytes, w, h); \ - \ - memcpy(&cl->updateBuf[cl->ublen], (char *)clientPixelData, \ - w * h * (bpp/8)); \ - \ - cl->ublen += w * h * (bpp/8); \ - } \ - \ - cl->rfbBytesSent[rfbEncodingHextile] += cl->ublen - startUblen; \ - } \ - } \ - \ - return TRUE; \ -} \ - \ - \ -static rfbBool \ -subrectEncode##bpp(rfbClientPtr cl, uint##bpp##_t *data, int w, int h, \ - uint##bpp##_t bg, uint##bpp##_t fg, rfbBool mono) \ -{ \ - uint##bpp##_t cl2; \ - int x,y; \ - int i,j; \ - int hx=0,hy,vx=0,vy; \ - int hyflag; \ - uint##bpp##_t *seg; \ - uint##bpp##_t *line; \ - int hw,hh,vw,vh; \ - int thex,they,thew,theh; \ - int numsubs = 0; \ - int newLen; \ - int nSubrectsUblen; \ - \ - nSubrectsUblen = cl->ublen; \ - cl->ublen++; \ - \ - for (y=0; y 0) && (i >= hx)) { \ - hy += 1; \ - } else { \ - hyflag = 0; \ - } \ - } \ - vy = j-1; \ - \ - /* We now have two possible subrects: (x,y,hx,hy) and \ - * (x,y,vx,vy). We'll choose the bigger of the two. \ - */ \ - hw = hx-x+1; \ - hh = hy-y+1; \ - vw = vx-x+1; \ - vh = vy-y+1; \ - \ - thex = x; \ - they = y; \ - \ - if ((hw*hh) > (vw*vh)) { \ - thew = hw; \ - theh = hh; \ - } else { \ - thew = vw; \ - theh = vh; \ - } \ - \ - if (mono) { \ - newLen = cl->ublen - nSubrectsUblen + 2; \ - } else { \ - newLen = cl->ublen - nSubrectsUblen + bpp/8 + 2; \ - } \ - \ - if (newLen > (w * h * (bpp/8))) \ - return FALSE; \ - \ - numsubs += 1; \ - \ - if (!mono) PUT_PIXEL##bpp(cl2); \ - \ - cl->updateBuf[cl->ublen++] = rfbHextilePackXY(thex,they); \ - cl->updateBuf[cl->ublen++] = rfbHextilePackWH(thew,theh); \ - \ - /* \ - * Now mark the subrect as done. \ - */ \ - for (j=they; j < (they+theh); j++) { \ - for (i=thex; i < (thex+thew); i++) { \ - data[j*w+i] = bg; \ - } \ - } \ - } \ - } \ - } \ - \ - cl->updateBuf[nSubrectsUblen] = numsubs; \ - \ - return TRUE; \ -} \ - \ - \ -/* \ - * testColours() tests if there are one (solid), two (mono) or more \ - * colours in a tile and gets a reasonable guess at the best background \ - * pixel, and the foreground pixel for mono. \ - */ \ - \ -static void \ -testColours##bpp(data,size,mono,solid,bg,fg) \ - uint##bpp##_t *data; \ - int size; \ - rfbBool *mono; \ - rfbBool *solid; \ - uint##bpp##_t *bg; \ - uint##bpp##_t *fg; \ -{ \ - uint##bpp##_t colour1 = 0, colour2 = 0; \ - int n1 = 0, n2 = 0; \ - *mono = TRUE; \ - *solid = TRUE; \ - \ - for (; size > 0; size--, data++) { \ - \ - if (n1 == 0) \ - colour1 = *data; \ - \ - if (*data == colour1) { \ - n1++; \ - continue; \ - } \ - \ - if (n2 == 0) { \ - *solid = FALSE; \ - colour2 = *data; \ - } \ - \ - if (*data == colour2) { \ - n2++; \ - continue; \ - } \ - \ - *mono = FALSE; \ - break; \ - } \ - \ - if (n1 > n2) { \ - *bg = colour1; \ - *fg = colour2; \ - } else { \ - *bg = colour2; \ - *fg = colour1; \ - } \ -} - -DEFINE_SEND_HEXTILES(8) -DEFINE_SEND_HEXTILES(16) -DEFINE_SEND_HEXTILES(32) diff --git a/httpd.c b/httpd.c deleted file mode 100644 index 25f2807..0000000 --- a/httpd.c +++ /dev/null @@ -1,579 +0,0 @@ -/* - * httpd.c - a simple HTTP server - */ - -/* - * Copyright (C) 2002 RealVNC Ltd. - * Copyright (C) 1999 AT&T Laboratories Cambridge. 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. - */ - -#include - -#include -#ifdef LIBVNCSERVER_HAVE_UNISTD_H -#include -#endif -#ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H -#include -#endif -#ifdef LIBVNCSERVER_HAVE_FCNTL_H -#include -#endif -#include - -#ifdef WIN32 -#include -#define close closesocket -#else -#ifdef LIBVNCSERVER_HAVE_SYS_TIME_H -#include -#endif -#ifdef LIBVNCSERVER_HAVE_SYS_SOCKET_H -#include -#endif -#ifdef LIBVNCSERVER_HAVE_NETINET_IN_H -#include -#include -#include -#include -#endif -#include -#endif - -#ifdef USE_LIBWRAP -#include -#endif - -#define NOT_FOUND_STR "HTTP/1.0 404 Not found\r\n\r\n" \ - "File Not Found\n" \ - "

File Not Found

\n" - -#define INVALID_REQUEST_STR "HTTP/1.0 400 Invalid Request\r\n\r\n" \ - "Invalid Request\n" \ - "

Invalid request

\n" - -#define OK_STR "HTTP/1.0 200 OK\nContent-Type: text/html\r\n\r\n" - -static void httpProcessInput(); -static rfbBool compareAndSkip(char **ptr, const char *str); -static rfbBool parseParams(const char *request, char *result, int max_bytes); -static rfbBool validateString(char *str); - -#define BUF_SIZE 32768 - -static char buf[BUF_SIZE]; -static size_t buf_filled=0; - -/* - * httpInitSockets sets up the TCP socket to listen for HTTP connections. - */ - -void -httpInitSockets(rfbScreenInfoPtr rfbScreen) -{ - if (rfbScreen->httpInitDone) - return; - - rfbScreen->httpInitDone = TRUE; - - if (!rfbScreen->httpDir) - return; - - if (rfbScreen->httpPort == 0) { - rfbScreen->httpPort = rfbScreen->rfbPort-100; - } - - rfbLog("Listening for HTTP connections on TCP port %d\n", rfbScreen->httpPort); - - rfbLog(" URL http://%s:%d\n",rfbScreen->rfbThisHost,rfbScreen->httpPort); - - if ((rfbScreen->httpListenSock = ListenOnTCPPort(rfbScreen->httpPort)) < 0) { - rfbLogPerror("ListenOnTCPPort"); - return; - } - - /*AddEnabledDevice(httpListenSock);*/ -} - - -/* - * httpCheckFds is called from ProcessInputEvents to check for input on the - * HTTP socket(s). If there is input to process, httpProcessInput is called. - */ - -void -httpCheckFds(rfbScreenInfoPtr rfbScreen) -{ - int nfds; - fd_set fds; - struct timeval tv; - struct sockaddr_in addr; - size_t addrlen = sizeof(addr); - - if (!rfbScreen->httpDir) - return; - - FD_ZERO(&fds); - FD_SET(rfbScreen->httpListenSock, &fds); - if (rfbScreen->httpSock >= 0) { - FD_SET(rfbScreen->httpSock, &fds); - } - tv.tv_sec = 0; - tv.tv_usec = 0; - nfds = select(max(rfbScreen->httpSock,rfbScreen->httpListenSock) + 1, &fds, NULL, NULL, &tv); - if (nfds == 0) { - return; - } - if (nfds < 0) { -#ifdef WIN32 - errno = WSAGetLastError(); -#endif - if (errno != EINTR) - rfbLogPerror("httpCheckFds: select"); - return; - } - - if ((rfbScreen->httpSock >= 0) && FD_ISSET(rfbScreen->httpSock, &fds)) { - httpProcessInput(rfbScreen); - } - - if (FD_ISSET(rfbScreen->httpListenSock, &fds)) { - int flags; - if (rfbScreen->httpSock >= 0) close(rfbScreen->httpSock); - - if ((rfbScreen->httpSock = accept(rfbScreen->httpListenSock, - (struct sockaddr *)&addr, &addrlen)) < 0) { - rfbLogPerror("httpCheckFds: accept"); - return; - } -#ifdef USE_LIBWRAP - if(!hosts_ctl("vnc",STRING_UNKNOWN,inet_ntoa(addr.sin_addr), - STRING_UNKNOWN)) { - rfbLog("Rejected HTTP connection from client %s\n", - inet_ntoa(addr.sin_addr)); -#else - flags = fcntl(rfbScreen->httpSock, F_GETFL); - - if (flags < 0 || fcntl(rfbScreen->httpSock, F_SETFL, flags | O_NONBLOCK) == -1) { - rfbLogPerror("httpCheckFds: fcntl"); -#endif - close(rfbScreen->httpSock); - rfbScreen->httpSock = -1; - return; - } - flags=fcntl(rfbScreen->httpSock,F_GETFL); - if(flags==-1 || - fcntl(rfbScreen->httpSock,F_SETFL,flags|O_NONBLOCK)==-1) { - rfbLogPerror("httpCheckFds: fcntl"); - close(rfbScreen->httpSock); - rfbScreen->httpSock=-1; - return; - } - - /*AddEnabledDevice(httpSock);*/ - } -} - - -static void -httpCloseSock(rfbScreenInfoPtr rfbScreen) -{ - close(rfbScreen->httpSock); - rfbScreen->httpSock = -1; - buf_filled = 0; -} - -static rfbClientRec cl; - -/* - * httpProcessInput is called when input is received on the HTTP socket. - */ - -static void -httpProcessInput(rfbScreenInfoPtr rfbScreen) -{ - struct sockaddr_in addr; - size_t addrlen = sizeof(addr); - char fullFname[512]; - char params[1024]; - char *ptr; - char *fname; - unsigned int maxFnameLen; - FILE* fd; - rfbBool performSubstitutions = FALSE; - char str[256+32]; -#ifndef WIN32 - struct passwd *user = getpwuid(getuid()); -#endif - - cl.sock=rfbScreen->httpSock; - - if (strlen(rfbScreen->httpDir) > 255) { - rfbErr("-httpd directory too long\n"); - httpCloseSock(rfbScreen); - return; - } - strcpy(fullFname, rfbScreen->httpDir); - fname = &fullFname[strlen(fullFname)]; - maxFnameLen = 511 - strlen(fullFname); - - buf_filled=0; - - /* Read data from the HTTP client until we get a complete request. */ - while (1) { - ssize_t got; - - if (buf_filled > sizeof (buf)) { - rfbErr("httpProcessInput: HTTP request is too long\n"); - httpCloseSock(rfbScreen); - return; - } - - got = read (rfbScreen->httpSock, buf + buf_filled, - sizeof (buf) - buf_filled - 1); - - if (got <= 0) { - if (got == 0) { - rfbErr("httpd: premature connection close\n"); - } else { - if (errno == EAGAIN) { - return; - } - rfbLogPerror("httpProcessInput: read"); - } - httpCloseSock(rfbScreen); - return; - } - - buf_filled += got; - buf[buf_filled] = '\0'; - - /* Is it complete yet (is there a blank line)? */ - if (strstr (buf, "\r\r") || strstr (buf, "\n\n") || - strstr (buf, "\r\n\r\n") || strstr (buf, "\n\r\n\r")) - break; - } - - - /* Process the request. */ - if(rfbScreen->httpEnableProxyConnect) { - const static char* PROXY_OK_STR = "HTTP/1.0 200 OK\r\nContent-Type: octet-stream\r\nPragma: no-cache\r\n\r\n"; - if(!strncmp(buf, "CONNECT ", 8)) { - if(atoi(strchr(buf, ':')+1)!=rfbScreen->rfbPort) { - rfbErr("httpd: CONNECT format invalid.\n"); - WriteExact(&cl,INVALID_REQUEST_STR, strlen(INVALID_REQUEST_STR)); - httpCloseSock(rfbScreen); - return; - } - /* proxy connection */ - rfbLog("httpd: client asked for CONNECT\n"); - WriteExact(&cl,PROXY_OK_STR,strlen(PROXY_OK_STR)); - rfbNewClientConnection(rfbScreen,rfbScreen->httpSock); - rfbScreen->httpSock = -1; - return; - } - if (!strncmp(buf, "GET ",4) && !strncmp(strchr(buf,'/'),"/proxied.connection HTTP/1.", 27)) { - /* proxy connection */ - rfbLog("httpd: client asked for /proxied.connection\n"); - WriteExact(&cl,PROXY_OK_STR,strlen(PROXY_OK_STR)); - rfbNewClientConnection(rfbScreen,rfbScreen->httpSock); - rfbScreen->httpSock = -1; - return; - } - } - - if (strncmp(buf, "GET ", 4)) { - rfbErr("httpd: no GET line\n"); - httpCloseSock(rfbScreen); - return; - } else { - /* Only use the first line. */ - buf[strcspn(buf, "\n\r")] = '\0'; - } - - if (strlen(buf) > maxFnameLen) { - rfbErr("httpd: GET line too long\n"); - httpCloseSock(rfbScreen); - return; - } - - if (sscanf(buf, "GET %s HTTP/1.0", fname) != 1) { - rfbErr("httpd: couldn't parse GET line\n"); - httpCloseSock(rfbScreen); - return; - } - - if (fname[0] != '/') { - rfbErr("httpd: filename didn't begin with '/'\n"); - WriteExact(&cl, NOT_FOUND_STR, strlen(NOT_FOUND_STR)); - httpCloseSock(rfbScreen); - return; - } - - if (strchr(fname+1, '/') != NULL) { - rfbErr("httpd: asking for file in other directory\n"); - WriteExact(&cl, NOT_FOUND_STR, strlen(NOT_FOUND_STR)); - httpCloseSock(rfbScreen); - return; - } - - getpeername(rfbScreen->httpSock, (struct sockaddr *)&addr, &addrlen); - rfbLog("httpd: get '%s' for %s\n", fname+1, - inet_ntoa(addr.sin_addr)); - - /* Extract parameters from the URL string if necessary */ - - params[0] = '\0'; - ptr = strchr(fname, '?'); - if (ptr != NULL) { - *ptr = '\0'; - if (!parseParams(&ptr[1], params, 1024)) { - params[0] = '\0'; - rfbErr("httpd: bad parameters in the URL\n"); - } - } - - - /* If we were asked for '/', actually read the file index.vnc */ - - if (strcmp(fname, "/") == 0) { - strcpy(fname, "/index.vnc"); - rfbLog("httpd: defaulting to '%s'\n", fname+1); - } - - /* Substitutions are performed on files ending .vnc */ - - if (strlen(fname) >= 4 && strcmp(&fname[strlen(fname)-4], ".vnc") == 0) { - performSubstitutions = TRUE; - } - - /* Open the file */ - - if ((fd = fopen(fullFname, "r")) == 0) { - rfbLogPerror("httpProcessInput: open"); - WriteExact(&cl, NOT_FOUND_STR, strlen(NOT_FOUND_STR)); - httpCloseSock(rfbScreen); - return; - } - - WriteExact(&cl, OK_STR, strlen(OK_STR)); - - while (1) { - int n = fread(buf, 1, BUF_SIZE-1, fd); - if (n < 0) { - rfbLogPerror("httpProcessInput: read"); - fclose(fd); - httpCloseSock(rfbScreen); - return; - } - - if (n == 0) - break; - - if (performSubstitutions) { - - /* Substitute $WIDTH, $HEIGHT, etc with the appropriate values. - This won't quite work properly if the .vnc file is longer than - BUF_SIZE, but it's reasonable to assume that .vnc files will - always be short. */ - - char *ptr = buf; - char *dollar; - buf[n] = 0; /* make sure it's null-terminated */ - - while ((dollar = strchr(ptr, '$'))!=NULL) { - WriteExact(&cl, ptr, (dollar - ptr)); - - ptr = dollar; - - if (compareAndSkip(&ptr, "$WIDTH")) { - - sprintf(str, "%d", rfbScreen->width); - WriteExact(&cl, str, strlen(str)); - - } else if (compareAndSkip(&ptr, "$HEIGHT")) { - - sprintf(str, "%d", rfbScreen->height); - WriteExact(&cl, str, strlen(str)); - - } else if (compareAndSkip(&ptr, "$APPLETWIDTH")) { - - sprintf(str, "%d", rfbScreen->width); - WriteExact(&cl, str, strlen(str)); - - } else if (compareAndSkip(&ptr, "$APPLETHEIGHT")) { - - sprintf(str, "%d", rfbScreen->height + 32); - WriteExact(&cl, str, strlen(str)); - - } else if (compareAndSkip(&ptr, "$PORT")) { - - sprintf(str, "%d", rfbScreen->rfbPort); - WriteExact(&cl, str, strlen(str)); - - } else if (compareAndSkip(&ptr, "$DESKTOP")) { - - WriteExact(&cl, rfbScreen->desktopName, strlen(rfbScreen->desktopName)); - - } else if (compareAndSkip(&ptr, "$DISPLAY")) { - - sprintf(str, "%s:%d", rfbScreen->rfbThisHost, rfbScreen->rfbPort-5900); - WriteExact(&cl, str, strlen(str)); - - } else if (compareAndSkip(&ptr, "$USER")) { -#ifndef WIN32 - if (user) { - WriteExact(&cl, user->pw_name, - strlen(user->pw_name)); - } else -#endif - WriteExact(&cl, "?", 1); - } else if (compareAndSkip(&ptr, "$PARAMS")) { - if (params[0] != '\0') - WriteExact(&cl, params, strlen(params)); - } else { - if (!compareAndSkip(&ptr, "$$")) - ptr++; - - if (WriteExact(&cl, "$", 1) < 0) { - fclose(fd); - httpCloseSock(rfbScreen); - return; - } - } - } - if (WriteExact(&cl, ptr, (&buf[n] - ptr)) < 0) - break; - - } else { - - /* For files not ending .vnc, just write out the buffer */ - - if (WriteExact(&cl, buf, n) < 0) - break; - } - } - - fclose(fd); - httpCloseSock(rfbScreen); -} - - -static rfbBool -compareAndSkip(char **ptr, const char *str) -{ - if (strncmp(*ptr, str, strlen(str)) == 0) { - *ptr += strlen(str); - return TRUE; - } - - return FALSE; -} - -/* - * Parse the request tail after the '?' character, and format a sequence - * of tags for inclusion into an HTML page with embedded applet. - */ - -static rfbBool -parseParams(const char *request, char *result, int max_bytes) -{ - char param_request[128]; - char param_formatted[196]; - const char *tail; - char *delim_ptr; - char *value_str; - int cur_bytes, len; - - result[0] = '\0'; - cur_bytes = 0; - - tail = request; - for (;;) { - /* Copy individual "name=value" string into a buffer */ - delim_ptr = strchr((char *)tail, '&'); - if (delim_ptr == NULL) { - if (strlen(tail) >= sizeof(param_request)) { - return FALSE; - } - strcpy(param_request, tail); - } else { - len = delim_ptr - tail; - if (len >= sizeof(param_request)) { - return FALSE; - } - memcpy(param_request, tail, len); - param_request[len] = '\0'; - } - - /* Split the request into parameter name and value */ - value_str = strchr(¶m_request[1], '='); - if (value_str == NULL) { - return FALSE; - } - *value_str++ = '\0'; - if (strlen(value_str) == 0) { - return FALSE; - } - - /* Validate both parameter name and value */ - if (!validateString(param_request) || !validateString(value_str)) { - return FALSE; - } - - /* Prepare HTML-formatted representation of the name=value pair */ - len = sprintf(param_formatted, - "\n", - param_request, value_str); - if (cur_bytes + len + 1 > max_bytes) { - return FALSE; - } - strcat(result, param_formatted); - cur_bytes += len; - - /* Go to the next parameter */ - if (delim_ptr == NULL) { - break; - } - tail = delim_ptr + 1; - } - return TRUE; -} - -/* - * Check if the string consists only of alphanumeric characters, '+' - * signs, underscores, and dots. Replace all '+' signs with spaces. - */ - -static rfbBool -validateString(char *str) -{ - char *ptr; - - for (ptr = str; *ptr != '\0'; ptr++) { - if (!isalnum(*ptr) && *ptr != '_' && *ptr != '.') { - if (*ptr == '+') { - *ptr = ' '; - } else { - return FALSE; - } - } - } - return TRUE; -} - diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c index 7ea14ae..bb2d7a4 100644 --- a/libvncclient/rfbproto.c +++ b/libvncclient/rfbproto.c @@ -1107,5 +1107,5 @@ JpegSetSrcManager(j_decompress_ptr cinfo, uint8_t *compressedData, #define usekey rfbUseKey #define cpkey rfbCPKey -#include "../vncauth.c" -#include "../d3des.c" +#include "../libvncserver/vncauth.c" +#include "../libvncserver/d3des.c" diff --git a/libvncserver/Makefile.am b/libvncserver/Makefile.am new file mode 100644 index 0000000..0a14983 --- /dev/null +++ b/libvncserver/Makefile.am @@ -0,0 +1,42 @@ +CFLAGS=-g -Wall + +includedir=$(prefix)/include/rfb +#include_HEADERS=rfb.h rfbconfig.h rfbint.h rfbproto.h keysym.h rfbregion.h + +include_HEADERS=../rfb/rfb.h ../rfb/rfbconfig.h ../rfb/rfbint.h \ + ../rfb/rfbproto.h ../rfb/keysym.h ../rfb/rfbregion.h ../rfb/rfbclient.h + +noinst_HEADERS=d3des.h ../rfb/default8x16.h zrleoutstream.h \ + zrlepalettehelper.h zrletypes.h + +EXTRA_DIST=tableinit24.c tableinittctemplate.c tabletranstemplate.c \ + tableinitcmtemplate.c tabletrans24template.c \ + zrleencodetemplate.c + +if HAVE_LIBZ +ZLIBSRCS = zlib.c zrle.c zrleoutstream.c zrlepalettehelper.c +if HAVE_LIBJPEG +JPEGSRCS = tight.c +endif +endif + +LIB_SRCS = main.c rfbserver.c rfbregion.c auth.c sockets.c \ + stats.c corre.c hextile.c rre.c translate.c cutpaste.c \ + httpd.c cursor.c font.c \ + draw.c selbox.c d3des.c vncauth.c cargs.c \ + $(ZLIBSRCS) $(JPEGSRCS) + +libvncserver_a_SOURCES=$(LIB_SRCS) + +lib_LIBRARIES=libvncserver.a + +if HAVE_RPM +$(PACKAGE)-$(VERSION).tar.gz: dist + +# Rule to build RPM distribution package +rpm: $(PACKAGE)-$(VERSION).tar.gz libvncserver.spec + cp $(PACKAGE)-$(VERSION).tar.gz @RPMSOURCEDIR@ + rpm -ba libvncserver.spec +endif + + diff --git a/libvncserver/auth.c b/libvncserver/auth.c new file mode 100755 index 0000000..ec253dd --- /dev/null +++ b/libvncserver/auth.c @@ -0,0 +1,104 @@ +/* + * auth.c - deal with authentication. + * + * This file implements the VNC authentication protocol when setting up an RFB + * connection. + */ + +/* + * OSXvnc Copyright (C) 2001 Dan McGuirk . + * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. + * 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. + */ + +#include + +/* + * rfbAuthNewClient is called when we reach the point of authenticating + * a new client. If authentication isn't being used then we simply send + * rfbNoAuth. Otherwise we send rfbVncAuth plus the challenge. + */ + +void +rfbAuthNewClient(cl) + rfbClientPtr cl; +{ + char buf[4 + CHALLENGESIZE]; + int len; + + cl->state = RFB_AUTHENTICATION; + + if (cl->screen->rfbAuthPasswdData && !cl->reverseConnection) { + *(uint32_t *)buf = Swap32IfLE(rfbVncAuth); + vncRandomBytes(cl->authChallenge); + memcpy(&buf[4], (char *)cl->authChallenge, CHALLENGESIZE); + len = 4 + CHALLENGESIZE; + } else { + *(uint32_t *)buf = Swap32IfLE(rfbNoAuth); + len = 4; + cl->state = RFB_INITIALISATION; + } + + if (WriteExact(cl, buf, len) < 0) { + rfbLogPerror("rfbAuthNewClient: write"); + rfbCloseClient(cl); + return; + } +} + + +/* + * rfbAuthProcessClientMessage is called when the client sends its + * authentication response. + */ + +void +rfbAuthProcessClientMessage(cl) + rfbClientPtr cl; +{ + int n; + uint8_t response[CHALLENGESIZE]; + uint32_t authResult; + + if ((n = ReadExact(cl, (char *)response, CHALLENGESIZE)) <= 0) { + if (n != 0) + rfbLogPerror("rfbAuthProcessClientMessage: read"); + rfbCloseClient(cl); + return; + } + + if(!cl->screen->passwordCheck(cl,(const char*)response,CHALLENGESIZE)) { + rfbErr("rfbAuthProcessClientMessage: password check failed\n"); + authResult = Swap32IfLE(rfbVncAuthFailed); + if (WriteExact(cl, (char *)&authResult, 4) < 0) { + rfbLogPerror("rfbAuthProcessClientMessage: write"); + } + rfbCloseClient(cl); + return; + } + + authResult = Swap32IfLE(rfbVncAuthOK); + + if (WriteExact(cl, (char *)&authResult, 4) < 0) { + rfbLogPerror("rfbAuthProcessClientMessage: write"); + rfbCloseClient(cl); + return; + } + + cl->state = RFB_INITIALISATION; +} diff --git a/libvncserver/cargs.c b/libvncserver/cargs.c new file mode 100644 index 0000000..c26050e --- /dev/null +++ b/libvncserver/cargs.c @@ -0,0 +1,169 @@ +/* + * This parses the command line arguments. It was seperated from main.c by + * Justin Dearing . + */ + +/* + * LibVNCServer (C) 2001 Johannes E. Schindelin + * Original OSXvnc (C) 2001 Dan McGuirk . + * Original Xvnc (C) 1999 AT&T Laboratories Cambridge. + * All Rights Reserved. + * + * see GPL (latest version) for full details + */ + +#include + +void +rfbUsage(void) +{ + fprintf(stderr, "-rfbport port TCP port for RFB protocol\n"); + fprintf(stderr, "-rfbwait time max time in ms to wait for RFB client\n"); + fprintf(stderr, "-rfbauth passwd-file use authentication on RFB protocol\n" + " (use 'storepasswd' to create a password file)\n"); + fprintf(stderr, "-passwd plain-password use authentication \n" + " (use plain-password as password, USE AT YOUR RISK)\n"); + fprintf(stderr, "-deferupdate time time in ms to defer updates " + "(default 40)\n"); + fprintf(stderr, "-desktop name VNC desktop name (default \"LibVNCServer\")\n"); + fprintf(stderr, "-alwaysshared always treat new clients as shared\n"); + fprintf(stderr, "-nevershared never treat new clients as shared\n"); + fprintf(stderr, "-dontdisconnect don't disconnect existing clients when a " + "new non-shared\n" + " connection comes in (refuse new connection " + "instead)\n"); + fprintf(stderr, "-httpdir dir-path enable http server using dir-path home\n"); + fprintf(stderr, "-httpport portnum use portnum for http connection\n"); + fprintf(stderr, "-enablehttpproxy enable http proxy support\n"); + fprintf(stderr, "-progressive height enable progressive updating for slow links\n"); +} + +/* purges COUNT arguments from ARGV at POSITION and decrements ARGC. + POSITION points to the first non purged argument afterwards. */ +void rfbPurgeArguments(int* argc,int* position,int count,char *argv[]) +{ + int amount=(*argc)-(*position)-count; + if(amount) + memmove(argv+(*position),argv+(*position)+count,sizeof(char*)*amount); + (*argc)-=count; +} + +rfbBool +rfbProcessArguments(rfbScreenInfoPtr rfbScreen,int* argc, char *argv[]) +{ + int i,i1; + + if(!argc) return TRUE; + + for (i = i1 = 1; i < *argc;) { + if (strcmp(argv[i], "-help") == 0) { + rfbUsage(); + return FALSE; + } else if (strcmp(argv[i], "-rfbport") == 0) { /* -rfbport port */ + if (i + 1 >= *argc) { + rfbUsage(); + return FALSE; + } + rfbScreen->rfbPort = atoi(argv[++i]); + } else if (strcmp(argv[i], "-rfbwait") == 0) { /* -rfbwait ms */ + if (i + 1 >= *argc) { + rfbUsage(); + return FALSE; + } + rfbScreen->rfbMaxClientWait = atoi(argv[++i]); + } else if (strcmp(argv[i], "-rfbauth") == 0) { /* -rfbauth passwd-file */ + if (i + 1 >= *argc) { + rfbUsage(); + return FALSE; + } + rfbScreen->rfbAuthPasswdData = argv[++i]; + } else if (strcmp(argv[i], "-passwd") == 0) { /* -passwd password */ + char **passwds = malloc(sizeof(char**)*2); + if (i + 1 >= *argc) { + rfbUsage(); + return FALSE; + } + passwds[0] = argv[++i]; + passwds[1] = 0; + rfbScreen->rfbAuthPasswdData = (void*)passwds; + rfbScreen->passwordCheck = rfbCheckPasswordByList; + } else if (strcmp(argv[i], "-deferupdate") == 0) { /* -deferupdate milliseconds */ + if (i + 1 >= *argc) { + rfbUsage(); + return FALSE; + } + rfbScreen->rfbDeferUpdateTime = atoi(argv[++i]); + } else if (strcmp(argv[i], "-desktop") == 0) { /* -desktop desktop-name */ + if (i + 1 >= *argc) { + rfbUsage(); + return FALSE; + } + rfbScreen->desktopName = argv[++i]; + } else if (strcmp(argv[i], "-alwaysshared") == 0) { + rfbScreen->rfbAlwaysShared = TRUE; + } else if (strcmp(argv[i], "-nevershared") == 0) { + rfbScreen->rfbNeverShared = TRUE; + } else if (strcmp(argv[i], "-dontdisconnect") == 0) { + rfbScreen->rfbDontDisconnect = TRUE; + } else if (strcmp(argv[i], "-httpdir") == 0) { /* -httpdir directory-path */ + if (i + 1 >= *argc) { + rfbUsage(); + return FALSE; + } + rfbScreen->httpDir = argv[++i]; + } else if (strcmp(argv[i], "-httpport") == 0) { /* -httpport portnum */ + if (i + 1 >= *argc) { + rfbUsage(); + return FALSE; + } + rfbScreen->httpPort = atoi(argv[++i]); + } else if (strcmp(argv[i], "-enablehttpproxy") == 0) { + rfbScreen->httpEnableProxyConnect = TRUE; + } else if (strcmp(argv[i], "-progressive") == 0) { /* -httpport portnum */ + if (i + 1 >= *argc) { + rfbUsage(); + return FALSE; + } + rfbScreen->progressiveSliceHeight = atoi(argv[++i]); + } else { + i++; + i1=i; + continue; + } + /* we just remove the processed arguments from the list */ + rfbPurgeArguments(argc,&i1,i-i1+1,argv); + i=i1; + } + return TRUE; +} + +void rfbSizeUsage() +{ + fprintf(stderr, "-width sets the width of the framebuffer\n"); + fprintf(stderr, "-height sets the height of the framebuffer\n"); +} + +rfbBool +rfbProcessSizeArguments(int* width,int* height,int* bpp,int* argc, char *argv[]) +{ + int i,i1; + + if(!argc) return TRUE; + for (i = i1 = 1; i < *argc-1;) { + if (strcmp(argv[i], "-bpp") == 0) { + *bpp = atoi(argv[++i]); + } else if (strcmp(argv[i], "-width") == 0) { + *width = atoi(argv[++i]); + } else if (strcmp(argv[i], "-height") == 0) { + *height = atoi(argv[++i]); + } else { + i++; + i1=i; + continue; + } + rfbPurgeArguments(argc,&i1,i-i1,argv); + i=i1; + } + return TRUE; +} + diff --git a/libvncserver/config.h b/libvncserver/config.h new file mode 100644 index 0000000..4daa20c --- /dev/null +++ b/libvncserver/config.h @@ -0,0 +1,240 @@ +/* config.h. Generated by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Enable 24 bit per pixel in native framebuffer */ +#define ALLOW24BPP 1 + +/* Enable BackChannel communication */ +#define BACKCHANNEL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ARPA_INET_H 1 + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `fork' function. */ +#define HAVE_FORK 1 + +/* Define to 1 if you have the `ftime' function. */ +#define HAVE_FTIME 1 + +/* Define to 1 if you have the `gethostbyname' function. */ +#define HAVE_GETHOSTBYNAME 1 + +/* Define to 1 if you have the `gethostname' function. */ +#define HAVE_GETHOSTNAME 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the `inet_ntoa' function. */ +#define HAVE_INET_NTOA 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `cygipc' library (-lcygipc). */ +/* #undef HAVE_LIBCYGIPC */ + +/* Define to 1 if you have the `jpeg' library (-ljpeg). */ +#define HAVE_LIBJPEG 1 + +/* Define to 1 if you have the `nsl' library (-lnsl). */ +#define HAVE_LIBNSL 1 + +/* Define to 1 if you have the `pthread' library (-lpthread). */ +#define HAVE_LIBPTHREAD 1 + +/* Define to 1 if you have the `socket' library (-lsocket). */ +/* #undef HAVE_LIBSOCKET */ + +/* Define to 1 if you have the `z' library (-lz). */ +#define HAVE_LIBZ 1 + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the `memmove' function. */ +#define HAVE_MEMMOVE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have the `mkfifo' function. */ +#define HAVE_MKFIFO 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETDB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the `select' function. */ +#define HAVE_SELECT 1 + +/* Define to 1 if you have the `setsid' function. */ +#define HAVE_SETSID 1 + +/* Define to 1 if you have the `socket' function. */ +#define HAVE_SOCKET 1 + +/* Define to 1 if `stat' has the bug that it succeeds when given the + zero-length file name argument. */ +/* #undef HAVE_STAT_EMPTY_STRING_BUG */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strchr' function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the `strcspn' function. */ +#define HAVE_STRCSPN 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the `strftime' function. */ +#define HAVE_STRFTIME 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strstr' function. */ +#define HAVE_STRSTR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYSLOG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIMEB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `vfork' function. */ +#define HAVE_VFORK 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_VFORK_H */ + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if `fork' works. */ +#define HAVE_WORKING_FORK 1 + +/* Define to 1 if `vfork' works. */ +#define HAVE_WORKING_VFORK 1 + +/* XKEYBOARD extension build environment present */ +#define HAVE_XKEYBOARD 1 + +/* Define to 1 if `lstat' dereferences a symlink specified with a trailing + slash. */ +#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 + +/* Name of package */ +#define PACKAGE "LibVNCServer" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "http://sourceforge.net/projects/libvncserver" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "LibVNCServer" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "LibVNCServer 0.7pre" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libvncserver" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.7pre" + +/* The number of bytes in type char */ +/* #undef SIZEOF_CHAR */ + +/* The number of bytes in type int */ +/* #undef SIZEOF_INT */ + +/* The number of bytes in type long */ +/* #undef SIZEOF_LONG */ + +/* The number of bytes in type short */ +/* #undef SIZEOF_SHORT */ + +/* The number of bytes in type void* */ +/* #undef SIZEOF_VOIDP */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Version number of package */ +#define VERSION "0.7pre" + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +/* #undef WORDS_BIGENDIAN */ + +/* Define to 1 if the X Window System is missing or not being used. */ +/* #undef X_DISPLAY_MISSING */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to rpl_malloc if the replacement function should be used. */ +/* #undef malloc */ + +/* Define to `int' if does not define. */ +/* #undef pid_t */ + +/* Define to `unsigned' if does not define. */ +/* #undef size_t */ + +/* The type for socklen */ +/* #undef socklen_t */ + +/* Define as `fork' if `vfork' does not work. */ +/* #undef vfork */ diff --git a/libvncserver/corre.c b/libvncserver/corre.c new file mode 100755 index 0000000..3f123b0 --- /dev/null +++ b/libvncserver/corre.c @@ -0,0 +1,352 @@ +/* + * corre.c + * + * Routines to implement Compact Rise-and-Run-length Encoding (CoRRE). This + * code is based on krw's original javatel rfbserver. + */ + +/* + * Copyright (C) 2002 RealVNC Ltd. + * OSXvnc Copyright (C) 2001 Dan McGuirk . + * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. + * 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. + */ + +#include + +/* + * rreBeforeBuf contains pixel data in the client's format. + * rreAfterBuf contains the RRE encoded version. If the RRE encoded version is + * larger than the raw data or if it exceeds rreAfterBufSize then + * raw encoding is used instead. + */ + +static int rreBeforeBufSize = 0; +static char *rreBeforeBuf = NULL; + +static int rreAfterBufSize = 0; +static char *rreAfterBuf = NULL; +static int rreAfterBufLen; + +static int subrectEncode8(uint8_t *data, int w, int h); +static int subrectEncode16(uint16_t *data, int w, int h); +static int subrectEncode32(uint32_t *data, int w, int h); +static uint32_t getBgColour(char *data, int size, int bpp); +static rfbBool rfbSendSmallRectEncodingCoRRE(rfbClientPtr cl, int x, int y, + int w, int h); + + +/* + * rfbSendRectEncodingCoRRE - send an arbitrary size rectangle using CoRRE + * encoding. + */ + +rfbBool +rfbSendRectEncodingCoRRE(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + if (h > cl->correMaxHeight) { + return (rfbSendRectEncodingCoRRE(cl, x, y, w, cl->correMaxHeight) && + rfbSendRectEncodingCoRRE(cl, x, y + cl->correMaxHeight, w, + h - cl->correMaxHeight)); + } + + if (w > cl->correMaxWidth) { + return (rfbSendRectEncodingCoRRE(cl, x, y, cl->correMaxWidth, h) && + rfbSendRectEncodingCoRRE(cl, x + cl->correMaxWidth, y, + w - cl->correMaxWidth, h)); + } + + rfbSendSmallRectEncodingCoRRE(cl, x, y, w, h); + return TRUE; +} + + + +/* + * rfbSendSmallRectEncodingCoRRE - send a small (guaranteed < 256x256) + * rectangle using CoRRE encoding. + */ + +static rfbBool +rfbSendSmallRectEncodingCoRRE(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + rfbFramebufferUpdateRectHeader rect; + rfbRREHeader hdr; + int nSubrects; + int i; + char *fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y) + + (x * (cl->screen->bitsPerPixel / 8))); + + int maxRawSize = (cl->screen->width * cl->screen->height + * (cl->format.bitsPerPixel / 8)); + + if (rreBeforeBufSize < maxRawSize) { + rreBeforeBufSize = maxRawSize; + if (rreBeforeBuf == NULL) + rreBeforeBuf = (char *)malloc(rreBeforeBufSize); + else + rreBeforeBuf = (char *)realloc(rreBeforeBuf, rreBeforeBufSize); + } + + if (rreAfterBufSize < maxRawSize) { + rreAfterBufSize = maxRawSize; + if (rreAfterBuf == NULL) + rreAfterBuf = (char *)malloc(rreAfterBufSize); + else + rreAfterBuf = (char *)realloc(rreAfterBuf, rreAfterBufSize); + } + + (*cl->translateFn)(cl->translateLookupTable,&(cl->screen->rfbServerFormat), + &cl->format, fbptr, rreBeforeBuf, + cl->screen->paddedWidthInBytes, w, h); + + switch (cl->format.bitsPerPixel) { + case 8: + nSubrects = subrectEncode8((uint8_t *)rreBeforeBuf, w, h); + break; + case 16: + nSubrects = subrectEncode16((uint16_t *)rreBeforeBuf, w, h); + break; + case 32: + nSubrects = subrectEncode32((uint32_t *)rreBeforeBuf, w, h); + break; + default: + rfbLog("getBgColour: bpp %d?\n",cl->format.bitsPerPixel); + return FALSE; + } + + if (nSubrects < 0) { + + /* RRE encoding was too large, use raw */ + + return rfbSendRectEncodingRaw(cl, x, y, w, h); + } + + cl->rfbRectanglesSent[rfbEncodingCoRRE]++; + cl->rfbBytesSent[rfbEncodingCoRRE] += (sz_rfbFramebufferUpdateRectHeader + + sz_rfbRREHeader + rreAfterBufLen); + + if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader + > UPDATE_BUF_SIZE) + { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + rect.r.x = Swap16IfLE(x); + rect.r.y = Swap16IfLE(y); + rect.r.w = Swap16IfLE(w); + rect.r.h = Swap16IfLE(h); + rect.encoding = Swap32IfLE(rfbEncodingCoRRE); + + memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + cl->ublen += sz_rfbFramebufferUpdateRectHeader; + + hdr.nSubrects = Swap32IfLE(nSubrects); + + memcpy(&cl->updateBuf[cl->ublen], (char *)&hdr, sz_rfbRREHeader); + cl->ublen += sz_rfbRREHeader; + + for (i = 0; i < rreAfterBufLen;) { + + int bytesToCopy = UPDATE_BUF_SIZE - cl->ublen; + + if (i + bytesToCopy > rreAfterBufLen) { + bytesToCopy = rreAfterBufLen - i; + } + + memcpy(&cl->updateBuf[cl->ublen], &rreAfterBuf[i], bytesToCopy); + + cl->ublen += bytesToCopy; + i += bytesToCopy; + + if (cl->ublen == UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + } + + return TRUE; +} + + + +/* + * subrectEncode() encodes the given multicoloured rectangle as a background + * colour overwritten by single-coloured rectangles. It returns the number + * of subrectangles in the encoded buffer, or -1 if subrect encoding won't + * fit in the buffer. It puts the encoded rectangles in rreAfterBuf. The + * single-colour rectangle partition is not optimal, but does find the biggest + * horizontal or vertical rectangle top-left anchored to each consecutive + * coordinate position. + * + * The coding scheme is simply [...] where each + * is []. + */ + +#define DEFINE_SUBRECT_ENCODE(bpp) \ +static int \ +subrectEncode##bpp(data,w,h) \ + uint##bpp##_t *data; \ + int w; \ + int h; \ +{ \ + uint##bpp##_t cl; \ + rfbCoRRERectangle subrect; \ + int x,y; \ + int i,j; \ + int hx=0,hy,vx=0,vy; \ + int hyflag; \ + uint##bpp##_t *seg; \ + uint##bpp##_t *line; \ + int hw,hh,vw,vh; \ + int thex,they,thew,theh; \ + int numsubs = 0; \ + int newLen; \ + uint##bpp##_t bg = (uint##bpp##_t)getBgColour((char*)data,w*h,bpp); \ + \ + *((uint##bpp##_t*)rreAfterBuf) = bg; \ + \ + rreAfterBufLen = (bpp/8); \ + \ + for (y=0; y 0) && (i >= hx)) {hy += 1;} else {hyflag = 0;} \ + } \ + vy = j-1; \ + \ + /* We now have two possible subrects: (x,y,hx,hy) and (x,y,vx,vy) \ + * We'll choose the bigger of the two. \ + */ \ + hw = hx-x+1; \ + hh = hy-y+1; \ + vw = vx-x+1; \ + vh = vy-y+1; \ + \ + thex = x; \ + they = y; \ + \ + if ((hw*hh) > (vw*vh)) { \ + thew = hw; \ + theh = hh; \ + } else { \ + thew = vw; \ + theh = vh; \ + } \ + \ + subrect.x = thex; \ + subrect.y = they; \ + subrect.w = thew; \ + subrect.h = theh; \ + \ + newLen = rreAfterBufLen + (bpp/8) + sz_rfbCoRRERectangle; \ + if ((newLen > (w * h * (bpp/8))) || (newLen > rreAfterBufSize)) \ + return -1; \ + \ + numsubs += 1; \ + *((uint##bpp##_t*)(rreAfterBuf + rreAfterBufLen)) = cl; \ + rreAfterBufLen += (bpp/8); \ + memcpy(&rreAfterBuf[rreAfterBufLen],&subrect,sz_rfbCoRRERectangle); \ + rreAfterBufLen += sz_rfbCoRRERectangle; \ + \ + /* \ + * Now mark the subrect as done. \ + */ \ + for (j=they; j < (they+theh); j++) { \ + for (i=thex; i < (thex+thew); i++) { \ + data[j*w+i] = bg; \ + } \ + } \ + } \ + } \ + } \ + \ + return numsubs; \ +} + +DEFINE_SUBRECT_ENCODE(8) +DEFINE_SUBRECT_ENCODE(16) +DEFINE_SUBRECT_ENCODE(32) + + +/* + * getBgColour() gets the most prevalent colour in a byte array. + */ +static uint32_t +getBgColour(data,size,bpp) + char *data; + int size; + int bpp; +{ + +#define NUMCLRS 256 + + static int counts[NUMCLRS]; + int i,j,k; + + int maxcount = 0; + uint8_t maxclr = 0; + + if (bpp != 8) { + if (bpp == 16) { + return ((uint16_t *)data)[0]; + } else if (bpp == 32) { + return ((uint32_t *)data)[0]; + } else { + rfbLog("getBgColour: bpp %d?\n",bpp); + return 0; + } + } + + for (i=0; i= NUMCLRS) { + rfbLog("getBgColour: unusual colour = %d\n", k); + return 0; + } + counts[k] += 1; + if (counts[k] > maxcount) { + maxcount = counts[k]; + maxclr = ((uint8_t *)data)[j]; + } + } + + return maxclr; +} diff --git a/libvncserver/cursor.c b/libvncserver/cursor.c new file mode 100644 index 0000000..4f290da --- /dev/null +++ b/libvncserver/cursor.c @@ -0,0 +1,527 @@ +/* + * cursor.c - support for cursor shape updates. + */ + +/* + * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. 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. + */ + +#include + +/* + * Send cursor shape either in X-style format or in client pixel format. + */ + +rfbBool +rfbSendCursorShape(cl) + rfbClientPtr cl; +{ + rfbCursorPtr pCursor; + rfbFramebufferUpdateRectHeader rect; + rfbXCursorColors colors; + int saved_ublen; + int bitmapRowBytes, maskBytes, dataBytes; + int i, j; + uint8_t *bitmapData; + uint8_t bitmapByte; + + pCursor = cl->screen->getCursorPtr(cl); + /*if(!pCursor) return TRUE;*/ + + if (cl->useRichCursorEncoding) { + if(pCursor && !pCursor->richSource) + MakeRichCursorFromXCursor(cl->screen,pCursor); + rect.encoding = Swap32IfLE(rfbEncodingRichCursor); + } else { + if(pCursor && !pCursor->source) + MakeXCursorFromRichCursor(cl->screen,pCursor); + rect.encoding = Swap32IfLE(rfbEncodingXCursor); + } + + /* If there is no cursor, send update with empty cursor data. */ + + if ( pCursor && pCursor->width == 1 && + pCursor->height == 1 && + pCursor->mask[0] == 0 ) { + pCursor = NULL; + } + + if (pCursor == NULL) { + if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE ) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + rect.r.x = rect.r.y = 0; + rect.r.w = rect.r.h = 0; + memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + cl->ublen += sz_rfbFramebufferUpdateRectHeader; + + cl->rfbCursorShapeBytesSent += sz_rfbFramebufferUpdateRectHeader; + cl->rfbCursorShapeUpdatesSent++; + + if (!rfbSendUpdateBuf(cl)) + return FALSE; + + return TRUE; + } + + /* Calculate data sizes. */ + + bitmapRowBytes = (pCursor->width + 7) / 8; + maskBytes = bitmapRowBytes * pCursor->height; + dataBytes = (cl->useRichCursorEncoding) ? + (pCursor->width * pCursor->height * + (cl->format.bitsPerPixel / 8)) : maskBytes; + + /* Send buffer contents if needed. */ + + if ( cl->ublen + sz_rfbFramebufferUpdateRectHeader + + sz_rfbXCursorColors + maskBytes + dataBytes > UPDATE_BUF_SIZE ) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + if ( cl->ublen + sz_rfbFramebufferUpdateRectHeader + + sz_rfbXCursorColors + maskBytes + dataBytes > UPDATE_BUF_SIZE ) { + return FALSE; /* FIXME. */ + } + + saved_ublen = cl->ublen; + + /* Prepare rectangle header. */ + + rect.r.x = Swap16IfLE(pCursor->xhot); + rect.r.y = Swap16IfLE(pCursor->yhot); + rect.r.w = Swap16IfLE(pCursor->width); + rect.r.h = Swap16IfLE(pCursor->height); + + memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader); + cl->ublen += sz_rfbFramebufferUpdateRectHeader; + + /* Prepare actual cursor data (depends on encoding used). */ + + if (!cl->useRichCursorEncoding) { + /* XCursor encoding. */ + colors.foreRed = (char)(pCursor->foreRed >> 8); + colors.foreGreen = (char)(pCursor->foreGreen >> 8); + colors.foreBlue = (char)(pCursor->foreBlue >> 8); + colors.backRed = (char)(pCursor->backRed >> 8); + colors.backGreen = (char)(pCursor->backGreen >> 8); + colors.backBlue = (char)(pCursor->backBlue >> 8); + + memcpy(&cl->updateBuf[cl->ublen], (char *)&colors, sz_rfbXCursorColors); + cl->ublen += sz_rfbXCursorColors; + + bitmapData = (uint8_t *)pCursor->source; + + for (i = 0; i < pCursor->height; i++) { + for (j = 0; j < bitmapRowBytes; j++) { + bitmapByte = bitmapData[i * bitmapRowBytes + j]; + cl->updateBuf[cl->ublen++] = (char)bitmapByte; + } + } + } else { + /* RichCursor encoding. */ + int bpp1=cl->screen->rfbServerFormat.bitsPerPixel/8, + bpp2=cl->format.bitsPerPixel/8; + (*cl->translateFn)(cl->translateLookupTable, + &(cl->screen->rfbServerFormat), + &cl->format, (char*)pCursor->richSource, + &cl->updateBuf[cl->ublen], + pCursor->width*bpp1, pCursor->width, pCursor->height); + + cl->ublen += pCursor->width*bpp2*pCursor->height; + } + + /* Prepare transparency mask. */ + + bitmapData = (uint8_t *)pCursor->mask; + + for (i = 0; i < pCursor->height; i++) { + for (j = 0; j < bitmapRowBytes; j++) { + bitmapByte = bitmapData[i * bitmapRowBytes + j]; + cl->updateBuf[cl->ublen++] = (char)bitmapByte; + } + } + + /* Send everything we have prepared in the cl->updateBuf[]. */ + + cl->rfbCursorShapeBytesSent += (cl->ublen - saved_ublen); + cl->rfbCursorShapeUpdatesSent++; + + if (!rfbSendUpdateBuf(cl)) + return FALSE; + + return TRUE; +} + +/* + * Send cursor position (PointerPos pseudo-encoding). + */ + +rfbBool +rfbSendCursorPos(rfbClientPtr cl) +{ + rfbFramebufferUpdateRectHeader rect; + + if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + rect.encoding = Swap32IfLE(rfbEncodingPointerPos); + rect.r.x = Swap16IfLE(cl->screen->cursorX); + rect.r.y = Swap16IfLE(cl->screen->cursorY); + rect.r.w = 0; + rect.r.h = 0; + + memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + cl->ublen += sz_rfbFramebufferUpdateRectHeader; + + cl->rfbCursorPosBytesSent += sz_rfbFramebufferUpdateRectHeader; + cl->rfbCursorPosUpdatesSent++; + + if (!rfbSendUpdateBuf(cl)) + return FALSE; + + return TRUE; +} + +/* conversion routine for predefined cursors in LSB order */ +unsigned char rfbReverseByte[0x100] = { + /* copied from Xvnc/lib/font/util/utilbitmap.c */ + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff +}; + +void rfbConvertLSBCursorBitmapOrMask(int width,int height,unsigned char* bitmap) +{ + int i,t=(width+7)/8*height; + for(i=0;icleanup=TRUE; + cursor->width=width; + cursor->height=height; + /*cursor->backRed=cursor->backGreen=cursor->backBlue=0xffff;*/ + cursor->foreRed=cursor->foreGreen=cursor->foreBlue=0xffff; + + cursor->source = (unsigned char*)calloc(w,height); + cursor->cleanupSource = TRUE; + for(j=0,cp=cursorString;j>1,cp++) + if(*cp!=' ') cursor->source[j*w+i/8]|=bit; + + if(maskString) { + cursor->mask = (unsigned char*)calloc(w,height); + for(j=0,cp=maskString;j>1,cp++) + if(*cp!=' ') cursor->mask[j*w+i/8]|=bit; + } else + cursor->mask = (unsigned char*)rfbMakeMaskForXCursor(width,height,(char*)cursor->source); + cursor->cleanupMask = TRUE; + + return(cursor); +} + +char* rfbMakeMaskForXCursor(int width,int height,char* source) +{ + int i,j,w=(width+7)/8; + char* mask=(char*)calloc(w,height); + unsigned char c; + + for(j=0;j=0;i--) { + c=source[j*w+i]; + if(j>0) c|=source[(j-1)*w+i]; + if(j0 && (c&0x80)) + mask[j*w+i-1]|=0x01; + if(i>1); + } + + return(mask); +} + +void rfbFreeCursor(rfbCursorPtr cursor) +{ + if(cursor) { + if(cursor->cleanupRichSource && cursor->richSource) + free(cursor->richSource); + if(cursor->cleanupSource && cursor->source) + free(cursor->source); + if(cursor->cleanupMask && cursor->mask) + free(cursor->mask); + if(cursor->cleanup) + free(cursor); + else { + cursor->cleanup=cursor->cleanupSource=cursor->cleanupMask + =cursor->cleanupRichSource=FALSE; + cursor->source=cursor->mask=cursor->richSource=0; + } + } + +} + +/* background and foregroud colour have to be set beforehand */ +void MakeXCursorFromRichCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr cursor) +{ + rfbPixelFormat* format=&rfbScreen->rfbServerFormat; + int i,j,w=(cursor->width+7)/8,bpp=format->bitsPerPixel/8, + width=cursor->width*bpp; + uint32_t background; + char *back=(char*)&background; + unsigned char bit; + + if(cursor->source && cursor->cleanupSource) + free(cursor->source); + cursor->source=(unsigned char*)calloc(w,cursor->height); + cursor->cleanupSource=TRUE; + + if(format->bigEndian) + back+=4-bpp; + + background=cursor->backRed<redShift| + cursor->backGreen<greenShift|cursor->backBlue<blueShift; + + for(j=0;jheight;j++) + for(i=0,bit=0x80;iwidth;i++,bit=(bit&1)?0x80:bit>>1) + if(memcmp(cursor->richSource+j*width+i*bpp,back,bpp)) + cursor->source[j*w+i/8]|=bit; +} + +void MakeRichCursorFromXCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr cursor) +{ + rfbPixelFormat* format=&rfbScreen->rfbServerFormat; + int i,j,w=(cursor->width+7)/8,bpp=format->bitsPerPixel/8; + uint32_t background,foreground; + char *back=(char*)&background,*fore=(char*)&foreground; + unsigned char *cp; + unsigned char bit; + + if(cursor->richSource && cursor->cleanupRichSource) + free(cursor->richSource); + cp=cursor->richSource=(unsigned char*)calloc(cursor->width*bpp,cursor->height); + cursor->cleanupRichSource=TRUE; + + if(format->bigEndian) { + back+=4-bpp; + fore+=4-bpp; + } + + background=cursor->backRed<redShift| + cursor->backGreen<greenShift|cursor->backBlue<blueShift; + foreground=cursor->foreRed<redShift| + cursor->foreGreen<greenShift|cursor->foreBlue<blueShift; + + for(j=0;jheight;j++) + for(i=0,bit=0x80;iheight;i++,bit=(bit&1)?0x80:bit>>1,cp+=bpp) + if(cursor->source[j*w+i/8]&bit) memcpy(cp,fore,bpp); + else memcpy(cp,back,bpp); +} + +/* functions to draw/hide cursor directly in the frame buffer */ + +void rfbUndrawCursor(rfbScreenInfoPtr s) +{ + rfbCursorPtr c=s->cursor; + int j,x1,x2,y1,y2,bpp=s->rfbServerFormat.bitsPerPixel/8, + rowstride=s->paddedWidthInBytes; + LOCK(s->cursorMutex); + if(!s->cursorIsDrawn) { + UNLOCK(s->cursorMutex); + return; + } + + /* restore what is under the cursor */ + x1=s->cursorX-c->xhot; + x2=x1+c->width; + if(x1<0) x1=0; + if(x2>=s->width) x2=s->width-1; + x2-=x1; if(x2<=0) { + UNLOCK(s->cursorMutex); + return; + } + y1=s->cursorY-c->yhot; + y2=y1+c->height; + if(y1<0) y1=0; + if(y2>=s->height) y2=s->height-1; + y2-=y1; if(y2<=0) { + UNLOCK(s->cursorMutex); + return; + } + + /* get saved data */ + for(j=0;jframeBuffer+(y1+j)*rowstride+x1*bpp, + s->underCursorBuffer+j*x2*bpp, + x2*bpp); + + /* rfbMarkRectAsModified(s,x1,y1,x1+x2,y1+y2); */ + s->cursorIsDrawn = FALSE; + UNLOCK(s->cursorMutex); +} + +void rfbDrawCursor(rfbScreenInfoPtr s) +{ + rfbCursorPtr c=s->cursor; + int i,j,x1,x2,y1,y2,i1,j1,bpp=s->rfbServerFormat.bitsPerPixel/8, + rowstride=s->paddedWidthInBytes, + bufSize,w; + rfbBool wasChanged=FALSE; + + if(!c) return; + LOCK(s->cursorMutex); + if(s->cursorIsDrawn) { + /* is already drawn */ + UNLOCK(s->cursorMutex); + return; + } + bufSize=c->width*c->height*bpp; + w=(c->width+7)/8; + if(s->underCursorBufferLenunderCursorBuffer!=NULL) + free(s->underCursorBuffer); + s->underCursorBuffer=malloc(bufSize); + s->underCursorBufferLen=bufSize; + } + /* save what is under the cursor */ + i1=j1=0; /* offset in cursor */ + x1=s->cursorX-c->xhot; + x2=x1+c->width; + if(x1<0) { i1=-x1; x1=0; } + if(x2>=s->width) x2=s->width-1; + x2-=x1; if(x2<=0) { + UNLOCK(s->cursorMutex); + return; /* nothing to do */ + } + y1=s->cursorY-c->yhot; + y2=y1+c->height; + if(y1<0) { j1=-y1; y1=0; } + if(y2>=s->height) y2=s->height-1; + y2-=y1; if(y2<=0) { + UNLOCK(s->cursorMutex); + return; /* nothing to do */ + } + + /* save data */ + for(j=0;junderCursorBuffer+j*x2*bpp; + const char* src=s->frameBuffer+(y1+j)*rowstride+x1*bpp; + unsigned int count=x2*bpp; + if(wasChanged || memcmp(dest,src,count)) { + wasChanged=TRUE; + memcpy(dest,src,count); + } + } + + if(!c->richSource) + MakeRichCursorFromXCursor(s,c); + + /* now the cursor has to be drawn */ + for(j=0;jmask[(j+j1)*w+(i+i1)/8]<<((i+i1)&7))&0x80) + memcpy(s->frameBuffer+(j+y1)*rowstride+(i+x1)*bpp, + c->richSource+(j+j1)*c->width*bpp+(i+i1)*bpp,bpp); + + if(wasChanged) + rfbMarkRectAsModified(s,x1,y1,x1+x2,y1+y2); + s->cursorIsDrawn = TRUE; + UNLOCK(s->cursorMutex); +} + +/* for debugging */ + +void rfbPrintXCursor(rfbCursorPtr cursor) +{ + int i,i1,j,w=(cursor->width+7)/8; + unsigned char bit; + for(j=0;jheight;j++) { + for(i=0,i1=0,bit=0x80;i1width;i1++,i+=(bit&1)?1:0,bit=(bit&1)?0x80:bit>>1) + if(cursor->source[j*w+i]&bit) putchar('#'); else putchar(' '); + putchar(':'); + for(i=0,i1=0,bit=0x80;i1width;i1++,i+=(bit&1)?1:0,bit=(bit&1)?0x80:bit>>1) + if(cursor->mask[j*w+i]&bit) putchar('#'); else putchar(' '); + putchar('\n'); + } +} + +void rfbSetCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr c,rfbBool freeOld) +{ + LOCK(rfbScreen->cursorMutex); + while(rfbScreen->cursorIsDrawn) { + UNLOCK(rfbScreen->cursorMutex); + rfbUndrawCursor(rfbScreen); + LOCK(rfbScreen->cursorMutex); + } + + if(rfbScreen->cursor && (freeOld || rfbScreen->cursor->cleanup)) + rfbFreeCursor(rfbScreen->cursor); + + rfbScreen->cursor = c; + + UNLOCK(rfbScreen->cursorMutex); +} diff --git a/libvncserver/cutpaste.c b/libvncserver/cutpaste.c new file mode 100755 index 0000000..6a9dcb9 --- /dev/null +++ b/libvncserver/cutpaste.c @@ -0,0 +1,38 @@ +/* + * cutpaste.c - routines to deal with cut & paste buffers / selection. + */ + +/* + * OSXvnc Copyright (C) 2001 Dan McGuirk . + * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. + * 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. + */ + +#include + + +/* + * rfbSetXCutText sets the cut buffer to be the given string. We also clear + * the primary selection. Ideally we'd like to set it to the same thing, but I + * can't work out how to do that without some kind of helper X client. + */ + +void rfbGotXCutText(rfbScreenInfoPtr rfbScreen, char *str, int len) +{ + rfbSendServerCutText(rfbScreen, str, len); +} diff --git a/libvncserver/d3des.c b/libvncserver/d3des.c new file mode 100755 index 0000000..4994afb --- /dev/null +++ b/libvncserver/d3des.c @@ -0,0 +1,442 @@ +/* + * This is D3DES (V5.09) by Richard Outerbridge with the double and + * triple-length support removed for use in VNC. Also the bytebit[] array + * has been reversed so that the most significant bit in each byte of the + * key is ignored, not the least significant. + * + * These changes are: + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * 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. + */ + +/* D3DES (V5.09) - + * + * A portable, public domain, version of the Data Encryption Standard. + * + * Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge. + * Thanks to: Dan Hoey for his excellent Initial and Inverse permutation + * code; Jim Gillogly & Phil Karn for the DES key schedule code; Dennis + * Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau, + * for humouring me on. + * + * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge. + * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992. + */ + +#include "d3des.h" + +static void scrunch(unsigned char *, unsigned long *); +static void unscrun(unsigned long *, unsigned char *); +static void desfunc(unsigned long *, unsigned long *); +static void cookey(unsigned long *); + +static unsigned long KnL[32] = { 0L }; +/* +static unsigned long KnR[32] = { 0L }; +static unsigned long Kn3[32] = { 0L }; +static unsigned char Df_Key[24] = { + 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, + 0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10, + 0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67 }; +*/ + +static unsigned short bytebit[8] = { + 01, 02, 04, 010, 020, 040, 0100, 0200 }; + +static unsigned long bigbyte[24] = { + 0x800000L, 0x400000L, 0x200000L, 0x100000L, + 0x80000L, 0x40000L, 0x20000L, 0x10000L, + 0x8000L, 0x4000L, 0x2000L, 0x1000L, + 0x800L, 0x400L, 0x200L, 0x100L, + 0x80L, 0x40L, 0x20L, 0x10L, + 0x8L, 0x4L, 0x2L, 0x1L }; + +/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */ + +static unsigned char pc1[56] = { + 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, + 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, + 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, + 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 }; + +static unsigned char totrot[16] = { + 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 }; + +static unsigned char pc2[48] = { + 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, + 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, + 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, + 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 }; + +void deskey(key, edf) /* Thanks to James Gillogly & Phil Karn! */ +unsigned char *key; +int edf; +{ + register int i, j, l, m, n; + unsigned char pc1m[56], pcr[56]; + unsigned long kn[32]; + + for ( j = 0; j < 56; j++ ) { + l = pc1[j]; + m = l & 07; + pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0; + } + for( i = 0; i < 16; i++ ) { + if( edf == DE1 ) m = (15 - i) << 1; + else m = i << 1; + n = m + 1; + kn[m] = kn[n] = 0L; + for( j = 0; j < 28; j++ ) { + l = j + totrot[i]; + if( l < 28 ) pcr[j] = pc1m[l]; + else pcr[j] = pc1m[l - 28]; + } + for( j = 28; j < 56; j++ ) { + l = j + totrot[i]; + if( l < 56 ) pcr[j] = pc1m[l]; + else pcr[j] = pc1m[l - 28]; + } + for( j = 0; j < 24; j++ ) { + if( pcr[pc2[j]] ) kn[m] |= bigbyte[j]; + if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j]; + } + } + cookey(kn); + return; + } + +static void cookey(raw1) +register unsigned long *raw1; +{ + register unsigned long *cook, *raw0; + unsigned long dough[32]; + register int i; + + cook = dough; + for( i = 0; i < 16; i++, raw1++ ) { + raw0 = raw1++; + *cook = (*raw0 & 0x00fc0000L) << 6; + *cook |= (*raw0 & 0x00000fc0L) << 10; + *cook |= (*raw1 & 0x00fc0000L) >> 10; + *cook++ |= (*raw1 & 0x00000fc0L) >> 6; + *cook = (*raw0 & 0x0003f000L) << 12; + *cook |= (*raw0 & 0x0000003fL) << 16; + *cook |= (*raw1 & 0x0003f000L) >> 4; + *cook++ |= (*raw1 & 0x0000003fL); + } + usekey(dough); + return; + } + +void cpkey(into) +register unsigned long *into; +{ + register unsigned long *from, *endp; + + from = KnL, endp = &KnL[32]; + while( from < endp ) *into++ = *from++; + return; + } + +void usekey(from) +register unsigned long *from; +{ + register unsigned long *to, *endp; + + to = KnL, endp = &KnL[32]; + while( to < endp ) *to++ = *from++; + return; + } + +void des(inblock, outblock) +unsigned char *inblock, *outblock; +{ + unsigned long work[2]; + + scrunch(inblock, work); + desfunc(work, KnL); + unscrun(work, outblock); + return; + } + +static void scrunch(outof, into) +register unsigned char *outof; +register unsigned long *into; +{ + *into = (*outof++ & 0xffL) << 24; + *into |= (*outof++ & 0xffL) << 16; + *into |= (*outof++ & 0xffL) << 8; + *into++ |= (*outof++ & 0xffL); + *into = (*outof++ & 0xffL) << 24; + *into |= (*outof++ & 0xffL) << 16; + *into |= (*outof++ & 0xffL) << 8; + *into |= (*outof & 0xffL); + return; + } + +static void unscrun(outof, into) +register unsigned long *outof; +register unsigned char *into; +{ + *into++ = (unsigned char)((*outof >> 24) & 0xffL); + *into++ = (unsigned char)((*outof >> 16) & 0xffL); + *into++ = (unsigned char)((*outof >> 8) & 0xffL); + *into++ = (unsigned char)( *outof++ & 0xffL); + *into++ = (unsigned char)((*outof >> 24) & 0xffL); + *into++ = (unsigned char)((*outof >> 16) & 0xffL); + *into++ = (unsigned char)((*outof >> 8) & 0xffL); + *into = (unsigned char)( *outof & 0xffL); + return; + } + +static unsigned long SP1[64] = { + 0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L, + 0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L, + 0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L, + 0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L, + 0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L, + 0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L, + 0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L, + 0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L, + 0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L, + 0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L, + 0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L, + 0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L, + 0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L, + 0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L, + 0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L, + 0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L }; + +static unsigned long SP2[64] = { + 0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L, + 0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L, + 0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L, + 0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L, + 0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L, + 0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L, + 0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L, + 0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L, + 0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L, + 0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L, + 0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L, + 0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L, + 0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L, + 0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L, + 0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L, + 0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L }; + +static unsigned long SP3[64] = { + 0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L, + 0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L, + 0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L, + 0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L, + 0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L, + 0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L, + 0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L, + 0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L, + 0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L, + 0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L, + 0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L, + 0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L, + 0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L, + 0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L, + 0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L, + 0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L }; + +static unsigned long SP4[64] = { + 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, + 0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L, + 0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L, + 0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L, + 0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L, + 0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L, + 0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L, + 0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L, + 0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L, + 0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L, + 0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L, + 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, + 0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L, + 0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L, + 0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L, + 0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L }; + +static unsigned long SP5[64] = { + 0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L, + 0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L, + 0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L, + 0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L, + 0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L, + 0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L, + 0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L, + 0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L, + 0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L, + 0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L, + 0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L, + 0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L, + 0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L, + 0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L, + 0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L, + 0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L }; + +static unsigned long SP6[64] = { + 0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L, + 0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L, + 0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L, + 0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L, + 0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L, + 0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L, + 0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L, + 0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L, + 0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L, + 0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L, + 0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L, + 0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L, + 0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L, + 0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L, + 0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L, + 0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L }; + +static unsigned long SP7[64] = { + 0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L, + 0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L, + 0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L, + 0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L, + 0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L, + 0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L, + 0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L, + 0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L, + 0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L, + 0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L, + 0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L, + 0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L, + 0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L, + 0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L, + 0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L, + 0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L }; + +static unsigned long SP8[64] = { + 0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L, + 0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L, + 0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L, + 0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L, + 0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L, + 0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L, + 0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L, + 0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L, + 0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L, + 0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L, + 0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L, + 0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L, + 0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L, + 0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L, + 0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L, + 0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L }; + +static void desfunc(block, keys) +register unsigned long *block, *keys; +{ + register unsigned long fval, work, right, leftt; + register int round; + + leftt = block[0]; + right = block[1]; + work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL; + right ^= work; + leftt ^= (work << 4); + work = ((leftt >> 16) ^ right) & 0x0000ffffL; + right ^= work; + leftt ^= (work << 16); + work = ((right >> 2) ^ leftt) & 0x33333333L; + leftt ^= work; + right ^= (work << 2); + work = ((right >> 8) ^ leftt) & 0x00ff00ffL; + leftt ^= work; + right ^= (work << 8); + right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL; + work = (leftt ^ right) & 0xaaaaaaaaL; + leftt ^= work; + right ^= work; + leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL; + + for( round = 0; round < 8; round++ ) { + work = (right << 28) | (right >> 4); + work ^= *keys++; + fval = SP7[ work & 0x3fL]; + fval |= SP5[(work >> 8) & 0x3fL]; + fval |= SP3[(work >> 16) & 0x3fL]; + fval |= SP1[(work >> 24) & 0x3fL]; + work = right ^ *keys++; + fval |= SP8[ work & 0x3fL]; + fval |= SP6[(work >> 8) & 0x3fL]; + fval |= SP4[(work >> 16) & 0x3fL]; + fval |= SP2[(work >> 24) & 0x3fL]; + leftt ^= fval; + work = (leftt << 28) | (leftt >> 4); + work ^= *keys++; + fval = SP7[ work & 0x3fL]; + fval |= SP5[(work >> 8) & 0x3fL]; + fval |= SP3[(work >> 16) & 0x3fL]; + fval |= SP1[(work >> 24) & 0x3fL]; + work = leftt ^ *keys++; + fval |= SP8[ work & 0x3fL]; + fval |= SP6[(work >> 8) & 0x3fL]; + fval |= SP4[(work >> 16) & 0x3fL]; + fval |= SP2[(work >> 24) & 0x3fL]; + right ^= fval; + } + + right = (right << 31) | (right >> 1); + work = (leftt ^ right) & 0xaaaaaaaaL; + leftt ^= work; + right ^= work; + leftt = (leftt << 31) | (leftt >> 1); + work = ((leftt >> 8) ^ right) & 0x00ff00ffL; + right ^= work; + leftt ^= (work << 8); + work = ((leftt >> 2) ^ right) & 0x33333333L; + right ^= work; + leftt ^= (work << 2); + work = ((right >> 16) ^ leftt) & 0x0000ffffL; + leftt ^= work; + right ^= (work << 16); + work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL; + leftt ^= work; + right ^= (work << 4); + *block++ = right; + *block = leftt; + return; + } + +/* Validation sets: + * + * Single-length key, single-length plaintext - + * Key : 0123 4567 89ab cdef + * Plain : 0123 4567 89ab cde7 + * Cipher : c957 4425 6a5e d31d + * + * Double-length key, single-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 + * Plain : 0123 4567 89ab cde7 + * Cipher : 7f1d 0a77 826b 8aff + * + * Double-length key, double-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 + * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff + * Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7 + * + * Triple-length key, single-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567 + * Plain : 0123 4567 89ab cde7 + * Cipher : de0b 7c06 ae5e 0ed5 + * + * Triple-length key, double-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567 + * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff + * Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5 + * + * d3des V5.0a rwo 9208.07 18:44 Graven Imagery + **********************************************************************/ diff --git a/libvncserver/d3des.h b/libvncserver/d3des.h new file mode 100755 index 0000000..b2f9724 --- /dev/null +++ b/libvncserver/d3des.h @@ -0,0 +1,56 @@ +#ifndef D3DES_H +#define D3DES_H + +/* + * This is D3DES (V5.09) by Richard Outerbridge with the double and + * triple-length support removed for use in VNC. + * + * These changes are: + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * 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. + */ + +/* d3des.h - + * + * Headers and defines for d3des.c + * Graven Imagery, 1992. + * + * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge + * (GEnie : OUTER; CIS : [71755,204]) + */ + +#define EN0 0 /* MODE == encrypt */ +#define DE1 1 /* MODE == decrypt */ + +extern void deskey(unsigned char *, int); +/* hexkey[8] MODE + * Sets the internal key register according to the hexadecimal + * key contained in the 8 bytes of hexkey, according to the DES, + * for encryption or decryption according to MODE. + */ + +extern void usekey(unsigned long *); +/* cookedkey[32] + * Loads the internal key register with the data in cookedkey. + */ + +extern void cpkey(unsigned long *); +/* cookedkey[32] + * Copies the contents of the internal key register into the storage + * located at &cookedkey[0]. + */ + +extern void des(unsigned char *, unsigned char *); +/* from[8] to[8] + * Encrypts/Decrypts (according to the key currently loaded in the + * internal key register) one block of eight bytes at address 'from' + * into the block at address 'to'. They can be the same. + */ + +/* d3des.h V5.09 rwo 9208.04 15:06 Graven Imagery + ********************************************************************/ + +#endif diff --git a/libvncserver/draw.c b/libvncserver/draw.c new file mode 100755 index 0000000..7e1ed49 --- /dev/null +++ b/libvncserver/draw.c @@ -0,0 +1,61 @@ +#include + +void rfbFillRect(rfbScreenInfoPtr s,int x1,int y1,int x2,int y2,rfbPixel col) +{ + int rowstride = s->paddedWidthInBytes, bpp = s->bitsPerPixel>>3; + int i,j; + char* colour=(char*)&col; + + if(!rfbEndianTest) + colour += 4-bpp; + for(j=y1;jframeBuffer+j*rowstride+i*bpp,colour,bpp); + rfbMarkRectAsModified(s,x1,y1,x2,y2); +} + +#define SETPIXEL(x,y) \ + memcpy(s->frameBuffer+(y)*rowstride+(x)*bpp,colour,bpp) + +void rfbDrawPixel(rfbScreenInfoPtr s,int x,int y,rfbPixel col) +{ + int rowstride = s->paddedWidthInBytes, bpp = s->bitsPerPixel>>3; + char* colour=(char*)&col; + + if(!rfbEndianTest) + colour += 4-bpp; + SETPIXEL(x,y); + rfbMarkRectAsModified(s,x,y,x+1,y+1); +} + +void rfbDrawLine(rfbScreenInfoPtr s,int x1,int y1,int x2,int y2,rfbPixel col) +{ + int rowstride = s->paddedWidthInBytes, bpp = s->bitsPerPixel>>3; + int i; + char* colour=(char*)&col; + + if(!rfbEndianTest) + colour += 4-bpp; + +#define SWAPPOINTS { i=x1; x1=x2; x2=i; i=y1; y1=y2; y2=i; } + if(abs(x1-x2)y2) + SWAPPOINTS + for(i=y1;i<=y2;i++) + SETPIXEL(x1+(i-y1)*(x2-x1)/(y2-y1),i); + /* TODO: Maybe make this more intelligently? */ + if(x2x2) + SWAPPOINTS + else if(x1==x2) { + rfbDrawPixel(s,x1,y1,col); + return; + } + for(i=x1;i<=x2;i++) + SETPIXEL(i,y1+(i-x1)*(y2-y1)/(x2-x1)); + if(y2 + +int rfbDrawChar(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font, + int x,int y,unsigned char c,rfbPixel col) +{ + int i,j,width,height; + unsigned char* data=font->data+font->metaData[c*5]; + unsigned char d=*data; + int rowstride=rfbScreen->paddedWidthInBytes; + int bpp=rfbScreen->rfbServerFormat.bitsPerPixel/8; + char *colour=(char*)&col; + + if(!rfbEndianTest) + colour += 4-bpp; + + width=font->metaData[c*5+1]; + height=font->metaData[c*5+2]; + x+=font->metaData[c*5+3]; + y+=-font->metaData[c*5+4]-height+1; + + for(j=0;jframeBuffer+(y+j)*rowstride+(x+i)*bpp,colour,bpp); + d<<=1; + } + /* if((i&7)!=0) data++; */ + } + return(width); +} + +void rfbDrawString(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font, + int x,int y,const char* string,rfbPixel colour) +{ + while(*string) { + x+=rfbDrawChar(rfbScreen,font,x,y,*string,colour); + string++; + } +} + +/* TODO: these two functions need to be more efficient */ +int rfbDrawCharWithClip(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font, + int x,int y,unsigned char c, + int x1,int y1,int x2,int y2, + rfbPixel col,rfbPixel bcol) +{ + int i,j,width,height; + unsigned char* data=font->data+font->metaData[c*5]; + unsigned char d; + int rowstride=rfbScreen->paddedWidthInBytes; + int bpp=rfbScreen->rfbServerFormat.bitsPerPixel/8,extra_bytes=0; + char* colour=(char*)&col; + char* bcolour=(char*)&bcol; + + if(!rfbEndianTest) { + colour+=4-bpp; + bcolour+=4-bpp; + } + + width=font->metaData[c*5+1]; + height=font->metaData[c*5+2]; + x+=font->metaData[c*5+3]; + y+=-font->metaData[c*5+4]-height+1; + + /* after clipping, x2 will be count of bytes between rows, + * x1 start of i, y1 start of j, width and height will be adjusted. */ + if(y1>y) { y1-=y; data+=(width+7)/8; height-=y1; y+=y1; } else y1=0; + if(x1>x) { x1-=x; data+=x1; width-=x1; x+=x1; extra_bytes+=x1/8; } else x1=0; + if(y2=x1 && x+i=y1 && y+jframeBuffer+(y+j)*rowstride+(x+i)*bpp, + colour,bpp); + } else if(bcol!=col) { + memcpy(rfbScreen->frameBuffer+(y+j)*rowstride+(x+i)*bpp, + bcolour,bpp); + } + } + d<<=1; + } + /* if((i&7)==0) data++; */ + data += extra_bytes; + } + return(width); +} + +void rfbDrawStringWithClip(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font, + int x,int y,const char* string, + int x1,int y1,int x2,int y2, + rfbPixel colour,rfbPixel backColour) +{ + while(*string) { + x+=rfbDrawCharWithClip(rfbScreen,font,x,y,*string,x1,y1,x2,y2, + colour,backColour); + string++; + } +} + +int rfbWidthOfString(rfbFontDataPtr font,const char* string) +{ + int i=0; + while(*string) { + i+=font->metaData[*string*5+1]; + string++; + } + return(i); +} + +int rfbWidthOfChar(rfbFontDataPtr font,unsigned char c) +{ + return(font->metaData[c*5+1]+font->metaData[c*5+3]); +} + +void rfbFontBBox(rfbFontDataPtr font,unsigned char c,int* x1,int* y1,int* x2,int* y2) +{ + *x1+=font->metaData[c*5+3]; + *y1+=-font->metaData[c*5+4]-font->metaData[c*5+2]+1; + *x2=*x1+font->metaData[c*5+1]; + *y2=*y1+font->metaData[c*5+2]; +} + +#ifndef INT_MAX +#define INT_MAX 0x7fffffff +#endif + +void rfbWholeFontBBox(rfbFontDataPtr font, + int *x1, int *y1, int *x2, int *y2) +{ + int i; + int* m=font->metaData; + + (*x1)=(*y1)=INT_MAX; (*x2)=(*y2)=-INT_MAX+1; + for(i=0;i<256;i++) { + if(m[i*5+1]-m[i*5+3]>(*x2)) + (*x2)=m[i*5+1]-m[i*5+3]; + if(-m[i*5+2]+m[i*5+4]<(*y1)) + (*y1)=-m[i*5+2]+m[i*5+4]; + if(m[i*5+3]<(*x1)) + (*x1)=m[i*5+3]; + if(-m[i*5+4]>(*y2)) + (*y2)=-m[i*5+4]; + } +} + +rfbFontDataPtr rfbLoadConsoleFont(char *filename) +{ + FILE *f=fopen(filename,"rb"); + rfbFontDataPtr p; + int i; + + if(!f) return(0); + + p=(rfbFontDataPtr)malloc(sizeof(rfbFontData)); + p->data=(unsigned char*)malloc(4096); + if(1!=fread(p->data,4096,1,f)) { + free(p->data); + free(p); + return(0); + } + fclose(f); + p->metaData=(int*)malloc(256*5*sizeof(int)); + for(i=0;i<256;i++) { + p->metaData[i*5+0]=i*16; /* offset */ + p->metaData[i*5+1]=8; /* width */ + p->metaData[i*5+2]=16; /* height */ + p->metaData[i*5+3]=0; /* xhot */ + p->metaData[i*5+4]=0; /* yhot */ + } + return(p); +} + +void rfbFreeFont(rfbFontDataPtr f) +{ + free(f->data); + free(f->metaData); + free(f); +} diff --git a/libvncserver/hextile.c b/libvncserver/hextile.c new file mode 100755 index 0000000..e13021b --- /dev/null +++ b/libvncserver/hextile.c @@ -0,0 +1,346 @@ +/* + * hextile.c + * + * Routines to implement Hextile Encoding + */ + +/* + * OSXvnc Copyright (C) 2001 Dan McGuirk . + * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. + * 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. + */ + +#include + +static rfbBool sendHextiles8(rfbClientPtr cl, int x, int y, int w, int h); +static rfbBool sendHextiles16(rfbClientPtr cl, int x, int y, int w, int h); +static rfbBool sendHextiles32(rfbClientPtr cl, int x, int y, int w, int h); + + +/* + * rfbSendRectEncodingHextile - send a rectangle using hextile encoding. + */ + +rfbBool +rfbSendRectEncodingHextile(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + rfbFramebufferUpdateRectHeader rect; + + if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + rect.r.x = Swap16IfLE(x); + rect.r.y = Swap16IfLE(y); + rect.r.w = Swap16IfLE(w); + rect.r.h = Swap16IfLE(h); + rect.encoding = Swap32IfLE(rfbEncodingHextile); + + memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + cl->ublen += sz_rfbFramebufferUpdateRectHeader; + + cl->rfbRectanglesSent[rfbEncodingHextile]++; + cl->rfbBytesSent[rfbEncodingHextile] += sz_rfbFramebufferUpdateRectHeader; + + switch (cl->format.bitsPerPixel) { + case 8: + return sendHextiles8(cl, x, y, w, h); + case 16: + return sendHextiles16(cl, x, y, w, h); + case 32: + return sendHextiles32(cl, x, y, w, h); + } + + rfbLog("rfbSendRectEncodingHextile: bpp %d?\n", cl->format.bitsPerPixel); + return FALSE; +} + + +#define PUT_PIXEL8(pix) (cl->updateBuf[cl->ublen++] = (pix)) + +#define PUT_PIXEL16(pix) (cl->updateBuf[cl->ublen++] = ((char*)&(pix))[0], \ + cl->updateBuf[cl->ublen++] = ((char*)&(pix))[1]) + +#define PUT_PIXEL32(pix) (cl->updateBuf[cl->ublen++] = ((char*)&(pix))[0], \ + cl->updateBuf[cl->ublen++] = ((char*)&(pix))[1], \ + cl->updateBuf[cl->ublen++] = ((char*)&(pix))[2], \ + cl->updateBuf[cl->ublen++] = ((char*)&(pix))[3]) + + +#define DEFINE_SEND_HEXTILES(bpp) \ + \ + \ +static rfbBool subrectEncode##bpp(rfbClientPtr cli, uint##bpp##_t *data, int w, int h, \ + uint##bpp##_t bg, uint##bpp##_t fg, rfbBool mono); \ +static void testColours##bpp(uint##bpp##_t *data, int size, rfbBool *mono, \ + rfbBool *solid, uint##bpp##_t *bg, uint##bpp##_t *fg); \ + \ + \ +/* \ + * rfbSendHextiles \ + */ \ + \ +static rfbBool \ +sendHextiles##bpp(cl, rx, ry, rw, rh) \ + rfbClientPtr cl; \ + int rx, ry, rw, rh; \ +{ \ + int x, y, w, h; \ + int startUblen; \ + char *fbptr; \ + uint##bpp##_t bg = 0, fg = 0, newBg, newFg; \ + rfbBool mono, solid; \ + rfbBool validBg = FALSE; \ + rfbBool validFg = FALSE; \ + uint##bpp##_t clientPixelData[16*16*(bpp/8)]; \ + \ + for (y = ry; y < ry+rh; y += 16) { \ + for (x = rx; x < rx+rw; x += 16) { \ + w = h = 16; \ + if (rx+rw - x < 16) \ + w = rx+rw - x; \ + if (ry+rh - y < 16) \ + h = ry+rh - y; \ + \ + if ((cl->ublen + 1 + (2 + 16 * 16) * (bpp/8)) > \ + UPDATE_BUF_SIZE) { \ + if (!rfbSendUpdateBuf(cl)) \ + return FALSE; \ + } \ + \ + fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y) \ + + (x * (cl->screen->bitsPerPixel / 8))); \ + \ + (*cl->translateFn)(cl->translateLookupTable, &(cl->screen->rfbServerFormat), \ + &cl->format, fbptr, (char *)clientPixelData, \ + cl->screen->paddedWidthInBytes, w, h); \ + \ + startUblen = cl->ublen; \ + cl->updateBuf[startUblen] = 0; \ + cl->ublen++; \ + \ + testColours##bpp(clientPixelData, w * h, \ + &mono, &solid, &newBg, &newFg); \ + \ + if (!validBg || (newBg != bg)) { \ + validBg = TRUE; \ + bg = newBg; \ + cl->updateBuf[startUblen] |= rfbHextileBackgroundSpecified; \ + PUT_PIXEL##bpp(bg); \ + } \ + \ + if (solid) { \ + cl->rfbBytesSent[rfbEncodingHextile] += cl->ublen - startUblen; \ + continue; \ + } \ + \ + cl->updateBuf[startUblen] |= rfbHextileAnySubrects; \ + \ + if (mono) { \ + if (!validFg || (newFg != fg)) { \ + validFg = TRUE; \ + fg = newFg; \ + cl->updateBuf[startUblen] |= rfbHextileForegroundSpecified; \ + PUT_PIXEL##bpp(fg); \ + } \ + } else { \ + validFg = FALSE; \ + cl->updateBuf[startUblen] |= rfbHextileSubrectsColoured; \ + } \ + \ + if (!subrectEncode##bpp(cl, clientPixelData, w, h, bg, fg, mono)) { \ + /* encoding was too large, use raw */ \ + validBg = FALSE; \ + validFg = FALSE; \ + cl->ublen = startUblen; \ + cl->updateBuf[cl->ublen++] = rfbHextileRaw; \ + (*cl->translateFn)(cl->translateLookupTable, \ + &(cl->screen->rfbServerFormat), &cl->format, fbptr, \ + (char *)clientPixelData, \ + cl->screen->paddedWidthInBytes, w, h); \ + \ + memcpy(&cl->updateBuf[cl->ublen], (char *)clientPixelData, \ + w * h * (bpp/8)); \ + \ + cl->ublen += w * h * (bpp/8); \ + } \ + \ + cl->rfbBytesSent[rfbEncodingHextile] += cl->ublen - startUblen; \ + } \ + } \ + \ + return TRUE; \ +} \ + \ + \ +static rfbBool \ +subrectEncode##bpp(rfbClientPtr cl, uint##bpp##_t *data, int w, int h, \ + uint##bpp##_t bg, uint##bpp##_t fg, rfbBool mono) \ +{ \ + uint##bpp##_t cl2; \ + int x,y; \ + int i,j; \ + int hx=0,hy,vx=0,vy; \ + int hyflag; \ + uint##bpp##_t *seg; \ + uint##bpp##_t *line; \ + int hw,hh,vw,vh; \ + int thex,they,thew,theh; \ + int numsubs = 0; \ + int newLen; \ + int nSubrectsUblen; \ + \ + nSubrectsUblen = cl->ublen; \ + cl->ublen++; \ + \ + for (y=0; y 0) && (i >= hx)) { \ + hy += 1; \ + } else { \ + hyflag = 0; \ + } \ + } \ + vy = j-1; \ + \ + /* We now have two possible subrects: (x,y,hx,hy) and \ + * (x,y,vx,vy). We'll choose the bigger of the two. \ + */ \ + hw = hx-x+1; \ + hh = hy-y+1; \ + vw = vx-x+1; \ + vh = vy-y+1; \ + \ + thex = x; \ + they = y; \ + \ + if ((hw*hh) > (vw*vh)) { \ + thew = hw; \ + theh = hh; \ + } else { \ + thew = vw; \ + theh = vh; \ + } \ + \ + if (mono) { \ + newLen = cl->ublen - nSubrectsUblen + 2; \ + } else { \ + newLen = cl->ublen - nSubrectsUblen + bpp/8 + 2; \ + } \ + \ + if (newLen > (w * h * (bpp/8))) \ + return FALSE; \ + \ + numsubs += 1; \ + \ + if (!mono) PUT_PIXEL##bpp(cl2); \ + \ + cl->updateBuf[cl->ublen++] = rfbHextilePackXY(thex,they); \ + cl->updateBuf[cl->ublen++] = rfbHextilePackWH(thew,theh); \ + \ + /* \ + * Now mark the subrect as done. \ + */ \ + for (j=they; j < (they+theh); j++) { \ + for (i=thex; i < (thex+thew); i++) { \ + data[j*w+i] = bg; \ + } \ + } \ + } \ + } \ + } \ + \ + cl->updateBuf[nSubrectsUblen] = numsubs; \ + \ + return TRUE; \ +} \ + \ + \ +/* \ + * testColours() tests if there are one (solid), two (mono) or more \ + * colours in a tile and gets a reasonable guess at the best background \ + * pixel, and the foreground pixel for mono. \ + */ \ + \ +static void \ +testColours##bpp(data,size,mono,solid,bg,fg) \ + uint##bpp##_t *data; \ + int size; \ + rfbBool *mono; \ + rfbBool *solid; \ + uint##bpp##_t *bg; \ + uint##bpp##_t *fg; \ +{ \ + uint##bpp##_t colour1 = 0, colour2 = 0; \ + int n1 = 0, n2 = 0; \ + *mono = TRUE; \ + *solid = TRUE; \ + \ + for (; size > 0; size--, data++) { \ + \ + if (n1 == 0) \ + colour1 = *data; \ + \ + if (*data == colour1) { \ + n1++; \ + continue; \ + } \ + \ + if (n2 == 0) { \ + *solid = FALSE; \ + colour2 = *data; \ + } \ + \ + if (*data == colour2) { \ + n2++; \ + continue; \ + } \ + \ + *mono = FALSE; \ + break; \ + } \ + \ + if (n1 > n2) { \ + *bg = colour1; \ + *fg = colour2; \ + } else { \ + *bg = colour2; \ + *fg = colour1; \ + } \ +} + +DEFINE_SEND_HEXTILES(8) +DEFINE_SEND_HEXTILES(16) +DEFINE_SEND_HEXTILES(32) diff --git a/libvncserver/httpd.c b/libvncserver/httpd.c new file mode 100755 index 0000000..85c8e44 --- /dev/null +++ b/libvncserver/httpd.c @@ -0,0 +1,579 @@ +/* + * httpd.c - a simple HTTP server + */ + +/* + * Copyright (C) 2002 RealVNC Ltd. + * Copyright (C) 1999 AT&T Laboratories Cambridge. 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. + */ + +#include + +#include +#ifdef LIBVNCSERVER_HAVE_UNISTD_H +#include +#endif +#ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H +#include +#endif +#ifdef LIBVNCSERVER_HAVE_FCNTL_H +#include +#endif +#include + +#ifdef WIN32 +#include +#define close closesocket +#else +#ifdef LIBVNCSERVER_HAVE_SYS_TIME_H +#include +#endif +#ifdef LIBVNCSERVER_HAVE_SYS_SOCKET_H +#include +#endif +#ifdef LIBVNCSERVER_HAVE_NETINET_IN_H +#include +#include +#include +#include +#endif +#include +#endif + +#ifdef USE_LIBWRAP +#include +#endif + +#define NOT_FOUND_STR "HTTP/1.0 404 Not found\r\n\r\n" \ + "File Not Found\n" \ + "

File Not Found

\n" + +#define INVALID_REQUEST_STR "HTTP/1.0 400 Invalid Request\r\n\r\n" \ + "Invalid Request\n" \ + "

Invalid request

\n" + +#define OK_STR "HTTP/1.0 200 OK\nContent-Type: text/html\r\n\r\n" + +static void httpProcessInput(); +static rfbBool compareAndSkip(char **ptr, const char *str); +static rfbBool parseParams(const char *request, char *result, int max_bytes); +static rfbBool validateString(char *str); + +#define BUF_SIZE 32768 + +static char buf[BUF_SIZE]; +static size_t buf_filled=0; + +/* + * httpInitSockets sets up the TCP socket to listen for HTTP connections. + */ + +void +httpInitSockets(rfbScreenInfoPtr rfbScreen) +{ + if (rfbScreen->httpInitDone) + return; + + rfbScreen->httpInitDone = TRUE; + + if (!rfbScreen->httpDir) + return; + + if (rfbScreen->httpPort == 0) { + rfbScreen->httpPort = rfbScreen->rfbPort-100; + } + + rfbLog("Listening for HTTP connections on TCP port %d\n", rfbScreen->httpPort); + + rfbLog(" URL http://%s:%d\n",rfbScreen->rfbThisHost,rfbScreen->httpPort); + + if ((rfbScreen->httpListenSock = ListenOnTCPPort(rfbScreen->httpPort)) < 0) { + rfbLogPerror("ListenOnTCPPort"); + return; + } + + /*AddEnabledDevice(httpListenSock);*/ +} + + +/* + * httpCheckFds is called from ProcessInputEvents to check for input on the + * HTTP socket(s). If there is input to process, httpProcessInput is called. + */ + +void +httpCheckFds(rfbScreenInfoPtr rfbScreen) +{ + int nfds; + fd_set fds; + struct timeval tv; + struct sockaddr_in addr; + size_t addrlen = sizeof(addr); + + if (!rfbScreen->httpDir) + return; + + FD_ZERO(&fds); + FD_SET(rfbScreen->httpListenSock, &fds); + if (rfbScreen->httpSock >= 0) { + FD_SET(rfbScreen->httpSock, &fds); + } + tv.tv_sec = 0; + tv.tv_usec = 0; + nfds = select(max(rfbScreen->httpSock,rfbScreen->httpListenSock) + 1, &fds, NULL, NULL, &tv); + if (nfds == 0) { + return; + } + if (nfds < 0) { +#ifdef WIN32 + errno = WSAGetLastError(); +#endif + if (errno != EINTR) + rfbLogPerror("httpCheckFds: select"); + return; + } + + if ((rfbScreen->httpSock >= 0) && FD_ISSET(rfbScreen->httpSock, &fds)) { + httpProcessInput(rfbScreen); + } + + if (FD_ISSET(rfbScreen->httpListenSock, &fds)) { + int flags; + if (rfbScreen->httpSock >= 0) close(rfbScreen->httpSock); + + if ((rfbScreen->httpSock = accept(rfbScreen->httpListenSock, + (struct sockaddr *)&addr, &addrlen)) < 0) { + rfbLogPerror("httpCheckFds: accept"); + return; + } +#ifdef USE_LIBWRAP + if(!hosts_ctl("vnc",STRING_UNKNOWN,inet_ntoa(addr.sin_addr), + STRING_UNKNOWN)) { + rfbLog("Rejected HTTP connection from client %s\n", + inet_ntoa(addr.sin_addr)); +#else + flags = fcntl(rfbScreen->httpSock, F_GETFL); + + if (flags < 0 || fcntl(rfbScreen->httpSock, F_SETFL, flags | O_NONBLOCK) == -1) { + rfbLogPerror("httpCheckFds: fcntl"); +#endif + close(rfbScreen->httpSock); + rfbScreen->httpSock = -1; + return; + } + flags=fcntl(rfbScreen->httpSock,F_GETFL); + if(flags==-1 || + fcntl(rfbScreen->httpSock,F_SETFL,flags|O_NONBLOCK)==-1) { + rfbLogPerror("httpCheckFds: fcntl"); + close(rfbScreen->httpSock); + rfbScreen->httpSock=-1; + return; + } + + /*AddEnabledDevice(httpSock);*/ + } +} + + +static void +httpCloseSock(rfbScreenInfoPtr rfbScreen) +{ + close(rfbScreen->httpSock); + rfbScreen->httpSock = -1; + buf_filled = 0; +} + +static rfbClientRec cl; + +/* + * httpProcessInput is called when input is received on the HTTP socket. + */ + +static void +httpProcessInput(rfbScreenInfoPtr rfbScreen) +{ + struct sockaddr_in addr; + size_t addrlen = sizeof(addr); + char fullFname[512]; + char params[1024]; + char *ptr; + char *fname; + unsigned int maxFnameLen; + FILE* fd; + rfbBool performSubstitutions = FALSE; + char str[256+32]; +#ifndef WIN32 + char* user=getenv("USER"); +#endif + + cl.sock=rfbScreen->httpSock; + + if (strlen(rfbScreen->httpDir) > 255) { + rfbErr("-httpd directory too long\n"); + httpCloseSock(rfbScreen); + return; + } + strcpy(fullFname, rfbScreen->httpDir); + fname = &fullFname[strlen(fullFname)]; + maxFnameLen = 511 - strlen(fullFname); + + buf_filled=0; + + /* Read data from the HTTP client until we get a complete request. */ + while (1) { + ssize_t got; + + if (buf_filled > sizeof (buf)) { + rfbErr("httpProcessInput: HTTP request is too long\n"); + httpCloseSock(rfbScreen); + return; + } + + got = read (rfbScreen->httpSock, buf + buf_filled, + sizeof (buf) - buf_filled - 1); + + if (got <= 0) { + if (got == 0) { + rfbErr("httpd: premature connection close\n"); + } else { + if (errno == EAGAIN) { + return; + } + rfbLogPerror("httpProcessInput: read"); + } + httpCloseSock(rfbScreen); + return; + } + + buf_filled += got; + buf[buf_filled] = '\0'; + + /* Is it complete yet (is there a blank line)? */ + if (strstr (buf, "\r\r") || strstr (buf, "\n\n") || + strstr (buf, "\r\n\r\n") || strstr (buf, "\n\r\n\r")) + break; + } + + + /* Process the request. */ + if(rfbScreen->httpEnableProxyConnect) { + const static char* PROXY_OK_STR = "HTTP/1.0 200 OK\r\nContent-Type: octet-stream\r\nPragma: no-cache\r\n\r\n"; + if(!strncmp(buf, "CONNECT ", 8)) { + if(atoi(strchr(buf, ':')+1)!=rfbScreen->rfbPort) { + rfbErr("httpd: CONNECT format invalid.\n"); + WriteExact(&cl,INVALID_REQUEST_STR, strlen(INVALID_REQUEST_STR)); + httpCloseSock(rfbScreen); + return; + } + /* proxy connection */ + rfbLog("httpd: client asked for CONNECT\n"); + WriteExact(&cl,PROXY_OK_STR,strlen(PROXY_OK_STR)); + rfbNewClientConnection(rfbScreen,rfbScreen->httpSock); + rfbScreen->httpSock = -1; + return; + } + if (!strncmp(buf, "GET ",4) && !strncmp(strchr(buf,'/'),"/proxied.connection HTTP/1.", 27)) { + /* proxy connection */ + rfbLog("httpd: client asked for /proxied.connection\n"); + WriteExact(&cl,PROXY_OK_STR,strlen(PROXY_OK_STR)); + rfbNewClientConnection(rfbScreen,rfbScreen->httpSock); + rfbScreen->httpSock = -1; + return; + } + } + + if (strncmp(buf, "GET ", 4)) { + rfbErr("httpd: no GET line\n"); + httpCloseSock(rfbScreen); + return; + } else { + /* Only use the first line. */ + buf[strcspn(buf, "\n\r")] = '\0'; + } + + if (strlen(buf) > maxFnameLen) { + rfbErr("httpd: GET line too long\n"); + httpCloseSock(rfbScreen); + return; + } + + if (sscanf(buf, "GET %s HTTP/1.0", fname) != 1) { + rfbErr("httpd: couldn't parse GET line\n"); + httpCloseSock(rfbScreen); + return; + } + + if (fname[0] != '/') { + rfbErr("httpd: filename didn't begin with '/'\n"); + WriteExact(&cl, NOT_FOUND_STR, strlen(NOT_FOUND_STR)); + httpCloseSock(rfbScreen); + return; + } + + if (strchr(fname+1, '/') != NULL) { + rfbErr("httpd: asking for file in other directory\n"); + WriteExact(&cl, NOT_FOUND_STR, strlen(NOT_FOUND_STR)); + httpCloseSock(rfbScreen); + return; + } + + getpeername(rfbScreen->httpSock, (struct sockaddr *)&addr, &addrlen); + rfbLog("httpd: get '%s' for %s\n", fname+1, + inet_ntoa(addr.sin_addr)); + + /* Extract parameters from the URL string if necessary */ + + params[0] = '\0'; + ptr = strchr(fname, '?'); + if (ptr != NULL) { + *ptr = '\0'; + if (!parseParams(&ptr[1], params, 1024)) { + params[0] = '\0'; + rfbErr("httpd: bad parameters in the URL\n"); + } + } + + + /* If we were asked for '/', actually read the file index.vnc */ + + if (strcmp(fname, "/") == 0) { + strcpy(fname, "/index.vnc"); + rfbLog("httpd: defaulting to '%s'\n", fname+1); + } + + /* Substitutions are performed on files ending .vnc */ + + if (strlen(fname) >= 4 && strcmp(&fname[strlen(fname)-4], ".vnc") == 0) { + performSubstitutions = TRUE; + } + + /* Open the file */ + + if ((fd = fopen(fullFname, "r")) == 0) { + rfbLogPerror("httpProcessInput: open"); + WriteExact(&cl, NOT_FOUND_STR, strlen(NOT_FOUND_STR)); + httpCloseSock(rfbScreen); + return; + } + + WriteExact(&cl, OK_STR, strlen(OK_STR)); + + while (1) { + int n = fread(buf, 1, BUF_SIZE-1, fd); + if (n < 0) { + rfbLogPerror("httpProcessInput: read"); + fclose(fd); + httpCloseSock(rfbScreen); + return; + } + + if (n == 0) + break; + + if (performSubstitutions) { + + /* Substitute $WIDTH, $HEIGHT, etc with the appropriate values. + This won't quite work properly if the .vnc file is longer than + BUF_SIZE, but it's reasonable to assume that .vnc files will + always be short. */ + + char *ptr = buf; + char *dollar; + buf[n] = 0; /* make sure it's null-terminated */ + + while ((dollar = strchr(ptr, '$'))!=NULL) { + WriteExact(&cl, ptr, (dollar - ptr)); + + ptr = dollar; + + if (compareAndSkip(&ptr, "$WIDTH")) { + + sprintf(str, "%d", rfbScreen->width); + WriteExact(&cl, str, strlen(str)); + + } else if (compareAndSkip(&ptr, "$HEIGHT")) { + + sprintf(str, "%d", rfbScreen->height); + WriteExact(&cl, str, strlen(str)); + + } else if (compareAndSkip(&ptr, "$APPLETWIDTH")) { + + sprintf(str, "%d", rfbScreen->width); + WriteExact(&cl, str, strlen(str)); + + } else if (compareAndSkip(&ptr, "$APPLETHEIGHT")) { + + sprintf(str, "%d", rfbScreen->height + 32); + WriteExact(&cl, str, strlen(str)); + + } else if (compareAndSkip(&ptr, "$PORT")) { + + sprintf(str, "%d", rfbScreen->rfbPort); + WriteExact(&cl, str, strlen(str)); + + } else if (compareAndSkip(&ptr, "$DESKTOP")) { + + WriteExact(&cl, rfbScreen->desktopName, strlen(rfbScreen->desktopName)); + + } else if (compareAndSkip(&ptr, "$DISPLAY")) { + + sprintf(str, "%s:%d", rfbScreen->rfbThisHost, rfbScreen->rfbPort-5900); + WriteExact(&cl, str, strlen(str)); + + } else if (compareAndSkip(&ptr, "$USER")) { +#ifndef WIN32 + if (user) { + WriteExact(&cl, user, + strlen(user)); + } else +#endif + WriteExact(&cl, "?", 1); + } else if (compareAndSkip(&ptr, "$PARAMS")) { + if (params[0] != '\0') + WriteExact(&cl, params, strlen(params)); + } else { + if (!compareAndSkip(&ptr, "$$")) + ptr++; + + if (WriteExact(&cl, "$", 1) < 0) { + fclose(fd); + httpCloseSock(rfbScreen); + return; + } + } + } + if (WriteExact(&cl, ptr, (&buf[n] - ptr)) < 0) + break; + + } else { + + /* For files not ending .vnc, just write out the buffer */ + + if (WriteExact(&cl, buf, n) < 0) + break; + } + } + + fclose(fd); + httpCloseSock(rfbScreen); +} + + +static rfbBool +compareAndSkip(char **ptr, const char *str) +{ + if (strncmp(*ptr, str, strlen(str)) == 0) { + *ptr += strlen(str); + return TRUE; + } + + return FALSE; +} + +/* + * Parse the request tail after the '?' character, and format a sequence + * of tags for inclusion into an HTML page with embedded applet. + */ + +static rfbBool +parseParams(const char *request, char *result, int max_bytes) +{ + char param_request[128]; + char param_formatted[196]; + const char *tail; + char *delim_ptr; + char *value_str; + int cur_bytes, len; + + result[0] = '\0'; + cur_bytes = 0; + + tail = request; + for (;;) { + /* Copy individual "name=value" string into a buffer */ + delim_ptr = strchr((char *)tail, '&'); + if (delim_ptr == NULL) { + if (strlen(tail) >= sizeof(param_request)) { + return FALSE; + } + strcpy(param_request, tail); + } else { + len = delim_ptr - tail; + if (len >= sizeof(param_request)) { + return FALSE; + } + memcpy(param_request, tail, len); + param_request[len] = '\0'; + } + + /* Split the request into parameter name and value */ + value_str = strchr(¶m_request[1], '='); + if (value_str == NULL) { + return FALSE; + } + *value_str++ = '\0'; + if (strlen(value_str) == 0) { + return FALSE; + } + + /* Validate both parameter name and value */ + if (!validateString(param_request) || !validateString(value_str)) { + return FALSE; + } + + /* Prepare HTML-formatted representation of the name=value pair */ + len = sprintf(param_formatted, + "\n", + param_request, value_str); + if (cur_bytes + len + 1 > max_bytes) { + return FALSE; + } + strcat(result, param_formatted); + cur_bytes += len; + + /* Go to the next parameter */ + if (delim_ptr == NULL) { + break; + } + tail = delim_ptr + 1; + } + return TRUE; +} + +/* + * Check if the string consists only of alphanumeric characters, '+' + * signs, underscores, and dots. Replace all '+' signs with spaces. + */ + +static rfbBool +validateString(char *str) +{ + char *ptr; + + for (ptr = str; *ptr != '\0'; ptr++) { + if (!isalnum(*ptr) && *ptr != '_' && *ptr != '.') { + if (*ptr == '+') { + *ptr = ' '; + } else { + return FALSE; + } + } + } + return TRUE; +} + diff --git a/libvncserver/main.c b/libvncserver/main.c new file mode 100644 index 0000000..488f09d --- /dev/null +++ b/libvncserver/main.c @@ -0,0 +1,851 @@ +/* + * This file is called main.c, because it contains most of the new functions + * for use with LibVNCServer. + * + * LibVNCServer (C) 2001 Johannes E. Schindelin + * Original OSXvnc (C) 2001 Dan McGuirk . + * Original Xvnc (C) 1999 AT&T Laboratories Cambridge. + * All Rights Reserved. + * + * see GPL (latest version) for full details + */ + +#include +#include + +#include +#include + +#ifndef false +#define false 0 +#define true -1 +#endif + +#ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H +#include +#endif + +#ifndef WIN32 +#include +#include +#include +#endif + +#include +#include + +#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD +MUTEX(logMutex); +#endif + +int rfbEnableLogging=1; + +#ifdef LIBVNCSERVER_WORDS_BIGENDIAN +char rfbEndianTest = 0; +#else +char rfbEndianTest = -1; +#endif + +/* from rfbserver.c */ +void rfbIncrClientRef(rfbClientPtr cl); +void rfbDecrClientRef(rfbClientPtr cl); + +void rfbLogEnable(int enabled) { + rfbEnableLogging=enabled; +} + +/* + * rfbLog prints a time-stamped message to the log file (stderr). + */ + +void +rfbDefaultLog(const char *format, ...) +{ + va_list args; + char buf[256]; + time_t log_clock; + + if(!rfbEnableLogging) + return; + + LOCK(logMutex); + va_start(args, format); + + time(&log_clock); + strftime(buf, 255, "%d/%m/%Y %X ", localtime(&log_clock)); + fprintf(stderr,buf); + + vfprintf(stderr, format, args); + fflush(stderr); + + va_end(args); + UNLOCK(logMutex); +} + +rfbLogProc rfbLog=rfbDefaultLog; +rfbLogProc rfbErr=rfbDefaultLog; + +void rfbLogPerror(const char *str) +{ + rfbErr("%s: %s\n", str, strerror(errno)); +} + +void rfbScheduleCopyRegion(rfbScreenInfoPtr rfbScreen,sraRegionPtr copyRegion,int dx,int dy) +{ + rfbClientIteratorPtr iterator; + rfbClientPtr cl; + + rfbUndrawCursor(rfbScreen); + + iterator=rfbGetClientIterator(rfbScreen); + while((cl=rfbClientIteratorNext(iterator))) { + LOCK(cl->updateMutex); + if(cl->useCopyRect) { + sraRegionPtr modifiedRegionBackup; + if(!sraRgnEmpty(cl->copyRegion)) { + if(cl->copyDX!=dx || cl->copyDY!=dy) { + /* if a copyRegion was not yet executed, treat it as a + * modifiedRegion. The idea: in this case it could be + * source of the new copyRect or modified anyway. */ + sraRgnOr(cl->modifiedRegion,cl->copyRegion); + sraRgnMakeEmpty(cl->copyRegion); + } else { + /* we have to set the intersection of the source of the copy + * and the old copy to modified. */ + modifiedRegionBackup=sraRgnCreateRgn(copyRegion); + sraRgnOffset(modifiedRegionBackup,-dx,-dy); + sraRgnAnd(modifiedRegionBackup,cl->copyRegion); + sraRgnOr(cl->modifiedRegion,modifiedRegionBackup); + sraRgnDestroy(modifiedRegionBackup); + } + } + + sraRgnOr(cl->copyRegion,copyRegion); + cl->copyDX = dx; + cl->copyDY = dy; + + /* if there were modified regions, which are now copied, + * mark them as modified, because the source of these can be overlapped + * either by new modified or now copied regions. */ + modifiedRegionBackup=sraRgnCreateRgn(cl->modifiedRegion); + sraRgnOffset(modifiedRegionBackup,dx,dy); + sraRgnAnd(modifiedRegionBackup,cl->copyRegion); + sraRgnOr(cl->modifiedRegion,modifiedRegionBackup); + sraRgnDestroy(modifiedRegionBackup); + +#if 0 + /* TODO: is this needed? Or does it mess up deferring? */ + /* while(!sraRgnEmpty(cl->copyRegion)) */ { +#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD + if(!cl->screen->backgroundLoop) +#endif + { + sraRegionPtr updateRegion = sraRgnCreateRgn(cl->modifiedRegion); + sraRgnOr(updateRegion,cl->copyRegion); + UNLOCK(cl->updateMutex); + rfbSendFramebufferUpdate(cl,updateRegion); + sraRgnDestroy(updateRegion); + continue; + } + } +#endif + } else { + sraRgnOr(cl->modifiedRegion,copyRegion); + } + TSIGNAL(cl->updateCond); + UNLOCK(cl->updateMutex); + } + + rfbReleaseClientIterator(iterator); +} + +void rfbDoCopyRegion(rfbScreenInfoPtr rfbScreen,sraRegionPtr copyRegion,int dx,int dy) +{ + sraRectangleIterator* i; + sraRect rect; + int j,widthInBytes,bpp=rfbScreen->rfbServerFormat.bitsPerPixel/8, + rowstride=rfbScreen->paddedWidthInBytes; + char *in,*out; + + rfbUndrawCursor(rfbScreen); + + /* copy it, really */ + i = sraRgnGetReverseIterator(copyRegion,dx<0,dy<0); + while(sraRgnIteratorNext(i,&rect)) { + widthInBytes = (rect.x2-rect.x1)*bpp; + out = rfbScreen->frameBuffer+rect.x1*bpp+rect.y1*rowstride; + in = rfbScreen->frameBuffer+(rect.x1-dx)*bpp+(rect.y1-dy)*rowstride; + if(dy<0) + for(j=rect.y1;j=rect.y1;j--,out-=rowstride,in-=rowstride) + memmove(out,in,widthInBytes); + } + } + + rfbScheduleCopyRegion(rfbScreen,copyRegion,dx,dy); +} + +void rfbDoCopyRect(rfbScreenInfoPtr rfbScreen,int x1,int y1,int x2,int y2,int dx,int dy) +{ + sraRegionPtr region = sraRgnCreateRect(x1,y1,x2,y2); + rfbDoCopyRegion(rfbScreen,region,dx,dy); +} + +void rfbScheduleCopyRect(rfbScreenInfoPtr rfbScreen,int x1,int y1,int x2,int y2,int dx,int dy) +{ + sraRegionPtr region = sraRgnCreateRect(x1,y1,x2,y2); + rfbScheduleCopyRegion(rfbScreen,region,dx,dy); +} + +void rfbMarkRegionAsModified(rfbScreenInfoPtr rfbScreen,sraRegionPtr modRegion) +{ + rfbClientIteratorPtr iterator; + rfbClientPtr cl; + + iterator=rfbGetClientIterator(rfbScreen); + while((cl=rfbClientIteratorNext(iterator))) { + LOCK(cl->updateMutex); + sraRgnOr(cl->modifiedRegion,modRegion); + TSIGNAL(cl->updateCond); + UNLOCK(cl->updateMutex); + } + + rfbReleaseClientIterator(iterator); +} + +void rfbMarkRectAsModified(rfbScreenInfoPtr rfbScreen,int x1,int y1,int x2,int y2) +{ + sraRegionPtr region; + int i; + + if(x1>x2) { i=x1; x1=x2; x2=i; } + if(x1<0) x1=0; + if(x2>=rfbScreen->width) x2=rfbScreen->width-1; + if(x1==x2) return; + + if(y1>y2) { i=y1; y1=y2; y2=i; } + if(y1<0) y1=0; + if(y2>=rfbScreen->height) y2=rfbScreen->height-1; + if(y1==y2) return; + + region = sraRgnCreateRect(x1,y1,x2,y2); + rfbMarkRegionAsModified(rfbScreen,region); + sraRgnDestroy(region); +} + +#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD +static void * +clientOutput(void *data) +{ + rfbClientPtr cl = (rfbClientPtr)data; + rfbBool haveUpdate; + sraRegion* updateRegion; + + while (1) { + haveUpdate = false; + while (!haveUpdate) { + if (cl->sock == -1) { + /* Client has disconnected. */ + return NULL; + } + LOCK(cl->updateMutex); + haveUpdate = FB_UPDATE_PENDING(cl); + if(!haveUpdate) { + updateRegion = sraRgnCreateRgn(cl->modifiedRegion); + haveUpdate = sraRgnAnd(updateRegion,cl->requestedRegion); + sraRgnDestroy(updateRegion); + } + UNLOCK(cl->updateMutex); + + if (!haveUpdate) { + WAIT(cl->updateCond, cl->updateMutex); + UNLOCK(cl->updateMutex); /* we really needn't lock now. */ + } + } + + /* OK, now, to save bandwidth, wait a little while for more + updates to come along. */ + usleep(cl->screen->rfbDeferUpdateTime * 1000); + + /* Now, get the region we're going to update, and remove + it from cl->modifiedRegion _before_ we send the update. + That way, if anything that overlaps the region we're sending + is updated, we'll be sure to do another update later. */ + LOCK(cl->updateMutex); + updateRegion = sraRgnCreateRgn(cl->modifiedRegion); + UNLOCK(cl->updateMutex); + + /* Now actually send the update. */ + rfbIncrClientRef(cl); + rfbSendFramebufferUpdate(cl, updateRegion); + rfbDecrClientRef(cl); + + sraRgnDestroy(updateRegion); + } + + return NULL; +} + +static void * +clientInput(void *data) +{ + rfbClientPtr cl = (rfbClientPtr)data; + pthread_t output_thread; + pthread_create(&output_thread, NULL, clientOutput, (void *)cl); + + while (1) { + rfbProcessClientMessage(cl); + if (cl->sock == -1) { + /* Client has disconnected. */ + break; + } + } + + /* Get rid of the output thread. */ + LOCK(cl->updateMutex); + TSIGNAL(cl->updateCond); + UNLOCK(cl->updateMutex); + IF_PTHREADS(pthread_join(output_thread, NULL)); + + rfbClientConnectionGone(cl); + + return NULL; +} + +static void* +listenerRun(void *data) +{ + rfbScreenInfoPtr rfbScreen=(rfbScreenInfoPtr)data; + int client_fd; + struct sockaddr_in peer; + rfbClientPtr cl; + size_t len; + + len = sizeof(peer); + + /* TODO: this thread wont die by restarting the server */ + while ((client_fd = accept(rfbScreen->rfbListenSock, + (struct sockaddr*)&peer, &len)) >= 0) { + cl = rfbNewClient(rfbScreen,client_fd); + len = sizeof(peer); + + if (cl && !cl->onHold ) + rfbStartOnHoldClient(cl); + } + return(NULL); +} + +void +rfbStartOnHoldClient(rfbClientPtr cl) +{ + pthread_create(&cl->client_thread, NULL, clientInput, (void *)cl); +} + +#else + +void +rfbStartOnHoldClient(rfbClientPtr cl) +{ + cl->onHold = FALSE; +} + +#endif + +void +rfbRefuseOnHoldClient(rfbClientPtr cl) +{ + rfbCloseClient(cl); + rfbClientConnectionGone(cl); +} + +static void +defaultKbdAddEvent(rfbBool down, rfbKeySym keySym, rfbClientPtr cl) +{ +} + +void +defaultPtrAddEvent(int buttonMask, int x, int y, rfbClientPtr cl) +{ + rfbClientIteratorPtr iterator; + rfbClientPtr other_client; + + if (x != cl->screen->cursorX || y != cl->screen->cursorY) { + if (cl->screen->cursorIsDrawn) + rfbUndrawCursor(cl->screen); + LOCK(cl->screen->cursorMutex); + if (!cl->screen->cursorIsDrawn) { + cl->screen->cursorX = x; + cl->screen->cursorY = y; + } + UNLOCK(cl->screen->cursorMutex); + + /* The cursor was moved by this client, so don't send CursorPos. */ + if (cl->enableCursorPosUpdates) + cl->cursorWasMoved = FALSE; + + /* But inform all remaining clients about this cursor movement. */ + iterator = rfbGetClientIterator(cl->screen); + while ((other_client = rfbClientIteratorNext(iterator)) != NULL) { + if (other_client != cl && other_client->enableCursorPosUpdates) { + other_client->cursorWasMoved = TRUE; + } + } + rfbReleaseClientIterator(iterator); + } +} + +void defaultSetXCutText(char* text, int len, rfbClientPtr cl) +{ +} + +/* TODO: add a nice VNC or RFB cursor */ + +#if defined(WIN32) || defined(sparc) || !defined(NO_STRICT_ANSI) +static rfbCursor myCursor = +{ + FALSE, FALSE, FALSE, FALSE, + (unsigned char*)"\000\102\044\030\044\102\000", + (unsigned char*)"\347\347\176\074\176\347\347", + 8, 7, 3, 3, + 0, 0, 0, + 0xffff, 0xffff, 0xffff, + 0 +}; +#else +static rfbCursor myCursor = +{ + cleanup: FALSE, + cleanupSource: FALSE, + cleanupMask: FALSE, + cleanupRichSource: FALSE, + source: "\000\102\044\030\044\102\000", + mask: "\347\347\176\074\176\347\347", + width: 8, height: 7, xhot: 3, yhot: 3, + foreRed: 0, foreGreen: 0, foreBlue: 0, + backRed: 0xffff, backGreen: 0xffff, backBlue: 0xffff, + richSource: 0 +}; +#endif + +rfbCursorPtr defaultGetCursorPtr(rfbClientPtr cl) +{ + return(cl->screen->cursor); +} + +/* response is cl->authChallenge vncEncrypted with passwd */ +rfbBool defaultPasswordCheck(rfbClientPtr cl,const char* response,int len) +{ + int i; + char *passwd=vncDecryptPasswdFromFile(cl->screen->rfbAuthPasswdData); + + if(!passwd) { + rfbErr("Couldn't read password file: %s\n",cl->screen->rfbAuthPasswdData); + return(FALSE); + } + + vncEncryptBytes(cl->authChallenge, passwd); + + /* Lose the password from memory */ + for (i = strlen(passwd); i >= 0; i--) { + passwd[i] = '\0'; + } + + free(passwd); + + if (memcmp(cl->authChallenge, response, len) != 0) { + rfbErr("rfbAuthProcessClientMessage: authentication failed from %s\n", + cl->host); + return(FALSE); + } + + return(TRUE); +} + +/* for this method, rfbAuthPasswdData is really a pointer to an array + of char*'s, where the last pointer is 0. */ +rfbBool rfbCheckPasswordByList(rfbClientPtr cl,const char* response,int len) +{ + char **passwds; + int i=0; + + for(passwds=(char**)cl->screen->rfbAuthPasswdData;*passwds;passwds++,i++) { + vncEncryptBytes(cl->authChallenge, *passwds); + + if (memcmp(cl->authChallenge, response, len) == 0) { + if(i>=cl->screen->rfbAuthPasswdFirstViewOnly) + cl->viewOnly=TRUE; + return(TRUE); + } + } + + rfbErr("rfbAuthProcessClientMessage: authentication failed from %s\n", + cl->host); + return(FALSE); +} + +void doNothingWithClient(rfbClientPtr cl) +{ +} + +enum rfbNewClientAction defaultNewClientHook(rfbClientPtr cl) +{ + return RFB_CLIENT_ACCEPT; +} + +/* + * Update server's pixel format in rfbScreenInfo structure. This + * function is called from rfbGetScreen() and rfbNewFramebuffer(). + */ + +static void rfbInitServerFormat(rfbScreenInfoPtr rfbScreen, int bitsPerSample) +{ + rfbPixelFormat* format=&rfbScreen->rfbServerFormat; + + format->bitsPerPixel = rfbScreen->bitsPerPixel; + format->depth = rfbScreen->depth; + format->bigEndian = rfbEndianTest?FALSE:TRUE; + format->trueColour = TRUE; + rfbScreen->colourMap.count = 0; + rfbScreen->colourMap.is16 = 0; + rfbScreen->colourMap.data.bytes = NULL; + + if (format->bitsPerPixel == 8) { + format->redMax = 7; + format->greenMax = 7; + format->blueMax = 3; + format->redShift = 0; + format->greenShift = 3; + format->blueShift = 6; + } else { + format->redMax = (1 << bitsPerSample) - 1; + format->greenMax = (1 << bitsPerSample) - 1; + format->blueMax = (1 << bitsPerSample) - 1; + if(rfbEndianTest) { + format->redShift = 0; + format->greenShift = bitsPerSample; + format->blueShift = bitsPerSample * 2; + } else { + if(format->bitsPerPixel==8*3) { + format->redShift = bitsPerSample*2; + format->greenShift = bitsPerSample*1; + format->blueShift = 0; + } else { + format->redShift = bitsPerSample*3; + format->greenShift = bitsPerSample*2; + format->blueShift = bitsPerSample; + } + } + } +} + +rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv, + int width,int height,int bitsPerSample,int samplesPerPixel, + int bytesPerPixel) +{ + rfbScreenInfoPtr rfbScreen=malloc(sizeof(rfbScreenInfo)); + + INIT_MUTEX(logMutex); + + if(width&3) + rfbErr("WARNING: Width (%d) is not a multiple of 4. VncViewer has problems with that.\n",width); + + rfbScreen->autoPort=FALSE; + rfbScreen->rfbClientHead=0; + rfbScreen->rfbPort=5900; + rfbScreen->socketInitDone=FALSE; + + rfbScreen->inetdInitDone = FALSE; + rfbScreen->inetdSock=-1; + + rfbScreen->udpSock=-1; + rfbScreen->udpSockConnected=FALSE; + rfbScreen->udpPort=0; + rfbScreen->udpClient=0; + + rfbScreen->maxFd=0; + rfbScreen->rfbListenSock=-1; + + rfbScreen->httpInitDone=FALSE; + rfbScreen->httpEnableProxyConnect=FALSE; + rfbScreen->httpPort=0; + rfbScreen->httpDir=NULL; + rfbScreen->httpListenSock=-1; + rfbScreen->httpSock=-1; + + rfbScreen->desktopName = "LibVNCServer"; + rfbScreen->rfbAlwaysShared = FALSE; + rfbScreen->rfbNeverShared = FALSE; + rfbScreen->rfbDontDisconnect = FALSE; + rfbScreen->rfbAuthPasswdData = 0; + rfbScreen->rfbAuthPasswdFirstViewOnly = 1; + + rfbScreen->width = width; + rfbScreen->height = height; + rfbScreen->bitsPerPixel = rfbScreen->depth = 8*bytesPerPixel; + + rfbScreen->passwordCheck = defaultPasswordCheck; + + rfbScreen->ignoreSIGPIPE = TRUE; + + /* disable progressive updating per default */ + rfbScreen->progressiveSliceHeight = 0; + + if(!rfbProcessArguments(rfbScreen,argc,argv)) { + free(rfbScreen); + return 0; + } + +#ifdef WIN32 + { + DWORD dummy=255; + GetComputerName(rfbScreen->rfbThisHost,&dummy); + } +#else + gethostname(rfbScreen->rfbThisHost, 255); +#endif + + rfbScreen->paddedWidthInBytes = width*bytesPerPixel; + + /* format */ + + rfbInitServerFormat(rfbScreen, bitsPerSample); + + /* cursor */ + + rfbScreen->cursorIsDrawn = FALSE; + rfbScreen->dontSendFramebufferUpdate = FALSE; + rfbScreen->cursorX=rfbScreen->cursorY=rfbScreen->underCursorBufferLen=0; + rfbScreen->underCursorBuffer=NULL; + rfbScreen->dontConvertRichCursorToXCursor = FALSE; + rfbScreen->cursor = &myCursor; + INIT_MUTEX(rfbScreen->cursorMutex); + + IF_PTHREADS(rfbScreen->backgroundLoop = FALSE); + + rfbScreen->rfbDeferUpdateTime=5; + rfbScreen->maxRectsPerUpdate=50; + + /* proc's and hook's */ + + rfbScreen->kbdAddEvent = defaultKbdAddEvent; + rfbScreen->kbdReleaseAllKeys = doNothingWithClient; + rfbScreen->ptrAddEvent = defaultPtrAddEvent; + rfbScreen->setXCutText = defaultSetXCutText; + rfbScreen->getCursorPtr = defaultGetCursorPtr; + rfbScreen->setTranslateFunction = rfbSetTranslateFunction; + rfbScreen->newClientHook = defaultNewClientHook; + rfbScreen->displayHook = 0; + + /* initialize client list and iterator mutex */ + rfbClientListInit(rfbScreen); + + return(rfbScreen); +} + +/* + * Switch to another framebuffer (maybe of different size and color + * format). Clients supporting NewFBSize pseudo-encoding will change + * their local framebuffer dimensions if necessary. + * NOTE: Rich cursor data should be converted to new pixel format by + * the caller. + */ + +void rfbNewFramebuffer(rfbScreenInfoPtr rfbScreen, char *framebuffer, + int width, int height, + int bitsPerSample, int samplesPerPixel, + int bytesPerPixel) +{ + rfbPixelFormat old_format; + rfbBool format_changed = FALSE; + rfbClientIteratorPtr iterator; + rfbClientPtr cl; + + /* Remove the pointer */ + + rfbUndrawCursor(rfbScreen); + + /* Update information in the rfbScreenInfo structure */ + + old_format = rfbScreen->rfbServerFormat; + + if (width & 3) + rfbErr("WARNING: New width (%d) is not a multiple of 4.\n", width); + + rfbScreen->width = width; + rfbScreen->height = height; + rfbScreen->bitsPerPixel = rfbScreen->depth = 8*bytesPerPixel; + rfbScreen->paddedWidthInBytes = width*bytesPerPixel; + + rfbInitServerFormat(rfbScreen, bitsPerSample); + + if (memcmp(&rfbScreen->rfbServerFormat, &old_format, + sizeof(rfbPixelFormat)) != 0) { + format_changed = TRUE; + } + + rfbScreen->frameBuffer = framebuffer; + + /* Adjust pointer position if necessary */ + + if (rfbScreen->cursorX >= width) + rfbScreen->cursorX = width - 1; + if (rfbScreen->cursorY >= height) + rfbScreen->cursorY = height - 1; + + /* For each client: */ + iterator = rfbGetClientIterator(rfbScreen); + while ((cl = rfbClientIteratorNext(iterator)) != NULL) { + + /* Re-install color translation tables if necessary */ + + if (format_changed) + rfbScreen->setTranslateFunction(cl); + + /* Mark the screen contents as changed, and schedule sending + NewFBSize message if supported by this client. */ + + LOCK(cl->updateMutex); + sraRgnDestroy(cl->modifiedRegion); + cl->modifiedRegion = sraRgnCreateRect(0, 0, width, height); + sraRgnMakeEmpty(cl->copyRegion); + cl->copyDX = 0; + cl->copyDY = 0; + + if (cl->useNewFBSize) + cl->newFBSizePending = TRUE; + + TSIGNAL(cl->updateCond); + UNLOCK(cl->updateMutex); + } + rfbReleaseClientIterator(iterator); +} + +#ifdef LIBVNCSERVER_HAVE_LIBJPEG +extern void TightCleanup(); +#endif + +void rfbScreenCleanup(rfbScreenInfoPtr rfbScreen) +{ + rfbClientIteratorPtr i=rfbGetClientIterator(rfbScreen); + rfbClientPtr cl,cl1=rfbClientIteratorNext(i); + while(cl1) { + cl=rfbClientIteratorNext(i); + rfbClientConnectionGone(cl1); + cl1=cl; + } + rfbReleaseClientIterator(i); + + /* TODO: hang up on all clients and free all reserved memory */ +#define FREE_IF(x) if(rfbScreen->x) free(rfbScreen->x) + FREE_IF(colourMap.data.bytes); + FREE_IF(underCursorBuffer); + TINI_MUTEX(rfbScreen->cursorMutex); + if(rfbScreen->cursor) + rfbFreeCursor(rfbScreen->cursor); + free(rfbScreen); +#ifdef LIBVNCSERVER_HAVE_LIBJPEG + TightCleanup(); +#endif +} + +void rfbInitServer(rfbScreenInfoPtr rfbScreen) +{ +#ifdef WIN32 + WSADATA trash; + int i=WSAStartup(MAKEWORD(2,2),&trash); +#endif + rfbInitSockets(rfbScreen); + httpInitSockets(rfbScreen); + if(rfbScreen->ignoreSIGPIPE) + signal(SIGPIPE,SIG_IGN); +} + +#ifndef LIBVNCSERVER_HAVE_GETTIMEOFDAY +#include +#include +#include + +void gettimeofday(struct timeval* tv,char* dummy) +{ + SYSTEMTIME t; + GetSystemTime(&t); + tv->tv_sec=t.wHour*3600+t.wMinute*60+t.wSecond; + tv->tv_usec=t.wMilliseconds*1000; +} +#endif + +/* defined in rfbserver.c, but kind of "private" */ +rfbClientPtr rfbClientIteratorHead(rfbClientIteratorPtr i); + +void +rfbProcessEvents(rfbScreenInfoPtr rfbScreen,long usec) +{ + rfbClientIteratorPtr i; + rfbClientPtr cl,clPrev; + struct timeval tv; + + if(usec<0) + usec=rfbScreen->rfbDeferUpdateTime*1000; + + rfbCheckFds(rfbScreen,usec); + httpCheckFds(rfbScreen); +#ifdef CORBA + corbaCheckFds(rfbScreen); +#endif + + i = rfbGetClientIterator(rfbScreen); + cl=rfbClientIteratorHead(i); + while(cl) { + if (cl->sock >= 0 && !cl->onHold && FB_UPDATE_PENDING(cl) && + !sraRgnEmpty(cl->requestedRegion)) { + if(rfbScreen->rfbDeferUpdateTime == 0) { + rfbSendFramebufferUpdate(cl,cl->modifiedRegion); + } else if(cl->startDeferring.tv_usec == 0) { + gettimeofday(&cl->startDeferring,NULL); + if(cl->startDeferring.tv_usec == 0) + cl->startDeferring.tv_usec++; + } else { + gettimeofday(&tv,NULL); + if(tv.tv_sec < cl->startDeferring.tv_sec /* at midnight */ + || ((tv.tv_sec-cl->startDeferring.tv_sec)*1000 + +(tv.tv_usec-cl->startDeferring.tv_usec)/1000) + > rfbScreen->rfbDeferUpdateTime) { + cl->startDeferring.tv_usec = 0; + rfbSendFramebufferUpdate(cl,cl->modifiedRegion); + } + } + } + clPrev=cl; + cl=rfbClientIteratorNext(i); + if(clPrev->sock==-1) + rfbClientConnectionGone(clPrev); + } + rfbReleaseClientIterator(i); +} + +void rfbRunEventLoop(rfbScreenInfoPtr rfbScreen, long usec, rfbBool runInBackground) +{ + if(runInBackground) { +#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD + pthread_t listener_thread; + + rfbScreen->backgroundLoop = TRUE; + + pthread_create(&listener_thread, NULL, listenerRun, rfbScreen); + return; +#else + rfbErr("Can't run in background, because I don't have PThreads!\n"); + return; +#endif + } + + if(usec<0) + usec=rfbScreen->rfbDeferUpdateTime*1000; + + while(1) + rfbProcessEvents(rfbScreen,usec); +} diff --git a/libvncserver/rfbconfig.h b/libvncserver/rfbconfig.h new file mode 100644 index 0000000..888fc9b --- /dev/null +++ b/libvncserver/rfbconfig.h @@ -0,0 +1,243 @@ +/* rfbconfig.h. Generated by configure. */ +/* rfbconfig.h.in. Generated from configure.ac by autoheader. */ + +/* Enable 24 bit per pixel in native framebuffer */ +#define ALLOW24BPP 1 + +/* Enable BackChannel communication */ +#define BACKCHANNEL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ARPA_INET_H 1 + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `fork' function. */ +#define HAVE_FORK 1 + +/* Define to 1 if you have the `ftime' function. */ +#define HAVE_FTIME 1 + +/* Define to 1 if you have the `gethostbyname' function. */ +#define HAVE_GETHOSTBYNAME 1 + +/* Define to 1 if you have the `gethostname' function. */ +#define HAVE_GETHOSTNAME 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the `inet_ntoa' function. */ +#define HAVE_INET_NTOA 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `cygipc' library (-lcygipc). */ +/* #undef HAVE_LIBCYGIPC */ + +/* Define to 1 if you have the `jpeg' library (-ljpeg). */ +#define HAVE_LIBJPEG 1 + +/* Define to 1 if you have the `nsl' library (-lnsl). */ +#define HAVE_LIBNSL 1 + +/* Define to 1 if you have the `pthread' library (-lpthread). */ +#define HAVE_LIBPTHREAD 1 + +/* Define to 1 if you have the `socket' library (-lsocket). */ +/* #undef HAVE_LIBSOCKET */ + +/* XINERAMA extension build environment present */ +#define HAVE_LIBXINERAMA 1 + +/* Define to 1 if you have the `z' library (-lz). */ +#define HAVE_LIBZ 1 + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the `memmove' function. */ +#define HAVE_MEMMOVE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have the `mkfifo' function. */ +#define HAVE_MKFIFO 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETDB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the `select' function. */ +#define HAVE_SELECT 1 + +/* Define to 1 if you have the `setsid' function. */ +#define HAVE_SETSID 1 + +/* Define to 1 if you have the `socket' function. */ +#define HAVE_SOCKET 1 + +/* Define to 1 if `stat' has the bug that it succeeds when given the + zero-length file name argument. */ +/* #undef HAVE_STAT_EMPTY_STRING_BUG */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strchr' function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the `strcspn' function. */ +#define HAVE_STRCSPN 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the `strftime' function. */ +#define HAVE_STRFTIME 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strstr' function. */ +#define HAVE_STRSTR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYSLOG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIMEB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `vfork' function. */ +#define HAVE_VFORK 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_VFORK_H */ + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if `fork' works. */ +#define HAVE_WORKING_FORK 1 + +/* Define to 1 if `vfork' works. */ +#define HAVE_WORKING_VFORK 1 + +/* XKEYBOARD extension build environment present */ +#define HAVE_XKEYBOARD 1 + +/* Define to 1 if `lstat' dereferences a symlink specified with a trailing + slash. */ +#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 + +/* Name of package */ +#define PACKAGE "LibVNCServer" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "http://sourceforge.net/projects/libvncserver" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "LibVNCServer" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "LibVNCServer 0.7pre" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libvncserver" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.7pre" + +/* The number of bytes in type char */ +/* #undef SIZEOF_CHAR */ + +/* The number of bytes in type int */ +/* #undef SIZEOF_INT */ + +/* The number of bytes in type long */ +/* #undef SIZEOF_LONG */ + +/* The number of bytes in type short */ +/* #undef SIZEOF_SHORT */ + +/* The number of bytes in type void* */ +/* #undef SIZEOF_VOIDP */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Version number of package */ +#define VERSION "0.7pre" + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +/* #undef WORDS_BIGENDIAN */ + +/* Define to 1 if the X Window System is missing or not being used. */ +/* #undef X_DISPLAY_MISSING */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to rpl_malloc if the replacement function should be used. */ +/* #undef malloc */ + +/* Define to `int' if does not define. */ +/* #undef pid_t */ + +/* Define to `unsigned' if does not define. */ +/* #undef size_t */ + +/* The type for socklen */ +/* #undef socklen_t */ + +/* Define as `fork' if `vfork' does not work. */ +/* #undef vfork */ diff --git a/libvncserver/rfbregion.c b/libvncserver/rfbregion.c new file mode 100755 index 0000000..a102bc2 --- /dev/null +++ b/libvncserver/rfbregion.c @@ -0,0 +1,862 @@ +/* -=- sraRegion.c + * Copyright (c) 2001 James "Wez" Weatherall, Johannes E. Schindelin + * + * A general purpose region clipping library + * Only deals with rectangular regions, though. + */ + +#include +#include + +/* -=- Internal Span structure */ + +struct sraRegion; + +typedef struct sraSpan { + struct sraSpan *_next; + struct sraSpan *_prev; + int start; + int end; + struct sraRegion *subspan; +} sraSpan; + +typedef struct sraRegion { + sraSpan front; + sraSpan back; +} sraSpanList; + +/* -=- Span routines */ + +sraSpanList *sraSpanListDup(const sraSpanList *src); +void sraSpanListDestroy(sraSpanList *list); + +sraSpan * +sraSpanCreate(int start, int end, const sraSpanList *subspan) { + sraSpan *item = (sraSpan*)malloc(sizeof(sraSpan)); + item->_next = item->_prev = NULL; + item->start = start; + item->end = end; + item->subspan = sraSpanListDup(subspan); + return item; +} + +sraSpan * +sraSpanDup(const sraSpan *src) { + sraSpan *span; + if (!src) return NULL; + span = sraSpanCreate(src->start, src->end, src->subspan); + return span; +} + +void +sraSpanInsertAfter(sraSpan *newspan, sraSpan *after) { + newspan->_next = after->_next; + newspan->_prev = after; + after->_next->_prev = newspan; + after->_next = newspan; +} + +void +sraSpanInsertBefore(sraSpan *newspan, sraSpan *before) { + newspan->_next = before; + newspan->_prev = before->_prev; + before->_prev->_next = newspan; + before->_prev = newspan; +} + +void +sraSpanRemove(sraSpan *span) { + span->_prev->_next = span->_next; + span->_next->_prev = span->_prev; +} + +void +sraSpanDestroy(sraSpan *span) { + if (span->subspan) sraSpanListDestroy(span->subspan); + free(span); +} + +void +sraSpanCheck(const sraSpan *span, const char *text) { + /* Check the span is valid! */ + if (span->start == span->end) { + printf(text); + printf(":%d-%d\n", span->start, span->end); + } +} + +/* -=- SpanList routines */ + +void sraSpanPrint(const sraSpan *s); + +void +sraSpanListPrint(const sraSpanList *l) { + sraSpan *curr; + if (!l) { + printf("NULL"); + return; + } + curr = l->front._next; + printf("["); + while (curr != &(l->back)) { + sraSpanPrint(curr); + curr = curr->_next; + } + printf("]"); +} + +void +sraSpanPrint(const sraSpan *s) { + printf("(%d-%d)", (s->start), (s->end)); + if (s->subspan) + sraSpanListPrint(s->subspan); +} + +sraSpanList * +sraSpanListCreate() { + sraSpanList *item = (sraSpanList*)malloc(sizeof(sraSpanList)); + item->front._next = &(item->back); + item->front._prev = NULL; + item->back._prev = &(item->front); + item->back._next = NULL; + return item; +} + +sraSpanList * +sraSpanListDup(const sraSpanList *src) { + sraSpanList *newlist; + sraSpan *newspan, *curr; + + if (!src) return NULL; + newlist = sraSpanListCreate(); + curr = src->front._next; + while (curr != &(src->back)) { + newspan = sraSpanDup(curr); + sraSpanInsertBefore(newspan, &(newlist->back)); + curr = curr->_next; + } + + return newlist; +} + +void +sraSpanListDestroy(sraSpanList *list) { + sraSpan *curr, *next; + while (list->front._next != &(list->back)) { + curr = list->front._next; + next = curr->_next; + sraSpanRemove(curr); + sraSpanDestroy(curr); + curr = next; + } + free(list); +} + +void +sraSpanListMakeEmpty(sraSpanList *list) { + sraSpan *curr, *next; + while (list->front._next != &(list->back)) { + curr = list->front._next; + next = curr->_next; + sraSpanRemove(curr); + sraSpanDestroy(curr); + curr = next; + } + list->front._next = &(list->back); + list->front._prev = NULL; + list->back._prev = &(list->front); + list->back._next = NULL; +} + +rfbBool +sraSpanListEqual(const sraSpanList *s1, const sraSpanList *s2) { + sraSpan *sp1, *sp2; + + if (!s1) { + if (!s2) { + return 1; + } else { + printf("sraSpanListEqual:incompatible spans (only one NULL!)\n"); + return FALSE; + } + } + + sp1 = s1->front._next; + sp2 = s2->front._next; + while ((sp1 != &(s1->back)) && + (sp2 != &(s2->back))) { + if ((sp1->start != sp2->start) || + (sp1->end != sp2->end) || + (!sraSpanListEqual(sp1->subspan, sp2->subspan))) { + return 0; + } + sp1 = sp1->_next; + sp2 = sp2->_next; + } + + if ((sp1 == &(s1->back)) && (sp2 == &(s2->back))) { + return 1; + } else { + return 0; + } +} + +rfbBool +sraSpanListEmpty(const sraSpanList *list) { + return (list->front._next == &(list->back)); +} + +unsigned long +sraSpanListCount(const sraSpanList *list) { + sraSpan *curr = list->front._next; + unsigned long count = 0; + while (curr != &(list->back)) { + if (curr->subspan) { + count += sraSpanListCount(curr->subspan); + } else { + count += 1; + } + curr = curr->_next; + } + return count; +} + +void +sraSpanMergePrevious(sraSpan *dest) { + sraSpan *prev = dest->_prev; + + while ((prev->_prev) && + (prev->end == dest->start) && + (sraSpanListEqual(prev->subspan, dest->subspan))) { + /* + printf("merge_prev:"); + sraSpanPrint(prev); + printf(" & "); + sraSpanPrint(dest); + printf("\n"); + */ + dest->start = prev->start; + sraSpanRemove(prev); + sraSpanDestroy(prev); + prev = dest->_prev; + } +} + +void +sraSpanMergeNext(sraSpan *dest) { + sraSpan *next = dest->_next; + while ((next->_next) && + (next->start == dest->end) && + (sraSpanListEqual(next->subspan, dest->subspan))) { +/* + printf("merge_next:"); + sraSpanPrint(dest); + printf(" & "); + sraSpanPrint(next); + printf("\n"); + */ + dest->end = next->end; + sraSpanRemove(next); + sraSpanDestroy(next); + next = dest->_next; + } +} + +void +sraSpanListOr(sraSpanList *dest, const sraSpanList *src) { + sraSpan *d_curr, *s_curr; + int s_start, s_end; + + if (!dest) { + if (!src) { + return; + } else { + printf("sraSpanListOr:incompatible spans (only one NULL!)\n"); + return; + } + } + + d_curr = dest->front._next; + s_curr = src->front._next; + s_start = s_curr->start; + s_end = s_curr->end; + while (s_curr != &(src->back)) { + + /* - If we are at end of destination list OR + If the new span comes before the next destination one */ + if ((d_curr == &(dest->back)) || + (d_curr->start >= s_end)) { + /* - Add the span */ + sraSpanInsertBefore(sraSpanCreate(s_start, s_end, + s_curr->subspan), + d_curr); + if (d_curr != &(dest->back)) + sraSpanMergePrevious(d_curr); + s_curr = s_curr->_next; + s_start = s_curr->start; + s_end = s_curr->end; + } else { + + /* - If the new span overlaps the existing one */ + if ((s_start < d_curr->end) && + (s_end > d_curr->start)) { + + /* - Insert new span before the existing destination one? */ + if (s_start < d_curr->start) { + sraSpanInsertBefore(sraSpanCreate(s_start, + d_curr->start, + s_curr->subspan), + d_curr); + sraSpanMergePrevious(d_curr); + } + + /* Split the existing span if necessary */ + if (s_end < d_curr->end) { + sraSpanInsertAfter(sraSpanCreate(s_end, + d_curr->end, + d_curr->subspan), + d_curr); + d_curr->end = s_end; + } + if (s_start > d_curr->start) { + sraSpanInsertBefore(sraSpanCreate(d_curr->start, + s_start, + d_curr->subspan), + d_curr); + d_curr->start = s_start; + } + + /* Recursively OR subspans */ + sraSpanListOr(d_curr->subspan, s_curr->subspan); + + /* Merge this span with previous or next? */ + if (d_curr->_prev != &(dest->front)) + sraSpanMergePrevious(d_curr); + if (d_curr->_next != &(dest->back)) + sraSpanMergeNext(d_curr); + + /* Move onto the next pair to compare */ + if (s_end > d_curr->end) { + s_start = d_curr->end; + d_curr = d_curr->_next; + } else { + s_curr = s_curr->_next; + s_start = s_curr->start; + s_end = s_curr->end; + } + } else { + /* - No overlap. Move to the next destination span */ + d_curr = d_curr->_next; + } + } + } +} + +rfbBool +sraSpanListAnd(sraSpanList *dest, const sraSpanList *src) { + sraSpan *d_curr, *s_curr, *d_next; + + if (!dest) { + if (!src) { + return 1; + } else { + printf("sraSpanListAnd:incompatible spans (only one NULL!)\n"); + return FALSE; + } + } + + d_curr = dest->front._next; + s_curr = src->front._next; + while ((s_curr != &(src->back)) && (d_curr != &(dest->back))) { + + /* - If we haven't reached a destination span yet then move on */ + if (d_curr->start >= s_curr->end) { + s_curr = s_curr->_next; + continue; + } + + /* - If we are beyond the current destination span then remove it */ + if (d_curr->end <= s_curr->start) { + sraSpan *next = d_curr->_next; + sraSpanRemove(d_curr); + sraSpanDestroy(d_curr); + d_curr = next; + continue; + } + + /* - If we partially overlap a span then split it up or remove bits */ + if (s_curr->start > d_curr->start) { + /* - The top bit of the span does not match */ + d_curr->start = s_curr->start; + } + if (s_curr->end < d_curr->end) { + /* - The end of the span does not match */ + sraSpanInsertAfter(sraSpanCreate(s_curr->end, + d_curr->end, + d_curr->subspan), + d_curr); + d_curr->end = s_curr->end; + } + + /* - Now recursively process the affected span */ + if (!sraSpanListAnd(d_curr->subspan, s_curr->subspan)) { + /* - The destination subspan is now empty, so we should remove it */ + sraSpan *next = d_curr->_next; + sraSpanRemove(d_curr); + sraSpanDestroy(d_curr); + d_curr = next; + } else { + /* Merge this span with previous or next? */ + if (d_curr->_prev != &(dest->front)) + sraSpanMergePrevious(d_curr); + + /* - Move on to the next span */ + d_next = d_curr; + if (s_curr->end >= d_curr->end) { + d_next = d_curr->_next; + } + if (s_curr->end <= d_curr->end) { + s_curr = s_curr->_next; + } + d_curr = d_next; + } + } + + while (d_curr != &(dest->back)) { + sraSpan *next = d_curr->_next; + sraSpanRemove(d_curr); + sraSpanDestroy(d_curr); + d_curr=next; + } + + return !sraSpanListEmpty(dest); +} + +rfbBool +sraSpanListSubtract(sraSpanList *dest, const sraSpanList *src) { + sraSpan *d_curr, *s_curr; + + if (!dest) { + if (!src) { + return 1; + } else { + printf("sraSpanListSubtract:incompatible spans (only one NULL!)\n"); + return FALSE; + } + } + + d_curr = dest->front._next; + s_curr = src->front._next; + while ((s_curr != &(src->back)) && (d_curr != &(dest->back))) { + + /* - If we haven't reached a destination span yet then move on */ + if (d_curr->start >= s_curr->end) { + s_curr = s_curr->_next; + continue; + } + + /* - If we are beyond the current destination span then skip it */ + if (d_curr->end <= s_curr->start) { + d_curr = d_curr->_next; + continue; + } + + /* - If we partially overlap the current span then split it up */ + if (s_curr->start > d_curr->start) { + sraSpanInsertBefore(sraSpanCreate(d_curr->start, + s_curr->start, + d_curr->subspan), + d_curr); + d_curr->start = s_curr->start; + } + if (s_curr->end < d_curr->end) { + sraSpanInsertAfter(sraSpanCreate(s_curr->end, + d_curr->end, + d_curr->subspan), + d_curr); + d_curr->end = s_curr->end; + } + + /* - Now recursively process the affected span */ + if ((!d_curr->subspan) || !sraSpanListSubtract(d_curr->subspan, s_curr->subspan)) { + /* - The destination subspan is now empty, so we should remove it */ + sraSpan *next = d_curr->_next; + sraSpanRemove(d_curr); + sraSpanDestroy(d_curr); + d_curr = next; + } else { + /* Merge this span with previous or next? */ + if (d_curr->_prev != &(dest->front)) + sraSpanMergePrevious(d_curr); + if (d_curr->_next != &(dest->back)) + sraSpanMergeNext(d_curr); + + /* - Move on to the next span */ + if (s_curr->end > d_curr->end) { + d_curr = d_curr->_next; + } else { + s_curr = s_curr->_next; + } + } + } + + return !sraSpanListEmpty(dest); +} + +/* -=- Region routines */ + +sraRegion * +sraRgnCreate() { + return (sraRegion*)sraSpanListCreate(); +} + +sraRegion * +sraRgnCreateRect(int x1, int y1, int x2, int y2) { + sraSpanList *vlist, *hlist; + sraSpan *vspan, *hspan; + + /* - Build the horizontal portion of the span */ + hlist = sraSpanListCreate(); + hspan = sraSpanCreate(x1, x2, NULL); + sraSpanInsertAfter(hspan, &(hlist->front)); + + /* - Build the vertical portion of the span */ + vlist = sraSpanListCreate(); + vspan = sraSpanCreate(y1, y2, hlist); + sraSpanInsertAfter(vspan, &(vlist->front)); + + sraSpanListDestroy(hlist); + + return (sraRegion*)vlist; +} + +sraRegion * +sraRgnCreateRgn(const sraRegion *src) { + return (sraRegion*)sraSpanListDup((sraSpanList*)src); +} + +void +sraRgnDestroy(sraRegion *rgn) { + sraSpanListDestroy((sraSpanList*)rgn); +} + +void +sraRgnMakeEmpty(sraRegion *rgn) { + sraSpanListMakeEmpty((sraSpanList*)rgn); +} + +/* -=- Boolean Region ops */ + +rfbBool +sraRgnAnd(sraRegion *dst, const sraRegion *src) { + return sraSpanListAnd((sraSpanList*)dst, (sraSpanList*)src); +} + +void +sraRgnOr(sraRegion *dst, const sraRegion *src) { + sraSpanListOr((sraSpanList*)dst, (sraSpanList*)src); +} + +rfbBool +sraRgnSubtract(sraRegion *dst, const sraRegion *src) { + return sraSpanListSubtract((sraSpanList*)dst, (sraSpanList*)src); +} + +void +sraRgnOffset(sraRegion *dst, int dx, int dy) { + sraSpan *vcurr, *hcurr; + + vcurr = ((sraSpanList*)dst)->front._next; + while (vcurr != &(((sraSpanList*)dst)->back)) { + vcurr->start += dy; + vcurr->end += dy; + + hcurr = vcurr->subspan->front._next; + while (hcurr != &(vcurr->subspan->back)) { + hcurr->start += dx; + hcurr->end += dx; + hcurr = hcurr->_next; + } + + vcurr = vcurr->_next; + } +} + +sraRegion *sraRgnBBox(const sraRegion *src) { + int xmin=((unsigned int)(int)-1)>>1,ymin=xmin,xmax=1-xmin,ymax=xmax; + sraSpan *vcurr, *hcurr; + + if(!src) + return sraRgnCreate(); + + vcurr = ((sraSpanList*)src)->front._next; + while (vcurr != &(((sraSpanList*)src)->back)) { + if(vcurr->startstart; + if(vcurr->end>ymax) + ymax=vcurr->end; + + hcurr = vcurr->subspan->front._next; + while (hcurr != &(vcurr->subspan->back)) { + if(hcurr->startstart; + if(hcurr->end>xmax) + xmax=hcurr->end; + hcurr = hcurr->_next; + } + + vcurr = vcurr->_next; + } + + if(xmaxback._prev; + vend = &(((sraSpanList*)rgn)->front); + } else { + vcurr = ((sraSpanList*)rgn)->front._next; + vend = &(((sraSpanList*)rgn)->back); + } + + if (vcurr != vend) { + rect->y1 = vcurr->start; + rect->y2 = vcurr->end; + + /* - Pick correct order */ + if (right2left) { + hcurr = vcurr->subspan->back._prev; + hend = &(vcurr->subspan->front); + } else { + hcurr = vcurr->subspan->front._next; + hend = &(vcurr->subspan->back); + } + + if (hcurr != hend) { + rect->x1 = hcurr->start; + rect->x2 = hcurr->end; + + sraSpanRemove(hcurr); + sraSpanDestroy(hcurr); + + if (sraSpanListEmpty(vcurr->subspan)) { + sraSpanRemove(vcurr); + sraSpanDestroy(vcurr); + } + +#if 0 + printf("poprect:(%dx%d)-(%dx%d)\n", + rect->x1, rect->y1, rect->x2, rect->y2); +#endif + return 1; + } + } + + return 0; +} + +unsigned long +sraRgnCountRects(const sraRegion *rgn) { + unsigned long count = sraSpanListCount((sraSpanList*)rgn); + return count; +} + +rfbBool +sraRgnEmpty(const sraRegion *rgn) { + return sraSpanListEmpty((sraSpanList*)rgn); +} + +/* iterator stuff */ +sraRectangleIterator *sraRgnGetIterator(sraRegion *s) +{ + /* these values have to be multiples of 4 */ +#define DEFSIZE 4 +#define DEFSTEP 8 + sraRectangleIterator *i = + (sraRectangleIterator*)malloc(sizeof(sraRectangleIterator)); + if(!i) + return(0); + + /* we have to recurse eventually. So, the first sPtr is the pointer to + the sraSpan in the first level. the second sPtr is the pointer to + the sraRegion.back. The third and fourth sPtr are for the second + recursion level and so on. */ + i->sPtrs = (sraSpan**)malloc(sizeof(sraSpan*)*DEFSIZE); + if(!i->sPtrs) { + free(i); + return(0); + } + i->ptrSize = DEFSIZE; + i->sPtrs[0] = &(s->front); + i->sPtrs[1] = &(s->back); + i->ptrPos = 0; + i->reverseX = 0; + i->reverseY = 0; + return(i); +} + +sraRectangleIterator *sraRgnGetReverseIterator(sraRegion *s,rfbBool reverseX,rfbBool reverseY) +{ + sraRectangleIterator *i = sraRgnGetIterator(s); + if(reverseY) { + i->sPtrs[1] = &(s->front); + i->sPtrs[0] = &(s->back); + } + i->reverseX = reverseX; + i->reverseY = reverseY; + return(i); +} + +rfbBool sraReverse(sraRectangleIterator *i) +{ + return( ((i->ptrPos&2) && i->reverseX) || + (!(i->ptrPos&2) && i->reverseY)); +} + +sraSpan* sraNextSpan(sraRectangleIterator *i) +{ + if(sraReverse(i)) + return(i->sPtrs[i->ptrPos]->_prev); + else + return(i->sPtrs[i->ptrPos]->_next); +} + +rfbBool sraRgnIteratorNext(sraRectangleIterator* i,sraRect* r) +{ + /* is the subspan finished? */ + while(sraNextSpan(i) == i->sPtrs[i->ptrPos+1]) { + i->ptrPos -= 2; + if(i->ptrPos < 0) /* the end */ + return(0); + } + + i->sPtrs[i->ptrPos] = sraNextSpan(i); + + /* is this a new subspan? */ + while(i->sPtrs[i->ptrPos]->subspan) { + if(i->ptrPos+2 > i->ptrSize) { /* array is too small */ + i->ptrSize += DEFSTEP; + i->sPtrs = (sraSpan**)realloc(i->sPtrs, sizeof(sraSpan*)*i->ptrSize); + } + i->ptrPos =+ 2; + if(sraReverse(i)) { + i->sPtrs[i->ptrPos] = i->sPtrs[i->ptrPos-2]->subspan->back._prev; + i->sPtrs[i->ptrPos+1] = &(i->sPtrs[i->ptrPos-2]->subspan->front); + } else { + i->sPtrs[i->ptrPos] = i->sPtrs[i->ptrPos-2]->subspan->front._next; + i->sPtrs[i->ptrPos+1] = &(i->sPtrs[i->ptrPos-2]->subspan->back); + } + } + + if((i->ptrPos%4)!=2) { + rfbErr("sraRgnIteratorNext: offset is wrong (%d%%4!=2)\n",i->ptrPos); + return FALSE; + } + + r->y1 = i->sPtrs[i->ptrPos-2]->start; + r->y2 = i->sPtrs[i->ptrPos-2]->end; + r->x1 = i->sPtrs[i->ptrPos]->start; + r->x2 = i->sPtrs[i->ptrPos]->end; + + return(-1); +} + +void sraRgnReleaseIterator(sraRectangleIterator* i) +{ + free(i->sPtrs); + free(i); +} + +void +sraRgnPrint(const sraRegion *rgn) { + sraSpanListPrint((sraSpanList*)rgn); +} + +rfbBool +sraClipRect(int *x, int *y, int *w, int *h, + int cx, int cy, int cw, int ch) { + if (*x < cx) { + *w -= (cx-*x); + *x = cx; + } + if (*y < cy) { + *h -= (cy-*y); + *y = cy; + } + if (*x+*w > cx+cw) { + *w = (cx+cw)-*x; + } + if (*y+*h > cy+ch) { + *h = (cy+ch)-*y; + } + return (*w>0) && (*h>0); +} + +/* test */ + +#ifdef SRA_TEST +/* pipe the output to sort|uniq -u and you'll get the errors. */ +int main(int argc, char** argv) +{ + sraRegionPtr region, region1, region2; + sraRectangleIterator* i; + sraRect rect; + rfbBool b; + + region = sraRgnCreateRect(10, 10, 600, 300); + region1 = sraRgnCreateRect(40, 50, 350, 200); + region2 = sraRgnCreateRect(0, 0, 20, 40); + + sraRgnPrint(region); + printf("\n[(10-300)[(10-600)]]\n\n"); + + b = sraRgnSubtract(region, region1); + printf("%s ",b?"true":"false"); + sraRgnPrint(region); + printf("\ntrue [(10-50)[(10-600)](50-200)[(10-40)(350-600)](200-300)[(10-600)]]\n\n"); + + sraRgnOr(region, region2); + printf("%ld\n6\n\n", sraRgnCountRects(region)); + + i = sraRgnGetIterator(region); + while(sraRgnIteratorNext(i, &rect)) + printf("%dx%d+%d+%d ", + rect.x2-rect.x1,rect.y2-rect.y1, + rect.x1,rect.y1); + sraRgnReleaseIterator(i); + printf("\n20x10+0+0 600x30+0+10 590x10+10+40 30x150+10+50 250x150+350+50 590x100+10+200 \n\n"); + + i = sraRgnGetReverseIterator(region,1,0); + while(sraRgnIteratorNext(i, &rect)) + printf("%dx%d+%d+%d ", + rect.x2-rect.x1,rect.y2-rect.y1, + rect.x1,rect.y1); + sraRgnReleaseIterator(i); + printf("\n20x10+0+0 600x30+0+10 590x10+10+40 250x150+350+50 30x150+10+50 590x100+10+200 \n\n"); + + i = sraRgnGetReverseIterator(region,1,1); + while(sraRgnIteratorNext(i, &rect)) + printf("%dx%d+%d+%d ", + rect.x2-rect.x1,rect.y2-rect.y1, + rect.x1,rect.y1); + sraRgnReleaseIterator(i); + printf("\n590x100+10+200 250x150+350+50 30x150+10+50 590x10+10+40 600x30+0+10 20x10+0+0 \n\n"); + + sraRgnDestroy(region); + sraRgnDestroy(region1); + sraRgnDestroy(region2); + + return(0); +} +#endif diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c new file mode 100644 index 0000000..e22283e --- /dev/null +++ b/libvncserver/rfbserver.c @@ -0,0 +1,1808 @@ +/* + * rfbserver.c - deal with server-side of the RFB protocol. + */ + +/* + * Copyright (C) 2002 RealVNC Ltd. + * OSXvnc Copyright (C) 2001 Dan McGuirk . + * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. + * 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. + */ + +#include +#include +#include + +#ifdef LIBVNCSERVER_HAVE_FCNTL_H +#include +#endif + +#ifdef WIN32 +#define write(sock,buf,len) send(sock,buf,len,0) +#else +#ifdef LIBVNCSERVER_HAVE_UNISTD_H +#include +#endif +#include +#ifdef LIBVNCSERVER_HAVE_SYS_SOCKET_H +#include +#endif +#ifdef LIBVNCSERVER_HAVE_NETINET_IN_H +#include +#include +#include +#endif +#endif + +#ifdef CORBA +#include +#endif + +#ifdef DEBUGPROTO +#undef DEBUGPROTO +#define DEBUGPROTO(x) x +#else +#define DEBUGPROTO(x) +#endif + +rfbClientPtr pointerClient = NULL; /* Mutex for pointer events */ + +static void rfbProcessClientProtocolVersion(rfbClientPtr cl); +static void rfbProcessClientNormalMessage(rfbClientPtr cl); +static void rfbProcessClientInitMessage(rfbClientPtr cl); + +#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD +void rfbIncrClientRef(rfbClientPtr cl) +{ + LOCK(cl->refCountMutex); + cl->refCount++; + UNLOCK(cl->refCountMutex); +} + +void rfbDecrClientRef(rfbClientPtr cl) +{ + LOCK(cl->refCountMutex); + cl->refCount--; + if(cl->refCount<=0) /* just to be sure also < 0 */ + TSIGNAL(cl->deleteCond); + UNLOCK(cl->refCountMutex); +} +#else +void rfbIncrClientRef(rfbClientPtr cl) {} +void rfbDecrClientRef(rfbClientPtr cl) {} +#endif + +#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD +MUTEX(rfbClientListMutex); +#endif + +struct rfbClientIterator { + rfbClientPtr next; + rfbScreenInfoPtr screen; +}; + +void +rfbClientListInit(rfbScreenInfoPtr rfbScreen) +{ + if(sizeof(rfbBool)!=1) { + /* a sanity check */ + fprintf(stderr,"rfbBool's size is not 1 (%d)!\n",sizeof(rfbBool)); + /* we cannot continue, because rfbBool is supposed to be char everywhere */ + exit(1); + } + rfbScreen->rfbClientHead = NULL; + INIT_MUTEX(rfbClientListMutex); +} + +rfbClientIteratorPtr +rfbGetClientIterator(rfbScreenInfoPtr rfbScreen) +{ + rfbClientIteratorPtr i = + (rfbClientIteratorPtr)malloc(sizeof(struct rfbClientIterator)); + i->next = 0; + i->screen = rfbScreen; + return i; +} + +rfbClientPtr +rfbClientIteratorHead(rfbClientIteratorPtr i) +{ +#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD + if(i->next != 0) { + rfbDecrClientRef(i->next); + rfbIncrClientRef(i->screen->rfbClientHead); + } +#endif + LOCK(rfbClientListMutex); + i->next = i->screen->rfbClientHead; + UNLOCK(rfbClientListMutex); + return i->next; +} + +rfbClientPtr +rfbClientIteratorNext(rfbClientIteratorPtr i) +{ + if(i->next == 0) { + LOCK(rfbClientListMutex); + i->next = i->screen->rfbClientHead; + UNLOCK(rfbClientListMutex); + } else { + IF_PTHREADS(rfbClientPtr cl = i->next); + i->next = i->next->next; + IF_PTHREADS(rfbDecrClientRef(cl)); + } + +#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD + while(i->next && i->next->sock<0) + i->next = i->next->next; + if(i->next) + rfbIncrClientRef(i->next); +#endif + + return i->next; +} + +void +rfbReleaseClientIterator(rfbClientIteratorPtr iterator) +{ + IF_PTHREADS(if(iterator->next) rfbDecrClientRef(iterator->next)); + free(iterator); +} + + +/* + * rfbNewClientConnection is called from sockets.c when a new connection + * comes in. + */ + +void +rfbNewClientConnection(rfbScreen,sock) + rfbScreenInfoPtr rfbScreen; + int sock; +{ + rfbClientPtr cl; + + cl = rfbNewClient(rfbScreen,sock); +#ifdef CORBA + if(cl!=NULL) + newConnection(cl, (KEYBOARD_DEVICE|POINTER_DEVICE),1,1,1); +#endif +} + + +/* + * rfbReverseConnection is called by the CORBA stuff to make an outward + * connection to a "listening" RFB client. + */ + +rfbClientPtr +rfbReverseConnection(rfbScreen,host, port) + rfbScreenInfoPtr rfbScreen; + char *host; + int port; +{ + int sock; + rfbClientPtr cl; + + if ((sock = rfbConnect(rfbScreen, host, port)) < 0) + return (rfbClientPtr)NULL; + + cl = rfbNewClient(rfbScreen, sock); + + if (cl) { + cl->reverseConnection = TRUE; + } + + return cl; +} + + +/* + * rfbNewClient is called when a new connection has been made by whatever + * means. + */ + +rfbClientPtr +rfbNewTCPOrUDPClient(rfbScreen,sock,isUDP) + rfbScreenInfoPtr rfbScreen; + int sock; + rfbBool isUDP; +{ + rfbProtocolVersionMsg pv; + rfbClientIteratorPtr iterator; + rfbClientPtr cl,cl_; + struct sockaddr_in addr; + size_t addrlen = sizeof(struct sockaddr_in); + + cl = (rfbClientPtr)calloc(sizeof(rfbClientRec),1); + + cl->screen = rfbScreen; + cl->sock = sock; + cl->viewOnly = FALSE; + + rfbResetStats(cl); + + if(isUDP) { + rfbLog(" accepted UDP client\n"); + } else { + int one=1; + + getpeername(sock, (struct sockaddr *)&addr, &addrlen); + cl->host = strdup(inet_ntoa(addr.sin_addr)); + + rfbLog(" other clients:\n"); + iterator = rfbGetClientIterator(rfbScreen); + while ((cl_ = rfbClientIteratorNext(iterator)) != NULL) { + rfbLog(" %s\n",cl_->host); + } + rfbReleaseClientIterator(iterator); + +#ifndef WIN32 + if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { + rfbLogPerror("fcntl failed"); + close(sock); + return NULL; + } +#endif + + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + (char *)&one, sizeof(one)) < 0) { + rfbLogPerror("setsockopt failed"); + close(sock); + return NULL; + } + + FD_SET(sock,&(rfbScreen->allFds)); + rfbScreen->maxFd = max(sock,rfbScreen->maxFd); + + INIT_MUTEX(cl->outputMutex); + INIT_MUTEX(cl->refCountMutex); + INIT_COND(cl->deleteCond); + + cl->state = RFB_PROTOCOL_VERSION; + + cl->reverseConnection = FALSE; + cl->readyForSetColourMapEntries = FALSE; + cl->useCopyRect = FALSE; + cl->preferredEncoding = rfbEncodingRaw; + cl->correMaxWidth = 48; + cl->correMaxHeight = 48; +#ifdef LIBVNCSERVER_HAVE_LIBZ + cl->zrleData = 0; +#endif + + cl->copyRegion = sraRgnCreate(); + cl->copyDX = 0; + cl->copyDY = 0; + + cl->modifiedRegion = + sraRgnCreateRect(0,0,rfbScreen->width,rfbScreen->height); + + INIT_MUTEX(cl->updateMutex); + INIT_COND(cl->updateCond); + + cl->requestedRegion = sraRgnCreate(); + + cl->format = cl->screen->rfbServerFormat; + cl->translateFn = rfbTranslateNone; + cl->translateLookupTable = NULL; + + LOCK(rfbClientListMutex); + + IF_PTHREADS(cl->refCount = 0); + cl->next = rfbScreen->rfbClientHead; + cl->prev = NULL; + if (rfbScreen->rfbClientHead) + rfbScreen->rfbClientHead->prev = cl; + + rfbScreen->rfbClientHead = cl; + UNLOCK(rfbClientListMutex); + +#ifdef LIBVNCSERVER_HAVE_LIBJPEG + cl->tightCompressLevel = TIGHT_DEFAULT_COMPRESSION; + cl->tightQualityLevel = -1; + { + int i; + for (i = 0; i < 4; i++) + cl->zsActive[i] = FALSE; + } +#endif + + cl->enableCursorShapeUpdates = FALSE; + cl->enableCursorPosUpdates = FALSE; + cl->useRichCursorEncoding = FALSE; + cl->enableLastRectEncoding = FALSE; + cl->useNewFBSize = FALSE; + +#ifdef LIBVNCSERVER_HAVE_LIBZ + cl->compStreamInited = FALSE; + cl->compStream.total_in = 0; + cl->compStream.total_out = 0; + cl->compStream.zalloc = Z_NULL; + cl->compStream.zfree = Z_NULL; + cl->compStream.opaque = Z_NULL; + + cl->zlibCompressLevel = 5; +#endif + + cl->progressiveSliceY = 0; + + sprintf(pv,rfbProtocolVersionFormat,rfbProtocolMajorVersion, + rfbProtocolMinorVersion); + + if (WriteExact(cl, pv, sz_rfbProtocolVersionMsg) < 0) { + rfbLogPerror("rfbNewClient: write"); + rfbCloseClient(cl); + /* TODO: memory leak here (cl is never freed) + * can rfbClientConnectionGone called at this time? + * tim@tjansen.de + */ + return NULL; + } + } + + cl->clientData = NULL; + cl->clientGoneHook = doNothingWithClient; + switch (cl->screen->newClientHook(cl)) { + case RFB_CLIENT_ON_HOLD: + cl->onHold = TRUE; + break; + case RFB_CLIENT_ACCEPT: + cl->onHold = FALSE; + break; + case RFB_CLIENT_REFUSE: + rfbCloseClient(cl); + rfbClientConnectionGone(cl); + cl = NULL; + break; + } + return cl; +} + +rfbClientPtr +rfbNewClient(rfbScreen,sock) + rfbScreenInfoPtr rfbScreen; + int sock; +{ + return(rfbNewTCPOrUDPClient(rfbScreen,sock,FALSE)); +} + +rfbClientPtr +rfbNewUDPClient(rfbScreen) + rfbScreenInfoPtr rfbScreen; +{ + return((rfbScreen->udpClient= + rfbNewTCPOrUDPClient(rfbScreen,rfbScreen->udpSock,TRUE))); +} + +/* + * rfbClientConnectionGone is called from sockets.c just after a connection + * has gone away. + */ + +void +rfbClientConnectionGone(cl) + rfbClientPtr cl; +{ + int i; + + LOCK(rfbClientListMutex); + + if (cl->prev) + cl->prev->next = cl->next; + else + cl->screen->rfbClientHead = cl->next; + if (cl->next) + cl->next->prev = cl->prev; + +#ifdef LIBVNCSERVER_HAVE_LIBZ + FreeZrleData(cl); +#endif + +#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD + if(cl->screen->backgroundLoop != FALSE) + do { + LOCK(cl->refCountMutex); + i=cl->refCount; + UNLOCK(cl->refCountMutex); + if(i>0) + WAIT(cl->deleteCond,cl->refCountMutex); + } while(i>0); +#endif + + if(cl->sock>=0) + FD_CLR(cl->sock,&(cl->screen->allFds)); + + cl->clientGoneHook(cl); + + rfbLog("Client %s gone\n",cl->host); + free(cl->host); + +#ifdef LIBVNCSERVER_HAVE_LIBZ + /* Release the compression state structures if any. */ + if ( cl->compStreamInited ) { + deflateEnd( &(cl->compStream) ); + } + +#ifdef LIBVNCSERVER_HAVE_LIBJPEG + for (i = 0; i < 4; i++) { + if (cl->zsActive[i]) + deflateEnd(&cl->zsStruct[i]); + } +#endif +#endif + + if (pointerClient == cl) + pointerClient = NULL; + + sraRgnDestroy(cl->modifiedRegion); + sraRgnDestroy(cl->requestedRegion); + sraRgnDestroy(cl->copyRegion); + + UNLOCK(rfbClientListMutex); + + if (cl->translateLookupTable) free(cl->translateLookupTable); + + TINI_COND(cl->updateCond); + TINI_MUTEX(cl->updateMutex); + + LOCK(cl->outputMutex); + TINI_MUTEX(cl->outputMutex); + +#ifdef CORBA + destroyConnection(cl); +#endif + + rfbPrintStats(cl); + + free(cl); +} + + +/* + * rfbProcessClientMessage is called when there is data to read from a client. + */ + +void +rfbProcessClientMessage(cl) + rfbClientPtr cl; +{ + switch (cl->state) { + case RFB_PROTOCOL_VERSION: + rfbProcessClientProtocolVersion(cl); + return; + case RFB_AUTHENTICATION: + rfbAuthProcessClientMessage(cl); + return; + case RFB_INITIALISATION: + rfbProcessClientInitMessage(cl); + return; + default: + rfbProcessClientNormalMessage(cl); + return; + } +} + + +/* + * rfbProcessClientProtocolVersion is called when the client sends its + * protocol version. + */ + +static void +rfbProcessClientProtocolVersion(cl) + rfbClientPtr cl; +{ + rfbProtocolVersionMsg pv; + int n, major_, minor_; + char failureReason[256]; + + if ((n = ReadExact(cl, pv, sz_rfbProtocolVersionMsg)) <= 0) { + if (n == 0) + rfbLog("rfbProcessClientProtocolVersion: client gone\n"); + else + rfbLogPerror("rfbProcessClientProtocolVersion: read"); + rfbCloseClient(cl); + return; + } + + pv[sz_rfbProtocolVersionMsg] = 0; + if (sscanf(pv,rfbProtocolVersionFormat,&major_,&minor_) != 2) { + char name[1024]; + if(sscanf(pv,"RFB %03d.%03d %1024s\n",&major_,&minor_,name) != 3) { + rfbErr("rfbProcessClientProtocolVersion: not a valid RFB client\n"); + rfbCloseClient(cl); + return; + } + free(cl->host); + cl->host=strdup(name); + } + rfbLog("Protocol version %d.%d\n", major_, minor_); + + if (major_ != rfbProtocolMajorVersion) { + /* Major version mismatch - send a ConnFailed message */ + + rfbErr("Major version mismatch\n"); + sprintf(failureReason, + "RFB protocol version mismatch - server %d.%d, client %d.%d", + rfbProtocolMajorVersion,rfbProtocolMinorVersion,major_,minor_); + rfbClientConnFailed(cl, failureReason); + return; + } + + if (minor_ != rfbProtocolMinorVersion) { + /* Minor version mismatch - warn but try to continue */ + rfbLog("Ignoring minor version mismatch\n"); + } + + rfbAuthNewClient(cl); +} + + +/* + * rfbClientConnFailed is called when a client connection has failed either + * because it talks the wrong protocol or it has failed authentication. + */ + +void +rfbClientConnFailed(cl, reason) + rfbClientPtr cl; + char *reason; +{ + char *buf; + int len = strlen(reason); + + buf = (char *)malloc(8 + len); + ((uint32_t *)buf)[0] = Swap32IfLE(rfbConnFailed); + ((uint32_t *)buf)[1] = Swap32IfLE(len); + memcpy(buf + 8, reason, len); + + if (WriteExact(cl, buf, 8 + len) < 0) + rfbLogPerror("rfbClientConnFailed: write"); + free(buf); + rfbCloseClient(cl); +} + + +/* + * rfbProcessClientInitMessage is called when the client sends its + * initialisation message. + */ + +static void +rfbProcessClientInitMessage(cl) + rfbClientPtr cl; +{ + rfbClientInitMsg ci; + char buf[256]; + rfbServerInitMsg *si = (rfbServerInitMsg *)buf; + int len, n; + rfbClientIteratorPtr iterator; + rfbClientPtr otherCl; + + if ((n = ReadExact(cl, (char *)&ci,sz_rfbClientInitMsg)) <= 0) { + if (n == 0) + rfbLog("rfbProcessClientInitMessage: client gone\n"); + else + rfbLogPerror("rfbProcessClientInitMessage: read"); + rfbCloseClient(cl); + return; + } + + si->framebufferWidth = Swap16IfLE(cl->screen->width); + si->framebufferHeight = Swap16IfLE(cl->screen->height); + si->format = cl->screen->rfbServerFormat; + si->format.redMax = Swap16IfLE(si->format.redMax); + si->format.greenMax = Swap16IfLE(si->format.greenMax); + si->format.blueMax = Swap16IfLE(si->format.blueMax); + + if (strlen(cl->screen->desktopName) > 128) /* sanity check on desktop name len */ + ((char*)cl->screen->desktopName)[128] = 0; + + strcpy(buf + sz_rfbServerInitMsg, cl->screen->desktopName); + len = strlen(buf + sz_rfbServerInitMsg); + si->nameLength = Swap32IfLE(len); + + if (WriteExact(cl, buf, sz_rfbServerInitMsg + len) < 0) { + rfbLogPerror("rfbProcessClientInitMessage: write"); + rfbCloseClient(cl); + return; + } + + cl->state = RFB_NORMAL; + + if (!cl->reverseConnection && + (cl->screen->rfbNeverShared || (!cl->screen->rfbAlwaysShared && !ci.shared))) { + + if (cl->screen->rfbDontDisconnect) { + iterator = rfbGetClientIterator(cl->screen); + while ((otherCl = rfbClientIteratorNext(iterator)) != NULL) { + if ((otherCl != cl) && (otherCl->state == RFB_NORMAL)) { + rfbLog("-dontdisconnect: Not shared & existing client\n"); + rfbLog(" refusing new client %s\n", cl->host); + rfbCloseClient(cl); + rfbReleaseClientIterator(iterator); + return; + } + } + rfbReleaseClientIterator(iterator); + } else { + iterator = rfbGetClientIterator(cl->screen); + while ((otherCl = rfbClientIteratorNext(iterator)) != NULL) { + if ((otherCl != cl) && (otherCl->state == RFB_NORMAL)) { + rfbLog("Not shared - closing connection to client %s\n", + otherCl->host); + rfbCloseClient(otherCl); + } + } + rfbReleaseClientIterator(iterator); + } + } +} + +static rfbBool rectSwapIfLEAndClip(uint16_t* x,uint16_t* y,uint16_t* w,uint16_t* h, + rfbScreenInfoPtr screen) +{ + *x=Swap16IfLE(*x); + *y=Swap16IfLE(*y); + *w=Swap16IfLE(*w); + *h=Swap16IfLE(*h); + if(*w>screen->width-*x) + *w=screen->width-*x; + /* possible underflow */ + if(*w>screen->width-*x) + return FALSE; + if(*h>screen->height-*y) + *h=screen->height-*y; + if(*h>screen->height-*y) + return FALSE; + + return TRUE; +} + +/* + * rfbProcessClientNormalMessage is called when the client has sent a normal + * protocol message. + */ + +static void +rfbProcessClientNormalMessage(cl) + rfbClientPtr cl; +{ + int n=0; + rfbClientToServerMsg msg; + char *str; + + if ((n = ReadExact(cl, (char *)&msg, 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + + switch (msg.type) { + + case rfbSetPixelFormat: + + if ((n = ReadExact(cl, ((char *)&msg) + 1, + sz_rfbSetPixelFormatMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + + cl->format.bitsPerPixel = msg.spf.format.bitsPerPixel; + cl->format.depth = msg.spf.format.depth; + cl->format.bigEndian = (msg.spf.format.bigEndian ? TRUE : FALSE); + cl->format.trueColour = (msg.spf.format.trueColour ? TRUE : FALSE); + cl->format.redMax = Swap16IfLE(msg.spf.format.redMax); + cl->format.greenMax = Swap16IfLE(msg.spf.format.greenMax); + cl->format.blueMax = Swap16IfLE(msg.spf.format.blueMax); + cl->format.redShift = msg.spf.format.redShift; + cl->format.greenShift = msg.spf.format.greenShift; + cl->format.blueShift = msg.spf.format.blueShift; + + cl->readyForSetColourMapEntries = TRUE; + cl->screen->setTranslateFunction(cl); + + return; + + + case rfbFixColourMapEntries: + if ((n = ReadExact(cl, ((char *)&msg) + 1, + sz_rfbFixColourMapEntriesMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + rfbLog("rfbProcessClientNormalMessage: %s", + "FixColourMapEntries unsupported\n"); + rfbCloseClient(cl); + return; + + + case rfbSetEncodings: + { + int i; + uint32_t enc; + + if ((n = ReadExact(cl, ((char *)&msg) + 1, + sz_rfbSetEncodingsMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + + msg.se.nEncodings = Swap16IfLE(msg.se.nEncodings); + + cl->preferredEncoding = -1; + cl->useCopyRect = FALSE; + cl->enableCursorShapeUpdates = FALSE; + cl->enableCursorPosUpdates = FALSE; + cl->enableLastRectEncoding = FALSE; + cl->useNewFBSize = FALSE; + + for (i = 0; i < msg.se.nEncodings; i++) { + if ((n = ReadExact(cl, (char *)&enc, 4)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + enc = Swap32IfLE(enc); + + switch (enc) { + + case rfbEncodingCopyRect: + cl->useCopyRect = TRUE; + break; + case rfbEncodingRaw: + if (cl->preferredEncoding == -1) { + cl->preferredEncoding = enc; + rfbLog("Using raw encoding for client %s\n", + cl->host); + } + break; + case rfbEncodingRRE: + if (cl->preferredEncoding == -1) { + cl->preferredEncoding = enc; + rfbLog("Using rre encoding for client %s\n", + cl->host); + } + break; + case rfbEncodingCoRRE: + if (cl->preferredEncoding == -1) { + cl->preferredEncoding = enc; + rfbLog("Using CoRRE encoding for client %s\n", + cl->host); + } + break; + case rfbEncodingHextile: + if (cl->preferredEncoding == -1) { + cl->preferredEncoding = enc; + rfbLog("Using hextile encoding for client %s\n", + cl->host); + } + break; +#ifdef LIBVNCSERVER_HAVE_LIBZ + case rfbEncodingZlib: + if (cl->preferredEncoding == -1) { + cl->preferredEncoding = enc; + rfbLog("Using zlib encoding for client %s\n", + cl->host); + } + break; +#ifdef LIBVNCSERVER_HAVE_LIBJPEG + case rfbEncodingTight: + if (cl->preferredEncoding == -1) { + cl->preferredEncoding = enc; + rfbLog("Using tight encoding for client %s\n", + cl->host); + } + break; +#endif +#endif + case rfbEncodingXCursor: + if(!cl->screen->dontConvertRichCursorToXCursor) { + rfbLog("Enabling X-style cursor updates for client %s\n", + cl->host); + cl->enableCursorShapeUpdates = TRUE; + cl->cursorWasChanged = TRUE; + } + break; + case rfbEncodingRichCursor: + rfbLog("Enabling full-color cursor updates for client %s\n", + cl->host); + cl->enableCursorShapeUpdates = TRUE; + cl->useRichCursorEncoding = TRUE; + cl->cursorWasChanged = TRUE; + break; + case rfbEncodingPointerPos: + if (!cl->enableCursorPosUpdates) { + rfbLog("Enabling cursor position updates for client %s\n", + cl->host); + cl->enableCursorPosUpdates = TRUE; + cl->cursorWasMoved = TRUE; + } + break; + case rfbEncodingLastRect: + if (!cl->enableLastRectEncoding) { + rfbLog("Enabling LastRect protocol extension for client " + "%s\n", cl->host); + cl->enableLastRectEncoding = TRUE; + } + break; + case rfbEncodingNewFBSize: + if (!cl->useNewFBSize) { + rfbLog("Enabling NewFBSize protocol extension for client " + "%s\n", cl->host); + cl->useNewFBSize = TRUE; + } + break; +#ifdef LIBVNCSERVER_BACKCHANNEL + case rfbEncodingBackChannel: + if (!cl->enableBackChannel) { + rfbLog("Enabling BackChannel protocol extension for " + "client %s\n", cl->host); + cl->enableBackChannel = TRUE; + } + break; +#endif +#ifdef LIBVNCSERVER_HAVE_LIBZ + case rfbEncodingZRLE: + if (cl->preferredEncoding == -1) { + cl->preferredEncoding = enc; + rfbLog("Using ZRLE encoding for client %s\n", + cl->host); + } + break; +#endif + default: +#ifdef LIBVNCSERVER_HAVE_LIBZ + if ( enc >= (uint32_t)rfbEncodingCompressLevel0 && + enc <= (uint32_t)rfbEncodingCompressLevel9 ) { + cl->zlibCompressLevel = enc & 0x0F; +#ifdef LIBVNCSERVER_HAVE_LIBJPEG + cl->tightCompressLevel = enc & 0x0F; + rfbLog("Using compression level %d for client %s\n", + cl->tightCompressLevel, cl->host); + } else if ( enc >= (uint32_t)rfbEncodingQualityLevel0 && + enc <= (uint32_t)rfbEncodingQualityLevel9 ) { + cl->tightQualityLevel = enc & 0x0F; + rfbLog("Using image quality level %d for client %s\n", + cl->tightQualityLevel, cl->host); +#endif + } else +#endif + rfbLog("rfbProcessClientNormalMessage: ignoring unknown " + "encoding type %d\n", (int)enc); + } + } + + if (cl->preferredEncoding == -1) { + cl->preferredEncoding = rfbEncodingRaw; + } + + if (cl->enableCursorPosUpdates && !cl->enableCursorShapeUpdates) { + rfbLog("Disabling cursor position updates for client %s\n", + cl->host); + cl->enableCursorPosUpdates = FALSE; + } + + return; + } + + + case rfbFramebufferUpdateRequest: + { + sraRegionPtr tmpRegion; + + if ((n = ReadExact(cl, ((char *)&msg) + 1, + sz_rfbFramebufferUpdateRequestMsg-1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + + if(!rectSwapIfLEAndClip(&msg.fur.x,&msg.fur.y,&msg.fur.w,&msg.fur.h, + cl->screen)) + return; + + tmpRegion = + sraRgnCreateRect(msg.fur.x, + msg.fur.y, + msg.fur.x+msg.fur.w, + msg.fur.y+msg.fur.h); + + LOCK(cl->updateMutex); + sraRgnOr(cl->requestedRegion,tmpRegion); + + if (!cl->readyForSetColourMapEntries) { + /* client hasn't sent a SetPixelFormat so is using server's */ + cl->readyForSetColourMapEntries = TRUE; + if (!cl->format.trueColour) { + if (!rfbSetClientColourMap(cl, 0, 0)) { + sraRgnDestroy(tmpRegion); + UNLOCK(cl->updateMutex); + return; + } + } + } + + if (!msg.fur.incremental) { + sraRgnOr(cl->modifiedRegion,tmpRegion); + sraRgnSubtract(cl->copyRegion,tmpRegion); + } + TSIGNAL(cl->updateCond); + UNLOCK(cl->updateMutex); + + sraRgnDestroy(tmpRegion); + + return; + } + + case rfbKeyEvent: + + cl->rfbKeyEventsRcvd++; + + if ((n = ReadExact(cl, ((char *)&msg) + 1, + sz_rfbKeyEventMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + + if(!cl->viewOnly) { + cl->screen->kbdAddEvent(msg.ke.down, (rfbKeySym)Swap32IfLE(msg.ke.key), cl); + } + + return; + + + case rfbPointerEvent: + + cl->rfbPointerEventsRcvd++; + + if ((n = ReadExact(cl, ((char *)&msg) + 1, + sz_rfbPointerEventMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + + if (pointerClient && (pointerClient != cl)) + return; + + if (msg.pe.buttonMask == 0) + pointerClient = NULL; + else + pointerClient = cl; + + if(!cl->viewOnly) { + cl->screen->ptrAddEvent(msg.pe.buttonMask, + Swap16IfLE(msg.pe.x), Swap16IfLE(msg.pe.y), cl); + } + + return; + + + case rfbClientCutText: + + if ((n = ReadExact(cl, ((char *)&msg) + 1, + sz_rfbClientCutTextMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + + if(!cl->viewOnly) { + msg.cct.length = Swap32IfLE(msg.cct.length); + + str = (char *)malloc(msg.cct.length); + + if ((n = ReadExact(cl, str, msg.cct.length)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + free(str); + rfbCloseClient(cl); + return; + } + + cl->screen->setXCutText(str, msg.cct.length, cl); + free(str); + } + + return; + + + default: + + rfbLog("rfbProcessClientNormalMessage: unknown message type %d\n", + msg.type); + rfbLog(" ... closing connection\n"); + rfbCloseClient(cl); + return; + } +} + + + +/* + * rfbSendFramebufferUpdate - send the currently pending framebuffer update to + * the RFB client. + * givenUpdateRegion is not changed. + */ + +rfbBool +rfbSendFramebufferUpdate(cl, givenUpdateRegion) + rfbClientPtr cl; + sraRegionPtr givenUpdateRegion; +{ + sraRectangleIterator* i; + sraRect rect; + int nUpdateRegionRects; + rfbFramebufferUpdateMsg *fu = (rfbFramebufferUpdateMsg *)cl->updateBuf; + sraRegionPtr updateRegion,updateCopyRegion,tmpRegion; + int dx, dy; + rfbBool sendCursorShape = FALSE; + rfbBool sendCursorPos = FALSE; + + if(cl->screen->displayHook) + cl->screen->displayHook(cl); + + /* + * If framebuffer size was changed and the client supports NewFBSize + * encoding, just send NewFBSize marker and return. + */ + + if (cl->useNewFBSize && cl->newFBSizePending) { + LOCK(cl->updateMutex); + cl->newFBSizePending = FALSE; + UNLOCK(cl->updateMutex); + cl->rfbFramebufferUpdateMessagesSent++; + fu->type = rfbFramebufferUpdate; + fu->nRects = Swap16IfLE(1); + cl->ublen = sz_rfbFramebufferUpdateMsg; + if (!rfbSendNewFBSize(cl, cl->screen->width, cl->screen->height)) { + return FALSE; + } + return rfbSendUpdateBuf(cl); + } + + /* + * If this client understands cursor shape updates, cursor should be + * removed from the framebuffer. Otherwise, make sure it's put up. + */ + + if (cl->enableCursorShapeUpdates) { + if (cl->screen->cursorIsDrawn) { + rfbUndrawCursor(cl->screen); + } + if (!cl->screen->cursorIsDrawn && cl->cursorWasChanged && + cl->readyForSetColourMapEntries) + sendCursorShape = TRUE; + } else { + if (!cl->screen->cursorIsDrawn) { + rfbDrawCursor(cl->screen); + } + } + + /* + * Do we plan to send cursor position update? + */ + + if (cl->enableCursorPosUpdates && cl->cursorWasMoved) + sendCursorPos = TRUE; + + LOCK(cl->updateMutex); + + /* + * The modifiedRegion may overlap the destination copyRegion. We remove + * any overlapping bits from the copyRegion (since they'd only be + * overwritten anyway). + */ + + sraRgnSubtract(cl->copyRegion,cl->modifiedRegion); + + /* + * The client is interested in the region requestedRegion. The region + * which should be updated now is the intersection of requestedRegion + * and the union of modifiedRegion and copyRegion. If it's empty then + * no update is needed. + */ + + updateRegion = sraRgnCreateRgn(givenUpdateRegion); + if(cl->screen->progressiveSliceHeight>0) { + int height=cl->screen->progressiveSliceHeight, + y=cl->progressiveSliceY; + sraRegionPtr bbox=sraRgnBBox(updateRegion); + sraRect rect; + if(sraRgnPopRect(bbox,&rect,0)) { + sraRegionPtr slice; + if(y=rect.y2) + y=rect.y1; + slice=sraRgnCreateRect(0,y,cl->screen->width,y+height); + sraRgnAnd(updateRegion,slice); + sraRgnDestroy(slice); + } + sraRgnDestroy(bbox); + y+=height; + if(y>=cl->screen->height) + y=0; + cl->progressiveSliceY=y; + } + + sraRgnOr(updateRegion,cl->copyRegion); + if(!sraRgnAnd(updateRegion,cl->requestedRegion) && + !sendCursorShape && !sendCursorPos) { + sraRgnDestroy(updateRegion); + UNLOCK(cl->updateMutex); + return TRUE; + } + + /* + * We assume that the client doesn't have any pixel data outside the + * requestedRegion. In other words, both the source and destination of a + * copy must lie within requestedRegion. So the region we can send as a + * copy is the intersection of the copyRegion with both the requestedRegion + * and the requestedRegion translated by the amount of the copy. We set + * updateCopyRegion to this. + */ + + updateCopyRegion = sraRgnCreateRgn(cl->copyRegion); + sraRgnAnd(updateCopyRegion,cl->requestedRegion); + tmpRegion = sraRgnCreateRgn(cl->requestedRegion); + sraRgnOffset(tmpRegion,cl->copyDX,cl->copyDY); + sraRgnAnd(updateCopyRegion,tmpRegion); + sraRgnDestroy(tmpRegion); + dx = cl->copyDX; + dy = cl->copyDY; + + /* + * Next we remove updateCopyRegion from updateRegion so that updateRegion + * is the part of this update which is sent as ordinary pixel data (i.e not + * a copy). + */ + + sraRgnSubtract(updateRegion,updateCopyRegion); + + /* + * Finally we leave modifiedRegion to be the remainder (if any) of parts of + * the screen which are modified but outside the requestedRegion. We also + * empty both the requestedRegion and the copyRegion - note that we never + * carry over a copyRegion for a future update. + */ + + + sraRgnOr(cl->modifiedRegion,cl->copyRegion); + sraRgnSubtract(cl->modifiedRegion,updateRegion); + sraRgnSubtract(cl->modifiedRegion,updateCopyRegion); + + sraRgnMakeEmpty(cl->requestedRegion); + sraRgnMakeEmpty(cl->copyRegion); + cl->copyDX = 0; + cl->copyDY = 0; + + UNLOCK(cl->updateMutex); + + /* + * Now send the update. + */ + + cl->rfbFramebufferUpdateMessagesSent++; + + if (cl->preferredEncoding == rfbEncodingCoRRE) { + nUpdateRegionRects = 0; + + for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){ + int x = rect.x1; + int y = rect.y1; + int w = rect.x2 - x; + int h = rect.y2 - y; + nUpdateRegionRects += (((w-1) / cl->correMaxWidth + 1) + * ((h-1) / cl->correMaxHeight + 1)); + } + sraRgnReleaseIterator(i); +#ifdef LIBVNCSERVER_HAVE_LIBZ + } else if (cl->preferredEncoding == rfbEncodingZlib) { + nUpdateRegionRects = 0; + + for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){ + int x = rect.x1; + int y = rect.y1; + int w = rect.x2 - x; + int h = rect.y2 - y; + nUpdateRegionRects += (((h-1) / (ZLIB_MAX_SIZE( w ) / w)) + 1); + } + sraRgnReleaseIterator(i); +#ifdef LIBVNCSERVER_HAVE_LIBJPEG + } else if (cl->preferredEncoding == rfbEncodingTight) { + nUpdateRegionRects = 0; + + for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){ + int x = rect.x1; + int y = rect.y1; + int w = rect.x2 - x; + int h = rect.y2 - y; + int n = rfbNumCodedRectsTight(cl, x, y, w, h); + if (n == 0) { + nUpdateRegionRects = 0xFFFF; + break; + } + nUpdateRegionRects += n; + } + sraRgnReleaseIterator(i); +#endif +#endif + } else { + nUpdateRegionRects = sraRgnCountRects(updateRegion); + } + + fu->type = rfbFramebufferUpdate; + if (nUpdateRegionRects != 0xFFFF) { + if(cl->screen->maxRectsPerUpdate>0 +#ifdef LIBVNCSERVER_HAVE_LIBJPEG + /* Tight encoding counts the rectangles differently */ + && cl->preferredEncoding != rfbEncodingTight +#endif + && nUpdateRegionRects>cl->screen->maxRectsPerUpdate) { + sraRegion* newUpdateRegion = sraRgnBBox(updateRegion); + sraRgnDestroy(updateRegion); + updateRegion = newUpdateRegion; + nUpdateRegionRects = sraRgnCountRects(updateRegion); + } + fu->nRects = Swap16IfLE((uint16_t)(sraRgnCountRects(updateCopyRegion) + + nUpdateRegionRects + + !!sendCursorShape + !!sendCursorPos)); + } else { + fu->nRects = 0xFFFF; + } + cl->ublen = sz_rfbFramebufferUpdateMsg; + + if (sendCursorShape) { + cl->cursorWasChanged = FALSE; + if (!rfbSendCursorShape(cl)) { + sraRgnDestroy(updateRegion); + return FALSE; + } + } + + if (sendCursorPos) { + cl->cursorWasMoved = FALSE; + if (!rfbSendCursorPos(cl)) { + sraRgnDestroy(updateRegion); + return FALSE; + } + } + + if (!sraRgnEmpty(updateCopyRegion)) { + if (!rfbSendCopyRegion(cl,updateCopyRegion,dx,dy)) { + sraRgnDestroy(updateRegion); + sraRgnDestroy(updateCopyRegion); + return FALSE; + } + } + + sraRgnDestroy(updateCopyRegion); + + for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){ + int x = rect.x1; + int y = rect.y1; + int w = rect.x2 - x; + int h = rect.y2 - y; + + cl->rfbRawBytesEquivalent += (sz_rfbFramebufferUpdateRectHeader + + w * (cl->format.bitsPerPixel / 8) * h); + + switch (cl->preferredEncoding) { + case rfbEncodingRaw: + if (!rfbSendRectEncodingRaw(cl, x, y, w, h)) { + sraRgnDestroy(updateRegion); + sraRgnReleaseIterator(i); + return FALSE; + } + break; + case rfbEncodingRRE: + if (!rfbSendRectEncodingRRE(cl, x, y, w, h)) { + sraRgnDestroy(updateRegion); + sraRgnReleaseIterator(i); + return FALSE; + } + break; + case rfbEncodingCoRRE: + if (!rfbSendRectEncodingCoRRE(cl, x, y, w, h)) { + sraRgnDestroy(updateRegion); + sraRgnReleaseIterator(i); + return FALSE; + } + break; + case rfbEncodingHextile: + if (!rfbSendRectEncodingHextile(cl, x, y, w, h)) { + sraRgnDestroy(updateRegion); + sraRgnReleaseIterator(i); + return FALSE; + } + break; +#ifdef LIBVNCSERVER_HAVE_LIBZ + case rfbEncodingZlib: + if (!rfbSendRectEncodingZlib(cl, x, y, w, h)) { + sraRgnDestroy(updateRegion); + sraRgnReleaseIterator(i); + return FALSE; + } + break; +#ifdef LIBVNCSERVER_HAVE_LIBJPEG + case rfbEncodingTight: + if (!rfbSendRectEncodingTight(cl, x, y, w, h)) { + sraRgnDestroy(updateRegion); + sraRgnReleaseIterator(i); + return FALSE; + } + break; +#endif +#endif +#ifdef LIBVNCSERVER_HAVE_LIBZ + case rfbEncodingZRLE: + if (!rfbSendRectEncodingZRLE(cl, x, y, w, h)) { + sraRgnDestroy(updateRegion); + sraRgnReleaseIterator(i); + return FALSE; + } + break; +#endif + } + } + sraRgnReleaseIterator(i); + + if ( nUpdateRegionRects == 0xFFFF && + !rfbSendLastRectMarker(cl) ) { + sraRgnDestroy(updateRegion); + return FALSE; + } + + if (!rfbSendUpdateBuf(cl)) { + sraRgnDestroy(updateRegion); + return FALSE; + } + + sraRgnDestroy(updateRegion); + return TRUE; +} + + +/* + * Send the copy region as a string of CopyRect encoded rectangles. + * The only slightly tricky thing is that we should send the messages in + * the correct order so that an earlier CopyRect will not corrupt the source + * of a later one. + */ + +rfbBool +rfbSendCopyRegion(cl, reg, dx, dy) + rfbClientPtr cl; + sraRegionPtr reg; + int dx, dy; +{ + int x, y, w, h; + rfbFramebufferUpdateRectHeader rect; + rfbCopyRect cr; + sraRectangleIterator* i; + sraRect rect1; + + /* printf("copyrect: "); sraRgnPrint(reg); putchar('\n');fflush(stdout); */ + i = sraRgnGetReverseIterator(reg,dx>0,dy>0); + + while(sraRgnIteratorNext(i,&rect1)) { + x = rect1.x1; + y = rect1.y1; + w = rect1.x2 - x; + h = rect1.y2 - y; + + rect.r.x = Swap16IfLE(x); + rect.r.y = Swap16IfLE(y); + rect.r.w = Swap16IfLE(w); + rect.r.h = Swap16IfLE(h); + rect.encoding = Swap32IfLE(rfbEncodingCopyRect); + + memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + cl->ublen += sz_rfbFramebufferUpdateRectHeader; + + cr.srcX = Swap16IfLE(x - dx); + cr.srcY = Swap16IfLE(y - dy); + + memcpy(&cl->updateBuf[cl->ublen], (char *)&cr, sz_rfbCopyRect); + cl->ublen += sz_rfbCopyRect; + + cl->rfbRectanglesSent[rfbEncodingCopyRect]++; + cl->rfbBytesSent[rfbEncodingCopyRect] + += sz_rfbFramebufferUpdateRectHeader + sz_rfbCopyRect; + + } + + return TRUE; +} + +/* + * Send a given rectangle in raw encoding (rfbEncodingRaw). + */ + +rfbBool +rfbSendRectEncodingRaw(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + rfbFramebufferUpdateRectHeader rect; + int nlines; + int bytesPerLine = w * (cl->format.bitsPerPixel / 8); + char *fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y) + + (x * (cl->screen->bitsPerPixel / 8))); + + /* Flush the buffer to guarantee correct alignment for translateFn(). */ + if (cl->ublen > 0) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + rect.r.x = Swap16IfLE(x); + rect.r.y = Swap16IfLE(y); + rect.r.w = Swap16IfLE(w); + rect.r.h = Swap16IfLE(h); + rect.encoding = Swap32IfLE(rfbEncodingRaw); + + memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader); + cl->ublen += sz_rfbFramebufferUpdateRectHeader; + + cl->rfbRectanglesSent[rfbEncodingRaw]++; + cl->rfbBytesSent[rfbEncodingRaw] + += sz_rfbFramebufferUpdateRectHeader + bytesPerLine * h; + + nlines = (UPDATE_BUF_SIZE - cl->ublen) / bytesPerLine; + + while (TRUE) { + if (nlines > h) + nlines = h; + + (*cl->translateFn)(cl->translateLookupTable, + &(cl->screen->rfbServerFormat), + &cl->format, fbptr, &cl->updateBuf[cl->ublen], + cl->screen->paddedWidthInBytes, w, nlines); + + cl->ublen += nlines * bytesPerLine; + h -= nlines; + + if (h == 0) /* rect fitted in buffer, do next one */ + return TRUE; + + /* buffer full - flush partial rect and do another nlines */ + + if (!rfbSendUpdateBuf(cl)) + return FALSE; + + fbptr += (cl->screen->paddedWidthInBytes * nlines); + + nlines = (UPDATE_BUF_SIZE - cl->ublen) / bytesPerLine; + if (nlines == 0) { + rfbErr("rfbSendRectEncodingRaw: send buffer too small for %d " + "bytes per line\n", bytesPerLine); + rfbCloseClient(cl); + return FALSE; + } + } +} + + + +/* + * Send an empty rectangle with encoding field set to value of + * rfbEncodingLastRect to notify client that this is the last + * rectangle in framebuffer update ("LastRect" extension of RFB + * protocol). + */ + +rfbBool +rfbSendLastRectMarker(cl) + rfbClientPtr cl; +{ + rfbFramebufferUpdateRectHeader rect; + + if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + rect.encoding = Swap32IfLE(rfbEncodingLastRect); + rect.r.x = 0; + rect.r.y = 0; + rect.r.w = 0; + rect.r.h = 0; + + memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader); + cl->ublen += sz_rfbFramebufferUpdateRectHeader; + + cl->rfbLastRectMarkersSent++; + cl->rfbLastRectBytesSent += sz_rfbFramebufferUpdateRectHeader; + + return TRUE; +} + + +/* + * Send NewFBSize pseudo-rectangle. This tells the client to change + * its framebuffer size. + */ + +rfbBool +rfbSendNewFBSize(cl, w, h) + rfbClientPtr cl; + int w, h; +{ + rfbFramebufferUpdateRectHeader rect; + + if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + rect.encoding = Swap32IfLE(rfbEncodingNewFBSize); + rect.r.x = 0; + rect.r.y = 0; + rect.r.w = Swap16IfLE(w); + rect.r.h = Swap16IfLE(h); + + memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + cl->ublen += sz_rfbFramebufferUpdateRectHeader; + + cl->rfbLastRectMarkersSent++; + cl->rfbLastRectBytesSent += sz_rfbFramebufferUpdateRectHeader; + + return TRUE; +} + + +/* + * Send the contents of cl->updateBuf. Returns 1 if successful, -1 if + * not (errno should be set). + */ + +rfbBool +rfbSendUpdateBuf(cl) + rfbClientPtr cl; +{ + if(cl->sock<0) + return FALSE; + + if (WriteExact(cl, cl->updateBuf, cl->ublen) < 0) { + rfbLogPerror("rfbSendUpdateBuf: write"); + rfbCloseClient(cl); + return FALSE; + } + + cl->ublen = 0; + return TRUE; +} + +/* + * rfbSendSetColourMapEntries sends a SetColourMapEntries message to the + * client, using values from the currently installed colormap. + */ + +rfbBool +rfbSendSetColourMapEntries(cl, firstColour, nColours) + rfbClientPtr cl; + int firstColour; + int nColours; +{ + char buf[sz_rfbSetColourMapEntriesMsg + 256 * 3 * 2]; + rfbSetColourMapEntriesMsg *scme = (rfbSetColourMapEntriesMsg *)buf; + uint16_t *rgb = (uint16_t *)(&buf[sz_rfbSetColourMapEntriesMsg]); + rfbColourMap* cm = &cl->screen->colourMap; + + int i, len; + + scme->type = rfbSetColourMapEntries; + + scme->firstColour = Swap16IfLE(firstColour); + scme->nColours = Swap16IfLE(nColours); + + len = sz_rfbSetColourMapEntriesMsg; + + for (i = 0; i < nColours; i++) { + if(i<(int)cm->count) { + if(cm->is16) { + rgb[i*3] = Swap16IfLE(cm->data.shorts[i*3]); + rgb[i*3+1] = Swap16IfLE(cm->data.shorts[i*3+1]); + rgb[i*3+2] = Swap16IfLE(cm->data.shorts[i*3+2]); + } else { + rgb[i*3] = Swap16IfLE(cm->data.bytes[i*3]); + rgb[i*3+1] = Swap16IfLE(cm->data.bytes[i*3+1]); + rgb[i*3+2] = Swap16IfLE(cm->data.bytes[i*3+2]); + } + } + } + + len += nColours * 3 * 2; + + if (WriteExact(cl, buf, len) < 0) { + rfbLogPerror("rfbSendSetColourMapEntries: write"); + rfbCloseClient(cl); + return FALSE; + } + return TRUE; +} + +/* + * rfbSendBell sends a Bell message to all the clients. + */ + +void +rfbSendBell(rfbScreenInfoPtr rfbScreen) +{ + rfbClientIteratorPtr i; + rfbClientPtr cl; + rfbBellMsg b; + + i = rfbGetClientIterator(rfbScreen); + while((cl=rfbClientIteratorNext(i))) { + b.type = rfbBell; + if (WriteExact(cl, (char *)&b, sz_rfbBellMsg) < 0) { + rfbLogPerror("rfbSendBell: write"); + rfbCloseClient(cl); + } + } + rfbReleaseClientIterator(i); +} + + +/* + * rfbSendServerCutText sends a ServerCutText message to all the clients. + */ + +void +rfbSendServerCutText(rfbScreenInfoPtr rfbScreen,char *str, int len) +{ + rfbClientPtr cl; + rfbServerCutTextMsg sct; + rfbClientIteratorPtr iterator; + + iterator = rfbGetClientIterator(rfbScreen); + while ((cl = rfbClientIteratorNext(iterator)) != NULL) { + sct.type = rfbServerCutText; + sct.length = Swap32IfLE(len); + if (WriteExact(cl, (char *)&sct, + sz_rfbServerCutTextMsg) < 0) { + rfbLogPerror("rfbSendServerCutText: write"); + rfbCloseClient(cl); + continue; + } + if (WriteExact(cl, str, len) < 0) { + rfbLogPerror("rfbSendServerCutText: write"); + rfbCloseClient(cl); + } + } + rfbReleaseClientIterator(iterator); +} + +/***************************************************************************** + * + * UDP can be used for keyboard and pointer events when the underlying + * network is highly reliable. This is really here to support ORL's + * videotile, whose TCP implementation doesn't like sending lots of small + * packets (such as 100s of pen readings per second!). + */ + +unsigned char ptrAcceleration = 50; + +void +rfbNewUDPConnection(rfbScreen,sock) + rfbScreenInfoPtr rfbScreen; + int sock; +{ + if (write(sock, &ptrAcceleration, 1) < 0) { + rfbLogPerror("rfbNewUDPConnection: write"); + } +} + +/* + * Because UDP is a message based service, we can't read the first byte and + * then the rest of the packet separately like we do with TCP. We will always + * get a whole packet delivered in one go, so we ask read() for the maximum + * number of bytes we can possibly get. + */ + +void +rfbProcessUDPInput(rfbScreenInfoPtr rfbScreen) +{ + int n; + rfbClientPtr cl=rfbScreen->udpClient; + rfbClientToServerMsg msg; + + if((!cl) || cl->onHold) + return; + + if ((n = read(rfbScreen->udpSock, (char *)&msg, sizeof(msg))) <= 0) { + if (n < 0) { + rfbLogPerror("rfbProcessUDPInput: read"); + } + rfbDisconnectUDPSock(rfbScreen); + return; + } + + switch (msg.type) { + + case rfbKeyEvent: + if (n != sz_rfbKeyEventMsg) { + rfbErr("rfbProcessUDPInput: key event incorrect length\n"); + rfbDisconnectUDPSock(rfbScreen); + return; + } + cl->screen->kbdAddEvent(msg.ke.down, (rfbKeySym)Swap32IfLE(msg.ke.key), cl); + break; + + case rfbPointerEvent: + if (n != sz_rfbPointerEventMsg) { + rfbErr("rfbProcessUDPInput: ptr event incorrect length\n"); + rfbDisconnectUDPSock(rfbScreen); + return; + } + cl->screen->ptrAddEvent(msg.pe.buttonMask, + Swap16IfLE(msg.pe.x), Swap16IfLE(msg.pe.y), cl); + break; + + default: + rfbErr("rfbProcessUDPInput: unknown message type %d\n", + msg.type); + rfbDisconnectUDPSock(rfbScreen); + } +} + +#ifdef LIBVNCSERVER_BACKCHANNEL +void rfbSendBackChannel(rfbScreenInfoPtr rfbScreen,char* str,int len) +{ + rfbClientPtr cl; + rfbBackChannelMsg sct; + rfbClientIteratorPtr iterator; + + iterator = rfbGetClientIterator(rfbScreen); + while ((cl = rfbClientIteratorNext(iterator)) != NULL) { + if (cl->enableBackChannel) { + sct.type = rfbBackChannel; + sct.length = Swap32IfLE(len); + if (WriteExact(cl, (char *)&sct, + sz_rfbBackChannelMsg) < 0) { + rfbLogPerror("rfbSendBackChannel: write"); + rfbCloseClient(cl); + continue; + } + if (WriteExact(cl, str, len) < 0) { + rfbLogPerror("rfbSendBackChannel: write"); + rfbCloseClient(cl); + } + } + } + rfbReleaseClientIterator(iterator); +} +#endif diff --git a/libvncserver/rre.c b/libvncserver/rre.c new file mode 100755 index 0000000..3e00c05 --- /dev/null +++ b/libvncserver/rre.c @@ -0,0 +1,321 @@ +/* + * rre.c + * + * Routines to implement Rise-and-Run-length Encoding (RRE). This + * code is based on krw's original javatel rfbserver. + */ + +/* + * OSXvnc Copyright (C) 2001 Dan McGuirk . + * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. + * 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. + */ + +#include + +/* + * rreBeforeBuf contains pixel data in the client's format. + * rreAfterBuf contains the RRE encoded version. If the RRE encoded version is + * larger than the raw data or if it exceeds rreAfterBufSize then + * raw encoding is used instead. + */ + +static int rreBeforeBufSize = 0; +static char *rreBeforeBuf = NULL; + +static int rreAfterBufSize = 0; +static char *rreAfterBuf = NULL; +static int rreAfterBufLen; + +static int subrectEncode8(uint8_t *data, int w, int h); +static int subrectEncode16(uint16_t *data, int w, int h); +static int subrectEncode32(uint32_t *data, int w, int h); +static uint32_t getBgColour(char *data, int size, int bpp); + + +/* + * rfbSendRectEncodingRRE - send a given rectangle using RRE encoding. + */ + +rfbBool +rfbSendRectEncodingRRE(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + rfbFramebufferUpdateRectHeader rect; + rfbRREHeader hdr; + int nSubrects; + int i; + char *fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y) + + (x * (cl->screen->bitsPerPixel / 8))); + + int maxRawSize = (cl->screen->width * cl->screen->height + * (cl->format.bitsPerPixel / 8)); + + if (rreBeforeBufSize < maxRawSize) { + rreBeforeBufSize = maxRawSize; + if (rreBeforeBuf == NULL) + rreBeforeBuf = (char *)malloc(rreBeforeBufSize); + else + rreBeforeBuf = (char *)realloc(rreBeforeBuf, rreBeforeBufSize); + } + + if (rreAfterBufSize < maxRawSize) { + rreAfterBufSize = maxRawSize; + if (rreAfterBuf == NULL) + rreAfterBuf = (char *)malloc(rreAfterBufSize); + else + rreAfterBuf = (char *)realloc(rreAfterBuf, rreAfterBufSize); + } + + (*cl->translateFn)(cl->translateLookupTable, + &(cl->screen->rfbServerFormat), + &cl->format, fbptr, rreBeforeBuf, + cl->screen->paddedWidthInBytes, w, h); + + switch (cl->format.bitsPerPixel) { + case 8: + nSubrects = subrectEncode8((uint8_t *)rreBeforeBuf, w, h); + break; + case 16: + nSubrects = subrectEncode16((uint16_t *)rreBeforeBuf, w, h); + break; + case 32: + nSubrects = subrectEncode32((uint32_t *)rreBeforeBuf, w, h); + break; + default: + rfbLog("getBgColour: bpp %d?\n",cl->format.bitsPerPixel); + return FALSE; + } + + if (nSubrects < 0) { + + /* RRE encoding was too large, use raw */ + + return rfbSendRectEncodingRaw(cl, x, y, w, h); + } + + cl->rfbRectanglesSent[rfbEncodingRRE]++; + cl->rfbBytesSent[rfbEncodingRRE] += (sz_rfbFramebufferUpdateRectHeader + + sz_rfbRREHeader + rreAfterBufLen); + + if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader + > UPDATE_BUF_SIZE) + { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + rect.r.x = Swap16IfLE(x); + rect.r.y = Swap16IfLE(y); + rect.r.w = Swap16IfLE(w); + rect.r.h = Swap16IfLE(h); + rect.encoding = Swap32IfLE(rfbEncodingRRE); + + memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + cl->ublen += sz_rfbFramebufferUpdateRectHeader; + + hdr.nSubrects = Swap32IfLE(nSubrects); + + memcpy(&cl->updateBuf[cl->ublen], (char *)&hdr, sz_rfbRREHeader); + cl->ublen += sz_rfbRREHeader; + + for (i = 0; i < rreAfterBufLen;) { + + int bytesToCopy = UPDATE_BUF_SIZE - cl->ublen; + + if (i + bytesToCopy > rreAfterBufLen) { + bytesToCopy = rreAfterBufLen - i; + } + + memcpy(&cl->updateBuf[cl->ublen], &rreAfterBuf[i], bytesToCopy); + + cl->ublen += bytesToCopy; + i += bytesToCopy; + + if (cl->ublen == UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + } + + return TRUE; +} + + + +/* + * subrectEncode() encodes the given multicoloured rectangle as a background + * colour overwritten by single-coloured rectangles. It returns the number + * of subrectangles in the encoded buffer, or -1 if subrect encoding won't + * fit in the buffer. It puts the encoded rectangles in rreAfterBuf. The + * single-colour rectangle partition is not optimal, but does find the biggest + * horizontal or vertical rectangle top-left anchored to each consecutive + * coordinate position. + * + * The coding scheme is simply [...] where each + * is []. + */ + +#define DEFINE_SUBRECT_ENCODE(bpp) \ +static int \ +subrectEncode##bpp(data,w,h) \ + uint##bpp##_t *data; \ + int w; \ + int h; \ +{ \ + uint##bpp##_t cl; \ + rfbRectangle subrect; \ + int x,y; \ + int i,j; \ + int hx=0,hy,vx=0,vy; \ + int hyflag; \ + uint##bpp##_t *seg; \ + uint##bpp##_t *line; \ + int hw,hh,vw,vh; \ + int thex,they,thew,theh; \ + int numsubs = 0; \ + int newLen; \ + uint##bpp##_t bg = (uint##bpp##_t)getBgColour((char*)data,w*h,bpp); \ + \ + *((uint##bpp##_t*)rreAfterBuf) = bg; \ + \ + rreAfterBufLen = (bpp/8); \ + \ + for (y=0; y 0) && (i >= hx)) {hy += 1;} else {hyflag = 0;} \ + } \ + vy = j-1; \ + \ + /* We now have two possible subrects: (x,y,hx,hy) and (x,y,vx,vy) \ + * We'll choose the bigger of the two. \ + */ \ + hw = hx-x+1; \ + hh = hy-y+1; \ + vw = vx-x+1; \ + vh = vy-y+1; \ + \ + thex = x; \ + they = y; \ + \ + if ((hw*hh) > (vw*vh)) { \ + thew = hw; \ + theh = hh; \ + } else { \ + thew = vw; \ + theh = vh; \ + } \ + \ + subrect.x = Swap16IfLE(thex); \ + subrect.y = Swap16IfLE(they); \ + subrect.w = Swap16IfLE(thew); \ + subrect.h = Swap16IfLE(theh); \ + \ + newLen = rreAfterBufLen + (bpp/8) + sz_rfbRectangle; \ + if ((newLen > (w * h * (bpp/8))) || (newLen > rreAfterBufSize)) \ + return -1; \ + \ + numsubs += 1; \ + *((uint##bpp##_t*)(rreAfterBuf + rreAfterBufLen)) = cl; \ + rreAfterBufLen += (bpp/8); \ + memcpy(&rreAfterBuf[rreAfterBufLen],&subrect,sz_rfbRectangle); \ + rreAfterBufLen += sz_rfbRectangle; \ + \ + /* \ + * Now mark the subrect as done. \ + */ \ + for (j=they; j < (they+theh); j++) { \ + for (i=thex; i < (thex+thew); i++) { \ + data[j*w+i] = bg; \ + } \ + } \ + } \ + } \ + } \ + \ + return numsubs; \ +} + +DEFINE_SUBRECT_ENCODE(8) +DEFINE_SUBRECT_ENCODE(16) +DEFINE_SUBRECT_ENCODE(32) + + +/* + * getBgColour() gets the most prevalent colour in a byte array. + */ +static uint32_t +getBgColour(data,size,bpp) + char *data; + int size; + int bpp; +{ + +#define NUMCLRS 256 + + static int counts[NUMCLRS]; + int i,j,k; + + int maxcount = 0; + uint8_t maxclr = 0; + + if (bpp != 8) { + if (bpp == 16) { + return ((uint16_t *)data)[0]; + } else if (bpp == 32) { + return ((uint32_t *)data)[0]; + } else { + rfbLog("getBgColour: bpp %d?\n",bpp); + return 0; + } + } + + for (i=0; i= NUMCLRS) { + rfbErr("getBgColour: unusual colour = %d\n", k); + return 0; + } + counts[k] += 1; + if (counts[k] > maxcount) { + maxcount = counts[k]; + maxclr = ((uint8_t *)data)[j]; + } + } + + return maxclr; +} diff --git a/libvncserver/selbox.c b/libvncserver/selbox.c new file mode 100755 index 0000000..8d76e23 --- /dev/null +++ b/libvncserver/selbox.c @@ -0,0 +1,301 @@ +#include +#include +#include + +typedef struct { + rfbScreenInfoPtr screen; + rfbFontDataPtr font; + char** list; + int listSize; + int selected; + int displayStart; + int x1,y1,x2,y2,textH,pageH; + int xhot,yhot; + int buttonWidth,okBX,cancelBX,okX,cancelX,okY; + rfbBool okInverted,cancelInverted; + int lastButtons; + rfbPixel colour,backColour; + SelectionChangedHookPtr selChangedHook; + enum { SELECTING, OK, CANCEL } state; +} rfbSelectData; + +static const char* okStr="OK"; +static const char* cancelStr="Cancel"; + +static void selPaintButtons(rfbSelectData* m,rfbBool invertOk,rfbBool invertCancel) +{ + rfbScreenInfoPtr s = m->screen; + rfbPixel bcolour = m->backColour; + rfbPixel colour = m->colour; + + rfbFillRect(s,m->x1,m->okY-m->textH,m->x2,m->okY,bcolour); + + if(invertOk) { + rfbFillRect(s,m->okBX,m->okY-m->textH,m->okBX+m->buttonWidth,m->okY,colour); + rfbDrawStringWithClip(s,m->font,m->okX+m->xhot,m->okY-1+m->yhot,okStr, + m->x1,m->okY-m->textH,m->x2,m->okY, + bcolour,colour); + } else + rfbDrawString(s,m->font,m->okX+m->xhot,m->okY-1+m->yhot,okStr,colour); + + if(invertCancel) { + rfbFillRect(s,m->cancelBX,m->okY-m->textH, + m->cancelBX+m->buttonWidth,m->okY,colour); + rfbDrawStringWithClip(s,m->font,m->cancelX+m->xhot,m->okY-1+m->yhot, + cancelStr,m->x1,m->okY-m->textH,m->x2,m->okY, + bcolour,colour); + } else + rfbDrawString(s,m->font,m->cancelX+m->xhot,m->okY-1+m->yhot,cancelStr,colour); + + m->okInverted = invertOk; + m->cancelInverted = invertCancel; +} + +/* line is relative to displayStart */ +static void selPaintLine(rfbSelectData* m,int line,rfbBool invert) +{ + int y1 = m->y1+line*m->textH, y2 = y1+m->textH; + if(y2>m->y2) + y2=m->y2; + rfbFillRect(m->screen,m->x1,y1,m->x2,y2,invert?m->colour:m->backColour); + if(m->displayStart+linelistSize) + rfbDrawStringWithClip(m->screen,m->font,m->x1+m->xhot,y2-1+m->yhot, + m->list[m->displayStart+line], + m->x1,y1,m->x2,y2, + invert?m->backColour:m->colour, + invert?m->backColour:m->colour); +} + +static void selSelect(rfbSelectData* m,int _index) +{ + int delta; + + if(_index==m->selected || _index<0 || _index>=m->listSize) + return; + + if(m->selected>=0) + selPaintLine(m,m->selected-m->displayStart,FALSE); + + if(_indexdisplayStart || _index>=m->displayStart+m->pageH) { + /* targetLine is the screen line in which the selected line will + be displayed. + targetLine = m->pageH/2 doesn't look so nice */ + int targetLine = m->selected-m->displayStart; + int lineStart,lineEnd; + + /* scroll */ + if(_indexpageH-targetLine>=m->listSize) + targetLine = _index+m->pageH-m->listSize; + delta = _index-(m->displayStart+targetLine); + + if(delta>-m->pageH && deltapageH) { + if(delta>0) { + lineStart = m->pageH-delta; + lineEnd = m->pageH; + rfbDoCopyRect(m->screen,m->x1,m->y1,m->x2,m->y1+lineStart*m->textH, + 0,-delta*m->textH); + } else { + lineStart = 0; + lineEnd = -delta; + rfbDoCopyRect(m->screen, + m->x1,m->y1+lineEnd*m->textH,m->x2,m->y2, + 0,-delta*m->textH); + } + } else { + lineStart = 0; + lineEnd = m->pageH; + } + m->displayStart += delta; + for(delta=lineStart;deltaselected = _index; + selPaintLine(m,m->selected-m->displayStart,TRUE); + + if(m->selChangedHook) + m->selChangedHook(_index); + + /* todo: scrollbars */ +} + +static void selKbdAddEvent(rfbBool down,rfbKeySym keySym,rfbClientPtr cl) +{ + if(down) { + if(keySym>' ' && keySym<0xff) { + int i; + rfbSelectData* m = (rfbSelectData*)cl->screen->screenData; + char c = tolower(keySym); + + for(i=m->selected+1;m->list[i] && tolower(m->list[i][0])!=c;i++); + if(!m->list[i]) + for(i=0;iselected && tolower(m->list[i][0])!=c;i++); + selSelect(m,i); + } else if(keySym==XK_Escape) { + rfbSelectData* m = (rfbSelectData*)cl->screen->screenData; + m->state = CANCEL; + } else if(keySym==XK_Return) { + rfbSelectData* m = (rfbSelectData*)cl->screen->screenData; + m->state = OK; + } else { + rfbSelectData* m = (rfbSelectData*)cl->screen->screenData; + int curSel=m->selected; + if(keySym==XK_Up) { + if(curSel>0) + selSelect(m,curSel-1); + } else if(keySym==XK_Down) { + if(curSel+1listSize) + selSelect(m,curSel+1); + } else { + if(keySym==XK_Page_Down) { + if(curSel+m->pageHlistSize) + selSelect(m,curSel+m->pageH); + else + selSelect(m,m->listSize-1); + } else if(keySym==XK_Page_Up) { + if(curSel-m->pageH>=0) + selSelect(m,curSel-m->pageH); + else + selSelect(m,0); + } + } + } + } +} + +static void selPtrAddEvent(int buttonMask,int x,int y,rfbClientPtr cl) +{ + rfbSelectData* m = (rfbSelectData*)cl->screen->screenData; + if(yokY && y>=m->okY-m->textH) { + if(x>=m->okBX && xokBX+m->buttonWidth) { + if(!m->okInverted) + selPaintButtons(m,TRUE,FALSE); + if(buttonMask) + m->state = OK; + } else if(x>=m->cancelBX && xcancelBX+m->buttonWidth) { + if(!m->cancelInverted) + selPaintButtons(m,FALSE,TRUE); + if(buttonMask) + m->state = CANCEL; + } else if(m->okInverted || m->cancelInverted) + selPaintButtons(m,FALSE,FALSE); + } else { + if(m->okInverted || m->cancelInverted) + selPaintButtons(m,FALSE,FALSE); + if(!m->lastButtons && buttonMask) { + if(x>=m->x1 && xx2 && y>=m->y1 && yy2) + selSelect(m,m->displayStart+(y-m->y1)/m->textH); + } + } + m->lastButtons = buttonMask; + + /* todo: scrollbars */ +} + +static rfbCursorPtr selGetCursorPtr(rfbClientPtr cl) +{ + return(0); +} + +int rfbSelectBox(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font, + char** list, + int x1,int y1,int x2,int y2, + rfbPixel colour,rfbPixel backColour, + int border,SelectionChangedHookPtr selChangedHook) +{ + int bpp = rfbScreen->bitsPerPixel/8; + char* frameBufferBackup; + void* screenDataBackup = rfbScreen->screenData; + KbdAddEventProcPtr kbdAddEventBackup = rfbScreen->kbdAddEvent; + PtrAddEventProcPtr ptrAddEventBackup = rfbScreen->ptrAddEvent; + GetCursorProcPtr getCursorPtrBackup = rfbScreen->getCursorPtr; + DisplayHookPtr displayHookBackup = rfbScreen->displayHook; + rfbSelectData selData; + int i,j,k; + int fx1,fy1,fx2,fy2; /* for font bbox */ + + if(list==0 || *list==0) + return(-1); + + rfbWholeFontBBox(font, &fx1, &fy1, &fx2, &fy2); + selData.textH = fy2-fy1; + /* I need at least one line for the choice and one for the buttons */ + if(y2-y1screenData = &selData; + rfbScreen->kbdAddEvent = selKbdAddEvent; + rfbScreen->ptrAddEvent = selPtrAddEvent; + rfbScreen->getCursorPtr = selGetCursorPtr; + rfbScreen->displayHook = 0; + + /* backup screen */ + for(j=0;jframeBuffer+j*rfbScreen->paddedWidthInBytes+x1*bpp, + (x2-x1)*bpp); + + /* paint list and buttons */ + rfbFillRect(rfbScreen,x1,y1,x2,y2,colour); + selPaintButtons(&selData,FALSE,FALSE); + selSelect(&selData,0); + + /* modal loop */ + while(selData.state == SELECTING) + rfbProcessEvents(rfbScreen,20000); + + /* copy back screen data */ + for(j=0;jframeBuffer+j*rfbScreen->paddedWidthInBytes+x1*bpp, + frameBufferBackup+j*(x2-x1)*bpp, + (x2-x1)*bpp); + free(frameBufferBackup); + rfbMarkRectAsModified(rfbScreen,x1,y1,x2,y2); + rfbScreen->screenData = screenDataBackup; + rfbScreen->kbdAddEvent = kbdAddEventBackup; + rfbScreen->ptrAddEvent = ptrAddEventBackup; + rfbScreen->getCursorPtr = getCursorPtrBackup; + rfbScreen->displayHook = displayHookBackup; + + if(selData.state==CANCEL) + selData.selected=-1; + return(selData.selected); +} + diff --git a/libvncserver/sockets.c b/libvncserver/sockets.c new file mode 100755 index 0000000..cea1898 --- /dev/null +++ b/libvncserver/sockets.c @@ -0,0 +1,619 @@ +/* + * sockets.c - deal with TCP & UDP sockets. + * + * This code should be independent of any changes in the RFB protocol. It just + * deals with the X server scheduling stuff, calling rfbNewClientConnection and + * rfbProcessClientMessage to actually deal with the protocol. If a socket + * needs to be closed for any reason then rfbCloseClient should be called, and + * this in turn will call rfbClientConnectionGone. To make an active + * connection out, call rfbConnect - note that this does _not_ call + * rfbNewClientConnection. + * + * This file is divided into two types of function. Those beginning with + * "rfb" are specific to sockets using the RFB protocol. Those without the + * "rfb" prefix are more general socket routines (which are used by the http + * code). + * + * Thanks to Karl Hakimian for pointing out that some platforms return EAGAIN + * not EWOULDBLOCK. + */ + +/* + * OSXvnc Copyright (C) 2001 Dan McGuirk . + * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. + * 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. + */ + +#include + +#ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H +#include +#endif + +#ifdef WIN32 +#pragma warning (disable: 4018 4761) +#define close closesocket +#define read(sock,buf,len) recv(sock,buf,len,0) +#define EWOULDBLOCK WSAEWOULDBLOCK +#define ETIMEDOUT WSAETIMEDOUT +#define write(sock,buf,len) send(sock,buf,len,0) +#else +#ifdef LIBVNCSERVER_HAVE_SYS_TIME_H +#include +#endif +#ifdef LIBVNCSERVER_HAVE_SYS_SOCKET_H +#include +#endif +#ifdef LIBVNCSERVER_HAVE_NETINET_IN_H +#include +#include +#include +#include +#endif +#ifdef LIBVNCSERVER_HAVE_UNISTD_H +#include +#endif +#endif + +#if defined(__linux__) && defined(NEED_TIMEVAL) +struct timeval +{ + long int tv_sec,tv_usec; +} +; +#endif + +#ifdef LIBVNCSERVER_HAVE_FCNTL_H +#include +#endif + +#include + +#ifdef USE_LIBWRAP +#include +#include +int allow_severity=LOG_INFO; +int deny_severity=LOG_WARNING; +#endif + +/*#ifndef WIN32 +int max(int i,int j) { return(isocketInitDone) + return; + + rfbScreen->socketInitDone = TRUE; + + if (rfbScreen->inetdSock != -1) { + const int one = 1; + +#ifndef WIN32 + if (fcntl(rfbScreen->inetdSock, F_SETFL, O_NONBLOCK) < 0) { + rfbLogPerror("fcntl"); + return; + } +#endif + + if (setsockopt(rfbScreen->inetdSock, IPPROTO_TCP, TCP_NODELAY, + (char *)&one, sizeof(one)) < 0) { + rfbLogPerror("setsockopt"); + return; + } + + FD_ZERO(&(rfbScreen->allFds)); + FD_SET(rfbScreen->inetdSock, &(rfbScreen->allFds)); + rfbScreen->maxFd = rfbScreen->inetdSock; + return; + } + + if(rfbScreen->autoPort) { + int i; + rfbLog("Autoprobing TCP port \n"); + + for (i = 5900; i < 6000; i++) { + if ((rfbScreen->rfbListenSock = ListenOnTCPPort(i)) >= 0) { + rfbScreen->rfbPort = i; + break; + } + } + + if (i >= 6000) { + rfbLogPerror("Failure autoprobing"); + return; + } + + rfbLog("Autoprobing selected port %d\n", rfbScreen->rfbPort); + FD_ZERO(&(rfbScreen->allFds)); + FD_SET(rfbScreen->rfbListenSock, &(rfbScreen->allFds)); + rfbScreen->maxFd = rfbScreen->rfbListenSock; + } + else if(rfbScreen->rfbPort>0) { + rfbLog("Listening for VNC connections on TCP port %d\n", rfbScreen->rfbPort); + + if ((rfbScreen->rfbListenSock = ListenOnTCPPort(rfbScreen->rfbPort)) < 0) { + rfbLogPerror("ListenOnTCPPort"); + return; + } + + FD_ZERO(&(rfbScreen->allFds)); + FD_SET(rfbScreen->rfbListenSock, &(rfbScreen->allFds)); + rfbScreen->maxFd = rfbScreen->rfbListenSock; + } + + if (rfbScreen->udpPort != 0) { + rfbLog("rfbInitSockets: listening for input on UDP port %d\n",rfbScreen->udpPort); + + if ((rfbScreen->udpSock = ListenOnUDPPort(rfbScreen->udpPort)) < 0) { + rfbLogPerror("ListenOnUDPPort"); + return; + } + FD_SET(rfbScreen->udpSock, &(rfbScreen->allFds)); + rfbScreen->maxFd = max((int)rfbScreen->udpSock,rfbScreen->maxFd); + } +} + + +/* + * rfbCheckFds is called from ProcessInputEvents to check for input on the RFB + * socket(s). If there is input to process, the appropriate function in the + * RFB server code will be called (rfbNewClientConnection, + * rfbProcessClientMessage, etc). + */ + +void +rfbCheckFds(rfbScreenInfoPtr rfbScreen,long usec) +{ + int nfds; + fd_set fds; + struct timeval tv; + struct sockaddr_in addr; + size_t addrlen = sizeof(addr); + char buf[6]; + const int one = 1; + int sock; + rfbClientIteratorPtr i; + rfbClientPtr cl; + + if (!rfbScreen->inetdInitDone && rfbScreen->inetdSock != -1) { + rfbNewClientConnection(rfbScreen,rfbScreen->inetdSock); + rfbScreen->inetdInitDone = TRUE; + } + + memcpy((char *)&fds, (char *)&(rfbScreen->allFds), sizeof(fd_set)); + tv.tv_sec = 0; + tv.tv_usec = usec; + nfds = select(rfbScreen->maxFd + 1, &fds, NULL, NULL /* &fds */, &tv); + if (nfds == 0) { + return; + } + if (nfds < 0) { +#ifdef WIN32 + errno = WSAGetLastError(); +#endif + if (errno != EINTR) + rfbLogPerror("rfbCheckFds: select"); + return; + } + + if (rfbScreen->rfbListenSock != -1 && FD_ISSET(rfbScreen->rfbListenSock, &fds)) { + + if ((sock = accept(rfbScreen->rfbListenSock, + (struct sockaddr *)&addr, &addrlen)) < 0) { + rfbLogPerror("rfbCheckFds: accept"); + return; + } + +#ifndef WIN32 + if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { + rfbLogPerror("rfbCheckFds: fcntl"); + close(sock); + return; + } +#endif + + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + (char *)&one, sizeof(one)) < 0) { + rfbLogPerror("rfbCheckFds: setsockopt"); + close(sock); + return; + } + +#ifdef USE_LIBWRAP + if(!hosts_ctl("vnc",STRING_UNKNOWN,inet_ntoa(addr.sin_addr), + STRING_UNKNOWN)) { + rfbLog("Rejected connection from client %s\n", + inet_ntoa(addr.sin_addr)); + close(sock); + return; + } +#endif + + rfbLog("Got connection from client %s\n", inet_ntoa(addr.sin_addr)); + + rfbNewClient(rfbScreen,sock); + + FD_CLR(rfbScreen->rfbListenSock, &fds); + if (--nfds == 0) + return; + } + + if ((rfbScreen->udpSock != -1) && FD_ISSET(rfbScreen->udpSock, &fds)) { + if(!rfbScreen->udpClient) + rfbNewUDPClient(rfbScreen); + if (recvfrom(rfbScreen->udpSock, buf, 1, MSG_PEEK, + (struct sockaddr *)&addr, &addrlen) < 0) { + rfbLogPerror("rfbCheckFds: UDP: recvfrom"); + rfbDisconnectUDPSock(rfbScreen); + rfbScreen->udpSockConnected = FALSE; + } else { + if (!rfbScreen->udpSockConnected || + (memcmp(&addr, &rfbScreen->udpRemoteAddr, addrlen) != 0)) + { + /* new remote end */ + rfbLog("rfbCheckFds: UDP: got connection\n"); + + memcpy(&rfbScreen->udpRemoteAddr, &addr, addrlen); + rfbScreen->udpSockConnected = TRUE; + + if (connect(rfbScreen->udpSock, + (struct sockaddr *)&addr, addrlen) < 0) { + rfbLogPerror("rfbCheckFds: UDP: connect"); + rfbDisconnectUDPSock(rfbScreen); + return; + } + + rfbNewUDPConnection(rfbScreen,rfbScreen->udpSock); + } + + rfbProcessUDPInput(rfbScreen); + } + + FD_CLR(rfbScreen->udpSock, &fds); + if (--nfds == 0) + return; + } + + i = rfbGetClientIterator(rfbScreen); + while((cl = rfbClientIteratorNext(i))) { + if (cl->onHold) + continue; + if (FD_ISSET(cl->sock, &fds) && FD_ISSET(cl->sock, &(rfbScreen->allFds))) + rfbProcessClientMessage(cl); + } + rfbReleaseClientIterator(i); +} + + +void +rfbDisconnectUDPSock(rfbScreenInfoPtr rfbScreen) +{ + rfbScreen->udpSockConnected = FALSE; +} + + + +void +rfbCloseClient(cl) + rfbClientPtr cl; +{ + LOCK(cl->updateMutex); +#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD + if (cl->sock != -1) +#endif + { + FD_CLR(cl->sock,&(cl->screen->allFds)); + if(cl->sock==cl->screen->maxFd) + while(cl->screen->maxFd>0 + && !FD_ISSET(cl->screen->maxFd,&(cl->screen->allFds))) + cl->screen->maxFd--; + shutdown(cl->sock,SHUT_RDWR); + close(cl->sock); + cl->sock = -1; + } + TSIGNAL(cl->updateCond); + UNLOCK(cl->updateMutex); +} + + +/* + * rfbConnect is called to make a connection out to a given TCP address. + */ + +int +rfbConnect(rfbScreen, host, port) + rfbScreenInfoPtr rfbScreen; + char *host; + int port; +{ + int sock; + int one = 1; + + rfbLog("Making connection to client on host %s port %d\n", + host,port); + + if ((sock = ConnectToTcpAddr(host, port)) < 0) { + rfbLogPerror("connection failed"); + return -1; + } + +#ifndef WIN32 + if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { + rfbLogPerror("fcntl failed"); + close(sock); + return -1; + } +#endif + + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + (char *)&one, sizeof(one)) < 0) { + rfbLogPerror("setsockopt failed"); + close(sock); + return -1; + } + + /* AddEnabledDevice(sock); */ + FD_SET(sock, &rfbScreen->allFds); + rfbScreen->maxFd = max(sock,rfbScreen->maxFd); + + return sock; +} + +/* + * ReadExact reads an exact number of bytes from a client. Returns 1 if + * those bytes have been read, 0 if the other end has closed, or -1 if an error + * occurred (errno is set to ETIMEDOUT if it timed out). + */ + +int +ReadExactTimeout(rfbClientPtr cl, char* buf, int len, int timeout) +{ + int sock = cl->sock; + int n; + fd_set fds; + struct timeval tv; + + while (len > 0) { + n = read(sock, buf, len); + + if (n > 0) { + + buf += n; + len -= n; + + } else if (n == 0) { + + return 0; + + } else { +#ifdef WIN32 + errno = WSAGetLastError(); +#endif + if (errno == EINTR) + continue; + + if (errno != EWOULDBLOCK && errno != EAGAIN) { + return n; + } + + FD_ZERO(&fds); + FD_SET(sock, &fds); + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + n = select(sock+1, &fds, NULL, &fds, &tv); + if (n < 0) { + rfbLogPerror("ReadExact: select"); + return n; + } + if (n == 0) { + errno = ETIMEDOUT; + return -1; + } + } + } + return 1; +} + +int ReadExact(rfbClientPtr cl,char* buf,int len) +{ + return(ReadExactTimeout(cl,buf,len,rfbMaxClientWait)); +} + +/* + * WriteExact writes an exact number of bytes to a client. Returns 1 if + * those bytes have been written, or -1 if an error occurred (errno is set to + * ETIMEDOUT if it timed out). + */ + +int +WriteExact(cl, buf, len) + rfbClientPtr cl; + const char *buf; + int len; +{ + int sock = cl->sock; + int n; + fd_set fds; + struct timeval tv; + int totalTimeWaited = 0; + + LOCK(cl->outputMutex); + while (len > 0) { + n = write(sock, buf, len); + + if (n > 0) { + + buf += n; + len -= n; + + } else if (n == 0) { + + rfbErr("WriteExact: write returned 0?\n"); + return 0; + + } else { +#ifdef WIN32 + errno = WSAGetLastError(); +#endif + if (errno == EINTR) + continue; + + if (errno != EWOULDBLOCK && errno != EAGAIN) { + UNLOCK(cl->outputMutex); + return n; + } + + /* Retry every 5 seconds until we exceed rfbMaxClientWait. We + need to do this because select doesn't necessarily return + immediately when the other end has gone away */ + + FD_ZERO(&fds); + FD_SET(sock, &fds); + tv.tv_sec = 5; + tv.tv_usec = 0; + n = select(sock+1, NULL, &fds, NULL /* &fds */, &tv); + if (n < 0) { + if(errno==EINTR) + continue; + rfbLogPerror("WriteExact: select"); + UNLOCK(cl->outputMutex); + return n; + } + if (n == 0) { + totalTimeWaited += 5000; + if (totalTimeWaited >= rfbMaxClientWait) { + errno = ETIMEDOUT; + UNLOCK(cl->outputMutex); + return -1; + } + } else { + totalTimeWaited = 0; + } + } + } + UNLOCK(cl->outputMutex); + return 1; +} + +int +ListenOnTCPPort(port) + int port; +{ + struct sockaddr_in addr; + int sock; + int one = 1; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + /* addr.sin_addr.s_addr = interface.s_addr; */ + addr.sin_addr.s_addr = INADDR_ANY; + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + return -1; + } + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (char *)&one, sizeof(one)) < 0) { + close(sock); + return -1; + } + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + close(sock); + return -1; + } + if (listen(sock, 5) < 0) { + close(sock); + return -1; + } + + return sock; +} + +int +ConnectToTcpAddr(host, port) + char *host; + int port; +{ + struct hostent *hp; + int sock; + struct sockaddr_in addr; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + + if ((addr.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) + { + if (!(hp = gethostbyname(host))) { + errno = EINVAL; + return -1; + } + addr.sin_addr.s_addr = *(unsigned long *)hp->h_addr; + } + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + return -1; + } + + if (connect(sock, (struct sockaddr *)&addr, (sizeof(addr))) < 0) { + close(sock); + return -1; + } + + return sock; +} + +int +ListenOnUDPPort(port) + int port; +{ + struct sockaddr_in addr; + int sock; + int one = 1; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + /* addr.sin_addr.s_addr = interface.s_addr; */ + addr.sin_addr.s_addr = INADDR_ANY; + + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + return -1; + } + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (char *)&one, sizeof(one)) < 0) { + return -1; + } + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + return -1; + } + + return sock; +} diff --git a/libvncserver/stats.c b/libvncserver/stats.c new file mode 100755 index 0000000..931c5d2 --- /dev/null +++ b/libvncserver/stats.c @@ -0,0 +1,119 @@ +/* + * stats.c + */ + +/* + * Copyright (C) 2002 RealVNC Ltd. + * OSXvnc Copyright (C) 2001 Dan McGuirk . + * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. + * 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. + */ + +#include + +static const char* encNames[] = { + "raw", "copyRect", "RRE", "[encoding 3]", "CoRRE", "hextile", + "zlib", "tight", "[encoding 8]", "[encoding 9]", "[encoding 10]", + "[encoding 11]", "[encoding 12]", "[encoding 13]", "[encoding 14]", +#ifdef LIBVNCSERVER_BACKCHANNEL + "BackChannel", +#else + "[encoding 15]", +#endif + "ZRLE", "[encoding 17]", "[encoding 18]", "[encoding 19]", "[encoding 20]" +}; + + +void +rfbResetStats(rfbClientPtr cl) +{ + int i; + for (i = 0; i < MAX_ENCODINGS; i++) { + cl->rfbBytesSent[i] = 0; + cl->rfbRectanglesSent[i] = 0; + } + cl->rfbLastRectMarkersSent = 0; + cl->rfbLastRectBytesSent = 0; + cl->rfbCursorShapeBytesSent = 0; + cl->rfbCursorShapeUpdatesSent = 0; + cl->rfbCursorPosBytesSent = 0; + cl->rfbCursorPosUpdatesSent = 0; + cl->rfbFramebufferUpdateMessagesSent = 0; + cl->rfbRawBytesEquivalent = 0; + cl->rfbKeyEventsRcvd = 0; + cl->rfbPointerEventsRcvd = 0; +} + +void +rfbPrintStats(rfbClientPtr cl) +{ + int i; + int totalRectanglesSent = 0; + int totalBytesSent = 0; + + rfbLog("Statistics:\n"); + + if ((cl->rfbKeyEventsRcvd != 0) || (cl->rfbPointerEventsRcvd != 0)) + rfbLog(" key events received %d, pointer events %d\n", + cl->rfbKeyEventsRcvd, cl->rfbPointerEventsRcvd); + + for (i = 0; i < MAX_ENCODINGS; i++) { + totalRectanglesSent += cl->rfbRectanglesSent[i]; + totalBytesSent += cl->rfbBytesSent[i]; + } + + totalRectanglesSent += (cl->rfbCursorShapeUpdatesSent + + cl->rfbCursorPosUpdatesSent + + cl->rfbLastRectMarkersSent); + totalBytesSent += (cl->rfbCursorShapeBytesSent + + cl->rfbCursorPosBytesSent + + cl->rfbLastRectBytesSent); + + rfbLog(" framebuffer updates %d, rectangles %d, bytes %d\n", + cl->rfbFramebufferUpdateMessagesSent, totalRectanglesSent, + totalBytesSent); + + if (cl->rfbLastRectMarkersSent != 0) + rfbLog(" LastRect and NewFBSize markers %d, bytes %d\n", + cl->rfbLastRectMarkersSent, cl->rfbLastRectBytesSent); + + if (cl->rfbCursorShapeUpdatesSent != 0) + rfbLog(" cursor shape updates %d, bytes %d\n", + cl->rfbCursorShapeUpdatesSent, cl->rfbCursorShapeBytesSent); + + if (cl->rfbCursorPosUpdatesSent != 0) + rfbLog(" cursor position updates %d, bytes %d\n", + cl->rfbCursorPosUpdatesSent, cl->rfbCursorPosBytesSent); + + for (i = 0; i < MAX_ENCODINGS; i++) { + if (cl->rfbRectanglesSent[i] != 0) + rfbLog(" %s rectangles %d, bytes %d\n", + encNames[i], cl->rfbRectanglesSent[i], cl->rfbBytesSent[i]); + } + + if ((totalBytesSent - cl->rfbBytesSent[rfbEncodingCopyRect]) != 0) { + rfbLog(" raw bytes equivalent %d, compression ratio %f\n", + cl->rfbRawBytesEquivalent, + (double)cl->rfbRawBytesEquivalent + / (double)(totalBytesSent + - cl->rfbBytesSent[rfbEncodingCopyRect]- + cl->rfbCursorShapeBytesSent - + cl->rfbCursorPosBytesSent - + cl->rfbLastRectBytesSent)); + } +} diff --git a/libvncserver/tableinit24.c b/libvncserver/tableinit24.c new file mode 100755 index 0000000..39e9920 --- /dev/null +++ b/libvncserver/tableinit24.c @@ -0,0 +1,157 @@ +/* + 24 bit + */ + +/* + * OSXvnc Copyright (C) 2001 Dan McGuirk . + * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. + * 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. + */ + +static void +rfbInitOneRGBTable24 (uint8_t *table, int inMax, int outMax, int outShift,int swap); + + +static void +rfbInitColourMapSingleTable24(char **table, rfbPixelFormat *in, + rfbPixelFormat *out,rfbColourMap* colourMap) +{ + uint32_t i, r, g, b, outValue; + uint8_t *t; + uint8_t c; + unsigned int nEntries = 1 << in->bitsPerPixel; + int shift = colourMap->is16?16:8; + + if (*table) free(*table); + *table = (char *)malloc(nEntries * 3 + 1); + t = (uint8_t *)*table; + + for (i = 0; i < nEntries; i++) { + r = g = b = 0; + if(i < colourMap->count) { + if(colourMap->is16) { + r = colourMap->data.shorts[3*i+0]; + g = colourMap->data.shorts[3*i+1]; + b = colourMap->data.shorts[3*i+2]; + } else { + r = colourMap->data.bytes[3*i+0]; + g = colourMap->data.bytes[3*i+1]; + b = colourMap->data.bytes[3*i+2]; + } + } + outValue = ((((r * (1 + out->redMax)) >> shift) << out->redShift) | + (((g * (1 + out->greenMax)) >> shift) << out->greenShift) | + (((b * (1 + out->blueMax)) >> shift) << out->blueShift)); + *(uint32_t*)&t[3*i] = outValue; + if(!rfbEndianTest) + memmove(t+3*i,t+3*i+1,3); + if (out->bigEndian != in->bigEndian) { + c = t[3*i]; t[3*i] = t[3*i+2]; t[3*i+2] = c; + } + } +} + +/* + * rfbInitTrueColourSingleTable sets up a single lookup table for truecolour + * translation. + */ + +static void +rfbInitTrueColourSingleTable24 (char **table, rfbPixelFormat *in, + rfbPixelFormat *out) +{ + int i,outValue; + int inRed, inGreen, inBlue, outRed, outGreen, outBlue; + uint8_t *t; + uint8_t c; + int nEntries = 1 << in->bitsPerPixel; + + if (*table) free(*table); + *table = (char *)malloc(nEntries * 3 + 1); + t = (uint8_t *)*table; + + for (i = 0; i < nEntries; i++) { + inRed = (i >> in->redShift) & in->redMax; + inGreen = (i >> in->greenShift) & in->greenMax; + inBlue = (i >> in->blueShift) & in->blueMax; + + outRed = (inRed * out->redMax + in->redMax / 2) / in->redMax; + outGreen = (inGreen * out->greenMax + in->greenMax / 2) / in->greenMax; + outBlue = (inBlue * out->blueMax + in->blueMax / 2) / in->blueMax; + + outValue = ((outRed << out->redShift) | + (outGreen << out->greenShift) | + (outBlue << out->blueShift)); + *(uint32_t*)&t[3*i] = outValue; + if(!rfbEndianTest) + memmove(t+3*i,t+3*i+1,3); + if (out->bigEndian != in->bigEndian) { + c = t[3*i]; t[3*i] = t[3*i+2]; t[3*i+2] = c; + } + } +} + + +/* + * rfbInitTrueColourRGBTables sets up three separate lookup tables for the + * red, green and blue values. + */ + +static void +rfbInitTrueColourRGBTables24 (char **table, rfbPixelFormat *in, + rfbPixelFormat *out) +{ + uint8_t *redTable; + uint8_t *greenTable; + uint8_t *blueTable; + + if (*table) free(*table); + *table = (char *)malloc((in->redMax + in->greenMax + in->blueMax + 3) + * 3 + 1); + redTable = (uint8_t *)*table; + greenTable = redTable + 3*(in->redMax + 1); + blueTable = greenTable + 3*(in->greenMax + 1); + + rfbInitOneRGBTable24 (redTable, in->redMax, out->redMax, + out->redShift, (out->bigEndian != in->bigEndian)); + rfbInitOneRGBTable24 (greenTable, in->greenMax, out->greenMax, + out->greenShift, (out->bigEndian != in->bigEndian)); + rfbInitOneRGBTable24 (blueTable, in->blueMax, out->blueMax, + out->blueShift, (out->bigEndian != in->bigEndian)); +} + +static void +rfbInitOneRGBTable24 (uint8_t *table, int inMax, int outMax, int outShift, + int swap) +{ + int i; + int nEntries = inMax + 1; + uint32_t outValue; + uint8_t c; + + for (i = 0; i < nEntries; i++) { + outValue = ((i * outMax + inMax / 2) / inMax) << outShift; + *(uint32_t *)&table[3*i] = outValue; + if(!rfbEndianTest) + memmove(table+3*i,table+3*i+1,3); + if (swap) { + c = table[3*i]; table[3*i] = table[3*i+2]; + table[3*i+2] = c; + } + } +} diff --git a/libvncserver/tableinitcmtemplate.c b/libvncserver/tableinitcmtemplate.c new file mode 100755 index 0000000..df01b23 --- /dev/null +++ b/libvncserver/tableinitcmtemplate.c @@ -0,0 +1,84 @@ +/* + * tableinitcmtemplate.c - template for initialising lookup tables for + * translation from a colour map to true colour. + * + * This file shouldn't be compiled. It is included multiple times by + * translate.c, each time with a different definition of the macro OUT. + * For each value of OUT, this file defines a function which allocates an + * appropriately sized lookup table and initialises it. + * + * I know this code isn't nice to read because of all the macros, but + * efficiency is important here. + */ + +/* + * OSXvnc Copyright (C) 2001 Dan McGuirk . + * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. + * 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. + */ + +#if !defined(OUT) +#error "This file shouldn't be compiled." +#error "It is included as part of translate.c" +#endif + +#define OUT_T CONCAT3E(uint,OUT,_t) +#define SwapOUT(x) CONCAT2E(Swap,OUT(x)) +#define rfbInitColourMapSingleTableOUT \ + CONCAT2E(rfbInitColourMapSingleTable,OUT) + +static void +rfbInitColourMapSingleTableOUT(char **table, rfbPixelFormat *in, + rfbPixelFormat *out,rfbColourMap* colourMap) +{ + uint32_t i, r, g, b; + OUT_T *t; + uint32_t nEntries = 1 << in->bitsPerPixel; + int shift = colourMap->is16?16:8; + + if (*table) free(*table); + *table = (char *)malloc(nEntries * sizeof(OUT_T)); + t = (OUT_T *)*table; + + for (i = 0; i < nEntries; i++) { + r = g = b = 0; + if(i < colourMap->count) { + if(colourMap->is16) { + r = colourMap->data.shorts[3*i+0]; + g = colourMap->data.shorts[3*i+1]; + b = colourMap->data.shorts[3*i+2]; + } else { + r = colourMap->data.bytes[3*i+0]; + g = colourMap->data.bytes[3*i+1]; + b = colourMap->data.bytes[3*i+2]; + } + } + t[i] = ((((r * (1 + out->redMax)) >> shift) << out->redShift) | + (((g * (1 + out->greenMax)) >> shift) << out->greenShift) | + (((b * (1 + out->blueMax)) >> shift) << out->blueShift)); +#if (OUT != 8) + if (out->bigEndian != in->bigEndian) { + t[i] = SwapOUT(t[i]); + } +#endif + } +} + +#undef OUT_T +#undef SwapOUT +#undef rfbInitColourMapSingleTableOUT diff --git a/libvncserver/tableinittctemplate.c b/libvncserver/tableinittctemplate.c new file mode 100755 index 0000000..8d4f742 --- /dev/null +++ b/libvncserver/tableinittctemplate.c @@ -0,0 +1,142 @@ +/* + * tableinittctemplate.c - template for initialising lookup tables for + * truecolour to truecolour translation. + * + * This file shouldn't be compiled. It is included multiple times by + * translate.c, each time with a different definition of the macro OUT. + * For each value of OUT, this file defines two functions for initialising + * lookup tables. One is for truecolour translation using a single lookup + * table, the other is for truecolour translation using three separate + * lookup tables for the red, green and blue values. + * + * I know this code isn't nice to read because of all the macros, but + * efficiency is important here. + */ + +/* + * OSXvnc Copyright (C) 2001 Dan McGuirk . + * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. + * 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. + */ + +#if !defined(OUT) +#error "This file shouldn't be compiled." +#error "It is included as part of translate.c" +#endif + +#define OUT_T CONCAT3E(uint,OUT,_t) +#define SwapOUT(x) CONCAT2E(Swap,OUT(x)) +#define rfbInitTrueColourSingleTableOUT \ + CONCAT2E(rfbInitTrueColourSingleTable,OUT) +#define rfbInitTrueColourRGBTablesOUT CONCAT2E(rfbInitTrueColourRGBTables,OUT) +#define rfbInitOneRGBTableOUT CONCAT2E(rfbInitOneRGBTable,OUT) + +static void +rfbInitOneRGBTableOUT (OUT_T *table, int inMax, int outMax, int outShift, + int swap); + + +/* + * rfbInitTrueColourSingleTable sets up a single lookup table for truecolour + * translation. + */ + +static void +rfbInitTrueColourSingleTableOUT (char **table, rfbPixelFormat *in, + rfbPixelFormat *out) +{ + int i; + int inRed, inGreen, inBlue, outRed, outGreen, outBlue; + OUT_T *t; + int nEntries = 1 << in->bitsPerPixel; + + if (*table) free(*table); + *table = (char *)malloc(nEntries * sizeof(OUT_T)); + t = (OUT_T *)*table; + + for (i = 0; i < nEntries; i++) { + inRed = (i >> in->redShift) & in->redMax; + inGreen = (i >> in->greenShift) & in->greenMax; + inBlue = (i >> in->blueShift) & in->blueMax; + + outRed = (inRed * out->redMax + in->redMax / 2) / in->redMax; + outGreen = (inGreen * out->greenMax + in->greenMax / 2) / in->greenMax; + outBlue = (inBlue * out->blueMax + in->blueMax / 2) / in->blueMax; + + t[i] = ((outRed << out->redShift) | + (outGreen << out->greenShift) | + (outBlue << out->blueShift)); +#if (OUT != 8) + if (out->bigEndian != in->bigEndian) { + t[i] = SwapOUT(t[i]); + } +#endif + } +} + + +/* + * rfbInitTrueColourRGBTables sets up three separate lookup tables for the + * red, green and blue values. + */ + +static void +rfbInitTrueColourRGBTablesOUT (char **table, rfbPixelFormat *in, + rfbPixelFormat *out) +{ + OUT_T *redTable; + OUT_T *greenTable; + OUT_T *blueTable; + + if (*table) free(*table); + *table = (char *)malloc((in->redMax + in->greenMax + in->blueMax + 3) + * sizeof(OUT_T)); + redTable = (OUT_T *)*table; + greenTable = redTable + in->redMax + 1; + blueTable = greenTable + in->greenMax + 1; + + rfbInitOneRGBTableOUT (redTable, in->redMax, out->redMax, + out->redShift, (out->bigEndian != in->bigEndian)); + rfbInitOneRGBTableOUT (greenTable, in->greenMax, out->greenMax, + out->greenShift, (out->bigEndian != in->bigEndian)); + rfbInitOneRGBTableOUT (blueTable, in->blueMax, out->blueMax, + out->blueShift, (out->bigEndian != in->bigEndian)); +} + +static void +rfbInitOneRGBTableOUT (OUT_T *table, int inMax, int outMax, int outShift, + int swap) +{ + int i; + int nEntries = inMax + 1; + + for (i = 0; i < nEntries; i++) { + table[i] = ((i * outMax + inMax / 2) / inMax) << outShift; +#if (OUT != 8) + if (swap) { + table[i] = SwapOUT(table[i]); + } +#endif + } +} + +#undef OUT_T +#undef SwapOUT +#undef rfbInitTrueColourSingleTableOUT +#undef rfbInitTrueColourRGBTablesOUT +#undef rfbInitOneRGBTableOUT diff --git a/libvncserver/tabletrans24template.c b/libvncserver/tabletrans24template.c new file mode 100755 index 0000000..4b3a0a0 --- /dev/null +++ b/libvncserver/tabletrans24template.c @@ -0,0 +1,281 @@ +/* + * tabletranstemplate.c - template for translation using lookup tables. + * + * This file shouldn't be compiled. It is included multiple times by + * translate.c, each time with different definitions of the macros IN and OUT. + * + * For each pair of values IN and OUT, this file defines two functions for + * translating a given rectangle of pixel data. One uses a single lookup + * table, and the other uses three separate lookup tables for the red, green + * and blue values. + * + * I know this code isn't nice to read because of all the macros, but + * efficiency is important here. + */ + +/* + * OSXvnc Copyright (C) 2001 Dan McGuirk . + * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. + * 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. + */ + +#if !defined(BPP) +#error "This file shouldn't be compiled." +#error "It is included as part of translate.c" +#endif + +#if BPP == 24 + +/* + * rfbTranslateWithSingleTableINtoOUT translates a rectangle of pixel data + * using a single lookup table. + */ + +static void +rfbTranslateWithSingleTable24to24 (char *table, rfbPixelFormat *in, + rfbPixelFormat *out, + char *iptr, char *optr, + int bytesBetweenInputLines, + int width, int height) +{ + uint8_t *ip = (uint8_t *)iptr; + uint8_t *op = (uint8_t *)optr; + int ipextra = bytesBetweenInputLines - width * 3; + uint8_t *opLineEnd; + uint8_t *t = (uint8_t *)table; + int shift = rfbEndianTest?0:8; + uint8_t c; + + while (height > 0) { + opLineEnd = op + width*3; + + while (op < opLineEnd) { + *(uint32_t*)op = t[((*(uint32_t *)ip)>>shift)&0x00ffffff]; + if(!rfbEndianTest) + memmove(op,op+1,3); + if (out->bigEndian != in->bigEndian) { + c = op[0]; op[0] = op[2]; op[2] = c; + } + op += 3; + ip += 3; + } + + ip += ipextra; + height--; + } +} + +/* + * rfbTranslateWithRGBTablesINtoOUT translates a rectangle of pixel data + * using three separate lookup tables for the red, green and blue values. + */ + +static void +rfbTranslateWithRGBTables24to24 (char *table, rfbPixelFormat *in, + rfbPixelFormat *out, + char *iptr, char *optr, + int bytesBetweenInputLines, + int width, int height) +{ + uint8_t *ip = (uint8_t *)iptr; + uint8_t *op = (uint8_t *)optr; + int ipextra = bytesBetweenInputLines - width*3; + uint8_t *opLineEnd; + uint8_t *redTable = (uint8_t *)table; + uint8_t *greenTable = redTable + 3*(in->redMax + 1); + uint8_t *blueTable = greenTable + 3*(in->greenMax + 1); + uint32_t outValue,inValue; + int shift = rfbEndianTest?0:8; + + while (height > 0) { + opLineEnd = op+3*width; + + while (op < opLineEnd) { + inValue = ((*(uint32_t *)ip)>>shift)&0x00ffffff; + outValue = (redTable[(inValue >> in->redShift) & in->redMax] | + greenTable[(inValue >> in->greenShift) & in->greenMax] | + blueTable[(inValue >> in->blueShift) & in->blueMax]); + memcpy(op,&outValue,3); + op += 3; + ip+=3; + } + ip += ipextra; + height--; + } +} + +#else + +#define IN_T CONCAT3E(uint,BPP,_t) +#define OUT_T CONCAT3E(uint,BPP,_t) +#define rfbTranslateWithSingleTable24toOUT \ + CONCAT4E(rfbTranslateWithSingleTable,24,to,BPP) +#define rfbTranslateWithSingleTableINto24 \ + CONCAT4E(rfbTranslateWithSingleTable,BPP,to,24) +#define rfbTranslateWithRGBTables24toOUT \ + CONCAT4E(rfbTranslateWithRGBTables,24,to,BPP) +#define rfbTranslateWithRGBTablesINto24 \ + CONCAT4E(rfbTranslateWithRGBTables,BPP,to,24) + +/* + * rfbTranslateWithSingleTableINtoOUT translates a rectangle of pixel data + * using a single lookup table. + */ + +static void +rfbTranslateWithSingleTable24toOUT (char *table, rfbPixelFormat *in, + rfbPixelFormat *out, + char *iptr, char *optr, + int bytesBetweenInputLines, + int width, int height) +{ + uint8_t *ip = (uint8_t *)iptr; + OUT_T *op = (OUT_T *)optr; + int ipextra = bytesBetweenInputLines - width*3; + OUT_T *opLineEnd; + OUT_T *t = (OUT_T *)table; + int shift = rfbEndianTest?0:8; + + while (height > 0) { + opLineEnd = op + width; + + while (op < opLineEnd) { + *(op++) = t[((*(uint32_t *)ip)>>shift)&0x00ffffff]; + ip+=3; + } + + ip += ipextra; + height--; + } +} + + +/* + * rfbTranslateWithRGBTablesINtoOUT translates a rectangle of pixel data + * using three separate lookup tables for the red, green and blue values. + */ + +static void +rfbTranslateWithRGBTables24toOUT (char *table, rfbPixelFormat *in, + rfbPixelFormat *out, + char *iptr, char *optr, + int bytesBetweenInputLines, + int width, int height) +{ + uint8_t *ip = (uint8_t *)iptr; + OUT_T *op = (OUT_T *)optr; + int ipextra = bytesBetweenInputLines - width*3; + OUT_T *opLineEnd; + OUT_T *redTable = (OUT_T *)table; + OUT_T *greenTable = redTable + in->redMax + 1; + OUT_T *blueTable = greenTable + in->greenMax + 1; + uint32_t inValue; + int shift = rfbEndianTest?0:8; + + while (height > 0) { + opLineEnd = &op[width]; + + while (op < opLineEnd) { + inValue = ((*(uint32_t *)ip)>>shift)&0x00ffffff; + *(op++) = (redTable[(inValue >> in->redShift) & in->redMax] | + greenTable[(inValue >> in->greenShift) & in->greenMax] | + blueTable[(inValue >> in->blueShift) & in->blueMax]); + ip+=3; + } + ip += ipextra; + height--; + } +} + +/* + * rfbTranslateWithSingleTableINto24 translates a rectangle of pixel data + * using a single lookup table. + */ + +static void +rfbTranslateWithSingleTableINto24 (char *table, rfbPixelFormat *in, + rfbPixelFormat *out, + char *iptr, char *optr, + int bytesBetweenInputLines, + int width, int height) +{ + IN_T *ip = (IN_T *)iptr; + uint8_t *op = (uint8_t *)optr; + int ipextra = bytesBetweenInputLines / sizeof(IN_T) - width; + uint8_t *opLineEnd; + uint8_t *t = (uint8_t *)table; + + while (height > 0) { + opLineEnd = op + width * 3; + + while (op < opLineEnd) { + memcpy(op,&t[3*(*(ip++))],3); + op += 3; + } + + ip += ipextra; + height--; + } +} + + +/* + * rfbTranslateWithRGBTablesINto24 translates a rectangle of pixel data + * using three separate lookup tables for the red, green and blue values. + */ + +static void +rfbTranslateWithRGBTablesINto24 (char *table, rfbPixelFormat *in, + rfbPixelFormat *out, + char *iptr, char *optr, + int bytesBetweenInputLines, + int width, int height) +{ + IN_T *ip = (IN_T *)iptr; + uint8_t *op = (uint8_t *)optr; + int ipextra = bytesBetweenInputLines / sizeof(IN_T) - width; + uint8_t *opLineEnd; + uint8_t *redTable = (uint8_t *)table; + uint8_t *greenTable = redTable + 3*(in->redMax + 1); + uint8_t *blueTable = greenTable + 3*(in->greenMax + 1); + uint32_t outValue; + + while (height > 0) { + opLineEnd = op+3*width; + + while (op < opLineEnd) { + outValue = (redTable[(*ip >> in->redShift) & in->redMax] | + greenTable[(*ip >> in->greenShift) & in->greenMax] | + blueTable[(*ip >> in->blueShift) & in->blueMax]); + memcpy(op,&outValue,3); + op += 3; + ip++; + } + ip += ipextra; + height--; + } +} + +#undef IN_T +#undef OUT_T +#undef rfbTranslateWithSingleTable24toOUT +#undef rfbTranslateWithRGBTables24toOUT +#undef rfbTranslateWithSingleTableINto24 +#undef rfbTranslateWithRGBTablesINto24 + +#endif diff --git a/libvncserver/tabletranstemplate.c b/libvncserver/tabletranstemplate.c new file mode 100755 index 0000000..e83c623 --- /dev/null +++ b/libvncserver/tabletranstemplate.c @@ -0,0 +1,117 @@ +/* + * tabletranstemplate.c - template for translation using lookup tables. + * + * This file shouldn't be compiled. It is included multiple times by + * translate.c, each time with different definitions of the macros IN and OUT. + * + * For each pair of values IN and OUT, this file defines two functions for + * translating a given rectangle of pixel data. One uses a single lookup + * table, and the other uses three separate lookup tables for the red, green + * and blue values. + * + * I know this code isn't nice to read because of all the macros, but + * efficiency is important here. + */ + +/* + * OSXvnc Copyright (C) 2001 Dan McGuirk . + * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. + * 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. + */ + +#if !defined(IN) || !defined(OUT) +#error "This file shouldn't be compiled." +#error "It is included as part of translate.c" +#endif + +#define IN_T CONCAT3E(uint,IN,_t) +#define OUT_T CONCAT3E(uint,OUT,_t) +#define rfbTranslateWithSingleTableINtoOUT \ + CONCAT4E(rfbTranslateWithSingleTable,IN,to,OUT) +#define rfbTranslateWithRGBTablesINtoOUT \ + CONCAT4E(rfbTranslateWithRGBTables,IN,to,OUT) + +/* + * rfbTranslateWithSingleTableINtoOUT translates a rectangle of pixel data + * using a single lookup table. + */ + +static void +rfbTranslateWithSingleTableINtoOUT (char *table, rfbPixelFormat *in, + rfbPixelFormat *out, + char *iptr, char *optr, + int bytesBetweenInputLines, + int width, int height) +{ + IN_T *ip = (IN_T *)iptr; + OUT_T *op = (OUT_T *)optr; + int ipextra = bytesBetweenInputLines / sizeof(IN_T) - width; + OUT_T *opLineEnd; + OUT_T *t = (OUT_T *)table; + + while (height > 0) { + opLineEnd = op + width; + + while (op < opLineEnd) { + *(op++) = t[*(ip++)]; + } + + ip += ipextra; + height--; + } +} + + +/* + * rfbTranslateWithRGBTablesINtoOUT translates a rectangle of pixel data + * using three separate lookup tables for the red, green and blue values. + */ + +static void +rfbTranslateWithRGBTablesINtoOUT (char *table, rfbPixelFormat *in, + rfbPixelFormat *out, + char *iptr, char *optr, + int bytesBetweenInputLines, + int width, int height) +{ + IN_T *ip = (IN_T *)iptr; + OUT_T *op = (OUT_T *)optr; + int ipextra = bytesBetweenInputLines / sizeof(IN_T) - width; + OUT_T *opLineEnd; + OUT_T *redTable = (OUT_T *)table; + OUT_T *greenTable = redTable + in->redMax + 1; + OUT_T *blueTable = greenTable + in->greenMax + 1; + + while (height > 0) { + opLineEnd = &op[width]; + + while (op < opLineEnd) { + *(op++) = (redTable[(*ip >> in->redShift) & in->redMax] | + greenTable[(*ip >> in->greenShift) & in->greenMax] | + blueTable[(*ip >> in->blueShift) & in->blueMax]); + ip++; + } + ip += ipextra; + height--; + } +} + +#undef IN_T +#undef OUT_T +#undef rfbTranslateWithSingleTableINtoOUT +#undef rfbTranslateWithRGBTablesINtoOUT diff --git a/libvncserver/tight.c b/libvncserver/tight.c new file mode 100644 index 0000000..b97adb9 --- /dev/null +++ b/libvncserver/tight.c @@ -0,0 +1,1820 @@ +/* + * tight.c + * + * Routines to implement Tight Encoding + */ + +/* + * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. 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. + */ + +/*#include */ +#include + +#ifdef WIN32 +#define XMD_H +#undef FAR +#define NEEDFAR_POINTERS +#endif + +#include + +/* Note: The following constant should not be changed. */ +#define TIGHT_MIN_TO_COMPRESS 12 + +/* The parameters below may be adjusted. */ +#define MIN_SPLIT_RECT_SIZE 4096 +#define MIN_SOLID_SUBRECT_SIZE 2048 +#define MAX_SPLIT_TILE_SIZE 16 + +/* May be set to TRUE with "-lazytight" Xvnc option. */ +rfbBool rfbTightDisableGradient = FALSE; + +/* This variable is set on every rfbSendRectEncodingTight() call. */ +static rfbBool usePixelFormat24; + + +/* Compression level stuff. The following array contains various + encoder parameters for each of 10 compression levels (0..9). + Last three parameters correspond to JPEG quality levels (0..9). */ + +typedef struct TIGHT_CONF_s { + int maxRectSize, maxRectWidth; + int monoMinRectSize, gradientMinRectSize; + int idxZlibLevel, monoZlibLevel, rawZlibLevel, gradientZlibLevel; + int gradientThreshold, gradientThreshold24; + int idxMaxColorsDivisor; + int jpegQuality, jpegThreshold, jpegThreshold24; +} TIGHT_CONF; + +static TIGHT_CONF tightConf[10] = { + { 512, 32, 6, 65536, 0, 0, 0, 0, 0, 0, 4, 5, 10000, 23000 }, + { 2048, 128, 6, 65536, 1, 1, 1, 0, 0, 0, 8, 10, 8000, 18000 }, + { 6144, 256, 8, 65536, 3, 3, 2, 0, 0, 0, 24, 15, 6500, 15000 }, + { 10240, 1024, 12, 65536, 5, 5, 3, 0, 0, 0, 32, 25, 5000, 12000 }, + { 16384, 2048, 12, 65536, 6, 6, 4, 0, 0, 0, 32, 37, 4000, 10000 }, + { 32768, 2048, 12, 4096, 7, 7, 5, 4, 150, 380, 32, 50, 3000, 8000 }, + { 65536, 2048, 16, 4096, 7, 7, 6, 4, 170, 420, 48, 60, 2000, 5000 }, + { 65536, 2048, 16, 4096, 8, 8, 7, 5, 180, 450, 64, 70, 1000, 2500 }, + { 65536, 2048, 32, 8192, 9, 9, 8, 6, 190, 475, 64, 75, 500, 1200 }, + { 65536, 2048, 32, 8192, 9, 9, 9, 6, 200, 500, 96, 80, 200, 500 } +}; + +static int compressLevel; +static int qualityLevel; + +/* Stuff dealing with palettes. */ + +typedef struct COLOR_LIST_s { + struct COLOR_LIST_s *next; + int idx; + uint32_t rgb; +} COLOR_LIST; + +typedef struct PALETTE_ENTRY_s { + COLOR_LIST *listNode; + int numPixels; +} PALETTE_ENTRY; + +typedef struct PALETTE_s { + PALETTE_ENTRY entry[256]; + COLOR_LIST *hash[256]; + COLOR_LIST list[256]; +} PALETTE; + +static int paletteNumColors, paletteMaxColors; +static uint32_t monoBackground, monoForeground; +static PALETTE palette; + +/* Pointers to dynamically-allocated buffers. */ + +static int tightBeforeBufSize = 0; +static char *tightBeforeBuf = NULL; + +static int tightAfterBufSize = 0; +static char *tightAfterBuf = NULL; + +static int *prevRowBuf = NULL; + +void TightCleanup() +{ + if(tightBeforeBufSize) { + free(tightBeforeBuf); + tightBeforeBufSize=0; + } + if(tightAfterBufSize) { + free(tightAfterBuf); + tightAfterBufSize=0; + } +} + +/* Prototypes for static functions. */ + +static void FindBestSolidArea (rfbClientPtr cl, int x, int y, int w, int h, + uint32_t colorValue, int *w_ptr, int *h_ptr); +static void ExtendSolidArea (rfbClientPtr cl, int x, int y, int w, int h, + uint32_t colorValue, + int *x_ptr, int *y_ptr, int *w_ptr, int *h_ptr); +static rfbBool CheckSolidTile (rfbClientPtr cl, int x, int y, int w, int h, + uint32_t *colorPtr, rfbBool needSameColor); +static rfbBool CheckSolidTile8 (rfbClientPtr cl, int x, int y, int w, int h, + uint32_t *colorPtr, rfbBool needSameColor); +static rfbBool CheckSolidTile16 (rfbClientPtr cl, int x, int y, int w, int h, + uint32_t *colorPtr, rfbBool needSameColor); +static rfbBool CheckSolidTile32 (rfbClientPtr cl, int x, int y, int w, int h, + uint32_t *colorPtr, rfbBool needSameColor); + +static rfbBool SendRectSimple (rfbClientPtr cl, int x, int y, int w, int h); +static rfbBool SendSubrect (rfbClientPtr cl, int x, int y, int w, int h); +static rfbBool SendTightHeader (rfbClientPtr cl, int x, int y, int w, int h); + +static rfbBool SendSolidRect (rfbClientPtr cl); +static rfbBool SendMonoRect (rfbClientPtr cl, int w, int h); +static rfbBool SendIndexedRect (rfbClientPtr cl, int w, int h); +static rfbBool SendFullColorRect (rfbClientPtr cl, int w, int h); +static rfbBool SendGradientRect (rfbClientPtr cl, int w, int h); + +static rfbBool CompressData(rfbClientPtr cl, int streamId, int dataLen, + int zlibLevel, int zlibStrategy); +static rfbBool SendCompressedData(rfbClientPtr cl, int compressedLen); + +static void FillPalette8(int count); +static void FillPalette16(int count); +static void FillPalette32(int count); + +static void PaletteReset(void); +static int PaletteInsert(uint32_t rgb, int numPixels, int bpp); + +static void Pack24(rfbClientPtr cl, char *buf, rfbPixelFormat *fmt, int count); + +static void EncodeIndexedRect16(uint8_t *buf, int count); +static void EncodeIndexedRect32(uint8_t *buf, int count); + +static void EncodeMonoRect8(uint8_t *buf, int w, int h); +static void EncodeMonoRect16(uint8_t *buf, int w, int h); +static void EncodeMonoRect32(uint8_t *buf, int w, int h); + +static void FilterGradient24(rfbClientPtr cl, char *buf, rfbPixelFormat *fmt, int w, int h); +static void FilterGradient16(rfbClientPtr cl, uint16_t *buf, rfbPixelFormat *fmt, int w, int h); +static void FilterGradient32(rfbClientPtr cl, uint32_t *buf, rfbPixelFormat *fmt, int w, int h); + +static int DetectSmoothImage(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h); +static unsigned long DetectSmoothImage24(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h); +static unsigned long DetectSmoothImage16(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h); +static unsigned long DetectSmoothImage32(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h); + +static rfbBool SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, + int quality); +static void PrepareRowForJpeg(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); +static void PrepareRowForJpeg24(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); +static void PrepareRowForJpeg16(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); +static void PrepareRowForJpeg32(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); + +static void JpegInitDestination(j_compress_ptr cinfo); +static boolean JpegEmptyOutputBuffer(j_compress_ptr cinfo); +static void JpegTermDestination(j_compress_ptr cinfo); +static void JpegSetDstManager(j_compress_ptr cinfo); + + +/* + * Tight encoding implementation. + */ + +int +rfbNumCodedRectsTight(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + int maxRectSize, maxRectWidth; + int subrectMaxWidth, subrectMaxHeight; + + /* No matter how many rectangles we will send if LastRect markers + are used to terminate rectangle stream. */ + if (cl->enableLastRectEncoding && w * h >= MIN_SPLIT_RECT_SIZE) + return 0; + + maxRectSize = tightConf[cl->tightCompressLevel].maxRectSize; + maxRectWidth = tightConf[cl->tightCompressLevel].maxRectWidth; + + if (w > maxRectWidth || w * h > maxRectSize) { + subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w; + subrectMaxHeight = maxRectSize / subrectMaxWidth; + return (((w - 1) / maxRectWidth + 1) * + ((h - 1) / subrectMaxHeight + 1)); + } else { + return 1; + } +} + +rfbBool +rfbSendRectEncodingTight(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + int nMaxRows; + uint32_t colorValue; + int dx, dy, dw, dh; + int x_best, y_best, w_best, h_best; + char *fbptr; + + rfbSendUpdateBuf(cl); + + compressLevel = cl->tightCompressLevel; + qualityLevel = cl->tightQualityLevel; + + if ( cl->format.depth == 24 && cl->format.redMax == 0xFF && + cl->format.greenMax == 0xFF && cl->format.blueMax == 0xFF ) { + usePixelFormat24 = TRUE; + } else { + usePixelFormat24 = FALSE; + } + + if (!cl->enableLastRectEncoding || w * h < MIN_SPLIT_RECT_SIZE) + return SendRectSimple(cl, x, y, w, h); + + /* Make sure we can write at least one pixel into tightBeforeBuf. */ + + if (tightBeforeBufSize < 4) { + tightBeforeBufSize = 4; + if (tightBeforeBuf == NULL) + tightBeforeBuf = (char *)malloc(tightBeforeBufSize); + else + tightBeforeBuf = (char *)realloc(tightBeforeBuf, + tightBeforeBufSize); + } + + /* Calculate maximum number of rows in one non-solid rectangle. */ + + { + int maxRectSize, maxRectWidth, nMaxWidth; + + maxRectSize = tightConf[compressLevel].maxRectSize; + maxRectWidth = tightConf[compressLevel].maxRectWidth; + nMaxWidth = (w > maxRectWidth) ? maxRectWidth : w; + nMaxRows = maxRectSize / nMaxWidth; + } + + /* Try to find large solid-color areas and send them separately. */ + + for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) { + + /* If a rectangle becomes too large, send its upper part now. */ + + if (dy - y >= nMaxRows) { + if (!SendRectSimple(cl, x, y, w, nMaxRows)) + return 0; + y += nMaxRows; + h -= nMaxRows; + } + + dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ? + MAX_SPLIT_TILE_SIZE : (y + h - dy); + + for (dx = x; dx < x + w; dx += MAX_SPLIT_TILE_SIZE) { + + dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w) ? + MAX_SPLIT_TILE_SIZE : (x + w - dx); + + if (CheckSolidTile(cl, dx, dy, dw, dh, &colorValue, FALSE)) { + + /* Get dimensions of solid-color area. */ + + FindBestSolidArea(cl, dx, dy, w - (dx - x), h - (dy - y), + colorValue, &w_best, &h_best); + + /* Make sure a solid rectangle is large enough + (or the whole rectangle is of the same color). */ + + if ( w_best * h_best != w * h && + w_best * h_best < MIN_SOLID_SUBRECT_SIZE ) + continue; + + /* Try to extend solid rectangle to maximum size. */ + + x_best = dx; y_best = dy; + ExtendSolidArea(cl, x, y, w, h, colorValue, + &x_best, &y_best, &w_best, &h_best); + + /* Send rectangles at top and left to solid-color area. */ + + if ( y_best != y && + !SendRectSimple(cl, x, y, w, y_best-y) ) + return FALSE; + if ( x_best != x && + !rfbSendRectEncodingTight(cl, x, y_best, + x_best-x, h_best) ) + return FALSE; + + /* Send solid-color rectangle. */ + + if (!SendTightHeader(cl, x_best, y_best, w_best, h_best)) + return FALSE; + + fbptr = (cl->screen->frameBuffer + + (cl->screen->paddedWidthInBytes * y_best) + + (x_best * (cl->screen->bitsPerPixel / 8))); + + (*cl->translateFn)(cl->translateLookupTable, &cl->screen->rfbServerFormat, + &cl->format, fbptr, tightBeforeBuf, + cl->screen->paddedWidthInBytes, 1, 1); + + if (!SendSolidRect(cl)) + return FALSE; + + /* Send remaining rectangles (at right and bottom). */ + + if ( x_best + w_best != x + w && + !rfbSendRectEncodingTight(cl, x_best+w_best, y_best, + w-(x_best-x)-w_best, h_best) ) + return FALSE; + if ( y_best + h_best != y + h && + !rfbSendRectEncodingTight(cl, x, y_best+h_best, + w, h-(y_best-y)-h_best) ) + return FALSE; + + /* Return after all recursive calls are done. */ + + return TRUE; + } + + } + + } + + /* No suitable solid-color rectangles found. */ + + return SendRectSimple(cl, x, y, w, h); +} + +static void +FindBestSolidArea(cl, x, y, w, h, colorValue, w_ptr, h_ptr) + rfbClientPtr cl; + int x, y, w, h; + uint32_t colorValue; + int *w_ptr, *h_ptr; +{ + int dx, dy, dw, dh; + int w_prev; + int w_best = 0, h_best = 0; + + w_prev = w; + + for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) { + + dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ? + MAX_SPLIT_TILE_SIZE : (y + h - dy); + dw = (w_prev > MAX_SPLIT_TILE_SIZE) ? + MAX_SPLIT_TILE_SIZE : w_prev; + + if (!CheckSolidTile(cl, x, dy, dw, dh, &colorValue, TRUE)) + break; + + for (dx = x + dw; dx < x + w_prev;) { + dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w_prev) ? + MAX_SPLIT_TILE_SIZE : (x + w_prev - dx); + if (!CheckSolidTile(cl, dx, dy, dw, dh, &colorValue, TRUE)) + break; + dx += dw; + } + + w_prev = dx - x; + if (w_prev * (dy + dh - y) > w_best * h_best) { + w_best = w_prev; + h_best = dy + dh - y; + } + } + + *w_ptr = w_best; + *h_ptr = h_best; +} + +static void +ExtendSolidArea(cl, x, y, w, h, colorValue, x_ptr, y_ptr, w_ptr, h_ptr) + rfbClientPtr cl; + int x, y, w, h; + uint32_t colorValue; + int *x_ptr, *y_ptr, *w_ptr, *h_ptr; +{ + int cx, cy; + + /* Try to extend the area upwards. */ + for ( cy = *y_ptr - 1; + cy >= y && CheckSolidTile(cl, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE); + cy-- ); + *h_ptr += *y_ptr - (cy + 1); + *y_ptr = cy + 1; + + /* ... downwards. */ + for ( cy = *y_ptr + *h_ptr; + cy < y + h && + CheckSolidTile(cl, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE); + cy++ ); + *h_ptr += cy - (*y_ptr + *h_ptr); + + /* ... to the left. */ + for ( cx = *x_ptr - 1; + cx >= x && CheckSolidTile(cl, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE); + cx-- ); + *w_ptr += *x_ptr - (cx + 1); + *x_ptr = cx + 1; + + /* ... to the right. */ + for ( cx = *x_ptr + *w_ptr; + cx < x + w && + CheckSolidTile(cl, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE); + cx++ ); + *w_ptr += cx - (*x_ptr + *w_ptr); +} + +/* + * Check if a rectangle is all of the same color. If needSameColor is + * set to non-zero, then also check that its color equals to the + * *colorPtr value. The result is 1 if the test is successfull, and in + * that case new color will be stored in *colorPtr. + */ + +static rfbBool CheckSolidTile(rfbClientPtr cl, int x, int y, int w, int h, uint32_t* colorPtr, rfbBool needSameColor) +{ + switch(cl->screen->rfbServerFormat.bitsPerPixel) { + case 32: + return CheckSolidTile32(cl, x, y, w, h, colorPtr, needSameColor); + case 16: + return CheckSolidTile16(cl, x, y, w, h, colorPtr, needSameColor); + default: + return CheckSolidTile8(cl, x, y, w, h, colorPtr, needSameColor); + } +} + +#define DEFINE_CHECK_SOLID_FUNCTION(bpp) \ + \ +static rfbBool \ +CheckSolidTile##bpp(rfbClientPtr cl, int x, int y, int w, int h, uint32_t* colorPtr, rfbBool needSameColor) \ +{ \ + uint##bpp##_t *fbptr; \ + uint##bpp##_t colorValue; \ + int dx, dy; \ + \ + fbptr = (uint##bpp##_t *) \ + &cl->screen->frameBuffer[y * cl->screen->paddedWidthInBytes + x * (bpp/8)]; \ + \ + colorValue = *fbptr; \ + if (needSameColor && (uint32_t)colorValue != *colorPtr) \ + return FALSE; \ + \ + for (dy = 0; dy < h; dy++) { \ + for (dx = 0; dx < w; dx++) { \ + if (colorValue != fbptr[dx]) \ + return FALSE; \ + } \ + fbptr = (uint##bpp##_t *)((uint8_t *)fbptr + cl->screen->paddedWidthInBytes); \ + } \ + \ + *colorPtr = (uint32_t)colorValue; \ + return TRUE; \ +} + +DEFINE_CHECK_SOLID_FUNCTION(8) +DEFINE_CHECK_SOLID_FUNCTION(16) +DEFINE_CHECK_SOLID_FUNCTION(32) + +static rfbBool +SendRectSimple(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + int maxBeforeSize, maxAfterSize; + int maxRectSize, maxRectWidth; + int subrectMaxWidth, subrectMaxHeight; + int dx, dy; + int rw, rh; + + maxRectSize = tightConf[compressLevel].maxRectSize; + maxRectWidth = tightConf[compressLevel].maxRectWidth; + + maxBeforeSize = maxRectSize * (cl->format.bitsPerPixel / 8); + maxAfterSize = maxBeforeSize + (maxBeforeSize + 99) / 100 + 12; + + if (tightBeforeBufSize < maxBeforeSize) { + tightBeforeBufSize = maxBeforeSize; + if (tightBeforeBuf == NULL) + tightBeforeBuf = (char *)malloc(tightBeforeBufSize); + else + tightBeforeBuf = (char *)realloc(tightBeforeBuf, + tightBeforeBufSize); + } + + if (tightAfterBufSize < maxAfterSize) { + tightAfterBufSize = maxAfterSize; + if (tightAfterBuf == NULL) + tightAfterBuf = (char *)malloc(tightAfterBufSize); + else + tightAfterBuf = (char *)realloc(tightAfterBuf, + tightAfterBufSize); + } + + if (w > maxRectWidth || w * h > maxRectSize) { + subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w; + subrectMaxHeight = maxRectSize / subrectMaxWidth; + + for (dy = 0; dy < h; dy += subrectMaxHeight) { + for (dx = 0; dx < w; dx += maxRectWidth) { + rw = (dx + maxRectWidth < w) ? maxRectWidth : w - dx; + rh = (dy + subrectMaxHeight < h) ? subrectMaxHeight : h - dy; + if (!SendSubrect(cl, x+dx, y+dy, rw, rh)) + return FALSE; + } + } + } else { + if (!SendSubrect(cl, x, y, w, h)) + return FALSE; + } + + return TRUE; +} + +static rfbBool +SendSubrect(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + char *fbptr; + rfbBool success = FALSE; + + /* Send pending data if there is more than 128 bytes. */ + if (cl->ublen > 128) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + if (!SendTightHeader(cl, x, y, w, h)) + return FALSE; + + fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y) + + (x * (cl->screen->bitsPerPixel / 8))); + + (*cl->translateFn)(cl->translateLookupTable, &cl->screen->rfbServerFormat, + &cl->format, fbptr, tightBeforeBuf, + cl->screen->paddedWidthInBytes, w, h); + + paletteMaxColors = w * h / tightConf[compressLevel].idxMaxColorsDivisor; + if ( paletteMaxColors < 2 && + w * h >= tightConf[compressLevel].monoMinRectSize ) { + paletteMaxColors = 2; + } + switch (cl->format.bitsPerPixel) { + case 8: + FillPalette8(w * h); + break; + case 16: + FillPalette16(w * h); + break; + default: + FillPalette32(w * h); + } + + switch (paletteNumColors) { + case 0: + /* Truecolor image */ + if (DetectSmoothImage(cl, &cl->format, w, h)) { + if (qualityLevel != -1) { + success = SendJpegRect(cl, x, y, w, h, + tightConf[qualityLevel].jpegQuality); + } else { + success = SendGradientRect(cl, w, h); + } + } else { + success = SendFullColorRect(cl, w, h); + } + break; + case 1: + /* Solid rectangle */ + success = SendSolidRect(cl); + break; + case 2: + /* Two-color rectangle */ + success = SendMonoRect(cl, w, h); + break; + default: + /* Up to 256 different colors */ + if ( paletteNumColors > 96 && + qualityLevel != -1 && qualityLevel <= 3 && + DetectSmoothImage(cl, &cl->format, w, h) ) { + success = SendJpegRect(cl, x, y, w, h, + tightConf[qualityLevel].jpegQuality); + } else { + success = SendIndexedRect(cl, w, h); + } + } + return success; +} + +static rfbBool +SendTightHeader(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + rfbFramebufferUpdateRectHeader rect; + + if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + rect.r.x = Swap16IfLE(x); + rect.r.y = Swap16IfLE(y); + rect.r.w = Swap16IfLE(w); + rect.r.h = Swap16IfLE(h); + rect.encoding = Swap32IfLE(rfbEncodingTight); + + memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + cl->ublen += sz_rfbFramebufferUpdateRectHeader; + + cl->rfbRectanglesSent[rfbEncodingTight]++; + cl->rfbBytesSent[rfbEncodingTight] += sz_rfbFramebufferUpdateRectHeader; + + return TRUE; +} + +/* + * Subencoding implementations. + */ + +static rfbBool +SendSolidRect(cl) + rfbClientPtr cl; +{ + int len; + + if (usePixelFormat24) { + Pack24(cl, tightBeforeBuf, &cl->format, 1); + len = 3; + } else + len = cl->format.bitsPerPixel / 8; + + if (cl->ublen + 1 + len > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + cl->updateBuf[cl->ublen++] = (char)(rfbTightFill << 4); + memcpy (&cl->updateBuf[cl->ublen], tightBeforeBuf, len); + cl->ublen += len; + + cl->rfbBytesSent[rfbEncodingTight] += len + 1; + + return TRUE; +} + +static rfbBool +SendMonoRect(cl, w, h) + rfbClientPtr cl; + int w, h; +{ + int streamId = 1; + int paletteLen, dataLen; + + if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 + + 2 * cl->format.bitsPerPixel / 8 > UPDATE_BUF_SIZE ) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + /* Prepare tight encoding header. */ + dataLen = (w + 7) / 8; + dataLen *= h; + + cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4; + cl->updateBuf[cl->ublen++] = rfbTightFilterPalette; + cl->updateBuf[cl->ublen++] = 1; + + /* Prepare palette, convert image. */ + switch (cl->format.bitsPerPixel) { + + case 32: + EncodeMonoRect32((uint8_t *)tightBeforeBuf, w, h); + + ((uint32_t *)tightAfterBuf)[0] = monoBackground; + ((uint32_t *)tightAfterBuf)[1] = monoForeground; + if (usePixelFormat24) { + Pack24(cl, tightAfterBuf, &cl->format, 2); + paletteLen = 6; + } else + paletteLen = 8; + + memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteLen); + cl->ublen += paletteLen; + cl->rfbBytesSent[rfbEncodingTight] += 3 + paletteLen; + break; + + case 16: + EncodeMonoRect16((uint8_t *)tightBeforeBuf, w, h); + + ((uint16_t *)tightAfterBuf)[0] = (uint16_t)monoBackground; + ((uint16_t *)tightAfterBuf)[1] = (uint16_t)monoForeground; + + memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, 4); + cl->ublen += 4; + cl->rfbBytesSent[rfbEncodingTight] += 7; + break; + + default: + EncodeMonoRect8((uint8_t *)tightBeforeBuf, w, h); + + cl->updateBuf[cl->ublen++] = (char)monoBackground; + cl->updateBuf[cl->ublen++] = (char)monoForeground; + cl->rfbBytesSent[rfbEncodingTight] += 5; + } + + return CompressData(cl, streamId, dataLen, + tightConf[compressLevel].monoZlibLevel, + Z_DEFAULT_STRATEGY); +} + +static rfbBool +SendIndexedRect(cl, w, h) + rfbClientPtr cl; + int w, h; +{ + int streamId = 2; + int i, entryLen; + + if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 + + paletteNumColors * cl->format.bitsPerPixel / 8 > + UPDATE_BUF_SIZE ) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + /* Prepare tight encoding header. */ + cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4; + cl->updateBuf[cl->ublen++] = rfbTightFilterPalette; + cl->updateBuf[cl->ublen++] = (char)(paletteNumColors - 1); + + /* Prepare palette, convert image. */ + switch (cl->format.bitsPerPixel) { + + case 32: + EncodeIndexedRect32((uint8_t *)tightBeforeBuf, w * h); + + for (i = 0; i < paletteNumColors; i++) { + ((uint32_t *)tightAfterBuf)[i] = + palette.entry[i].listNode->rgb; + } + if (usePixelFormat24) { + Pack24(cl, tightAfterBuf, &cl->format, paletteNumColors); + entryLen = 3; + } else + entryLen = 4; + + memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * entryLen); + cl->ublen += paletteNumColors * entryLen; + cl->rfbBytesSent[rfbEncodingTight] += 3 + paletteNumColors * entryLen; + break; + + case 16: + EncodeIndexedRect16((uint8_t *)tightBeforeBuf, w * h); + + for (i = 0; i < paletteNumColors; i++) { + ((uint16_t *)tightAfterBuf)[i] = + (uint16_t)palette.entry[i].listNode->rgb; + } + + memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * 2); + cl->ublen += paletteNumColors * 2; + cl->rfbBytesSent[rfbEncodingTight] += 3 + paletteNumColors * 2; + break; + + default: + return FALSE; /* Should never happen. */ + } + + return CompressData(cl, streamId, w * h, + tightConf[compressLevel].idxZlibLevel, + Z_DEFAULT_STRATEGY); +} + +static rfbBool +SendFullColorRect(cl, w, h) + rfbClientPtr cl; + int w, h; +{ + int streamId = 0; + int len; + + if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + cl->updateBuf[cl->ublen++] = 0x00; /* stream id = 0, no flushing, no filter */ + cl->rfbBytesSent[rfbEncodingTight]++; + + if (usePixelFormat24) { + Pack24(cl, tightBeforeBuf, &cl->format, w * h); + len = 3; + } else + len = cl->format.bitsPerPixel / 8; + + return CompressData(cl, streamId, w * h * len, + tightConf[compressLevel].rawZlibLevel, + Z_DEFAULT_STRATEGY); +} + +static rfbBool +SendGradientRect(cl, w, h) + rfbClientPtr cl; + int w, h; +{ + int streamId = 3; + int len; + + if (cl->format.bitsPerPixel == 8) + return SendFullColorRect(cl, w, h); + + if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 2 > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + if (prevRowBuf == NULL) + prevRowBuf = (int *)malloc(2048 * 3 * sizeof(int)); + + cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4; + cl->updateBuf[cl->ublen++] = rfbTightFilterGradient; + cl->rfbBytesSent[rfbEncodingTight] += 2; + + if (usePixelFormat24) { + FilterGradient24(cl, tightBeforeBuf, &cl->format, w, h); + len = 3; + } else if (cl->format.bitsPerPixel == 32) { + FilterGradient32(cl, (uint32_t *)tightBeforeBuf, &cl->format, w, h); + len = 4; + } else { + FilterGradient16(cl, (uint16_t *)tightBeforeBuf, &cl->format, w, h); + len = 2; + } + + return CompressData(cl, streamId, w * h * len, + tightConf[compressLevel].gradientZlibLevel, + Z_FILTERED); +} + +static rfbBool +CompressData(cl, streamId, dataLen, zlibLevel, zlibStrategy) + rfbClientPtr cl; + int streamId, dataLen, zlibLevel, zlibStrategy; +{ + z_streamp pz; + int err; + + if (dataLen < TIGHT_MIN_TO_COMPRESS) { + memcpy(&cl->updateBuf[cl->ublen], tightBeforeBuf, dataLen); + cl->ublen += dataLen; + cl->rfbBytesSent[rfbEncodingTight] += dataLen; + return TRUE; + } + + pz = &cl->zsStruct[streamId]; + + /* Initialize compression stream if needed. */ + if (!cl->zsActive[streamId]) { + pz->zalloc = Z_NULL; + pz->zfree = Z_NULL; + pz->opaque = Z_NULL; + + err = deflateInit2 (pz, zlibLevel, Z_DEFLATED, MAX_WBITS, + MAX_MEM_LEVEL, zlibStrategy); + if (err != Z_OK) + return FALSE; + + cl->zsActive[streamId] = TRUE; + cl->zsLevel[streamId] = zlibLevel; + } + + /* Prepare buffer pointers. */ + pz->next_in = (Bytef *)tightBeforeBuf; + pz->avail_in = dataLen; + pz->next_out = (Bytef *)tightAfterBuf; + pz->avail_out = tightAfterBufSize; + + /* Change compression parameters if needed. */ + if (zlibLevel != cl->zsLevel[streamId]) { + if (deflateParams (pz, zlibLevel, zlibStrategy) != Z_OK) { + return FALSE; + } + cl->zsLevel[streamId] = zlibLevel; + } + + /* Actual compression. */ + if ( deflate (pz, Z_SYNC_FLUSH) != Z_OK || + pz->avail_in != 0 || pz->avail_out == 0 ) { + return FALSE; + } + + return SendCompressedData(cl, tightAfterBufSize - pz->avail_out); +} + +static rfbBool SendCompressedData(cl, compressedLen) + rfbClientPtr cl; + int compressedLen; +{ + int i, portionLen; + + cl->updateBuf[cl->ublen++] = compressedLen & 0x7F; + cl->rfbBytesSent[rfbEncodingTight]++; + if (compressedLen > 0x7F) { + cl->updateBuf[cl->ublen-1] |= 0x80; + cl->updateBuf[cl->ublen++] = compressedLen >> 7 & 0x7F; + cl->rfbBytesSent[rfbEncodingTight]++; + if (compressedLen > 0x3FFF) { + cl->updateBuf[cl->ublen-1] |= 0x80; + cl->updateBuf[cl->ublen++] = compressedLen >> 14 & 0xFF; + cl->rfbBytesSent[rfbEncodingTight]++; + } + } + + portionLen = UPDATE_BUF_SIZE; + for (i = 0; i < compressedLen; i += portionLen) { + if (i + portionLen > compressedLen) { + portionLen = compressedLen - i; + } + if (cl->ublen + portionLen > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + memcpy(&cl->updateBuf[cl->ublen], &tightAfterBuf[i], portionLen); + cl->ublen += portionLen; + } + cl->rfbBytesSent[rfbEncodingTight] += compressedLen; + + return TRUE; +} + +/* + * Code to determine how many different colors used in rectangle. + */ + +static void +FillPalette8(count) + int count; +{ + uint8_t *data = (uint8_t *)tightBeforeBuf; + uint8_t c0, c1; + int i, n0, n1; + + paletteNumColors = 0; + + c0 = data[0]; + for (i = 1; i < count && data[i] == c0; i++); + if (i == count) { + paletteNumColors = 1; + return; /* Solid rectangle */ + } + + if (paletteMaxColors < 2) + return; + + n0 = i; + c1 = data[i]; + n1 = 0; + for (i++; i < count; i++) { + if (data[i] == c0) { + n0++; + } else if (data[i] == c1) { + n1++; + } else + break; + } + if (i == count) { + if (n0 > n1) { + monoBackground = (uint32_t)c0; + monoForeground = (uint32_t)c1; + } else { + monoBackground = (uint32_t)c1; + monoForeground = (uint32_t)c0; + } + paletteNumColors = 2; /* Two colors */ + } +} + +#define DEFINE_FILL_PALETTE_FUNCTION(bpp) \ + \ +static void \ +FillPalette##bpp(count) \ + int count; \ +{ \ + uint##bpp##_t *data = (uint##bpp##_t *)tightBeforeBuf; \ + uint##bpp##_t c0, c1, ci; \ + int i, n0, n1, ni; \ + \ + c0 = data[0]; \ + for (i = 1; i < count && data[i] == c0; i++); \ + if (i >= count) { \ + paletteNumColors = 1; /* Solid rectangle */ \ + return; \ + } \ + \ + if (paletteMaxColors < 2) { \ + paletteNumColors = 0; /* Full-color encoding preferred */ \ + return; \ + } \ + \ + n0 = i; \ + c1 = data[i]; \ + n1 = 0; \ + for (i++; i < count; i++) { \ + ci = data[i]; \ + if (ci == c0) { \ + n0++; \ + } else if (ci == c1) { \ + n1++; \ + } else \ + break; \ + } \ + if (i >= count) { \ + if (n0 > n1) { \ + monoBackground = (uint32_t)c0; \ + monoForeground = (uint32_t)c1; \ + } else { \ + monoBackground = (uint32_t)c1; \ + monoForeground = (uint32_t)c0; \ + } \ + paletteNumColors = 2; /* Two colors */ \ + return; \ + } \ + \ + PaletteReset(); \ + PaletteInsert (c0, (uint32_t)n0, bpp); \ + PaletteInsert (c1, (uint32_t)n1, bpp); \ + \ + ni = 1; \ + for (i++; i < count; i++) { \ + if (data[i] == ci) { \ + ni++; \ + } else { \ + if (!PaletteInsert (ci, (uint32_t)ni, bpp)) \ + return; \ + ci = data[i]; \ + ni = 1; \ + } \ + } \ + PaletteInsert (ci, (uint32_t)ni, bpp); \ +} + +DEFINE_FILL_PALETTE_FUNCTION(16) +DEFINE_FILL_PALETTE_FUNCTION(32) + + +/* + * Functions to operate with palette structures. + */ + +#define HASH_FUNC16(rgb) ((int)(((rgb >> 8) + rgb) & 0xFF)) +#define HASH_FUNC32(rgb) ((int)(((rgb >> 16) + (rgb >> 8)) & 0xFF)) + +static void +PaletteReset(void) +{ + paletteNumColors = 0; + memset(palette.hash, 0, 256 * sizeof(COLOR_LIST *)); +} + +static int +PaletteInsert(rgb, numPixels, bpp) + uint32_t rgb; + int numPixels; + int bpp; +{ + COLOR_LIST *pnode; + COLOR_LIST *prev_pnode = NULL; + int hash_key, idx, new_idx, count; + + hash_key = (bpp == 16) ? HASH_FUNC16(rgb) : HASH_FUNC32(rgb); + + pnode = palette.hash[hash_key]; + + while (pnode != NULL) { + if (pnode->rgb == rgb) { + /* Such palette entry already exists. */ + new_idx = idx = pnode->idx; + count = palette.entry[idx].numPixels + numPixels; + if (new_idx && palette.entry[new_idx-1].numPixels < count) { + do { + palette.entry[new_idx] = palette.entry[new_idx-1]; + palette.entry[new_idx].listNode->idx = new_idx; + new_idx--; + } + while (new_idx && palette.entry[new_idx-1].numPixels < count); + palette.entry[new_idx].listNode = pnode; + pnode->idx = new_idx; + } + palette.entry[new_idx].numPixels = count; + return paletteNumColors; + } + prev_pnode = pnode; + pnode = pnode->next; + } + + /* Check if palette is full. */ + if (paletteNumColors == 256 || paletteNumColors == paletteMaxColors) { + paletteNumColors = 0; + return 0; + } + + /* Move palette entries with lesser pixel counts. */ + for ( idx = paletteNumColors; + idx > 0 && palette.entry[idx-1].numPixels < numPixels; + idx-- ) { + palette.entry[idx] = palette.entry[idx-1]; + palette.entry[idx].listNode->idx = idx; + } + + /* Add new palette entry into the freed slot. */ + pnode = &palette.list[paletteNumColors]; + if (prev_pnode != NULL) { + prev_pnode->next = pnode; + } else { + palette.hash[hash_key] = pnode; + } + pnode->next = NULL; + pnode->idx = idx; + pnode->rgb = rgb; + palette.entry[idx].listNode = pnode; + palette.entry[idx].numPixels = numPixels; + + return (++paletteNumColors); +} + + +/* + * Converting 32-bit color samples into 24-bit colors. + * Should be called only when redMax, greenMax and blueMax are 255. + * Color components assumed to be byte-aligned. + */ + +static void Pack24(cl, buf, fmt, count) + rfbClientPtr cl; + char *buf; + rfbPixelFormat *fmt; + int count; +{ + uint32_t *buf32; + uint32_t pix; + int r_shift, g_shift, b_shift; + + buf32 = (uint32_t *)buf; + + if (!cl->screen->rfbServerFormat.bigEndian == !fmt->bigEndian) { + r_shift = fmt->redShift; + g_shift = fmt->greenShift; + b_shift = fmt->blueShift; + } else { + r_shift = 24 - fmt->redShift; + g_shift = 24 - fmt->greenShift; + b_shift = 24 - fmt->blueShift; + } + + while (count--) { + pix = *buf32++; + *buf++ = (char)(pix >> r_shift); + *buf++ = (char)(pix >> g_shift); + *buf++ = (char)(pix >> b_shift); + } +} + + +/* + * Converting truecolor samples into palette indices. + */ + +#define DEFINE_IDX_ENCODE_FUNCTION(bpp) \ + \ +static void \ +EncodeIndexedRect##bpp(buf, count) \ + uint8_t *buf; \ + int count; \ +{ \ + COLOR_LIST *pnode; \ + uint##bpp##_t *src; \ + uint##bpp##_t rgb; \ + int rep = 0; \ + \ + src = (uint##bpp##_t *) buf; \ + \ + while (count--) { \ + rgb = *src++; \ + while (count && *src == rgb) { \ + rep++, src++, count--; \ + } \ + pnode = palette.hash[HASH_FUNC##bpp(rgb)]; \ + while (pnode != NULL) { \ + if ((uint##bpp##_t)pnode->rgb == rgb) { \ + *buf++ = (uint8_t)pnode->idx; \ + while (rep) { \ + *buf++ = (uint8_t)pnode->idx; \ + rep--; \ + } \ + break; \ + } \ + pnode = pnode->next; \ + } \ + } \ +} + +DEFINE_IDX_ENCODE_FUNCTION(16) +DEFINE_IDX_ENCODE_FUNCTION(32) + +#define DEFINE_MONO_ENCODE_FUNCTION(bpp) \ + \ +static void \ +EncodeMonoRect##bpp(buf, w, h) \ + uint8_t *buf; \ + int w, h; \ +{ \ + uint##bpp##_t *ptr; \ + uint##bpp##_t bg; \ + unsigned int value, mask; \ + int aligned_width; \ + int x, y, bg_bits; \ + \ + ptr = (uint##bpp##_t *) buf; \ + bg = (uint##bpp##_t) monoBackground; \ + aligned_width = w - w % 8; \ + \ + for (y = 0; y < h; y++) { \ + for (x = 0; x < aligned_width; x += 8) { \ + for (bg_bits = 0; bg_bits < 8; bg_bits++) { \ + if (*ptr++ != bg) \ + break; \ + } \ + if (bg_bits == 8) { \ + *buf++ = 0; \ + continue; \ + } \ + mask = 0x80 >> bg_bits; \ + value = mask; \ + for (bg_bits++; bg_bits < 8; bg_bits++) { \ + mask >>= 1; \ + if (*ptr++ != bg) { \ + value |= mask; \ + } \ + } \ + *buf++ = (uint8_t)value; \ + } \ + \ + mask = 0x80; \ + value = 0; \ + if (x >= w) \ + continue; \ + \ + for (; x < w; x++) { \ + if (*ptr++ != bg) { \ + value |= mask; \ + } \ + mask >>= 1; \ + } \ + *buf++ = (uint8_t)value; \ + } \ +} + +DEFINE_MONO_ENCODE_FUNCTION(8) +DEFINE_MONO_ENCODE_FUNCTION(16) +DEFINE_MONO_ENCODE_FUNCTION(32) + + +/* + * ``Gradient'' filter for 24-bit color samples. + * Should be called only when redMax, greenMax and blueMax are 255. + * Color components assumed to be byte-aligned. + */ + +static void +FilterGradient24(cl, buf, fmt, w, h) + rfbClientPtr cl; + char *buf; + rfbPixelFormat *fmt; + int w, h; +{ + uint32_t *buf32; + uint32_t pix32; + int *prevRowPtr; + int shiftBits[3]; + int pixHere[3], pixUpper[3], pixLeft[3], pixUpperLeft[3]; + int prediction; + int x, y, c; + + buf32 = (uint32_t *)buf; + memset (prevRowBuf, 0, w * 3 * sizeof(int)); + + if (!cl->screen->rfbServerFormat.bigEndian == !fmt->bigEndian) { + shiftBits[0] = fmt->redShift; + shiftBits[1] = fmt->greenShift; + shiftBits[2] = fmt->blueShift; + } else { + shiftBits[0] = 24 - fmt->redShift; + shiftBits[1] = 24 - fmt->greenShift; + shiftBits[2] = 24 - fmt->blueShift; + } + + for (y = 0; y < h; y++) { + for (c = 0; c < 3; c++) { + pixUpper[c] = 0; + pixHere[c] = 0; + } + prevRowPtr = prevRowBuf; + for (x = 0; x < w; x++) { + pix32 = *buf32++; + for (c = 0; c < 3; c++) { + pixUpperLeft[c] = pixUpper[c]; + pixLeft[c] = pixHere[c]; + pixUpper[c] = *prevRowPtr; + pixHere[c] = (int)(pix32 >> shiftBits[c] & 0xFF); + *prevRowPtr++ = pixHere[c]; + + prediction = pixLeft[c] + pixUpper[c] - pixUpperLeft[c]; + if (prediction < 0) { + prediction = 0; + } else if (prediction > 0xFF) { + prediction = 0xFF; + } + *buf++ = (char)(pixHere[c] - prediction); + } + } + } +} + + +/* + * ``Gradient'' filter for other color depths. + */ + +#define DEFINE_GRADIENT_FILTER_FUNCTION(bpp) \ + \ +static void \ +FilterGradient##bpp(cl, buf, fmt, w, h) \ + rfbClientPtr cl; \ + uint##bpp##_t *buf; \ + rfbPixelFormat *fmt; \ + int w, h; \ +{ \ + uint##bpp##_t pix, diff; \ + rfbBool endianMismatch; \ + int *prevRowPtr; \ + int maxColor[3], shiftBits[3]; \ + int pixHere[3], pixUpper[3], pixLeft[3], pixUpperLeft[3]; \ + int prediction; \ + int x, y, c; \ + \ + memset (prevRowBuf, 0, w * 3 * sizeof(int)); \ + \ + endianMismatch = (!cl->screen->rfbServerFormat.bigEndian != !fmt->bigEndian); \ + \ + maxColor[0] = fmt->redMax; \ + maxColor[1] = fmt->greenMax; \ + maxColor[2] = fmt->blueMax; \ + shiftBits[0] = fmt->redShift; \ + shiftBits[1] = fmt->greenShift; \ + shiftBits[2] = fmt->blueShift; \ + \ + for (y = 0; y < h; y++) { \ + for (c = 0; c < 3; c++) { \ + pixUpper[c] = 0; \ + pixHere[c] = 0; \ + } \ + prevRowPtr = prevRowBuf; \ + for (x = 0; x < w; x++) { \ + pix = *buf; \ + if (endianMismatch) { \ + pix = Swap##bpp(pix); \ + } \ + diff = 0; \ + for (c = 0; c < 3; c++) { \ + pixUpperLeft[c] = pixUpper[c]; \ + pixLeft[c] = pixHere[c]; \ + pixUpper[c] = *prevRowPtr; \ + pixHere[c] = (int)(pix >> shiftBits[c] & maxColor[c]); \ + *prevRowPtr++ = pixHere[c]; \ + \ + prediction = pixLeft[c] + pixUpper[c] - pixUpperLeft[c]; \ + if (prediction < 0) { \ + prediction = 0; \ + } else if (prediction > maxColor[c]) { \ + prediction = maxColor[c]; \ + } \ + diff |= ((pixHere[c] - prediction) & maxColor[c]) \ + << shiftBits[c]; \ + } \ + if (endianMismatch) { \ + diff = Swap##bpp(diff); \ + } \ + *buf++ = diff; \ + } \ + } \ +} + +DEFINE_GRADIENT_FILTER_FUNCTION(16) +DEFINE_GRADIENT_FILTER_FUNCTION(32) + + +/* + * Code to guess if given rectangle is suitable for smooth image + * compression (by applying "gradient" filter or JPEG coder). + */ + +#define JPEG_MIN_RECT_SIZE 4096 + +#define DETECT_SUBROW_WIDTH 7 +#define DETECT_MIN_WIDTH 8 +#define DETECT_MIN_HEIGHT 8 + +static int +DetectSmoothImage (cl, fmt, w, h) + rfbClientPtr cl; + rfbPixelFormat *fmt; + int w, h; +{ + long avgError; + + if ( cl->screen->rfbServerFormat.bitsPerPixel == 8 || fmt->bitsPerPixel == 8 || + w < DETECT_MIN_WIDTH || h < DETECT_MIN_HEIGHT ) { + return 0; + } + + if (qualityLevel != -1) { + if (w * h < JPEG_MIN_RECT_SIZE) { + return 0; + } + } else { + if ( rfbTightDisableGradient || + w * h < tightConf[compressLevel].gradientMinRectSize ) { + return 0; + } + } + + if (fmt->bitsPerPixel == 32) { + if (usePixelFormat24) { + avgError = DetectSmoothImage24(cl, fmt, w, h); + if (qualityLevel != -1) { + return (avgError < tightConf[qualityLevel].jpegThreshold24); + } + return (avgError < tightConf[compressLevel].gradientThreshold24); + } else { + avgError = DetectSmoothImage32(cl, fmt, w, h); + } + } else { + avgError = DetectSmoothImage16(cl, fmt, w, h); + } + if (qualityLevel != -1) { + return (avgError < tightConf[qualityLevel].jpegThreshold); + } + return (avgError < tightConf[compressLevel].gradientThreshold); +} + +static unsigned long +DetectSmoothImage24 (cl, fmt, w, h) + rfbClientPtr cl; + rfbPixelFormat *fmt; + int w, h; +{ + int off; + int x, y, d, dx, c; + int diffStat[256]; + int pixelCount = 0; + int pix, left[3]; + unsigned long avgError; + + /* If client is big-endian, color samples begin from the second + byte (offset 1) of a 32-bit pixel value. */ + off = (fmt->bigEndian != 0); + + memset(diffStat, 0, 256*sizeof(int)); + + y = 0, x = 0; + while (y < h && x < w) { + for (d = 0; d < h - y && d < w - x - DETECT_SUBROW_WIDTH; d++) { + for (c = 0; c < 3; c++) { + left[c] = (int)tightBeforeBuf[((y+d)*w+x+d)*4+off+c] & 0xFF; + } + for (dx = 1; dx <= DETECT_SUBROW_WIDTH; dx++) { + for (c = 0; c < 3; c++) { + pix = (int)tightBeforeBuf[((y+d)*w+x+d+dx)*4+off+c] & 0xFF; + diffStat[abs(pix - left[c])]++; + left[c] = pix; + } + pixelCount++; + } + } + if (w > h) { + x += h; + y = 0; + } else { + x = 0; + y += w; + } + } + + if (diffStat[0] * 33 / pixelCount >= 95) + return 0; + + avgError = 0; + for (c = 1; c < 8; c++) { + avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); + if (diffStat[c] == 0 || diffStat[c] > diffStat[c-1] * 2) + return 0; + } + for (; c < 256; c++) { + avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); + } + avgError /= (pixelCount * 3 - diffStat[0]); + + return avgError; +} + +#define DEFINE_DETECT_FUNCTION(bpp) \ + \ +static unsigned long \ +DetectSmoothImage##bpp (cl, fmt, w, h) \ + rfbClientPtr cl; \ + rfbPixelFormat *fmt; \ + int w, h; \ +{ \ + rfbBool endianMismatch; \ + uint##bpp##_t pix; \ + int maxColor[3], shiftBits[3]; \ + int x, y, d, dx, c; \ + int diffStat[256]; \ + int pixelCount = 0; \ + int sample, sum, left[3]; \ + unsigned long avgError; \ + \ + endianMismatch = (!cl->screen->rfbServerFormat.bigEndian != !fmt->bigEndian); \ + \ + maxColor[0] = fmt->redMax; \ + maxColor[1] = fmt->greenMax; \ + maxColor[2] = fmt->blueMax; \ + shiftBits[0] = fmt->redShift; \ + shiftBits[1] = fmt->greenShift; \ + shiftBits[2] = fmt->blueShift; \ + \ + memset(diffStat, 0, 256*sizeof(int)); \ + \ + y = 0, x = 0; \ + while (y < h && x < w) { \ + for (d = 0; d < h - y && d < w - x - DETECT_SUBROW_WIDTH; d++) { \ + pix = ((uint##bpp##_t *)tightBeforeBuf)[(y+d)*w+x+d]; \ + if (endianMismatch) { \ + pix = Swap##bpp(pix); \ + } \ + for (c = 0; c < 3; c++) { \ + left[c] = (int)(pix >> shiftBits[c] & maxColor[c]); \ + } \ + for (dx = 1; dx <= DETECT_SUBROW_WIDTH; dx++) { \ + pix = ((uint##bpp##_t *)tightBeforeBuf)[(y+d)*w+x+d+dx]; \ + if (endianMismatch) { \ + pix = Swap##bpp(pix); \ + } \ + sum = 0; \ + for (c = 0; c < 3; c++) { \ + sample = (int)(pix >> shiftBits[c] & maxColor[c]); \ + sum += abs(sample - left[c]); \ + left[c] = sample; \ + } \ + if (sum > 255) \ + sum = 255; \ + diffStat[sum]++; \ + pixelCount++; \ + } \ + } \ + if (w > h) { \ + x += h; \ + y = 0; \ + } else { \ + x = 0; \ + y += w; \ + } \ + } \ + \ + if ((diffStat[0] + diffStat[1]) * 100 / pixelCount >= 90) \ + return 0; \ + \ + avgError = 0; \ + for (c = 1; c < 8; c++) { \ + avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); \ + if (diffStat[c] == 0 || diffStat[c] > diffStat[c-1] * 2) \ + return 0; \ + } \ + for (; c < 256; c++) { \ + avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); \ + } \ + avgError /= (pixelCount - diffStat[0]); \ + \ + return avgError; \ +} + +DEFINE_DETECT_FUNCTION(16) +DEFINE_DETECT_FUNCTION(32) + + +/* + * JPEG compression stuff. + */ + +static struct jpeg_destination_mgr jpegDstManager; +static rfbBool jpegError; +static int jpegDstDataLen; + +static rfbBool +SendJpegRect(cl, x, y, w, h, quality) + rfbClientPtr cl; + int x, y, w, h; + int quality; +{ + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + uint8_t *srcBuf; + JSAMPROW rowPointer[1]; + int dy; + + if (cl->screen->rfbServerFormat.bitsPerPixel == 8) + return SendFullColorRect(cl, w, h); + + srcBuf = (uint8_t *)malloc(w * 3); + if (srcBuf == NULL) { + return SendFullColorRect(cl, w, h); + } + rowPointer[0] = srcBuf; + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + + cinfo.image_width = w; + cinfo.image_height = h; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, quality, TRUE); + + JpegSetDstManager (&cinfo); + + jpeg_start_compress(&cinfo, TRUE); + + for (dy = 0; dy < h; dy++) { + PrepareRowForJpeg(cl, srcBuf, x, y + dy, w); + jpeg_write_scanlines(&cinfo, rowPointer, 1); + if (jpegError) + break; + } + + if (!jpegError) + jpeg_finish_compress(&cinfo); + + jpeg_destroy_compress(&cinfo); + free(srcBuf); + + if (jpegError) + return SendFullColorRect(cl, w, h); + + if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4); + cl->rfbBytesSent[rfbEncodingTight]++; + + return SendCompressedData(cl, jpegDstDataLen); +} + +static void +PrepareRowForJpeg(cl, dst, x, y, count) + rfbClientPtr cl; + uint8_t *dst; + int x, y, count; +{ + if (cl->screen->rfbServerFormat.bitsPerPixel == 32) { + if ( cl->screen->rfbServerFormat.redMax == 0xFF && + cl->screen->rfbServerFormat.greenMax == 0xFF && + cl->screen->rfbServerFormat.blueMax == 0xFF ) { + PrepareRowForJpeg24(cl, dst, x, y, count); + } else { + PrepareRowForJpeg32(cl, dst, x, y, count); + } + } else { + /* 16 bpp assumed. */ + PrepareRowForJpeg16(cl, dst, x, y, count); + } +} + +static void +PrepareRowForJpeg24(cl, dst, x, y, count) + rfbClientPtr cl; + uint8_t *dst; + int x, y, count; +{ + uint32_t *fbptr; + uint32_t pix; + + fbptr = (uint32_t *) + &cl->screen->frameBuffer[y * cl->screen->paddedWidthInBytes + x * 4]; + + while (count--) { + pix = *fbptr++; + *dst++ = (uint8_t)(pix >> cl->screen->rfbServerFormat.redShift); + *dst++ = (uint8_t)(pix >> cl->screen->rfbServerFormat.greenShift); + *dst++ = (uint8_t)(pix >> cl->screen->rfbServerFormat.blueShift); + } +} + +#define DEFINE_JPEG_GET_ROW_FUNCTION(bpp) \ + \ +static void \ +PrepareRowForJpeg##bpp(cl, dst, x, y, count) \ + rfbClientPtr cl; \ + uint8_t *dst; \ + int x, y, count; \ +{ \ + uint##bpp##_t *fbptr; \ + uint##bpp##_t pix; \ + int inRed, inGreen, inBlue; \ + \ + fbptr = (uint##bpp##_t *) \ + &cl->screen->frameBuffer[y * cl->screen->paddedWidthInBytes + \ + x * (bpp / 8)]; \ + \ + while (count--) { \ + pix = *fbptr++; \ + \ + inRed = (int) \ + (pix >> cl->screen->rfbServerFormat.redShift & cl->screen->rfbServerFormat.redMax); \ + inGreen = (int) \ + (pix >> cl->screen->rfbServerFormat.greenShift & cl->screen->rfbServerFormat.greenMax); \ + inBlue = (int) \ + (pix >> cl->screen->rfbServerFormat.blueShift & cl->screen->rfbServerFormat.blueMax); \ + \ + *dst++ = (uint8_t)((inRed * 255 + cl->screen->rfbServerFormat.redMax / 2) / \ + cl->screen->rfbServerFormat.redMax); \ + *dst++ = (uint8_t)((inGreen * 255 + cl->screen->rfbServerFormat.greenMax / 2) / \ + cl->screen->rfbServerFormat.greenMax); \ + *dst++ = (uint8_t)((inBlue * 255 + cl->screen->rfbServerFormat.blueMax / 2) / \ + cl->screen->rfbServerFormat.blueMax); \ + } \ +} + +DEFINE_JPEG_GET_ROW_FUNCTION(16) +DEFINE_JPEG_GET_ROW_FUNCTION(32) + +/* + * Destination manager implementation for JPEG library. + */ + +static void +JpegInitDestination(j_compress_ptr cinfo) +{ + jpegError = FALSE; + jpegDstManager.next_output_byte = (JOCTET *)tightAfterBuf; + jpegDstManager.free_in_buffer = (size_t)tightAfterBufSize; +} + +static boolean +JpegEmptyOutputBuffer(j_compress_ptr cinfo) +{ + jpegError = TRUE; + jpegDstManager.next_output_byte = (JOCTET *)tightAfterBuf; + jpegDstManager.free_in_buffer = (size_t)tightAfterBufSize; + + return TRUE; +} + +static void +JpegTermDestination(j_compress_ptr cinfo) +{ + jpegDstDataLen = tightAfterBufSize - jpegDstManager.free_in_buffer; +} + +static void +JpegSetDstManager(j_compress_ptr cinfo) +{ + jpegDstManager.init_destination = JpegInitDestination; + jpegDstManager.empty_output_buffer = JpegEmptyOutputBuffer; + jpegDstManager.term_destination = JpegTermDestination; + cinfo->dest = &jpegDstManager; +} + diff --git a/libvncserver/translate.c b/libvncserver/translate.c new file mode 100755 index 0000000..d5f0896 --- /dev/null +++ b/libvncserver/translate.c @@ -0,0 +1,484 @@ +/* + * translate.c - translate between different pixel formats + */ + +/* + * OSXvnc Copyright (C) 2001 Dan McGuirk . + * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. + * 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. + */ + +#include +#include + +static void PrintPixelFormat(rfbPixelFormat *pf); +static rfbBool rfbSetClientColourMapBGR233(rfbClientPtr cl); + +rfbBool rfbEconomicTranslate = FALSE; + +/* + * Some standard pixel formats. + */ + +static const rfbPixelFormat BGR233Format = { + 8, 8, 0, 1, 7, 7, 3, 0, 3, 6, 0, 0 +}; + + +/* + * Macro to compare pixel formats. + */ + +#define PF_EQ(x,y) \ + ((x.bitsPerPixel == y.bitsPerPixel) && \ + (x.depth == y.depth) && \ + ((x.bigEndian == y.bigEndian) || (x.bitsPerPixel == 8)) && \ + (x.trueColour == y.trueColour) && \ + (!x.trueColour || ((x.redMax == y.redMax) && \ + (x.greenMax == y.greenMax) && \ + (x.blueMax == y.blueMax) && \ + (x.redShift == y.redShift) && \ + (x.greenShift == y.greenShift) && \ + (x.blueShift == y.blueShift)))) + +#define CONCAT2(a,b) a##b +#define CONCAT2E(a,b) CONCAT2(a,b) +#define CONCAT3(a,b,c) a##b##c +#define CONCAT3E(a,b,c) CONCAT3(a,b,c) +#define CONCAT4(a,b,c,d) a##b##c##d +#define CONCAT4E(a,b,c,d) CONCAT4(a,b,c,d) + +#undef OUT +#undef IN + +#define OUT 8 +#include "tableinitcmtemplate.c" +#include "tableinittctemplate.c" +#define IN 8 +#include "tabletranstemplate.c" +#undef IN +#define IN 16 +#include "tabletranstemplate.c" +#undef IN +#define IN 32 +#include "tabletranstemplate.c" +#undef IN +#undef OUT + +#define OUT 16 +#include "tableinitcmtemplate.c" +#include "tableinittctemplate.c" +#define IN 8 +#include "tabletranstemplate.c" +#undef IN +#define IN 16 +#include "tabletranstemplate.c" +#undef IN +#define IN 32 +#include "tabletranstemplate.c" +#undef IN +#undef OUT + +#define OUT 32 +#include "tableinitcmtemplate.c" +#include "tableinittctemplate.c" +#define IN 8 +#include "tabletranstemplate.c" +#undef IN +#define IN 16 +#include "tabletranstemplate.c" +#undef IN +#define IN 32 +#include "tabletranstemplate.c" +#undef IN +#undef OUT + +#ifdef LIBVNCSERVER_ALLOW24BPP +#define COUNT_OFFSETS 4 +#define BPP2OFFSET(bpp) ((bpp)/8-1) +#include "tableinit24.c" +#define BPP 8 +#include "tabletrans24template.c" +#undef BPP +#define BPP 16 +#include "tabletrans24template.c" +#undef BPP +#define BPP 24 +#include "tabletrans24template.c" +#undef BPP +#define BPP 32 +#include "tabletrans24template.c" +#undef BPP +#else +#define COUNT_OFFSETS 3 +#define BPP2OFFSET(bpp) ((int)(bpp)/16) +#endif + +typedef void (*rfbInitCMTableFnType)(char **table, rfbPixelFormat *in, + rfbPixelFormat *out,rfbColourMap* cm); +typedef void (*rfbInitTableFnType)(char **table, rfbPixelFormat *in, + rfbPixelFormat *out); + +rfbInitCMTableFnType rfbInitColourMapSingleTableFns[COUNT_OFFSETS] = { + rfbInitColourMapSingleTable8, + rfbInitColourMapSingleTable16, +#ifdef LIBVNCSERVER_ALLOW24BPP + rfbInitColourMapSingleTable24, +#endif + rfbInitColourMapSingleTable32 +}; + +rfbInitTableFnType rfbInitTrueColourSingleTableFns[COUNT_OFFSETS] = { + rfbInitTrueColourSingleTable8, + rfbInitTrueColourSingleTable16, +#ifdef LIBVNCSERVER_ALLOW24BPP + rfbInitTrueColourSingleTable24, +#endif + rfbInitTrueColourSingleTable32 +}; + +rfbInitTableFnType rfbInitTrueColourRGBTablesFns[COUNT_OFFSETS] = { + rfbInitTrueColourRGBTables8, + rfbInitTrueColourRGBTables16, +#ifdef LIBVNCSERVER_ALLOW24BPP + rfbInitTrueColourRGBTables24, +#endif + rfbInitTrueColourRGBTables32 +}; + +rfbTranslateFnType rfbTranslateWithSingleTableFns[COUNT_OFFSETS][COUNT_OFFSETS] = { + { rfbTranslateWithSingleTable8to8, + rfbTranslateWithSingleTable8to16, +#ifdef LIBVNCSERVER_ALLOW24BPP + rfbTranslateWithSingleTable8to24, +#endif + rfbTranslateWithSingleTable8to32 }, + { rfbTranslateWithSingleTable16to8, + rfbTranslateWithSingleTable16to16, +#ifdef LIBVNCSERVER_ALLOW24BPP + rfbTranslateWithSingleTable16to24, +#endif + rfbTranslateWithSingleTable16to32 }, +#ifdef LIBVNCSERVER_ALLOW24BPP + { rfbTranslateWithSingleTable24to8, + rfbTranslateWithSingleTable24to16, + rfbTranslateWithSingleTable24to24, + rfbTranslateWithSingleTable24to32 }, +#endif + { rfbTranslateWithSingleTable32to8, + rfbTranslateWithSingleTable32to16, +#ifdef LIBVNCSERVER_ALLOW24BPP + rfbTranslateWithSingleTable32to24, +#endif + rfbTranslateWithSingleTable32to32 } +}; + +rfbTranslateFnType rfbTranslateWithRGBTablesFns[COUNT_OFFSETS][COUNT_OFFSETS] = { + { rfbTranslateWithRGBTables8to8, + rfbTranslateWithRGBTables8to16, +#ifdef LIBVNCSERVER_ALLOW24BPP + rfbTranslateWithRGBTables8to24, +#endif + rfbTranslateWithRGBTables8to32 }, + { rfbTranslateWithRGBTables16to8, + rfbTranslateWithRGBTables16to16, +#ifdef LIBVNCSERVER_ALLOW24BPP + rfbTranslateWithRGBTables16to24, +#endif + rfbTranslateWithRGBTables16to32 }, +#ifdef LIBVNCSERVER_ALLOW24BPP + { rfbTranslateWithRGBTables24to8, + rfbTranslateWithRGBTables24to16, + rfbTranslateWithRGBTables24to24, + rfbTranslateWithRGBTables24to32 }, +#endif + { rfbTranslateWithRGBTables32to8, + rfbTranslateWithRGBTables32to16, +#ifdef LIBVNCSERVER_ALLOW24BPP + rfbTranslateWithRGBTables32to24, +#endif + rfbTranslateWithRGBTables32to32 } +}; + + + +/* + * rfbTranslateNone is used when no translation is required. + */ + +void +rfbTranslateNone(char *table, rfbPixelFormat *in, rfbPixelFormat *out, + char *iptr, char *optr, int bytesBetweenInputLines, + int width, int height) +{ + int bytesPerOutputLine = width * (out->bitsPerPixel / 8); + + while (height > 0) { + memcpy(optr, iptr, bytesPerOutputLine); + iptr += bytesBetweenInputLines; + optr += bytesPerOutputLine; + height--; + } +} + + +/* + * rfbSetTranslateFunction sets the translation function. + */ + +rfbBool +rfbSetTranslateFunction(cl) + rfbClientPtr cl; +{ + rfbLog("Pixel format for client %s:\n",cl->host); + PrintPixelFormat(&cl->format); + + /* + * Check that bits per pixel values are valid + */ + + if ((cl->screen->rfbServerFormat.bitsPerPixel != 8) && + (cl->screen->rfbServerFormat.bitsPerPixel != 16) && +#ifdef LIBVNCSERVER_ALLOW24BPP + (cl->screen->rfbServerFormat.bitsPerPixel != 24) && +#endif + (cl->screen->rfbServerFormat.bitsPerPixel != 32)) + { + rfbErr("%s: server bits per pixel not 8, 16 or 32 (is %d)\n", + "rfbSetTranslateFunction", + cl->screen->rfbServerFormat.bitsPerPixel); + rfbCloseClient(cl); + return FALSE; + } + + if ((cl->format.bitsPerPixel != 8) && + (cl->format.bitsPerPixel != 16) && +#ifdef LIBVNCSERVER_ALLOW24BPP + (cl->format.bitsPerPixel != 24) && +#endif + (cl->format.bitsPerPixel != 32)) + { + rfbErr("%s: client bits per pixel not 8, 16 or 32\n", + "rfbSetTranslateFunction"); + rfbCloseClient(cl); + return FALSE; + } + + if (!cl->format.trueColour && (cl->format.bitsPerPixel != 8)) { + rfbErr("rfbSetTranslateFunction: client has colour map " + "but %d-bit - can only cope with 8-bit colour maps\n", + cl->format.bitsPerPixel); + rfbCloseClient(cl); + return FALSE; + } + + /* + * bpp is valid, now work out how to translate + */ + + if (!cl->format.trueColour) { + /* + * truecolour -> colour map + * + * Set client's colour map to BGR233, then effectively it's + * truecolour as well + */ + + if (!rfbSetClientColourMapBGR233(cl)) + return FALSE; + + cl->format = BGR233Format; + } + + /* truecolour -> truecolour */ + + if (PF_EQ(cl->format,cl->screen->rfbServerFormat)) { + + /* client & server the same */ + + rfbLog("no translation needed\n"); + cl->translateFn = rfbTranslateNone; + return TRUE; + } + + if ((cl->screen->rfbServerFormat.bitsPerPixel < 16) || + ((!cl->screen->rfbServerFormat.trueColour || !rfbEconomicTranslate) && + (cl->screen->rfbServerFormat.bitsPerPixel == 16))) { + + /* we can use a single lookup table for <= 16 bpp */ + + cl->translateFn = rfbTranslateWithSingleTableFns + [BPP2OFFSET(cl->screen->rfbServerFormat.bitsPerPixel)] + [BPP2OFFSET(cl->format.bitsPerPixel)]; + + if(cl->screen->rfbServerFormat.trueColour) + (*rfbInitTrueColourSingleTableFns + [BPP2OFFSET(cl->format.bitsPerPixel)]) (&cl->translateLookupTable, + &(cl->screen->rfbServerFormat), &cl->format); + else + (*rfbInitColourMapSingleTableFns + [BPP2OFFSET(cl->format.bitsPerPixel)]) (&cl->translateLookupTable, + &(cl->screen->rfbServerFormat), &cl->format,&cl->screen->colourMap); + + } else { + + /* otherwise we use three separate tables for red, green and blue */ + + cl->translateFn = rfbTranslateWithRGBTablesFns + [BPP2OFFSET(cl->screen->rfbServerFormat.bitsPerPixel)] + [BPP2OFFSET(cl->format.bitsPerPixel)]; + + (*rfbInitTrueColourRGBTablesFns + [BPP2OFFSET(cl->format.bitsPerPixel)]) (&cl->translateLookupTable, + &(cl->screen->rfbServerFormat), &cl->format); + } + + return TRUE; +} + + + +/* + * rfbSetClientColourMapBGR233 sets the client's colour map so that it's + * just like an 8-bit BGR233 true colour client. + */ + +static rfbBool +rfbSetClientColourMapBGR233(cl) + rfbClientPtr cl; +{ + char buf[sz_rfbSetColourMapEntriesMsg + 256 * 3 * 2]; + rfbSetColourMapEntriesMsg *scme = (rfbSetColourMapEntriesMsg *)buf; + uint16_t *rgb = (uint16_t *)(&buf[sz_rfbSetColourMapEntriesMsg]); + int i, len; + int r, g, b; + + if (cl->format.bitsPerPixel != 8 ) { + rfbErr("%s: client not 8 bits per pixel\n", + "rfbSetClientColourMapBGR233"); + rfbCloseClient(cl); + return FALSE; + } + + scme->type = rfbSetColourMapEntries; + + scme->firstColour = Swap16IfLE(0); + scme->nColours = Swap16IfLE(256); + + len = sz_rfbSetColourMapEntriesMsg; + + i = 0; + + for (b = 0; b < 4; b++) { + for (g = 0; g < 8; g++) { + for (r = 0; r < 8; r++) { + rgb[i++] = Swap16IfLE(r * 65535 / 7); + rgb[i++] = Swap16IfLE(g * 65535 / 7); + rgb[i++] = Swap16IfLE(b * 65535 / 3); + } + } + } + + len += 256 * 3 * 2; + + if (WriteExact(cl, buf, len) < 0) { + rfbLogPerror("rfbSetClientColourMapBGR233: write"); + rfbCloseClient(cl); + return FALSE; + } + return TRUE; +} + +/* this function is not called very often, so it needn't be + efficient. */ + +/* + * rfbSetClientColourMap is called to set the client's colour map. If the + * client is a true colour client, we simply update our own translation table + * and mark the whole screen as having been modified. + */ + +rfbBool +rfbSetClientColourMap(cl, firstColour, nColours) + rfbClientPtr cl; + int firstColour; + int nColours; +{ + if (cl->screen->rfbServerFormat.trueColour || !cl->readyForSetColourMapEntries) { + return TRUE; + } + + if (nColours == 0) { + nColours = cl->screen->colourMap.count; + } + + if (cl->format.trueColour) { + (*rfbInitColourMapSingleTableFns + [BPP2OFFSET(cl->format.bitsPerPixel)]) (&cl->translateLookupTable, + &cl->screen->rfbServerFormat, &cl->format,&cl->screen->colourMap); + + sraRgnDestroy(cl->modifiedRegion); + cl->modifiedRegion = + sraRgnCreateRect(0,0,cl->screen->width,cl->screen->height); + + return TRUE; + } + + return rfbSendSetColourMapEntries(cl, firstColour, nColours); +} + + +/* + * rfbSetClientColourMaps sets the colour map for each RFB client. + */ + +void +rfbSetClientColourMaps(rfbScreen, firstColour, nColours) + rfbScreenInfoPtr rfbScreen; + int firstColour; + int nColours; +{ + rfbClientIteratorPtr i; + rfbClientPtr cl; + + i = rfbGetClientIterator(rfbScreen); + while((cl = rfbClientIteratorNext(i))) + rfbSetClientColourMap(cl, firstColour, nColours); + rfbReleaseClientIterator(i); +} + +static void +PrintPixelFormat(pf) + rfbPixelFormat *pf; +{ + if (pf->bitsPerPixel == 1) { + rfbLog(" 1 bpp, %s sig bit in each byte is leftmost on the screen.\n", + (pf->bigEndian ? "most" : "least")); + } else { + rfbLog(" %d bpp, depth %d%s\n",pf->bitsPerPixel,pf->depth, + ((pf->bitsPerPixel == 8) ? "" + : (pf->bigEndian ? ", big endian" : ", little endian"))); + if (pf->trueColour) { + rfbLog(" true colour: max r %d g %d b %d, shift r %d g %d b %d\n", + pf->redMax, pf->greenMax, pf->blueMax, + pf->redShift, pf->greenShift, pf->blueShift); + } else { + rfbLog(" uses a colour map (not true colour).\n"); + } + } +} diff --git a/libvncserver/vncauth.c b/libvncserver/vncauth.c new file mode 100644 index 0000000..2146e67 --- /dev/null +++ b/libvncserver/vncauth.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 1999 AT&T Laboratories Cambridge. 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 program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * vncauth.c - Functions for VNC password management and authentication. + */ + +#include +#include +#include +#include +#include "d3des.h" + +#include +#include + +#ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H +#include +#endif +#ifdef LIBVNCSERVER_HAVE_SYS_STAT_H +#include +#endif + +#include + +#ifdef WIN32 +#define srandom srand +#define random rand +#else +#include +#endif + + +/* + * We use a fixed key to store passwords, since we assume that our local + * file system is secure but nonetheless don't want to store passwords + * as plaintext. + */ + +static unsigned char fixedkey[8] = {23,82,107,6,35,78,88,7}; + + +/* + * Encrypt a password and store it in a file. Returns 0 if successful, + * 1 if the file could not be written. + */ + +int +vncEncryptAndStorePasswd(char *passwd, char *fname) +{ + FILE *fp; + unsigned int i; + unsigned char encryptedPasswd[8]; + + if ((fp = fopen(fname,"w")) == NULL) return 1; + + /* windows security sux */ +#ifndef WIN32 + fchmod(fileno(fp), S_IRUSR|S_IWUSR); +#endif + + /* pad password with nulls */ + + for (i = 0; i < 8; i++) { + if (i < strlen(passwd)) { + encryptedPasswd[i] = passwd[i]; + } else { + encryptedPasswd[i] = 0; + } + } + + /* Do encryption in-place - this way we overwrite our copy of the plaintext + password */ + + deskey(fixedkey, EN0); + des(encryptedPasswd, encryptedPasswd); + + for (i = 0; i < 8; i++) { + putc(encryptedPasswd[i], fp); + } + + fclose(fp); + return 0; +} + + +/* + * Decrypt a password from a file. Returns a pointer to a newly allocated + * string containing the password or a null pointer if the password could + * not be retrieved for some reason. + */ + +char * +vncDecryptPasswdFromFile(char *fname) +{ + FILE *fp; + int i, ch; + unsigned char *passwd = (unsigned char *)malloc(9); + + if ((fp = fopen(fname,"r")) == NULL) return NULL; + + for (i = 0; i < 8; i++) { + ch = getc(fp); + if (ch == EOF) { + fclose(fp); + return NULL; + } + passwd[i] = ch; + } + + fclose(fp); + + deskey(fixedkey, DE1); + des(passwd, passwd); + + passwd[8] = 0; + + return (char *)passwd; +} + + +/* + * Generate CHALLENGESIZE random bytes for use in challenge-response + * authentication. + */ + +void +vncRandomBytes(unsigned char *bytes) +{ + int i; + static rfbBool s_srandom_called = FALSE; + + if (!s_srandom_called) { + srandom((unsigned int)time(0) ^ (unsigned int)getpid()); + s_srandom_called = TRUE; + } + + for (i = 0; i < CHALLENGESIZE; i++) { + bytes[i] = (unsigned char)(random() & 255); + } +} + + +/* + * Encrypt CHALLENGESIZE bytes in memory using a password. + */ + +void +vncEncryptBytes(unsigned char *bytes, char *passwd) +{ + unsigned char key[8]; + unsigned int i; + + /* key is simply password padded with nulls */ + + for (i = 0; i < 8; i++) { + if (i < strlen(passwd)) { + key[i] = passwd[i]; + } else { + key[i] = 0; + } + } + + deskey(key, EN0); + + for (i = 0; i < CHALLENGESIZE; i += 8) { + des(bytes+i, bytes+i); + } +} diff --git a/libvncserver/zlib.c b/libvncserver/zlib.c new file mode 100644 index 0000000..9905810 --- /dev/null +++ b/libvncserver/zlib.c @@ -0,0 +1,302 @@ +/* + * zlib.c + * + * Routines to implement zlib based encoding (deflate). + */ + +/* + * Copyright (C) 2000 Tridia Corporation. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. 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. + * + * For the latest source code, please check: + * + * http://www.developVNC.org/ + * + * or send email to feedback@developvnc.org. + */ + +#include + +/* + * zlibBeforeBuf contains pixel data in the client's format. + * zlibAfterBuf contains the zlib (deflated) encoding version. + * If the zlib compressed/encoded version is + * larger than the raw data or if it exceeds zlibAfterBufSize then + * raw encoding is used instead. + */ + +static int zlibBeforeBufSize = 0; +static char *zlibBeforeBuf = NULL; + +static int zlibAfterBufSize = 0; +static char *zlibAfterBuf = NULL; +static int zlibAfterBufLen; + +/* + * rfbSendOneRectEncodingZlib - send a given rectangle using one Zlib + * rectangle encoding. + */ + +rfbBool +rfbSendOneRectEncodingZlib(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + rfbFramebufferUpdateRectHeader rect; + rfbZlibHeader hdr; + int deflateResult; + int previousOut; + int i; + char *fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y) + + (x * (cl->screen->bitsPerPixel / 8))); + + int maxRawSize; + int maxCompSize; + + maxRawSize = (cl->screen->width * cl->screen->height + * (cl->format.bitsPerPixel / 8)); + + if (zlibBeforeBufSize < maxRawSize) { + zlibBeforeBufSize = maxRawSize; + if (zlibBeforeBuf == NULL) + zlibBeforeBuf = (char *)malloc(zlibBeforeBufSize); + else + zlibBeforeBuf = (char *)realloc(zlibBeforeBuf, zlibBeforeBufSize); + } + + /* zlib compression is not useful for very small data sets. + * So, we just send these raw without any compression. + */ + if (( w * h * (cl->screen->bitsPerPixel / 8)) < + VNC_ENCODE_ZLIB_MIN_COMP_SIZE ) { + + int result; + + /* The translation function (used also by the in raw encoding) + * requires 4/2/1 byte alignment in the output buffer (which is + * updateBuf for the raw encoding) based on the bitsPerPixel of + * the viewer/client. This prevents SIGBUS errors on some + * architectures like SPARC, PARISC... + */ + if (( cl->format.bitsPerPixel > 8 ) && + ( cl->ublen % ( cl->format.bitsPerPixel / 8 )) != 0 ) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + result = rfbSendRectEncodingRaw(cl, x, y, w, h); + + return result; + + } + + /* + * zlib requires output buffer to be slightly larger than the input + * buffer, in the worst case. + */ + maxCompSize = maxRawSize + (( maxRawSize + 99 ) / 100 ) + 12; + + if (zlibAfterBufSize < maxCompSize) { + zlibAfterBufSize = maxCompSize; + if (zlibAfterBuf == NULL) + zlibAfterBuf = (char *)malloc(zlibAfterBufSize); + else + zlibAfterBuf = (char *)realloc(zlibAfterBuf, zlibAfterBufSize); + } + + /* + * Convert pixel data to client format. + */ + (*cl->translateFn)(cl->translateLookupTable, &cl->screen->rfbServerFormat, + &cl->format, fbptr, zlibBeforeBuf, + cl->screen->paddedWidthInBytes, w, h); + + cl->compStream.next_in = ( Bytef * )zlibBeforeBuf; + cl->compStream.avail_in = w * h * (cl->format.bitsPerPixel / 8); + cl->compStream.next_out = ( Bytef * )zlibAfterBuf; + cl->compStream.avail_out = maxCompSize; + cl->compStream.data_type = Z_BINARY; + + /* Initialize the deflation state. */ + if ( cl->compStreamInited == FALSE ) { + + cl->compStream.total_in = 0; + cl->compStream.total_out = 0; + cl->compStream.zalloc = Z_NULL; + cl->compStream.zfree = Z_NULL; + cl->compStream.opaque = Z_NULL; + + deflateInit2( &(cl->compStream), + cl->zlibCompressLevel, + Z_DEFLATED, + MAX_WBITS, + MAX_MEM_LEVEL, + Z_DEFAULT_STRATEGY ); + /* deflateInit( &(cl->compStream), Z_BEST_COMPRESSION ); */ + /* deflateInit( &(cl->compStream), Z_BEST_SPEED ); */ + cl->compStreamInited = TRUE; + + } + + previousOut = cl->compStream.total_out; + + /* Perform the compression here. */ + deflateResult = deflate( &(cl->compStream), Z_SYNC_FLUSH ); + + /* Find the total size of the resulting compressed data. */ + zlibAfterBufLen = cl->compStream.total_out - previousOut; + + if ( deflateResult != Z_OK ) { + rfbErr("zlib deflation error: %s\n", cl->compStream.msg); + return FALSE; + } + + /* Note that it is not possible to switch zlib parameters based on + * the results of the compression pass. The reason is + * that we rely on the compressor and decompressor states being + * in sync. Compressing and then discarding the results would + * cause lose of synchronization. + */ + + /* Update statics */ + cl->rfbRectanglesSent[rfbEncodingZlib]++; + cl->rfbBytesSent[rfbEncodingZlib] += (sz_rfbFramebufferUpdateRectHeader + + sz_rfbZlibHeader + zlibAfterBufLen); + + if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader + > UPDATE_BUF_SIZE) + { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + rect.r.x = Swap16IfLE(x); + rect.r.y = Swap16IfLE(y); + rect.r.w = Swap16IfLE(w); + rect.r.h = Swap16IfLE(h); + rect.encoding = Swap32IfLE(rfbEncodingZlib); + + memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + cl->ublen += sz_rfbFramebufferUpdateRectHeader; + + hdr.nBytes = Swap32IfLE(zlibAfterBufLen); + + memcpy(&cl->updateBuf[cl->ublen], (char *)&hdr, sz_rfbZlibHeader); + cl->ublen += sz_rfbZlibHeader; + + for (i = 0; i < zlibAfterBufLen;) { + + int bytesToCopy = UPDATE_BUF_SIZE - cl->ublen; + + if (i + bytesToCopy > zlibAfterBufLen) { + bytesToCopy = zlibAfterBufLen - i; + } + + memcpy(&cl->updateBuf[cl->ublen], &zlibAfterBuf[i], bytesToCopy); + + cl->ublen += bytesToCopy; + i += bytesToCopy; + + if (cl->ublen == UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + } + + return TRUE; + +} + + +/* + * rfbSendRectEncodingZlib - send a given rectangle using one or more + * Zlib encoding rectangles. + */ + +rfbBool +rfbSendRectEncodingZlib(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + int maxLines; + int linesRemaining; + rfbRectangle partialRect; + + partialRect.x = x; + partialRect.y = y; + partialRect.w = w; + partialRect.h = h; + + /* Determine maximum pixel/scan lines allowed per rectangle. */ + maxLines = ( ZLIB_MAX_SIZE(w) / w ); + + /* Initialize number of scan lines left to do. */ + linesRemaining = h; + + /* Loop until all work is done. */ + while ( linesRemaining > 0 ) { + + int linesToComp; + + if ( maxLines < linesRemaining ) + linesToComp = maxLines; + else + linesToComp = linesRemaining; + + partialRect.h = linesToComp; + + /* Encode (compress) and send the next rectangle. */ + if ( ! rfbSendOneRectEncodingZlib( cl, + partialRect.x, + partialRect.y, + partialRect.w, + partialRect.h )) { + + return FALSE; + } + + /* Technically, flushing the buffer here is not extrememly + * efficient. However, this improves the overall throughput + * of the system over very slow networks. By flushing + * the buffer with every maximum size zlib rectangle, we + * improve the pipelining usage of the server CPU, network, + * and viewer CPU components. Insuring that these components + * are working in parallel actually improves the performance + * seen by the user. + * Since, zlib is most useful for slow networks, this flush + * is appropriate for the desired behavior of the zlib encoding. + */ + if (( cl->ublen > 0 ) && + ( linesToComp == maxLines )) { + if (!rfbSendUpdateBuf(cl)) { + + return FALSE; + } + } + + /* Update remaining and incremental rectangle location. */ + linesRemaining -= linesToComp; + partialRect.y += linesToComp; + + } + + return TRUE; + +} + diff --git a/libvncserver/zrle.c b/libvncserver/zrle.c new file mode 100644 index 0000000..6ab933e --- /dev/null +++ b/libvncserver/zrle.c @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2003 Sun Microsystems, Inc. + * + * 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. + */ + +/* + * zrle.c + * + * Routines to implement Zlib Run-length Encoding (ZRLE). + */ + +#include "rfb/rfb.h" +#include "zrleoutstream.h" + + +#define GET_IMAGE_INTO_BUF(tx,ty,tw,th,buf) \ +{ char *fbptr = (cl->screen->frameBuffer \ + + (cl->screen->paddedWidthInBytes * ty) \ + + (tx * (cl->screen->bitsPerPixel / 8))); \ + \ + (*cl->translateFn)(cl->translateLookupTable, &cl->screen->rfbServerFormat,\ + &cl->format, fbptr, (char*)buf, \ + cl->screen->paddedWidthInBytes, tw, th); } + +#define EXTRA_ARGS , rfbClientPtr cl + +#define BPP 8 +#include +#undef BPP +#define BPP 16 +#include +#undef BPP +#define BPP 32 +#include +#define CPIXEL 24A +#include +#undef CPIXEL +#define CPIXEL 24B +#include +#undef CPIXEL +#undef BPP + + +/* + * zrleBeforeBuf contains pixel data in the client's format. It must be at + * least one pixel bigger than the largest tile of pixel data, since the + * ZRLE encoding algorithm writes to the position one past the end of the pixel + * data. + */ + +static char zrleBeforeBuf[rfbZRLETileWidth * rfbZRLETileHeight * 4 + 4]; + + + +/* + * rfbSendRectEncodingZRLE - send a given rectangle using ZRLE encoding. + */ + + +rfbBool rfbSendRectEncodingZRLE(rfbClientPtr cl, int x, int y, int w, int h) +{ + zrleOutStream* zos; + rfbFramebufferUpdateRectHeader rect; + rfbZRLEHeader hdr; + int i; + + if (!cl->zrleData) + cl->zrleData = zrleOutStreamNew(); + zos = cl->zrleData; + zos->in.ptr = zos->in.start; + zos->out.ptr = zos->out.start; + + switch (cl->format.bitsPerPixel) { + + case 8: + zrleEncode8( x, y, w, h, zos, zrleBeforeBuf, cl); + break; + + case 16: + zrleEncode16(x, y, w, h, zos, zrleBeforeBuf, cl); + break; + + case 32: { + rfbBool fitsInLS3Bytes + = ((cl->format.redMax << cl->format.redShift) < (1<<24) && + (cl->format.greenMax << cl->format.greenShift) < (1<<24) && + (cl->format.blueMax << cl->format.blueShift) < (1<<24)); + + rfbBool fitsInMS3Bytes = (cl->format.redShift > 7 && + cl->format.greenShift > 7 && + cl->format.blueShift > 7); + + if ((fitsInLS3Bytes && !cl->format.bigEndian) || + (fitsInMS3Bytes && cl->format.bigEndian)) + { + zrleEncode24A(x, y, w, h, zos, zrleBeforeBuf, cl); + } + else if ((fitsInLS3Bytes && cl->format.bigEndian) || + (fitsInMS3Bytes && !cl->format.bigEndian)) + { + zrleEncode24B(x, y, w, h, zos, zrleBeforeBuf, cl); + } + else + { + zrleEncode32(x, y, w, h, zos, zrleBeforeBuf, cl); + } + } + break; + } + + cl->rfbRectanglesSent[rfbEncodingZRLE]++; + cl->rfbBytesSent[rfbEncodingZRLE] += (sz_rfbFramebufferUpdateRectHeader + + sz_rfbZRLEHeader + ZRLE_BUFFER_LENGTH(&zos->out)); + + if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbZRLEHeader + > UPDATE_BUF_SIZE) + { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + rect.r.x = Swap16IfLE(x); + rect.r.y = Swap16IfLE(y); + rect.r.w = Swap16IfLE(w); + rect.r.h = Swap16IfLE(h); + rect.encoding = Swap32IfLE(rfbEncodingZRLE); + + memcpy(cl->updateBuf+cl->ublen, (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + cl->ublen += sz_rfbFramebufferUpdateRectHeader; + + hdr.length = Swap32IfLE(ZRLE_BUFFER_LENGTH(&zos->out)); + + memcpy(cl->updateBuf+cl->ublen, (char *)&hdr, sz_rfbZRLEHeader); + cl->ublen += sz_rfbZRLEHeader; + + /* copy into updateBuf and send from there. Maybe should send directly? */ + + for (i = 0; i < ZRLE_BUFFER_LENGTH(&zos->out);) { + + int bytesToCopy = UPDATE_BUF_SIZE - cl->ublen; + + if (i + bytesToCopy > ZRLE_BUFFER_LENGTH(&zos->out)) { + bytesToCopy = ZRLE_BUFFER_LENGTH(&zos->out) - i; + } + + memcpy(cl->updateBuf+cl->ublen, (uint8_t*)zos->out.start + i, bytesToCopy); + + cl->ublen += bytesToCopy; + i += bytesToCopy; + + if (cl->ublen == UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + } + + return TRUE; +} + + +void FreeZrleData(rfbClientPtr cl) +{ + if (cl->zrleData) + zrleOutStreamFree(cl->zrleData); + cl->zrleData = NULL; +} + diff --git a/libvncserver/zrleencodetemplate.c b/libvncserver/zrleencodetemplate.c new file mode 100644 index 0000000..a1772ae --- /dev/null +++ b/libvncserver/zrleencodetemplate.c @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2003 Sun Microsystems, Inc. + * + * 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. + */ + +/* + * Before including this file, you must define a number of CPP macros. + * + * BPP should be 8, 16 or 32 depending on the bits per pixel. + * GET_IMAGE_INTO_BUF should be some code which gets a rectangle of pixel data + * into the given buffer. EXTRA_ARGS can be defined to pass any other + * arguments needed by GET_IMAGE_INTO_BUF. + * + * Note that the buf argument to ZRLE_ENCODE needs to be at least one pixel + * bigger than the largest tile of pixel data, since the ZRLE encoding + * algorithm writes to the position one past the end of the pixel data. + */ + +#include "zrleoutstream.h" +#include "zrlepalettehelper.h" +#include + +/* __RFB_CONCAT2 concatenates its two arguments. __RFB_CONCAT2E does the same + but also expands its arguments if they are macros */ + +#ifndef __RFB_CONCAT2E +#define __RFB_CONCAT2(a,b) a##b +#define __RFB_CONCAT2E(a,b) __RFB_CONCAT2(a,b) +#endif + +#ifdef CPIXEL +#define PIXEL_T __RFB_CONCAT2E(zrle_U,BPP) +#define zrleOutStreamWRITE_PIXEL __RFB_CONCAT2E(zrleOutStreamWriteOpaque,CPIXEL) +#define ZRLE_ENCODE __RFB_CONCAT2E(zrleEncode,CPIXEL) +#define ZRLE_ENCODE_TILE __RFB_CONCAT2E(zrleEncodeTile,CPIXEL) +#define BPPOUT 24 +#else +#define PIXEL_T __RFB_CONCAT2E(zrle_U,BPP) +#define zrleOutStreamWRITE_PIXEL __RFB_CONCAT2E(zrleOutStreamWriteOpaque,BPP) +#define ZRLE_ENCODE __RFB_CONCAT2E(zrleEncode,BPP) +#define ZRLE_ENCODE_TILE __RFB_CONCAT2E(zrleEncodeTile,BPP) +#define BPPOUT BPP +#endif + +#ifndef ZRLE_ONCE +#define ZRLE_ONCE + +static const int bitsPerPackedPixel[] = { + 0, 1, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 +}; + +static zrlePaletteHelper paletteHelper; + +#endif /* ZRLE_ONCE */ + +void ZRLE_ENCODE_TILE (PIXEL_T* data, int w, int h, zrleOutStream* os); + +void ZRLE_ENCODE (int x, int y, int w, int h, + zrleOutStream* os, void* buf + EXTRA_ARGS + ) +{ + int ty; + for (ty = y; ty < y+h; ty += rfbZRLETileHeight) { + int tx, th = rfbZRLETileHeight; + if (th > y+h-ty) th = y+h-ty; + for (tx = x; tx < x+w; tx += rfbZRLETileWidth) { + int tw = rfbZRLETileWidth; + if (tw > x+w-tx) tw = x+w-tx; + + GET_IMAGE_INTO_BUF(tx,ty,tw,th,buf); + + ZRLE_ENCODE_TILE((PIXEL_T*)buf, tw, th, os); + } + } + zrleOutStreamFlush(os); +} + + +void ZRLE_ENCODE_TILE (PIXEL_T* data, int w, int h, zrleOutStream* os) +{ + /* First find the palette and the number of runs */ + + zrlePaletteHelper *ph; + + int runs = 0; + int singlePixels = 0; + + rfbBool useRle; + rfbBool usePalette; + + int estimatedBytes; + int plainRleBytes; + int i; + + PIXEL_T* ptr = data; + PIXEL_T* end = ptr + h * w; + *end = ~*(end-1); /* one past the end is different so the while loop ends */ + + ph = &paletteHelper; + zrlePaletteHelperInit(ph); + + while (ptr < end) { + PIXEL_T pix = *ptr; + if (*++ptr != pix) { + singlePixels++; + } else { + while (*++ptr == pix) ; + runs++; + } + zrlePaletteHelperInsert(ph, pix); + } + + /* Solid tile is a special case */ + + if (ph->size == 1) { + zrleOutStreamWriteU8(os, 1); + zrleOutStreamWRITE_PIXEL(os, ph->palette[0]); + return; + } + + // Try to work out whether to use RLE and/or a palette. We do this by + // estimating the number of bytes which will be generated and picking the + // method which results in the fewest bytes. Of course this may not result + // in the fewest bytes after compression... + + useRle = FALSE; + usePalette = FALSE; + + estimatedBytes = w * h * (BPPOUT/8); // start assuming raw + + plainRleBytes = ((BPPOUT/8)+1) * (runs + singlePixels); + + if (plainRleBytes < estimatedBytes) { + useRle = TRUE; + estimatedBytes = plainRleBytes; + } + + if (ph->size < 128) { + int paletteRleBytes = (BPPOUT/8) * ph->size + 2 * runs + singlePixels; + + if (paletteRleBytes < estimatedBytes) { + useRle = TRUE; + usePalette = TRUE; + estimatedBytes = paletteRleBytes; + } + + if (ph->size < 17) { + int packedBytes = ((BPPOUT/8) * ph->size + + w * h * bitsPerPackedPixel[ph->size-1] / 8); + + if (packedBytes < estimatedBytes) { + useRle = FALSE; + usePalette = TRUE; + estimatedBytes = packedBytes; + } + } + } + + if (!usePalette) ph->size = 0; + + zrleOutStreamWriteU8(os, (useRle ? 128 : 0) | ph->size); + + for (i = 0; i < ph->size; i++) { + zrleOutStreamWRITE_PIXEL(os, ph->palette[i]); + } + + if (useRle) { + + PIXEL_T* ptr = data; + PIXEL_T* end = ptr + w * h; + PIXEL_T* runStart; + PIXEL_T pix; + while (ptr < end) { + int len; + runStart = ptr; + pix = *ptr++; + while (*ptr == pix && ptr < end) + ptr++; + len = ptr - runStart; + if (len <= 2 && usePalette) { + int index = zrlePaletteHelperLookup(ph, pix); + if (len == 2) + zrleOutStreamWriteU8(os, index); + zrleOutStreamWriteU8(os, index); + continue; + } + if (usePalette) { + int index = zrlePaletteHelperLookup(ph, pix); + zrleOutStreamWriteU8(os, index | 128); + } else { + zrleOutStreamWRITE_PIXEL(os, pix); + } + len -= 1; + while (len >= 255) { + zrleOutStreamWriteU8(os, 255); + len -= 255; + } + zrleOutStreamWriteU8(os, len); + } + + } else { + + // no RLE + + if (usePalette) { + int bppp; + PIXEL_T* ptr = data; + + // packed pixels + + assert (ph->size < 17); + + bppp = bitsPerPackedPixel[ph->size-1]; + + for (i = 0; i < h; i++) { + zrle_U8 nbits = 0; + zrle_U8 byte = 0; + + PIXEL_T* eol = ptr + w; + + while (ptr < eol) { + PIXEL_T pix = *ptr++; + zrle_U8 index = zrlePaletteHelperLookup(ph, pix); + byte = (byte << bppp) | index; + nbits += bppp; + if (nbits >= 8) { + zrleOutStreamWriteU8(os, byte); + nbits = 0; + } + } + if (nbits > 0) { + byte <<= 8 - nbits; + zrleOutStreamWriteU8(os, byte); + } + } + } else { + + // raw + +#ifdef CPIXEL + PIXEL_T *ptr; + for (ptr = data; ptr < data+w*h; ptr++) { + zrleOutStreamWRITE_PIXEL(os, *ptr); + } +#else + zrleOutStreamWriteBytes(os, (zrle_U8 *)data, w*h*(BPP/8)); +#endif + } + } +} + +#undef PIXEL_T +#undef zrleOutStreamWRITE_PIXEL +#undef ZRLE_ENCODE +#undef ZRLE_ENCODE_TILE +#undef BPPOUT diff --git a/libvncserver/zrleoutstream.c b/libvncserver/zrleoutstream.c new file mode 100644 index 0000000..d22d649 --- /dev/null +++ b/libvncserver/zrleoutstream.c @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2003 Sun Microsystems, Inc. + * + * 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. + */ + +#include "zrleoutstream.h" +#include + +#define ZRLE_IN_BUFFER_SIZE 16384 +#define ZRLE_OUT_BUFFER_SIZE 1024 +#undef ZRLE_DEBUG + +static rfbBool zrleBufferAlloc(zrleBuffer *buffer, int size) +{ + buffer->ptr = buffer->start = malloc(size); + if (buffer->start == NULL) { + buffer->end = NULL; + return FALSE; + } + + buffer->end = buffer->start + size; + + return TRUE; +} + +static void zrleBufferFree(zrleBuffer *buffer) +{ + if (buffer->start) + free(buffer->start); + buffer->start = buffer->ptr = buffer->end = NULL; +} + +static rfbBool zrleBufferGrow(zrleBuffer *buffer, int size) +{ + int offset; + + size += buffer->end - buffer->start; + offset = ZRLE_BUFFER_LENGTH (buffer); + + buffer->start = realloc(buffer->start, size); + if (!buffer->start) { + return FALSE; + } + + buffer->end = buffer->start + size; + buffer->ptr = buffer->start + offset; + + return TRUE; +} + +zrleOutStream *zrleOutStreamNew(void) +{ + zrleOutStream *os; + + os = malloc(sizeof(zrleOutStream)); + if (os == NULL) + return NULL; + + if (!zrleBufferAlloc(&os->in, ZRLE_IN_BUFFER_SIZE)) { + free(os); + return NULL; + } + + if (!zrleBufferAlloc(&os->out, ZRLE_OUT_BUFFER_SIZE)) { + zrleBufferFree(&os->in); + free(os); + return NULL; + } + + os->zs.zalloc = Z_NULL; + os->zs.zfree = Z_NULL; + os->zs.opaque = Z_NULL; + if (deflateInit(&os->zs, Z_DEFAULT_COMPRESSION) != Z_OK) { + zrleBufferFree(&os->in); + free(os); + return NULL; + } + + return os; +} + +void zrleOutStreamFree (zrleOutStream *os) +{ + deflateEnd(&os->zs); + zrleBufferFree(&os->in); + zrleBufferFree(&os->out); + free(os); +} + +rfbBool zrleOutStreamFlush(zrleOutStream *os) +{ + os->zs.next_in = os->in.start; + os->zs.avail_in = ZRLE_BUFFER_LENGTH (&os->in); + +#ifdef ZRLE_DEBUG + rfbLog("zrleOutStreamFlush: avail_in %d\n", os->zs.avail_in); +#endif + + while (os->zs.avail_in != 0) { + do { + int ret; + + if (os->out.ptr >= os->out.end && + !zrleBufferGrow(&os->out, os->out.end - os->out.start)) { + rfbLog("zrleOutStreamFlush: failed to grow output buffer\n"); + return FALSE; + } + + os->zs.next_out = os->out.ptr; + os->zs.avail_out = os->out.end - os->out.ptr; + +#ifdef ZRLE_DEBUG + rfbLog("zrleOutStreamFlush: calling deflate, avail_in %d, avail_out %d\n", + os->zs.avail_in, os->zs.avail_out); +#endif + + if ((ret = deflate(&os->zs, Z_SYNC_FLUSH)) != Z_OK) { + rfbLog("zrleOutStreamFlush: deflate failed with error code %d\n", ret); + return FALSE; + } + +#ifdef ZRLE_DEBUG + rfbLog("zrleOutStreamFlush: after deflate: %d bytes\n", + os->zs.next_out - os->out.ptr); +#endif + + os->out.ptr = os->zs.next_out; + } while (os->zs.avail_out == 0); + } + + os->in.ptr = os->in.start; + + return TRUE; +} + +static int zrleOutStreamOverrun(zrleOutStream *os, + int size) +{ +#ifdef ZRLE_DEBUG + rfbLog("zrleOutStreamOverrun\n"); +#endif + + while (os->in.end - os->in.ptr < size && os->in.ptr > os->in.start) { + os->zs.next_in = os->in.start; + os->zs.avail_in = ZRLE_BUFFER_LENGTH (&os->in); + + do { + int ret; + + if (os->out.ptr >= os->out.end && + !zrleBufferGrow(&os->out, os->out.end - os->out.start)) { + rfbLog("zrleOutStreamOverrun: failed to grow output buffer\n"); + return FALSE; + } + + os->zs.next_out = os->out.ptr; + os->zs.avail_out = os->out.end - os->out.ptr; + +#ifdef ZRLE_DEBUG + rfbLog("zrleOutStreamOverrun: calling deflate, avail_in %d, avail_out %d\n", + os->zs.avail_in, os->zs.avail_out); +#endif + + if ((ret = deflate(&os->zs, 0)) != Z_OK) { + rfbLog("zrleOutStreamOverrun: deflate failed with error code %d\n", ret); + return 0; + } + +#ifdef ZRLE_DEBUG + rfbLog("zrleOutStreamOverrun: after deflate: %d bytes\n", + os->zs.next_out - os->out.ptr); +#endif + + os->out.ptr = os->zs.next_out; + } while (os->zs.avail_out == 0); + + /* output buffer not full */ + + if (os->zs.avail_in == 0) { + os->in.ptr = os->in.start; + } else { + /* but didn't consume all the data? try shifting what's left to the + * start of the buffer. + */ + rfbLog("zrleOutStreamOverrun: out buf not full, but in data not consumed\n"); + memmove(os->in.start, os->zs.next_in, os->in.ptr - os->zs.next_in); + os->in.ptr -= os->zs.next_in - os->in.start; + } + } + + if (size > os->in.end - os->in.ptr) + size = os->in.end - os->in.ptr; + + return size; +} + +static inline int zrleOutStreamCheck(zrleOutStream *os, + int size) +{ + if (os->in.ptr + size > os->in.end) { + return zrleOutStreamOverrun(os, size); + } + return size; +} + +void zrleOutStreamWriteBytes(zrleOutStream *os, + const zrle_U8 *data, + int length) +{ + const zrle_U8* dataEnd = data + length; + while (data < dataEnd) { + int n = zrleOutStreamCheck(os, dataEnd - data); + memcpy(os->in.ptr, data, n); + os->in.ptr += n; + data += n; + } +} + +void zrleOutStreamWriteU8(zrleOutStream *os, zrle_U8 u) +{ + zrleOutStreamCheck(os, 1); + *os->in.ptr++ = u; +} + +void zrleOutStreamWriteOpaque8(zrleOutStream *os, zrle_U8 u) +{ + zrleOutStreamCheck(os, 1); + *os->in.ptr++ = u; +} + +void zrleOutStreamWriteOpaque16 (zrleOutStream *os, zrle_U16 u) +{ + zrleOutStreamCheck(os, 2); + *os->in.ptr++ = ((zrle_U8*)&u)[0]; + *os->in.ptr++ = ((zrle_U8*)&u)[1]; +} + +void zrleOutStreamWriteOpaque32 (zrleOutStream *os, zrle_U32 u) +{ + zrleOutStreamCheck(os, 4); + *os->in.ptr++ = ((zrle_U8*)&u)[0]; + *os->in.ptr++ = ((zrle_U8*)&u)[1]; + *os->in.ptr++ = ((zrle_U8*)&u)[2]; + *os->in.ptr++ = ((zrle_U8*)&u)[3]; +} + +void zrleOutStreamWriteOpaque24A(zrleOutStream *os, zrle_U32 u) +{ + zrleOutStreamCheck(os, 3); + *os->in.ptr++ = ((zrle_U8*)&u)[0]; + *os->in.ptr++ = ((zrle_U8*)&u)[1]; + *os->in.ptr++ = ((zrle_U8*)&u)[2]; +} + +void zrleOutStreamWriteOpaque24B(zrleOutStream *os, zrle_U32 u) +{ + zrleOutStreamCheck(os, 3); + *os->in.ptr++ = ((zrle_U8*)&u)[1]; + *os->in.ptr++ = ((zrle_U8*)&u)[2]; + *os->in.ptr++ = ((zrle_U8*)&u)[3]; +} diff --git a/libvncserver/zrleoutstream.h b/libvncserver/zrleoutstream.h new file mode 100644 index 0000000..9e4fe51 --- /dev/null +++ b/libvncserver/zrleoutstream.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2003 Sun Microsystems, Inc. + * + * 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. + */ + +#ifndef __ZRLE_OUT_STREAM_H__ +#define __ZRLE_OUT_STREAM_H__ + +#include +#include "zrletypes.h" +#include "rfb/rfb.h" + +typedef struct { + zrle_U8 *start; + zrle_U8 *ptr; + zrle_U8 *end; +} zrleBuffer; + +typedef struct { + zrleBuffer in; + zrleBuffer out; + + z_stream zs; +} zrleOutStream; + +#define ZRLE_BUFFER_LENGTH(b) ((b)->ptr - (b)->start) + +zrleOutStream *zrleOutStreamNew (void); +void zrleOutStreamFree (zrleOutStream *os); +rfbBool zrleOutStreamFlush (zrleOutStream *os); +void zrleOutStreamWriteBytes (zrleOutStream *os, + const zrle_U8 *data, + int length); +void zrleOutStreamWriteU8 (zrleOutStream *os, + zrle_U8 u); +void zrleOutStreamWriteOpaque8 (zrleOutStream *os, + zrle_U8 u); +void zrleOutStreamWriteOpaque16 (zrleOutStream *os, + zrle_U16 u); +void zrleOutStreamWriteOpaque32 (zrleOutStream *os, + zrle_U32 u); +void zrleOutStreamWriteOpaque24A(zrleOutStream *os, + zrle_U32 u); +void zrleOutStreamWriteOpaque24B(zrleOutStream *os, + zrle_U32 u); + +#endif /* __ZRLE_OUT_STREAM_H__ */ diff --git a/libvncserver/zrlepalettehelper.c b/libvncserver/zrlepalettehelper.c new file mode 100644 index 0000000..d758a26 --- /dev/null +++ b/libvncserver/zrlepalettehelper.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2003 Sun Microsystems, Inc. + * + * 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. + */ + +#include "zrlepalettehelper.h" +#include +#include + +#define ZRLE_HASH(pix) (((pix) ^ ((pix) >> 17)) & 4095) + +void zrlePaletteHelperInit(zrlePaletteHelper *helper) +{ + memset(helper->palette, 0, sizeof(helper->palette)); + memset(helper->index, 255, sizeof(helper->index)); + memset(helper->key, 0, sizeof(helper->key)); + helper->size = 0; +} + +void zrlePaletteHelperInsert(zrlePaletteHelper *helper, zrle_U32 pix) +{ + if (helper->size < ZRLE_PALETTE_MAX_SIZE) { + int i = ZRLE_HASH(pix); + + while (helper->index[i] != 255 && helper->key[i] != pix) + i++; + if (helper->index[i] != 255) return; + + helper->index[i] = helper->size; + helper->key[i] = pix; + helper->palette[helper->size] = pix; + } + helper->size++; +} + +int zrlePaletteHelperLookup(zrlePaletteHelper *helper, zrle_U32 pix) +{ + int i = ZRLE_HASH(pix); + + assert(helper->size <= ZRLE_PALETTE_MAX_SIZE); + + while (helper->index[i] != 255 && helper->key[i] != pix) + i++; + if (helper->index[i] != 255) return helper->index[i]; + + return -1; +} diff --git a/libvncserver/zrlepalettehelper.h b/libvncserver/zrlepalettehelper.h new file mode 100644 index 0000000..e1213d1 --- /dev/null +++ b/libvncserver/zrlepalettehelper.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2003 Sun Microsystems, Inc. + * + * 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. + */ + +/* + * The PaletteHelper class helps us build up the palette from pixel data by + * storing a reverse index using a simple hash-table + */ + +#ifndef __ZRLE_PALETTE_HELPER_H__ +#define __ZRLE_PALETTE_HELPER_H__ + +#include "zrletypes.h" + +#define ZRLE_PALETTE_MAX_SIZE 127 + +typedef struct { + zrle_U32 palette[ZRLE_PALETTE_MAX_SIZE]; + zrle_U8 index[ZRLE_PALETTE_MAX_SIZE + 4096]; + zrle_U32 key[ZRLE_PALETTE_MAX_SIZE + 4096]; + int size; +} zrlePaletteHelper; + +void zrlePaletteHelperInit (zrlePaletteHelper *helper); +void zrlePaletteHelperInsert(zrlePaletteHelper *helper, + zrle_U32 pix); +int zrlePaletteHelperLookup(zrlePaletteHelper *helper, + zrle_U32 pix); + +#endif /* __ZRLE_PALETTE_HELPER_H__ */ diff --git a/libvncserver/zrletypes.h b/libvncserver/zrletypes.h new file mode 100755 index 0000000..0df42c9 --- /dev/null +++ b/libvncserver/zrletypes.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2002 RealVNC Ltd. 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. + */ + +#ifndef __ZRLE_TYPES_H__ +#define __ZRLE_TYPES_H__ + +typedef unsigned char zrle_U8; +typedef unsigned short zrle_U16; +typedef unsigned int zrle_U32; +typedef signed char zrle_S8; +typedef signed short zrle_S16; +typedef signed int zrle_S32; + +#endif /* __ZRLE_TYPES_H__ */ diff --git a/main.c b/main.c deleted file mode 100644 index 488f09d..0000000 --- a/main.c +++ /dev/null @@ -1,851 +0,0 @@ -/* - * This file is called main.c, because it contains most of the new functions - * for use with LibVNCServer. - * - * LibVNCServer (C) 2001 Johannes E. Schindelin - * Original OSXvnc (C) 2001 Dan McGuirk . - * Original Xvnc (C) 1999 AT&T Laboratories Cambridge. - * All Rights Reserved. - * - * see GPL (latest version) for full details - */ - -#include -#include - -#include -#include - -#ifndef false -#define false 0 -#define true -1 -#endif - -#ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H -#include -#endif - -#ifndef WIN32 -#include -#include -#include -#endif - -#include -#include - -#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD -MUTEX(logMutex); -#endif - -int rfbEnableLogging=1; - -#ifdef LIBVNCSERVER_WORDS_BIGENDIAN -char rfbEndianTest = 0; -#else -char rfbEndianTest = -1; -#endif - -/* from rfbserver.c */ -void rfbIncrClientRef(rfbClientPtr cl); -void rfbDecrClientRef(rfbClientPtr cl); - -void rfbLogEnable(int enabled) { - rfbEnableLogging=enabled; -} - -/* - * rfbLog prints a time-stamped message to the log file (stderr). - */ - -void -rfbDefaultLog(const char *format, ...) -{ - va_list args; - char buf[256]; - time_t log_clock; - - if(!rfbEnableLogging) - return; - - LOCK(logMutex); - va_start(args, format); - - time(&log_clock); - strftime(buf, 255, "%d/%m/%Y %X ", localtime(&log_clock)); - fprintf(stderr,buf); - - vfprintf(stderr, format, args); - fflush(stderr); - - va_end(args); - UNLOCK(logMutex); -} - -rfbLogProc rfbLog=rfbDefaultLog; -rfbLogProc rfbErr=rfbDefaultLog; - -void rfbLogPerror(const char *str) -{ - rfbErr("%s: %s\n", str, strerror(errno)); -} - -void rfbScheduleCopyRegion(rfbScreenInfoPtr rfbScreen,sraRegionPtr copyRegion,int dx,int dy) -{ - rfbClientIteratorPtr iterator; - rfbClientPtr cl; - - rfbUndrawCursor(rfbScreen); - - iterator=rfbGetClientIterator(rfbScreen); - while((cl=rfbClientIteratorNext(iterator))) { - LOCK(cl->updateMutex); - if(cl->useCopyRect) { - sraRegionPtr modifiedRegionBackup; - if(!sraRgnEmpty(cl->copyRegion)) { - if(cl->copyDX!=dx || cl->copyDY!=dy) { - /* if a copyRegion was not yet executed, treat it as a - * modifiedRegion. The idea: in this case it could be - * source of the new copyRect or modified anyway. */ - sraRgnOr(cl->modifiedRegion,cl->copyRegion); - sraRgnMakeEmpty(cl->copyRegion); - } else { - /* we have to set the intersection of the source of the copy - * and the old copy to modified. */ - modifiedRegionBackup=sraRgnCreateRgn(copyRegion); - sraRgnOffset(modifiedRegionBackup,-dx,-dy); - sraRgnAnd(modifiedRegionBackup,cl->copyRegion); - sraRgnOr(cl->modifiedRegion,modifiedRegionBackup); - sraRgnDestroy(modifiedRegionBackup); - } - } - - sraRgnOr(cl->copyRegion,copyRegion); - cl->copyDX = dx; - cl->copyDY = dy; - - /* if there were modified regions, which are now copied, - * mark them as modified, because the source of these can be overlapped - * either by new modified or now copied regions. */ - modifiedRegionBackup=sraRgnCreateRgn(cl->modifiedRegion); - sraRgnOffset(modifiedRegionBackup,dx,dy); - sraRgnAnd(modifiedRegionBackup,cl->copyRegion); - sraRgnOr(cl->modifiedRegion,modifiedRegionBackup); - sraRgnDestroy(modifiedRegionBackup); - -#if 0 - /* TODO: is this needed? Or does it mess up deferring? */ - /* while(!sraRgnEmpty(cl->copyRegion)) */ { -#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD - if(!cl->screen->backgroundLoop) -#endif - { - sraRegionPtr updateRegion = sraRgnCreateRgn(cl->modifiedRegion); - sraRgnOr(updateRegion,cl->copyRegion); - UNLOCK(cl->updateMutex); - rfbSendFramebufferUpdate(cl,updateRegion); - sraRgnDestroy(updateRegion); - continue; - } - } -#endif - } else { - sraRgnOr(cl->modifiedRegion,copyRegion); - } - TSIGNAL(cl->updateCond); - UNLOCK(cl->updateMutex); - } - - rfbReleaseClientIterator(iterator); -} - -void rfbDoCopyRegion(rfbScreenInfoPtr rfbScreen,sraRegionPtr copyRegion,int dx,int dy) -{ - sraRectangleIterator* i; - sraRect rect; - int j,widthInBytes,bpp=rfbScreen->rfbServerFormat.bitsPerPixel/8, - rowstride=rfbScreen->paddedWidthInBytes; - char *in,*out; - - rfbUndrawCursor(rfbScreen); - - /* copy it, really */ - i = sraRgnGetReverseIterator(copyRegion,dx<0,dy<0); - while(sraRgnIteratorNext(i,&rect)) { - widthInBytes = (rect.x2-rect.x1)*bpp; - out = rfbScreen->frameBuffer+rect.x1*bpp+rect.y1*rowstride; - in = rfbScreen->frameBuffer+(rect.x1-dx)*bpp+(rect.y1-dy)*rowstride; - if(dy<0) - for(j=rect.y1;j=rect.y1;j--,out-=rowstride,in-=rowstride) - memmove(out,in,widthInBytes); - } - } - - rfbScheduleCopyRegion(rfbScreen,copyRegion,dx,dy); -} - -void rfbDoCopyRect(rfbScreenInfoPtr rfbScreen,int x1,int y1,int x2,int y2,int dx,int dy) -{ - sraRegionPtr region = sraRgnCreateRect(x1,y1,x2,y2); - rfbDoCopyRegion(rfbScreen,region,dx,dy); -} - -void rfbScheduleCopyRect(rfbScreenInfoPtr rfbScreen,int x1,int y1,int x2,int y2,int dx,int dy) -{ - sraRegionPtr region = sraRgnCreateRect(x1,y1,x2,y2); - rfbScheduleCopyRegion(rfbScreen,region,dx,dy); -} - -void rfbMarkRegionAsModified(rfbScreenInfoPtr rfbScreen,sraRegionPtr modRegion) -{ - rfbClientIteratorPtr iterator; - rfbClientPtr cl; - - iterator=rfbGetClientIterator(rfbScreen); - while((cl=rfbClientIteratorNext(iterator))) { - LOCK(cl->updateMutex); - sraRgnOr(cl->modifiedRegion,modRegion); - TSIGNAL(cl->updateCond); - UNLOCK(cl->updateMutex); - } - - rfbReleaseClientIterator(iterator); -} - -void rfbMarkRectAsModified(rfbScreenInfoPtr rfbScreen,int x1,int y1,int x2,int y2) -{ - sraRegionPtr region; - int i; - - if(x1>x2) { i=x1; x1=x2; x2=i; } - if(x1<0) x1=0; - if(x2>=rfbScreen->width) x2=rfbScreen->width-1; - if(x1==x2) return; - - if(y1>y2) { i=y1; y1=y2; y2=i; } - if(y1<0) y1=0; - if(y2>=rfbScreen->height) y2=rfbScreen->height-1; - if(y1==y2) return; - - region = sraRgnCreateRect(x1,y1,x2,y2); - rfbMarkRegionAsModified(rfbScreen,region); - sraRgnDestroy(region); -} - -#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD -static void * -clientOutput(void *data) -{ - rfbClientPtr cl = (rfbClientPtr)data; - rfbBool haveUpdate; - sraRegion* updateRegion; - - while (1) { - haveUpdate = false; - while (!haveUpdate) { - if (cl->sock == -1) { - /* Client has disconnected. */ - return NULL; - } - LOCK(cl->updateMutex); - haveUpdate = FB_UPDATE_PENDING(cl); - if(!haveUpdate) { - updateRegion = sraRgnCreateRgn(cl->modifiedRegion); - haveUpdate = sraRgnAnd(updateRegion,cl->requestedRegion); - sraRgnDestroy(updateRegion); - } - UNLOCK(cl->updateMutex); - - if (!haveUpdate) { - WAIT(cl->updateCond, cl->updateMutex); - UNLOCK(cl->updateMutex); /* we really needn't lock now. */ - } - } - - /* OK, now, to save bandwidth, wait a little while for more - updates to come along. */ - usleep(cl->screen->rfbDeferUpdateTime * 1000); - - /* Now, get the region we're going to update, and remove - it from cl->modifiedRegion _before_ we send the update. - That way, if anything that overlaps the region we're sending - is updated, we'll be sure to do another update later. */ - LOCK(cl->updateMutex); - updateRegion = sraRgnCreateRgn(cl->modifiedRegion); - UNLOCK(cl->updateMutex); - - /* Now actually send the update. */ - rfbIncrClientRef(cl); - rfbSendFramebufferUpdate(cl, updateRegion); - rfbDecrClientRef(cl); - - sraRgnDestroy(updateRegion); - } - - return NULL; -} - -static void * -clientInput(void *data) -{ - rfbClientPtr cl = (rfbClientPtr)data; - pthread_t output_thread; - pthread_create(&output_thread, NULL, clientOutput, (void *)cl); - - while (1) { - rfbProcessClientMessage(cl); - if (cl->sock == -1) { - /* Client has disconnected. */ - break; - } - } - - /* Get rid of the output thread. */ - LOCK(cl->updateMutex); - TSIGNAL(cl->updateCond); - UNLOCK(cl->updateMutex); - IF_PTHREADS(pthread_join(output_thread, NULL)); - - rfbClientConnectionGone(cl); - - return NULL; -} - -static void* -listenerRun(void *data) -{ - rfbScreenInfoPtr rfbScreen=(rfbScreenInfoPtr)data; - int client_fd; - struct sockaddr_in peer; - rfbClientPtr cl; - size_t len; - - len = sizeof(peer); - - /* TODO: this thread wont die by restarting the server */ - while ((client_fd = accept(rfbScreen->rfbListenSock, - (struct sockaddr*)&peer, &len)) >= 0) { - cl = rfbNewClient(rfbScreen,client_fd); - len = sizeof(peer); - - if (cl && !cl->onHold ) - rfbStartOnHoldClient(cl); - } - return(NULL); -} - -void -rfbStartOnHoldClient(rfbClientPtr cl) -{ - pthread_create(&cl->client_thread, NULL, clientInput, (void *)cl); -} - -#else - -void -rfbStartOnHoldClient(rfbClientPtr cl) -{ - cl->onHold = FALSE; -} - -#endif - -void -rfbRefuseOnHoldClient(rfbClientPtr cl) -{ - rfbCloseClient(cl); - rfbClientConnectionGone(cl); -} - -static void -defaultKbdAddEvent(rfbBool down, rfbKeySym keySym, rfbClientPtr cl) -{ -} - -void -defaultPtrAddEvent(int buttonMask, int x, int y, rfbClientPtr cl) -{ - rfbClientIteratorPtr iterator; - rfbClientPtr other_client; - - if (x != cl->screen->cursorX || y != cl->screen->cursorY) { - if (cl->screen->cursorIsDrawn) - rfbUndrawCursor(cl->screen); - LOCK(cl->screen->cursorMutex); - if (!cl->screen->cursorIsDrawn) { - cl->screen->cursorX = x; - cl->screen->cursorY = y; - } - UNLOCK(cl->screen->cursorMutex); - - /* The cursor was moved by this client, so don't send CursorPos. */ - if (cl->enableCursorPosUpdates) - cl->cursorWasMoved = FALSE; - - /* But inform all remaining clients about this cursor movement. */ - iterator = rfbGetClientIterator(cl->screen); - while ((other_client = rfbClientIteratorNext(iterator)) != NULL) { - if (other_client != cl && other_client->enableCursorPosUpdates) { - other_client->cursorWasMoved = TRUE; - } - } - rfbReleaseClientIterator(iterator); - } -} - -void defaultSetXCutText(char* text, int len, rfbClientPtr cl) -{ -} - -/* TODO: add a nice VNC or RFB cursor */ - -#if defined(WIN32) || defined(sparc) || !defined(NO_STRICT_ANSI) -static rfbCursor myCursor = -{ - FALSE, FALSE, FALSE, FALSE, - (unsigned char*)"\000\102\044\030\044\102\000", - (unsigned char*)"\347\347\176\074\176\347\347", - 8, 7, 3, 3, - 0, 0, 0, - 0xffff, 0xffff, 0xffff, - 0 -}; -#else -static rfbCursor myCursor = -{ - cleanup: FALSE, - cleanupSource: FALSE, - cleanupMask: FALSE, - cleanupRichSource: FALSE, - source: "\000\102\044\030\044\102\000", - mask: "\347\347\176\074\176\347\347", - width: 8, height: 7, xhot: 3, yhot: 3, - foreRed: 0, foreGreen: 0, foreBlue: 0, - backRed: 0xffff, backGreen: 0xffff, backBlue: 0xffff, - richSource: 0 -}; -#endif - -rfbCursorPtr defaultGetCursorPtr(rfbClientPtr cl) -{ - return(cl->screen->cursor); -} - -/* response is cl->authChallenge vncEncrypted with passwd */ -rfbBool defaultPasswordCheck(rfbClientPtr cl,const char* response,int len) -{ - int i; - char *passwd=vncDecryptPasswdFromFile(cl->screen->rfbAuthPasswdData); - - if(!passwd) { - rfbErr("Couldn't read password file: %s\n",cl->screen->rfbAuthPasswdData); - return(FALSE); - } - - vncEncryptBytes(cl->authChallenge, passwd); - - /* Lose the password from memory */ - for (i = strlen(passwd); i >= 0; i--) { - passwd[i] = '\0'; - } - - free(passwd); - - if (memcmp(cl->authChallenge, response, len) != 0) { - rfbErr("rfbAuthProcessClientMessage: authentication failed from %s\n", - cl->host); - return(FALSE); - } - - return(TRUE); -} - -/* for this method, rfbAuthPasswdData is really a pointer to an array - of char*'s, where the last pointer is 0. */ -rfbBool rfbCheckPasswordByList(rfbClientPtr cl,const char* response,int len) -{ - char **passwds; - int i=0; - - for(passwds=(char**)cl->screen->rfbAuthPasswdData;*passwds;passwds++,i++) { - vncEncryptBytes(cl->authChallenge, *passwds); - - if (memcmp(cl->authChallenge, response, len) == 0) { - if(i>=cl->screen->rfbAuthPasswdFirstViewOnly) - cl->viewOnly=TRUE; - return(TRUE); - } - } - - rfbErr("rfbAuthProcessClientMessage: authentication failed from %s\n", - cl->host); - return(FALSE); -} - -void doNothingWithClient(rfbClientPtr cl) -{ -} - -enum rfbNewClientAction defaultNewClientHook(rfbClientPtr cl) -{ - return RFB_CLIENT_ACCEPT; -} - -/* - * Update server's pixel format in rfbScreenInfo structure. This - * function is called from rfbGetScreen() and rfbNewFramebuffer(). - */ - -static void rfbInitServerFormat(rfbScreenInfoPtr rfbScreen, int bitsPerSample) -{ - rfbPixelFormat* format=&rfbScreen->rfbServerFormat; - - format->bitsPerPixel = rfbScreen->bitsPerPixel; - format->depth = rfbScreen->depth; - format->bigEndian = rfbEndianTest?FALSE:TRUE; - format->trueColour = TRUE; - rfbScreen->colourMap.count = 0; - rfbScreen->colourMap.is16 = 0; - rfbScreen->colourMap.data.bytes = NULL; - - if (format->bitsPerPixel == 8) { - format->redMax = 7; - format->greenMax = 7; - format->blueMax = 3; - format->redShift = 0; - format->greenShift = 3; - format->blueShift = 6; - } else { - format->redMax = (1 << bitsPerSample) - 1; - format->greenMax = (1 << bitsPerSample) - 1; - format->blueMax = (1 << bitsPerSample) - 1; - if(rfbEndianTest) { - format->redShift = 0; - format->greenShift = bitsPerSample; - format->blueShift = bitsPerSample * 2; - } else { - if(format->bitsPerPixel==8*3) { - format->redShift = bitsPerSample*2; - format->greenShift = bitsPerSample*1; - format->blueShift = 0; - } else { - format->redShift = bitsPerSample*3; - format->greenShift = bitsPerSample*2; - format->blueShift = bitsPerSample; - } - } - } -} - -rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv, - int width,int height,int bitsPerSample,int samplesPerPixel, - int bytesPerPixel) -{ - rfbScreenInfoPtr rfbScreen=malloc(sizeof(rfbScreenInfo)); - - INIT_MUTEX(logMutex); - - if(width&3) - rfbErr("WARNING: Width (%d) is not a multiple of 4. VncViewer has problems with that.\n",width); - - rfbScreen->autoPort=FALSE; - rfbScreen->rfbClientHead=0; - rfbScreen->rfbPort=5900; - rfbScreen->socketInitDone=FALSE; - - rfbScreen->inetdInitDone = FALSE; - rfbScreen->inetdSock=-1; - - rfbScreen->udpSock=-1; - rfbScreen->udpSockConnected=FALSE; - rfbScreen->udpPort=0; - rfbScreen->udpClient=0; - - rfbScreen->maxFd=0; - rfbScreen->rfbListenSock=-1; - - rfbScreen->httpInitDone=FALSE; - rfbScreen->httpEnableProxyConnect=FALSE; - rfbScreen->httpPort=0; - rfbScreen->httpDir=NULL; - rfbScreen->httpListenSock=-1; - rfbScreen->httpSock=-1; - - rfbScreen->desktopName = "LibVNCServer"; - rfbScreen->rfbAlwaysShared = FALSE; - rfbScreen->rfbNeverShared = FALSE; - rfbScreen->rfbDontDisconnect = FALSE; - rfbScreen->rfbAuthPasswdData = 0; - rfbScreen->rfbAuthPasswdFirstViewOnly = 1; - - rfbScreen->width = width; - rfbScreen->height = height; - rfbScreen->bitsPerPixel = rfbScreen->depth = 8*bytesPerPixel; - - rfbScreen->passwordCheck = defaultPasswordCheck; - - rfbScreen->ignoreSIGPIPE = TRUE; - - /* disable progressive updating per default */ - rfbScreen->progressiveSliceHeight = 0; - - if(!rfbProcessArguments(rfbScreen,argc,argv)) { - free(rfbScreen); - return 0; - } - -#ifdef WIN32 - { - DWORD dummy=255; - GetComputerName(rfbScreen->rfbThisHost,&dummy); - } -#else - gethostname(rfbScreen->rfbThisHost, 255); -#endif - - rfbScreen->paddedWidthInBytes = width*bytesPerPixel; - - /* format */ - - rfbInitServerFormat(rfbScreen, bitsPerSample); - - /* cursor */ - - rfbScreen->cursorIsDrawn = FALSE; - rfbScreen->dontSendFramebufferUpdate = FALSE; - rfbScreen->cursorX=rfbScreen->cursorY=rfbScreen->underCursorBufferLen=0; - rfbScreen->underCursorBuffer=NULL; - rfbScreen->dontConvertRichCursorToXCursor = FALSE; - rfbScreen->cursor = &myCursor; - INIT_MUTEX(rfbScreen->cursorMutex); - - IF_PTHREADS(rfbScreen->backgroundLoop = FALSE); - - rfbScreen->rfbDeferUpdateTime=5; - rfbScreen->maxRectsPerUpdate=50; - - /* proc's and hook's */ - - rfbScreen->kbdAddEvent = defaultKbdAddEvent; - rfbScreen->kbdReleaseAllKeys = doNothingWithClient; - rfbScreen->ptrAddEvent = defaultPtrAddEvent; - rfbScreen->setXCutText = defaultSetXCutText; - rfbScreen->getCursorPtr = defaultGetCursorPtr; - rfbScreen->setTranslateFunction = rfbSetTranslateFunction; - rfbScreen->newClientHook = defaultNewClientHook; - rfbScreen->displayHook = 0; - - /* initialize client list and iterator mutex */ - rfbClientListInit(rfbScreen); - - return(rfbScreen); -} - -/* - * Switch to another framebuffer (maybe of different size and color - * format). Clients supporting NewFBSize pseudo-encoding will change - * their local framebuffer dimensions if necessary. - * NOTE: Rich cursor data should be converted to new pixel format by - * the caller. - */ - -void rfbNewFramebuffer(rfbScreenInfoPtr rfbScreen, char *framebuffer, - int width, int height, - int bitsPerSample, int samplesPerPixel, - int bytesPerPixel) -{ - rfbPixelFormat old_format; - rfbBool format_changed = FALSE; - rfbClientIteratorPtr iterator; - rfbClientPtr cl; - - /* Remove the pointer */ - - rfbUndrawCursor(rfbScreen); - - /* Update information in the rfbScreenInfo structure */ - - old_format = rfbScreen->rfbServerFormat; - - if (width & 3) - rfbErr("WARNING: New width (%d) is not a multiple of 4.\n", width); - - rfbScreen->width = width; - rfbScreen->height = height; - rfbScreen->bitsPerPixel = rfbScreen->depth = 8*bytesPerPixel; - rfbScreen->paddedWidthInBytes = width*bytesPerPixel; - - rfbInitServerFormat(rfbScreen, bitsPerSample); - - if (memcmp(&rfbScreen->rfbServerFormat, &old_format, - sizeof(rfbPixelFormat)) != 0) { - format_changed = TRUE; - } - - rfbScreen->frameBuffer = framebuffer; - - /* Adjust pointer position if necessary */ - - if (rfbScreen->cursorX >= width) - rfbScreen->cursorX = width - 1; - if (rfbScreen->cursorY >= height) - rfbScreen->cursorY = height - 1; - - /* For each client: */ - iterator = rfbGetClientIterator(rfbScreen); - while ((cl = rfbClientIteratorNext(iterator)) != NULL) { - - /* Re-install color translation tables if necessary */ - - if (format_changed) - rfbScreen->setTranslateFunction(cl); - - /* Mark the screen contents as changed, and schedule sending - NewFBSize message if supported by this client. */ - - LOCK(cl->updateMutex); - sraRgnDestroy(cl->modifiedRegion); - cl->modifiedRegion = sraRgnCreateRect(0, 0, width, height); - sraRgnMakeEmpty(cl->copyRegion); - cl->copyDX = 0; - cl->copyDY = 0; - - if (cl->useNewFBSize) - cl->newFBSizePending = TRUE; - - TSIGNAL(cl->updateCond); - UNLOCK(cl->updateMutex); - } - rfbReleaseClientIterator(iterator); -} - -#ifdef LIBVNCSERVER_HAVE_LIBJPEG -extern void TightCleanup(); -#endif - -void rfbScreenCleanup(rfbScreenInfoPtr rfbScreen) -{ - rfbClientIteratorPtr i=rfbGetClientIterator(rfbScreen); - rfbClientPtr cl,cl1=rfbClientIteratorNext(i); - while(cl1) { - cl=rfbClientIteratorNext(i); - rfbClientConnectionGone(cl1); - cl1=cl; - } - rfbReleaseClientIterator(i); - - /* TODO: hang up on all clients and free all reserved memory */ -#define FREE_IF(x) if(rfbScreen->x) free(rfbScreen->x) - FREE_IF(colourMap.data.bytes); - FREE_IF(underCursorBuffer); - TINI_MUTEX(rfbScreen->cursorMutex); - if(rfbScreen->cursor) - rfbFreeCursor(rfbScreen->cursor); - free(rfbScreen); -#ifdef LIBVNCSERVER_HAVE_LIBJPEG - TightCleanup(); -#endif -} - -void rfbInitServer(rfbScreenInfoPtr rfbScreen) -{ -#ifdef WIN32 - WSADATA trash; - int i=WSAStartup(MAKEWORD(2,2),&trash); -#endif - rfbInitSockets(rfbScreen); - httpInitSockets(rfbScreen); - if(rfbScreen->ignoreSIGPIPE) - signal(SIGPIPE,SIG_IGN); -} - -#ifndef LIBVNCSERVER_HAVE_GETTIMEOFDAY -#include -#include -#include - -void gettimeofday(struct timeval* tv,char* dummy) -{ - SYSTEMTIME t; - GetSystemTime(&t); - tv->tv_sec=t.wHour*3600+t.wMinute*60+t.wSecond; - tv->tv_usec=t.wMilliseconds*1000; -} -#endif - -/* defined in rfbserver.c, but kind of "private" */ -rfbClientPtr rfbClientIteratorHead(rfbClientIteratorPtr i); - -void -rfbProcessEvents(rfbScreenInfoPtr rfbScreen,long usec) -{ - rfbClientIteratorPtr i; - rfbClientPtr cl,clPrev; - struct timeval tv; - - if(usec<0) - usec=rfbScreen->rfbDeferUpdateTime*1000; - - rfbCheckFds(rfbScreen,usec); - httpCheckFds(rfbScreen); -#ifdef CORBA - corbaCheckFds(rfbScreen); -#endif - - i = rfbGetClientIterator(rfbScreen); - cl=rfbClientIteratorHead(i); - while(cl) { - if (cl->sock >= 0 && !cl->onHold && FB_UPDATE_PENDING(cl) && - !sraRgnEmpty(cl->requestedRegion)) { - if(rfbScreen->rfbDeferUpdateTime == 0) { - rfbSendFramebufferUpdate(cl,cl->modifiedRegion); - } else if(cl->startDeferring.tv_usec == 0) { - gettimeofday(&cl->startDeferring,NULL); - if(cl->startDeferring.tv_usec == 0) - cl->startDeferring.tv_usec++; - } else { - gettimeofday(&tv,NULL); - if(tv.tv_sec < cl->startDeferring.tv_sec /* at midnight */ - || ((tv.tv_sec-cl->startDeferring.tv_sec)*1000 - +(tv.tv_usec-cl->startDeferring.tv_usec)/1000) - > rfbScreen->rfbDeferUpdateTime) { - cl->startDeferring.tv_usec = 0; - rfbSendFramebufferUpdate(cl,cl->modifiedRegion); - } - } - } - clPrev=cl; - cl=rfbClientIteratorNext(i); - if(clPrev->sock==-1) - rfbClientConnectionGone(clPrev); - } - rfbReleaseClientIterator(i); -} - -void rfbRunEventLoop(rfbScreenInfoPtr rfbScreen, long usec, rfbBool runInBackground) -{ - if(runInBackground) { -#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD - pthread_t listener_thread; - - rfbScreen->backgroundLoop = TRUE; - - pthread_create(&listener_thread, NULL, listenerRun, rfbScreen); - return; -#else - rfbErr("Can't run in background, because I don't have PThreads!\n"); - return; -#endif - } - - if(usec<0) - usec=rfbScreen->rfbDeferUpdateTime*1000; - - while(1) - rfbProcessEvents(rfbScreen,usec); -} diff --git a/rfbregion.c b/rfbregion.c deleted file mode 100755 index a102bc2..0000000 --- a/rfbregion.c +++ /dev/null @@ -1,862 +0,0 @@ -/* -=- sraRegion.c - * Copyright (c) 2001 James "Wez" Weatherall, Johannes E. Schindelin - * - * A general purpose region clipping library - * Only deals with rectangular regions, though. - */ - -#include -#include - -/* -=- Internal Span structure */ - -struct sraRegion; - -typedef struct sraSpan { - struct sraSpan *_next; - struct sraSpan *_prev; - int start; - int end; - struct sraRegion *subspan; -} sraSpan; - -typedef struct sraRegion { - sraSpan front; - sraSpan back; -} sraSpanList; - -/* -=- Span routines */ - -sraSpanList *sraSpanListDup(const sraSpanList *src); -void sraSpanListDestroy(sraSpanList *list); - -sraSpan * -sraSpanCreate(int start, int end, const sraSpanList *subspan) { - sraSpan *item = (sraSpan*)malloc(sizeof(sraSpan)); - item->_next = item->_prev = NULL; - item->start = start; - item->end = end; - item->subspan = sraSpanListDup(subspan); - return item; -} - -sraSpan * -sraSpanDup(const sraSpan *src) { - sraSpan *span; - if (!src) return NULL; - span = sraSpanCreate(src->start, src->end, src->subspan); - return span; -} - -void -sraSpanInsertAfter(sraSpan *newspan, sraSpan *after) { - newspan->_next = after->_next; - newspan->_prev = after; - after->_next->_prev = newspan; - after->_next = newspan; -} - -void -sraSpanInsertBefore(sraSpan *newspan, sraSpan *before) { - newspan->_next = before; - newspan->_prev = before->_prev; - before->_prev->_next = newspan; - before->_prev = newspan; -} - -void -sraSpanRemove(sraSpan *span) { - span->_prev->_next = span->_next; - span->_next->_prev = span->_prev; -} - -void -sraSpanDestroy(sraSpan *span) { - if (span->subspan) sraSpanListDestroy(span->subspan); - free(span); -} - -void -sraSpanCheck(const sraSpan *span, const char *text) { - /* Check the span is valid! */ - if (span->start == span->end) { - printf(text); - printf(":%d-%d\n", span->start, span->end); - } -} - -/* -=- SpanList routines */ - -void sraSpanPrint(const sraSpan *s); - -void -sraSpanListPrint(const sraSpanList *l) { - sraSpan *curr; - if (!l) { - printf("NULL"); - return; - } - curr = l->front._next; - printf("["); - while (curr != &(l->back)) { - sraSpanPrint(curr); - curr = curr->_next; - } - printf("]"); -} - -void -sraSpanPrint(const sraSpan *s) { - printf("(%d-%d)", (s->start), (s->end)); - if (s->subspan) - sraSpanListPrint(s->subspan); -} - -sraSpanList * -sraSpanListCreate() { - sraSpanList *item = (sraSpanList*)malloc(sizeof(sraSpanList)); - item->front._next = &(item->back); - item->front._prev = NULL; - item->back._prev = &(item->front); - item->back._next = NULL; - return item; -} - -sraSpanList * -sraSpanListDup(const sraSpanList *src) { - sraSpanList *newlist; - sraSpan *newspan, *curr; - - if (!src) return NULL; - newlist = sraSpanListCreate(); - curr = src->front._next; - while (curr != &(src->back)) { - newspan = sraSpanDup(curr); - sraSpanInsertBefore(newspan, &(newlist->back)); - curr = curr->_next; - } - - return newlist; -} - -void -sraSpanListDestroy(sraSpanList *list) { - sraSpan *curr, *next; - while (list->front._next != &(list->back)) { - curr = list->front._next; - next = curr->_next; - sraSpanRemove(curr); - sraSpanDestroy(curr); - curr = next; - } - free(list); -} - -void -sraSpanListMakeEmpty(sraSpanList *list) { - sraSpan *curr, *next; - while (list->front._next != &(list->back)) { - curr = list->front._next; - next = curr->_next; - sraSpanRemove(curr); - sraSpanDestroy(curr); - curr = next; - } - list->front._next = &(list->back); - list->front._prev = NULL; - list->back._prev = &(list->front); - list->back._next = NULL; -} - -rfbBool -sraSpanListEqual(const sraSpanList *s1, const sraSpanList *s2) { - sraSpan *sp1, *sp2; - - if (!s1) { - if (!s2) { - return 1; - } else { - printf("sraSpanListEqual:incompatible spans (only one NULL!)\n"); - return FALSE; - } - } - - sp1 = s1->front._next; - sp2 = s2->front._next; - while ((sp1 != &(s1->back)) && - (sp2 != &(s2->back))) { - if ((sp1->start != sp2->start) || - (sp1->end != sp2->end) || - (!sraSpanListEqual(sp1->subspan, sp2->subspan))) { - return 0; - } - sp1 = sp1->_next; - sp2 = sp2->_next; - } - - if ((sp1 == &(s1->back)) && (sp2 == &(s2->back))) { - return 1; - } else { - return 0; - } -} - -rfbBool -sraSpanListEmpty(const sraSpanList *list) { - return (list->front._next == &(list->back)); -} - -unsigned long -sraSpanListCount(const sraSpanList *list) { - sraSpan *curr = list->front._next; - unsigned long count = 0; - while (curr != &(list->back)) { - if (curr->subspan) { - count += sraSpanListCount(curr->subspan); - } else { - count += 1; - } - curr = curr->_next; - } - return count; -} - -void -sraSpanMergePrevious(sraSpan *dest) { - sraSpan *prev = dest->_prev; - - while ((prev->_prev) && - (prev->end == dest->start) && - (sraSpanListEqual(prev->subspan, dest->subspan))) { - /* - printf("merge_prev:"); - sraSpanPrint(prev); - printf(" & "); - sraSpanPrint(dest); - printf("\n"); - */ - dest->start = prev->start; - sraSpanRemove(prev); - sraSpanDestroy(prev); - prev = dest->_prev; - } -} - -void -sraSpanMergeNext(sraSpan *dest) { - sraSpan *next = dest->_next; - while ((next->_next) && - (next->start == dest->end) && - (sraSpanListEqual(next->subspan, dest->subspan))) { -/* - printf("merge_next:"); - sraSpanPrint(dest); - printf(" & "); - sraSpanPrint(next); - printf("\n"); - */ - dest->end = next->end; - sraSpanRemove(next); - sraSpanDestroy(next); - next = dest->_next; - } -} - -void -sraSpanListOr(sraSpanList *dest, const sraSpanList *src) { - sraSpan *d_curr, *s_curr; - int s_start, s_end; - - if (!dest) { - if (!src) { - return; - } else { - printf("sraSpanListOr:incompatible spans (only one NULL!)\n"); - return; - } - } - - d_curr = dest->front._next; - s_curr = src->front._next; - s_start = s_curr->start; - s_end = s_curr->end; - while (s_curr != &(src->back)) { - - /* - If we are at end of destination list OR - If the new span comes before the next destination one */ - if ((d_curr == &(dest->back)) || - (d_curr->start >= s_end)) { - /* - Add the span */ - sraSpanInsertBefore(sraSpanCreate(s_start, s_end, - s_curr->subspan), - d_curr); - if (d_curr != &(dest->back)) - sraSpanMergePrevious(d_curr); - s_curr = s_curr->_next; - s_start = s_curr->start; - s_end = s_curr->end; - } else { - - /* - If the new span overlaps the existing one */ - if ((s_start < d_curr->end) && - (s_end > d_curr->start)) { - - /* - Insert new span before the existing destination one? */ - if (s_start < d_curr->start) { - sraSpanInsertBefore(sraSpanCreate(s_start, - d_curr->start, - s_curr->subspan), - d_curr); - sraSpanMergePrevious(d_curr); - } - - /* Split the existing span if necessary */ - if (s_end < d_curr->end) { - sraSpanInsertAfter(sraSpanCreate(s_end, - d_curr->end, - d_curr->subspan), - d_curr); - d_curr->end = s_end; - } - if (s_start > d_curr->start) { - sraSpanInsertBefore(sraSpanCreate(d_curr->start, - s_start, - d_curr->subspan), - d_curr); - d_curr->start = s_start; - } - - /* Recursively OR subspans */ - sraSpanListOr(d_curr->subspan, s_curr->subspan); - - /* Merge this span with previous or next? */ - if (d_curr->_prev != &(dest->front)) - sraSpanMergePrevious(d_curr); - if (d_curr->_next != &(dest->back)) - sraSpanMergeNext(d_curr); - - /* Move onto the next pair to compare */ - if (s_end > d_curr->end) { - s_start = d_curr->end; - d_curr = d_curr->_next; - } else { - s_curr = s_curr->_next; - s_start = s_curr->start; - s_end = s_curr->end; - } - } else { - /* - No overlap. Move to the next destination span */ - d_curr = d_curr->_next; - } - } - } -} - -rfbBool -sraSpanListAnd(sraSpanList *dest, const sraSpanList *src) { - sraSpan *d_curr, *s_curr, *d_next; - - if (!dest) { - if (!src) { - return 1; - } else { - printf("sraSpanListAnd:incompatible spans (only one NULL!)\n"); - return FALSE; - } - } - - d_curr = dest->front._next; - s_curr = src->front._next; - while ((s_curr != &(src->back)) && (d_curr != &(dest->back))) { - - /* - If we haven't reached a destination span yet then move on */ - if (d_curr->start >= s_curr->end) { - s_curr = s_curr->_next; - continue; - } - - /* - If we are beyond the current destination span then remove it */ - if (d_curr->end <= s_curr->start) { - sraSpan *next = d_curr->_next; - sraSpanRemove(d_curr); - sraSpanDestroy(d_curr); - d_curr = next; - continue; - } - - /* - If we partially overlap a span then split it up or remove bits */ - if (s_curr->start > d_curr->start) { - /* - The top bit of the span does not match */ - d_curr->start = s_curr->start; - } - if (s_curr->end < d_curr->end) { - /* - The end of the span does not match */ - sraSpanInsertAfter(sraSpanCreate(s_curr->end, - d_curr->end, - d_curr->subspan), - d_curr); - d_curr->end = s_curr->end; - } - - /* - Now recursively process the affected span */ - if (!sraSpanListAnd(d_curr->subspan, s_curr->subspan)) { - /* - The destination subspan is now empty, so we should remove it */ - sraSpan *next = d_curr->_next; - sraSpanRemove(d_curr); - sraSpanDestroy(d_curr); - d_curr = next; - } else { - /* Merge this span with previous or next? */ - if (d_curr->_prev != &(dest->front)) - sraSpanMergePrevious(d_curr); - - /* - Move on to the next span */ - d_next = d_curr; - if (s_curr->end >= d_curr->end) { - d_next = d_curr->_next; - } - if (s_curr->end <= d_curr->end) { - s_curr = s_curr->_next; - } - d_curr = d_next; - } - } - - while (d_curr != &(dest->back)) { - sraSpan *next = d_curr->_next; - sraSpanRemove(d_curr); - sraSpanDestroy(d_curr); - d_curr=next; - } - - return !sraSpanListEmpty(dest); -} - -rfbBool -sraSpanListSubtract(sraSpanList *dest, const sraSpanList *src) { - sraSpan *d_curr, *s_curr; - - if (!dest) { - if (!src) { - return 1; - } else { - printf("sraSpanListSubtract:incompatible spans (only one NULL!)\n"); - return FALSE; - } - } - - d_curr = dest->front._next; - s_curr = src->front._next; - while ((s_curr != &(src->back)) && (d_curr != &(dest->back))) { - - /* - If we haven't reached a destination span yet then move on */ - if (d_curr->start >= s_curr->end) { - s_curr = s_curr->_next; - continue; - } - - /* - If we are beyond the current destination span then skip it */ - if (d_curr->end <= s_curr->start) { - d_curr = d_curr->_next; - continue; - } - - /* - If we partially overlap the current span then split it up */ - if (s_curr->start > d_curr->start) { - sraSpanInsertBefore(sraSpanCreate(d_curr->start, - s_curr->start, - d_curr->subspan), - d_curr); - d_curr->start = s_curr->start; - } - if (s_curr->end < d_curr->end) { - sraSpanInsertAfter(sraSpanCreate(s_curr->end, - d_curr->end, - d_curr->subspan), - d_curr); - d_curr->end = s_curr->end; - } - - /* - Now recursively process the affected span */ - if ((!d_curr->subspan) || !sraSpanListSubtract(d_curr->subspan, s_curr->subspan)) { - /* - The destination subspan is now empty, so we should remove it */ - sraSpan *next = d_curr->_next; - sraSpanRemove(d_curr); - sraSpanDestroy(d_curr); - d_curr = next; - } else { - /* Merge this span with previous or next? */ - if (d_curr->_prev != &(dest->front)) - sraSpanMergePrevious(d_curr); - if (d_curr->_next != &(dest->back)) - sraSpanMergeNext(d_curr); - - /* - Move on to the next span */ - if (s_curr->end > d_curr->end) { - d_curr = d_curr->_next; - } else { - s_curr = s_curr->_next; - } - } - } - - return !sraSpanListEmpty(dest); -} - -/* -=- Region routines */ - -sraRegion * -sraRgnCreate() { - return (sraRegion*)sraSpanListCreate(); -} - -sraRegion * -sraRgnCreateRect(int x1, int y1, int x2, int y2) { - sraSpanList *vlist, *hlist; - sraSpan *vspan, *hspan; - - /* - Build the horizontal portion of the span */ - hlist = sraSpanListCreate(); - hspan = sraSpanCreate(x1, x2, NULL); - sraSpanInsertAfter(hspan, &(hlist->front)); - - /* - Build the vertical portion of the span */ - vlist = sraSpanListCreate(); - vspan = sraSpanCreate(y1, y2, hlist); - sraSpanInsertAfter(vspan, &(vlist->front)); - - sraSpanListDestroy(hlist); - - return (sraRegion*)vlist; -} - -sraRegion * -sraRgnCreateRgn(const sraRegion *src) { - return (sraRegion*)sraSpanListDup((sraSpanList*)src); -} - -void -sraRgnDestroy(sraRegion *rgn) { - sraSpanListDestroy((sraSpanList*)rgn); -} - -void -sraRgnMakeEmpty(sraRegion *rgn) { - sraSpanListMakeEmpty((sraSpanList*)rgn); -} - -/* -=- Boolean Region ops */ - -rfbBool -sraRgnAnd(sraRegion *dst, const sraRegion *src) { - return sraSpanListAnd((sraSpanList*)dst, (sraSpanList*)src); -} - -void -sraRgnOr(sraRegion *dst, const sraRegion *src) { - sraSpanListOr((sraSpanList*)dst, (sraSpanList*)src); -} - -rfbBool -sraRgnSubtract(sraRegion *dst, const sraRegion *src) { - return sraSpanListSubtract((sraSpanList*)dst, (sraSpanList*)src); -} - -void -sraRgnOffset(sraRegion *dst, int dx, int dy) { - sraSpan *vcurr, *hcurr; - - vcurr = ((sraSpanList*)dst)->front._next; - while (vcurr != &(((sraSpanList*)dst)->back)) { - vcurr->start += dy; - vcurr->end += dy; - - hcurr = vcurr->subspan->front._next; - while (hcurr != &(vcurr->subspan->back)) { - hcurr->start += dx; - hcurr->end += dx; - hcurr = hcurr->_next; - } - - vcurr = vcurr->_next; - } -} - -sraRegion *sraRgnBBox(const sraRegion *src) { - int xmin=((unsigned int)(int)-1)>>1,ymin=xmin,xmax=1-xmin,ymax=xmax; - sraSpan *vcurr, *hcurr; - - if(!src) - return sraRgnCreate(); - - vcurr = ((sraSpanList*)src)->front._next; - while (vcurr != &(((sraSpanList*)src)->back)) { - if(vcurr->startstart; - if(vcurr->end>ymax) - ymax=vcurr->end; - - hcurr = vcurr->subspan->front._next; - while (hcurr != &(vcurr->subspan->back)) { - if(hcurr->startstart; - if(hcurr->end>xmax) - xmax=hcurr->end; - hcurr = hcurr->_next; - } - - vcurr = vcurr->_next; - } - - if(xmaxback._prev; - vend = &(((sraSpanList*)rgn)->front); - } else { - vcurr = ((sraSpanList*)rgn)->front._next; - vend = &(((sraSpanList*)rgn)->back); - } - - if (vcurr != vend) { - rect->y1 = vcurr->start; - rect->y2 = vcurr->end; - - /* - Pick correct order */ - if (right2left) { - hcurr = vcurr->subspan->back._prev; - hend = &(vcurr->subspan->front); - } else { - hcurr = vcurr->subspan->front._next; - hend = &(vcurr->subspan->back); - } - - if (hcurr != hend) { - rect->x1 = hcurr->start; - rect->x2 = hcurr->end; - - sraSpanRemove(hcurr); - sraSpanDestroy(hcurr); - - if (sraSpanListEmpty(vcurr->subspan)) { - sraSpanRemove(vcurr); - sraSpanDestroy(vcurr); - } - -#if 0 - printf("poprect:(%dx%d)-(%dx%d)\n", - rect->x1, rect->y1, rect->x2, rect->y2); -#endif - return 1; - } - } - - return 0; -} - -unsigned long -sraRgnCountRects(const sraRegion *rgn) { - unsigned long count = sraSpanListCount((sraSpanList*)rgn); - return count; -} - -rfbBool -sraRgnEmpty(const sraRegion *rgn) { - return sraSpanListEmpty((sraSpanList*)rgn); -} - -/* iterator stuff */ -sraRectangleIterator *sraRgnGetIterator(sraRegion *s) -{ - /* these values have to be multiples of 4 */ -#define DEFSIZE 4 -#define DEFSTEP 8 - sraRectangleIterator *i = - (sraRectangleIterator*)malloc(sizeof(sraRectangleIterator)); - if(!i) - return(0); - - /* we have to recurse eventually. So, the first sPtr is the pointer to - the sraSpan in the first level. the second sPtr is the pointer to - the sraRegion.back. The third and fourth sPtr are for the second - recursion level and so on. */ - i->sPtrs = (sraSpan**)malloc(sizeof(sraSpan*)*DEFSIZE); - if(!i->sPtrs) { - free(i); - return(0); - } - i->ptrSize = DEFSIZE; - i->sPtrs[0] = &(s->front); - i->sPtrs[1] = &(s->back); - i->ptrPos = 0; - i->reverseX = 0; - i->reverseY = 0; - return(i); -} - -sraRectangleIterator *sraRgnGetReverseIterator(sraRegion *s,rfbBool reverseX,rfbBool reverseY) -{ - sraRectangleIterator *i = sraRgnGetIterator(s); - if(reverseY) { - i->sPtrs[1] = &(s->front); - i->sPtrs[0] = &(s->back); - } - i->reverseX = reverseX; - i->reverseY = reverseY; - return(i); -} - -rfbBool sraReverse(sraRectangleIterator *i) -{ - return( ((i->ptrPos&2) && i->reverseX) || - (!(i->ptrPos&2) && i->reverseY)); -} - -sraSpan* sraNextSpan(sraRectangleIterator *i) -{ - if(sraReverse(i)) - return(i->sPtrs[i->ptrPos]->_prev); - else - return(i->sPtrs[i->ptrPos]->_next); -} - -rfbBool sraRgnIteratorNext(sraRectangleIterator* i,sraRect* r) -{ - /* is the subspan finished? */ - while(sraNextSpan(i) == i->sPtrs[i->ptrPos+1]) { - i->ptrPos -= 2; - if(i->ptrPos < 0) /* the end */ - return(0); - } - - i->sPtrs[i->ptrPos] = sraNextSpan(i); - - /* is this a new subspan? */ - while(i->sPtrs[i->ptrPos]->subspan) { - if(i->ptrPos+2 > i->ptrSize) { /* array is too small */ - i->ptrSize += DEFSTEP; - i->sPtrs = (sraSpan**)realloc(i->sPtrs, sizeof(sraSpan*)*i->ptrSize); - } - i->ptrPos =+ 2; - if(sraReverse(i)) { - i->sPtrs[i->ptrPos] = i->sPtrs[i->ptrPos-2]->subspan->back._prev; - i->sPtrs[i->ptrPos+1] = &(i->sPtrs[i->ptrPos-2]->subspan->front); - } else { - i->sPtrs[i->ptrPos] = i->sPtrs[i->ptrPos-2]->subspan->front._next; - i->sPtrs[i->ptrPos+1] = &(i->sPtrs[i->ptrPos-2]->subspan->back); - } - } - - if((i->ptrPos%4)!=2) { - rfbErr("sraRgnIteratorNext: offset is wrong (%d%%4!=2)\n",i->ptrPos); - return FALSE; - } - - r->y1 = i->sPtrs[i->ptrPos-2]->start; - r->y2 = i->sPtrs[i->ptrPos-2]->end; - r->x1 = i->sPtrs[i->ptrPos]->start; - r->x2 = i->sPtrs[i->ptrPos]->end; - - return(-1); -} - -void sraRgnReleaseIterator(sraRectangleIterator* i) -{ - free(i->sPtrs); - free(i); -} - -void -sraRgnPrint(const sraRegion *rgn) { - sraSpanListPrint((sraSpanList*)rgn); -} - -rfbBool -sraClipRect(int *x, int *y, int *w, int *h, - int cx, int cy, int cw, int ch) { - if (*x < cx) { - *w -= (cx-*x); - *x = cx; - } - if (*y < cy) { - *h -= (cy-*y); - *y = cy; - } - if (*x+*w > cx+cw) { - *w = (cx+cw)-*x; - } - if (*y+*h > cy+ch) { - *h = (cy+ch)-*y; - } - return (*w>0) && (*h>0); -} - -/* test */ - -#ifdef SRA_TEST -/* pipe the output to sort|uniq -u and you'll get the errors. */ -int main(int argc, char** argv) -{ - sraRegionPtr region, region1, region2; - sraRectangleIterator* i; - sraRect rect; - rfbBool b; - - region = sraRgnCreateRect(10, 10, 600, 300); - region1 = sraRgnCreateRect(40, 50, 350, 200); - region2 = sraRgnCreateRect(0, 0, 20, 40); - - sraRgnPrint(region); - printf("\n[(10-300)[(10-600)]]\n\n"); - - b = sraRgnSubtract(region, region1); - printf("%s ",b?"true":"false"); - sraRgnPrint(region); - printf("\ntrue [(10-50)[(10-600)](50-200)[(10-40)(350-600)](200-300)[(10-600)]]\n\n"); - - sraRgnOr(region, region2); - printf("%ld\n6\n\n", sraRgnCountRects(region)); - - i = sraRgnGetIterator(region); - while(sraRgnIteratorNext(i, &rect)) - printf("%dx%d+%d+%d ", - rect.x2-rect.x1,rect.y2-rect.y1, - rect.x1,rect.y1); - sraRgnReleaseIterator(i); - printf("\n20x10+0+0 600x30+0+10 590x10+10+40 30x150+10+50 250x150+350+50 590x100+10+200 \n\n"); - - i = sraRgnGetReverseIterator(region,1,0); - while(sraRgnIteratorNext(i, &rect)) - printf("%dx%d+%d+%d ", - rect.x2-rect.x1,rect.y2-rect.y1, - rect.x1,rect.y1); - sraRgnReleaseIterator(i); - printf("\n20x10+0+0 600x30+0+10 590x10+10+40 250x150+350+50 30x150+10+50 590x100+10+200 \n\n"); - - i = sraRgnGetReverseIterator(region,1,1); - while(sraRgnIteratorNext(i, &rect)) - printf("%dx%d+%d+%d ", - rect.x2-rect.x1,rect.y2-rect.y1, - rect.x1,rect.y1); - sraRgnReleaseIterator(i); - printf("\n590x100+10+200 250x150+350+50 30x150+10+50 590x10+10+40 600x30+0+10 20x10+0+0 \n\n"); - - sraRgnDestroy(region); - sraRgnDestroy(region1); - sraRgnDestroy(region2); - - return(0); -} -#endif diff --git a/rfbserver.c b/rfbserver.c deleted file mode 100644 index e22283e..0000000 --- a/rfbserver.c +++ /dev/null @@ -1,1808 +0,0 @@ -/* - * rfbserver.c - deal with server-side of the RFB protocol. - */ - -/* - * Copyright (C) 2002 RealVNC Ltd. - * OSXvnc Copyright (C) 2001 Dan McGuirk . - * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. - * 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. - */ - -#include -#include -#include - -#ifdef LIBVNCSERVER_HAVE_FCNTL_H -#include -#endif - -#ifdef WIN32 -#define write(sock,buf,len) send(sock,buf,len,0) -#else -#ifdef LIBVNCSERVER_HAVE_UNISTD_H -#include -#endif -#include -#ifdef LIBVNCSERVER_HAVE_SYS_SOCKET_H -#include -#endif -#ifdef LIBVNCSERVER_HAVE_NETINET_IN_H -#include -#include -#include -#endif -#endif - -#ifdef CORBA -#include -#endif - -#ifdef DEBUGPROTO -#undef DEBUGPROTO -#define DEBUGPROTO(x) x -#else -#define DEBUGPROTO(x) -#endif - -rfbClientPtr pointerClient = NULL; /* Mutex for pointer events */ - -static void rfbProcessClientProtocolVersion(rfbClientPtr cl); -static void rfbProcessClientNormalMessage(rfbClientPtr cl); -static void rfbProcessClientInitMessage(rfbClientPtr cl); - -#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD -void rfbIncrClientRef(rfbClientPtr cl) -{ - LOCK(cl->refCountMutex); - cl->refCount++; - UNLOCK(cl->refCountMutex); -} - -void rfbDecrClientRef(rfbClientPtr cl) -{ - LOCK(cl->refCountMutex); - cl->refCount--; - if(cl->refCount<=0) /* just to be sure also < 0 */ - TSIGNAL(cl->deleteCond); - UNLOCK(cl->refCountMutex); -} -#else -void rfbIncrClientRef(rfbClientPtr cl) {} -void rfbDecrClientRef(rfbClientPtr cl) {} -#endif - -#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD -MUTEX(rfbClientListMutex); -#endif - -struct rfbClientIterator { - rfbClientPtr next; - rfbScreenInfoPtr screen; -}; - -void -rfbClientListInit(rfbScreenInfoPtr rfbScreen) -{ - if(sizeof(rfbBool)!=1) { - /* a sanity check */ - fprintf(stderr,"rfbBool's size is not 1 (%d)!\n",sizeof(rfbBool)); - /* we cannot continue, because rfbBool is supposed to be char everywhere */ - exit(1); - } - rfbScreen->rfbClientHead = NULL; - INIT_MUTEX(rfbClientListMutex); -} - -rfbClientIteratorPtr -rfbGetClientIterator(rfbScreenInfoPtr rfbScreen) -{ - rfbClientIteratorPtr i = - (rfbClientIteratorPtr)malloc(sizeof(struct rfbClientIterator)); - i->next = 0; - i->screen = rfbScreen; - return i; -} - -rfbClientPtr -rfbClientIteratorHead(rfbClientIteratorPtr i) -{ -#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD - if(i->next != 0) { - rfbDecrClientRef(i->next); - rfbIncrClientRef(i->screen->rfbClientHead); - } -#endif - LOCK(rfbClientListMutex); - i->next = i->screen->rfbClientHead; - UNLOCK(rfbClientListMutex); - return i->next; -} - -rfbClientPtr -rfbClientIteratorNext(rfbClientIteratorPtr i) -{ - if(i->next == 0) { - LOCK(rfbClientListMutex); - i->next = i->screen->rfbClientHead; - UNLOCK(rfbClientListMutex); - } else { - IF_PTHREADS(rfbClientPtr cl = i->next); - i->next = i->next->next; - IF_PTHREADS(rfbDecrClientRef(cl)); - } - -#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD - while(i->next && i->next->sock<0) - i->next = i->next->next; - if(i->next) - rfbIncrClientRef(i->next); -#endif - - return i->next; -} - -void -rfbReleaseClientIterator(rfbClientIteratorPtr iterator) -{ - IF_PTHREADS(if(iterator->next) rfbDecrClientRef(iterator->next)); - free(iterator); -} - - -/* - * rfbNewClientConnection is called from sockets.c when a new connection - * comes in. - */ - -void -rfbNewClientConnection(rfbScreen,sock) - rfbScreenInfoPtr rfbScreen; - int sock; -{ - rfbClientPtr cl; - - cl = rfbNewClient(rfbScreen,sock); -#ifdef CORBA - if(cl!=NULL) - newConnection(cl, (KEYBOARD_DEVICE|POINTER_DEVICE),1,1,1); -#endif -} - - -/* - * rfbReverseConnection is called by the CORBA stuff to make an outward - * connection to a "listening" RFB client. - */ - -rfbClientPtr -rfbReverseConnection(rfbScreen,host, port) - rfbScreenInfoPtr rfbScreen; - char *host; - int port; -{ - int sock; - rfbClientPtr cl; - - if ((sock = rfbConnect(rfbScreen, host, port)) < 0) - return (rfbClientPtr)NULL; - - cl = rfbNewClient(rfbScreen, sock); - - if (cl) { - cl->reverseConnection = TRUE; - } - - return cl; -} - - -/* - * rfbNewClient is called when a new connection has been made by whatever - * means. - */ - -rfbClientPtr -rfbNewTCPOrUDPClient(rfbScreen,sock,isUDP) - rfbScreenInfoPtr rfbScreen; - int sock; - rfbBool isUDP; -{ - rfbProtocolVersionMsg pv; - rfbClientIteratorPtr iterator; - rfbClientPtr cl,cl_; - struct sockaddr_in addr; - size_t addrlen = sizeof(struct sockaddr_in); - - cl = (rfbClientPtr)calloc(sizeof(rfbClientRec),1); - - cl->screen = rfbScreen; - cl->sock = sock; - cl->viewOnly = FALSE; - - rfbResetStats(cl); - - if(isUDP) { - rfbLog(" accepted UDP client\n"); - } else { - int one=1; - - getpeername(sock, (struct sockaddr *)&addr, &addrlen); - cl->host = strdup(inet_ntoa(addr.sin_addr)); - - rfbLog(" other clients:\n"); - iterator = rfbGetClientIterator(rfbScreen); - while ((cl_ = rfbClientIteratorNext(iterator)) != NULL) { - rfbLog(" %s\n",cl_->host); - } - rfbReleaseClientIterator(iterator); - -#ifndef WIN32 - if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { - rfbLogPerror("fcntl failed"); - close(sock); - return NULL; - } -#endif - - if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, - (char *)&one, sizeof(one)) < 0) { - rfbLogPerror("setsockopt failed"); - close(sock); - return NULL; - } - - FD_SET(sock,&(rfbScreen->allFds)); - rfbScreen->maxFd = max(sock,rfbScreen->maxFd); - - INIT_MUTEX(cl->outputMutex); - INIT_MUTEX(cl->refCountMutex); - INIT_COND(cl->deleteCond); - - cl->state = RFB_PROTOCOL_VERSION; - - cl->reverseConnection = FALSE; - cl->readyForSetColourMapEntries = FALSE; - cl->useCopyRect = FALSE; - cl->preferredEncoding = rfbEncodingRaw; - cl->correMaxWidth = 48; - cl->correMaxHeight = 48; -#ifdef LIBVNCSERVER_HAVE_LIBZ - cl->zrleData = 0; -#endif - - cl->copyRegion = sraRgnCreate(); - cl->copyDX = 0; - cl->copyDY = 0; - - cl->modifiedRegion = - sraRgnCreateRect(0,0,rfbScreen->width,rfbScreen->height); - - INIT_MUTEX(cl->updateMutex); - INIT_COND(cl->updateCond); - - cl->requestedRegion = sraRgnCreate(); - - cl->format = cl->screen->rfbServerFormat; - cl->translateFn = rfbTranslateNone; - cl->translateLookupTable = NULL; - - LOCK(rfbClientListMutex); - - IF_PTHREADS(cl->refCount = 0); - cl->next = rfbScreen->rfbClientHead; - cl->prev = NULL; - if (rfbScreen->rfbClientHead) - rfbScreen->rfbClientHead->prev = cl; - - rfbScreen->rfbClientHead = cl; - UNLOCK(rfbClientListMutex); - -#ifdef LIBVNCSERVER_HAVE_LIBJPEG - cl->tightCompressLevel = TIGHT_DEFAULT_COMPRESSION; - cl->tightQualityLevel = -1; - { - int i; - for (i = 0; i < 4; i++) - cl->zsActive[i] = FALSE; - } -#endif - - cl->enableCursorShapeUpdates = FALSE; - cl->enableCursorPosUpdates = FALSE; - cl->useRichCursorEncoding = FALSE; - cl->enableLastRectEncoding = FALSE; - cl->useNewFBSize = FALSE; - -#ifdef LIBVNCSERVER_HAVE_LIBZ - cl->compStreamInited = FALSE; - cl->compStream.total_in = 0; - cl->compStream.total_out = 0; - cl->compStream.zalloc = Z_NULL; - cl->compStream.zfree = Z_NULL; - cl->compStream.opaque = Z_NULL; - - cl->zlibCompressLevel = 5; -#endif - - cl->progressiveSliceY = 0; - - sprintf(pv,rfbProtocolVersionFormat,rfbProtocolMajorVersion, - rfbProtocolMinorVersion); - - if (WriteExact(cl, pv, sz_rfbProtocolVersionMsg) < 0) { - rfbLogPerror("rfbNewClient: write"); - rfbCloseClient(cl); - /* TODO: memory leak here (cl is never freed) - * can rfbClientConnectionGone called at this time? - * tim@tjansen.de - */ - return NULL; - } - } - - cl->clientData = NULL; - cl->clientGoneHook = doNothingWithClient; - switch (cl->screen->newClientHook(cl)) { - case RFB_CLIENT_ON_HOLD: - cl->onHold = TRUE; - break; - case RFB_CLIENT_ACCEPT: - cl->onHold = FALSE; - break; - case RFB_CLIENT_REFUSE: - rfbCloseClient(cl); - rfbClientConnectionGone(cl); - cl = NULL; - break; - } - return cl; -} - -rfbClientPtr -rfbNewClient(rfbScreen,sock) - rfbScreenInfoPtr rfbScreen; - int sock; -{ - return(rfbNewTCPOrUDPClient(rfbScreen,sock,FALSE)); -} - -rfbClientPtr -rfbNewUDPClient(rfbScreen) - rfbScreenInfoPtr rfbScreen; -{ - return((rfbScreen->udpClient= - rfbNewTCPOrUDPClient(rfbScreen,rfbScreen->udpSock,TRUE))); -} - -/* - * rfbClientConnectionGone is called from sockets.c just after a connection - * has gone away. - */ - -void -rfbClientConnectionGone(cl) - rfbClientPtr cl; -{ - int i; - - LOCK(rfbClientListMutex); - - if (cl->prev) - cl->prev->next = cl->next; - else - cl->screen->rfbClientHead = cl->next; - if (cl->next) - cl->next->prev = cl->prev; - -#ifdef LIBVNCSERVER_HAVE_LIBZ - FreeZrleData(cl); -#endif - -#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD - if(cl->screen->backgroundLoop != FALSE) - do { - LOCK(cl->refCountMutex); - i=cl->refCount; - UNLOCK(cl->refCountMutex); - if(i>0) - WAIT(cl->deleteCond,cl->refCountMutex); - } while(i>0); -#endif - - if(cl->sock>=0) - FD_CLR(cl->sock,&(cl->screen->allFds)); - - cl->clientGoneHook(cl); - - rfbLog("Client %s gone\n",cl->host); - free(cl->host); - -#ifdef LIBVNCSERVER_HAVE_LIBZ - /* Release the compression state structures if any. */ - if ( cl->compStreamInited ) { - deflateEnd( &(cl->compStream) ); - } - -#ifdef LIBVNCSERVER_HAVE_LIBJPEG - for (i = 0; i < 4; i++) { - if (cl->zsActive[i]) - deflateEnd(&cl->zsStruct[i]); - } -#endif -#endif - - if (pointerClient == cl) - pointerClient = NULL; - - sraRgnDestroy(cl->modifiedRegion); - sraRgnDestroy(cl->requestedRegion); - sraRgnDestroy(cl->copyRegion); - - UNLOCK(rfbClientListMutex); - - if (cl->translateLookupTable) free(cl->translateLookupTable); - - TINI_COND(cl->updateCond); - TINI_MUTEX(cl->updateMutex); - - LOCK(cl->outputMutex); - TINI_MUTEX(cl->outputMutex); - -#ifdef CORBA - destroyConnection(cl); -#endif - - rfbPrintStats(cl); - - free(cl); -} - - -/* - * rfbProcessClientMessage is called when there is data to read from a client. - */ - -void -rfbProcessClientMessage(cl) - rfbClientPtr cl; -{ - switch (cl->state) { - case RFB_PROTOCOL_VERSION: - rfbProcessClientProtocolVersion(cl); - return; - case RFB_AUTHENTICATION: - rfbAuthProcessClientMessage(cl); - return; - case RFB_INITIALISATION: - rfbProcessClientInitMessage(cl); - return; - default: - rfbProcessClientNormalMessage(cl); - return; - } -} - - -/* - * rfbProcessClientProtocolVersion is called when the client sends its - * protocol version. - */ - -static void -rfbProcessClientProtocolVersion(cl) - rfbClientPtr cl; -{ - rfbProtocolVersionMsg pv; - int n, major_, minor_; - char failureReason[256]; - - if ((n = ReadExact(cl, pv, sz_rfbProtocolVersionMsg)) <= 0) { - if (n == 0) - rfbLog("rfbProcessClientProtocolVersion: client gone\n"); - else - rfbLogPerror("rfbProcessClientProtocolVersion: read"); - rfbCloseClient(cl); - return; - } - - pv[sz_rfbProtocolVersionMsg] = 0; - if (sscanf(pv,rfbProtocolVersionFormat,&major_,&minor_) != 2) { - char name[1024]; - if(sscanf(pv,"RFB %03d.%03d %1024s\n",&major_,&minor_,name) != 3) { - rfbErr("rfbProcessClientProtocolVersion: not a valid RFB client\n"); - rfbCloseClient(cl); - return; - } - free(cl->host); - cl->host=strdup(name); - } - rfbLog("Protocol version %d.%d\n", major_, minor_); - - if (major_ != rfbProtocolMajorVersion) { - /* Major version mismatch - send a ConnFailed message */ - - rfbErr("Major version mismatch\n"); - sprintf(failureReason, - "RFB protocol version mismatch - server %d.%d, client %d.%d", - rfbProtocolMajorVersion,rfbProtocolMinorVersion,major_,minor_); - rfbClientConnFailed(cl, failureReason); - return; - } - - if (minor_ != rfbProtocolMinorVersion) { - /* Minor version mismatch - warn but try to continue */ - rfbLog("Ignoring minor version mismatch\n"); - } - - rfbAuthNewClient(cl); -} - - -/* - * rfbClientConnFailed is called when a client connection has failed either - * because it talks the wrong protocol or it has failed authentication. - */ - -void -rfbClientConnFailed(cl, reason) - rfbClientPtr cl; - char *reason; -{ - char *buf; - int len = strlen(reason); - - buf = (char *)malloc(8 + len); - ((uint32_t *)buf)[0] = Swap32IfLE(rfbConnFailed); - ((uint32_t *)buf)[1] = Swap32IfLE(len); - memcpy(buf + 8, reason, len); - - if (WriteExact(cl, buf, 8 + len) < 0) - rfbLogPerror("rfbClientConnFailed: write"); - free(buf); - rfbCloseClient(cl); -} - - -/* - * rfbProcessClientInitMessage is called when the client sends its - * initialisation message. - */ - -static void -rfbProcessClientInitMessage(cl) - rfbClientPtr cl; -{ - rfbClientInitMsg ci; - char buf[256]; - rfbServerInitMsg *si = (rfbServerInitMsg *)buf; - int len, n; - rfbClientIteratorPtr iterator; - rfbClientPtr otherCl; - - if ((n = ReadExact(cl, (char *)&ci,sz_rfbClientInitMsg)) <= 0) { - if (n == 0) - rfbLog("rfbProcessClientInitMessage: client gone\n"); - else - rfbLogPerror("rfbProcessClientInitMessage: read"); - rfbCloseClient(cl); - return; - } - - si->framebufferWidth = Swap16IfLE(cl->screen->width); - si->framebufferHeight = Swap16IfLE(cl->screen->height); - si->format = cl->screen->rfbServerFormat; - si->format.redMax = Swap16IfLE(si->format.redMax); - si->format.greenMax = Swap16IfLE(si->format.greenMax); - si->format.blueMax = Swap16IfLE(si->format.blueMax); - - if (strlen(cl->screen->desktopName) > 128) /* sanity check on desktop name len */ - ((char*)cl->screen->desktopName)[128] = 0; - - strcpy(buf + sz_rfbServerInitMsg, cl->screen->desktopName); - len = strlen(buf + sz_rfbServerInitMsg); - si->nameLength = Swap32IfLE(len); - - if (WriteExact(cl, buf, sz_rfbServerInitMsg + len) < 0) { - rfbLogPerror("rfbProcessClientInitMessage: write"); - rfbCloseClient(cl); - return; - } - - cl->state = RFB_NORMAL; - - if (!cl->reverseConnection && - (cl->screen->rfbNeverShared || (!cl->screen->rfbAlwaysShared && !ci.shared))) { - - if (cl->screen->rfbDontDisconnect) { - iterator = rfbGetClientIterator(cl->screen); - while ((otherCl = rfbClientIteratorNext(iterator)) != NULL) { - if ((otherCl != cl) && (otherCl->state == RFB_NORMAL)) { - rfbLog("-dontdisconnect: Not shared & existing client\n"); - rfbLog(" refusing new client %s\n", cl->host); - rfbCloseClient(cl); - rfbReleaseClientIterator(iterator); - return; - } - } - rfbReleaseClientIterator(iterator); - } else { - iterator = rfbGetClientIterator(cl->screen); - while ((otherCl = rfbClientIteratorNext(iterator)) != NULL) { - if ((otherCl != cl) && (otherCl->state == RFB_NORMAL)) { - rfbLog("Not shared - closing connection to client %s\n", - otherCl->host); - rfbCloseClient(otherCl); - } - } - rfbReleaseClientIterator(iterator); - } - } -} - -static rfbBool rectSwapIfLEAndClip(uint16_t* x,uint16_t* y,uint16_t* w,uint16_t* h, - rfbScreenInfoPtr screen) -{ - *x=Swap16IfLE(*x); - *y=Swap16IfLE(*y); - *w=Swap16IfLE(*w); - *h=Swap16IfLE(*h); - if(*w>screen->width-*x) - *w=screen->width-*x; - /* possible underflow */ - if(*w>screen->width-*x) - return FALSE; - if(*h>screen->height-*y) - *h=screen->height-*y; - if(*h>screen->height-*y) - return FALSE; - - return TRUE; -} - -/* - * rfbProcessClientNormalMessage is called when the client has sent a normal - * protocol message. - */ - -static void -rfbProcessClientNormalMessage(cl) - rfbClientPtr cl; -{ - int n=0; - rfbClientToServerMsg msg; - char *str; - - if ((n = ReadExact(cl, (char *)&msg, 1)) <= 0) { - if (n != 0) - rfbLogPerror("rfbProcessClientNormalMessage: read"); - rfbCloseClient(cl); - return; - } - - switch (msg.type) { - - case rfbSetPixelFormat: - - if ((n = ReadExact(cl, ((char *)&msg) + 1, - sz_rfbSetPixelFormatMsg - 1)) <= 0) { - if (n != 0) - rfbLogPerror("rfbProcessClientNormalMessage: read"); - rfbCloseClient(cl); - return; - } - - cl->format.bitsPerPixel = msg.spf.format.bitsPerPixel; - cl->format.depth = msg.spf.format.depth; - cl->format.bigEndian = (msg.spf.format.bigEndian ? TRUE : FALSE); - cl->format.trueColour = (msg.spf.format.trueColour ? TRUE : FALSE); - cl->format.redMax = Swap16IfLE(msg.spf.format.redMax); - cl->format.greenMax = Swap16IfLE(msg.spf.format.greenMax); - cl->format.blueMax = Swap16IfLE(msg.spf.format.blueMax); - cl->format.redShift = msg.spf.format.redShift; - cl->format.greenShift = msg.spf.format.greenShift; - cl->format.blueShift = msg.spf.format.blueShift; - - cl->readyForSetColourMapEntries = TRUE; - cl->screen->setTranslateFunction(cl); - - return; - - - case rfbFixColourMapEntries: - if ((n = ReadExact(cl, ((char *)&msg) + 1, - sz_rfbFixColourMapEntriesMsg - 1)) <= 0) { - if (n != 0) - rfbLogPerror("rfbProcessClientNormalMessage: read"); - rfbCloseClient(cl); - return; - } - rfbLog("rfbProcessClientNormalMessage: %s", - "FixColourMapEntries unsupported\n"); - rfbCloseClient(cl); - return; - - - case rfbSetEncodings: - { - int i; - uint32_t enc; - - if ((n = ReadExact(cl, ((char *)&msg) + 1, - sz_rfbSetEncodingsMsg - 1)) <= 0) { - if (n != 0) - rfbLogPerror("rfbProcessClientNormalMessage: read"); - rfbCloseClient(cl); - return; - } - - msg.se.nEncodings = Swap16IfLE(msg.se.nEncodings); - - cl->preferredEncoding = -1; - cl->useCopyRect = FALSE; - cl->enableCursorShapeUpdates = FALSE; - cl->enableCursorPosUpdates = FALSE; - cl->enableLastRectEncoding = FALSE; - cl->useNewFBSize = FALSE; - - for (i = 0; i < msg.se.nEncodings; i++) { - if ((n = ReadExact(cl, (char *)&enc, 4)) <= 0) { - if (n != 0) - rfbLogPerror("rfbProcessClientNormalMessage: read"); - rfbCloseClient(cl); - return; - } - enc = Swap32IfLE(enc); - - switch (enc) { - - case rfbEncodingCopyRect: - cl->useCopyRect = TRUE; - break; - case rfbEncodingRaw: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using raw encoding for client %s\n", - cl->host); - } - break; - case rfbEncodingRRE: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using rre encoding for client %s\n", - cl->host); - } - break; - case rfbEncodingCoRRE: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using CoRRE encoding for client %s\n", - cl->host); - } - break; - case rfbEncodingHextile: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using hextile encoding for client %s\n", - cl->host); - } - break; -#ifdef LIBVNCSERVER_HAVE_LIBZ - case rfbEncodingZlib: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using zlib encoding for client %s\n", - cl->host); - } - break; -#ifdef LIBVNCSERVER_HAVE_LIBJPEG - case rfbEncodingTight: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using tight encoding for client %s\n", - cl->host); - } - break; -#endif -#endif - case rfbEncodingXCursor: - if(!cl->screen->dontConvertRichCursorToXCursor) { - rfbLog("Enabling X-style cursor updates for client %s\n", - cl->host); - cl->enableCursorShapeUpdates = TRUE; - cl->cursorWasChanged = TRUE; - } - break; - case rfbEncodingRichCursor: - rfbLog("Enabling full-color cursor updates for client %s\n", - cl->host); - cl->enableCursorShapeUpdates = TRUE; - cl->useRichCursorEncoding = TRUE; - cl->cursorWasChanged = TRUE; - break; - case rfbEncodingPointerPos: - if (!cl->enableCursorPosUpdates) { - rfbLog("Enabling cursor position updates for client %s\n", - cl->host); - cl->enableCursorPosUpdates = TRUE; - cl->cursorWasMoved = TRUE; - } - break; - case rfbEncodingLastRect: - if (!cl->enableLastRectEncoding) { - rfbLog("Enabling LastRect protocol extension for client " - "%s\n", cl->host); - cl->enableLastRectEncoding = TRUE; - } - break; - case rfbEncodingNewFBSize: - if (!cl->useNewFBSize) { - rfbLog("Enabling NewFBSize protocol extension for client " - "%s\n", cl->host); - cl->useNewFBSize = TRUE; - } - break; -#ifdef LIBVNCSERVER_BACKCHANNEL - case rfbEncodingBackChannel: - if (!cl->enableBackChannel) { - rfbLog("Enabling BackChannel protocol extension for " - "client %s\n", cl->host); - cl->enableBackChannel = TRUE; - } - break; -#endif -#ifdef LIBVNCSERVER_HAVE_LIBZ - case rfbEncodingZRLE: - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = enc; - rfbLog("Using ZRLE encoding for client %s\n", - cl->host); - } - break; -#endif - default: -#ifdef LIBVNCSERVER_HAVE_LIBZ - if ( enc >= (uint32_t)rfbEncodingCompressLevel0 && - enc <= (uint32_t)rfbEncodingCompressLevel9 ) { - cl->zlibCompressLevel = enc & 0x0F; -#ifdef LIBVNCSERVER_HAVE_LIBJPEG - cl->tightCompressLevel = enc & 0x0F; - rfbLog("Using compression level %d for client %s\n", - cl->tightCompressLevel, cl->host); - } else if ( enc >= (uint32_t)rfbEncodingQualityLevel0 && - enc <= (uint32_t)rfbEncodingQualityLevel9 ) { - cl->tightQualityLevel = enc & 0x0F; - rfbLog("Using image quality level %d for client %s\n", - cl->tightQualityLevel, cl->host); -#endif - } else -#endif - rfbLog("rfbProcessClientNormalMessage: ignoring unknown " - "encoding type %d\n", (int)enc); - } - } - - if (cl->preferredEncoding == -1) { - cl->preferredEncoding = rfbEncodingRaw; - } - - if (cl->enableCursorPosUpdates && !cl->enableCursorShapeUpdates) { - rfbLog("Disabling cursor position updates for client %s\n", - cl->host); - cl->enableCursorPosUpdates = FALSE; - } - - return; - } - - - case rfbFramebufferUpdateRequest: - { - sraRegionPtr tmpRegion; - - if ((n = ReadExact(cl, ((char *)&msg) + 1, - sz_rfbFramebufferUpdateRequestMsg-1)) <= 0) { - if (n != 0) - rfbLogPerror("rfbProcessClientNormalMessage: read"); - rfbCloseClient(cl); - return; - } - - if(!rectSwapIfLEAndClip(&msg.fur.x,&msg.fur.y,&msg.fur.w,&msg.fur.h, - cl->screen)) - return; - - tmpRegion = - sraRgnCreateRect(msg.fur.x, - msg.fur.y, - msg.fur.x+msg.fur.w, - msg.fur.y+msg.fur.h); - - LOCK(cl->updateMutex); - sraRgnOr(cl->requestedRegion,tmpRegion); - - if (!cl->readyForSetColourMapEntries) { - /* client hasn't sent a SetPixelFormat so is using server's */ - cl->readyForSetColourMapEntries = TRUE; - if (!cl->format.trueColour) { - if (!rfbSetClientColourMap(cl, 0, 0)) { - sraRgnDestroy(tmpRegion); - UNLOCK(cl->updateMutex); - return; - } - } - } - - if (!msg.fur.incremental) { - sraRgnOr(cl->modifiedRegion,tmpRegion); - sraRgnSubtract(cl->copyRegion,tmpRegion); - } - TSIGNAL(cl->updateCond); - UNLOCK(cl->updateMutex); - - sraRgnDestroy(tmpRegion); - - return; - } - - case rfbKeyEvent: - - cl->rfbKeyEventsRcvd++; - - if ((n = ReadExact(cl, ((char *)&msg) + 1, - sz_rfbKeyEventMsg - 1)) <= 0) { - if (n != 0) - rfbLogPerror("rfbProcessClientNormalMessage: read"); - rfbCloseClient(cl); - return; - } - - if(!cl->viewOnly) { - cl->screen->kbdAddEvent(msg.ke.down, (rfbKeySym)Swap32IfLE(msg.ke.key), cl); - } - - return; - - - case rfbPointerEvent: - - cl->rfbPointerEventsRcvd++; - - if ((n = ReadExact(cl, ((char *)&msg) + 1, - sz_rfbPointerEventMsg - 1)) <= 0) { - if (n != 0) - rfbLogPerror("rfbProcessClientNormalMessage: read"); - rfbCloseClient(cl); - return; - } - - if (pointerClient && (pointerClient != cl)) - return; - - if (msg.pe.buttonMask == 0) - pointerClient = NULL; - else - pointerClient = cl; - - if(!cl->viewOnly) { - cl->screen->ptrAddEvent(msg.pe.buttonMask, - Swap16IfLE(msg.pe.x), Swap16IfLE(msg.pe.y), cl); - } - - return; - - - case rfbClientCutText: - - if ((n = ReadExact(cl, ((char *)&msg) + 1, - sz_rfbClientCutTextMsg - 1)) <= 0) { - if (n != 0) - rfbLogPerror("rfbProcessClientNormalMessage: read"); - rfbCloseClient(cl); - return; - } - - if(!cl->viewOnly) { - msg.cct.length = Swap32IfLE(msg.cct.length); - - str = (char *)malloc(msg.cct.length); - - if ((n = ReadExact(cl, str, msg.cct.length)) <= 0) { - if (n != 0) - rfbLogPerror("rfbProcessClientNormalMessage: read"); - free(str); - rfbCloseClient(cl); - return; - } - - cl->screen->setXCutText(str, msg.cct.length, cl); - free(str); - } - - return; - - - default: - - rfbLog("rfbProcessClientNormalMessage: unknown message type %d\n", - msg.type); - rfbLog(" ... closing connection\n"); - rfbCloseClient(cl); - return; - } -} - - - -/* - * rfbSendFramebufferUpdate - send the currently pending framebuffer update to - * the RFB client. - * givenUpdateRegion is not changed. - */ - -rfbBool -rfbSendFramebufferUpdate(cl, givenUpdateRegion) - rfbClientPtr cl; - sraRegionPtr givenUpdateRegion; -{ - sraRectangleIterator* i; - sraRect rect; - int nUpdateRegionRects; - rfbFramebufferUpdateMsg *fu = (rfbFramebufferUpdateMsg *)cl->updateBuf; - sraRegionPtr updateRegion,updateCopyRegion,tmpRegion; - int dx, dy; - rfbBool sendCursorShape = FALSE; - rfbBool sendCursorPos = FALSE; - - if(cl->screen->displayHook) - cl->screen->displayHook(cl); - - /* - * If framebuffer size was changed and the client supports NewFBSize - * encoding, just send NewFBSize marker and return. - */ - - if (cl->useNewFBSize && cl->newFBSizePending) { - LOCK(cl->updateMutex); - cl->newFBSizePending = FALSE; - UNLOCK(cl->updateMutex); - cl->rfbFramebufferUpdateMessagesSent++; - fu->type = rfbFramebufferUpdate; - fu->nRects = Swap16IfLE(1); - cl->ublen = sz_rfbFramebufferUpdateMsg; - if (!rfbSendNewFBSize(cl, cl->screen->width, cl->screen->height)) { - return FALSE; - } - return rfbSendUpdateBuf(cl); - } - - /* - * If this client understands cursor shape updates, cursor should be - * removed from the framebuffer. Otherwise, make sure it's put up. - */ - - if (cl->enableCursorShapeUpdates) { - if (cl->screen->cursorIsDrawn) { - rfbUndrawCursor(cl->screen); - } - if (!cl->screen->cursorIsDrawn && cl->cursorWasChanged && - cl->readyForSetColourMapEntries) - sendCursorShape = TRUE; - } else { - if (!cl->screen->cursorIsDrawn) { - rfbDrawCursor(cl->screen); - } - } - - /* - * Do we plan to send cursor position update? - */ - - if (cl->enableCursorPosUpdates && cl->cursorWasMoved) - sendCursorPos = TRUE; - - LOCK(cl->updateMutex); - - /* - * The modifiedRegion may overlap the destination copyRegion. We remove - * any overlapping bits from the copyRegion (since they'd only be - * overwritten anyway). - */ - - sraRgnSubtract(cl->copyRegion,cl->modifiedRegion); - - /* - * The client is interested in the region requestedRegion. The region - * which should be updated now is the intersection of requestedRegion - * and the union of modifiedRegion and copyRegion. If it's empty then - * no update is needed. - */ - - updateRegion = sraRgnCreateRgn(givenUpdateRegion); - if(cl->screen->progressiveSliceHeight>0) { - int height=cl->screen->progressiveSliceHeight, - y=cl->progressiveSliceY; - sraRegionPtr bbox=sraRgnBBox(updateRegion); - sraRect rect; - if(sraRgnPopRect(bbox,&rect,0)) { - sraRegionPtr slice; - if(y=rect.y2) - y=rect.y1; - slice=sraRgnCreateRect(0,y,cl->screen->width,y+height); - sraRgnAnd(updateRegion,slice); - sraRgnDestroy(slice); - } - sraRgnDestroy(bbox); - y+=height; - if(y>=cl->screen->height) - y=0; - cl->progressiveSliceY=y; - } - - sraRgnOr(updateRegion,cl->copyRegion); - if(!sraRgnAnd(updateRegion,cl->requestedRegion) && - !sendCursorShape && !sendCursorPos) { - sraRgnDestroy(updateRegion); - UNLOCK(cl->updateMutex); - return TRUE; - } - - /* - * We assume that the client doesn't have any pixel data outside the - * requestedRegion. In other words, both the source and destination of a - * copy must lie within requestedRegion. So the region we can send as a - * copy is the intersection of the copyRegion with both the requestedRegion - * and the requestedRegion translated by the amount of the copy. We set - * updateCopyRegion to this. - */ - - updateCopyRegion = sraRgnCreateRgn(cl->copyRegion); - sraRgnAnd(updateCopyRegion,cl->requestedRegion); - tmpRegion = sraRgnCreateRgn(cl->requestedRegion); - sraRgnOffset(tmpRegion,cl->copyDX,cl->copyDY); - sraRgnAnd(updateCopyRegion,tmpRegion); - sraRgnDestroy(tmpRegion); - dx = cl->copyDX; - dy = cl->copyDY; - - /* - * Next we remove updateCopyRegion from updateRegion so that updateRegion - * is the part of this update which is sent as ordinary pixel data (i.e not - * a copy). - */ - - sraRgnSubtract(updateRegion,updateCopyRegion); - - /* - * Finally we leave modifiedRegion to be the remainder (if any) of parts of - * the screen which are modified but outside the requestedRegion. We also - * empty both the requestedRegion and the copyRegion - note that we never - * carry over a copyRegion for a future update. - */ - - - sraRgnOr(cl->modifiedRegion,cl->copyRegion); - sraRgnSubtract(cl->modifiedRegion,updateRegion); - sraRgnSubtract(cl->modifiedRegion,updateCopyRegion); - - sraRgnMakeEmpty(cl->requestedRegion); - sraRgnMakeEmpty(cl->copyRegion); - cl->copyDX = 0; - cl->copyDY = 0; - - UNLOCK(cl->updateMutex); - - /* - * Now send the update. - */ - - cl->rfbFramebufferUpdateMessagesSent++; - - if (cl->preferredEncoding == rfbEncodingCoRRE) { - nUpdateRegionRects = 0; - - for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){ - int x = rect.x1; - int y = rect.y1; - int w = rect.x2 - x; - int h = rect.y2 - y; - nUpdateRegionRects += (((w-1) / cl->correMaxWidth + 1) - * ((h-1) / cl->correMaxHeight + 1)); - } - sraRgnReleaseIterator(i); -#ifdef LIBVNCSERVER_HAVE_LIBZ - } else if (cl->preferredEncoding == rfbEncodingZlib) { - nUpdateRegionRects = 0; - - for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){ - int x = rect.x1; - int y = rect.y1; - int w = rect.x2 - x; - int h = rect.y2 - y; - nUpdateRegionRects += (((h-1) / (ZLIB_MAX_SIZE( w ) / w)) + 1); - } - sraRgnReleaseIterator(i); -#ifdef LIBVNCSERVER_HAVE_LIBJPEG - } else if (cl->preferredEncoding == rfbEncodingTight) { - nUpdateRegionRects = 0; - - for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){ - int x = rect.x1; - int y = rect.y1; - int w = rect.x2 - x; - int h = rect.y2 - y; - int n = rfbNumCodedRectsTight(cl, x, y, w, h); - if (n == 0) { - nUpdateRegionRects = 0xFFFF; - break; - } - nUpdateRegionRects += n; - } - sraRgnReleaseIterator(i); -#endif -#endif - } else { - nUpdateRegionRects = sraRgnCountRects(updateRegion); - } - - fu->type = rfbFramebufferUpdate; - if (nUpdateRegionRects != 0xFFFF) { - if(cl->screen->maxRectsPerUpdate>0 -#ifdef LIBVNCSERVER_HAVE_LIBJPEG - /* Tight encoding counts the rectangles differently */ - && cl->preferredEncoding != rfbEncodingTight -#endif - && nUpdateRegionRects>cl->screen->maxRectsPerUpdate) { - sraRegion* newUpdateRegion = sraRgnBBox(updateRegion); - sraRgnDestroy(updateRegion); - updateRegion = newUpdateRegion; - nUpdateRegionRects = sraRgnCountRects(updateRegion); - } - fu->nRects = Swap16IfLE((uint16_t)(sraRgnCountRects(updateCopyRegion) + - nUpdateRegionRects + - !!sendCursorShape + !!sendCursorPos)); - } else { - fu->nRects = 0xFFFF; - } - cl->ublen = sz_rfbFramebufferUpdateMsg; - - if (sendCursorShape) { - cl->cursorWasChanged = FALSE; - if (!rfbSendCursorShape(cl)) { - sraRgnDestroy(updateRegion); - return FALSE; - } - } - - if (sendCursorPos) { - cl->cursorWasMoved = FALSE; - if (!rfbSendCursorPos(cl)) { - sraRgnDestroy(updateRegion); - return FALSE; - } - } - - if (!sraRgnEmpty(updateCopyRegion)) { - if (!rfbSendCopyRegion(cl,updateCopyRegion,dx,dy)) { - sraRgnDestroy(updateRegion); - sraRgnDestroy(updateCopyRegion); - return FALSE; - } - } - - sraRgnDestroy(updateCopyRegion); - - for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){ - int x = rect.x1; - int y = rect.y1; - int w = rect.x2 - x; - int h = rect.y2 - y; - - cl->rfbRawBytesEquivalent += (sz_rfbFramebufferUpdateRectHeader - + w * (cl->format.bitsPerPixel / 8) * h); - - switch (cl->preferredEncoding) { - case rfbEncodingRaw: - if (!rfbSendRectEncodingRaw(cl, x, y, w, h)) { - sraRgnDestroy(updateRegion); - sraRgnReleaseIterator(i); - return FALSE; - } - break; - case rfbEncodingRRE: - if (!rfbSendRectEncodingRRE(cl, x, y, w, h)) { - sraRgnDestroy(updateRegion); - sraRgnReleaseIterator(i); - return FALSE; - } - break; - case rfbEncodingCoRRE: - if (!rfbSendRectEncodingCoRRE(cl, x, y, w, h)) { - sraRgnDestroy(updateRegion); - sraRgnReleaseIterator(i); - return FALSE; - } - break; - case rfbEncodingHextile: - if (!rfbSendRectEncodingHextile(cl, x, y, w, h)) { - sraRgnDestroy(updateRegion); - sraRgnReleaseIterator(i); - return FALSE; - } - break; -#ifdef LIBVNCSERVER_HAVE_LIBZ - case rfbEncodingZlib: - if (!rfbSendRectEncodingZlib(cl, x, y, w, h)) { - sraRgnDestroy(updateRegion); - sraRgnReleaseIterator(i); - return FALSE; - } - break; -#ifdef LIBVNCSERVER_HAVE_LIBJPEG - case rfbEncodingTight: - if (!rfbSendRectEncodingTight(cl, x, y, w, h)) { - sraRgnDestroy(updateRegion); - sraRgnReleaseIterator(i); - return FALSE; - } - break; -#endif -#endif -#ifdef LIBVNCSERVER_HAVE_LIBZ - case rfbEncodingZRLE: - if (!rfbSendRectEncodingZRLE(cl, x, y, w, h)) { - sraRgnDestroy(updateRegion); - sraRgnReleaseIterator(i); - return FALSE; - } - break; -#endif - } - } - sraRgnReleaseIterator(i); - - if ( nUpdateRegionRects == 0xFFFF && - !rfbSendLastRectMarker(cl) ) { - sraRgnDestroy(updateRegion); - return FALSE; - } - - if (!rfbSendUpdateBuf(cl)) { - sraRgnDestroy(updateRegion); - return FALSE; - } - - sraRgnDestroy(updateRegion); - return TRUE; -} - - -/* - * Send the copy region as a string of CopyRect encoded rectangles. - * The only slightly tricky thing is that we should send the messages in - * the correct order so that an earlier CopyRect will not corrupt the source - * of a later one. - */ - -rfbBool -rfbSendCopyRegion(cl, reg, dx, dy) - rfbClientPtr cl; - sraRegionPtr reg; - int dx, dy; -{ - int x, y, w, h; - rfbFramebufferUpdateRectHeader rect; - rfbCopyRect cr; - sraRectangleIterator* i; - sraRect rect1; - - /* printf("copyrect: "); sraRgnPrint(reg); putchar('\n');fflush(stdout); */ - i = sraRgnGetReverseIterator(reg,dx>0,dy>0); - - while(sraRgnIteratorNext(i,&rect1)) { - x = rect1.x1; - y = rect1.y1; - w = rect1.x2 - x; - h = rect1.y2 - y; - - rect.r.x = Swap16IfLE(x); - rect.r.y = Swap16IfLE(y); - rect.r.w = Swap16IfLE(w); - rect.r.h = Swap16IfLE(h); - rect.encoding = Swap32IfLE(rfbEncodingCopyRect); - - memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, - sz_rfbFramebufferUpdateRectHeader); - cl->ublen += sz_rfbFramebufferUpdateRectHeader; - - cr.srcX = Swap16IfLE(x - dx); - cr.srcY = Swap16IfLE(y - dy); - - memcpy(&cl->updateBuf[cl->ublen], (char *)&cr, sz_rfbCopyRect); - cl->ublen += sz_rfbCopyRect; - - cl->rfbRectanglesSent[rfbEncodingCopyRect]++; - cl->rfbBytesSent[rfbEncodingCopyRect] - += sz_rfbFramebufferUpdateRectHeader + sz_rfbCopyRect; - - } - - return TRUE; -} - -/* - * Send a given rectangle in raw encoding (rfbEncodingRaw). - */ - -rfbBool -rfbSendRectEncodingRaw(cl, x, y, w, h) - rfbClientPtr cl; - int x, y, w, h; -{ - rfbFramebufferUpdateRectHeader rect; - int nlines; - int bytesPerLine = w * (cl->format.bitsPerPixel / 8); - char *fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y) - + (x * (cl->screen->bitsPerPixel / 8))); - - /* Flush the buffer to guarantee correct alignment for translateFn(). */ - if (cl->ublen > 0) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - rect.r.x = Swap16IfLE(x); - rect.r.y = Swap16IfLE(y); - rect.r.w = Swap16IfLE(w); - rect.r.h = Swap16IfLE(h); - rect.encoding = Swap32IfLE(rfbEncodingRaw); - - memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader); - cl->ublen += sz_rfbFramebufferUpdateRectHeader; - - cl->rfbRectanglesSent[rfbEncodingRaw]++; - cl->rfbBytesSent[rfbEncodingRaw] - += sz_rfbFramebufferUpdateRectHeader + bytesPerLine * h; - - nlines = (UPDATE_BUF_SIZE - cl->ublen) / bytesPerLine; - - while (TRUE) { - if (nlines > h) - nlines = h; - - (*cl->translateFn)(cl->translateLookupTable, - &(cl->screen->rfbServerFormat), - &cl->format, fbptr, &cl->updateBuf[cl->ublen], - cl->screen->paddedWidthInBytes, w, nlines); - - cl->ublen += nlines * bytesPerLine; - h -= nlines; - - if (h == 0) /* rect fitted in buffer, do next one */ - return TRUE; - - /* buffer full - flush partial rect and do another nlines */ - - if (!rfbSendUpdateBuf(cl)) - return FALSE; - - fbptr += (cl->screen->paddedWidthInBytes * nlines); - - nlines = (UPDATE_BUF_SIZE - cl->ublen) / bytesPerLine; - if (nlines == 0) { - rfbErr("rfbSendRectEncodingRaw: send buffer too small for %d " - "bytes per line\n", bytesPerLine); - rfbCloseClient(cl); - return FALSE; - } - } -} - - - -/* - * Send an empty rectangle with encoding field set to value of - * rfbEncodingLastRect to notify client that this is the last - * rectangle in framebuffer update ("LastRect" extension of RFB - * protocol). - */ - -rfbBool -rfbSendLastRectMarker(cl) - rfbClientPtr cl; -{ - rfbFramebufferUpdateRectHeader rect; - - if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - rect.encoding = Swap32IfLE(rfbEncodingLastRect); - rect.r.x = 0; - rect.r.y = 0; - rect.r.w = 0; - rect.r.h = 0; - - memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader); - cl->ublen += sz_rfbFramebufferUpdateRectHeader; - - cl->rfbLastRectMarkersSent++; - cl->rfbLastRectBytesSent += sz_rfbFramebufferUpdateRectHeader; - - return TRUE; -} - - -/* - * Send NewFBSize pseudo-rectangle. This tells the client to change - * its framebuffer size. - */ - -rfbBool -rfbSendNewFBSize(cl, w, h) - rfbClientPtr cl; - int w, h; -{ - rfbFramebufferUpdateRectHeader rect; - - if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - rect.encoding = Swap32IfLE(rfbEncodingNewFBSize); - rect.r.x = 0; - rect.r.y = 0; - rect.r.w = Swap16IfLE(w); - rect.r.h = Swap16IfLE(h); - - memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, - sz_rfbFramebufferUpdateRectHeader); - cl->ublen += sz_rfbFramebufferUpdateRectHeader; - - cl->rfbLastRectMarkersSent++; - cl->rfbLastRectBytesSent += sz_rfbFramebufferUpdateRectHeader; - - return TRUE; -} - - -/* - * Send the contents of cl->updateBuf. Returns 1 if successful, -1 if - * not (errno should be set). - */ - -rfbBool -rfbSendUpdateBuf(cl) - rfbClientPtr cl; -{ - if(cl->sock<0) - return FALSE; - - if (WriteExact(cl, cl->updateBuf, cl->ublen) < 0) { - rfbLogPerror("rfbSendUpdateBuf: write"); - rfbCloseClient(cl); - return FALSE; - } - - cl->ublen = 0; - return TRUE; -} - -/* - * rfbSendSetColourMapEntries sends a SetColourMapEntries message to the - * client, using values from the currently installed colormap. - */ - -rfbBool -rfbSendSetColourMapEntries(cl, firstColour, nColours) - rfbClientPtr cl; - int firstColour; - int nColours; -{ - char buf[sz_rfbSetColourMapEntriesMsg + 256 * 3 * 2]; - rfbSetColourMapEntriesMsg *scme = (rfbSetColourMapEntriesMsg *)buf; - uint16_t *rgb = (uint16_t *)(&buf[sz_rfbSetColourMapEntriesMsg]); - rfbColourMap* cm = &cl->screen->colourMap; - - int i, len; - - scme->type = rfbSetColourMapEntries; - - scme->firstColour = Swap16IfLE(firstColour); - scme->nColours = Swap16IfLE(nColours); - - len = sz_rfbSetColourMapEntriesMsg; - - for (i = 0; i < nColours; i++) { - if(i<(int)cm->count) { - if(cm->is16) { - rgb[i*3] = Swap16IfLE(cm->data.shorts[i*3]); - rgb[i*3+1] = Swap16IfLE(cm->data.shorts[i*3+1]); - rgb[i*3+2] = Swap16IfLE(cm->data.shorts[i*3+2]); - } else { - rgb[i*3] = Swap16IfLE(cm->data.bytes[i*3]); - rgb[i*3+1] = Swap16IfLE(cm->data.bytes[i*3+1]); - rgb[i*3+2] = Swap16IfLE(cm->data.bytes[i*3+2]); - } - } - } - - len += nColours * 3 * 2; - - if (WriteExact(cl, buf, len) < 0) { - rfbLogPerror("rfbSendSetColourMapEntries: write"); - rfbCloseClient(cl); - return FALSE; - } - return TRUE; -} - -/* - * rfbSendBell sends a Bell message to all the clients. - */ - -void -rfbSendBell(rfbScreenInfoPtr rfbScreen) -{ - rfbClientIteratorPtr i; - rfbClientPtr cl; - rfbBellMsg b; - - i = rfbGetClientIterator(rfbScreen); - while((cl=rfbClientIteratorNext(i))) { - b.type = rfbBell; - if (WriteExact(cl, (char *)&b, sz_rfbBellMsg) < 0) { - rfbLogPerror("rfbSendBell: write"); - rfbCloseClient(cl); - } - } - rfbReleaseClientIterator(i); -} - - -/* - * rfbSendServerCutText sends a ServerCutText message to all the clients. - */ - -void -rfbSendServerCutText(rfbScreenInfoPtr rfbScreen,char *str, int len) -{ - rfbClientPtr cl; - rfbServerCutTextMsg sct; - rfbClientIteratorPtr iterator; - - iterator = rfbGetClientIterator(rfbScreen); - while ((cl = rfbClientIteratorNext(iterator)) != NULL) { - sct.type = rfbServerCutText; - sct.length = Swap32IfLE(len); - if (WriteExact(cl, (char *)&sct, - sz_rfbServerCutTextMsg) < 0) { - rfbLogPerror("rfbSendServerCutText: write"); - rfbCloseClient(cl); - continue; - } - if (WriteExact(cl, str, len) < 0) { - rfbLogPerror("rfbSendServerCutText: write"); - rfbCloseClient(cl); - } - } - rfbReleaseClientIterator(iterator); -} - -/***************************************************************************** - * - * UDP can be used for keyboard and pointer events when the underlying - * network is highly reliable. This is really here to support ORL's - * videotile, whose TCP implementation doesn't like sending lots of small - * packets (such as 100s of pen readings per second!). - */ - -unsigned char ptrAcceleration = 50; - -void -rfbNewUDPConnection(rfbScreen,sock) - rfbScreenInfoPtr rfbScreen; - int sock; -{ - if (write(sock, &ptrAcceleration, 1) < 0) { - rfbLogPerror("rfbNewUDPConnection: write"); - } -} - -/* - * Because UDP is a message based service, we can't read the first byte and - * then the rest of the packet separately like we do with TCP. We will always - * get a whole packet delivered in one go, so we ask read() for the maximum - * number of bytes we can possibly get. - */ - -void -rfbProcessUDPInput(rfbScreenInfoPtr rfbScreen) -{ - int n; - rfbClientPtr cl=rfbScreen->udpClient; - rfbClientToServerMsg msg; - - if((!cl) || cl->onHold) - return; - - if ((n = read(rfbScreen->udpSock, (char *)&msg, sizeof(msg))) <= 0) { - if (n < 0) { - rfbLogPerror("rfbProcessUDPInput: read"); - } - rfbDisconnectUDPSock(rfbScreen); - return; - } - - switch (msg.type) { - - case rfbKeyEvent: - if (n != sz_rfbKeyEventMsg) { - rfbErr("rfbProcessUDPInput: key event incorrect length\n"); - rfbDisconnectUDPSock(rfbScreen); - return; - } - cl->screen->kbdAddEvent(msg.ke.down, (rfbKeySym)Swap32IfLE(msg.ke.key), cl); - break; - - case rfbPointerEvent: - if (n != sz_rfbPointerEventMsg) { - rfbErr("rfbProcessUDPInput: ptr event incorrect length\n"); - rfbDisconnectUDPSock(rfbScreen); - return; - } - cl->screen->ptrAddEvent(msg.pe.buttonMask, - Swap16IfLE(msg.pe.x), Swap16IfLE(msg.pe.y), cl); - break; - - default: - rfbErr("rfbProcessUDPInput: unknown message type %d\n", - msg.type); - rfbDisconnectUDPSock(rfbScreen); - } -} - -#ifdef LIBVNCSERVER_BACKCHANNEL -void rfbSendBackChannel(rfbScreenInfoPtr rfbScreen,char* str,int len) -{ - rfbClientPtr cl; - rfbBackChannelMsg sct; - rfbClientIteratorPtr iterator; - - iterator = rfbGetClientIterator(rfbScreen); - while ((cl = rfbClientIteratorNext(iterator)) != NULL) { - if (cl->enableBackChannel) { - sct.type = rfbBackChannel; - sct.length = Swap32IfLE(len); - if (WriteExact(cl, (char *)&sct, - sz_rfbBackChannelMsg) < 0) { - rfbLogPerror("rfbSendBackChannel: write"); - rfbCloseClient(cl); - continue; - } - if (WriteExact(cl, str, len) < 0) { - rfbLogPerror("rfbSendBackChannel: write"); - rfbCloseClient(cl); - } - } - } - rfbReleaseClientIterator(iterator); -} -#endif diff --git a/rre.c b/rre.c deleted file mode 100644 index 3e00c05..0000000 --- a/rre.c +++ /dev/null @@ -1,321 +0,0 @@ -/* - * rre.c - * - * Routines to implement Rise-and-Run-length Encoding (RRE). This - * code is based on krw's original javatel rfbserver. - */ - -/* - * OSXvnc Copyright (C) 2001 Dan McGuirk . - * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. - * 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. - */ - -#include - -/* - * rreBeforeBuf contains pixel data in the client's format. - * rreAfterBuf contains the RRE encoded version. If the RRE encoded version is - * larger than the raw data or if it exceeds rreAfterBufSize then - * raw encoding is used instead. - */ - -static int rreBeforeBufSize = 0; -static char *rreBeforeBuf = NULL; - -static int rreAfterBufSize = 0; -static char *rreAfterBuf = NULL; -static int rreAfterBufLen; - -static int subrectEncode8(uint8_t *data, int w, int h); -static int subrectEncode16(uint16_t *data, int w, int h); -static int subrectEncode32(uint32_t *data, int w, int h); -static uint32_t getBgColour(char *data, int size, int bpp); - - -/* - * rfbSendRectEncodingRRE - send a given rectangle using RRE encoding. - */ - -rfbBool -rfbSendRectEncodingRRE(cl, x, y, w, h) - rfbClientPtr cl; - int x, y, w, h; -{ - rfbFramebufferUpdateRectHeader rect; - rfbRREHeader hdr; - int nSubrects; - int i; - char *fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y) - + (x * (cl->screen->bitsPerPixel / 8))); - - int maxRawSize = (cl->screen->width * cl->screen->height - * (cl->format.bitsPerPixel / 8)); - - if (rreBeforeBufSize < maxRawSize) { - rreBeforeBufSize = maxRawSize; - if (rreBeforeBuf == NULL) - rreBeforeBuf = (char *)malloc(rreBeforeBufSize); - else - rreBeforeBuf = (char *)realloc(rreBeforeBuf, rreBeforeBufSize); - } - - if (rreAfterBufSize < maxRawSize) { - rreAfterBufSize = maxRawSize; - if (rreAfterBuf == NULL) - rreAfterBuf = (char *)malloc(rreAfterBufSize); - else - rreAfterBuf = (char *)realloc(rreAfterBuf, rreAfterBufSize); - } - - (*cl->translateFn)(cl->translateLookupTable, - &(cl->screen->rfbServerFormat), - &cl->format, fbptr, rreBeforeBuf, - cl->screen->paddedWidthInBytes, w, h); - - switch (cl->format.bitsPerPixel) { - case 8: - nSubrects = subrectEncode8((uint8_t *)rreBeforeBuf, w, h); - break; - case 16: - nSubrects = subrectEncode16((uint16_t *)rreBeforeBuf, w, h); - break; - case 32: - nSubrects = subrectEncode32((uint32_t *)rreBeforeBuf, w, h); - break; - default: - rfbLog("getBgColour: bpp %d?\n",cl->format.bitsPerPixel); - return FALSE; - } - - if (nSubrects < 0) { - - /* RRE encoding was too large, use raw */ - - return rfbSendRectEncodingRaw(cl, x, y, w, h); - } - - cl->rfbRectanglesSent[rfbEncodingRRE]++; - cl->rfbBytesSent[rfbEncodingRRE] += (sz_rfbFramebufferUpdateRectHeader - + sz_rfbRREHeader + rreAfterBufLen); - - if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader - > UPDATE_BUF_SIZE) - { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - rect.r.x = Swap16IfLE(x); - rect.r.y = Swap16IfLE(y); - rect.r.w = Swap16IfLE(w); - rect.r.h = Swap16IfLE(h); - rect.encoding = Swap32IfLE(rfbEncodingRRE); - - memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, - sz_rfbFramebufferUpdateRectHeader); - cl->ublen += sz_rfbFramebufferUpdateRectHeader; - - hdr.nSubrects = Swap32IfLE(nSubrects); - - memcpy(&cl->updateBuf[cl->ublen], (char *)&hdr, sz_rfbRREHeader); - cl->ublen += sz_rfbRREHeader; - - for (i = 0; i < rreAfterBufLen;) { - - int bytesToCopy = UPDATE_BUF_SIZE - cl->ublen; - - if (i + bytesToCopy > rreAfterBufLen) { - bytesToCopy = rreAfterBufLen - i; - } - - memcpy(&cl->updateBuf[cl->ublen], &rreAfterBuf[i], bytesToCopy); - - cl->ublen += bytesToCopy; - i += bytesToCopy; - - if (cl->ublen == UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - } - - return TRUE; -} - - - -/* - * subrectEncode() encodes the given multicoloured rectangle as a background - * colour overwritten by single-coloured rectangles. It returns the number - * of subrectangles in the encoded buffer, or -1 if subrect encoding won't - * fit in the buffer. It puts the encoded rectangles in rreAfterBuf. The - * single-colour rectangle partition is not optimal, but does find the biggest - * horizontal or vertical rectangle top-left anchored to each consecutive - * coordinate position. - * - * The coding scheme is simply [...] where each - * is []. - */ - -#define DEFINE_SUBRECT_ENCODE(bpp) \ -static int \ -subrectEncode##bpp(data,w,h) \ - uint##bpp##_t *data; \ - int w; \ - int h; \ -{ \ - uint##bpp##_t cl; \ - rfbRectangle subrect; \ - int x,y; \ - int i,j; \ - int hx=0,hy,vx=0,vy; \ - int hyflag; \ - uint##bpp##_t *seg; \ - uint##bpp##_t *line; \ - int hw,hh,vw,vh; \ - int thex,they,thew,theh; \ - int numsubs = 0; \ - int newLen; \ - uint##bpp##_t bg = (uint##bpp##_t)getBgColour((char*)data,w*h,bpp); \ - \ - *((uint##bpp##_t*)rreAfterBuf) = bg; \ - \ - rreAfterBufLen = (bpp/8); \ - \ - for (y=0; y 0) && (i >= hx)) {hy += 1;} else {hyflag = 0;} \ - } \ - vy = j-1; \ - \ - /* We now have two possible subrects: (x,y,hx,hy) and (x,y,vx,vy) \ - * We'll choose the bigger of the two. \ - */ \ - hw = hx-x+1; \ - hh = hy-y+1; \ - vw = vx-x+1; \ - vh = vy-y+1; \ - \ - thex = x; \ - they = y; \ - \ - if ((hw*hh) > (vw*vh)) { \ - thew = hw; \ - theh = hh; \ - } else { \ - thew = vw; \ - theh = vh; \ - } \ - \ - subrect.x = Swap16IfLE(thex); \ - subrect.y = Swap16IfLE(they); \ - subrect.w = Swap16IfLE(thew); \ - subrect.h = Swap16IfLE(theh); \ - \ - newLen = rreAfterBufLen + (bpp/8) + sz_rfbRectangle; \ - if ((newLen > (w * h * (bpp/8))) || (newLen > rreAfterBufSize)) \ - return -1; \ - \ - numsubs += 1; \ - *((uint##bpp##_t*)(rreAfterBuf + rreAfterBufLen)) = cl; \ - rreAfterBufLen += (bpp/8); \ - memcpy(&rreAfterBuf[rreAfterBufLen],&subrect,sz_rfbRectangle); \ - rreAfterBufLen += sz_rfbRectangle; \ - \ - /* \ - * Now mark the subrect as done. \ - */ \ - for (j=they; j < (they+theh); j++) { \ - for (i=thex; i < (thex+thew); i++) { \ - data[j*w+i] = bg; \ - } \ - } \ - } \ - } \ - } \ - \ - return numsubs; \ -} - -DEFINE_SUBRECT_ENCODE(8) -DEFINE_SUBRECT_ENCODE(16) -DEFINE_SUBRECT_ENCODE(32) - - -/* - * getBgColour() gets the most prevalent colour in a byte array. - */ -static uint32_t -getBgColour(data,size,bpp) - char *data; - int size; - int bpp; -{ - -#define NUMCLRS 256 - - static int counts[NUMCLRS]; - int i,j,k; - - int maxcount = 0; - uint8_t maxclr = 0; - - if (bpp != 8) { - if (bpp == 16) { - return ((uint16_t *)data)[0]; - } else if (bpp == 32) { - return ((uint32_t *)data)[0]; - } else { - rfbLog("getBgColour: bpp %d?\n",bpp); - return 0; - } - } - - for (i=0; i= NUMCLRS) { - rfbErr("getBgColour: unusual colour = %d\n", k); - return 0; - } - counts[k] += 1; - if (counts[k] > maxcount) { - maxcount = counts[k]; - maxclr = ((uint8_t *)data)[j]; - } - } - - return maxclr; -} diff --git a/selbox.c b/selbox.c deleted file mode 100644 index 8d76e23..0000000 --- a/selbox.c +++ /dev/null @@ -1,301 +0,0 @@ -#include -#include -#include - -typedef struct { - rfbScreenInfoPtr screen; - rfbFontDataPtr font; - char** list; - int listSize; - int selected; - int displayStart; - int x1,y1,x2,y2,textH,pageH; - int xhot,yhot; - int buttonWidth,okBX,cancelBX,okX,cancelX,okY; - rfbBool okInverted,cancelInverted; - int lastButtons; - rfbPixel colour,backColour; - SelectionChangedHookPtr selChangedHook; - enum { SELECTING, OK, CANCEL } state; -} rfbSelectData; - -static const char* okStr="OK"; -static const char* cancelStr="Cancel"; - -static void selPaintButtons(rfbSelectData* m,rfbBool invertOk,rfbBool invertCancel) -{ - rfbScreenInfoPtr s = m->screen; - rfbPixel bcolour = m->backColour; - rfbPixel colour = m->colour; - - rfbFillRect(s,m->x1,m->okY-m->textH,m->x2,m->okY,bcolour); - - if(invertOk) { - rfbFillRect(s,m->okBX,m->okY-m->textH,m->okBX+m->buttonWidth,m->okY,colour); - rfbDrawStringWithClip(s,m->font,m->okX+m->xhot,m->okY-1+m->yhot,okStr, - m->x1,m->okY-m->textH,m->x2,m->okY, - bcolour,colour); - } else - rfbDrawString(s,m->font,m->okX+m->xhot,m->okY-1+m->yhot,okStr,colour); - - if(invertCancel) { - rfbFillRect(s,m->cancelBX,m->okY-m->textH, - m->cancelBX+m->buttonWidth,m->okY,colour); - rfbDrawStringWithClip(s,m->font,m->cancelX+m->xhot,m->okY-1+m->yhot, - cancelStr,m->x1,m->okY-m->textH,m->x2,m->okY, - bcolour,colour); - } else - rfbDrawString(s,m->font,m->cancelX+m->xhot,m->okY-1+m->yhot,cancelStr,colour); - - m->okInverted = invertOk; - m->cancelInverted = invertCancel; -} - -/* line is relative to displayStart */ -static void selPaintLine(rfbSelectData* m,int line,rfbBool invert) -{ - int y1 = m->y1+line*m->textH, y2 = y1+m->textH; - if(y2>m->y2) - y2=m->y2; - rfbFillRect(m->screen,m->x1,y1,m->x2,y2,invert?m->colour:m->backColour); - if(m->displayStart+linelistSize) - rfbDrawStringWithClip(m->screen,m->font,m->x1+m->xhot,y2-1+m->yhot, - m->list[m->displayStart+line], - m->x1,y1,m->x2,y2, - invert?m->backColour:m->colour, - invert?m->backColour:m->colour); -} - -static void selSelect(rfbSelectData* m,int _index) -{ - int delta; - - if(_index==m->selected || _index<0 || _index>=m->listSize) - return; - - if(m->selected>=0) - selPaintLine(m,m->selected-m->displayStart,FALSE); - - if(_indexdisplayStart || _index>=m->displayStart+m->pageH) { - /* targetLine is the screen line in which the selected line will - be displayed. - targetLine = m->pageH/2 doesn't look so nice */ - int targetLine = m->selected-m->displayStart; - int lineStart,lineEnd; - - /* scroll */ - if(_indexpageH-targetLine>=m->listSize) - targetLine = _index+m->pageH-m->listSize; - delta = _index-(m->displayStart+targetLine); - - if(delta>-m->pageH && deltapageH) { - if(delta>0) { - lineStart = m->pageH-delta; - lineEnd = m->pageH; - rfbDoCopyRect(m->screen,m->x1,m->y1,m->x2,m->y1+lineStart*m->textH, - 0,-delta*m->textH); - } else { - lineStart = 0; - lineEnd = -delta; - rfbDoCopyRect(m->screen, - m->x1,m->y1+lineEnd*m->textH,m->x2,m->y2, - 0,-delta*m->textH); - } - } else { - lineStart = 0; - lineEnd = m->pageH; - } - m->displayStart += delta; - for(delta=lineStart;deltaselected = _index; - selPaintLine(m,m->selected-m->displayStart,TRUE); - - if(m->selChangedHook) - m->selChangedHook(_index); - - /* todo: scrollbars */ -} - -static void selKbdAddEvent(rfbBool down,rfbKeySym keySym,rfbClientPtr cl) -{ - if(down) { - if(keySym>' ' && keySym<0xff) { - int i; - rfbSelectData* m = (rfbSelectData*)cl->screen->screenData; - char c = tolower(keySym); - - for(i=m->selected+1;m->list[i] && tolower(m->list[i][0])!=c;i++); - if(!m->list[i]) - for(i=0;iselected && tolower(m->list[i][0])!=c;i++); - selSelect(m,i); - } else if(keySym==XK_Escape) { - rfbSelectData* m = (rfbSelectData*)cl->screen->screenData; - m->state = CANCEL; - } else if(keySym==XK_Return) { - rfbSelectData* m = (rfbSelectData*)cl->screen->screenData; - m->state = OK; - } else { - rfbSelectData* m = (rfbSelectData*)cl->screen->screenData; - int curSel=m->selected; - if(keySym==XK_Up) { - if(curSel>0) - selSelect(m,curSel-1); - } else if(keySym==XK_Down) { - if(curSel+1listSize) - selSelect(m,curSel+1); - } else { - if(keySym==XK_Page_Down) { - if(curSel+m->pageHlistSize) - selSelect(m,curSel+m->pageH); - else - selSelect(m,m->listSize-1); - } else if(keySym==XK_Page_Up) { - if(curSel-m->pageH>=0) - selSelect(m,curSel-m->pageH); - else - selSelect(m,0); - } - } - } - } -} - -static void selPtrAddEvent(int buttonMask,int x,int y,rfbClientPtr cl) -{ - rfbSelectData* m = (rfbSelectData*)cl->screen->screenData; - if(yokY && y>=m->okY-m->textH) { - if(x>=m->okBX && xokBX+m->buttonWidth) { - if(!m->okInverted) - selPaintButtons(m,TRUE,FALSE); - if(buttonMask) - m->state = OK; - } else if(x>=m->cancelBX && xcancelBX+m->buttonWidth) { - if(!m->cancelInverted) - selPaintButtons(m,FALSE,TRUE); - if(buttonMask) - m->state = CANCEL; - } else if(m->okInverted || m->cancelInverted) - selPaintButtons(m,FALSE,FALSE); - } else { - if(m->okInverted || m->cancelInverted) - selPaintButtons(m,FALSE,FALSE); - if(!m->lastButtons && buttonMask) { - if(x>=m->x1 && xx2 && y>=m->y1 && yy2) - selSelect(m,m->displayStart+(y-m->y1)/m->textH); - } - } - m->lastButtons = buttonMask; - - /* todo: scrollbars */ -} - -static rfbCursorPtr selGetCursorPtr(rfbClientPtr cl) -{ - return(0); -} - -int rfbSelectBox(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font, - char** list, - int x1,int y1,int x2,int y2, - rfbPixel colour,rfbPixel backColour, - int border,SelectionChangedHookPtr selChangedHook) -{ - int bpp = rfbScreen->bitsPerPixel/8; - char* frameBufferBackup; - void* screenDataBackup = rfbScreen->screenData; - KbdAddEventProcPtr kbdAddEventBackup = rfbScreen->kbdAddEvent; - PtrAddEventProcPtr ptrAddEventBackup = rfbScreen->ptrAddEvent; - GetCursorProcPtr getCursorPtrBackup = rfbScreen->getCursorPtr; - DisplayHookPtr displayHookBackup = rfbScreen->displayHook; - rfbSelectData selData; - int i,j,k; - int fx1,fy1,fx2,fy2; /* for font bbox */ - - if(list==0 || *list==0) - return(-1); - - rfbWholeFontBBox(font, &fx1, &fy1, &fx2, &fy2); - selData.textH = fy2-fy1; - /* I need at least one line for the choice and one for the buttons */ - if(y2-y1screenData = &selData; - rfbScreen->kbdAddEvent = selKbdAddEvent; - rfbScreen->ptrAddEvent = selPtrAddEvent; - rfbScreen->getCursorPtr = selGetCursorPtr; - rfbScreen->displayHook = 0; - - /* backup screen */ - for(j=0;jframeBuffer+j*rfbScreen->paddedWidthInBytes+x1*bpp, - (x2-x1)*bpp); - - /* paint list and buttons */ - rfbFillRect(rfbScreen,x1,y1,x2,y2,colour); - selPaintButtons(&selData,FALSE,FALSE); - selSelect(&selData,0); - - /* modal loop */ - while(selData.state == SELECTING) - rfbProcessEvents(rfbScreen,20000); - - /* copy back screen data */ - for(j=0;jframeBuffer+j*rfbScreen->paddedWidthInBytes+x1*bpp, - frameBufferBackup+j*(x2-x1)*bpp, - (x2-x1)*bpp); - free(frameBufferBackup); - rfbMarkRectAsModified(rfbScreen,x1,y1,x2,y2); - rfbScreen->screenData = screenDataBackup; - rfbScreen->kbdAddEvent = kbdAddEventBackup; - rfbScreen->ptrAddEvent = ptrAddEventBackup; - rfbScreen->getCursorPtr = getCursorPtrBackup; - rfbScreen->displayHook = displayHookBackup; - - if(selData.state==CANCEL) - selData.selected=-1; - return(selData.selected); -} - diff --git a/sockets.c b/sockets.c deleted file mode 100644 index cea1898..0000000 --- a/sockets.c +++ /dev/null @@ -1,619 +0,0 @@ -/* - * sockets.c - deal with TCP & UDP sockets. - * - * This code should be independent of any changes in the RFB protocol. It just - * deals with the X server scheduling stuff, calling rfbNewClientConnection and - * rfbProcessClientMessage to actually deal with the protocol. If a socket - * needs to be closed for any reason then rfbCloseClient should be called, and - * this in turn will call rfbClientConnectionGone. To make an active - * connection out, call rfbConnect - note that this does _not_ call - * rfbNewClientConnection. - * - * This file is divided into two types of function. Those beginning with - * "rfb" are specific to sockets using the RFB protocol. Those without the - * "rfb" prefix are more general socket routines (which are used by the http - * code). - * - * Thanks to Karl Hakimian for pointing out that some platforms return EAGAIN - * not EWOULDBLOCK. - */ - -/* - * OSXvnc Copyright (C) 2001 Dan McGuirk . - * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. - * 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. - */ - -#include - -#ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H -#include -#endif - -#ifdef WIN32 -#pragma warning (disable: 4018 4761) -#define close closesocket -#define read(sock,buf,len) recv(sock,buf,len,0) -#define EWOULDBLOCK WSAEWOULDBLOCK -#define ETIMEDOUT WSAETIMEDOUT -#define write(sock,buf,len) send(sock,buf,len,0) -#else -#ifdef LIBVNCSERVER_HAVE_SYS_TIME_H -#include -#endif -#ifdef LIBVNCSERVER_HAVE_SYS_SOCKET_H -#include -#endif -#ifdef LIBVNCSERVER_HAVE_NETINET_IN_H -#include -#include -#include -#include -#endif -#ifdef LIBVNCSERVER_HAVE_UNISTD_H -#include -#endif -#endif - -#if defined(__linux__) && defined(NEED_TIMEVAL) -struct timeval -{ - long int tv_sec,tv_usec; -} -; -#endif - -#ifdef LIBVNCSERVER_HAVE_FCNTL_H -#include -#endif - -#include - -#ifdef USE_LIBWRAP -#include -#include -int allow_severity=LOG_INFO; -int deny_severity=LOG_WARNING; -#endif - -/*#ifndef WIN32 -int max(int i,int j) { return(isocketInitDone) - return; - - rfbScreen->socketInitDone = TRUE; - - if (rfbScreen->inetdSock != -1) { - const int one = 1; - -#ifndef WIN32 - if (fcntl(rfbScreen->inetdSock, F_SETFL, O_NONBLOCK) < 0) { - rfbLogPerror("fcntl"); - return; - } -#endif - - if (setsockopt(rfbScreen->inetdSock, IPPROTO_TCP, TCP_NODELAY, - (char *)&one, sizeof(one)) < 0) { - rfbLogPerror("setsockopt"); - return; - } - - FD_ZERO(&(rfbScreen->allFds)); - FD_SET(rfbScreen->inetdSock, &(rfbScreen->allFds)); - rfbScreen->maxFd = rfbScreen->inetdSock; - return; - } - - if(rfbScreen->autoPort) { - int i; - rfbLog("Autoprobing TCP port \n"); - - for (i = 5900; i < 6000; i++) { - if ((rfbScreen->rfbListenSock = ListenOnTCPPort(i)) >= 0) { - rfbScreen->rfbPort = i; - break; - } - } - - if (i >= 6000) { - rfbLogPerror("Failure autoprobing"); - return; - } - - rfbLog("Autoprobing selected port %d\n", rfbScreen->rfbPort); - FD_ZERO(&(rfbScreen->allFds)); - FD_SET(rfbScreen->rfbListenSock, &(rfbScreen->allFds)); - rfbScreen->maxFd = rfbScreen->rfbListenSock; - } - else if(rfbScreen->rfbPort>0) { - rfbLog("Listening for VNC connections on TCP port %d\n", rfbScreen->rfbPort); - - if ((rfbScreen->rfbListenSock = ListenOnTCPPort(rfbScreen->rfbPort)) < 0) { - rfbLogPerror("ListenOnTCPPort"); - return; - } - - FD_ZERO(&(rfbScreen->allFds)); - FD_SET(rfbScreen->rfbListenSock, &(rfbScreen->allFds)); - rfbScreen->maxFd = rfbScreen->rfbListenSock; - } - - if (rfbScreen->udpPort != 0) { - rfbLog("rfbInitSockets: listening for input on UDP port %d\n",rfbScreen->udpPort); - - if ((rfbScreen->udpSock = ListenOnUDPPort(rfbScreen->udpPort)) < 0) { - rfbLogPerror("ListenOnUDPPort"); - return; - } - FD_SET(rfbScreen->udpSock, &(rfbScreen->allFds)); - rfbScreen->maxFd = max((int)rfbScreen->udpSock,rfbScreen->maxFd); - } -} - - -/* - * rfbCheckFds is called from ProcessInputEvents to check for input on the RFB - * socket(s). If there is input to process, the appropriate function in the - * RFB server code will be called (rfbNewClientConnection, - * rfbProcessClientMessage, etc). - */ - -void -rfbCheckFds(rfbScreenInfoPtr rfbScreen,long usec) -{ - int nfds; - fd_set fds; - struct timeval tv; - struct sockaddr_in addr; - size_t addrlen = sizeof(addr); - char buf[6]; - const int one = 1; - int sock; - rfbClientIteratorPtr i; - rfbClientPtr cl; - - if (!rfbScreen->inetdInitDone && rfbScreen->inetdSock != -1) { - rfbNewClientConnection(rfbScreen,rfbScreen->inetdSock); - rfbScreen->inetdInitDone = TRUE; - } - - memcpy((char *)&fds, (char *)&(rfbScreen->allFds), sizeof(fd_set)); - tv.tv_sec = 0; - tv.tv_usec = usec; - nfds = select(rfbScreen->maxFd + 1, &fds, NULL, NULL /* &fds */, &tv); - if (nfds == 0) { - return; - } - if (nfds < 0) { -#ifdef WIN32 - errno = WSAGetLastError(); -#endif - if (errno != EINTR) - rfbLogPerror("rfbCheckFds: select"); - return; - } - - if (rfbScreen->rfbListenSock != -1 && FD_ISSET(rfbScreen->rfbListenSock, &fds)) { - - if ((sock = accept(rfbScreen->rfbListenSock, - (struct sockaddr *)&addr, &addrlen)) < 0) { - rfbLogPerror("rfbCheckFds: accept"); - return; - } - -#ifndef WIN32 - if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { - rfbLogPerror("rfbCheckFds: fcntl"); - close(sock); - return; - } -#endif - - if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, - (char *)&one, sizeof(one)) < 0) { - rfbLogPerror("rfbCheckFds: setsockopt"); - close(sock); - return; - } - -#ifdef USE_LIBWRAP - if(!hosts_ctl("vnc",STRING_UNKNOWN,inet_ntoa(addr.sin_addr), - STRING_UNKNOWN)) { - rfbLog("Rejected connection from client %s\n", - inet_ntoa(addr.sin_addr)); - close(sock); - return; - } -#endif - - rfbLog("Got connection from client %s\n", inet_ntoa(addr.sin_addr)); - - rfbNewClient(rfbScreen,sock); - - FD_CLR(rfbScreen->rfbListenSock, &fds); - if (--nfds == 0) - return; - } - - if ((rfbScreen->udpSock != -1) && FD_ISSET(rfbScreen->udpSock, &fds)) { - if(!rfbScreen->udpClient) - rfbNewUDPClient(rfbScreen); - if (recvfrom(rfbScreen->udpSock, buf, 1, MSG_PEEK, - (struct sockaddr *)&addr, &addrlen) < 0) { - rfbLogPerror("rfbCheckFds: UDP: recvfrom"); - rfbDisconnectUDPSock(rfbScreen); - rfbScreen->udpSockConnected = FALSE; - } else { - if (!rfbScreen->udpSockConnected || - (memcmp(&addr, &rfbScreen->udpRemoteAddr, addrlen) != 0)) - { - /* new remote end */ - rfbLog("rfbCheckFds: UDP: got connection\n"); - - memcpy(&rfbScreen->udpRemoteAddr, &addr, addrlen); - rfbScreen->udpSockConnected = TRUE; - - if (connect(rfbScreen->udpSock, - (struct sockaddr *)&addr, addrlen) < 0) { - rfbLogPerror("rfbCheckFds: UDP: connect"); - rfbDisconnectUDPSock(rfbScreen); - return; - } - - rfbNewUDPConnection(rfbScreen,rfbScreen->udpSock); - } - - rfbProcessUDPInput(rfbScreen); - } - - FD_CLR(rfbScreen->udpSock, &fds); - if (--nfds == 0) - return; - } - - i = rfbGetClientIterator(rfbScreen); - while((cl = rfbClientIteratorNext(i))) { - if (cl->onHold) - continue; - if (FD_ISSET(cl->sock, &fds) && FD_ISSET(cl->sock, &(rfbScreen->allFds))) - rfbProcessClientMessage(cl); - } - rfbReleaseClientIterator(i); -} - - -void -rfbDisconnectUDPSock(rfbScreenInfoPtr rfbScreen) -{ - rfbScreen->udpSockConnected = FALSE; -} - - - -void -rfbCloseClient(cl) - rfbClientPtr cl; -{ - LOCK(cl->updateMutex); -#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD - if (cl->sock != -1) -#endif - { - FD_CLR(cl->sock,&(cl->screen->allFds)); - if(cl->sock==cl->screen->maxFd) - while(cl->screen->maxFd>0 - && !FD_ISSET(cl->screen->maxFd,&(cl->screen->allFds))) - cl->screen->maxFd--; - shutdown(cl->sock,SHUT_RDWR); - close(cl->sock); - cl->sock = -1; - } - TSIGNAL(cl->updateCond); - UNLOCK(cl->updateMutex); -} - - -/* - * rfbConnect is called to make a connection out to a given TCP address. - */ - -int -rfbConnect(rfbScreen, host, port) - rfbScreenInfoPtr rfbScreen; - char *host; - int port; -{ - int sock; - int one = 1; - - rfbLog("Making connection to client on host %s port %d\n", - host,port); - - if ((sock = ConnectToTcpAddr(host, port)) < 0) { - rfbLogPerror("connection failed"); - return -1; - } - -#ifndef WIN32 - if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { - rfbLogPerror("fcntl failed"); - close(sock); - return -1; - } -#endif - - if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, - (char *)&one, sizeof(one)) < 0) { - rfbLogPerror("setsockopt failed"); - close(sock); - return -1; - } - - /* AddEnabledDevice(sock); */ - FD_SET(sock, &rfbScreen->allFds); - rfbScreen->maxFd = max(sock,rfbScreen->maxFd); - - return sock; -} - -/* - * ReadExact reads an exact number of bytes from a client. Returns 1 if - * those bytes have been read, 0 if the other end has closed, or -1 if an error - * occurred (errno is set to ETIMEDOUT if it timed out). - */ - -int -ReadExactTimeout(rfbClientPtr cl, char* buf, int len, int timeout) -{ - int sock = cl->sock; - int n; - fd_set fds; - struct timeval tv; - - while (len > 0) { - n = read(sock, buf, len); - - if (n > 0) { - - buf += n; - len -= n; - - } else if (n == 0) { - - return 0; - - } else { -#ifdef WIN32 - errno = WSAGetLastError(); -#endif - if (errno == EINTR) - continue; - - if (errno != EWOULDBLOCK && errno != EAGAIN) { - return n; - } - - FD_ZERO(&fds); - FD_SET(sock, &fds); - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - n = select(sock+1, &fds, NULL, &fds, &tv); - if (n < 0) { - rfbLogPerror("ReadExact: select"); - return n; - } - if (n == 0) { - errno = ETIMEDOUT; - return -1; - } - } - } - return 1; -} - -int ReadExact(rfbClientPtr cl,char* buf,int len) -{ - return(ReadExactTimeout(cl,buf,len,rfbMaxClientWait)); -} - -/* - * WriteExact writes an exact number of bytes to a client. Returns 1 if - * those bytes have been written, or -1 if an error occurred (errno is set to - * ETIMEDOUT if it timed out). - */ - -int -WriteExact(cl, buf, len) - rfbClientPtr cl; - const char *buf; - int len; -{ - int sock = cl->sock; - int n; - fd_set fds; - struct timeval tv; - int totalTimeWaited = 0; - - LOCK(cl->outputMutex); - while (len > 0) { - n = write(sock, buf, len); - - if (n > 0) { - - buf += n; - len -= n; - - } else if (n == 0) { - - rfbErr("WriteExact: write returned 0?\n"); - return 0; - - } else { -#ifdef WIN32 - errno = WSAGetLastError(); -#endif - if (errno == EINTR) - continue; - - if (errno != EWOULDBLOCK && errno != EAGAIN) { - UNLOCK(cl->outputMutex); - return n; - } - - /* Retry every 5 seconds until we exceed rfbMaxClientWait. We - need to do this because select doesn't necessarily return - immediately when the other end has gone away */ - - FD_ZERO(&fds); - FD_SET(sock, &fds); - tv.tv_sec = 5; - tv.tv_usec = 0; - n = select(sock+1, NULL, &fds, NULL /* &fds */, &tv); - if (n < 0) { - if(errno==EINTR) - continue; - rfbLogPerror("WriteExact: select"); - UNLOCK(cl->outputMutex); - return n; - } - if (n == 0) { - totalTimeWaited += 5000; - if (totalTimeWaited >= rfbMaxClientWait) { - errno = ETIMEDOUT; - UNLOCK(cl->outputMutex); - return -1; - } - } else { - totalTimeWaited = 0; - } - } - } - UNLOCK(cl->outputMutex); - return 1; -} - -int -ListenOnTCPPort(port) - int port; -{ - struct sockaddr_in addr; - int sock; - int one = 1; - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - /* addr.sin_addr.s_addr = interface.s_addr; */ - addr.sin_addr.s_addr = INADDR_ANY; - - if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - return -1; - } - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, - (char *)&one, sizeof(one)) < 0) { - close(sock); - return -1; - } - if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - close(sock); - return -1; - } - if (listen(sock, 5) < 0) { - close(sock); - return -1; - } - - return sock; -} - -int -ConnectToTcpAddr(host, port) - char *host; - int port; -{ - struct hostent *hp; - int sock; - struct sockaddr_in addr; - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - - if ((addr.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) - { - if (!(hp = gethostbyname(host))) { - errno = EINVAL; - return -1; - } - addr.sin_addr.s_addr = *(unsigned long *)hp->h_addr; - } - - if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - return -1; - } - - if (connect(sock, (struct sockaddr *)&addr, (sizeof(addr))) < 0) { - close(sock); - return -1; - } - - return sock; -} - -int -ListenOnUDPPort(port) - int port; -{ - struct sockaddr_in addr; - int sock; - int one = 1; - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - /* addr.sin_addr.s_addr = interface.s_addr; */ - addr.sin_addr.s_addr = INADDR_ANY; - - if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - return -1; - } - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, - (char *)&one, sizeof(one)) < 0) { - return -1; - } - if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - return -1; - } - - return sock; -} diff --git a/stats.c b/stats.c deleted file mode 100644 index 931c5d2..0000000 --- a/stats.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * stats.c - */ - -/* - * Copyright (C) 2002 RealVNC Ltd. - * OSXvnc Copyright (C) 2001 Dan McGuirk . - * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. - * 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. - */ - -#include - -static const char* encNames[] = { - "raw", "copyRect", "RRE", "[encoding 3]", "CoRRE", "hextile", - "zlib", "tight", "[encoding 8]", "[encoding 9]", "[encoding 10]", - "[encoding 11]", "[encoding 12]", "[encoding 13]", "[encoding 14]", -#ifdef LIBVNCSERVER_BACKCHANNEL - "BackChannel", -#else - "[encoding 15]", -#endif - "ZRLE", "[encoding 17]", "[encoding 18]", "[encoding 19]", "[encoding 20]" -}; - - -void -rfbResetStats(rfbClientPtr cl) -{ - int i; - for (i = 0; i < MAX_ENCODINGS; i++) { - cl->rfbBytesSent[i] = 0; - cl->rfbRectanglesSent[i] = 0; - } - cl->rfbLastRectMarkersSent = 0; - cl->rfbLastRectBytesSent = 0; - cl->rfbCursorShapeBytesSent = 0; - cl->rfbCursorShapeUpdatesSent = 0; - cl->rfbCursorPosBytesSent = 0; - cl->rfbCursorPosUpdatesSent = 0; - cl->rfbFramebufferUpdateMessagesSent = 0; - cl->rfbRawBytesEquivalent = 0; - cl->rfbKeyEventsRcvd = 0; - cl->rfbPointerEventsRcvd = 0; -} - -void -rfbPrintStats(rfbClientPtr cl) -{ - int i; - int totalRectanglesSent = 0; - int totalBytesSent = 0; - - rfbLog("Statistics:\n"); - - if ((cl->rfbKeyEventsRcvd != 0) || (cl->rfbPointerEventsRcvd != 0)) - rfbLog(" key events received %d, pointer events %d\n", - cl->rfbKeyEventsRcvd, cl->rfbPointerEventsRcvd); - - for (i = 0; i < MAX_ENCODINGS; i++) { - totalRectanglesSent += cl->rfbRectanglesSent[i]; - totalBytesSent += cl->rfbBytesSent[i]; - } - - totalRectanglesSent += (cl->rfbCursorShapeUpdatesSent + - cl->rfbCursorPosUpdatesSent + - cl->rfbLastRectMarkersSent); - totalBytesSent += (cl->rfbCursorShapeBytesSent + - cl->rfbCursorPosBytesSent + - cl->rfbLastRectBytesSent); - - rfbLog(" framebuffer updates %d, rectangles %d, bytes %d\n", - cl->rfbFramebufferUpdateMessagesSent, totalRectanglesSent, - totalBytesSent); - - if (cl->rfbLastRectMarkersSent != 0) - rfbLog(" LastRect and NewFBSize markers %d, bytes %d\n", - cl->rfbLastRectMarkersSent, cl->rfbLastRectBytesSent); - - if (cl->rfbCursorShapeUpdatesSent != 0) - rfbLog(" cursor shape updates %d, bytes %d\n", - cl->rfbCursorShapeUpdatesSent, cl->rfbCursorShapeBytesSent); - - if (cl->rfbCursorPosUpdatesSent != 0) - rfbLog(" cursor position updates %d, bytes %d\n", - cl->rfbCursorPosUpdatesSent, cl->rfbCursorPosBytesSent); - - for (i = 0; i < MAX_ENCODINGS; i++) { - if (cl->rfbRectanglesSent[i] != 0) - rfbLog(" %s rectangles %d, bytes %d\n", - encNames[i], cl->rfbRectanglesSent[i], cl->rfbBytesSent[i]); - } - - if ((totalBytesSent - cl->rfbBytesSent[rfbEncodingCopyRect]) != 0) { - rfbLog(" raw bytes equivalent %d, compression ratio %f\n", - cl->rfbRawBytesEquivalent, - (double)cl->rfbRawBytesEquivalent - / (double)(totalBytesSent - - cl->rfbBytesSent[rfbEncodingCopyRect]- - cl->rfbCursorShapeBytesSent - - cl->rfbCursorPosBytesSent - - cl->rfbLastRectBytesSent)); - } -} diff --git a/tableinit24.c b/tableinit24.c deleted file mode 100644 index 39e9920..0000000 --- a/tableinit24.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - 24 bit - */ - -/* - * OSXvnc Copyright (C) 2001 Dan McGuirk . - * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. - * 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. - */ - -static void -rfbInitOneRGBTable24 (uint8_t *table, int inMax, int outMax, int outShift,int swap); - - -static void -rfbInitColourMapSingleTable24(char **table, rfbPixelFormat *in, - rfbPixelFormat *out,rfbColourMap* colourMap) -{ - uint32_t i, r, g, b, outValue; - uint8_t *t; - uint8_t c; - unsigned int nEntries = 1 << in->bitsPerPixel; - int shift = colourMap->is16?16:8; - - if (*table) free(*table); - *table = (char *)malloc(nEntries * 3 + 1); - t = (uint8_t *)*table; - - for (i = 0; i < nEntries; i++) { - r = g = b = 0; - if(i < colourMap->count) { - if(colourMap->is16) { - r = colourMap->data.shorts[3*i+0]; - g = colourMap->data.shorts[3*i+1]; - b = colourMap->data.shorts[3*i+2]; - } else { - r = colourMap->data.bytes[3*i+0]; - g = colourMap->data.bytes[3*i+1]; - b = colourMap->data.bytes[3*i+2]; - } - } - outValue = ((((r * (1 + out->redMax)) >> shift) << out->redShift) | - (((g * (1 + out->greenMax)) >> shift) << out->greenShift) | - (((b * (1 + out->blueMax)) >> shift) << out->blueShift)); - *(uint32_t*)&t[3*i] = outValue; - if(!rfbEndianTest) - memmove(t+3*i,t+3*i+1,3); - if (out->bigEndian != in->bigEndian) { - c = t[3*i]; t[3*i] = t[3*i+2]; t[3*i+2] = c; - } - } -} - -/* - * rfbInitTrueColourSingleTable sets up a single lookup table for truecolour - * translation. - */ - -static void -rfbInitTrueColourSingleTable24 (char **table, rfbPixelFormat *in, - rfbPixelFormat *out) -{ - int i,outValue; - int inRed, inGreen, inBlue, outRed, outGreen, outBlue; - uint8_t *t; - uint8_t c; - int nEntries = 1 << in->bitsPerPixel; - - if (*table) free(*table); - *table = (char *)malloc(nEntries * 3 + 1); - t = (uint8_t *)*table; - - for (i = 0; i < nEntries; i++) { - inRed = (i >> in->redShift) & in->redMax; - inGreen = (i >> in->greenShift) & in->greenMax; - inBlue = (i >> in->blueShift) & in->blueMax; - - outRed = (inRed * out->redMax + in->redMax / 2) / in->redMax; - outGreen = (inGreen * out->greenMax + in->greenMax / 2) / in->greenMax; - outBlue = (inBlue * out->blueMax + in->blueMax / 2) / in->blueMax; - - outValue = ((outRed << out->redShift) | - (outGreen << out->greenShift) | - (outBlue << out->blueShift)); - *(uint32_t*)&t[3*i] = outValue; - if(!rfbEndianTest) - memmove(t+3*i,t+3*i+1,3); - if (out->bigEndian != in->bigEndian) { - c = t[3*i]; t[3*i] = t[3*i+2]; t[3*i+2] = c; - } - } -} - - -/* - * rfbInitTrueColourRGBTables sets up three separate lookup tables for the - * red, green and blue values. - */ - -static void -rfbInitTrueColourRGBTables24 (char **table, rfbPixelFormat *in, - rfbPixelFormat *out) -{ - uint8_t *redTable; - uint8_t *greenTable; - uint8_t *blueTable; - - if (*table) free(*table); - *table = (char *)malloc((in->redMax + in->greenMax + in->blueMax + 3) - * 3 + 1); - redTable = (uint8_t *)*table; - greenTable = redTable + 3*(in->redMax + 1); - blueTable = greenTable + 3*(in->greenMax + 1); - - rfbInitOneRGBTable24 (redTable, in->redMax, out->redMax, - out->redShift, (out->bigEndian != in->bigEndian)); - rfbInitOneRGBTable24 (greenTable, in->greenMax, out->greenMax, - out->greenShift, (out->bigEndian != in->bigEndian)); - rfbInitOneRGBTable24 (blueTable, in->blueMax, out->blueMax, - out->blueShift, (out->bigEndian != in->bigEndian)); -} - -static void -rfbInitOneRGBTable24 (uint8_t *table, int inMax, int outMax, int outShift, - int swap) -{ - int i; - int nEntries = inMax + 1; - uint32_t outValue; - uint8_t c; - - for (i = 0; i < nEntries; i++) { - outValue = ((i * outMax + inMax / 2) / inMax) << outShift; - *(uint32_t *)&table[3*i] = outValue; - if(!rfbEndianTest) - memmove(table+3*i,table+3*i+1,3); - if (swap) { - c = table[3*i]; table[3*i] = table[3*i+2]; - table[3*i+2] = c; - } - } -} diff --git a/tableinitcmtemplate.c b/tableinitcmtemplate.c deleted file mode 100644 index df01b23..0000000 --- a/tableinitcmtemplate.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * tableinitcmtemplate.c - template for initialising lookup tables for - * translation from a colour map to true colour. - * - * This file shouldn't be compiled. It is included multiple times by - * translate.c, each time with a different definition of the macro OUT. - * For each value of OUT, this file defines a function which allocates an - * appropriately sized lookup table and initialises it. - * - * I know this code isn't nice to read because of all the macros, but - * efficiency is important here. - */ - -/* - * OSXvnc Copyright (C) 2001 Dan McGuirk . - * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. - * 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. - */ - -#if !defined(OUT) -#error "This file shouldn't be compiled." -#error "It is included as part of translate.c" -#endif - -#define OUT_T CONCAT3E(uint,OUT,_t) -#define SwapOUT(x) CONCAT2E(Swap,OUT(x)) -#define rfbInitColourMapSingleTableOUT \ - CONCAT2E(rfbInitColourMapSingleTable,OUT) - -static void -rfbInitColourMapSingleTableOUT(char **table, rfbPixelFormat *in, - rfbPixelFormat *out,rfbColourMap* colourMap) -{ - uint32_t i, r, g, b; - OUT_T *t; - uint32_t nEntries = 1 << in->bitsPerPixel; - int shift = colourMap->is16?16:8; - - if (*table) free(*table); - *table = (char *)malloc(nEntries * sizeof(OUT_T)); - t = (OUT_T *)*table; - - for (i = 0; i < nEntries; i++) { - r = g = b = 0; - if(i < colourMap->count) { - if(colourMap->is16) { - r = colourMap->data.shorts[3*i+0]; - g = colourMap->data.shorts[3*i+1]; - b = colourMap->data.shorts[3*i+2]; - } else { - r = colourMap->data.bytes[3*i+0]; - g = colourMap->data.bytes[3*i+1]; - b = colourMap->data.bytes[3*i+2]; - } - } - t[i] = ((((r * (1 + out->redMax)) >> shift) << out->redShift) | - (((g * (1 + out->greenMax)) >> shift) << out->greenShift) | - (((b * (1 + out->blueMax)) >> shift) << out->blueShift)); -#if (OUT != 8) - if (out->bigEndian != in->bigEndian) { - t[i] = SwapOUT(t[i]); - } -#endif - } -} - -#undef OUT_T -#undef SwapOUT -#undef rfbInitColourMapSingleTableOUT diff --git a/tableinittctemplate.c b/tableinittctemplate.c deleted file mode 100644 index 8d4f742..0000000 --- a/tableinittctemplate.c +++ /dev/null @@ -1,142 +0,0 @@ -/* - * tableinittctemplate.c - template for initialising lookup tables for - * truecolour to truecolour translation. - * - * This file shouldn't be compiled. It is included multiple times by - * translate.c, each time with a different definition of the macro OUT. - * For each value of OUT, this file defines two functions for initialising - * lookup tables. One is for truecolour translation using a single lookup - * table, the other is for truecolour translation using three separate - * lookup tables for the red, green and blue values. - * - * I know this code isn't nice to read because of all the macros, but - * efficiency is important here. - */ - -/* - * OSXvnc Copyright (C) 2001 Dan McGuirk . - * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. - * 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. - */ - -#if !defined(OUT) -#error "This file shouldn't be compiled." -#error "It is included as part of translate.c" -#endif - -#define OUT_T CONCAT3E(uint,OUT,_t) -#define SwapOUT(x) CONCAT2E(Swap,OUT(x)) -#define rfbInitTrueColourSingleTableOUT \ - CONCAT2E(rfbInitTrueColourSingleTable,OUT) -#define rfbInitTrueColourRGBTablesOUT CONCAT2E(rfbInitTrueColourRGBTables,OUT) -#define rfbInitOneRGBTableOUT CONCAT2E(rfbInitOneRGBTable,OUT) - -static void -rfbInitOneRGBTableOUT (OUT_T *table, int inMax, int outMax, int outShift, - int swap); - - -/* - * rfbInitTrueColourSingleTable sets up a single lookup table for truecolour - * translation. - */ - -static void -rfbInitTrueColourSingleTableOUT (char **table, rfbPixelFormat *in, - rfbPixelFormat *out) -{ - int i; - int inRed, inGreen, inBlue, outRed, outGreen, outBlue; - OUT_T *t; - int nEntries = 1 << in->bitsPerPixel; - - if (*table) free(*table); - *table = (char *)malloc(nEntries * sizeof(OUT_T)); - t = (OUT_T *)*table; - - for (i = 0; i < nEntries; i++) { - inRed = (i >> in->redShift) & in->redMax; - inGreen = (i >> in->greenShift) & in->greenMax; - inBlue = (i >> in->blueShift) & in->blueMax; - - outRed = (inRed * out->redMax + in->redMax / 2) / in->redMax; - outGreen = (inGreen * out->greenMax + in->greenMax / 2) / in->greenMax; - outBlue = (inBlue * out->blueMax + in->blueMax / 2) / in->blueMax; - - t[i] = ((outRed << out->redShift) | - (outGreen << out->greenShift) | - (outBlue << out->blueShift)); -#if (OUT != 8) - if (out->bigEndian != in->bigEndian) { - t[i] = SwapOUT(t[i]); - } -#endif - } -} - - -/* - * rfbInitTrueColourRGBTables sets up three separate lookup tables for the - * red, green and blue values. - */ - -static void -rfbInitTrueColourRGBTablesOUT (char **table, rfbPixelFormat *in, - rfbPixelFormat *out) -{ - OUT_T *redTable; - OUT_T *greenTable; - OUT_T *blueTable; - - if (*table) free(*table); - *table = (char *)malloc((in->redMax + in->greenMax + in->blueMax + 3) - * sizeof(OUT_T)); - redTable = (OUT_T *)*table; - greenTable = redTable + in->redMax + 1; - blueTable = greenTable + in->greenMax + 1; - - rfbInitOneRGBTableOUT (redTable, in->redMax, out->redMax, - out->redShift, (out->bigEndian != in->bigEndian)); - rfbInitOneRGBTableOUT (greenTable, in->greenMax, out->greenMax, - out->greenShift, (out->bigEndian != in->bigEndian)); - rfbInitOneRGBTableOUT (blueTable, in->blueMax, out->blueMax, - out->blueShift, (out->bigEndian != in->bigEndian)); -} - -static void -rfbInitOneRGBTableOUT (OUT_T *table, int inMax, int outMax, int outShift, - int swap) -{ - int i; - int nEntries = inMax + 1; - - for (i = 0; i < nEntries; i++) { - table[i] = ((i * outMax + inMax / 2) / inMax) << outShift; -#if (OUT != 8) - if (swap) { - table[i] = SwapOUT(table[i]); - } -#endif - } -} - -#undef OUT_T -#undef SwapOUT -#undef rfbInitTrueColourSingleTableOUT -#undef rfbInitTrueColourRGBTablesOUT -#undef rfbInitOneRGBTableOUT diff --git a/tabletrans24template.c b/tabletrans24template.c deleted file mode 100644 index 4b3a0a0..0000000 --- a/tabletrans24template.c +++ /dev/null @@ -1,281 +0,0 @@ -/* - * tabletranstemplate.c - template for translation using lookup tables. - * - * This file shouldn't be compiled. It is included multiple times by - * translate.c, each time with different definitions of the macros IN and OUT. - * - * For each pair of values IN and OUT, this file defines two functions for - * translating a given rectangle of pixel data. One uses a single lookup - * table, and the other uses three separate lookup tables for the red, green - * and blue values. - * - * I know this code isn't nice to read because of all the macros, but - * efficiency is important here. - */ - -/* - * OSXvnc Copyright (C) 2001 Dan McGuirk . - * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. - * 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. - */ - -#if !defined(BPP) -#error "This file shouldn't be compiled." -#error "It is included as part of translate.c" -#endif - -#if BPP == 24 - -/* - * rfbTranslateWithSingleTableINtoOUT translates a rectangle of pixel data - * using a single lookup table. - */ - -static void -rfbTranslateWithSingleTable24to24 (char *table, rfbPixelFormat *in, - rfbPixelFormat *out, - char *iptr, char *optr, - int bytesBetweenInputLines, - int width, int height) -{ - uint8_t *ip = (uint8_t *)iptr; - uint8_t *op = (uint8_t *)optr; - int ipextra = bytesBetweenInputLines - width * 3; - uint8_t *opLineEnd; - uint8_t *t = (uint8_t *)table; - int shift = rfbEndianTest?0:8; - uint8_t c; - - while (height > 0) { - opLineEnd = op + width*3; - - while (op < opLineEnd) { - *(uint32_t*)op = t[((*(uint32_t *)ip)>>shift)&0x00ffffff]; - if(!rfbEndianTest) - memmove(op,op+1,3); - if (out->bigEndian != in->bigEndian) { - c = op[0]; op[0] = op[2]; op[2] = c; - } - op += 3; - ip += 3; - } - - ip += ipextra; - height--; - } -} - -/* - * rfbTranslateWithRGBTablesINtoOUT translates a rectangle of pixel data - * using three separate lookup tables for the red, green and blue values. - */ - -static void -rfbTranslateWithRGBTables24to24 (char *table, rfbPixelFormat *in, - rfbPixelFormat *out, - char *iptr, char *optr, - int bytesBetweenInputLines, - int width, int height) -{ - uint8_t *ip = (uint8_t *)iptr; - uint8_t *op = (uint8_t *)optr; - int ipextra = bytesBetweenInputLines - width*3; - uint8_t *opLineEnd; - uint8_t *redTable = (uint8_t *)table; - uint8_t *greenTable = redTable + 3*(in->redMax + 1); - uint8_t *blueTable = greenTable + 3*(in->greenMax + 1); - uint32_t outValue,inValue; - int shift = rfbEndianTest?0:8; - - while (height > 0) { - opLineEnd = op+3*width; - - while (op < opLineEnd) { - inValue = ((*(uint32_t *)ip)>>shift)&0x00ffffff; - outValue = (redTable[(inValue >> in->redShift) & in->redMax] | - greenTable[(inValue >> in->greenShift) & in->greenMax] | - blueTable[(inValue >> in->blueShift) & in->blueMax]); - memcpy(op,&outValue,3); - op += 3; - ip+=3; - } - ip += ipextra; - height--; - } -} - -#else - -#define IN_T CONCAT3E(uint,BPP,_t) -#define OUT_T CONCAT3E(uint,BPP,_t) -#define rfbTranslateWithSingleTable24toOUT \ - CONCAT4E(rfbTranslateWithSingleTable,24,to,BPP) -#define rfbTranslateWithSingleTableINto24 \ - CONCAT4E(rfbTranslateWithSingleTable,BPP,to,24) -#define rfbTranslateWithRGBTables24toOUT \ - CONCAT4E(rfbTranslateWithRGBTables,24,to,BPP) -#define rfbTranslateWithRGBTablesINto24 \ - CONCAT4E(rfbTranslateWithRGBTables,BPP,to,24) - -/* - * rfbTranslateWithSingleTableINtoOUT translates a rectangle of pixel data - * using a single lookup table. - */ - -static void -rfbTranslateWithSingleTable24toOUT (char *table, rfbPixelFormat *in, - rfbPixelFormat *out, - char *iptr, char *optr, - int bytesBetweenInputLines, - int width, int height) -{ - uint8_t *ip = (uint8_t *)iptr; - OUT_T *op = (OUT_T *)optr; - int ipextra = bytesBetweenInputLines - width*3; - OUT_T *opLineEnd; - OUT_T *t = (OUT_T *)table; - int shift = rfbEndianTest?0:8; - - while (height > 0) { - opLineEnd = op + width; - - while (op < opLineEnd) { - *(op++) = t[((*(uint32_t *)ip)>>shift)&0x00ffffff]; - ip+=3; - } - - ip += ipextra; - height--; - } -} - - -/* - * rfbTranslateWithRGBTablesINtoOUT translates a rectangle of pixel data - * using three separate lookup tables for the red, green and blue values. - */ - -static void -rfbTranslateWithRGBTables24toOUT (char *table, rfbPixelFormat *in, - rfbPixelFormat *out, - char *iptr, char *optr, - int bytesBetweenInputLines, - int width, int height) -{ - uint8_t *ip = (uint8_t *)iptr; - OUT_T *op = (OUT_T *)optr; - int ipextra = bytesBetweenInputLines - width*3; - OUT_T *opLineEnd; - OUT_T *redTable = (OUT_T *)table; - OUT_T *greenTable = redTable + in->redMax + 1; - OUT_T *blueTable = greenTable + in->greenMax + 1; - uint32_t inValue; - int shift = rfbEndianTest?0:8; - - while (height > 0) { - opLineEnd = &op[width]; - - while (op < opLineEnd) { - inValue = ((*(uint32_t *)ip)>>shift)&0x00ffffff; - *(op++) = (redTable[(inValue >> in->redShift) & in->redMax] | - greenTable[(inValue >> in->greenShift) & in->greenMax] | - blueTable[(inValue >> in->blueShift) & in->blueMax]); - ip+=3; - } - ip += ipextra; - height--; - } -} - -/* - * rfbTranslateWithSingleTableINto24 translates a rectangle of pixel data - * using a single lookup table. - */ - -static void -rfbTranslateWithSingleTableINto24 (char *table, rfbPixelFormat *in, - rfbPixelFormat *out, - char *iptr, char *optr, - int bytesBetweenInputLines, - int width, int height) -{ - IN_T *ip = (IN_T *)iptr; - uint8_t *op = (uint8_t *)optr; - int ipextra = bytesBetweenInputLines / sizeof(IN_T) - width; - uint8_t *opLineEnd; - uint8_t *t = (uint8_t *)table; - - while (height > 0) { - opLineEnd = op + width * 3; - - while (op < opLineEnd) { - memcpy(op,&t[3*(*(ip++))],3); - op += 3; - } - - ip += ipextra; - height--; - } -} - - -/* - * rfbTranslateWithRGBTablesINto24 translates a rectangle of pixel data - * using three separate lookup tables for the red, green and blue values. - */ - -static void -rfbTranslateWithRGBTablesINto24 (char *table, rfbPixelFormat *in, - rfbPixelFormat *out, - char *iptr, char *optr, - int bytesBetweenInputLines, - int width, int height) -{ - IN_T *ip = (IN_T *)iptr; - uint8_t *op = (uint8_t *)optr; - int ipextra = bytesBetweenInputLines / sizeof(IN_T) - width; - uint8_t *opLineEnd; - uint8_t *redTable = (uint8_t *)table; - uint8_t *greenTable = redTable + 3*(in->redMax + 1); - uint8_t *blueTable = greenTable + 3*(in->greenMax + 1); - uint32_t outValue; - - while (height > 0) { - opLineEnd = op+3*width; - - while (op < opLineEnd) { - outValue = (redTable[(*ip >> in->redShift) & in->redMax] | - greenTable[(*ip >> in->greenShift) & in->greenMax] | - blueTable[(*ip >> in->blueShift) & in->blueMax]); - memcpy(op,&outValue,3); - op += 3; - ip++; - } - ip += ipextra; - height--; - } -} - -#undef IN_T -#undef OUT_T -#undef rfbTranslateWithSingleTable24toOUT -#undef rfbTranslateWithRGBTables24toOUT -#undef rfbTranslateWithSingleTableINto24 -#undef rfbTranslateWithRGBTablesINto24 - -#endif diff --git a/tabletranstemplate.c b/tabletranstemplate.c deleted file mode 100644 index e83c623..0000000 --- a/tabletranstemplate.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * tabletranstemplate.c - template for translation using lookup tables. - * - * This file shouldn't be compiled. It is included multiple times by - * translate.c, each time with different definitions of the macros IN and OUT. - * - * For each pair of values IN and OUT, this file defines two functions for - * translating a given rectangle of pixel data. One uses a single lookup - * table, and the other uses three separate lookup tables for the red, green - * and blue values. - * - * I know this code isn't nice to read because of all the macros, but - * efficiency is important here. - */ - -/* - * OSXvnc Copyright (C) 2001 Dan McGuirk . - * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. - * 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. - */ - -#if !defined(IN) || !defined(OUT) -#error "This file shouldn't be compiled." -#error "It is included as part of translate.c" -#endif - -#define IN_T CONCAT3E(uint,IN,_t) -#define OUT_T CONCAT3E(uint,OUT,_t) -#define rfbTranslateWithSingleTableINtoOUT \ - CONCAT4E(rfbTranslateWithSingleTable,IN,to,OUT) -#define rfbTranslateWithRGBTablesINtoOUT \ - CONCAT4E(rfbTranslateWithRGBTables,IN,to,OUT) - -/* - * rfbTranslateWithSingleTableINtoOUT translates a rectangle of pixel data - * using a single lookup table. - */ - -static void -rfbTranslateWithSingleTableINtoOUT (char *table, rfbPixelFormat *in, - rfbPixelFormat *out, - char *iptr, char *optr, - int bytesBetweenInputLines, - int width, int height) -{ - IN_T *ip = (IN_T *)iptr; - OUT_T *op = (OUT_T *)optr; - int ipextra = bytesBetweenInputLines / sizeof(IN_T) - width; - OUT_T *opLineEnd; - OUT_T *t = (OUT_T *)table; - - while (height > 0) { - opLineEnd = op + width; - - while (op < opLineEnd) { - *(op++) = t[*(ip++)]; - } - - ip += ipextra; - height--; - } -} - - -/* - * rfbTranslateWithRGBTablesINtoOUT translates a rectangle of pixel data - * using three separate lookup tables for the red, green and blue values. - */ - -static void -rfbTranslateWithRGBTablesINtoOUT (char *table, rfbPixelFormat *in, - rfbPixelFormat *out, - char *iptr, char *optr, - int bytesBetweenInputLines, - int width, int height) -{ - IN_T *ip = (IN_T *)iptr; - OUT_T *op = (OUT_T *)optr; - int ipextra = bytesBetweenInputLines / sizeof(IN_T) - width; - OUT_T *opLineEnd; - OUT_T *redTable = (OUT_T *)table; - OUT_T *greenTable = redTable + in->redMax + 1; - OUT_T *blueTable = greenTable + in->greenMax + 1; - - while (height > 0) { - opLineEnd = &op[width]; - - while (op < opLineEnd) { - *(op++) = (redTable[(*ip >> in->redShift) & in->redMax] | - greenTable[(*ip >> in->greenShift) & in->greenMax] | - blueTable[(*ip >> in->blueShift) & in->blueMax]); - ip++; - } - ip += ipextra; - height--; - } -} - -#undef IN_T -#undef OUT_T -#undef rfbTranslateWithSingleTableINtoOUT -#undef rfbTranslateWithRGBTablesINtoOUT diff --git a/test/Makefile.am b/test/Makefile.am index c250fe6..1ca7941 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -2,5 +2,5 @@ CFLAGS=-I.. -g -Wall noinst_PROGRAMS=tight-1 cargstest -LDADD = ../libvncserver.a ../libvncclient/libvncclient.a +LDADD = ../libvncserver/libvncserver.a ../libvncclient/libvncclient.a diff --git a/tight.c b/tight.c deleted file mode 100644 index b97adb9..0000000 --- a/tight.c +++ /dev/null @@ -1,1820 +0,0 @@ -/* - * tight.c - * - * Routines to implement Tight Encoding - */ - -/* - * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. - * Copyright (C) 1999 AT&T Laboratories Cambridge. 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. - */ - -/*#include */ -#include - -#ifdef WIN32 -#define XMD_H -#undef FAR -#define NEEDFAR_POINTERS -#endif - -#include - -/* Note: The following constant should not be changed. */ -#define TIGHT_MIN_TO_COMPRESS 12 - -/* The parameters below may be adjusted. */ -#define MIN_SPLIT_RECT_SIZE 4096 -#define MIN_SOLID_SUBRECT_SIZE 2048 -#define MAX_SPLIT_TILE_SIZE 16 - -/* May be set to TRUE with "-lazytight" Xvnc option. */ -rfbBool rfbTightDisableGradient = FALSE; - -/* This variable is set on every rfbSendRectEncodingTight() call. */ -static rfbBool usePixelFormat24; - - -/* Compression level stuff. The following array contains various - encoder parameters for each of 10 compression levels (0..9). - Last three parameters correspond to JPEG quality levels (0..9). */ - -typedef struct TIGHT_CONF_s { - int maxRectSize, maxRectWidth; - int monoMinRectSize, gradientMinRectSize; - int idxZlibLevel, monoZlibLevel, rawZlibLevel, gradientZlibLevel; - int gradientThreshold, gradientThreshold24; - int idxMaxColorsDivisor; - int jpegQuality, jpegThreshold, jpegThreshold24; -} TIGHT_CONF; - -static TIGHT_CONF tightConf[10] = { - { 512, 32, 6, 65536, 0, 0, 0, 0, 0, 0, 4, 5, 10000, 23000 }, - { 2048, 128, 6, 65536, 1, 1, 1, 0, 0, 0, 8, 10, 8000, 18000 }, - { 6144, 256, 8, 65536, 3, 3, 2, 0, 0, 0, 24, 15, 6500, 15000 }, - { 10240, 1024, 12, 65536, 5, 5, 3, 0, 0, 0, 32, 25, 5000, 12000 }, - { 16384, 2048, 12, 65536, 6, 6, 4, 0, 0, 0, 32, 37, 4000, 10000 }, - { 32768, 2048, 12, 4096, 7, 7, 5, 4, 150, 380, 32, 50, 3000, 8000 }, - { 65536, 2048, 16, 4096, 7, 7, 6, 4, 170, 420, 48, 60, 2000, 5000 }, - { 65536, 2048, 16, 4096, 8, 8, 7, 5, 180, 450, 64, 70, 1000, 2500 }, - { 65536, 2048, 32, 8192, 9, 9, 8, 6, 190, 475, 64, 75, 500, 1200 }, - { 65536, 2048, 32, 8192, 9, 9, 9, 6, 200, 500, 96, 80, 200, 500 } -}; - -static int compressLevel; -static int qualityLevel; - -/* Stuff dealing with palettes. */ - -typedef struct COLOR_LIST_s { - struct COLOR_LIST_s *next; - int idx; - uint32_t rgb; -} COLOR_LIST; - -typedef struct PALETTE_ENTRY_s { - COLOR_LIST *listNode; - int numPixels; -} PALETTE_ENTRY; - -typedef struct PALETTE_s { - PALETTE_ENTRY entry[256]; - COLOR_LIST *hash[256]; - COLOR_LIST list[256]; -} PALETTE; - -static int paletteNumColors, paletteMaxColors; -static uint32_t monoBackground, monoForeground; -static PALETTE palette; - -/* Pointers to dynamically-allocated buffers. */ - -static int tightBeforeBufSize = 0; -static char *tightBeforeBuf = NULL; - -static int tightAfterBufSize = 0; -static char *tightAfterBuf = NULL; - -static int *prevRowBuf = NULL; - -void TightCleanup() -{ - if(tightBeforeBufSize) { - free(tightBeforeBuf); - tightBeforeBufSize=0; - } - if(tightAfterBufSize) { - free(tightAfterBuf); - tightAfterBufSize=0; - } -} - -/* Prototypes for static functions. */ - -static void FindBestSolidArea (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t colorValue, int *w_ptr, int *h_ptr); -static void ExtendSolidArea (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t colorValue, - int *x_ptr, int *y_ptr, int *w_ptr, int *h_ptr); -static rfbBool CheckSolidTile (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t *colorPtr, rfbBool needSameColor); -static rfbBool CheckSolidTile8 (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t *colorPtr, rfbBool needSameColor); -static rfbBool CheckSolidTile16 (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t *colorPtr, rfbBool needSameColor); -static rfbBool CheckSolidTile32 (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t *colorPtr, rfbBool needSameColor); - -static rfbBool SendRectSimple (rfbClientPtr cl, int x, int y, int w, int h); -static rfbBool SendSubrect (rfbClientPtr cl, int x, int y, int w, int h); -static rfbBool SendTightHeader (rfbClientPtr cl, int x, int y, int w, int h); - -static rfbBool SendSolidRect (rfbClientPtr cl); -static rfbBool SendMonoRect (rfbClientPtr cl, int w, int h); -static rfbBool SendIndexedRect (rfbClientPtr cl, int w, int h); -static rfbBool SendFullColorRect (rfbClientPtr cl, int w, int h); -static rfbBool SendGradientRect (rfbClientPtr cl, int w, int h); - -static rfbBool CompressData(rfbClientPtr cl, int streamId, int dataLen, - int zlibLevel, int zlibStrategy); -static rfbBool SendCompressedData(rfbClientPtr cl, int compressedLen); - -static void FillPalette8(int count); -static void FillPalette16(int count); -static void FillPalette32(int count); - -static void PaletteReset(void); -static int PaletteInsert(uint32_t rgb, int numPixels, int bpp); - -static void Pack24(rfbClientPtr cl, char *buf, rfbPixelFormat *fmt, int count); - -static void EncodeIndexedRect16(uint8_t *buf, int count); -static void EncodeIndexedRect32(uint8_t *buf, int count); - -static void EncodeMonoRect8(uint8_t *buf, int w, int h); -static void EncodeMonoRect16(uint8_t *buf, int w, int h); -static void EncodeMonoRect32(uint8_t *buf, int w, int h); - -static void FilterGradient24(rfbClientPtr cl, char *buf, rfbPixelFormat *fmt, int w, int h); -static void FilterGradient16(rfbClientPtr cl, uint16_t *buf, rfbPixelFormat *fmt, int w, int h); -static void FilterGradient32(rfbClientPtr cl, uint32_t *buf, rfbPixelFormat *fmt, int w, int h); - -static int DetectSmoothImage(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h); -static unsigned long DetectSmoothImage24(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h); -static unsigned long DetectSmoothImage16(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h); -static unsigned long DetectSmoothImage32(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h); - -static rfbBool SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, - int quality); -static void PrepareRowForJpeg(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); -static void PrepareRowForJpeg24(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); -static void PrepareRowForJpeg16(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); -static void PrepareRowForJpeg32(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); - -static void JpegInitDestination(j_compress_ptr cinfo); -static boolean JpegEmptyOutputBuffer(j_compress_ptr cinfo); -static void JpegTermDestination(j_compress_ptr cinfo); -static void JpegSetDstManager(j_compress_ptr cinfo); - - -/* - * Tight encoding implementation. - */ - -int -rfbNumCodedRectsTight(cl, x, y, w, h) - rfbClientPtr cl; - int x, y, w, h; -{ - int maxRectSize, maxRectWidth; - int subrectMaxWidth, subrectMaxHeight; - - /* No matter how many rectangles we will send if LastRect markers - are used to terminate rectangle stream. */ - if (cl->enableLastRectEncoding && w * h >= MIN_SPLIT_RECT_SIZE) - return 0; - - maxRectSize = tightConf[cl->tightCompressLevel].maxRectSize; - maxRectWidth = tightConf[cl->tightCompressLevel].maxRectWidth; - - if (w > maxRectWidth || w * h > maxRectSize) { - subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w; - subrectMaxHeight = maxRectSize / subrectMaxWidth; - return (((w - 1) / maxRectWidth + 1) * - ((h - 1) / subrectMaxHeight + 1)); - } else { - return 1; - } -} - -rfbBool -rfbSendRectEncodingTight(cl, x, y, w, h) - rfbClientPtr cl; - int x, y, w, h; -{ - int nMaxRows; - uint32_t colorValue; - int dx, dy, dw, dh; - int x_best, y_best, w_best, h_best; - char *fbptr; - - rfbSendUpdateBuf(cl); - - compressLevel = cl->tightCompressLevel; - qualityLevel = cl->tightQualityLevel; - - if ( cl->format.depth == 24 && cl->format.redMax == 0xFF && - cl->format.greenMax == 0xFF && cl->format.blueMax == 0xFF ) { - usePixelFormat24 = TRUE; - } else { - usePixelFormat24 = FALSE; - } - - if (!cl->enableLastRectEncoding || w * h < MIN_SPLIT_RECT_SIZE) - return SendRectSimple(cl, x, y, w, h); - - /* Make sure we can write at least one pixel into tightBeforeBuf. */ - - if (tightBeforeBufSize < 4) { - tightBeforeBufSize = 4; - if (tightBeforeBuf == NULL) - tightBeforeBuf = (char *)malloc(tightBeforeBufSize); - else - tightBeforeBuf = (char *)realloc(tightBeforeBuf, - tightBeforeBufSize); - } - - /* Calculate maximum number of rows in one non-solid rectangle. */ - - { - int maxRectSize, maxRectWidth, nMaxWidth; - - maxRectSize = tightConf[compressLevel].maxRectSize; - maxRectWidth = tightConf[compressLevel].maxRectWidth; - nMaxWidth = (w > maxRectWidth) ? maxRectWidth : w; - nMaxRows = maxRectSize / nMaxWidth; - } - - /* Try to find large solid-color areas and send them separately. */ - - for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) { - - /* If a rectangle becomes too large, send its upper part now. */ - - if (dy - y >= nMaxRows) { - if (!SendRectSimple(cl, x, y, w, nMaxRows)) - return 0; - y += nMaxRows; - h -= nMaxRows; - } - - dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ? - MAX_SPLIT_TILE_SIZE : (y + h - dy); - - for (dx = x; dx < x + w; dx += MAX_SPLIT_TILE_SIZE) { - - dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w) ? - MAX_SPLIT_TILE_SIZE : (x + w - dx); - - if (CheckSolidTile(cl, dx, dy, dw, dh, &colorValue, FALSE)) { - - /* Get dimensions of solid-color area. */ - - FindBestSolidArea(cl, dx, dy, w - (dx - x), h - (dy - y), - colorValue, &w_best, &h_best); - - /* Make sure a solid rectangle is large enough - (or the whole rectangle is of the same color). */ - - if ( w_best * h_best != w * h && - w_best * h_best < MIN_SOLID_SUBRECT_SIZE ) - continue; - - /* Try to extend solid rectangle to maximum size. */ - - x_best = dx; y_best = dy; - ExtendSolidArea(cl, x, y, w, h, colorValue, - &x_best, &y_best, &w_best, &h_best); - - /* Send rectangles at top and left to solid-color area. */ - - if ( y_best != y && - !SendRectSimple(cl, x, y, w, y_best-y) ) - return FALSE; - if ( x_best != x && - !rfbSendRectEncodingTight(cl, x, y_best, - x_best-x, h_best) ) - return FALSE; - - /* Send solid-color rectangle. */ - - if (!SendTightHeader(cl, x_best, y_best, w_best, h_best)) - return FALSE; - - fbptr = (cl->screen->frameBuffer + - (cl->screen->paddedWidthInBytes * y_best) + - (x_best * (cl->screen->bitsPerPixel / 8))); - - (*cl->translateFn)(cl->translateLookupTable, &cl->screen->rfbServerFormat, - &cl->format, fbptr, tightBeforeBuf, - cl->screen->paddedWidthInBytes, 1, 1); - - if (!SendSolidRect(cl)) - return FALSE; - - /* Send remaining rectangles (at right and bottom). */ - - if ( x_best + w_best != x + w && - !rfbSendRectEncodingTight(cl, x_best+w_best, y_best, - w-(x_best-x)-w_best, h_best) ) - return FALSE; - if ( y_best + h_best != y + h && - !rfbSendRectEncodingTight(cl, x, y_best+h_best, - w, h-(y_best-y)-h_best) ) - return FALSE; - - /* Return after all recursive calls are done. */ - - return TRUE; - } - - } - - } - - /* No suitable solid-color rectangles found. */ - - return SendRectSimple(cl, x, y, w, h); -} - -static void -FindBestSolidArea(cl, x, y, w, h, colorValue, w_ptr, h_ptr) - rfbClientPtr cl; - int x, y, w, h; - uint32_t colorValue; - int *w_ptr, *h_ptr; -{ - int dx, dy, dw, dh; - int w_prev; - int w_best = 0, h_best = 0; - - w_prev = w; - - for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) { - - dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ? - MAX_SPLIT_TILE_SIZE : (y + h - dy); - dw = (w_prev > MAX_SPLIT_TILE_SIZE) ? - MAX_SPLIT_TILE_SIZE : w_prev; - - if (!CheckSolidTile(cl, x, dy, dw, dh, &colorValue, TRUE)) - break; - - for (dx = x + dw; dx < x + w_prev;) { - dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w_prev) ? - MAX_SPLIT_TILE_SIZE : (x + w_prev - dx); - if (!CheckSolidTile(cl, dx, dy, dw, dh, &colorValue, TRUE)) - break; - dx += dw; - } - - w_prev = dx - x; - if (w_prev * (dy + dh - y) > w_best * h_best) { - w_best = w_prev; - h_best = dy + dh - y; - } - } - - *w_ptr = w_best; - *h_ptr = h_best; -} - -static void -ExtendSolidArea(cl, x, y, w, h, colorValue, x_ptr, y_ptr, w_ptr, h_ptr) - rfbClientPtr cl; - int x, y, w, h; - uint32_t colorValue; - int *x_ptr, *y_ptr, *w_ptr, *h_ptr; -{ - int cx, cy; - - /* Try to extend the area upwards. */ - for ( cy = *y_ptr - 1; - cy >= y && CheckSolidTile(cl, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE); - cy-- ); - *h_ptr += *y_ptr - (cy + 1); - *y_ptr = cy + 1; - - /* ... downwards. */ - for ( cy = *y_ptr + *h_ptr; - cy < y + h && - CheckSolidTile(cl, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE); - cy++ ); - *h_ptr += cy - (*y_ptr + *h_ptr); - - /* ... to the left. */ - for ( cx = *x_ptr - 1; - cx >= x && CheckSolidTile(cl, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE); - cx-- ); - *w_ptr += *x_ptr - (cx + 1); - *x_ptr = cx + 1; - - /* ... to the right. */ - for ( cx = *x_ptr + *w_ptr; - cx < x + w && - CheckSolidTile(cl, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE); - cx++ ); - *w_ptr += cx - (*x_ptr + *w_ptr); -} - -/* - * Check if a rectangle is all of the same color. If needSameColor is - * set to non-zero, then also check that its color equals to the - * *colorPtr value. The result is 1 if the test is successfull, and in - * that case new color will be stored in *colorPtr. - */ - -static rfbBool CheckSolidTile(rfbClientPtr cl, int x, int y, int w, int h, uint32_t* colorPtr, rfbBool needSameColor) -{ - switch(cl->screen->rfbServerFormat.bitsPerPixel) { - case 32: - return CheckSolidTile32(cl, x, y, w, h, colorPtr, needSameColor); - case 16: - return CheckSolidTile16(cl, x, y, w, h, colorPtr, needSameColor); - default: - return CheckSolidTile8(cl, x, y, w, h, colorPtr, needSameColor); - } -} - -#define DEFINE_CHECK_SOLID_FUNCTION(bpp) \ - \ -static rfbBool \ -CheckSolidTile##bpp(rfbClientPtr cl, int x, int y, int w, int h, uint32_t* colorPtr, rfbBool needSameColor) \ -{ \ - uint##bpp##_t *fbptr; \ - uint##bpp##_t colorValue; \ - int dx, dy; \ - \ - fbptr = (uint##bpp##_t *) \ - &cl->screen->frameBuffer[y * cl->screen->paddedWidthInBytes + x * (bpp/8)]; \ - \ - colorValue = *fbptr; \ - if (needSameColor && (uint32_t)colorValue != *colorPtr) \ - return FALSE; \ - \ - for (dy = 0; dy < h; dy++) { \ - for (dx = 0; dx < w; dx++) { \ - if (colorValue != fbptr[dx]) \ - return FALSE; \ - } \ - fbptr = (uint##bpp##_t *)((uint8_t *)fbptr + cl->screen->paddedWidthInBytes); \ - } \ - \ - *colorPtr = (uint32_t)colorValue; \ - return TRUE; \ -} - -DEFINE_CHECK_SOLID_FUNCTION(8) -DEFINE_CHECK_SOLID_FUNCTION(16) -DEFINE_CHECK_SOLID_FUNCTION(32) - -static rfbBool -SendRectSimple(cl, x, y, w, h) - rfbClientPtr cl; - int x, y, w, h; -{ - int maxBeforeSize, maxAfterSize; - int maxRectSize, maxRectWidth; - int subrectMaxWidth, subrectMaxHeight; - int dx, dy; - int rw, rh; - - maxRectSize = tightConf[compressLevel].maxRectSize; - maxRectWidth = tightConf[compressLevel].maxRectWidth; - - maxBeforeSize = maxRectSize * (cl->format.bitsPerPixel / 8); - maxAfterSize = maxBeforeSize + (maxBeforeSize + 99) / 100 + 12; - - if (tightBeforeBufSize < maxBeforeSize) { - tightBeforeBufSize = maxBeforeSize; - if (tightBeforeBuf == NULL) - tightBeforeBuf = (char *)malloc(tightBeforeBufSize); - else - tightBeforeBuf = (char *)realloc(tightBeforeBuf, - tightBeforeBufSize); - } - - if (tightAfterBufSize < maxAfterSize) { - tightAfterBufSize = maxAfterSize; - if (tightAfterBuf == NULL) - tightAfterBuf = (char *)malloc(tightAfterBufSize); - else - tightAfterBuf = (char *)realloc(tightAfterBuf, - tightAfterBufSize); - } - - if (w > maxRectWidth || w * h > maxRectSize) { - subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w; - subrectMaxHeight = maxRectSize / subrectMaxWidth; - - for (dy = 0; dy < h; dy += subrectMaxHeight) { - for (dx = 0; dx < w; dx += maxRectWidth) { - rw = (dx + maxRectWidth < w) ? maxRectWidth : w - dx; - rh = (dy + subrectMaxHeight < h) ? subrectMaxHeight : h - dy; - if (!SendSubrect(cl, x+dx, y+dy, rw, rh)) - return FALSE; - } - } - } else { - if (!SendSubrect(cl, x, y, w, h)) - return FALSE; - } - - return TRUE; -} - -static rfbBool -SendSubrect(cl, x, y, w, h) - rfbClientPtr cl; - int x, y, w, h; -{ - char *fbptr; - rfbBool success = FALSE; - - /* Send pending data if there is more than 128 bytes. */ - if (cl->ublen > 128) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - if (!SendTightHeader(cl, x, y, w, h)) - return FALSE; - - fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y) - + (x * (cl->screen->bitsPerPixel / 8))); - - (*cl->translateFn)(cl->translateLookupTable, &cl->screen->rfbServerFormat, - &cl->format, fbptr, tightBeforeBuf, - cl->screen->paddedWidthInBytes, w, h); - - paletteMaxColors = w * h / tightConf[compressLevel].idxMaxColorsDivisor; - if ( paletteMaxColors < 2 && - w * h >= tightConf[compressLevel].monoMinRectSize ) { - paletteMaxColors = 2; - } - switch (cl->format.bitsPerPixel) { - case 8: - FillPalette8(w * h); - break; - case 16: - FillPalette16(w * h); - break; - default: - FillPalette32(w * h); - } - - switch (paletteNumColors) { - case 0: - /* Truecolor image */ - if (DetectSmoothImage(cl, &cl->format, w, h)) { - if (qualityLevel != -1) { - success = SendJpegRect(cl, x, y, w, h, - tightConf[qualityLevel].jpegQuality); - } else { - success = SendGradientRect(cl, w, h); - } - } else { - success = SendFullColorRect(cl, w, h); - } - break; - case 1: - /* Solid rectangle */ - success = SendSolidRect(cl); - break; - case 2: - /* Two-color rectangle */ - success = SendMonoRect(cl, w, h); - break; - default: - /* Up to 256 different colors */ - if ( paletteNumColors > 96 && - qualityLevel != -1 && qualityLevel <= 3 && - DetectSmoothImage(cl, &cl->format, w, h) ) { - success = SendJpegRect(cl, x, y, w, h, - tightConf[qualityLevel].jpegQuality); - } else { - success = SendIndexedRect(cl, w, h); - } - } - return success; -} - -static rfbBool -SendTightHeader(cl, x, y, w, h) - rfbClientPtr cl; - int x, y, w, h; -{ - rfbFramebufferUpdateRectHeader rect; - - if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - rect.r.x = Swap16IfLE(x); - rect.r.y = Swap16IfLE(y); - rect.r.w = Swap16IfLE(w); - rect.r.h = Swap16IfLE(h); - rect.encoding = Swap32IfLE(rfbEncodingTight); - - memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, - sz_rfbFramebufferUpdateRectHeader); - cl->ublen += sz_rfbFramebufferUpdateRectHeader; - - cl->rfbRectanglesSent[rfbEncodingTight]++; - cl->rfbBytesSent[rfbEncodingTight] += sz_rfbFramebufferUpdateRectHeader; - - return TRUE; -} - -/* - * Subencoding implementations. - */ - -static rfbBool -SendSolidRect(cl) - rfbClientPtr cl; -{ - int len; - - if (usePixelFormat24) { - Pack24(cl, tightBeforeBuf, &cl->format, 1); - len = 3; - } else - len = cl->format.bitsPerPixel / 8; - - if (cl->ublen + 1 + len > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - cl->updateBuf[cl->ublen++] = (char)(rfbTightFill << 4); - memcpy (&cl->updateBuf[cl->ublen], tightBeforeBuf, len); - cl->ublen += len; - - cl->rfbBytesSent[rfbEncodingTight] += len + 1; - - return TRUE; -} - -static rfbBool -SendMonoRect(cl, w, h) - rfbClientPtr cl; - int w, h; -{ - int streamId = 1; - int paletteLen, dataLen; - - if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 + - 2 * cl->format.bitsPerPixel / 8 > UPDATE_BUF_SIZE ) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - /* Prepare tight encoding header. */ - dataLen = (w + 7) / 8; - dataLen *= h; - - cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4; - cl->updateBuf[cl->ublen++] = rfbTightFilterPalette; - cl->updateBuf[cl->ublen++] = 1; - - /* Prepare palette, convert image. */ - switch (cl->format.bitsPerPixel) { - - case 32: - EncodeMonoRect32((uint8_t *)tightBeforeBuf, w, h); - - ((uint32_t *)tightAfterBuf)[0] = monoBackground; - ((uint32_t *)tightAfterBuf)[1] = monoForeground; - if (usePixelFormat24) { - Pack24(cl, tightAfterBuf, &cl->format, 2); - paletteLen = 6; - } else - paletteLen = 8; - - memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteLen); - cl->ublen += paletteLen; - cl->rfbBytesSent[rfbEncodingTight] += 3 + paletteLen; - break; - - case 16: - EncodeMonoRect16((uint8_t *)tightBeforeBuf, w, h); - - ((uint16_t *)tightAfterBuf)[0] = (uint16_t)monoBackground; - ((uint16_t *)tightAfterBuf)[1] = (uint16_t)monoForeground; - - memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, 4); - cl->ublen += 4; - cl->rfbBytesSent[rfbEncodingTight] += 7; - break; - - default: - EncodeMonoRect8((uint8_t *)tightBeforeBuf, w, h); - - cl->updateBuf[cl->ublen++] = (char)monoBackground; - cl->updateBuf[cl->ublen++] = (char)monoForeground; - cl->rfbBytesSent[rfbEncodingTight] += 5; - } - - return CompressData(cl, streamId, dataLen, - tightConf[compressLevel].monoZlibLevel, - Z_DEFAULT_STRATEGY); -} - -static rfbBool -SendIndexedRect(cl, w, h) - rfbClientPtr cl; - int w, h; -{ - int streamId = 2; - int i, entryLen; - - if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 + - paletteNumColors * cl->format.bitsPerPixel / 8 > - UPDATE_BUF_SIZE ) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - /* Prepare tight encoding header. */ - cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4; - cl->updateBuf[cl->ublen++] = rfbTightFilterPalette; - cl->updateBuf[cl->ublen++] = (char)(paletteNumColors - 1); - - /* Prepare palette, convert image. */ - switch (cl->format.bitsPerPixel) { - - case 32: - EncodeIndexedRect32((uint8_t *)tightBeforeBuf, w * h); - - for (i = 0; i < paletteNumColors; i++) { - ((uint32_t *)tightAfterBuf)[i] = - palette.entry[i].listNode->rgb; - } - if (usePixelFormat24) { - Pack24(cl, tightAfterBuf, &cl->format, paletteNumColors); - entryLen = 3; - } else - entryLen = 4; - - memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * entryLen); - cl->ublen += paletteNumColors * entryLen; - cl->rfbBytesSent[rfbEncodingTight] += 3 + paletteNumColors * entryLen; - break; - - case 16: - EncodeIndexedRect16((uint8_t *)tightBeforeBuf, w * h); - - for (i = 0; i < paletteNumColors; i++) { - ((uint16_t *)tightAfterBuf)[i] = - (uint16_t)palette.entry[i].listNode->rgb; - } - - memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * 2); - cl->ublen += paletteNumColors * 2; - cl->rfbBytesSent[rfbEncodingTight] += 3 + paletteNumColors * 2; - break; - - default: - return FALSE; /* Should never happen. */ - } - - return CompressData(cl, streamId, w * h, - tightConf[compressLevel].idxZlibLevel, - Z_DEFAULT_STRATEGY); -} - -static rfbBool -SendFullColorRect(cl, w, h) - rfbClientPtr cl; - int w, h; -{ - int streamId = 0; - int len; - - if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - cl->updateBuf[cl->ublen++] = 0x00; /* stream id = 0, no flushing, no filter */ - cl->rfbBytesSent[rfbEncodingTight]++; - - if (usePixelFormat24) { - Pack24(cl, tightBeforeBuf, &cl->format, w * h); - len = 3; - } else - len = cl->format.bitsPerPixel / 8; - - return CompressData(cl, streamId, w * h * len, - tightConf[compressLevel].rawZlibLevel, - Z_DEFAULT_STRATEGY); -} - -static rfbBool -SendGradientRect(cl, w, h) - rfbClientPtr cl; - int w, h; -{ - int streamId = 3; - int len; - - if (cl->format.bitsPerPixel == 8) - return SendFullColorRect(cl, w, h); - - if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 2 > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - if (prevRowBuf == NULL) - prevRowBuf = (int *)malloc(2048 * 3 * sizeof(int)); - - cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4; - cl->updateBuf[cl->ublen++] = rfbTightFilterGradient; - cl->rfbBytesSent[rfbEncodingTight] += 2; - - if (usePixelFormat24) { - FilterGradient24(cl, tightBeforeBuf, &cl->format, w, h); - len = 3; - } else if (cl->format.bitsPerPixel == 32) { - FilterGradient32(cl, (uint32_t *)tightBeforeBuf, &cl->format, w, h); - len = 4; - } else { - FilterGradient16(cl, (uint16_t *)tightBeforeBuf, &cl->format, w, h); - len = 2; - } - - return CompressData(cl, streamId, w * h * len, - tightConf[compressLevel].gradientZlibLevel, - Z_FILTERED); -} - -static rfbBool -CompressData(cl, streamId, dataLen, zlibLevel, zlibStrategy) - rfbClientPtr cl; - int streamId, dataLen, zlibLevel, zlibStrategy; -{ - z_streamp pz; - int err; - - if (dataLen < TIGHT_MIN_TO_COMPRESS) { - memcpy(&cl->updateBuf[cl->ublen], tightBeforeBuf, dataLen); - cl->ublen += dataLen; - cl->rfbBytesSent[rfbEncodingTight] += dataLen; - return TRUE; - } - - pz = &cl->zsStruct[streamId]; - - /* Initialize compression stream if needed. */ - if (!cl->zsActive[streamId]) { - pz->zalloc = Z_NULL; - pz->zfree = Z_NULL; - pz->opaque = Z_NULL; - - err = deflateInit2 (pz, zlibLevel, Z_DEFLATED, MAX_WBITS, - MAX_MEM_LEVEL, zlibStrategy); - if (err != Z_OK) - return FALSE; - - cl->zsActive[streamId] = TRUE; - cl->zsLevel[streamId] = zlibLevel; - } - - /* Prepare buffer pointers. */ - pz->next_in = (Bytef *)tightBeforeBuf; - pz->avail_in = dataLen; - pz->next_out = (Bytef *)tightAfterBuf; - pz->avail_out = tightAfterBufSize; - - /* Change compression parameters if needed. */ - if (zlibLevel != cl->zsLevel[streamId]) { - if (deflateParams (pz, zlibLevel, zlibStrategy) != Z_OK) { - return FALSE; - } - cl->zsLevel[streamId] = zlibLevel; - } - - /* Actual compression. */ - if ( deflate (pz, Z_SYNC_FLUSH) != Z_OK || - pz->avail_in != 0 || pz->avail_out == 0 ) { - return FALSE; - } - - return SendCompressedData(cl, tightAfterBufSize - pz->avail_out); -} - -static rfbBool SendCompressedData(cl, compressedLen) - rfbClientPtr cl; - int compressedLen; -{ - int i, portionLen; - - cl->updateBuf[cl->ublen++] = compressedLen & 0x7F; - cl->rfbBytesSent[rfbEncodingTight]++; - if (compressedLen > 0x7F) { - cl->updateBuf[cl->ublen-1] |= 0x80; - cl->updateBuf[cl->ublen++] = compressedLen >> 7 & 0x7F; - cl->rfbBytesSent[rfbEncodingTight]++; - if (compressedLen > 0x3FFF) { - cl->updateBuf[cl->ublen-1] |= 0x80; - cl->updateBuf[cl->ublen++] = compressedLen >> 14 & 0xFF; - cl->rfbBytesSent[rfbEncodingTight]++; - } - } - - portionLen = UPDATE_BUF_SIZE; - for (i = 0; i < compressedLen; i += portionLen) { - if (i + portionLen > compressedLen) { - portionLen = compressedLen - i; - } - if (cl->ublen + portionLen > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - memcpy(&cl->updateBuf[cl->ublen], &tightAfterBuf[i], portionLen); - cl->ublen += portionLen; - } - cl->rfbBytesSent[rfbEncodingTight] += compressedLen; - - return TRUE; -} - -/* - * Code to determine how many different colors used in rectangle. - */ - -static void -FillPalette8(count) - int count; -{ - uint8_t *data = (uint8_t *)tightBeforeBuf; - uint8_t c0, c1; - int i, n0, n1; - - paletteNumColors = 0; - - c0 = data[0]; - for (i = 1; i < count && data[i] == c0; i++); - if (i == count) { - paletteNumColors = 1; - return; /* Solid rectangle */ - } - - if (paletteMaxColors < 2) - return; - - n0 = i; - c1 = data[i]; - n1 = 0; - for (i++; i < count; i++) { - if (data[i] == c0) { - n0++; - } else if (data[i] == c1) { - n1++; - } else - break; - } - if (i == count) { - if (n0 > n1) { - monoBackground = (uint32_t)c0; - monoForeground = (uint32_t)c1; - } else { - monoBackground = (uint32_t)c1; - monoForeground = (uint32_t)c0; - } - paletteNumColors = 2; /* Two colors */ - } -} - -#define DEFINE_FILL_PALETTE_FUNCTION(bpp) \ - \ -static void \ -FillPalette##bpp(count) \ - int count; \ -{ \ - uint##bpp##_t *data = (uint##bpp##_t *)tightBeforeBuf; \ - uint##bpp##_t c0, c1, ci; \ - int i, n0, n1, ni; \ - \ - c0 = data[0]; \ - for (i = 1; i < count && data[i] == c0; i++); \ - if (i >= count) { \ - paletteNumColors = 1; /* Solid rectangle */ \ - return; \ - } \ - \ - if (paletteMaxColors < 2) { \ - paletteNumColors = 0; /* Full-color encoding preferred */ \ - return; \ - } \ - \ - n0 = i; \ - c1 = data[i]; \ - n1 = 0; \ - for (i++; i < count; i++) { \ - ci = data[i]; \ - if (ci == c0) { \ - n0++; \ - } else if (ci == c1) { \ - n1++; \ - } else \ - break; \ - } \ - if (i >= count) { \ - if (n0 > n1) { \ - monoBackground = (uint32_t)c0; \ - monoForeground = (uint32_t)c1; \ - } else { \ - monoBackground = (uint32_t)c1; \ - monoForeground = (uint32_t)c0; \ - } \ - paletteNumColors = 2; /* Two colors */ \ - return; \ - } \ - \ - PaletteReset(); \ - PaletteInsert (c0, (uint32_t)n0, bpp); \ - PaletteInsert (c1, (uint32_t)n1, bpp); \ - \ - ni = 1; \ - for (i++; i < count; i++) { \ - if (data[i] == ci) { \ - ni++; \ - } else { \ - if (!PaletteInsert (ci, (uint32_t)ni, bpp)) \ - return; \ - ci = data[i]; \ - ni = 1; \ - } \ - } \ - PaletteInsert (ci, (uint32_t)ni, bpp); \ -} - -DEFINE_FILL_PALETTE_FUNCTION(16) -DEFINE_FILL_PALETTE_FUNCTION(32) - - -/* - * Functions to operate with palette structures. - */ - -#define HASH_FUNC16(rgb) ((int)(((rgb >> 8) + rgb) & 0xFF)) -#define HASH_FUNC32(rgb) ((int)(((rgb >> 16) + (rgb >> 8)) & 0xFF)) - -static void -PaletteReset(void) -{ - paletteNumColors = 0; - memset(palette.hash, 0, 256 * sizeof(COLOR_LIST *)); -} - -static int -PaletteInsert(rgb, numPixels, bpp) - uint32_t rgb; - int numPixels; - int bpp; -{ - COLOR_LIST *pnode; - COLOR_LIST *prev_pnode = NULL; - int hash_key, idx, new_idx, count; - - hash_key = (bpp == 16) ? HASH_FUNC16(rgb) : HASH_FUNC32(rgb); - - pnode = palette.hash[hash_key]; - - while (pnode != NULL) { - if (pnode->rgb == rgb) { - /* Such palette entry already exists. */ - new_idx = idx = pnode->idx; - count = palette.entry[idx].numPixels + numPixels; - if (new_idx && palette.entry[new_idx-1].numPixels < count) { - do { - palette.entry[new_idx] = palette.entry[new_idx-1]; - palette.entry[new_idx].listNode->idx = new_idx; - new_idx--; - } - while (new_idx && palette.entry[new_idx-1].numPixels < count); - palette.entry[new_idx].listNode = pnode; - pnode->idx = new_idx; - } - palette.entry[new_idx].numPixels = count; - return paletteNumColors; - } - prev_pnode = pnode; - pnode = pnode->next; - } - - /* Check if palette is full. */ - if (paletteNumColors == 256 || paletteNumColors == paletteMaxColors) { - paletteNumColors = 0; - return 0; - } - - /* Move palette entries with lesser pixel counts. */ - for ( idx = paletteNumColors; - idx > 0 && palette.entry[idx-1].numPixels < numPixels; - idx-- ) { - palette.entry[idx] = palette.entry[idx-1]; - palette.entry[idx].listNode->idx = idx; - } - - /* Add new palette entry into the freed slot. */ - pnode = &palette.list[paletteNumColors]; - if (prev_pnode != NULL) { - prev_pnode->next = pnode; - } else { - palette.hash[hash_key] = pnode; - } - pnode->next = NULL; - pnode->idx = idx; - pnode->rgb = rgb; - palette.entry[idx].listNode = pnode; - palette.entry[idx].numPixels = numPixels; - - return (++paletteNumColors); -} - - -/* - * Converting 32-bit color samples into 24-bit colors. - * Should be called only when redMax, greenMax and blueMax are 255. - * Color components assumed to be byte-aligned. - */ - -static void Pack24(cl, buf, fmt, count) - rfbClientPtr cl; - char *buf; - rfbPixelFormat *fmt; - int count; -{ - uint32_t *buf32; - uint32_t pix; - int r_shift, g_shift, b_shift; - - buf32 = (uint32_t *)buf; - - if (!cl->screen->rfbServerFormat.bigEndian == !fmt->bigEndian) { - r_shift = fmt->redShift; - g_shift = fmt->greenShift; - b_shift = fmt->blueShift; - } else { - r_shift = 24 - fmt->redShift; - g_shift = 24 - fmt->greenShift; - b_shift = 24 - fmt->blueShift; - } - - while (count--) { - pix = *buf32++; - *buf++ = (char)(pix >> r_shift); - *buf++ = (char)(pix >> g_shift); - *buf++ = (char)(pix >> b_shift); - } -} - - -/* - * Converting truecolor samples into palette indices. - */ - -#define DEFINE_IDX_ENCODE_FUNCTION(bpp) \ - \ -static void \ -EncodeIndexedRect##bpp(buf, count) \ - uint8_t *buf; \ - int count; \ -{ \ - COLOR_LIST *pnode; \ - uint##bpp##_t *src; \ - uint##bpp##_t rgb; \ - int rep = 0; \ - \ - src = (uint##bpp##_t *) buf; \ - \ - while (count--) { \ - rgb = *src++; \ - while (count && *src == rgb) { \ - rep++, src++, count--; \ - } \ - pnode = palette.hash[HASH_FUNC##bpp(rgb)]; \ - while (pnode != NULL) { \ - if ((uint##bpp##_t)pnode->rgb == rgb) { \ - *buf++ = (uint8_t)pnode->idx; \ - while (rep) { \ - *buf++ = (uint8_t)pnode->idx; \ - rep--; \ - } \ - break; \ - } \ - pnode = pnode->next; \ - } \ - } \ -} - -DEFINE_IDX_ENCODE_FUNCTION(16) -DEFINE_IDX_ENCODE_FUNCTION(32) - -#define DEFINE_MONO_ENCODE_FUNCTION(bpp) \ - \ -static void \ -EncodeMonoRect##bpp(buf, w, h) \ - uint8_t *buf; \ - int w, h; \ -{ \ - uint##bpp##_t *ptr; \ - uint##bpp##_t bg; \ - unsigned int value, mask; \ - int aligned_width; \ - int x, y, bg_bits; \ - \ - ptr = (uint##bpp##_t *) buf; \ - bg = (uint##bpp##_t) monoBackground; \ - aligned_width = w - w % 8; \ - \ - for (y = 0; y < h; y++) { \ - for (x = 0; x < aligned_width; x += 8) { \ - for (bg_bits = 0; bg_bits < 8; bg_bits++) { \ - if (*ptr++ != bg) \ - break; \ - } \ - if (bg_bits == 8) { \ - *buf++ = 0; \ - continue; \ - } \ - mask = 0x80 >> bg_bits; \ - value = mask; \ - for (bg_bits++; bg_bits < 8; bg_bits++) { \ - mask >>= 1; \ - if (*ptr++ != bg) { \ - value |= mask; \ - } \ - } \ - *buf++ = (uint8_t)value; \ - } \ - \ - mask = 0x80; \ - value = 0; \ - if (x >= w) \ - continue; \ - \ - for (; x < w; x++) { \ - if (*ptr++ != bg) { \ - value |= mask; \ - } \ - mask >>= 1; \ - } \ - *buf++ = (uint8_t)value; \ - } \ -} - -DEFINE_MONO_ENCODE_FUNCTION(8) -DEFINE_MONO_ENCODE_FUNCTION(16) -DEFINE_MONO_ENCODE_FUNCTION(32) - - -/* - * ``Gradient'' filter for 24-bit color samples. - * Should be called only when redMax, greenMax and blueMax are 255. - * Color components assumed to be byte-aligned. - */ - -static void -FilterGradient24(cl, buf, fmt, w, h) - rfbClientPtr cl; - char *buf; - rfbPixelFormat *fmt; - int w, h; -{ - uint32_t *buf32; - uint32_t pix32; - int *prevRowPtr; - int shiftBits[3]; - int pixHere[3], pixUpper[3], pixLeft[3], pixUpperLeft[3]; - int prediction; - int x, y, c; - - buf32 = (uint32_t *)buf; - memset (prevRowBuf, 0, w * 3 * sizeof(int)); - - if (!cl->screen->rfbServerFormat.bigEndian == !fmt->bigEndian) { - shiftBits[0] = fmt->redShift; - shiftBits[1] = fmt->greenShift; - shiftBits[2] = fmt->blueShift; - } else { - shiftBits[0] = 24 - fmt->redShift; - shiftBits[1] = 24 - fmt->greenShift; - shiftBits[2] = 24 - fmt->blueShift; - } - - for (y = 0; y < h; y++) { - for (c = 0; c < 3; c++) { - pixUpper[c] = 0; - pixHere[c] = 0; - } - prevRowPtr = prevRowBuf; - for (x = 0; x < w; x++) { - pix32 = *buf32++; - for (c = 0; c < 3; c++) { - pixUpperLeft[c] = pixUpper[c]; - pixLeft[c] = pixHere[c]; - pixUpper[c] = *prevRowPtr; - pixHere[c] = (int)(pix32 >> shiftBits[c] & 0xFF); - *prevRowPtr++ = pixHere[c]; - - prediction = pixLeft[c] + pixUpper[c] - pixUpperLeft[c]; - if (prediction < 0) { - prediction = 0; - } else if (prediction > 0xFF) { - prediction = 0xFF; - } - *buf++ = (char)(pixHere[c] - prediction); - } - } - } -} - - -/* - * ``Gradient'' filter for other color depths. - */ - -#define DEFINE_GRADIENT_FILTER_FUNCTION(bpp) \ - \ -static void \ -FilterGradient##bpp(cl, buf, fmt, w, h) \ - rfbClientPtr cl; \ - uint##bpp##_t *buf; \ - rfbPixelFormat *fmt; \ - int w, h; \ -{ \ - uint##bpp##_t pix, diff; \ - rfbBool endianMismatch; \ - int *prevRowPtr; \ - int maxColor[3], shiftBits[3]; \ - int pixHere[3], pixUpper[3], pixLeft[3], pixUpperLeft[3]; \ - int prediction; \ - int x, y, c; \ - \ - memset (prevRowBuf, 0, w * 3 * sizeof(int)); \ - \ - endianMismatch = (!cl->screen->rfbServerFormat.bigEndian != !fmt->bigEndian); \ - \ - maxColor[0] = fmt->redMax; \ - maxColor[1] = fmt->greenMax; \ - maxColor[2] = fmt->blueMax; \ - shiftBits[0] = fmt->redShift; \ - shiftBits[1] = fmt->greenShift; \ - shiftBits[2] = fmt->blueShift; \ - \ - for (y = 0; y < h; y++) { \ - for (c = 0; c < 3; c++) { \ - pixUpper[c] = 0; \ - pixHere[c] = 0; \ - } \ - prevRowPtr = prevRowBuf; \ - for (x = 0; x < w; x++) { \ - pix = *buf; \ - if (endianMismatch) { \ - pix = Swap##bpp(pix); \ - } \ - diff = 0; \ - for (c = 0; c < 3; c++) { \ - pixUpperLeft[c] = pixUpper[c]; \ - pixLeft[c] = pixHere[c]; \ - pixUpper[c] = *prevRowPtr; \ - pixHere[c] = (int)(pix >> shiftBits[c] & maxColor[c]); \ - *prevRowPtr++ = pixHere[c]; \ - \ - prediction = pixLeft[c] + pixUpper[c] - pixUpperLeft[c]; \ - if (prediction < 0) { \ - prediction = 0; \ - } else if (prediction > maxColor[c]) { \ - prediction = maxColor[c]; \ - } \ - diff |= ((pixHere[c] - prediction) & maxColor[c]) \ - << shiftBits[c]; \ - } \ - if (endianMismatch) { \ - diff = Swap##bpp(diff); \ - } \ - *buf++ = diff; \ - } \ - } \ -} - -DEFINE_GRADIENT_FILTER_FUNCTION(16) -DEFINE_GRADIENT_FILTER_FUNCTION(32) - - -/* - * Code to guess if given rectangle is suitable for smooth image - * compression (by applying "gradient" filter or JPEG coder). - */ - -#define JPEG_MIN_RECT_SIZE 4096 - -#define DETECT_SUBROW_WIDTH 7 -#define DETECT_MIN_WIDTH 8 -#define DETECT_MIN_HEIGHT 8 - -static int -DetectSmoothImage (cl, fmt, w, h) - rfbClientPtr cl; - rfbPixelFormat *fmt; - int w, h; -{ - long avgError; - - if ( cl->screen->rfbServerFormat.bitsPerPixel == 8 || fmt->bitsPerPixel == 8 || - w < DETECT_MIN_WIDTH || h < DETECT_MIN_HEIGHT ) { - return 0; - } - - if (qualityLevel != -1) { - if (w * h < JPEG_MIN_RECT_SIZE) { - return 0; - } - } else { - if ( rfbTightDisableGradient || - w * h < tightConf[compressLevel].gradientMinRectSize ) { - return 0; - } - } - - if (fmt->bitsPerPixel == 32) { - if (usePixelFormat24) { - avgError = DetectSmoothImage24(cl, fmt, w, h); - if (qualityLevel != -1) { - return (avgError < tightConf[qualityLevel].jpegThreshold24); - } - return (avgError < tightConf[compressLevel].gradientThreshold24); - } else { - avgError = DetectSmoothImage32(cl, fmt, w, h); - } - } else { - avgError = DetectSmoothImage16(cl, fmt, w, h); - } - if (qualityLevel != -1) { - return (avgError < tightConf[qualityLevel].jpegThreshold); - } - return (avgError < tightConf[compressLevel].gradientThreshold); -} - -static unsigned long -DetectSmoothImage24 (cl, fmt, w, h) - rfbClientPtr cl; - rfbPixelFormat *fmt; - int w, h; -{ - int off; - int x, y, d, dx, c; - int diffStat[256]; - int pixelCount = 0; - int pix, left[3]; - unsigned long avgError; - - /* If client is big-endian, color samples begin from the second - byte (offset 1) of a 32-bit pixel value. */ - off = (fmt->bigEndian != 0); - - memset(diffStat, 0, 256*sizeof(int)); - - y = 0, x = 0; - while (y < h && x < w) { - for (d = 0; d < h - y && d < w - x - DETECT_SUBROW_WIDTH; d++) { - for (c = 0; c < 3; c++) { - left[c] = (int)tightBeforeBuf[((y+d)*w+x+d)*4+off+c] & 0xFF; - } - for (dx = 1; dx <= DETECT_SUBROW_WIDTH; dx++) { - for (c = 0; c < 3; c++) { - pix = (int)tightBeforeBuf[((y+d)*w+x+d+dx)*4+off+c] & 0xFF; - diffStat[abs(pix - left[c])]++; - left[c] = pix; - } - pixelCount++; - } - } - if (w > h) { - x += h; - y = 0; - } else { - x = 0; - y += w; - } - } - - if (diffStat[0] * 33 / pixelCount >= 95) - return 0; - - avgError = 0; - for (c = 1; c < 8; c++) { - avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); - if (diffStat[c] == 0 || diffStat[c] > diffStat[c-1] * 2) - return 0; - } - for (; c < 256; c++) { - avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); - } - avgError /= (pixelCount * 3 - diffStat[0]); - - return avgError; -} - -#define DEFINE_DETECT_FUNCTION(bpp) \ - \ -static unsigned long \ -DetectSmoothImage##bpp (cl, fmt, w, h) \ - rfbClientPtr cl; \ - rfbPixelFormat *fmt; \ - int w, h; \ -{ \ - rfbBool endianMismatch; \ - uint##bpp##_t pix; \ - int maxColor[3], shiftBits[3]; \ - int x, y, d, dx, c; \ - int diffStat[256]; \ - int pixelCount = 0; \ - int sample, sum, left[3]; \ - unsigned long avgError; \ - \ - endianMismatch = (!cl->screen->rfbServerFormat.bigEndian != !fmt->bigEndian); \ - \ - maxColor[0] = fmt->redMax; \ - maxColor[1] = fmt->greenMax; \ - maxColor[2] = fmt->blueMax; \ - shiftBits[0] = fmt->redShift; \ - shiftBits[1] = fmt->greenShift; \ - shiftBits[2] = fmt->blueShift; \ - \ - memset(diffStat, 0, 256*sizeof(int)); \ - \ - y = 0, x = 0; \ - while (y < h && x < w) { \ - for (d = 0; d < h - y && d < w - x - DETECT_SUBROW_WIDTH; d++) { \ - pix = ((uint##bpp##_t *)tightBeforeBuf)[(y+d)*w+x+d]; \ - if (endianMismatch) { \ - pix = Swap##bpp(pix); \ - } \ - for (c = 0; c < 3; c++) { \ - left[c] = (int)(pix >> shiftBits[c] & maxColor[c]); \ - } \ - for (dx = 1; dx <= DETECT_SUBROW_WIDTH; dx++) { \ - pix = ((uint##bpp##_t *)tightBeforeBuf)[(y+d)*w+x+d+dx]; \ - if (endianMismatch) { \ - pix = Swap##bpp(pix); \ - } \ - sum = 0; \ - for (c = 0; c < 3; c++) { \ - sample = (int)(pix >> shiftBits[c] & maxColor[c]); \ - sum += abs(sample - left[c]); \ - left[c] = sample; \ - } \ - if (sum > 255) \ - sum = 255; \ - diffStat[sum]++; \ - pixelCount++; \ - } \ - } \ - if (w > h) { \ - x += h; \ - y = 0; \ - } else { \ - x = 0; \ - y += w; \ - } \ - } \ - \ - if ((diffStat[0] + diffStat[1]) * 100 / pixelCount >= 90) \ - return 0; \ - \ - avgError = 0; \ - for (c = 1; c < 8; c++) { \ - avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); \ - if (diffStat[c] == 0 || diffStat[c] > diffStat[c-1] * 2) \ - return 0; \ - } \ - for (; c < 256; c++) { \ - avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); \ - } \ - avgError /= (pixelCount - diffStat[0]); \ - \ - return avgError; \ -} - -DEFINE_DETECT_FUNCTION(16) -DEFINE_DETECT_FUNCTION(32) - - -/* - * JPEG compression stuff. - */ - -static struct jpeg_destination_mgr jpegDstManager; -static rfbBool jpegError; -static int jpegDstDataLen; - -static rfbBool -SendJpegRect(cl, x, y, w, h, quality) - rfbClientPtr cl; - int x, y, w, h; - int quality; -{ - struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr jerr; - uint8_t *srcBuf; - JSAMPROW rowPointer[1]; - int dy; - - if (cl->screen->rfbServerFormat.bitsPerPixel == 8) - return SendFullColorRect(cl, w, h); - - srcBuf = (uint8_t *)malloc(w * 3); - if (srcBuf == NULL) { - return SendFullColorRect(cl, w, h); - } - rowPointer[0] = srcBuf; - - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_compress(&cinfo); - - cinfo.image_width = w; - cinfo.image_height = h; - cinfo.input_components = 3; - cinfo.in_color_space = JCS_RGB; - - jpeg_set_defaults(&cinfo); - jpeg_set_quality(&cinfo, quality, TRUE); - - JpegSetDstManager (&cinfo); - - jpeg_start_compress(&cinfo, TRUE); - - for (dy = 0; dy < h; dy++) { - PrepareRowForJpeg(cl, srcBuf, x, y + dy, w); - jpeg_write_scanlines(&cinfo, rowPointer, 1); - if (jpegError) - break; - } - - if (!jpegError) - jpeg_finish_compress(&cinfo); - - jpeg_destroy_compress(&cinfo); - free(srcBuf); - - if (jpegError) - return SendFullColorRect(cl, w, h); - - if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4); - cl->rfbBytesSent[rfbEncodingTight]++; - - return SendCompressedData(cl, jpegDstDataLen); -} - -static void -PrepareRowForJpeg(cl, dst, x, y, count) - rfbClientPtr cl; - uint8_t *dst; - int x, y, count; -{ - if (cl->screen->rfbServerFormat.bitsPerPixel == 32) { - if ( cl->screen->rfbServerFormat.redMax == 0xFF && - cl->screen->rfbServerFormat.greenMax == 0xFF && - cl->screen->rfbServerFormat.blueMax == 0xFF ) { - PrepareRowForJpeg24(cl, dst, x, y, count); - } else { - PrepareRowForJpeg32(cl, dst, x, y, count); - } - } else { - /* 16 bpp assumed. */ - PrepareRowForJpeg16(cl, dst, x, y, count); - } -} - -static void -PrepareRowForJpeg24(cl, dst, x, y, count) - rfbClientPtr cl; - uint8_t *dst; - int x, y, count; -{ - uint32_t *fbptr; - uint32_t pix; - - fbptr = (uint32_t *) - &cl->screen->frameBuffer[y * cl->screen->paddedWidthInBytes + x * 4]; - - while (count--) { - pix = *fbptr++; - *dst++ = (uint8_t)(pix >> cl->screen->rfbServerFormat.redShift); - *dst++ = (uint8_t)(pix >> cl->screen->rfbServerFormat.greenShift); - *dst++ = (uint8_t)(pix >> cl->screen->rfbServerFormat.blueShift); - } -} - -#define DEFINE_JPEG_GET_ROW_FUNCTION(bpp) \ - \ -static void \ -PrepareRowForJpeg##bpp(cl, dst, x, y, count) \ - rfbClientPtr cl; \ - uint8_t *dst; \ - int x, y, count; \ -{ \ - uint##bpp##_t *fbptr; \ - uint##bpp##_t pix; \ - int inRed, inGreen, inBlue; \ - \ - fbptr = (uint##bpp##_t *) \ - &cl->screen->frameBuffer[y * cl->screen->paddedWidthInBytes + \ - x * (bpp / 8)]; \ - \ - while (count--) { \ - pix = *fbptr++; \ - \ - inRed = (int) \ - (pix >> cl->screen->rfbServerFormat.redShift & cl->screen->rfbServerFormat.redMax); \ - inGreen = (int) \ - (pix >> cl->screen->rfbServerFormat.greenShift & cl->screen->rfbServerFormat.greenMax); \ - inBlue = (int) \ - (pix >> cl->screen->rfbServerFormat.blueShift & cl->screen->rfbServerFormat.blueMax); \ - \ - *dst++ = (uint8_t)((inRed * 255 + cl->screen->rfbServerFormat.redMax / 2) / \ - cl->screen->rfbServerFormat.redMax); \ - *dst++ = (uint8_t)((inGreen * 255 + cl->screen->rfbServerFormat.greenMax / 2) / \ - cl->screen->rfbServerFormat.greenMax); \ - *dst++ = (uint8_t)((inBlue * 255 + cl->screen->rfbServerFormat.blueMax / 2) / \ - cl->screen->rfbServerFormat.blueMax); \ - } \ -} - -DEFINE_JPEG_GET_ROW_FUNCTION(16) -DEFINE_JPEG_GET_ROW_FUNCTION(32) - -/* - * Destination manager implementation for JPEG library. - */ - -static void -JpegInitDestination(j_compress_ptr cinfo) -{ - jpegError = FALSE; - jpegDstManager.next_output_byte = (JOCTET *)tightAfterBuf; - jpegDstManager.free_in_buffer = (size_t)tightAfterBufSize; -} - -static boolean -JpegEmptyOutputBuffer(j_compress_ptr cinfo) -{ - jpegError = TRUE; - jpegDstManager.next_output_byte = (JOCTET *)tightAfterBuf; - jpegDstManager.free_in_buffer = (size_t)tightAfterBufSize; - - return TRUE; -} - -static void -JpegTermDestination(j_compress_ptr cinfo) -{ - jpegDstDataLen = tightAfterBufSize - jpegDstManager.free_in_buffer; -} - -static void -JpegSetDstManager(j_compress_ptr cinfo) -{ - jpegDstManager.init_destination = JpegInitDestination; - jpegDstManager.empty_output_buffer = JpegEmptyOutputBuffer; - jpegDstManager.term_destination = JpegTermDestination; - cinfo->dest = &jpegDstManager; -} - diff --git a/translate.c b/translate.c deleted file mode 100644 index d5f0896..0000000 --- a/translate.c +++ /dev/null @@ -1,484 +0,0 @@ -/* - * translate.c - translate between different pixel formats - */ - -/* - * OSXvnc Copyright (C) 2001 Dan McGuirk . - * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. - * 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. - */ - -#include -#include - -static void PrintPixelFormat(rfbPixelFormat *pf); -static rfbBool rfbSetClientColourMapBGR233(rfbClientPtr cl); - -rfbBool rfbEconomicTranslate = FALSE; - -/* - * Some standard pixel formats. - */ - -static const rfbPixelFormat BGR233Format = { - 8, 8, 0, 1, 7, 7, 3, 0, 3, 6, 0, 0 -}; - - -/* - * Macro to compare pixel formats. - */ - -#define PF_EQ(x,y) \ - ((x.bitsPerPixel == y.bitsPerPixel) && \ - (x.depth == y.depth) && \ - ((x.bigEndian == y.bigEndian) || (x.bitsPerPixel == 8)) && \ - (x.trueColour == y.trueColour) && \ - (!x.trueColour || ((x.redMax == y.redMax) && \ - (x.greenMax == y.greenMax) && \ - (x.blueMax == y.blueMax) && \ - (x.redShift == y.redShift) && \ - (x.greenShift == y.greenShift) && \ - (x.blueShift == y.blueShift)))) - -#define CONCAT2(a,b) a##b -#define CONCAT2E(a,b) CONCAT2(a,b) -#define CONCAT3(a,b,c) a##b##c -#define CONCAT3E(a,b,c) CONCAT3(a,b,c) -#define CONCAT4(a,b,c,d) a##b##c##d -#define CONCAT4E(a,b,c,d) CONCAT4(a,b,c,d) - -#undef OUT -#undef IN - -#define OUT 8 -#include "tableinitcmtemplate.c" -#include "tableinittctemplate.c" -#define IN 8 -#include "tabletranstemplate.c" -#undef IN -#define IN 16 -#include "tabletranstemplate.c" -#undef IN -#define IN 32 -#include "tabletranstemplate.c" -#undef IN -#undef OUT - -#define OUT 16 -#include "tableinitcmtemplate.c" -#include "tableinittctemplate.c" -#define IN 8 -#include "tabletranstemplate.c" -#undef IN -#define IN 16 -#include "tabletranstemplate.c" -#undef IN -#define IN 32 -#include "tabletranstemplate.c" -#undef IN -#undef OUT - -#define OUT 32 -#include "tableinitcmtemplate.c" -#include "tableinittctemplate.c" -#define IN 8 -#include "tabletranstemplate.c" -#undef IN -#define IN 16 -#include "tabletranstemplate.c" -#undef IN -#define IN 32 -#include "tabletranstemplate.c" -#undef IN -#undef OUT - -#ifdef LIBVNCSERVER_ALLOW24BPP -#define COUNT_OFFSETS 4 -#define BPP2OFFSET(bpp) ((bpp)/8-1) -#include "tableinit24.c" -#define BPP 8 -#include "tabletrans24template.c" -#undef BPP -#define BPP 16 -#include "tabletrans24template.c" -#undef BPP -#define BPP 24 -#include "tabletrans24template.c" -#undef BPP -#define BPP 32 -#include "tabletrans24template.c" -#undef BPP -#else -#define COUNT_OFFSETS 3 -#define BPP2OFFSET(bpp) ((int)(bpp)/16) -#endif - -typedef void (*rfbInitCMTableFnType)(char **table, rfbPixelFormat *in, - rfbPixelFormat *out,rfbColourMap* cm); -typedef void (*rfbInitTableFnType)(char **table, rfbPixelFormat *in, - rfbPixelFormat *out); - -rfbInitCMTableFnType rfbInitColourMapSingleTableFns[COUNT_OFFSETS] = { - rfbInitColourMapSingleTable8, - rfbInitColourMapSingleTable16, -#ifdef LIBVNCSERVER_ALLOW24BPP - rfbInitColourMapSingleTable24, -#endif - rfbInitColourMapSingleTable32 -}; - -rfbInitTableFnType rfbInitTrueColourSingleTableFns[COUNT_OFFSETS] = { - rfbInitTrueColourSingleTable8, - rfbInitTrueColourSingleTable16, -#ifdef LIBVNCSERVER_ALLOW24BPP - rfbInitTrueColourSingleTable24, -#endif - rfbInitTrueColourSingleTable32 -}; - -rfbInitTableFnType rfbInitTrueColourRGBTablesFns[COUNT_OFFSETS] = { - rfbInitTrueColourRGBTables8, - rfbInitTrueColourRGBTables16, -#ifdef LIBVNCSERVER_ALLOW24BPP - rfbInitTrueColourRGBTables24, -#endif - rfbInitTrueColourRGBTables32 -}; - -rfbTranslateFnType rfbTranslateWithSingleTableFns[COUNT_OFFSETS][COUNT_OFFSETS] = { - { rfbTranslateWithSingleTable8to8, - rfbTranslateWithSingleTable8to16, -#ifdef LIBVNCSERVER_ALLOW24BPP - rfbTranslateWithSingleTable8to24, -#endif - rfbTranslateWithSingleTable8to32 }, - { rfbTranslateWithSingleTable16to8, - rfbTranslateWithSingleTable16to16, -#ifdef LIBVNCSERVER_ALLOW24BPP - rfbTranslateWithSingleTable16to24, -#endif - rfbTranslateWithSingleTable16to32 }, -#ifdef LIBVNCSERVER_ALLOW24BPP - { rfbTranslateWithSingleTable24to8, - rfbTranslateWithSingleTable24to16, - rfbTranslateWithSingleTable24to24, - rfbTranslateWithSingleTable24to32 }, -#endif - { rfbTranslateWithSingleTable32to8, - rfbTranslateWithSingleTable32to16, -#ifdef LIBVNCSERVER_ALLOW24BPP - rfbTranslateWithSingleTable32to24, -#endif - rfbTranslateWithSingleTable32to32 } -}; - -rfbTranslateFnType rfbTranslateWithRGBTablesFns[COUNT_OFFSETS][COUNT_OFFSETS] = { - { rfbTranslateWithRGBTables8to8, - rfbTranslateWithRGBTables8to16, -#ifdef LIBVNCSERVER_ALLOW24BPP - rfbTranslateWithRGBTables8to24, -#endif - rfbTranslateWithRGBTables8to32 }, - { rfbTranslateWithRGBTables16to8, - rfbTranslateWithRGBTables16to16, -#ifdef LIBVNCSERVER_ALLOW24BPP - rfbTranslateWithRGBTables16to24, -#endif - rfbTranslateWithRGBTables16to32 }, -#ifdef LIBVNCSERVER_ALLOW24BPP - { rfbTranslateWithRGBTables24to8, - rfbTranslateWithRGBTables24to16, - rfbTranslateWithRGBTables24to24, - rfbTranslateWithRGBTables24to32 }, -#endif - { rfbTranslateWithRGBTables32to8, - rfbTranslateWithRGBTables32to16, -#ifdef LIBVNCSERVER_ALLOW24BPP - rfbTranslateWithRGBTables32to24, -#endif - rfbTranslateWithRGBTables32to32 } -}; - - - -/* - * rfbTranslateNone is used when no translation is required. - */ - -void -rfbTranslateNone(char *table, rfbPixelFormat *in, rfbPixelFormat *out, - char *iptr, char *optr, int bytesBetweenInputLines, - int width, int height) -{ - int bytesPerOutputLine = width * (out->bitsPerPixel / 8); - - while (height > 0) { - memcpy(optr, iptr, bytesPerOutputLine); - iptr += bytesBetweenInputLines; - optr += bytesPerOutputLine; - height--; - } -} - - -/* - * rfbSetTranslateFunction sets the translation function. - */ - -rfbBool -rfbSetTranslateFunction(cl) - rfbClientPtr cl; -{ - rfbLog("Pixel format for client %s:\n",cl->host); - PrintPixelFormat(&cl->format); - - /* - * Check that bits per pixel values are valid - */ - - if ((cl->screen->rfbServerFormat.bitsPerPixel != 8) && - (cl->screen->rfbServerFormat.bitsPerPixel != 16) && -#ifdef LIBVNCSERVER_ALLOW24BPP - (cl->screen->rfbServerFormat.bitsPerPixel != 24) && -#endif - (cl->screen->rfbServerFormat.bitsPerPixel != 32)) - { - rfbErr("%s: server bits per pixel not 8, 16 or 32 (is %d)\n", - "rfbSetTranslateFunction", - cl->screen->rfbServerFormat.bitsPerPixel); - rfbCloseClient(cl); - return FALSE; - } - - if ((cl->format.bitsPerPixel != 8) && - (cl->format.bitsPerPixel != 16) && -#ifdef LIBVNCSERVER_ALLOW24BPP - (cl->format.bitsPerPixel != 24) && -#endif - (cl->format.bitsPerPixel != 32)) - { - rfbErr("%s: client bits per pixel not 8, 16 or 32\n", - "rfbSetTranslateFunction"); - rfbCloseClient(cl); - return FALSE; - } - - if (!cl->format.trueColour && (cl->format.bitsPerPixel != 8)) { - rfbErr("rfbSetTranslateFunction: client has colour map " - "but %d-bit - can only cope with 8-bit colour maps\n", - cl->format.bitsPerPixel); - rfbCloseClient(cl); - return FALSE; - } - - /* - * bpp is valid, now work out how to translate - */ - - if (!cl->format.trueColour) { - /* - * truecolour -> colour map - * - * Set client's colour map to BGR233, then effectively it's - * truecolour as well - */ - - if (!rfbSetClientColourMapBGR233(cl)) - return FALSE; - - cl->format = BGR233Format; - } - - /* truecolour -> truecolour */ - - if (PF_EQ(cl->format,cl->screen->rfbServerFormat)) { - - /* client & server the same */ - - rfbLog("no translation needed\n"); - cl->translateFn = rfbTranslateNone; - return TRUE; - } - - if ((cl->screen->rfbServerFormat.bitsPerPixel < 16) || - ((!cl->screen->rfbServerFormat.trueColour || !rfbEconomicTranslate) && - (cl->screen->rfbServerFormat.bitsPerPixel == 16))) { - - /* we can use a single lookup table for <= 16 bpp */ - - cl->translateFn = rfbTranslateWithSingleTableFns - [BPP2OFFSET(cl->screen->rfbServerFormat.bitsPerPixel)] - [BPP2OFFSET(cl->format.bitsPerPixel)]; - - if(cl->screen->rfbServerFormat.trueColour) - (*rfbInitTrueColourSingleTableFns - [BPP2OFFSET(cl->format.bitsPerPixel)]) (&cl->translateLookupTable, - &(cl->screen->rfbServerFormat), &cl->format); - else - (*rfbInitColourMapSingleTableFns - [BPP2OFFSET(cl->format.bitsPerPixel)]) (&cl->translateLookupTable, - &(cl->screen->rfbServerFormat), &cl->format,&cl->screen->colourMap); - - } else { - - /* otherwise we use three separate tables for red, green and blue */ - - cl->translateFn = rfbTranslateWithRGBTablesFns - [BPP2OFFSET(cl->screen->rfbServerFormat.bitsPerPixel)] - [BPP2OFFSET(cl->format.bitsPerPixel)]; - - (*rfbInitTrueColourRGBTablesFns - [BPP2OFFSET(cl->format.bitsPerPixel)]) (&cl->translateLookupTable, - &(cl->screen->rfbServerFormat), &cl->format); - } - - return TRUE; -} - - - -/* - * rfbSetClientColourMapBGR233 sets the client's colour map so that it's - * just like an 8-bit BGR233 true colour client. - */ - -static rfbBool -rfbSetClientColourMapBGR233(cl) - rfbClientPtr cl; -{ - char buf[sz_rfbSetColourMapEntriesMsg + 256 * 3 * 2]; - rfbSetColourMapEntriesMsg *scme = (rfbSetColourMapEntriesMsg *)buf; - uint16_t *rgb = (uint16_t *)(&buf[sz_rfbSetColourMapEntriesMsg]); - int i, len; - int r, g, b; - - if (cl->format.bitsPerPixel != 8 ) { - rfbErr("%s: client not 8 bits per pixel\n", - "rfbSetClientColourMapBGR233"); - rfbCloseClient(cl); - return FALSE; - } - - scme->type = rfbSetColourMapEntries; - - scme->firstColour = Swap16IfLE(0); - scme->nColours = Swap16IfLE(256); - - len = sz_rfbSetColourMapEntriesMsg; - - i = 0; - - for (b = 0; b < 4; b++) { - for (g = 0; g < 8; g++) { - for (r = 0; r < 8; r++) { - rgb[i++] = Swap16IfLE(r * 65535 / 7); - rgb[i++] = Swap16IfLE(g * 65535 / 7); - rgb[i++] = Swap16IfLE(b * 65535 / 3); - } - } - } - - len += 256 * 3 * 2; - - if (WriteExact(cl, buf, len) < 0) { - rfbLogPerror("rfbSetClientColourMapBGR233: write"); - rfbCloseClient(cl); - return FALSE; - } - return TRUE; -} - -/* this function is not called very often, so it needn't be - efficient. */ - -/* - * rfbSetClientColourMap is called to set the client's colour map. If the - * client is a true colour client, we simply update our own translation table - * and mark the whole screen as having been modified. - */ - -rfbBool -rfbSetClientColourMap(cl, firstColour, nColours) - rfbClientPtr cl; - int firstColour; - int nColours; -{ - if (cl->screen->rfbServerFormat.trueColour || !cl->readyForSetColourMapEntries) { - return TRUE; - } - - if (nColours == 0) { - nColours = cl->screen->colourMap.count; - } - - if (cl->format.trueColour) { - (*rfbInitColourMapSingleTableFns - [BPP2OFFSET(cl->format.bitsPerPixel)]) (&cl->translateLookupTable, - &cl->screen->rfbServerFormat, &cl->format,&cl->screen->colourMap); - - sraRgnDestroy(cl->modifiedRegion); - cl->modifiedRegion = - sraRgnCreateRect(0,0,cl->screen->width,cl->screen->height); - - return TRUE; - } - - return rfbSendSetColourMapEntries(cl, firstColour, nColours); -} - - -/* - * rfbSetClientColourMaps sets the colour map for each RFB client. - */ - -void -rfbSetClientColourMaps(rfbScreen, firstColour, nColours) - rfbScreenInfoPtr rfbScreen; - int firstColour; - int nColours; -{ - rfbClientIteratorPtr i; - rfbClientPtr cl; - - i = rfbGetClientIterator(rfbScreen); - while((cl = rfbClientIteratorNext(i))) - rfbSetClientColourMap(cl, firstColour, nColours); - rfbReleaseClientIterator(i); -} - -static void -PrintPixelFormat(pf) - rfbPixelFormat *pf; -{ - if (pf->bitsPerPixel == 1) { - rfbLog(" 1 bpp, %s sig bit in each byte is leftmost on the screen.\n", - (pf->bigEndian ? "most" : "least")); - } else { - rfbLog(" %d bpp, depth %d%s\n",pf->bitsPerPixel,pf->depth, - ((pf->bitsPerPixel == 8) ? "" - : (pf->bigEndian ? ", big endian" : ", little endian"))); - if (pf->trueColour) { - rfbLog(" true colour: max r %d g %d b %d, shift r %d g %d b %d\n", - pf->redMax, pf->greenMax, pf->blueMax, - pf->redShift, pf->greenShift, pf->blueShift); - } else { - rfbLog(" uses a colour map (not true colour).\n"); - } - } -} diff --git a/vncauth.c b/vncauth.c deleted file mode 100644 index 2146e67..0000000 --- a/vncauth.c +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) 1999 AT&T Laboratories Cambridge. 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 program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ - -/* - * vncauth.c - Functions for VNC password management and authentication. - */ - -#include -#include -#include -#include -#include "d3des.h" - -#include -#include - -#ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H -#include -#endif -#ifdef LIBVNCSERVER_HAVE_SYS_STAT_H -#include -#endif - -#include - -#ifdef WIN32 -#define srandom srand -#define random rand -#else -#include -#endif - - -/* - * We use a fixed key to store passwords, since we assume that our local - * file system is secure but nonetheless don't want to store passwords - * as plaintext. - */ - -static unsigned char fixedkey[8] = {23,82,107,6,35,78,88,7}; - - -/* - * Encrypt a password and store it in a file. Returns 0 if successful, - * 1 if the file could not be written. - */ - -int -vncEncryptAndStorePasswd(char *passwd, char *fname) -{ - FILE *fp; - unsigned int i; - unsigned char encryptedPasswd[8]; - - if ((fp = fopen(fname,"w")) == NULL) return 1; - - /* windows security sux */ -#ifndef WIN32 - fchmod(fileno(fp), S_IRUSR|S_IWUSR); -#endif - - /* pad password with nulls */ - - for (i = 0; i < 8; i++) { - if (i < strlen(passwd)) { - encryptedPasswd[i] = passwd[i]; - } else { - encryptedPasswd[i] = 0; - } - } - - /* Do encryption in-place - this way we overwrite our copy of the plaintext - password */ - - deskey(fixedkey, EN0); - des(encryptedPasswd, encryptedPasswd); - - for (i = 0; i < 8; i++) { - putc(encryptedPasswd[i], fp); - } - - fclose(fp); - return 0; -} - - -/* - * Decrypt a password from a file. Returns a pointer to a newly allocated - * string containing the password or a null pointer if the password could - * not be retrieved for some reason. - */ - -char * -vncDecryptPasswdFromFile(char *fname) -{ - FILE *fp; - int i, ch; - unsigned char *passwd = (unsigned char *)malloc(9); - - if ((fp = fopen(fname,"r")) == NULL) return NULL; - - for (i = 0; i < 8; i++) { - ch = getc(fp); - if (ch == EOF) { - fclose(fp); - return NULL; - } - passwd[i] = ch; - } - - fclose(fp); - - deskey(fixedkey, DE1); - des(passwd, passwd); - - passwd[8] = 0; - - return (char *)passwd; -} - - -/* - * Generate CHALLENGESIZE random bytes for use in challenge-response - * authentication. - */ - -void -vncRandomBytes(unsigned char *bytes) -{ - int i; - static rfbBool s_srandom_called = FALSE; - - if (!s_srandom_called) { - srandom((unsigned int)time(0) ^ (unsigned int)getpid()); - s_srandom_called = TRUE; - } - - for (i = 0; i < CHALLENGESIZE; i++) { - bytes[i] = (unsigned char)(random() & 255); - } -} - - -/* - * Encrypt CHALLENGESIZE bytes in memory using a password. - */ - -void -vncEncryptBytes(unsigned char *bytes, char *passwd) -{ - unsigned char key[8]; - unsigned int i; - - /* key is simply password padded with nulls */ - - for (i = 0; i < 8; i++) { - if (i < strlen(passwd)) { - key[i] = passwd[i]; - } else { - key[i] = 0; - } - } - - deskey(key, EN0); - - for (i = 0; i < CHALLENGESIZE; i += 8) { - des(bytes+i, bytes+i); - } -} diff --git a/vncterm/Makefile.am b/vncterm/Makefile.am index 5588b03..3a631ab 100644 --- a/vncterm/Makefile.am +++ b/vncterm/Makefile.am @@ -4,8 +4,8 @@ noinst_HEADERS=VNConsole.h vga.h CFLAGS_ADD=-I.. -LDADD=../libvncserver.a -INCLUDES=-I. -I../include +LDADD=../libvncserver/libvncserver.a +INCLUDES=-I. if LINUX bin_PROGRAMS=LinuxVNC diff --git a/x11vnc/ChangeLog b/x11vnc/ChangeLog new file mode 100644 index 0000000..9e468f6 --- /dev/null +++ b/x11vnc/ChangeLog @@ -0,0 +1,143 @@ +2004-05-21 Karl Runge + * -accept: add view-only decision and other improvements. + * add -gone command option for when a client leaves. + Thanks to Jesus Alvarez for these ideas. + * -passwdfile to keep passwd off of cmd line. + * -o logfile send stderr to a logfile. + +2004-05-14 Karl Runge + * improvements to -accept popup: yes/no buttons and timeout. + * less fprintf under -q so '-q -inetd' has no stderr output. + +2004-05-08 Karl Runge + * add -accept some-command/xmessage/popup to prompt local X11 user + or otherwise decide to accept an incoming client. + * clean up -Wall warnings. + +2004-05-05 Karl Runge + * enable mouse button -> keystrokes mapping in -buttonmap (mousewheel) + * enable keystroke -> mouse button mapping in -remap (touchpad paste) + (-remap incompat ':' -> '-', sorry...) + * shm OS blacklist (i.e. <= SunOS 5.8) -> -onetile + * revert to check_user_input() under -nofb + * cleanup: lastmod, remove tile_shm and update_client_pointer, + debug output, rfbPort failure. + * user friendly last line: 'The VNC desktop is hostname:0' + +2004-04-28 Karl Runge + * -auth cmdline option for xauthority. + * decrease default deferupdate under -nofb. + * update_client_pointer() from Edoardo Tirtarahardja. + * remove some assumptions about libvncserver defaults. + +2004-04-19 Karl Runge + * support for cursor positions updates -cursorpos + * option for SIGPIPE handling -sigpipe + +2004-04-13 Karl Runge + * solve problem with sending selection when client initializing + (not yet in RFB_NORMAL state). Increase delay to 15s as well. + * when threaded: limit rfbMaxClientWait to >= 20 secs and + increase it to a huge value unless -rfbwait is supplied. + +2004-04-08 Karl Runge + * added support for blacking out regions of the screen, primarily + for Xinerama usage, options: -blackout -xinerama + * Xinerama workaround mouse problem on 'embedded' system, + option -xwarppointer (XWarpPointer instead of XTEST) + * let -remap option take key remappings on cmdline as well as file. + * use cargs fix to test for invalid cmdline options. Add --option. + * remove copy_tile, use copy_tiles(..., 1) instead. + +2004-03-10 Karl Runge + * added reverse connection for vncconnect(1) and other means + -vncconnect, -connect host:port, and -connect watchfile + * added first pass at user keysym remapping feature via + -remap file. Ignores modifier state, need to generalize. + * debugging options for users -debug_pointer and -debug_keyboard + * clear -passwd from argv for privacy (if OS allows). + +2004-02-19 Karl Runge + * added handling of clipboard/selection exchange to/from clients, + even holds PRIMARY which Xvnc does not do. disable with -nosel. + use -noprimary to disable polling of PRIMARY selection. + * added -visual option to force framebuffer visual. not really + of general use, more for testing and workarounds (e.g. win2vnc + fails under 8bpp index color) + * improve cleanup and error handling WRT shm and other failures. + +2004-01-19 Karl Runge + * improvements to pointer event handling primarily during window + dragging. check_user_input() for non-threaded and pointer() + for threaded. Revert to old way via -old_pointer option. + * some memory I/O improvement by using copy_tiles() instead + of copy_tile(). New one does rows of tiles at same time. + Revert to old way via -old_copytile. + * handle case of more mouse buttons on client than on X server. + * added -buttonmap option for finer control over button differences. + +2004-01-09 Karl Runge + * options -allow / -localhost for simple IP based access screening + * option -nodragging to skip all screen updates during mouse drags + (thanks to Michal Sabala) + * option -input_skip to allow users to tune watch_loop dropthru rate + * try to avoid wasting RAM for framebuffer under -nofb + * cleanup wrt bpp vs. depth + +2003-12-08 Karl Runge + * add Xbell support using XKEYBOARD extension (disable: -nobell) + * add "-nofb" to disable framebuffer, i.e. mouse + keyboard only (!) + * add "-notruecolor" to force indexed 8bpp color (when 8bpp) + * make alias "-forever" for "-many" + +From Karl (x11vnc's father) on Apr 2, 2003: + +New option -nocursor to not display the vncviewer local cursor if user +does not want it (also caused some problems with older vncviewers) + +New option -mouse to show the position of the X server mouse (i.e. lagged +from the user's vnc cursor position). Also: -mouseX will try to show +the a different cursor (X) when on the root background. + +New option -many to wait for more connections rather than exiting when +the first client(s) disconnect. + +New option -flashcmap to try to follow installed colormaps under 8bpp +indexed color as pointer is moved. + +New option -nap to watch for low activity and throttle down the polling +rate. Useful on shared machines to keep the load down. + +Experimental option -id to show just that window and not +the whole display. Some remaining bugs and inconvenient behavior... +(e.g. new toplevels can be unseen) + +Fixed bug on multi-headed machines where the screen number was being +ignored in a number of places. + +Fixed bug wrt connect_once mode. Now just refuses new clients unless +shared rather than terminating all clients. + +Try to follow changing default colormap under 8bpp indexed color +as color cells are added. + +Needed to pick up HAVE_LIBPTHREAD from autoconf. + +defined a select() macro for usleep() since usleep is not always thread +safe. + +Catch and exit on errors in the shm setup work (XShmCreateImage, shmget,...) +and moved the creation and removal work to separate utility functions. + +Added signal and X error handlers to try to clean out the shm objects +before exiting on interrupt, etc. + +Improved performance a bit on the memcmp() in scan_display() by checking +the whole line first. + +Added a workaround when threaded where libvncserver may disconnect too +early if it does not hear from a client (a small heartbeat is sent). +This may not be needed any longer. + +If -desktop has not been prescribed, try to choose a title based on DISPLAY +and the hostname (and window name under -id). diff --git a/x11vnc/Makefile.am b/x11vnc/Makefile.am new file mode 100644 index 0000000..d06d694 --- /dev/null +++ b/x11vnc/Makefile.am @@ -0,0 +1,16 @@ +CFLAGS = -I .. +LDADD = ../libvncserver/libvncserver.a + +EXTRA_DIST=ChangeLog + +if CYGIPC +LD_CYGIPC=-lcygipc +endif + +if HAVE_X +bin_PROGRAMS=x11vnc +x11vnc_SOURCES=x11vnc.c +INCLUDES=@X_CFLAGS@ +x11vnc_LDADD=@X_LIBS@ $(LD_CYGIPC) $(LDADD) +endif + diff --git a/x11vnc/x11vnc.c b/x11vnc/x11vnc.c new file mode 100644 index 0000000..144b280 --- /dev/null +++ b/x11vnc/x11vnc.c @@ -0,0 +1,6742 @@ +/* + * x11vnc.c: a VNC server for X displays. + * + * Copyright (c) 2002-2003 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: + * + * the originial x11vnc.c in libvncserver (Johannes E. Schindelin) + * krfb, the KDE desktopsharing project (Tim Jansen) + * x0rfbserver, the original native X vnc server (Jens Wagner) + * + * The primary goal of this program is to create a portable and simple + * command-line server utility that allows a VNC viewer to connect to an + * actual X display (as the above do). The only non-standard dependency + * of this program is the static library libvncserver.a (although in + * some environments libjpeg.so may not be readily available and needs + * to be installed, it may be found at ftp://ftp.uu.net/graphics/jpeg/). + * To increase portability it is written in plain C. + * + * The next goal is to improve performance and interactive response. + * The algorithm currently used here to achieve this is that of krfb + * (based on x0rfbserver algorithm). Additional heuristics are also + * applied (currently there are a bit too many of these...) + * + * To build: + * + * Obtain the libvncserver package (http://libvncserver.sourceforge.net). + * As of 12/2002 this version of x11vnc.c is contained in the libvncserver + * CVS tree and released in version 0.5. + * + * gcc should be used on all platforms. To build a threaded version put + * "-D_REENTRANT -DX11VNC_THREADED" in the environment variable CFLAGS + * or CPPFLAGS (e.g. before running the libvncserver configure). The + * threaded mode is a bit more responsive, but can be unstable. + * + * Known shortcomings: + * + * The screen updates are good, but of course not perfect since the X + * display must be continuously polled and read for changes (as opposed to + * receiving a change callback from the X server, if that were generally + * possible...). So, e.g., opaque moves and similar window activity + * can be very painful; one has to modify one's behavior a bit. + * + * General audio at the remote display is lost unless one separately + * sets up some audio side-channel such as esd. + * + * It does not appear possible to query the X server for the current + * cursor shape. We can use XTest to compare cursor to current window's + * cursor, but we cannot extract what the cursor is... + * + * Nevertheless, the current *position* of the remote X mouse pointer + * is shown with the -mouse option. Further, if -mouseX or -X is used, a + * trick is done to at least show the root window cursor vs non-root cursor. + * (perhaps some heuristic can be done to further distinguish cases...) + * + * With -mouse there are occasionally some repainting errors involving + * big areas near the cursor. The mouse painting is in general a bit + * ragged and not very pleasant. + * + * Windows using visuals other than the default X visual may have + * their colors messed up. When using 8bpp indexed color, the colormap + * is attempted to be followed, but may become out of date. Use the + * -flashcmap option to have colormap flashing as the pointer moves + * windows with private colormaps (slow). Displays with mixed depth 8 and + * 24 visuals will incorrect display the non-default one. + * + * Feature -id can be picky: it can crash for things like the + * window not sufficiently mapped into server memory, use of -mouse, etc. + * SaveUnders menus, popups, etc will not be seen. + * + * Occasionally, a few tile updates can be missed leaving a patch of + * color that needs to be refreshed. + * + * There seems to be a serious bug with simultaneous clients when + * threaded, currently the only workaround in this case is -nothreads. + * + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#ifdef LIBVNCSERVER_HAVE_XKEYBOARD +#include +#endif + +/* + * Temporary kludge: to run with -xinerama define the following macro + * and be sure to link with * -lXinerama (e.g. LDFLAGS=-lXinerama before + * configure). Support for this is being added to libvncserver 'configure.ac' + * so it will all be done automatically. + +#define LIBVNCSERVER_HAVE_LIBXINERAMA + */ +#ifdef LIBVNCSERVER_HAVE_LIBXINERAMA +#include +#endif + +/* date +'"lastmod: %Y-%m-%d";' */ +char lastmod[] = "lastmod: 2004-05-21"; + + +/* X and rfb framebuffer */ +Display *dpy = 0; +Visual *visual; +Window window, rootwin; +int scr; +int bpp, depth; +int button_mask = 0; +int dpy_x, dpy_y; +int off_x, off_y; +int subwin = 0; +int indexed_colour = 0; + +XImage *tile; +XImage **tile_row; /* for all possible row runs */ +XImage *scanline; +XImage *fullscreen; +int fs_factor = 0; + +XShmSegmentInfo *tile_row_shm; /* for all possible row runs */ +XShmSegmentInfo scanline_shm; +XShmSegmentInfo fullscreen_shm; + +rfbScreenInfoPtr screen; +rfbCursorPtr cursor; +int bytes_per_line; + +/* size of the basic tile unit that is polled for changes: */ +int tile_x = 32; +int tile_y = 32; +int ntiles, ntiles_x, ntiles_y; + +/* arrays that indicate changed or checked tiles. */ +unsigned char *tile_has_diff, *tile_tried; + +/* blacked-out region things */ +typedef struct bout { + int x1, y1, x2, y2; +} blackout_t; +typedef struct tbout { + blackout_t bo[10]; /* hardwired max rectangles. */ + int cover; + int count; +} tile_blackout_t; +blackout_t black[100]; /* hardwired max blackouts */ +int blackouts = 0; +tile_blackout_t *tile_blackout; + + +typedef struct tile_change_region { + /* start and end lines, along y, of the changed area inside a tile. */ + unsigned short first_line, last_line; + /* info about differences along edges. */ + unsigned short left_diff, right_diff; + unsigned short top_diff, bot_diff; +} region_t; + +/* array to hold the tiles region_t-s. */ +region_t *tile_region; + +typedef struct hint { + /* location x, y, height, and width of a change-rectangle */ + /* (grows as adjacent horizontal tiles are glued together) */ + int x, y, w, h; +} hint_t; + +/* array to hold the hints: */ +hint_t *hint_list; + +/* various command line options */ + +int shared = 0; /* share vnc display. */ +char *allow_list = NULL; /* for -allow and -localhost */ +char *accept_cmd = NULL; /* for -accept */ +char *gone_cmd = NULL; /* for -gone */ +int view_only = 0; /* clients can only watch. */ +int inetd = 0; /* spawned from inetd(1) */ +int connect_once = 1; /* disconnect after first connection session. */ +int flash_cmap = 0; /* follow installed colormaps */ +int force_indexed_color = 0; /* whether to force indexed color for 8bpp */ + +int use_modifier_tweak = 0; /* use the altgr_keyboard modifier tweak */ +char *remap_file = NULL; /* user supplied remapping file or list */ +int nofb = 0; /* do not send any fb updates */ + +char *blackout_string = NULL; /* -blackout */ +int xinerama = 0; /* -xinerama */ + +char *client_connect = NULL; /* strings for -connect option */ +char *client_connect_file = NULL; +int vnc_connect = 0; /* -vncconnect option */ + +int local_cursor = 1; /* whether the viewer draws a local cursor */ +int cursor_pos = 0; /* cursor position updates -cursorpos */ +int show_mouse = 0; /* display a cursor for the real mouse */ +int use_xwarppointer = 0; /* use XWarpPointer instead of XTestFake... */ +int show_root_cursor = 0; /* show X when on root background */ +int show_dragging = 1; /* process mouse movement events */ +int watch_bell = 1; /* watch for the bell using XKEYBOARD */ + +int old_pointer = 0; /* use the old way of updating the pointer */ +int single_copytile = 0; /* use the old way copy_tiles() */ + +int using_shm = 1; /* whether mit-shm is used */ +int flip_byte_order = 0; /* sometimes needed when using_shm = 0 */ +/* + * waitms is the msec to wait between screen polls. Not too old h/w shows + * poll times of 10-35ms, so maybe this value cuts the idle load by 2 or so. + */ +int waitms = 30; +int defer_update = 30; /* rfbDeferUpdateTime ms to wait before sends. */ +int defer_update_nofb = 6; /* defer a shorter time under -nofb */ + +int screen_blank = 60; /* number of seconds of no activity to throttle */ + /* down the screen polls. zero to disable. */ +int take_naps = 0; +int naptile = 3; /* tile change threshold per poll to take a nap */ +int napfac = 4; /* time = napfac*waitms, cut load with extra waits */ +int napmax = 1500; /* longest nap in ms. */ +int ui_skip = 10; /* see watchloop. negative means ignore input */ + +/* for -visual override */ +VisualID visual_id = (VisualID) 0; +int visual_depth = 0; + +int nap_ok = 0, nap_diff_count = 0; +time_t last_event, last_input, last_client = 0; + +/* tile heuristics: */ +double fs_frac = 0.75; /* threshold tile fraction to do fullscreen updates. */ +int use_hints = 1; /* use the krfb scheme of gluing tiles together. */ +int tile_fuzz = 2; /* tolerance for suspecting changed tiles touching */ + /* a known changed tile. */ +int grow_fill = 3; /* do the grow islands heuristic with this width. */ +int gaps_fill = 4; /* do a final pass to try to fill gaps between tiles. */ + +/* scan pattern jitter from x0rfbserver */ +#define NSCAN 32 +int scanlines[NSCAN] = { + 0, 16, 8, 24, 4, 20, 12, 28, + 10, 26, 18, 2, 22, 6, 30, 14, + 1, 17, 9, 25, 7, 23, 15, 31, + 19, 3, 27, 11, 29, 13, 5, 21 +}; +int count = 0; /* indicates which scan pattern we are on */ + +int cursor_x, cursor_y; /* x and y from the viewer(s) */ +int got_user_input = 0; +int got_pointer_input = 0; +int got_keyboard_input = 0; +int scan_in_progress = 0; +int fb_copy_in_progress = 0; +int client_count = 0; +int shut_down = 0; +int sigpipe = 1; /* 0=skip, 1=ignore, 2=exit */ + +int debug_pointer = 0; +int debug_keyboard = 0; + +int quiet = 0; +double dtime(double *); +int all_clients_initialized(void); + +void zero_fb(int, int, int, int); + +#if defined(LIBVNCSERVER_X11VNC_THREADED) && ! defined(X11VNC_THREADED) +#define X11VNC_THREADED +#endif + +#if defined(LIBVNCSERVER_HAVE_LIBPTHREAD) && defined(X11VNC_THREADED) + int use_threads = 1; +#else + int use_threads = 0; +#endif + +/* XXX usleep(3) is not thread safe on some older systems... */ +struct timeval _mysleep; +#define usleep2(x) \ + _mysleep.tv_sec = (x) / 1000000; \ + _mysleep.tv_usec = (x) % 1000000; \ + select(0, NULL, NULL, NULL, &_mysleep); +#if !defined(X11VNC_USLEEP) +#undef usleep +#define usleep usleep2 +#endif + +/* + * Not sure why... but when threaded we have to mutex our X11 calls to + * avoid XIO crashes. + */ +MUTEX(x11Mutex); +#define X_LOCK LOCK(x11Mutex) +#define X_UNLOCK UNLOCK(x11Mutex) +#define X_INIT INIT_MUTEX(x11Mutex) + +/* + * Exiting and error handling: + */ +void shm_clean(XShmSegmentInfo *, XImage *); +void shm_delete(XShmSegmentInfo *); + +int exit_flag = 0; +void clean_up_exit (int ret) { + int i; + exit_flag = 1; + + /* remove the shm areas: */ + shm_clean(&scanline_shm, scanline); + shm_clean(&fullscreen_shm, fullscreen); + + for(i=1; i<=ntiles_x; i++) { + shm_clean(&tile_row_shm[i], tile_row[i]); + if (single_copytile && i >= single_copytile) { + break; + } + } + + X_LOCK; + XTestDiscard(dpy); + X_UNLOCK; + + fflush(stderr); + exit(ret); +} + +/* + * General problem handler + */ +void interrupted (int sig) { + int i; + if (exit_flag) { + exit_flag++; + if (use_threads) { + usleep2(250 * 1000); + } else if (exit_flag <= 2) { + return; + } + exit(4); + } + exit_flag++; + if (sig == 0) { + fprintf(stderr, "caught X11 error:\n"); + } else { + fprintf(stderr, "caught signal: %d\n", sig); + } + /* + * to avoid deadlock, etc, just delete the shm areas and + * leave the X stuff hanging. + */ + shm_delete(&scanline_shm); + shm_delete(&fullscreen_shm); + + /* + * Here we have to clean up quite a few shm areas for all + * the possible tile row runs (40 for 1280), not as robust + * as one might like... sometimes need to run ipcrm(1). + */ + for(i=1; i<=ntiles_x; i++) { + shm_delete(&tile_row_shm[i]); + if (single_copytile && i >= single_copytile) { + break; + } + } + if (sig) { + exit(2); + } +} + +XErrorHandler Xerror_def; +XIOErrorHandler XIOerr_def; +int Xerror(Display *d, XErrorEvent *error) { + X_UNLOCK; + interrupted(0); + return (*Xerror_def)(d, error); +} +int XIOerr(Display *d) { + X_UNLOCK; + interrupted(0); + return (*XIOerr_def)(d); +} + +void set_signals(void) { + signal(SIGHUP, interrupted); + signal(SIGINT, interrupted); + signal(SIGQUIT, interrupted); + signal(SIGABRT, interrupted); + signal(SIGTERM, interrupted); + signal(SIGBUS, interrupted); + signal(SIGSEGV, interrupted); + signal(SIGFPE, interrupted); + + if (sigpipe == 1) { +#ifdef SIG_IGN + signal(SIGPIPE, SIG_IGN); +#endif + } else if (sigpipe == 2) { + rfbLog("set_signals: will exit on SIGPIPE\n"); + signal(SIGPIPE, interrupted); + } + + X_LOCK; + Xerror_def = XSetErrorHandler(Xerror); + XIOerr_def = XSetIOErrorHandler(XIOerr); + X_UNLOCK; +} + +int run_user_command(char *, rfbClientPtr); + +int accepted_client = 0; +void client_gone(rfbClientPtr client) { + + client_count--; + rfbLog("client_count: %d\n", client_count); + + if (gone_cmd) { + rfbLog("client_gone: using cmd for: %s\n", client->host); + run_user_command(gone_cmd, client); + } + + if (inetd) { + rfbLog("viewer exited.\n"); + clean_up_exit(0); + } + if (connect_once) { + /* + * This non-exit is done for a bad passwd to be consistent + * with our RFB_CLIENT_REFUSE behavior in new_client() (i.e. + * we disconnect after 1 successful connection). + */ + if (client->state == RFB_PROTOCOL_VERSION || + client->state == RFB_AUTHENTICATION && accepted_client) { + rfbLog("connect_once: bad password or early " + "disconnect.\n"); + rfbLog("connect_once: waiting for next connection.\n"); + accepted_client = 0; + return; + } + + rfbLog("viewer exited.\n"); + clean_up_exit(0); + } +} + +/* + * Simple routine to limit access via string compare. A power user will + * want to compile libvncserver with libwrap support and use /etc/hosts.allow. + */ +int check_access(char *addr) { + int allowed = 0; + char *p, *list; + + if (allow_list == NULL || *allow_list == '\0') { + return 1; + } + if (addr == NULL || *addr == '\0') { + rfbLog("check_access: denying empty host IP address string.\n"); + return 0; + } + + list = strdup(allow_list); + p = strtok(list, ","); + while (p) { + char *q = strstr(addr, p); + if (q == addr) { + rfbLog("check_access: client %s matches pattern %s\n", + addr, p); + allowed = 1; + + } else if(!strcmp(p,"localhost") && !strcmp(addr,"127.0.0.1")) { + allowed = 1; + } + p = strtok(NULL, ","); + } + free(list); + return allowed; +} + +/* + * x11vnc's first (and only) visible widget: accept/reject dialog window. + * We go through this pain to avoid dependency on libXt. + */ + +int ugly_accept_window(char *addr, int X, int Y, int timeout, char *mode) { + +#define t2x2_width 16 +#define t2x2_height 16 +static char t2x2_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, + 0x33, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33, + 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33}; + + Window awin; + GC gc; + XSizeHints hints; + XGCValues values; + static XFontStruct *font_info = NULL; + static Pixmap ico = 0; + unsigned long valuemask = 0; + static char dash_list[] = {20, 40}; + int list_length = sizeof(dash_list); + + Atom wm_protocols; + Atom wm_delete_window; + + XEvent ev; + long evmask = ExposureMask | KeyPressMask | ButtonPressMask + | StructureNotifyMask; + double waited = 0.0; + + /* strings and geometries y/n */ + KeyCode key_y, key_n, key_v; + char strh[100]; + char str1_b[] = "To accept: press \"y\" or click the \"Yes\" button"; + char str2_b[] = "To reject: press \"n\" or click the \"No\" button"; + char str3_b[] = "View only: press \"v\" or click the \"View\" button"; + char str1_m[] = "To accept: click the \"Yes\" button"; + char str2_m[] = "To reject: click the \"No\" button"; + char str3_m[] = "View only: click the \"View\" button"; + char str1_k[] = "To accept: press \"y\""; + char str2_k[] = "To reject: press \"n\""; + char str3_k[] = "View only: press \"v\""; + char *str1, *str2, *str3; + char str_y[] = "Yes"; + char str_n[] = "No"; + char str_v[] = "View"; + int x, y, w = 345, h = 150, ret = 0; + int X_sh = 20, Y_sh = 30, dY = 20; + int Ye_x = 20, Ye_y = 0, Ye_w = 45, Ye_h = 20; + int No_x = 75, No_y = 0, No_w = 45, No_h = 20; + int Vi_x = 130, Vi_y = 0, Vi_w = 45, Vi_h = 20; + + if (!strcmp(mode, "mouse_only")) { + str1 = str1_m; + str2 = str2_m; + str3 = str3_m; + } else if (!strcmp(mode, "key_only")) { + str1 = str1_k; + str2 = str2_k; + str3 = str3_k; + h -= dY; + } else { + str1 = str1_b; + str2 = str2_b; + str3 = str3_b; + } + if (view_only) { + h -= dY; + } + + if (X < -dpy_x) { + x = (dpy_x - w)/2; /* large negative: center */ + if (x < 0) x = 0; + } else if (X < 0) { + x = dpy_x + X - w; /* from lower right */ + } else { + x = X; /* from upper left */ + } + + if (Y < -dpy_y) { + y = (dpy_y - h)/2; + if (y < 0) y = 0; + } else if (Y < 0) { + y = dpy_y + Y - h; + } else { + y = Y; + } + + X_LOCK; + + awin = XCreateSimpleWindow(dpy, window, x, y, w, h, 4, + BlackPixel(dpy, scr), WhitePixel(dpy, scr)); + + wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False); + wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + XSetWMProtocols(dpy, awin, &wm_delete_window, 1); + + if (! ico) { + ico = XCreateBitmapFromData(dpy, awin, t2x2_bits, t2x2_width, + t2x2_height); + } + + hints.flags = PPosition | PSize | PMinSize; + hints.x = x; + hints.y = y; + hints.width = w; + hints.height = h; + hints.min_width = w; + hints.min_height = h; + + XSetStandardProperties(dpy, awin, "new x11vnc client", "x11vnc query", + ico, NULL, 0, &hints); + + XSelectInput(dpy, awin, evmask); + + if (! font_info && (font_info = XLoadQueryFont(dpy, "fixed")) == NULL) { + rfbLog("ugly_accept_window: cannot locate font fixed.\n"); + X_UNLOCK; + clean_up_exit(1); + } + + gc = XCreateGC(dpy, awin, valuemask, &values); + XSetFont(dpy, gc, font_info->fid); + XSetForeground(dpy, gc, BlackPixel(dpy, scr)); + XSetLineAttributes(dpy, gc, 1, LineSolid, CapButt, JoinMiter); + XSetDashes(dpy, gc, 0, dash_list, list_length); + + XMapWindow(dpy, awin); + XFlush(dpy); + + sprintf(strh, "x11vnc: accept connection from %s?", addr); + key_y = XKeysymToKeycode(dpy, XStringToKeysym("y")); + key_n = XKeysymToKeycode(dpy, XStringToKeysym("n")); + key_v = XKeysymToKeycode(dpy, XStringToKeysym("v")); + + while (1) { + int out = -1, x, y, tw, k; + + if (XCheckWindowEvent(dpy, awin, evmask, &ev)) { + ; /* proceed to handling */ + } else if (XCheckTypedEvent(dpy, ClientMessage, &ev)) { + ; /* proceed to handling */ + } else { + int ms = 100; /* sleep a bit */ + usleep(ms * 1000); + waited += ((double) ms)/1000.; + if (timeout && (int) waited >= timeout) { + rfbLog("accept_client: popup timed out after " + "%d seconds.\n", timeout); + out = 0; + ev.type = 0; + } else { + continue; + } + } + + switch(ev.type) { + case Expose: + while (XCheckTypedEvent(dpy, Expose, &ev)) { + ; + } + k=0; + + /* instructions */ + XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, + strh, strlen(strh)); + XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, + str1, strlen(str1)); + XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, + str2, strlen(str2)); + if (! view_only) { + XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, + str3, strlen(str3)); + } + + if (!strcmp(mode, "key_only")) { + break; + } + + /* buttons */ + Ye_y = Y_sh+k*dY; + No_y = Y_sh+k*dY; + Vi_y = Y_sh+k*dY; + XDrawRectangle(dpy, awin, gc, Ye_x, Ye_y, Ye_w, Ye_h); + XDrawRectangle(dpy, awin, gc, No_x, No_y, No_w, No_h); + if (! view_only) { + XDrawRectangle(dpy, awin, gc, Vi_x, Vi_y, + Vi_w, Vi_h); + } + + tw = XTextWidth(font_info, str_y, strlen(str_y)); + tw = (Ye_w - tw)/2; + if (tw < 0) tw = 1; + XDrawString(dpy, awin, gc, Ye_x+tw, Ye_y+Ye_h-5, + str_y, strlen(str_y)); + + tw = XTextWidth(font_info, str_n, strlen(str_n)); + tw = (No_w - tw)/2; + if (tw < 0) tw = 1; + XDrawString(dpy, awin, gc, No_x+tw, No_y+No_h-5, + str_n, strlen(str_n)); + + if (! view_only) { + tw = XTextWidth(font_info, str_v, + strlen(str_v)); + tw = (Vi_w - tw)/2; + if (tw < 0) tw = 1; + XDrawString(dpy, awin, gc, Vi_x+tw, Vi_y+Vi_h-5, + str_v, strlen(str_v)); + } + + break; + + case ClientMessage: + if (ev.xclient.message_type == wm_protocols && + ev.xclient.data.l[0] == wm_delete_window) { + out = 0; + } + break; + + case ButtonPress: + x = ev.xbutton.x; + y = ev.xbutton.y; + if (!strcmp(mode, "key_only")) { + ; + } else if (x > No_x && x < No_x+No_w && y > No_y + && y < No_y+No_h) { + out = 0; + } else if (x > Ye_x && x < Ye_x+Ye_w && y > Ye_y + && y < Ye_y+Ye_h) { + out = 1; + } else if (! view_only && x > Vi_x && x < Vi_x+Vi_w + && y > Vi_y && y < Vi_y+Ye_h) { + out = 2; + } + break; + + case KeyPress: + if (!strcmp(mode, "mouse_only")) { + ; + } else if (ev.xkey.keycode == key_y) { + out = 1; + } else if (ev.xkey.keycode == key_n) { + out = 0; + } else if (! view_only && ev.xkey.keycode == key_v) { + out = 2; + } + break; + default: + break; + } + if (out != -1) { + ret = out; + XUnmapWindow(dpy, awin); + XFreeGC(dpy, gc); + XDestroyWindow(dpy, awin); + XFlush(dpy); + break; + } + } + X_UNLOCK; + + return ret; +} + +/* + * utility to run a user supplied command setting some RFB_ env vars. + * used by, e.g., accept_client() and client_gone() + */ +int run_user_command(char *cmd, rfbClientPtr client) { + char *dpystr = DisplayString(dpy); + static char *display_env = NULL; + static char env_rfb_client_id[100]; + static char env_rfb_client_ip[100]; + static char env_rfb_client_port[100]; + static char env_rfb_x11vnc_pid[100]; + char *addr = client->host; + int rc, fromlen, fromport; + struct sockaddr_in from; + + if (addr == NULL || addr[0] == '\0') { + addr = "unknown-host"; + } + + /* set RFB_CLIENT_ID to semi unique id for command to use */ + sprintf(env_rfb_client_id, "RFB_CLIENT_ID=%d", (int) client); + putenv(env_rfb_client_id); + + /* set RFB_CLIENT_IP to IP addr for command to use */ + sprintf(env_rfb_client_ip, "RFB_CLIENT_IP=%s", addr); + putenv(env_rfb_client_ip); + + /* set RFB_X11VNC_PID to our pid for command to use */ + sprintf(env_rfb_x11vnc_pid, "RFB_X11VNC_PID=%d", (int) getpid()); + putenv(env_rfb_x11vnc_pid); + + /* set RFB_CLIENT_PORT to peer port for command to use */ + fromlen = sizeof(from); + memset(&from, 0, sizeof(from)); + fromport = -1; + if (!getpeername(client->sock, (struct sockaddr *)&from, &fromlen)) { + fromport = ntohs(from.sin_port); + } + sprintf(env_rfb_client_port, "RFB_CLIENT_PORT=%d", fromport); + putenv(env_rfb_client_port); + + /* + * Better set DISPLAY to the one we are polling, if they + * want something trickier, they can handle on their own + * via environment, etc. XXX really should save/restore old. + */ + if (display_env == NULL) { + display_env = (char *) malloc(strlen(dpystr)+10); + } + sprintf(display_env, "DISPLAY=%s", dpystr); + putenv(display_env); + + rfbLog("running command:\n"); + rfbLog(" %s\n", cmd); + + rc = system(cmd); + + if (rc >= 256) { + rc = rc/256; + } + rfbLog("command returned: %d\n", rc); + + sprintf(env_rfb_client_id, "RFB_CLIENT_ID="); + putenv(env_rfb_client_id); + + sprintf(env_rfb_client_ip, "RFB_CLIENT_IP="); + putenv(env_rfb_client_ip); + + sprintf(env_rfb_client_port, "RFB_CLIENT_PORT="); + putenv(env_rfb_client_port); + + sprintf(env_rfb_x11vnc_pid, "RFB_X11VNC_PID="); + putenv(env_rfb_x11vnc_pid); + + return rc; +} + +/* + * process a "yes:0,no:*,view:3" type action list comparing to command + * return code rc. * means the default action with no other match. + */ +int action_match(char *action, int rc) { + char *p, *q, *s = strdup(action); + int cases[4], i, result; + char *labels[4]; + + labels[1] = "yes"; + labels[2] = "no"; + labels[3] = "view"; + + rfbLog("accept_client: process action line: %s\n", + action); + + for (i=1; i <= 3; i++) { + cases[i] = -2; + } + + p = strtok(s, ","); + while (p) { + if ((q = strchr(p, ':')) != NULL) { + int in, k; + *q = '\0'; + q++; + if (strstr(p, "yes") == p) { + k = 1; + } else if (strstr(p, "no") == p) { + k = 2; + } else if (strstr(p, "view") == p) { + k = 3; + } else { + rfbLog("bad action line: %s\n", action); + clean_up_exit(1); + } + if (*q == '*') { + cases[k] = -1; + } else if (sscanf(q, "%d", &in) == 1) { + if (in < 0) { + rfbLog("bad action line: %s\n", action); + clean_up_exit(1); + } + cases[k] = in; + } else { + rfbLog("bad action line: %s\n", action); + clean_up_exit(1); + } + } else { + rfbLog("bad action line: %s\n", action); + clean_up_exit(1); + } + p = strtok(NULL, ","); + } + free(s); + + result = -1; + for (i=1; i <= 3; i++) { + if (cases[i] == -1) { + rfbLog("accept_client: default action is case=%d %s\n", + i, labels[i]); + result = i; + break; + } + } + if (result == -1) { + rfbLog("accept_client: no default action\n"); + } + for (i=1; i <= 3; i++) { + if (cases[i] >= 0 && cases[i] == rc) { + rfbLog("accept_client: matched action is case=%d %s\n", + i, labels[i]); + result = i; + break; + } + } + if (result < 0) { + rfbLog("no action match: %s rc=%d set to no\n", action, rc); + result = 2; + } + return result; +} + +/* + * Simple routine to prompt the user on the X display whether an incoming + * client should be allowed to connect or not. If a gui is involved it + * will be running in the environment/context of the X11 DISPLAY. + * + * The command supplied via -accept is run as is (i.e. no string + * substitution) with the RFB_CLIENT_IP environment variable set to the + * incoming client's numerical IP address. + * + * If the external command exits with 0 the client is accepted, otherwise + * the client is rejected. + * + * Some builtins are provided: + * + * xmessage: use homebrew xmessage(1) for the external command. + * popup: use internal X widgets for prompting. + * + */ +int accept_client(rfbClientPtr client) { + + char xmessage[200], *cmd = NULL; + char *addr = client->host; + char *action = NULL; + + if (accept_cmd == NULL) { + return 1; /* no command specified, so we accept */ + } + + if (addr == NULL || addr[0] == '\0') { + addr = "unknown-host"; + } + + if (strstr(accept_cmd, "popup") == accept_cmd) { + /* use our builtin popup button */ + + /* (popup|popupkey|popupmouse)[+-X+-Y][:timeout] */ + + int ret, timeout = 120; + int x = -64000, y = -64000; + char *p, *mode; + + /* extract timeout */ + if ((p = strchr(accept_cmd, ':')) != NULL) { + int in; + if (sscanf(p+1, "%d", &in) == 1) { + timeout = in; + } + } + /* extract geometry */ + if ((p = strpbrk(accept_cmd, "+-")) != NULL) { + int x1, y1; + if (sscanf(p, "+%d+%d", &x1, &y1) == 2) { + x = x1; + y = y1; + } else if (sscanf(p, "+%d-%d", &x1, &y1) == 2) { + x = x1; + y = -y1; + } else if (sscanf(p, "-%d+%d", &x1, &y1) == 2) { + x = -x1; + y = y1; + } else if (sscanf(p, "-%d-%d", &x1, &y1) == 2) { + x = -x1; + y = -y1; + } + } + + /* find mode: mouse, key, or both */ + if (strstr(accept_cmd, "popupmouse") == accept_cmd) { + mode = "mouse_only"; + } else if (strstr(accept_cmd, "popupkey") == accept_cmd) { + mode = "key_only"; + } else { + mode = "both"; + } + + rfbLog("accept_client: using builtin popup for: %s\n", addr); + if ((ret = ugly_accept_window(addr, x, y, timeout, mode))) { + if (ret == 2) { + rfbLog("accept_client: viewonly: %s\n", addr); + client->viewOnly = TRUE; + } + rfbLog("accept_client: popup accepted: %s\n", addr); + return 1; + } else { + rfbLog("accept_client: popup rejected: %s\n", addr); + return 0; + } + + } else if (!strcmp(accept_cmd, "xmessage")) { + /* make our own command using xmessage(1) */ + + if (view_only) { + sprintf(xmessage, "xmessage -buttons yes:0,no:2 -center" + " 'x11vnc: accept connection from %s?'", addr); + } else { + sprintf(xmessage, "xmessage -buttons yes:0,no:2," + "view-only:3 -center" " 'x11vnc: accept connection" + " from %s?'", addr); + action = "yes:0,no:*,view:3"; + } + cmd = xmessage; + + } else { + /* use the user supplied command: */ + + cmd = accept_cmd; + + /* extract any action prefix: yes:N,no:M,view:K */ + if (strstr(accept_cmd, "yes:") == accept_cmd) { + char *p; + if ((p = strpbrk(accept_cmd, " \t")) != NULL) { + int i; + cmd = p; + p = accept_cmd; + for (i=0; i<200; i++) { + if (*p == ' ' || *p == '\t') { + xmessage[i] = '\0'; + break; + } + xmessage[i] = *p; + p++; + } + xmessage[200-1] = '\0'; + action = xmessage; + } + } + } + + if (cmd) { + int rc; + + rfbLog("accept_client: using cmd for: %s\n", addr); + rc = run_user_command(cmd, client); + + if (action) { + int result; + + if (rc < 0) { + rfbLog("accept_client: cannot use negative " + "rc: %d, action %s\n", rc, action); + result = 2; + } else { + result = action_match(action, rc); + } + + if (result == 1) { + rc = 0; + } else if (result == 2) { + rc = 1; + } else if (result == 3) { + rc = 0; + rfbLog("accept_client: viewonly: %s\n", addr); + client->viewOnly = TRUE; + } else { + rc = 1; /* NOTREACHED */ + } + } + + if (rc == 0) { + rfbLog("accept_client: accepted: %s\n", addr); + return 1; + } else { + rfbLog("accept_client: rejected: %s\n", addr); + return 0; + } + } else { + rfbLog("accept_client: no command, rejecting %s\n", addr); + return 0; + } + + return 0; /* NOTREACHED */ +} + +/* + * For the -connect option: periodically read the file looking for + * a connect string. If one is found set client_connect to it. + */ +void check_connect_file(char *file) { + FILE *in; + char line[512], host[512]; + static int first_warn = 1, truncate_ok = 1; + static time_t last_time = 0; + time_t now = time(0); + + if (now - last_time < 1) { + /* check only once a second */ + return; + } + last_time = now; + + if (! truncate_ok) { + /* check if permissions changed */ + if (access(file, W_OK) == 0) { + truncate_ok = 1; + } else { + return; + } + } + + in = fopen(file, "r"); + if (in == NULL) { + if (first_warn) { + rfbLog("check_connect_file: fopen failure: %s\n", file); + perror("fopen"); + first_warn = 0; + } + return; + } + + if (fgets(line, 512, in) != NULL) { + if (sscanf(line, "%s", host) == 1) { + if (strlen(host) > 0) { + client_connect = strdup(host); + rfbLog("read connect file: %s\n", host); + } + } + } + fclose(in); + + /* truncate file */ + in = fopen(file, "w"); + if (in != NULL) { + fclose(in); + } else { + /* disable if we cannot truncate */ + rfbLog("check_connect_file: could not truncate %s, " + "disabling checking.\n", file); + truncate_ok = 0; + } +} + +/* Do a reverse connect for a single "host" or "host:port" */ +int do_reverse_connect(char *str) { + rfbClientPtr cl; + char *host, *p; + int port = 5500, len = strlen(str); + + if (len < 1) { + return 0; + } + if (len > 512) { + rfbLog("reverse_connect: string too long: %d bytes\n", len); + return 0; + } + + /* copy in to host */ + host = (char *) malloc((size_t) len+1); + if (! host) { + rfbLog("reverse_connect: could not malloc string %d\n", len); + return 0; + } + strncpy(host, str, len); + host[len] = '\0'; + + /* extract port, if any */ + if ((p = strchr(host, ':')) != NULL) { + port = atoi(p+1); + *p = '\0'; + } + + cl = rfbReverseConnection(screen, host, port); + free(host); + + if (cl == NULL) { + rfbLog("reverse_connect: %s failed\n", str); + return 0; + } else { + rfbLog("reverse_connect: %s/%s OK\n", str, cl->host); + return 1; + } +} + +void rfbPE(rfbScreenInfoPtr scr, long us) { + if (! use_threads) { + return rfbProcessEvents(scr, us); + } +} + +/* break up comma separated list of hosts and call do_reverse_connect() */ + +void reverse_connect(char *str) { + char *p, *tmp = strdup(str); + int sleep_between_host = 300; + int sleep_min = 1500, sleep_max = 4500, n_max = 5; + int n, tot, t, dt = 100, cnt = 0; + + p = strtok(tmp, ","); + while (p) { + if ((n = do_reverse_connect(p)) != 0) { + rfbPE(screen, -1); + } + cnt += n; + + p = strtok(NULL, ","); + if (p) { + t = 0; + while (t < sleep_between_host) { + usleep(dt * 1000); + rfbPE(screen, -1); + t += dt; + } + } + } + free(tmp); + + if (cnt == 0) { + return; + } + + /* + * XXX: we need to process some of the initial handshaking + * events, otherwise the client can get messed up (why??) + * so we send rfbProcessEvents() all over the place. + */ + + n = cnt; + if (n >= n_max) { + n = n_max; + } + t = sleep_max - sleep_min; + tot = sleep_min + ((n-1) * t) / (n_max-1); + + t = 0; + while (t < tot) { + rfbPE(screen, -1); + usleep(dt * 1000); + t += dt; + } +} + +/* check if client_connect has been set, if so make the reverse connections. */ + +void send_client_connect() { + if (client_connect != NULL) { + reverse_connect(client_connect); + free(client_connect); + client_connect = NULL; + } +} + +/* string for the VNC_CONNECT property */ +#define VNC_CONNECT_MAX 512 +char vnc_connect_str[VNC_CONNECT_MAX+1]; + +/* monitor the various input methods */ +void check_connect_inputs() { + + /* flush any already set: */ + send_client_connect(); + + /* connect file: */ + if (client_connect_file != NULL) { + check_connect_file(client_connect_file); + } + send_client_connect(); + + /* VNC_CONNECT property (vncconnect program) */ + if (vnc_connect && *vnc_connect_str != '\0') { + client_connect = strdup(vnc_connect_str); + vnc_connect_str[0] = '\0'; + } + send_client_connect(); +} + +/* + * libvncserver callback for when a new client connects + */ +enum rfbNewClientAction new_client(rfbClientPtr client) { + last_event = last_input = time(0); + + if (inetd) { + /* + * Set this so we exit as soon as connection closes, + * otherwise client_gone is only called after RFB_CLIENT_ACCEPT + */ + client->clientGoneHook = client_gone; + } + + if (connect_once) { + if (screen->rfbDontDisconnect && screen->rfbNeverShared) { + if (! shared && accepted_client) { + rfbLog("denying additional client: %s\n", + client->host); + return(RFB_CLIENT_REFUSE); + } + } + } + if (! check_access(client->host)) { + rfbLog("denying client: %s does not match %s\n", client->host, + allow_list ? allow_list : "(null)" ); + return(RFB_CLIENT_REFUSE); + } + if (! accept_client(client)) { + rfbLog("denying client: %s local user rejected connection.\n", + client->host); + rfbLog("denying client: accept_cmd=\"%s\"\n", + accept_cmd ? accept_cmd : "(null)" ); + return(RFB_CLIENT_REFUSE); + } + + if (view_only) { + client->clientData = (void *) -1; + client->viewOnly = TRUE; + } else { + client->clientData = (void *) 0; + } + + client->clientGoneHook = client_gone; + client_count++; + + accepted_client = 1; + last_client = time(0); + + return(RFB_CLIENT_ACCEPT); +} + +/* + * For tweaking modifiers wrt the Alt-Graph key, etc. + */ +#define LEFTSHIFT 1 +#define RIGHTSHIFT 2 +#define ALTGR 4 +char mod_state = 0; + +char modifiers[0x100]; +KeyCode keycodes[0x100], left_shift_code, right_shift_code, altgr_code; + +void initialize_modtweak() { + KeySym key, *keymap; + int i, j, minkey, maxkey, syms_per_keycode; + + memset(modifiers, -1, sizeof(modifiers)); + + XDisplayKeycodes(dpy, &minkey, &maxkey); + + keymap = XGetKeyboardMapping(dpy, minkey, (maxkey - minkey + 1), + &syms_per_keycode); + + /* handle alphabetic char with only one keysym (no upper + lower) */ + for (i = minkey; i <= maxkey; i++) { + KeySym lower, upper; + /* 2nd one */ + key = keymap[(i - minkey) * syms_per_keycode + 1]; + if (key != NoSymbol) { + continue; + } + /* 1st one */ + key = keymap[(i - minkey) * syms_per_keycode + 0]; + if (key == NoSymbol) { + continue; + } + XConvertCase(key, &lower, &upper); + if (lower != upper) { + keymap[(i - minkey) * syms_per_keycode + 0] = lower; + keymap[(i - minkey) * syms_per_keycode + 1] = upper; + } + } + for (i = minkey; i <= maxkey; i++) { + for (j = 0; j < syms_per_keycode; j++) { + key = keymap[ (i - minkey) * syms_per_keycode + j ]; + if ( key >= ' ' && key < 0x100 + && i == XKeysymToKeycode(dpy, key) ) { + keycodes[key] = i; + modifiers[key] = j; + } + } + } + + left_shift_code = XKeysymToKeycode(dpy, XK_Shift_L); + right_shift_code = XKeysymToKeycode(dpy, XK_Shift_R); + altgr_code = XKeysymToKeycode(dpy, XK_Mode_switch); + + XFree ((void *) keymap); +} + +/* + * The following is for an experimental -remap option to allow the user + * to remap keystrokes. It is currently confusing wrt modifiers... + */ +typedef struct keyremap { + KeySym before; + KeySym after; + int isbutton; + struct keyremap *next; +} keyremap_t; + +keyremap_t *keyremaps = NULL; + +void initialize_remap(char *infile) { + FILE *in; + char *p, *q, line[256], str1[256], str2[256]; + int i; + KeySym ksym1, ksym2; + keyremap_t *remap, *current; + + in = fopen(infile, "r"); + if (in == NULL) { + /* assume cmd line key1-key2,key3-key4 */ + if (! strchr(infile, '-') || (in = tmpfile()) == NULL) { + rfbLog("remap: cannot open: %s\n", infile); + perror("fopen"); + clean_up_exit(1); + } + p = infile; + while (*p) { + if (*p == '-') { + fprintf(in, " "); + } else if (*p == ',') { + fprintf(in, "\n"); + } else { + fprintf(in, "%c", *p); + } + p++; + } + fprintf(in, "\n"); + fflush(in); + rewind(in); + } + while (fgets(line, 256, in) != NULL) { + int blank = 1, isbtn = 0; + p = line; + while (*p) { + if (! isspace(*p)) { + blank = 0; + break; + } + p++; + } + if (blank) { + continue; + } + if (strchr(line, '#')) { + continue; + } + if ( (q = strchr(line, '-')) != NULL) { + /* allow Keysym1-Keysym2 notation */ + *q = ' '; + } + + if (sscanf(line, "%s %s", str1, str2) != 2) { + rfbLog("remap: bad line: %s\n", line); + fclose(in); + clean_up_exit(1); + } + if (sscanf(str1, "0x%x", &i) == 1) { + ksym1 = (KeySym) i; + } else { + ksym1 = XStringToKeysym(str1); + } + if (sscanf(str2, "0x%x", &i) == 1) { + ksym2 = (KeySym) i; + } else { + ksym2 = XStringToKeysym(str2); + } + if (ksym2 == NoSymbol) { + int i; + if (sscanf(str2, "Button%d", &i) == 1) { + ksym2 = (KeySym) i; + isbtn = 1; + } + } + if (ksym1 == NoSymbol || ksym2 == NoSymbol) { + rfbLog("warning: skipping bad remap line: %s", line); + continue; + } + remap = (keyremap_t *) malloc((size_t) sizeof(keyremap_t)); + remap->before = ksym1; + remap->after = ksym2; + remap->isbutton = isbtn; + remap->next = NULL; + rfbLog("remapping: (%s, 0x%x) -> (%s, 0x%x) isbtn=%d\n", str1, + ksym1, str2, ksym2, isbtn); + if (keyremaps == NULL) { + keyremaps = remap; + } else { + current->next = remap; + + } + current = remap; + } + fclose(in); +} + +void DebugXTestFakeKeyEvent(Display* dpy, KeyCode key, Bool down, time_t cur_time) +{ + if (debug_keyboard) { + rfbLog("XTestFakeKeyEvent(dpy, keycode=0x%x \"%s\", %s)\n", + key, XKeysymToString(XKeycodeToKeysym(dpy, key, 0)), + down ? "down":"up"); + } + XTestFakeKeyEvent(dpy, key, down, cur_time); +} + +/* + * This is to allow debug_keyboard option trap everything: + */ +#define XTestFakeKeyEvent DebugXTestFakeKeyEvent + +void tweak_mod(signed char mod, rfbBool down) { + rfbBool is_shift = mod_state & (LEFTSHIFT|RIGHTSHIFT); + Bool dn = (Bool) down; + + if (mod < 0) { + if (debug_keyboard) { + rfbLog("tweak_mod: Skip: down=%d mod=0x%x\n", down, + (int) mod); + } + return; + } + if (debug_keyboard) { + rfbLog("tweak_mod: Start: down=%d mod=0x%x mod_state=0x%x" + " is_shift=%d\n", down, (int) mod, (int) mod_state, + is_shift); + } + + X_LOCK; + if (is_shift && mod != 1) { + if (mod_state & LEFTSHIFT) { + XTestFakeKeyEvent(dpy, left_shift_code, !dn, CurrentTime); + } + if (mod_state & RIGHTSHIFT) { + XTestFakeKeyEvent(dpy, right_shift_code, !dn, CurrentTime); + } + } + if ( ! is_shift && mod == 1 ) { + XTestFakeKeyEvent(dpy, left_shift_code, dn, CurrentTime); + } + if ( altgr_code && (mod_state & ALTGR) && mod != 2 ) { + XTestFakeKeyEvent(dpy, altgr_code, !dn, CurrentTime); + } + if ( altgr_code && ! (mod_state & ALTGR) && mod == 2 ) { + XTestFakeKeyEvent(dpy, altgr_code, dn, CurrentTime); + } + X_UNLOCK; + if (debug_keyboard) { + rfbLog("tweak_mod: Finish: down=%d mod=0x%x mod_state=0x%x" + " is_shift=%d\n", down, (int) mod, (int) mod_state, + is_shift); + } +} + +static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, + rfbClientPtr client) { + KeyCode k; + int tweak = 0; + if (debug_keyboard) { + rfbLog("modifier_tweak_keyboard: %s keysym=0x%x\n", + down ? "down" : "up", (int) keysym); + } + + if (view_only) { + return; + } + if (client->viewOnly) { + return; + } + +#define ADJUSTMOD(sym, state) \ + if (keysym == sym) { \ + if (down) { \ + mod_state |= state; \ + } else { \ + mod_state &= ~state; \ + } \ + } + + ADJUSTMOD(XK_Shift_L, LEFTSHIFT) + ADJUSTMOD(XK_Shift_R, RIGHTSHIFT) + ADJUSTMOD(XK_Mode_switch, ALTGR) + + if ( down && keysym >= ' ' && keysym < 0x100 ) { + tweak = 1; + tweak_mod(modifiers[keysym], True); + k = keycodes[keysym]; + } else { + X_LOCK; + k = XKeysymToKeycode(dpy, (KeySym) keysym); + X_UNLOCK; + } + if (debug_keyboard) { + rfbLog("modifier_tweak_keyboard: KeySym 0x%x \"%s\" -> " + "KeyCode 0x%x%s\n", (int) keysym, XKeysymToString(keysym), + (int) k, k ? "" : " *ignored*"); + } + if ( k != NoSymbol ) { + X_LOCK; + XTestFakeKeyEvent(dpy, k, (Bool) down, CurrentTime); + X_UNLOCK; + } + + if ( tweak ) { + tweak_mod(modifiers[keysym], False); + } +} + +/* + * key event handler. See the above functions for contortions for + * running under -modtweak. + */ +rfbClientPtr last_keyboard_client = NULL; +int num_buttons = -1; + +static void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { + KeyCode k; + int isbutton = 0; + + if (debug_keyboard) { + X_LOCK; + rfbLog("keyboard(%s, 0x%x \"%s\")\n", down ? "down":"up", + (int) keysym, XKeysymToString(keysym)); + X_UNLOCK; + } + + if (view_only) { + return; + } + if (client->viewOnly) { + return; + } + last_keyboard_client = client; + + if (keyremaps) { + keyremap_t *remap = keyremaps; + while (remap != NULL) { + if (remap->before == keysym) { + keysym = remap->after; + isbutton = remap->isbutton; + if (debug_keyboard) { + X_LOCK; + rfbLog("keyboard(): remapping keysym: " + "0x%x \"%s\" -> 0x%x \"%s\"\n", + (int) remap->before, + XKeysymToString(remap->before), + (int) remap->after, + remap->isbutton ? "button" : + XKeysymToString(remap->after)); + X_UNLOCK; + } + break; + } + remap = remap->next; + } + } + + if (isbutton) { + int button = (int) keysym; + if (! down) { + return; /* nothing to send */ + } + if (debug_keyboard) { + rfbLog("keyboard(): remapping keystroke to button %d" + " click\n", button); + } + if (button < 1 || button > num_buttons) { + rfbLog("keyboard(): ignoring mouse button out of " + "bounds: %d\n", button); + return; + } + X_LOCK; + XTestFakeButtonEvent(dpy, button, True, CurrentTime); + XTestFakeButtonEvent(dpy, button, False, CurrentTime); + XFlush(dpy); + X_UNLOCK; + return; + } + + if (use_modifier_tweak) { + modifier_tweak_keyboard(down, keysym, client); + X_LOCK; + XFlush(dpy); + X_UNLOCK; + return; + } + + X_LOCK; + + k = XKeysymToKeycode(dpy, (KeySym) keysym); + + if (debug_keyboard) { + rfbLog("keyboard(): KeySym 0x%x \"%s\" -> KeyCode 0x%x%s\n", + (int) keysym, XKeysymToString(keysym), (int) k, + k ? "" : " *ignored*"); + } + + if ( k != NoSymbol ) { + XTestFakeKeyEvent(dpy, k, (Bool) down, CurrentTime); + XFlush(dpy); + + last_event = last_input = time(0); + got_user_input++; + got_keyboard_input++; + } + + X_UNLOCK; +} + +/* + * pointer event handling routines. + */ +typedef struct ptrremap { + KeySym keysym; + KeyCode keycode; + int end; + int button; + int down; + int up; +} prtremap_t; + +MUTEX(pointerMutex); +#define MAX_BUTTONS 5 +#define MAX_BUTTON_EVENTS 50 +prtremap_t pointer_map[MAX_BUTTONS+1][MAX_BUTTON_EVENTS]; +char *pointer_remap = NULL; +void update_pointer(int, int, int); + + +/* based on IsModifierKey in Xutil.h */ +#define ismodkey(keysym) \ + ((((KeySym)(keysym) >= XK_Shift_L) && ((KeySym)(keysym) <= XK_Hyper_R))) + + +/* + * For parsing the -buttonmap sections, e.g. "4" or ":Up+Up+Up:" + */ +void buttonparse(int from, char **s) { + char *q; + int to, i; + int modisdown[256]; + + q = *s; + + for (i=0; i<256; i++) { + modisdown[i] = 0; + } + + if (*q == ':') { + /* :sym1+sym2+...+symN: format */ + int l = 0, n = 0; + char list[1000]; + char *t, *kp = q + 1; + KeyCode kcode; + + while (*(kp+l) != ':' && *(kp+l) != '\0') { + /* loop to the matching ':' */ + l++; + if (l >= 1000) { + rfbLog("buttonparse: keysym list too long: " + "%s\n", q); + break; + } + } + *(kp+l) = '\0'; + strncpy(list, kp, l); + list[l] = '\0'; + rfbLog("remap button %d using \"%s\"\n", from, list); + + /* loop over tokens separated by '+' */ + t = strtok(list, "+"); + while (t) { + KeySym ksym; + int i; + if (n >= MAX_BUTTON_EVENTS - 20) { + rfbLog("buttonparse: too many button map " + "events: %s\n", list); + break; + } + if (sscanf(t, "0x%x", &i) == 1) { + ksym = (KeySym) i; /* hex value */ + } else { + ksym = XStringToKeysym(t); /* string value */ + } + if (ksym == NoSymbol) { + /* see if Button "keysym" was used: */ + if (sscanf(t, "Button%d", &i) == 1) { + rfbLog(" event %d: button %d\n", + from, n+1, i); + if (i == 0) i = -1; /* bah */ + pointer_map[from][n].keysym = NoSymbol; + pointer_map[from][n].keycode = NoSymbol; + pointer_map[from][n].button = i; + pointer_map[from][n].end = 0; + pointer_map[from][n].down = 0; + pointer_map[from][n].up = 0; + } else { + rfbLog("buttonparse: ignoring unknown " + "keysym: %s\n", t); + n--; + } + } else { + kcode = XKeysymToKeycode(dpy, ksym); + + pointer_map[from][n].keysym = ksym; + pointer_map[from][n].keycode = kcode; + pointer_map[from][n].button = 0; + pointer_map[from][n].end = 0; + if (! ismodkey(ksym) ) { + /* do both down then up */ + pointer_map[from][n].down = 1; + pointer_map[from][n].up = 1; + } else { + if (modisdown[kcode]) { + pointer_map[from][n].down = 0; + pointer_map[from][n].up = 1; + modisdown[kcode] = 0; + } else { + pointer_map[from][n].down = 1; + pointer_map[from][n].up = 0; + modisdown[kcode] = 1; + } + } + rfbLog(" event %d: keysym %s (0x%x) -> " + "keycode 0x%x down=%d up=%d\n", n+1, + XKeysymToString(ksym), ksym, kcode, + pointer_map[from][n].down, + pointer_map[from][n].up); + } + t = strtok(NULL, "+"); + n++; + } + + /* we must release any modifiers that are still down: */ + for (i=0; i<256; i++) { + kcode = (KeyCode) i; + if (n >= MAX_BUTTON_EVENTS) { + rfbLog("buttonparse: too many button map " + "events: %s\n", list); + break; + } + if (modisdown[kcode]) { + pointer_map[from][n].keysym = NoSymbol; + pointer_map[from][n].keycode = kcode; + pointer_map[from][n].button = 0; + pointer_map[from][n].end = 0; + pointer_map[from][n].down = 0; + pointer_map[from][n].up = 1; + modisdown[kcode] = 0; + n++; + } + } + + /* advance the source pointer position */ + (*s) += l+2; + } else { + /* single digit format */ + char str[2]; + str[0] = *q; + str[1] = '\0'; + + to = atoi(str); + if (to < 1) { + rfbLog("skipping invalid remap button \"%d\" for button" + " %d from string \"%s\"\n", + to, from, str); + } else { + rfbLog("remap button %d using \"%s\"\n", from, str); + rfbLog(" button: %d -> %d\n", from, to); + pointer_map[from][0].keysym = NoSymbol; + pointer_map[from][0].keycode = NoSymbol; + pointer_map[from][0].button = to; + pointer_map[from][0].end = 0; + pointer_map[from][0].down = 0; + pointer_map[from][0].up = 0; + } + /* advance the source pointer position */ + (*s)++; + } +} + +void initialize_pointer_map(void) { + unsigned char map[MAX_BUTTONS]; + int i, k; + /* + * This routine counts the number of pointer buttons on the X + * server (to avoid problems, even crashes, if a client has more + * buttons). And also initializes any pointer button remapping + * from -buttonmap option. + */ + + X_LOCK; + num_buttons = XGetPointerMapping(dpy, map, MAX_BUTTONS); + + if (num_buttons < 0) { + num_buttons = 0; + } + + /* FIXME: should use info in map[] */ + for (i=1; i<= MAX_BUTTONS; i++) { + for (k=0; k < MAX_BUTTON_EVENTS; k++) { + pointer_map[i][k].end = 1; + } + pointer_map[i][0].keysym = NoSymbol; + pointer_map[i][0].keycode = NoSymbol; + pointer_map[i][0].button = i; + pointer_map[i][0].end = 0; + pointer_map[i][0].down = 0; + pointer_map[i][0].up = 0; + } + + if (pointer_remap) { + /* -buttonmap, format is like: 12-21=2 */ + char *p, *q, *remap = pointer_remap; + int n; + + if ((p = strchr(remap, '=')) != NULL) { + /* undocumented max button number */ + n = atoi(p+1); + *p = '\0'; + if (n < num_buttons || num_buttons == 0) { + num_buttons = n; + } else { + rfbLog("warning: increasing number of mouse " + "buttons from %d to %d\n", num_buttons, n); + num_buttons = n; + } + } + if ((q = strchr(remap, '-')) != NULL) { + /* + * The '-' separates the 'from' and 'to' lists, + * then it is kind of like tr(1). + */ + char str[2]; + int from; + + rfbLog("remapping pointer buttons using string:\n"); + rfbLog(" \"%s\"\n", remap); + + p = remap; + q++; + i = 0; + str[1] = '\0'; + while (*p != '-') { + str[0] = *p; + from = atoi(str); + buttonparse(from, &q); + p++; + } + } + } + X_UNLOCK; +} + +/* + * Actual callback from libvncserver when it gets a pointer event. + */ +rfbClientPtr last_pointer_client = NULL; + +static void pointer(int mask, int x, int y, rfbClientPtr client) { + + if (debug_pointer && mask >= 0) { + rfbLog("pointer(mask: 0x%x, x:%4d, y:%4d)\n", mask, x, y); + } + + if (view_only) { + return; + } + if (client->viewOnly) { + return; + } + + if (mask >= 0) { + /* + * mask = -1 is a special case call from scan_for_updates() + * to flush the event queue; there is no real pointer event. + */ + got_user_input++; + got_pointer_input++; + last_pointer_client = client; + } + + /* + * The following is hopefully an improvement wrt response during + * pointer user input (window drags) for the threaded case. + * See check_user_input() for the more complicated things we do + * in the non-threaded case. + */ + if (use_threads && ! old_pointer) { +# define NEV 32 + /* storage for the event queue */ + static int mutex_init = 0; + static int nevents = 0; + static int ev[NEV][3]; + int i; + /* timer things */ + static double dt = 0.0, tmr = 0.0, maxwait = 0.4; + + if (! mutex_init) { + INIT_MUTEX(pointerMutex); + mutex_init = 1; + } + + LOCK(pointerMutex); + + /* + * If the framebuffer is being copied in another thread + * (scan_for_updates()), we will queue up to 32 pointer + * events for later. The idea is by delaying these input + * events, the screen is less likely to change during the + * copying period, and so will give rise to less window + * "tearing". + * + * Tearing is not completely eliminated because we do + * not suspend work in the other libvncserver threads. + * Maybe that is a possibility with a mutex... + */ + if (fb_copy_in_progress && mask >= 0) { + /* + * mask = -1 is an all-clear signal from + * scan_for_updates(). + * + * dt is a timer in seconds; we only queue for so long. + */ + dt += dtime(&tmr); + + if (nevents < NEV && dt < maxwait) { + i = nevents++; + ev[i][0] = mask; + ev[i][1] = x; + ev[i][2] = y; + UNLOCK(pointerMutex); + if (debug_pointer) { + rfbLog("pointer(): deferring event " + "%d\n", i); + } + return; + } + } + + /* time to send the queue */ + for (i=0; i maxwait) { + X_LOCK; + XFlush(dpy); + X_UNLOCK; + } + nevents = 0; /* reset everything */ + tmr = 0.0; + dt = 0.0; + dtime(&tmr); + + UNLOCK(pointerMutex); + } + if (mask < 0) { /* -1 just means flush the event queue */ + if (debug_pointer > 1) { + rfbLog("pointer(): flush only.\n"); + } + return; + } + + /* update the X display with the event: */ + update_pointer(mask, x, y); +} + +/* + * Send a pointer event to the X server. + */ + +void update_pointer(int mask, int x, int y) { + int i, mb; + + X_LOCK; + + if (! use_xwarppointer) { + XTestFakeMotionEvent(dpy, scr, x+off_x, y+off_y, CurrentTime); + } else { + XWarpPointer(dpy, None, window, 0, 0, 0, 0, x+off_x, y+off_y); + } + + cursor_x = x; + cursor_y = y; + + last_event = last_input = time(0); + + for (i=0; i < MAX_BUTTONS; i++) { + /* look for buttons that have be clicked or released: */ + if ( (button_mask & (1< " + "0x%x button: %d\n", button_mask, mask,i+1); + } + for (k=0; k < MAX_BUTTON_EVENTS; k++) { + int bmask = (mask & (1< num_buttons) + || mb < 1) { + rfbLog("ignoring mouse button out of " + "bounds: %d>%d mask: 0x%x -> 0x%x\n", + mb, num_buttons, button_mask, mask); + continue; + } + if (debug_pointer) { + rfbLog("pointer(): sending button %d" + " %s (event %d)\n", mb, bmask + ? "down" : "up", k+1); + } + XTestFakeButtonEvent(dpy, mb, (mask & (1<xkb_type == XkbBellNotify) { + got_bell = 1; + } + X_UNLOCK; + + if (got_bell) { + if (! all_clients_initialized()) { + rfbLog("watch_bell_event: not sending bell: " + "uninitialized clients\n"); + } else { + rfbSendBell(screen); + } + } +} +#else +void watch_bell_event() {} +#endif + + +/* + * Selection/Cutbuffer/Clipboard handlers. + */ + +int watch_selection = 1; /* normal selection/cutbuffer maintenance */ +int watch_primary = 1; /* more dicey, poll for changes in PRIMARY */ +int own_selection = 0; /* whether we currently own PRIMARY or not */ +int set_cutbuffer = 0; /* to avoid bouncing the CutText right back */ +int sel_waittime = 15; /* some seconds to skip before first send */ +Window selwin; /* special window for our selection */ + +/* + * This is where we keep our selection: the string sent TO us from VNC + * clients, and the string sent BY us to requesting X11 clients. + */ +char *xcut_string = NULL; + +/* + * Our callbacks instruct us to check for changes in the cutbuffer + * and PRIMARY selection on the local X11 display. + * + * We store the new cutbuffer and/or PRIMARY selection data in this + * constant sized array selection_str[]. + * TODO: check if malloc does not cause performance issues (esp. WRT + * SelectionNotify handling). + */ +#define PROP_MAX (131072L) +char selection_str[PROP_MAX+1]; + +/* + * An X11 (not VNC) client on the local display has requested the selection + * from us (because we are the current owner). + * + * n.b.: our caller already has the X_LOCK. + */ +void selection_request(XEvent *ev) { + XSelectionEvent notify_event; + XSelectionRequestEvent *req_event; + unsigned int length; + unsigned char *data; +#ifndef XA_LENGTH + unsigned long XA_LENGTH = XInternAtom(dpy, "LENGTH", True); +#endif + + req_event = &(ev->xselectionrequest); + notify_event.type = SelectionNotify; + notify_event.display = req_event->display; + notify_event.requestor = req_event->requestor; + notify_event.selection = req_event->selection; + notify_event.target = req_event->target; + notify_event.time = req_event->time; + + if (req_event->property == None) { + notify_event.property = req_event->target; + } else { + notify_event.property = req_event->property; + } + if (xcut_string) { + length = strlen(xcut_string); + } else { + length = 0; + } + + + if (ev->xselectionrequest.target == XA_LENGTH) { + /* length request */ + + XChangeProperty(ev->xselectionrequest.display, + ev->xselectionrequest.requestor, + ev->xselectionrequest.property, + ev->xselectionrequest.target, 32, PropModeReplace, + (unsigned char *) &length, sizeof(unsigned int)); + + } else { + /* data request */ + + data = (unsigned char *)xcut_string; + + XChangeProperty(ev->xselectionrequest.display, + ev->xselectionrequest.requestor, + ev->xselectionrequest.property, + ev->xselectionrequest.target, 8, PropModeReplace, + data, length); + } + + XSendEvent(req_event->display, req_event->requestor, False, 0, + (XEvent *)¬ify_event); + + XFlush(dpy); +} + +int all_clients_initialized() { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + int ok = 1; + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + if (cl->state != RFB_NORMAL) { + ok = 0; + break; + } + } + rfbReleaseClientIterator(iter); + + return ok; +} + +/* + * CUT_BUFFER0 property on the local display has changed, we read and + * store it and send it out to any connected VNC clients. + * + * n.b.: our caller already has the X_LOCK. + */ +void cutbuffer_send() { + Atom type; + int format, slen, dlen; + unsigned long nitems = 0, bytes_after = 0; + unsigned char* data = NULL; + + selection_str[0] = '\0'; + slen = 0; + + /* read the property value into selection_str: */ + do { + if (XGetWindowProperty(dpy, DefaultRootWindow(dpy), + XA_CUT_BUFFER0, nitems/4, PROP_MAX/16, False, + AnyPropertyType, &type, &format, &nitems, &bytes_after, + &data) == Success) { + + dlen = nitems * (format/8); + if (slen + dlen > PROP_MAX) { + /* too big */ + rfbLog("warning: truncating large CUT_BUFFER0" + " selection > %d bytes.\n", PROP_MAX); + XFree(data); + break; + } + memcpy(selection_str+slen, data, dlen); + slen += dlen; + selection_str[slen] = '\0'; + XFree(data); + } + } while (bytes_after > 0); + + selection_str[PROP_MAX] = '\0'; + + if (! all_clients_initialized()) { + rfbLog("cutbuffer_send: no send: uninitialized clients\n"); + return; /* some clients initializing, cannot send */ + } + + /* now send it to any connected VNC clients (rfbServerCutText) */ + rfbSendServerCutText(screen, selection_str, strlen(selection_str)); +} + +/* + * "callback" for our SelectionNotify polling. We try to determine if + * the PRIMARY selection has changed (checking length and first CHKSZ bytes) + * and if it has we store it and send it off to any connected VNC clients. + * + * n.b.: our caller already has the X_LOCK. + * + * TODO: if we were willing to use libXt, we could perhaps get selection + * timestamps to speed up the checking... XtGetSelectionValue(). + */ +#define CHKSZ 32 +void selection_send(XEvent *ev) { + Atom type; + int format, slen, dlen, oldlen, newlen, toobig = 0; + static int err = 0, sent_one = 0; + char before[CHKSZ], after[CHKSZ]; + unsigned long nitems = 0, bytes_after = 0; + unsigned char* data = NULL; + + /* + * remember info about our last value of PRIMARY (or CUT_BUFFER0) + * so we can check for any changes below. + */ + oldlen = strlen(selection_str); + strncpy(before, selection_str, CHKSZ); + + selection_str[0] = '\0'; + slen = 0; + + /* read in the current value of PRIMARY: */ + do { + if (XGetWindowProperty(dpy, ev->xselection.requestor, + ev->xselection.property, nitems/4, PROP_MAX/16, True, + AnyPropertyType, &type, &format, &nitems, &bytes_after, + &data) == Success) { + + dlen = nitems * (format/8); + if (slen + dlen > PROP_MAX) { + /* too big */ + toobig = 1; + XFree(data); + if (err) { /* cut down on messages */ + break; + } else { + err = 5; + } + rfbLog("warning: truncating large PRIMARY" + " selection > %d bytes.\n", PROP_MAX); + break; + } + memcpy(selection_str+slen, data, dlen); + slen += dlen; + selection_str[slen] = '\0'; + XFree(data); + } + } while (bytes_after > 0); + + if (! toobig) { + err = 0; + } else if (err) { + err--; + } + + if (! sent_one) { + /* try to force a send first time in */ + oldlen = -1; + sent_one = 1; + } + + /* look for changes in the new value */ + newlen = strlen(selection_str); + strncpy(after, selection_str, CHKSZ); + + if (oldlen == newlen && strncmp(before, after, CHKSZ) == 0) { + /* evidently no change */ + return; + } + if (newlen == 0) { + /* do not bother sending a null string out */ + return; + } + + if (! all_clients_initialized()) { + rfbLog("selection_send: no send: uninitialized clients\n"); + return; /* some clients initializing, cannot send */ + } + + /* now send it to any connected VNC clients (rfbServerCutText) */ + rfbSendServerCutText(screen, selection_str, newlen); +} + + +/* + * Routines for monitoring the VNC_CONNECT property for changes. + * The vncconnect(1) will set it on our X display. + */ + +Atom vnc_connect_prop = None; + +void read_vnc_connect_prop() { + Atom type; + int format, slen, dlen; + unsigned long nitems = 0, bytes_after = 0; + unsigned char* data = NULL; + + vnc_connect_str[0] = '\0'; + slen = 0; + + if (! vnc_connect || vnc_connect_prop == None) { + /* not active or problem with VNC_CONNECT atom */ + return; + } + + /* read the property value into vnc_connect_str: */ + do { + if (XGetWindowProperty(dpy, DefaultRootWindow(dpy), + vnc_connect_prop, nitems/4, VNC_CONNECT_MAX/16, False, + AnyPropertyType, &type, &format, &nitems, &bytes_after, + &data) == Success) { + + dlen = nitems * (format/8); + if (slen + dlen > VNC_CONNECT_MAX) { + /* too big */ + rfbLog("warning: truncating large VNC_CONNECT" + " string > %d bytes.\n", VNC_CONNECT_MAX); + XFree(data); + break; + } + memcpy(vnc_connect_str+slen, data, dlen); + slen += dlen; + vnc_connect_str[slen] = '\0'; + XFree(data); + } + } while (bytes_after > 0); + + vnc_connect_str[VNC_CONNECT_MAX] = '\0'; + rfbLog("read property VNC_CONNECT: %s\n", vnc_connect_str); +} + +/* + * This routine is periodically called to check for selection related + * and other X11 events and respond to them as needed. + */ +void watch_xevents() { + XEvent xev; + static int first = 1, sent_sel = 0; + int have_clients = screen->rfbClientHead ? 1 : 0; + time_t last_request = 0, now = time(0); + + X_LOCK; + if (first && (watch_selection || vnc_connect)) { + /* + * register desired event(s) for notification. + * PropertyChangeMask is for CUT_BUFFER0 changes. + * TODO: does this cause a flood of other stuff? + */ + XSelectInput(dpy, rootwin, PropertyChangeMask); + } + if (first && watch_selection) { + /* create fake window for our selection ownership, etc */ + selwin = XCreateSimpleWindow(dpy, rootwin, 0, 0, 1, 1, 0, 0, 0); + } + if (first && vnc_connect) { + vnc_connect_str[0] = '\0'; + vnc_connect_prop = XInternAtom(dpy, "VNC_CONNECT", False); + } + first = 0; + + /* + * There is a bug where we have to wait before sending text to + * the client... so instead of sending right away we wait a + * the few seconds. + */ + if (have_clients && watch_selection && ! sent_sel + && now > last_client + sel_waittime) { + if (XGetSelectionOwner(dpy, XA_PRIMARY) == None) { + cutbuffer_send(); + } + sent_sel = 1; + } + + /* check for CUT_BUFFER0 and VNC_CONNECT changes: */ + if (XCheckTypedEvent(dpy, PropertyNotify, &xev)) { + if (xev.type == PropertyNotify) { + if (xev.xproperty.atom == XA_CUT_BUFFER0) { + /* + * Go retrieve CUT_BUFFER0 and send it. + * + * set_cutbuffer is a flag to try to avoid + * processing our own cutbuffer changes. + */ + if (have_clients && watch_selection + && ! set_cutbuffer) { + cutbuffer_send(); + sent_sel = 1; + } + set_cutbuffer = 0; + } else if (vnc_connect && vnc_connect_prop != None + && xev.xproperty.atom == vnc_connect_prop) { + + /* + * Go retrieve VNC_CONNECT string. + */ + read_vnc_connect_prop(); + } + } + } + + if (! have_clients || ! watch_selection) { + /* + * no need to monitor selections if no current clients + * or -nosel. + */ + X_UNLOCK; + return; + } + + /* check for our PRIMARY request notification: */ + if (watch_primary) { + if (XCheckTypedEvent(dpy, SelectionNotify, &xev)) { + if (xev.type == SelectionNotify && + xev.xselection.requestor == selwin && + xev.xselection.selection == XA_PRIMARY && + xev.xselection.property != None && + xev.xselection.target == XA_STRING) { + + /* go retrieve PRIMARY and check it */ + if (now > last_client + sel_waittime + || sent_sel) { + selection_send(&xev); + } + } + } + if (now > last_request + 1) { + /* + * Every second or two, request PRIMARY, unless we + * already own it or there is no owner. + * TODO: even at this low rate we should look into + * and performance problems in odds cases, etc. + */ + last_request = now; + if (! own_selection && + XGetSelectionOwner(dpy, XA_PRIMARY) != None) { + XConvertSelection(dpy, XA_PRIMARY, XA_STRING, + XA_STRING, selwin, CurrentTime); + } + } + } + + if (! own_selection) { + /* + * no need to do the PRIMARY maintenance tasks below if + * no we do not own it (right?). + */ + X_UNLOCK; + return; + } + + /* we own PRIMARY, see if someone requested it: */ + if (XCheckTypedEvent(dpy, SelectionRequest, &xev)) { + if (xev.type == SelectionRequest && + xev.xselectionrequest.selection == XA_PRIMARY) { + selection_request(&xev); + } + } + + /* we own PRIMARY, see if we no longer own it: */ + if (XCheckTypedEvent(dpy, SelectionClear, &xev)) { + if (xev.type == SelectionClear && + xev.xselectionclear.selection == XA_PRIMARY) { + + own_selection = 0; + if (xcut_string) { + free(xcut_string); + xcut_string = NULL; + } + } + } + X_UNLOCK; +} + +/* + * hook called when a VNC client sends us some "XCut" text (rfbClientCutText). + */ +void xcut_receive(char *text, int len, rfbClientPtr cl) { + + if (cl->viewOnly) { + return; + } + if (text == NULL || len == 0) { + return; + } + + X_LOCK; + + /* associate this text with PRIMARY (and SECONDARY...) */ + if (! own_selection) { + own_selection = 1; + /* we need to grab the PRIMARY selection */ + XSetSelectionOwner(dpy, XA_PRIMARY, selwin, CurrentTime); + XFlush(dpy); + } + + /* duplicate the text string for our own use. */ + if (xcut_string != NULL) { + free(xcut_string); + } + xcut_string = (unsigned char *) + malloc((size_t) (len+1) * sizeof(unsigned char)); + strncpy(xcut_string, text, len); + xcut_string[len] = '\0'; /* make sure null terminated */ + + /* copy this text to CUT_BUFFER0 as well: */ + XChangeProperty(dpy, rootwin, XA_CUT_BUFFER0, XA_STRING, 8, + PropModeReplace, text, len); + XFlush(dpy); + + X_UNLOCK; + + set_cutbuffer = 1; +} + +void mark_hint(hint_t); + +/* + * Here begins a bit of a mess to experiment with multiple cursors ... + */ +typedef struct cursor_info { + char *data; /* data and mask pointers */ + char *mask; + int wx, wy; /* size of cursor */ + int sx, sy; /* shift to its centering point */ + int reverse; /* swap black and white */ +} cursor_info_t; + +/* main cursor */ +static char* cur_data = +" " +" x " +" xx " +" xxx " +" xxxx " +" xxxxx " +" xxxxxx " +" xxxxxxx " +" xxxxxxxx " +" xxxxx " +" xx xx " +" x xx " +" xx " +" xx " +" xx " +" " +" " +" "; + +static char* cur_mask = +"xx " +"xxx " +"xxxx " +"xxxxx " +"xxxxxx " +"xxxxxxx " +"xxxxxxxx " +"xxxxxxxxx " +"xxxxxxxxxx " +"xxxxxxxxxx " +"xxxxxxx " +"xxx xxxx " +"xx xxxx " +" xxxx " +" xxxx " +" xx " +" " +" "; +#define CUR_SIZE 18 +#define CUR_DATA cur_data +#define CUR_MASK cur_mask +cursor_info_t cur0 = {NULL, NULL, CUR_SIZE, CUR_SIZE, 0, 0, 0}; + +/* + * It turns out we can at least detect mouse is on the root window so + * show it (under -mouseX or -X) with this familiar cursor... + */ +static char* root_data = +" " +" " +" xxx xxx " +" xxxx xxxx " +" xxxxx xxxxx " +" xxxxx xxxxx " +" xxxxxxxxxx " +" xxxxxxxx " +" xxxxxx " +" xxxxxx " +" xxxxxxxx " +" xxxxxxxxxx " +" xxxxx xxxxx " +" xxxxx xxxxx " +" xxxx xxxx " +" xxx xxx " +" " +" "; + +static char* root_mask = +" " +" xxxx xxxx " +" xxxxx xxxxx " +" xxxxxx xxxxxx " +" xxxxxxx xxxxxxx " +" xxxxxxxxxxxxxx " +" xxxxxxxxxxxx " +" xxxxxxxxxx " +" xxxxxxxx " +" xxxxxxxx " +" xxxxxxxxxx " +" xxxxxxxxxxxx " +" xxxxxxxxxxxxxx " +" xxxxxxx xxxxxxx " +" xxxxxx xxxxxx " +" xxxxx xxxxx " +" xxxx xxxx " +" "; +cursor_info_t cur1 = {NULL, NULL, 18, 18, 8, 8, 1}; + +cursor_info_t *cursors[2]; +void setup_cursors(void) { + /* TODO clean this up if we ever do more cursors... */ + + cur0.data = cur_data; + cur0.mask = cur_mask; + + cur1.data = root_data; + cur1.mask = root_mask; + + cursors[0] = &cur0; + cursors[1] = &cur1; +} + +/* + * data and functions for -mouse real pointer position updates + */ +char cur_save[(4 * CUR_SIZE * CUR_SIZE)]; +int cur_save_x, cur_save_y, cur_save_w, cur_save_h; +int cur_save_cx, cur_save_cy, cur_save_which, cur_saved = 0; + +/* + * save current cursor info and the patch of non-cursor data it covers + */ +void save_mouse_patch(int x, int y, int w, int h, int cx, int cy, int which) { + int pixelsize = bpp >> 3; + char *rfb_fb = screen->frameBuffer; + int ly, i = 0; + + for (ly = y; ly < y + h; ly++) { + memcpy(cur_save+i, rfb_fb + ly * bytes_per_line + + x * pixelsize, w * pixelsize); + + i += w * pixelsize; + } + cur_save_x = x; /* patch geometry */ + cur_save_y = y; + cur_save_w = w; + cur_save_h = h; + + cur_save_which = which; /* which cursor and its position */ + cur_save_cx = cx; + cur_save_cy = cy; + + cur_saved = 1; +} + +/* + * put the non-cursor patch back in the rfb fb + */ +void restore_mouse_patch() { + int pixelsize = bpp >> 3; + char *rfb_fb = screen->frameBuffer; + int ly, i = 0; + + if (! cur_saved) { + return; /* not yet saved */ + } + + for (ly = cur_save_y; ly < cur_save_y + cur_save_h; ly++) { + memcpy(rfb_fb + ly * bytes_per_line + cur_save_x * pixelsize, + cur_save+i, cur_save_w * pixelsize); + i += cur_save_w * pixelsize; + } +} + +/* + * Descends window tree at pointer until the window cursor matches the current + * cursor. So far only used to detect if mouse is on root background or not. + * (returns 0 in that case, 1 otherwise). + * + * It seems impossible to do, but if the actual cursor could ever be + * determined we might want to hash that info on window ID or something... + */ +int tree_descend_cursor(void) { + Window r, c; + int rx, ry, wx, wy; + unsigned int mask; + int descend = 0, tries = 0, maxtries = 1; + + X_LOCK; + c = window; + while (c) { + if (++tries > maxtries) { + descend = maxtries; + break; + } + if ( XTestCompareCurrentCursorWithWindow(dpy, c) ) { + break; + } + XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &mask); + descend++; + } + X_UNLOCK; + return descend; +} + +void blackout_nearby_tiles(x, y, dt) { + int sx, sy, n, b; + int tx = x/tile_x; + int ty = y/tile_y; + + if (! blackouts) { + return; + } + if (dt < 1) { + dt = 1; + } + /* loop over a range of tiles, blacking out as needed */ + + for (sx = tx - dt; sx <= tx + dt; sx++) { + if (sx < 0 || sx >= tile_x) { + continue; + } + for (sy = ty - dt; sy <= ty + dt; sy++) { + if (sy < 0 || sy >= tile_y) { + continue; + } + n = sx + sy * ntiles_x; + if (tile_blackout[n].cover == 0) { + continue; + } + for (b=0; b <= tile_blackout[n].count; b++) { + int x1, y1, x2, y2; + x1 = tile_blackout[n].bo[b].x1; + y1 = tile_blackout[n].bo[b].y1; + x2 = tile_blackout[n].bo[b].x2; + y2 = tile_blackout[n].bo[b].y2; + zero_fb(x1, y1, x2, y2); + } + } + } +} + +/* + * Send rfbCursorPosUpdates back to clients that understand them. This + * seems to be TightVNC specific. + */ +void cursor_pos_updates(int x, int y) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + int cnt = 0; + + if (! cursor_pos) { + return; + } + /* x and y are current positions of X11 pointer on the X11 display */ + if (x == screen->cursorX && y == screen->cursorY) { + return; + } + + if (screen->cursorIsDrawn) { + rfbUndrawCursor(screen); + } + LOCK(screen->cursorMutex); + if (! screen->cursorIsDrawn) { + screen->cursorX = x; + screen->cursorY = y; + } + UNLOCK(screen->cursorMutex); + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + if (! cl->enableCursorPosUpdates) { + continue; + } + if (cl == last_pointer_client) { + /* + * special case if this client was the last one to + * send a pointer position. + */ + if (x == cursor_x && y == cursor_y) { + cl->cursorWasMoved = FALSE; + } else { + /* an X11 app evidently warped the pointer */ + if (debug_pointer) { + rfbLog("cursor_pos_updates: warp " + "detected dx=%3d dy=%3d\n", + cursor_x - x, cursor_y - y); + } + cl->cursorWasMoved = TRUE; + cnt++; + } + } else { + cl->cursorWasMoved = TRUE; + cnt++; + } + } + rfbReleaseClientIterator(iter); + + if (debug_pointer && cnt) { + rfbLog("cursor_pos_updates: sent position x=%3d y=%3d to %d" + " clients\n", x, y, cnt); + } +} + +/* + * draw one of the mouse cursors into the rfb fb + */ +void draw_mouse(int x, int y, int which, int update) { + int px, py, i, offset; + int pixelsize = bpp >> 3; + char *rfb_fb = screen->frameBuffer; + char cdata, cmask; + char *data, *mask; + int white = 255, black = 0, shade; + int x0, x1, x2, y0, y1, y2; + int cur_x, cur_y, cur_sx, cur_sy, reverse; + static int first = 1; + + if (! show_mouse) { + return; + } + if (first) { + first = 0; + setup_cursors(); + } + + data = cursors[which]->data; /* pattern data */ + mask = cursors[which]->mask; + cur_x = cursors[which]->wx; /* widths */ + cur_y = cursors[which]->wy; + cur_sx = cursors[which]->sx; /* shifts */ + cur_sy = cursors[which]->sy; + reverse = cursors[which]->reverse; /* reverse video */ + + if (indexed_colour) { + black = BlackPixel(dpy, scr) % 256; + white = WhitePixel(dpy, scr) % 256; + } + if (reverse) { + int tmp = black; + black = white; + white = tmp; + } + + /* + * notation: + * x0, y0: position after cursor shift (no edge corrections) + * x1, y1: corrected for lower boundary < 0 + * x2, y2: position + cursor width and corrected for upper boundary + */ + + x0 = x1 = x - cur_sx; /* apply shift */ + if (x1 < 0) x1 = 0; + + y0 = y1 = y - cur_sy; + if (y1 < 0) y1 = 0; + + x2 = x0 + cur_x; /* apply width for upper endpoints */ + if (x2 >= dpy_x) x2 = dpy_x - 1; + + y2 = y0 + cur_y; + if (y2 >= dpy_y) y2 = dpy_y - 1; + + /* save the patch and info about which cursor will overwrite it */ + save_mouse_patch(x1, y1, x2 - x1, y2 - y1, x, y, which); + + for (py = 0; py < cur_y; py++) { + if (y0 + py < 0 || y0 + py >= dpy_y) { + continue; /* off screen */ + } + for (px = 0; px < cur_x; px++) { + if (x0 + px < 0 || x0 + px >= dpy_x){ + continue; /* off screen */ + } + cdata = data[px + py * cur_x]; + cmask = mask[px + py * cur_x]; + + if (cmask != 'x') { + continue; /* transparent */ + } + + shade = white; + if (cdata != cmask) { + shade = black; + } + + offset = (y0 + py)*bytes_per_line + (x0 + px)*pixelsize; + + /* fill in each color byte in the fb */ + for (i=0; i < pixelsize; i++) { + rfb_fb[offset+i] = (char) shade; + } + } + } + + if (blackouts) { + /* + * loop over a range of tiles, blacking out as needed + * note we currently disable mouse drawing under blackouts. + */ + static int mx = -1, my = -1; + int skip = 0; + if (mx < 0) { + mx = x; + my = y; + } else if (mx == x && my == y) { + skip = 1; + } + mx = x; + my = y; + + if (! skip) { + blackout_nearby_tiles(x, y, 2); + } + } + + if (update) { + /* x and y of the real (X server) mouse */ + static int mouse_x = -1; + static int mouse_y = -1; + + if (x != mouse_x || y != mouse_y) { + hint_t hint; + + hint.x = x1; + hint.y = y2; + hint.w = x2 - x1; + hint.h = y2 - y1; + + mark_hint(hint); + + if (mouse_x < 0) { + mouse_x = 0; + } + if (mouse_y < 0) { + mouse_y = 0; + } + + /* XXX this ignores change of shift... */ + x1 = mouse_x - cur_sx; + if (x1 < 0) x1 = 0; + + y1 = mouse_y - cur_sy; + if (y1 < 0) y1 = 0; + + x2 = mouse_x - cur_sx + cur_x; + if (x2 >= dpy_x) x2 = dpy_x - 1; + + y2 = mouse_y - cur_sy + cur_y; + if (y2 >= dpy_y) y2 = dpy_y - 1; + + hint.x = x1; + hint.y = y2; + hint.w = x2 - x1; + hint.h = y2 - y1; + + mark_hint(hint); + + mouse_x = x; + mouse_y = y; + } + } +} + +void redraw_mouse(void) { + if (cur_saved) { + /* redraw saved mouse from info (save_mouse_patch) */ + draw_mouse(cur_save_cx, cur_save_cy, cur_save_which, 0); + } +} + +void update_mouse(void) { + Window root_w, child_w; + rfbBool ret; + int root_x, root_y, win_x, win_y, which = 0; + unsigned int mask; + + X_LOCK; + ret = XQueryPointer(dpy, rootwin, &root_w, &child_w, &root_x, &root_y, + &win_x, &win_y, &mask); + X_UNLOCK; + + if (! ret) { + return; + } + + if (show_root_cursor) { + int descend; + if ( (descend = tree_descend_cursor()) ) { + which = 0; + } else { + which = 1; + } + } + + cursor_pos_updates(root_x - off_x, root_y - off_y); + draw_mouse(root_x - off_x, root_y - off_y, which, 1); +} + +/* + * For the subwin case follows the window if it is moved. + */ +void set_offset(void) { + Window w; + if (! subwin) { + return; + } + X_LOCK; + XTranslateCoordinates(dpy, window, rootwin, 0, 0, &off_x, &off_y, &w); + X_UNLOCK; +} + +/* + * Some handling of 8bpp PseudoColor colormaps. Called for initializing + * the clients and dynamically if -flashcmap is specified. + */ +#define NCOLOR 256 +void set_colormap(void) { + static int first = 1; + static XColor color[NCOLOR], prev[NCOLOR]; + Colormap cmap; + Visual *vis; + int i, ncells, diffs = 0; + + if (first) { + screen->colourMap.count = NCOLOR; + screen->rfbServerFormat.trueColour = FALSE; + screen->colourMap.is16 = TRUE; + screen->colourMap.data.shorts = (unsigned short*) + malloc(3*sizeof(short) * NCOLOR); + } + + for (i=0; i < NCOLOR; i++) { + prev[i].red = color[i].red; + prev[i].green = color[i].green; + prev[i].blue = color[i].blue; + } + + X_LOCK; + + cmap = DefaultColormap(dpy, scr); + ncells = CellsOfScreen(ScreenOfDisplay(dpy, scr)); + vis = visual; + + if (subwin) { + XWindowAttributes attr; + + if (XGetWindowAttributes(dpy, window, &attr)) { + cmap = attr.colormap; + vis = attr.visual; + ncells = vis->map_entries; + } + } + + if (first && ncells != NCOLOR) { + if (! quiet) { + fprintf(stderr, "set_colormap: number of cells is %d " + "instead of %d.\n", ncells, NCOLOR); + } + screen->colourMap.count = ncells; + } + + if (flash_cmap && ! first) { + XWindowAttributes attr; + Window r, c; + int rx, ry, wx, wy, tries = 0; + unsigned int m; + + c = window; + while (c && tries++ < 16) { + /* XXX XQueryTree somehow? */ + XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &m); + if (c && XGetWindowAttributes(dpy, c, &attr)) { + if (attr.colormap && attr.map_installed) { + cmap = attr.colormap; + vis = attr.visual; + ncells = vis->map_entries; + break; + } + } else { + break; + } + } + } + if (ncells > NCOLOR && ! quiet) { + fprintf(stderr, "set_colormap: big problem: ncells=%d > %d\n", + ncells, NCOLOR); + } + + if (vis->class == TrueColor || vis->class == DirectColor) { + /* + * Kludge to make 8bpp TrueColor & DirectColor be like + * the StaticColor map. The ncells = 8 is "8 per subfield" + * mentioned in xdpyinfo. Looks OK... likely fortuitously. + */ + if (ncells == 8) { + ncells = NCOLOR; + } + } + + for (i=0; i < ncells; i++) { + color[i].pixel = i; + color[i].pad = 0; + } + + XQueryColors(dpy, cmap, color, ncells); + + X_UNLOCK; + + for(i=0; i < ncells; i++) { + screen->colourMap.data.shorts[i*3+0] = color[i].red; + screen->colourMap.data.shorts[i*3+1] = color[i].green; + screen->colourMap.data.shorts[i*3+2] = color[i].blue; + + if (prev[i].red != color[i].red || + prev[i].green != color[i].green || + prev[i].blue != color[i].blue ) { + diffs++; + } + } + + if (diffs && ! first) { + if (! all_clients_initialized()) { + rfbLog("set_colormap: warning: sending cmap " + "with uninitialized clients.\n"); + } + rfbSetClientColourMaps(screen, 0, ncells); + } + + first = 0; +} + +/* + * Experimental mode to force the visual of the window instead of querying + * it. Currently just used for testing or overriding some rare cases. + * Input string can be a decimal or 0x hex or something like TrueColor + * or TrueColor:24 to force a depth as well. + */ +void set_visual(char *vstring) { + int vis, defdepth = DefaultDepth(dpy, scr); + XVisualInfo vinfo; + char *p; + + if (! quiet) { + fprintf(stderr, "set_visual: %s\n", vstring); + } + + if ((p = strchr(vstring, ':')) != NULL) { + visual_depth = atoi(p+1); + *p = '\0'; + } else { + visual_depth = defdepth; + } + if (strcmp(vstring, "StaticGray") == 0) { + vis = StaticGray; + } else if (strcmp(vstring, "GrayScale") == 0) { + vis = GrayScale; + } else if (strcmp(vstring, "StaticColor") == 0) { + vis = StaticColor; + } else if (strcmp(vstring, "PseudoColor") == 0) { + vis = PseudoColor; + } else if (strcmp(vstring, "TrueColor") == 0) { + vis = TrueColor; + } else if (strcmp(vstring, "DirectColor") == 0) { + vis = DirectColor; + } else { + int v_in; + if (sscanf(vstring, "0x%x", &v_in) != 1) { + if (sscanf(vstring, "%d", &v_in) == 1) { + visual_id = (VisualID) v_in; + return; + } + fprintf(stderr, "bad -visual arg: %s\n", vstring); + exit(1); + } + visual_id = (VisualID) v_in; + return; + } + if (XMatchVisualInfo(dpy, scr, visual_depth, vis, &vinfo)) { + ; + } else if (XMatchVisualInfo(dpy, scr, defdepth, vis, &vinfo)) { + ; + } else { + fprintf(stderr, "could not find visual: %s\n", vstring); + exit(1); + } + visual_id = vinfo.visualid; +} + +/* + * Presumably under -nofb the clients will never request the framebuffer. + * But we have gotten such a request... so let's just give them + * the current view on the display. n.b. x2vnc and perhaps win2vnc + * requests a 1x1 pixel for some workaround so sadly this evidently + * nearly always happens. + */ +void nofb_hook(rfbClientPtr cl) { + static int loaded_fb = 0; + XImage *fb; + if (loaded_fb) { + return; + } + rfbLog("framebuffer requested in -nofb mode by client %s\n", cl->host); + fb = XGetImage(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, ZPixmap); + screen->frameBuffer = fb->data; + loaded_fb = 1; + screen->displayHook = NULL; +} + +int got_rfbport = 0; +int got_alwaysshared = 0; +int got_nevershared = 0; +/* + * initialize the rfb framebuffer/screen + */ +void initialize_screen(int *argc, char **argv, XImage *fb) { + int have_masks = 0; + + screen = rfbGetScreen(argc, argv, fb->width, fb->height, + fb->bits_per_pixel, 8, fb->bits_per_pixel/8); + + if (! quiet) { + fprintf(stderr, "\n"); + } + + if (! screen) { + int i; + rfbLog("\n"); + rfbLog("failed to create rfb screen.\n"); + for (i=0; i< *argc; i++) { + rfbLog("\t[%d] %s\n", i, argv[i]); + } + clean_up_exit(1); + } + +/* + * This ifdef is a transient for source compatibility for people who download + * the x11vnc.c file by itself and plop it down into their libvncserver tree. + * Remove at some point. BTW, this assumes no usage of earlier "0.7pre". + */ +#ifdef LIBVNCSERVER_VERSION +if (strcmp(LIBVNCSERVER_VERSION, "0.5") && strcmp(LIBVNCSERVER_VERSION, "0.6")) { + if (*argc != 1) { + int i; + rfbLog("*** unrecognized option(s) ***\n"); + for (i=1; i< *argc; i++) { + rfbLog("\t[%d] %s\n", i, argv[i]); + } + rfbLog("for a list of options run: x11vnc -help\n"); + clean_up_exit(1); + } +} +#endif + + screen->paddedWidthInBytes = fb->bytes_per_line; + screen->rfbServerFormat.bitsPerPixel = fb->bits_per_pixel; + screen->rfbServerFormat.depth = fb->depth; + screen->rfbServerFormat.trueColour = (uint8_t) TRUE; + have_masks = ((fb->red_mask|fb->green_mask|fb->blue_mask) != 0); + if (force_indexed_color) { + have_masks = 0; + } + + if ( ! have_masks && screen->rfbServerFormat.bitsPerPixel == 8 + && CellsOfScreen(ScreenOfDisplay(dpy,scr)) ) { + /* indexed colour */ + if (!quiet) rfbLog("Using X display with 8bpp indexed color\n"); + indexed_colour = 1; + set_colormap(); + } else { + /* general case ... */ + if (! quiet) { + rfbLog("Using X display with %dbpp depth=%d true " + "color\n", fb->bits_per_pixel, fb->depth); + } + + /* convert masks to bit shifts and max # colors */ + screen->rfbServerFormat.redShift = 0; + if ( fb->red_mask ) { + while ( ! (fb->red_mask + & (1 << screen->rfbServerFormat.redShift) ) ) { + screen->rfbServerFormat.redShift++; + } + } + screen->rfbServerFormat.greenShift = 0; + if ( fb->green_mask ) { + while ( ! (fb->green_mask + & (1 << screen->rfbServerFormat.greenShift) ) ) { + screen->rfbServerFormat.greenShift++; + } + } + screen->rfbServerFormat.blueShift = 0; + if ( fb->blue_mask ) { + while ( ! (fb->blue_mask + & (1 << screen->rfbServerFormat.blueShift) ) ) { + screen->rfbServerFormat.blueShift++; + } + } + screen->rfbServerFormat.redMax + = fb->red_mask >> screen->rfbServerFormat.redShift; + screen->rfbServerFormat.greenMax + = fb->green_mask >> screen->rfbServerFormat.greenShift; + screen->rfbServerFormat.blueMax + = fb->blue_mask >> screen->rfbServerFormat.blueShift; + } + + /* nofb is for pointer/keyboard only handling. */ + if (nofb) { + screen->frameBuffer = NULL; + screen->displayHook = nofb_hook; + } else { + screen->frameBuffer = fb->data; + } + + /* called from inetd, we need to treat stdio as our socket */ + if (inetd) { + int fd = dup(0); + if (fd < 3) { + rfbErr("dup(0) = %d failed.\n", fd); + perror("dup"); + clean_up_exit(1); + } + fclose(stdin); + fclose(stdout); + /* we keep stderr for logging */ + screen->inetdSock = fd; + screen->rfbPort = 0; + + } else if (! got_rfbport) { + screen->autoPort = TRUE; + } + + if (! got_nevershared && ! got_alwaysshared) { + if (shared) { + screen->rfbAlwaysShared = TRUE; + } else { + screen->rfbDontDisconnect = TRUE; + screen->rfbNeverShared = TRUE; + } + } + /* XXX the following is based on libvncserver defaults. */ + if (screen->rfbDeferUpdateTime == 5) { + /* XXX will be fixed someday */ + screen->rfbDeferUpdateTime = defer_update; + } + + /* event callbacks: */ + screen->newClientHook = new_client; + screen->kbdAddEvent = keyboard; + screen->ptrAddEvent = pointer; + if (watch_selection) { + screen->setXCutText = xcut_receive; + } + + if (local_cursor) { + cursor = rfbMakeXCursor(CUR_SIZE, CUR_SIZE, CUR_DATA, CUR_MASK); + screen->cursor = cursor; + } else { + screen->cursor = NULL; + } + + rfbInitServer(screen); + + bytes_per_line = screen->paddedWidthInBytes; + bpp = screen->rfbServerFormat.bitsPerPixel; + depth = screen->rfbServerFormat.depth; +} + +/* + * Take a comma separated list of geometries: WxH+X+Y and register them as + * rectangles to black out from the screen. + */ +void initialize_blackout (char *list) { + char *p, *blist = strdup(list); + int x, y, X, Y, h, w; + + p = strtok(blist, ","); + while (p) { + /* handle +/-x and +/-y */ + if (sscanf(p, "%dx%d+%d+%d", &w, &h, &x, &y) == 4) { + ; + } else if (sscanf(p, "%dx%d-%d+%d", &w, &h, &x, &y) == 4) { + x = dpy_x - x - w; + } else if (sscanf(p, "%dx%d+%d-%d", &w, &h, &x, &y) == 4) { + y = dpy_y - y - h; + } else if (sscanf(p, "%dx%d-%d-%d", &w, &h, &x, &y) == 4) { + x = dpy_x - x - w; + y = dpy_y - y - h; + } else { + if (*p != '\0') { + rfbLog("skipping invalid geometry: %s\n", p); + } + p = strtok(NULL, ","); + continue; + } + X = x + w; + Y = y + h; + if (x < 0 || x > dpy_x || y < 0 || y > dpy_y || + X < 0 || X > dpy_x || Y < 0 || Y > dpy_y) { + rfbLog("skipping invalid blackout geometry: %s x=" + "%d-%d,y=%d-%d,w=%d,h=%d\n", p, x, X, y, Y, w, h); + } else { + rfbLog("blackout rect: %s: x=%d-%d y=%d-%d\n", p, + x, X, y, Y); + + /* + * note that the black out is x1 <= x but x < x2 + * for the region. i.e. the x2, y2 are outside + * by 1 pixel. + */ + black[blackouts].x1 = x; + black[blackouts].y1 = y; + black[blackouts].x2 = X; + black[blackouts].y2 = Y; + blackouts++; + if (blackouts >= 100) { + rfbLog("too many blackouts: %d\n", blackouts); + break; + } + } + p = strtok(NULL, ","); + } + free(blist); +} + +/* + * Now that all blackout rectangles have been constructed, see what overlap + * they have with the tiles in the system. If a tile is touched by a + * blackout, record information. + */ +void blackout_tiles() { + int tx, ty; + if (! blackouts) { + return; + } + + /* + * to simplify things drop down to single copy mode, no vcr, etc... + */ + single_copytile = 1; + if (show_mouse) { + rfbLog("disabling remote mouse drawing due to blackouts\n"); + show_mouse = 0; + } + + /* loop over all tiles. */ + for (ty=0; ty < ntiles_y; ty++) { + for (tx=0; tx < ntiles_x; tx++) { + sraRegionPtr tile_reg, black_reg; + sraRect rect; + sraRectangleIterator *iter; + int n, b, x1, y1, x2, y2, cnt; + + /* tile number and coordinates: */ + n = tx + ty * ntiles_x; + x1 = tx * tile_x; + y1 = ty * tile_y; + x2 = x1 + tile_x; + y2 = y1 + tile_y; + if (x2 > dpy_x) { + x2 = dpy_x; + } + if (y2 > dpy_y) { + y2 = dpy_y; + } + + /* make regions for the tile and the blackouts: */ + black_reg = (sraRegionPtr) sraRgnCreate(); + tile_reg = (sraRegionPtr) sraRgnCreateRect(x1, y1, + x2, y2); + + tile_blackout[n].cover = 0; + tile_blackout[n].count = 0; + + /* union of blackouts */ + for (b=0; b < blackouts; b++) { + sraRegionPtr tmp_reg = (sraRegionPtr) + sraRgnCreateRect(black[b].x1, black[b].y1, + black[b].x2, black[b].y2); + + sraRgnOr(black_reg, tmp_reg); + sraRgnDestroy(tmp_reg); + } + + if (! sraRgnAnd(black_reg, tile_reg)) { + /* + * no intersection for this tile, so we + * are done. + */ + sraRgnDestroy(black_reg); + sraRgnDestroy(tile_reg); + continue; + } + + /* + * loop over rectangles that make up the blackout + * region: + */ + cnt = 0; + iter = sraRgnGetIterator(black_reg); + while (sraRgnIteratorNext(iter, &rect)) { + + /* make sure x1 < x2 and y1 < y2 */ + if (rect.x1 > rect.x2) { + int tmp = rect.x2; + rect.x2 = rect.x1; + rect.x1 = tmp; + } + if (rect.y1 > rect.y2) { + int tmp = rect.y2; + rect.y2 = rect.y1; + rect.y1 = tmp; + } + + /* store coordinates */ + tile_blackout[n].bo[cnt].x1 = rect.x1; + tile_blackout[n].bo[cnt].y1 = rect.y1; + tile_blackout[n].bo[cnt].x2 = rect.x2; + tile_blackout[n].bo[cnt].y2 = rect.y2; + + /* note if the tile is completely obscured */ + if (rect.x1 == x1 && rect.y1 == y1 && + rect.x2 == x2 && rect.y2 == y2) { + tile_blackout[n].cover = 2; + } else { + tile_blackout[n].cover = 1; + } + + if (++cnt >= 10) { + rfbLog("too many blackout rectangles " + "for tile %d=%d,%d.\n", n, tx, ty); + break; + } + } + + sraRgnReleaseIterator(iter); + sraRgnDestroy(black_reg); + sraRgnDestroy(tile_reg); + + tile_blackout[n].count = cnt; + } + } +} + +void initialize_xinerama () { +#ifndef LIBVNCSERVER_HAVE_LIBXINERAMA + rfbLog("Xinerama: Library libXinerama is not available to determine\n"); + rfbLog("Xinerama: the head geometries, consider using -blackout\n"); + rfbLog("Xinerama: if the screen is non-rectangular.\n"); +#else + XineramaScreenInfo *sc, *xineramas; + sraRegionPtr black_region, tmp_region; + sraRectangleIterator *iter; + sraRect rect; + char *bstr, *tstr; + int ev, er, i, n, rcnt; + + if (! XineramaQueryExtension(dpy, &ev, &er)) { + rfbLog("Xinerama: disabling: display does not support it.\n"); + xinerama = 0; + return; + } + if (! XineramaIsActive(dpy)) { + /* n.b. change to XineramaActive(dpy, window) someday */ + rfbLog("Xinerama: disabling: not active on display.\n"); + xinerama = 0; + return; + } + + /* n.b. change to XineramaGetData() someday */ + xineramas = XineramaQueryScreens(dpy, &n); + rfbLog("Xinerama: number of sub-screens: %d\n", n); + + if (n == 1) { + rfbLog("Xinerama: no blackouts needed (only one" + " sub-screen)\n"); + XFree(xineramas); + return; /* must be OK w/o change */ + } + + black_region = sraRgnCreateRect(0, 0, dpy_x, dpy_y); + + sc = xineramas; + for (i=0; ix_org; + y = sc->y_org; + w = sc->width; + h = sc->height; + + tmp_region = sraRgnCreateRect(x, y, x + w, y + h); + + sraRgnSubtract(black_region, tmp_region); + sraRgnDestroy(tmp_region); + sc++; + } + XFree(xineramas); + + if (sraRgnEmpty(black_region)) { + rfbLog("Xinerama: no blackouts needed (screen fills" + " rectangle)\n"); + sraRgnDestroy(black_region); + return; + } + + /* max len is 10000x10000+10000+10000 (23 chars) per geometry */ + rcnt = (int) sraRgnCountRects(black_region); + bstr = (char *) malloc(30 * rcnt * sizeof(char)); + tstr = (char *) malloc(30 * sizeof(char)); + bstr[0] = '\0'; + + iter = sraRgnGetIterator(black_region); + while (sraRgnIteratorNext(iter, &rect)) { + int x, y, w, h; + + /* make sure x1 < x2 and y1 < y2 */ + if (rect.x1 > rect.x2) { + int tmp = rect.x2; + rect.x2 = rect.x1; + rect.x1 = tmp; + } + if (rect.y1 > rect.y2) { + int tmp = rect.y2; + rect.y2 = rect.y1; + rect.y1 = tmp; + } + x = rect.x1; + y = rect.y1; + w = rect.x2 - x; + h = rect.y2 - y; + sprintf(tstr, "%dx%d+%d+%d,", w, h, x, y); + strcat(bstr, tstr); + } + initialize_blackout(bstr); + + free(bstr); + free(tstr); +#endif +} + +/* + * Fill the framebuffer with zero for the prescribed rectangle + */ +void zero_fb(x1, y1, x2, y2) { + int pixelsize = bpp >> 3; + int line, fill = 0; + char *dst; + + if (x1 < 0 || x2 <= x1 || x2 > dpy_x) { + return; + } + if (y1 < 0 || y2 <= y1 || y2 > dpy_y) { + return; + } + + dst = screen->frameBuffer + y1 * bytes_per_line + x1 * pixelsize; + line = y1; + while (line++ < y2) { + memset(dst, fill, (size_t) (x2 - x1) * pixelsize); + dst += bytes_per_line; + } +} + +/* + * Fill the framebuffer with zeros for each blackout region + */ +void blackout_regions() { + int i; + for (i=0; i < blackouts; i++) { + zero_fb(black[i].x1, black[i].y1, black[i].x2, black[i].y2); + } +} + +/* + * setup tile numbers and allocate the tile and hint arrays: + */ +void initialize_tiles() { + + ntiles_x = (dpy_x - 1)/tile_x + 1; + ntiles_y = (dpy_y - 1)/tile_y + 1; + ntiles = ntiles_x * ntiles_y; + + tile_has_diff = (unsigned char *) + malloc((size_t) (ntiles * sizeof(unsigned char))); + tile_tried = (unsigned char *) + malloc((size_t) (ntiles * sizeof(unsigned char))); + tile_blackout = (tile_blackout_t *) + malloc((size_t) (ntiles * sizeof(tile_blackout_t))); + tile_region = (region_t *) malloc((size_t) (ntiles * sizeof(region_t))); + + tile_row = (XImage **) + malloc((size_t) ((ntiles_x + 1) * sizeof(XImage *))); + tile_row_shm = (XShmSegmentInfo *) + malloc((size_t) ((ntiles_x + 1) * sizeof(XShmSegmentInfo))); + + /* there will never be more hints than tiles: */ + hint_list = (hint_t *) malloc((size_t) (ntiles * sizeof(hint_t))); +} + +/* + * silly function to factor dpy_y until fullscreen shm is not bigger than max. + * should always work unless dpy_y is a large prime or something... under + * failure fs_factor remains 0 and no fullscreen updates will be tried. + */ +void set_fs_factor(int max) { + int f, fac = 1, n = dpy_y; + + if ( (bpp/8) * dpy_x * dpy_y <= max ) { + fs_factor = 1; + return; + } + for (f=2; f <= 101; f++) { + while (n % f == 0) { + n = n / f; + fac = fac * f; + if ( (bpp/8) * dpy_x * (dpy_y/fac) <= max ) { + fs_factor = fac; + return; + } + } + } +} + +/* + * set up an XShm image + */ +int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h, + char *name) { + + XImage *xim; + + shm->shmid = -1; + shm->shmaddr = (char *) -1; + *ximg_ptr = NULL; + + if (nofb) { + return 1; + } + + X_LOCK; + + if (! using_shm) { + /* we only need the XImage created */ + xim = XCreateImage(dpy, visual, depth, ZPixmap, 0, NULL, w, h, + BitmapPad(dpy), 0); + + X_UNLOCK; + + if (xim == NULL) { + rfbErr("XCreateImage(%s) failed.\n", name); + return 0; + } + xim->data = (char *) malloc(xim->bytes_per_line * xim->height); + if (xim->data == NULL) { + rfbErr("XCreateImage(%s) data malloc failed.\n", name); + return 0; + } + if (flip_byte_order) { + static int reported = 0; + char *bo; + if (xim->byte_order == LSBFirst) { + bo = "MSBFirst"; + xim->byte_order = MSBFirst; + xim->bitmap_bit_order = MSBFirst; + } else { + bo = "LSBFirst"; + xim->byte_order = LSBFirst; + xim->bitmap_bit_order = LSBFirst; + } + if (! reported && ! quiet) { + rfbLog("changing XImage byte order" + " to %s\n", bo); + reported = 1; + } + } + + *ximg_ptr = xim; + return 1; + } + + xim = XShmCreateImage(dpy, visual, depth, ZPixmap, NULL, shm, w, h); + + if (xim == NULL) { + rfbErr("XShmCreateImage(%s) failed.\n", name); + X_UNLOCK; + return 0; + } + + *ximg_ptr = xim; + + shm->shmid = shmget(IPC_PRIVATE, + xim->bytes_per_line * xim->height, IPC_CREAT | 0777); + + if (shm->shmid == -1) { + rfbErr("shmget(%s) failed.\n", name); + perror("shmget"); + + XDestroyImage(xim); + *ximg_ptr = NULL; + + X_UNLOCK; + return 0; + } + + shm->shmaddr = xim->data = (char *) shmat(shm->shmid, 0, 0); + + if (shm->shmaddr == (char *)-1) { + rfbErr("shmat(%s) failed.\n", name); + perror("shmat"); + + XDestroyImage(xim); + *ximg_ptr = NULL; + + shmctl(shm->shmid, IPC_RMID, 0); + shm->shmid = -1; + + X_UNLOCK; + return 0; + } + + shm->readOnly = False; + + if (! XShmAttach(dpy, shm)) { + rfbErr("XShmAttach(%s) failed.\n", name); + XDestroyImage(xim); + *ximg_ptr = NULL; + + shmdt(shm->shmaddr); + shm->shmaddr = (char *) -1; + + shmctl(shm->shmid, IPC_RMID, 0); + shm->shmid = -1; + + X_UNLOCK; + return 0; + } + + X_UNLOCK; + return 1; +} + +void shm_delete(XShmSegmentInfo *shm) { + if (! using_shm) { + return; + } + if (shm->shmaddr != (char *) -1) { + shmdt(shm->shmaddr); + } + if (shm->shmid != -1) { + shmctl(shm->shmid, IPC_RMID, 0); + } +} + +void shm_clean(XShmSegmentInfo *shm, XImage *xim) { + if (! using_shm || nofb) { + return; + } + X_LOCK; + if (shm->shmid != -1) { + XShmDetach(dpy, shm); + } + if (xim != NULL) { + XDestroyImage(xim); + } + X_UNLOCK; + + shm_delete(shm); +} + +void initialize_shm() { + int i; + + /* set all shm areas to "none" before trying to create any */ + scanline_shm.shmid = -1; + scanline_shm.shmaddr = (char *) -1; + scanline = NULL; + fullscreen_shm.shmid = -1; + fullscreen_shm.shmaddr = (char *) -1; + fullscreen = NULL; + for (i=1; i<=ntiles_x; i++) { + tile_row_shm[i].shmid = -1; + tile_row_shm[i].shmaddr = (char *) -1; + tile_row[i] = NULL; + } + + /* the scanline (e.g. 1280x1) shared memory area image: */ + + if (! shm_create(&scanline_shm, &scanline, dpy_x, 1, "scanline")) { + clean_up_exit(1); + } + + /* + * the fullscreen (e.g. 1280x1024/fs_factor) shared memory area image: + * (we cut down the size of the shm area to try avoid and shm segment + * limits, e.g. the default 1MB on Solaris) + */ + set_fs_factor(1024 * 1024); + if (fs_frac >= 1.0) { + fs_frac = 1.1; + fs_factor = 0; + } + if (! fs_factor) { + rfbLog("warning: fullscreen updates are disabled.\n"); + } else { + if (! shm_create(&fullscreen_shm, &fullscreen, dpy_x, + dpy_y/fs_factor, "fullscreen")) { + clean_up_exit(1); + } + } + + /* + * for copy_tiles we need a lot of shared memory areas, one for + * each possible run length of changed tiles. 32 for 1024x768 + * and 40 for 1280x1024, etc. + */ + + for (i=1; i<=ntiles_x; i++) { + if (! shm_create(&tile_row_shm[i], &tile_row[i], tile_x * i, + tile_y, "tile_row")) { + if (i == 1) { + clean_up_exit(1); + } + rfbLog("shm: Error creating shared memory tile-row for" + " len=%d,\n", i); + rfbLog("shm: reverting to -onetile mode. If this" + " problem persists\n"); + rfbLog("shm: try using the -onetile or -noshm options" + " to limit\n"); + rfbLog("shm: shared memory usage, or run ipcrm(1)" + " to manually\n"); + rfbLog("shm: delete unattached shm segments.\n"); + /* n.b.: "i" not "1", a kludge for cleanup */ + single_copytile = i; + } + if (single_copytile && i >= 1) { + /* only need 1x1 tiles */ + break; + } + } +} + + +/* + * A hint is a rectangular region built from 1 or more adjacent tiles + * glued together. Ultimately, this information in a single hint is sent + * to libvncserver rather than sending each tile separately. + */ +void create_tile_hint(int x, int y, int th, hint_t *hint) { + int w = dpy_x - x; + int h = dpy_y - y; + + if (w > tile_x) { + w = tile_x; + } + if (h > th) { + h = th; + } + + hint->x = x; + hint->y = y; + hint->w = w; + hint->h = h; +} + +void extend_tile_hint(int x, int y, int th, hint_t *hint) { + int w = dpy_x - x; + int h = dpy_y - y; + + if (w > tile_x) { + w = tile_x; + } + if (h > th) { + h = th; + } + + if (hint->x > x) { /* extend to the left */ + hint->w += hint->x - x; + hint->x = x; + } + if (hint->y > y) { /* extend upward */ + hint->h += hint->y - y; + hint->y = y; + } + + if (hint->x + hint->w < x + w) { /* extend to the right */ + hint->w = x + w - hint->x; + } + if (hint->y + hint->h < y + h) { /* extend downward */ + hint->h = y + h - hint->y; + } +} + +void save_hint(hint_t hint, int loc) { + /* simply copy it to the global array for later use. */ + hint_list[loc].x = hint.x; + hint_list[loc].y = hint.y; + hint_list[loc].w = hint.w; + hint_list[loc].h = hint.h; +} + +/* + * Glue together horizontal "runs" of adjacent changed tiles into one big + * rectangle change "hint" to be passed to the vnc machinery. + */ +void hint_updates() { + hint_t hint; + int x, y, i, n, ty, th; + int hint_count = 0, in_run = 0; + + for (y=0; y < ntiles_y; y++) { + for (x=0; x < ntiles_x; x++) { + n = x + y * ntiles_x; + + if (tile_has_diff[n]) { + ty = tile_region[n].first_line; + th = tile_region[n].last_line - ty + 1; + if (! in_run) { + create_tile_hint( x * tile_x, + y * tile_y + ty, th, &hint); + in_run = 1; + } else { + extend_tile_hint( x * tile_x, + y * tile_y + ty, th, &hint); + } + } else { + if (in_run) { + /* end of a row run of altered tiles: */ + save_hint(hint, hint_count++); + in_run = 0; + } + } + } + if (in_run) { /* save the last row run */ + save_hint(hint, hint_count++); + in_run = 0; + } + } + + + for (i=0; i < hint_count; i++) { + /* pass update info to vnc: */ + mark_hint(hint_list[i]); + } +} + +/* + * Notifies libvncserver of a changed hint rectangle. + */ +void mark_hint(hint_t hint) { + int x = hint.x; + int y = hint.y; + int w = hint.w; + int h = hint.h; + + rfbMarkRectAsModified(screen, x, y, x + w, y + h); +} + +/* + * Notifies libvncserver of a changed tile rectangle. + */ +void mark_tile(int x, int y, int height) { + int w = dpy_x - x; + int h = dpy_y - y; + + if (w > tile_x) { + w = tile_x; + } + + /* height is the height of the changed portion of the tile */ + if (h > height) { + h = height; + } + + rfbMarkRectAsModified(screen, x, y, x + w, y + h); +} + +/* + * Simply send each modified tile separately to the vnc machinery: + * (i.e. no hints) + */ +void tile_updates() { + int x, y, n, ty, th; + + for (y=0; y < ntiles_y; y++) { + for (x=0; x < ntiles_x; x++) { + n = x + y * ntiles_x; + + if (tile_has_diff[n]) { + ty = tile_region[n].first_line; + th = tile_region[n].last_line - ty + 1; + + mark_tile(x * tile_x, y * tile_y + ty, th); + } + } + } +} + + +/* + * copy_tiles() gives a slight improvement over copy_tile() since + * adjacent runs of tiles are done all at once there is some savings + * due to contiguous memory access. Not a great speedup, but in + * some cases it can be up to 2X. Even more on a SunRay where no + * graphics hardware is involved in the read. Generally, graphics + * devices are optimized for write, not read, so we are limited by + * the read bandwidth, sometimes only 5 MB/sec on otherwise fast + * hardware. + */ + +int *first_line = NULL, *last_line; +unsigned short *left_diff, *right_diff; + +void copy_tiles(int tx, int ty, int nt) { + int x, y, line; + int size_x, size_y, width1, width2; + int off, len, n, dw, dx, t; + int w1, w2, dx1, dx2; /* tmps for normal and short tiles */ + int pixelsize = bpp >> 3; + int first_min, last_max; + + int restored_patch = 0; /* for show_mouse */ + + char *src, *dst, *s_src, *s_dst, *m_src, *m_dst; + char *h_src, *h_dst; + if (! first_line) { + /* allocate arrays first time in. */ + int n = ntiles_x + 1; + first_line = (int *) malloc((size_t) (n * sizeof(int))); + last_line = (int *) malloc((size_t) (n * sizeof(int))); + left_diff = (unsigned short *) + malloc((size_t) (n * sizeof(unsigned short))); + right_diff = (unsigned short *) + malloc((size_t) (n * sizeof(unsigned short))); + } + + x = tx * tile_x; + y = ty * tile_y; + + size_x = dpy_x - x; + if ( size_x > tile_x * nt ) { + size_x = tile_x * nt; + width1 = tile_x; + width2 = tile_x; + } else { + /* short tile */ + width1 = tile_x; /* internal tile */ + width2 = size_x - (nt - 1) * tile_x; /* right hand tile */ + } + + size_y = dpy_y - y; + if ( size_y > tile_y ) { + size_y = tile_y; + } + + n = tx + ty * ntiles_x; /* number of the first tile */ + + if (blackouts && tile_blackout[n].cover == 2) { + /* + * If there are blackouts and this tile is completely covered + * no need to poll screen or do anything else.. + * n.b. we are int single copy_tile mode: nt=1 + */ + tile_has_diff[n] = 0; + return; + } + + X_LOCK; + /* read in the whole tile run at once: */ + if ( using_shm && size_x == tile_x * nt && size_y == tile_y ) { + /* general case: */ + XShmGetImage(dpy, window, tile_row[nt], x, y, AllPlanes); + } else { + /* + * No shm or near bottom/rhs edge case: + * (but only if tile size does not divide screen size) + */ + XGetSubImage(dpy, window, x, y, size_x, size_y, AllPlanes, + ZPixmap, tile_row[nt], 0, 0); + } + X_UNLOCK; + + if (blackouts && tile_blackout[n].cover == 1) { + /* + * If there are blackouts and this tile is partially covered + * we should re-black-out the portion. + * n.b. we are int single copy_tile mode: nt=1 + */ + int x1, x2, y1, y2, b; + int w, s, fill = 0; + + for (b=0; b < tile_blackout[n].count; b++) { + char *b_dst = tile_row[nt]->data; + + x1 = tile_blackout[n].bo[b].x1 - x; + y1 = tile_blackout[n].bo[b].y1 - y; + x2 = tile_blackout[n].bo[b].x2 - x; + y2 = tile_blackout[n].bo[b].y2 - y; + + w = (x2 - x1) * pixelsize; + s = x1 * pixelsize; + + for (line = 0; line < size_y; line++) { + if (y1 <= line && line < y2) { + memset(b_dst + s, fill, (size_t) w); + } + b_dst += tile_row[nt]->bytes_per_line; + } + } + } + + /* + * Some awkwardness wrt the little remote mouse patch we display. + * When threaded we want to have as small a window of time + * as possible when the mouse image is not in the fb, otherwise + * a libvncserver thread may send the uncorrected patch to the + * clients. + */ + if (show_mouse && use_threads && cur_saved) { + /* check for overlap */ + if (cur_save_x + cur_save_w > x && x + size_x > cur_save_x && + cur_save_y + cur_save_h > y && y + size_y > cur_save_y) { + + /* restore the real data to the rfb fb */ + restore_mouse_patch(); + restored_patch = 1; + } + } + + src = tile_row[nt]->data; + dst = screen->frameBuffer + y * bytes_per_line + x * pixelsize; + + s_src = src; + s_dst = dst; + + for (t=1; t <= nt; t++) { + first_line[t] = -1; + } + /* find the first line with difference: */ + w1 = width1 * pixelsize; + w2 = width2 * pixelsize; + + + /* foreach line: */ + for (line = 0; line < size_y; line++) { + /* foreach horizontal tile: */ + for (t=1; t <= nt; t++) { + if (first_line[t] != -1) { + continue; + } + + off = (t-1) * w1; + if (t == nt) { + len = w2; /* possible short tile */ + } else { + len = w1; + } + + if (memcmp(s_dst + off, s_src + off, len)) { + first_line[t] = line; + } + } + s_src += tile_row[nt]->bytes_per_line; + s_dst += bytes_per_line; + } + + /* see if there were any differences for any tile: */ + first_min = -1; + for (t=1; t <= nt; t++) { + tile_tried[n+(t-1)] = 1; + if (first_line[t] != -1) { + if (first_min == -1 || first_line[t] < first_min) { + first_min = first_line[t]; + } + } + } + if (first_min == -1) { + /* no tile has a difference, note this and get out: */ + for (t=1; t <= nt; t++) { + tile_has_diff[n+(t-1)] = 0; + } + if (restored_patch) { + redraw_mouse(); + } + return; + } else { + /* + * at least one tile has a difference. make sure info + * is recorded (e.g. sometimes we guess tiles and they + * came in with tile_has_diff 0) + */ + for (t=1; t <= nt; t++) { + if (first_line[t] == -1) { + tile_has_diff[n+(t-1)] = 0; + } else { + tile_has_diff[n+(t-1)] = 1; + } + } + } + + m_src = src + (tile_row[nt]->bytes_per_line * size_y); + m_dst = dst + (bytes_per_line * size_y); + + for (t=1; t <= nt; t++) { + last_line[t] = first_line[t]; + } + + /* find the last line with difference: */ + w1 = width1 * pixelsize; + w2 = width2 * pixelsize; + + /* foreach line: */ + for (line = size_y - 1; line > first_min; line--) { + + m_src -= tile_row[nt]->bytes_per_line; + m_dst -= bytes_per_line; + + /* foreach tile: */ + for (t=1; t <= nt; t++) { + if (first_line[t] == -1 + || last_line[t] != first_line[t]) { + /* tile has no changes or already done */ + continue; + } + + off = (t-1) * w1; + if (t == nt) { + len = w2; /* possible short tile */ + } else { + len = w1; + } + if (memcmp(m_dst + off, m_src + off, len)) { + last_line[t] = line; + } + } + } + + /* + * determine the farthest down last changed line + * will be used below to limit our memcpy() to the framebuffer. + */ + last_max = -1; + for (t=1; t <= nt; t++) { + if (first_line[t] == -1) { + continue; + } + if (last_max == -1 || last_line[t] > last_max) { + last_max = last_line[t]; + } + } + + /* look for differences on left and right hand edges: */ + for (t=1; t <= nt; t++) { + left_diff[t] = 0; + right_diff[t] = 0; + } + + h_src = src; + h_dst = dst; + + w1 = width1 * pixelsize; + w2 = width2 * pixelsize; + + dx1 = (width1 - tile_fuzz) * pixelsize; + dx2 = (width2 - tile_fuzz) * pixelsize; + dw = tile_fuzz * pixelsize; + + /* foreach line: */ + for (line = 0; line < size_y; line++) { + /* foreach tile: */ + for (t=1; t <= nt; t++) { + if (first_line[t] == -1) { + /* tile has no changes at all */ + continue; + } + + off = (t-1) * w1; + if (t == nt) { + dx = dx2; /* possible short tile */ + if (dx <= 0) { + break; + } + } else { + dx = dx1; + } + + if (! left_diff[t] && memcmp(h_dst + off, + h_src + off, dw)) { + left_diff[t] = 1; + } + if (! right_diff[t] && memcmp(h_dst + off + dx, + h_src + off + dx, dw) ) { + right_diff[t] = 1; + } + } + h_src += tile_row[nt]->bytes_per_line; + h_dst += bytes_per_line; + } + + /* now finally copy the difference to the rfb framebuffer: */ + s_src = src + tile_row[nt]->bytes_per_line * first_min; + s_dst = dst + bytes_per_line * first_min; + + for (line = first_min; line <= last_max; line++) { + /* for I/O speed we do not do this tile by tile */ + memcpy(s_dst, s_src, size_x * pixelsize); + s_src += tile_row[nt]->bytes_per_line; + s_dst += bytes_per_line; + } + + if (restored_patch) { + redraw_mouse(); + } + + /* record all the info in the region array for this tile: */ + for (t=1; t <= nt; t++) { + int s = t - 1; + + if (first_line[t] == -1) { + /* tile unchanged */ + continue; + } + tile_region[n+s].first_line = first_line[t]; + tile_region[n+s].last_line = last_line[t]; + + tile_region[n+s].top_diff = 0; + tile_region[n+s].bot_diff = 0; + if ( first_line[t] < tile_fuzz ) { + tile_region[n+s].top_diff = 1; + } + if ( last_line[t] > (size_y - 1) - tile_fuzz ) { + tile_region[n+s].bot_diff = 1; + } + + tile_region[n+s].left_diff = left_diff[t]; + tile_region[n+s].right_diff = right_diff[t]; + } + +} + +/* + * The copy_tile() call in the loop below copies the changed tile into + * the rfb framebuffer. Note that copy_tile() sets the tile_region + * struct to have info about the y-range of the changed region and also + * whether the tile edges contain diffs (within distance tile_fuzz). + * + * We use this tile_region info to try to guess if the downward and right + * tiles will have diffs. These tiles will be checked later in the loop + * (since y+1 > y and x+1 > x). + * + * See copy_tiles_backward_pass() for analogous checking upward and + * left tiles. + */ +int copy_all_tiles() { + int x, y, n, m; + int diffs = 0; + + for (y=0; y < ntiles_y; y++) { + for (x=0; x < ntiles_x; x++) { + n = x + y * ntiles_x; + + if (tile_has_diff[n]) { + copy_tiles(x, y, 1); + } + if (! tile_has_diff[n]) { + /* + * n.b. copy_tiles() may have detected + * no change and reset tile_has_diff to 0. + */ + continue; + } + diffs++; + + /* neighboring tile downward: */ + if ( (y+1) < ntiles_y && tile_region[n].bot_diff) { + m = x + (y+1) * ntiles_x; + if (! tile_has_diff[m]) { + tile_has_diff[m] = 2; + } + } + /* neighboring tile to right: */ + if ( (x+1) < ntiles_x && tile_region[n].right_diff) { + m = (x+1) + y * ntiles_x; + if (! tile_has_diff[m]) { + tile_has_diff[m] = 2; + } + } + } + } + return diffs; +} + +/* + * Routine analogous to copy_all_tiles() above, but for horizontal runs + * of adjacent changed tiles. + */ +int copy_all_tile_runs() { + int x, y, n, m, i; + int diffs = 0; + int in_run = 0, run = 0; + int ntave = 0, ntcnt = 0; + + for (y=0; y < ntiles_y; y++) { + for (x=0; x < ntiles_x + 1; x++) { + n = x + y * ntiles_x; + + if (x != ntiles_x && tile_has_diff[n]) { + in_run = 1; + run++; + } else { + if (! in_run) { + in_run = 0; + run = 0; + continue; + } + copy_tiles(x - run, y, run); + + ntcnt++; + ntave += run; + diffs += run; + + /* neighboring tile downward: */ + for (i=1; i <= run; i++) { + if ((y+1) < ntiles_y + && tile_region[n-i].bot_diff) { + m = (x-i) + (y+1) * ntiles_x; + if (! tile_has_diff[m]) { + tile_has_diff[m] = 2; + } + } + } + + /* neighboring tile to right: */ + if (((x-1)+1) < ntiles_x + && tile_region[n-1].right_diff) { + m = ((x-1)+1) + y * ntiles_x; + if (! tile_has_diff[m]) { + tile_has_diff[m] = 2; + } + + /* note that this starts a new run */ + in_run = 1; + run = 1; + } else { + in_run = 0; + run = 0; + } + } + } + } + return diffs; +} + +/* + * Here starts a bunch of heuristics to guess/detect changed tiles. + * They are: + * copy_tiles_backward_pass, fill_tile_gaps/gap_try, grow_islands/island_try + */ + +/* + * Try to predict whether the upward and/or leftward tile has been modified. + * copy_all_tiles() has already done downward and rightward tiles. + */ +int copy_tiles_backward_pass() { + int x, y, n, m; + int diffs = 0; + + for (y = ntiles_y - 1; y >= 0; y--) { + for (x = ntiles_x - 1; x >= 0; x--) { + n = x + y * ntiles_x; /* number of this tile */ + + if (! tile_has_diff[n]) { + continue; + } + + m = x + (y-1) * ntiles_x; /* neighboring tile upward */ + + if (y >= 1 && ! tile_has_diff[m] && tile_region[n].top_diff) { + if (! tile_tried[m]) { + tile_has_diff[m] = 2; + copy_tiles(x, y-1, 1); + } + } + + m = (x-1) + y * ntiles_x; /* neighboring tile to left */ + + if (x >= 1 && ! tile_has_diff[m] && tile_region[n].left_diff) { + if (! tile_tried[m]) { + tile_has_diff[m] = 2; + copy_tiles(x-1, y, 1); + } + } + } + } + for (n=0; n < ntiles; n++) { + if (tile_has_diff[n]) { + diffs++; + } + } + return diffs; +} + +void gap_try(int x, int y, int *run, int *saw, int along_x) { + int n, m, i, xt, yt; + + n = x + y * ntiles_x; + + if (! tile_has_diff[n]) { + if (*saw) { + (*run)++; /* extend the gap run. */ + } + return; + } + if (! *saw || *run == 0 || *run > gaps_fill) { + *run = 0; /* unacceptable run. */ + *saw = 1; + return; + } + + for (i=1; i <= *run; i++) { /* iterate thru the run. */ + if (along_x) { + xt = x - i; + yt = y; + } else { + xt = x; + yt = y - i; + } + + m = xt + yt * ntiles_x; + if (tile_tried[m]) { /* do not repeat tiles */ + continue; + } + + copy_tiles(xt, yt, 1); + } + *run = 0; + *saw = 1; +} + +/* + * Look for small gaps of unchanged tiles that may actually contain changes. + * E.g. when paging up and down in a web broswer or terminal there can + * be a distracting delayed filling in of such gaps. gaps_fill is the + * tweak parameter that sets the width of the gaps that are checked. + * + * BTW, grow_islands() is actually pretty successful at doing this too... + */ +int fill_tile_gaps() { + int x, y, run, saw; + int n, diffs = 0; + + /* horizontal: */ + for (y=0; y < ntiles_y; y++) { + run = 0; + saw = 0; + for (x=0; x < ntiles_x; x++) { + gap_try(x, y, &run, &saw, 1); + } + } + + /* vertical: */ + for (x=0; x < ntiles_x; x++) { + run = 0; + saw = 0; + for (y=0; y < ntiles_y; y++) { + gap_try(x, y, &run, &saw, 0); + } + } + + for (n=0; n < ntiles; n++) { + if (tile_has_diff[n]) { + diffs++; + } + } + return diffs; +} + +void island_try(int x, int y, int u, int v, int *run) { + int n, m; + + n = x + y * ntiles_x; + m = u + v * ntiles_x; + + if (tile_has_diff[n]) { + (*run)++; + } else { + *run = 0; + } + + if (tile_has_diff[n] && ! tile_has_diff[m]) { + /* found a discontinuity */ + + if (tile_tried[m]) { + return; + } else if (*run < grow_fill) { + return; + } + + copy_tiles(u, v, 1); + } +} + +/* + * Scan looking for discontinuities in tile_has_diff[]. Try to extend + * the boundary of the discontinuity (i.e. make the island larger). + * Vertical scans are skipped since they do not seem to yield much... + */ +int grow_islands() { + int x, y, n, run; + int diffs = 0; + + /* + * n.b. the way we scan here should keep an extension going, + * and so also fill in gaps effectively... + */ + + /* left to right: */ + for (y=0; y < ntiles_y; y++) { + run = 0; + for (x=0; x <= ntiles_x - 2; x++) { + island_try(x, y, x+1, y, &run); + } + } + /* right to left: */ + for (y=0; y < ntiles_y; y++) { + run = 0; + for (x = ntiles_x - 1; x >= 1; x--) { + island_try(x, y, x-1, y, &run); + } + } + for (n=0; n < ntiles; n++) { + if (tile_has_diff[n]) { + diffs++; + } + } + return diffs; +} + +/* + * copy the whole X screen to the rfb framebuffer. For a large enough + * number of changed tiles, this is faster than tiles scheme at retrieving + * the info from the X server. Bandwidth to client and compression time + * are other issues... use -fs 1.0 to disable. + */ +void copy_screen() { + int pixelsize = bpp >> 3; + char *rfb_fb; + int i, y, block_size; + + block_size = (dpy_x * (dpy_y/fs_factor) * pixelsize); + + rfb_fb = screen->frameBuffer; + y = 0; + + X_LOCK; + + /* screen may be too big for 1 shm area, so broken into fs_factor */ + for (i=0; i < fs_factor; i++) { + if (using_shm) { + XShmGetImage(dpy, window, fullscreen, 0, y, AllPlanes); + } else { + XGetSubImage(dpy, window, 0, y, fullscreen->width, + fullscreen->height, AllPlanes, ZPixmap, fullscreen, + 0, 0); + } + memcpy(rfb_fb, fullscreen->data, (size_t) block_size); + + y += dpy_y / fs_factor; + rfb_fb += block_size; + } + + X_UNLOCK; + + if (blackouts) { + blackout_regions(); + } + + rfbMarkRectAsModified(screen, 0, 0, dpy_x, dpy_y); +} + +/* profiling routines */ + +double dtime(double *t_old) { + /* + * usage: call with 0.0 to initialize, subsequent calls give + * the time differences. + */ + double t_now, dt; + struct timeval now; + + gettimeofday(&now, NULL); + t_now = now.tv_sec + ( (double) now.tv_usec/1000000. ); + if (*t_old == 0) { + *t_old = t_now; + return t_now; + } + dt = t_now - *t_old; + *t_old = t_now; + return(dt); +} + + +/* + * Utilities for managing the "naps" to cut down on amount of polling. + */ +void nap_set(int tile_cnt) { + + if (count == 0) { + /* roll up check for all NSCAN scans */ + nap_ok = 0; + if (naptile && nap_diff_count < 2 * NSCAN * naptile) { + /* "2" is a fudge to permit a bit of bg drawing */ + nap_ok = 1; + } + nap_diff_count = 0; + } + + if (show_mouse) { + /* kludge for the up to 4 tiles the mouse patch could occupy */ + if ( tile_cnt > 4) { + last_event = time(0); + } + } else if (tile_cnt != 0) { + last_event = time(0); + } +} + +void nap_sleep(int ms, int split) { + int i, input = got_user_input; + + /* split up a long nap to improve the wakeup time */ + for (i=0; i 0) { + int dt = (int) (now - last_event); + int ms = 1500; + + /* if no activity, pause here for a second or so. */ + if (dt > screen_blank) { + nap_sleep(ms, 8); + return; + } + } + if (naptile && nap_ok && tile_cnt < naptile) { + int ms = napfac * waitms; + ms = ms > napmax ? napmax : ms; + if (now - last_input <= 2) { + nap_ok = 0; + } else { + nap_sleep(ms, 1); + } + } +} + +/* + * This is called to avoid a ~20 second timeout in libvncserver. + * May no longer be needed. + */ +void ping_clients(int tile_cnt) { + static time_t last_send = 0; + time_t now = time(0); + + if (rfbMaxClientWait < 20000) { + rfbMaxClientWait = 20000; + rfbLog("reset rfbMaxClientWait to %d ms.\n", + rfbMaxClientWait); + } + if (tile_cnt) { + last_send = now; + } else if (now - last_send > 1) { + /* Send small heartbeat to client */ + rfbMarkRectAsModified(screen, 0, 0, 1, 1); + last_send = now; + } +} + +/* + * scan_display() wants to know if this tile can be skipped due to + * blackout regions: (no data compare is done, just a quick geometric test) + */ +int blackout_line_skip(int n, int x, int y, int rescan, int *tile_count) { + + if (tile_blackout[n].cover == 2) { + tile_has_diff[n] = 0; + return 1; /* skip it */ + + } else if (tile_blackout[n].cover == 1) { + int w, x1, y1, x2, y2, b, hit = 0; + if (x + NSCAN > dpy_x) { + w = dpy_x - x; + } else { + w = NSCAN; + } + + for (b=0; b < tile_blackout[n].count; b++) { + + /* n.b. these coords are in full display space: */ + x1 = tile_blackout[n].bo[b].x1; + x2 = tile_blackout[n].bo[b].x2; + y1 = tile_blackout[n].bo[b].y1; + y2 = tile_blackout[n].bo[b].y2; + + if (x2 - x1 < w) { + /* need to cover full width */ + continue; + } + if (y1 <= y && y < y2) { + hit = 1; + break; + } + } + if (hit) { + if (! rescan) { + tile_has_diff[n] = 0; + } else { + *tile_count += tile_has_diff[n]; + } + return 1; /* skip */ + } + } + return 0; /* do not skip */ +} + +/* + * scan_display() wants to know if this changed tile can be skipped due + * to blackout regions (we do an actual compare to find the changed region). + */ +int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src, + int w, int pixelsize) { + + int i, x1, y1, x2, y2, b, hit = 0; + int beg = -1, end = -1; + + if (tile_blackout[n].cover == 0) { + return 0; /* 0 means do not skip it. */ + } else if (tile_blackout[n].cover == 2) { + return 1; /* 1 means skip it. */ + } + + /* tile has partial coverage: */ + + for (i=0; i < w * pixelsize; i++) { + if (*(dst+i) != *(src+i)) { + beg = i/pixelsize; /* beginning difference */ + break; + } + } + for (i = w * pixelsize - 1; i >= 0; i--) { + if (*(dst+i) != *(src+i)) { + end = i/pixelsize; /* ending difference */ + break; + } + } + if (beg < 0 || end < 0) { + /* problem finding range... */ + return 0; + } + + /* loop over blackout rectangles: */ + for (b=0; b < tile_blackout[n].count; b++) { + + /* y in full display space: */ + y1 = tile_blackout[n].bo[b].y1; + y2 = tile_blackout[n].bo[b].y2; + + /* x relative to tile origin: */ + x1 = tile_blackout[n].bo[b].x1 - x; + x2 = tile_blackout[n].bo[b].x2 - x; + + if (y1 > y || y >= y2) { + continue; + } + if (x1 <= beg && end <= x2) { + hit = 1; + break; + } + } + if (hit) { + return 1; + } else { + return 0; + } +} + +/* + * Loop over 1-pixel tall horizontal scanlines looking for changes. + * Record the changes in tile_has_diff[]. Scanlines in the loop are + * equally spaced along y by NSCAN pixels, but have a slightly random + * starting offset ystart ( < NSCAN ) from scanlines[]. + */ +int scan_display(int ystart, int rescan) { + char *src, *dst; + int pixelsize = bpp >> 3; + int x, y, w, n; + int tile_count = 0; + int whole_line = 1, nodiffs = 0; + + y = ystart; + + while (y < dpy_y) { + + /* grab the horizontal scanline from the display: */ + X_LOCK; + if (using_shm) { + XShmGetImage(dpy, window, scanline, 0, y, AllPlanes); + } else { + XGetSubImage(dpy, window, 0, y, scanline->width, + scanline->height, AllPlanes, ZPixmap, scanline, + 0, 0); + } + X_UNLOCK; + + /* for better memory i/o try the whole line at once */ + src = scanline->data; + dst = screen->frameBuffer + y * bytes_per_line; + + if (whole_line && ! memcmp(dst, src, bytes_per_line)) { + /* no changes anywhere in scan line */ + nodiffs = 1; + if (! rescan) { + y += NSCAN; + continue; + } + } + + x = 0; + while (x < dpy_x) { + n = (x/tile_x) + (y/tile_y) * ntiles_x; + + if (blackouts) { + if (blackout_line_skip(n, x, y, rescan, + &tile_count)) { + x += NSCAN; + continue; + } + } + + if (rescan) { + if (nodiffs || tile_has_diff[n]) { + tile_count += tile_has_diff[n]; + x += NSCAN; + continue; + } + } + + /* set ptrs to correspond to the x offset: */ + src = scanline->data + x * pixelsize; + dst = screen->frameBuffer + y * bytes_per_line + + x * pixelsize; + + /* compute the width of data to be compared: */ + if (x + NSCAN > dpy_x) { + w = dpy_x - x; + } else { + w = NSCAN; + } + + if (memcmp(dst, src, w * pixelsize)) { + /* found a difference, record it: */ + if (! blackouts) { + tile_has_diff[n] = 1; + tile_count++; + } else { + if (blackout_line_cmpskip(n, x, y, + dst, src, w, pixelsize)) { + tile_has_diff[n] = 0; + } else { + tile_has_diff[n] = 1; + tile_count++; + } + } + } + x += NSCAN; + } + y += NSCAN; + } + return tile_count; +} + +/* + * toplevel for the scanning, rescanning, and applying the heuristics. + */ +void scan_for_updates() { + int i, tile_count, tile_diffs; + double frac1 = 0.1; /* tweak parameter to try a 2nd scan_display() */ + double frac2 = 0.35; /* or 3rd */ + for (i=0; i < ntiles; i++) { + tile_has_diff[i] = 0; + tile_tried[i] = 0; + } + + /* + * n.b. this program has only been tested so far with + * tile_x = tile_y = NSCAN = 32! + */ + + count++; + count %= NSCAN; + + if (count % (NSCAN/4) == 0) { + /* some periodic maintenance */ + + if (subwin) { + set_offset(); /* follow the subwindow */ + } + if (indexed_colour) { /* check for changed colormap */ + set_colormap(); + } + } + + if (show_mouse && ! use_threads) { + /* single-thread is safe to do it here for all scanning */ + restore_mouse_patch(); + } + + /* scan with the initial y to the jitter value from scanlines: */ + scan_in_progress = 1; + tile_count = scan_display(scanlines[count], 0); + + nap_set(tile_count); + + if (fs_factor && frac1 >= fs_frac) { + /* make frac1 < fs_frac if fullscreen updates are enabled */ + frac1 = fs_frac/2.0; + } + + if (tile_count > frac1 * ntiles) { + /* + * many tiles have changed, so try a rescan (since it should + * be short compared to the many upcoming copy_tiles() calls) + */ + + /* this check is done to skip the extra scan_display() call */ + if (! fs_factor || tile_count <= fs_frac * ntiles) { + int cp, tile_count_old = tile_count; + + /* choose a different y shift for the 2nd scan: */ + cp = (NSCAN - count) % NSCAN; + + tile_count = scan_display(scanlines[cp], 1); + + if (tile_count >= (1 + frac2) * tile_count_old) { + /* on a roll... do a 3rd scan */ + cp = (NSCAN - count + 7) % NSCAN; + tile_count = scan_display(scanlines[cp], 1); + } + } + scan_in_progress = 0; + + /* + * At some number of changed tiles it is better to just + * copy the full screen at once. I.e. time = c1 + m * r1 + * where m is number of tiles, r1 is the copy_tiles() + * time, and c1 is the scan_display() time: for some m + * it crosses the full screen update time. + * + * We try to predict that crossover with the fs_frac + * fudge factor... seems to be about 1/2 the total number + * of tiles. n.b. this ignores network bandwidth, + * compression time etc... + * + * Use -fs 1.0 to disable on slow links. + */ + if (fs_factor && tile_count > fs_frac * ntiles) { + fb_copy_in_progress = 1; + copy_screen(); + if (show_mouse || cursor_pos) { + if (show_mouse && ! use_threads) { + redraw_mouse(); + } + update_mouse(); + } + fb_copy_in_progress = 0; + if (use_threads && ! old_pointer) { + pointer(-1, 0, 0, NULL); + } + nap_check(tile_count); + return; + } + } + scan_in_progress = 0; + + /* copy all tiles with differences from display to rfb framebuffer: */ + fb_copy_in_progress = 1; + + if (single_copytile) { + /* + * Old way, copy I/O one tile at a time. + */ + tile_diffs = copy_all_tiles(); + } else { + /* + * New way, does runs of horizontal tiles at once. + * Note that below, for simplicity, the extra tile finding + * (e.g. copy_tiles_backward_pass) is done the old way. + */ + tile_diffs = copy_all_tile_runs(); + } + + /* + * This backward pass for upward and left tiles complements what + * was done in copy_all_tiles() for downward and right tiles. + */ + tile_diffs = copy_tiles_backward_pass(); + + /* Given enough tile diffs, try the islands: */ + if (grow_fill && tile_diffs > 4) { + tile_diffs = grow_islands(); + } + + /* Given enough tile diffs, try the gaps: */ + if (gaps_fill && tile_diffs > 4) { + tile_diffs = fill_tile_gaps(); + } + + fb_copy_in_progress = 0; + if (use_threads && ! old_pointer) { + /* + * tell the pointer handler it can process any queued + * pointer events: + */ + pointer(-1, 0, 0, NULL); + } + + if (blackouts) { + /* ignore any diffs in completely covered tiles */ + int x, y, n; + for (y=0; y < ntiles_y; y++) { + for (x=0; x < ntiles_x; x++) { + n = x + y * ntiles_x; + if (tile_blackout[n].cover == 2) { + tile_has_diff[n] = 0; + } + } + } + } + + if (use_hints) { + hint_updates(); /* use krfb/x0rfbserver hints algorithm */ + } else { + tile_updates(); /* send each tile change individually */ + } + + /* Work around threaded rfbProcessClientMessage() calls timeouts */ + if (use_threads) { + ping_clients(tile_diffs); + } + + /* Handle the remote mouse pointer */ + if (show_mouse || cursor_pos) { + if (show_mouse && ! use_threads) { + redraw_mouse(); + } + update_mouse(); + } + + nap_check(tile_diffs); +} + +int check_user_input(double, int *); + +void watch_loop(void) { + int cnt = 0; + double dt = 0.0; + + if (use_threads) { + rfbRunEventLoop(screen, -1, TRUE); + } + + while (1) { + + got_user_input = 0; + got_pointer_input = 0; + got_keyboard_input = 0; + + if (! use_threads) { + rfbProcessEvents(screen, -1); + if (check_user_input(dt, &cnt)) { + /* true means loop back for more input */ + continue; + } + } + + if (shut_down) { + clean_up_exit(0); + } + + watch_xevents(); + check_connect_inputs(); + + if (! screen->rfbClientHead) { /* waiting for a client */ + usleep(200 * 1000); + continue; + } + + if (nofb) { /* no framebuffer polling needed */ + if (cursor_pos) { + update_mouse(); + } + continue; + } + + if (watch_bell) { + /* + * check for any bell events. + * n.b. assumes -nofb folks do not want bell... + */ + watch_bell_event(); + } + if (! show_dragging && button_mask) { + /* if any button is pressed do not update screen */ + /* XXX consider: use_threads || got_pointer_input */ + X_LOCK; + XFlush(dpy); + X_UNLOCK; + } else { + /* for timing the scan to try to detect thrashing */ + double tm = 0.0; + dtime(&tm); + + rfbUndrawCursor(screen); + scan_for_updates(); + + dt = dtime(&tm); + } + + /* sleep a bit to lessen load */ + usleep(waitms * 1000); + cnt++; + } +} + +/* + * We need to handle user input, particularly pointer input, carefully. + * This function is only called when non-threaded. Note that + * rfbProcessEvents() only processes *one* pointer event per call, + * so if we interlace it with scan_for_updates(), we can get swamped + * with queued up pointer inputs. And if the pointer inputs are inducing + * large changes on the screen (e.g. window drags), the whole thing + * bogs down miserably and only comes back to life at some time after + * one stops moving the mouse. So, to first approximation, we are trying + * to eat as much user input here as we can using some hints from the + * duration of the previous scan_for_updates() call (in dt). + * + * note: we do this even under -nofb + * + * return of 1 means watch_loop should short-circuit and reloop, + * return of 0 means watch_loop should proceed to scan_for_updates(). + */ + +int check_user_input(double dt, int *cnt) { + + if (old_pointer) { + /* every n-th drops thru to scan */ + if ((got_user_input || ui_skip < 0) && *cnt % ui_skip != 0) { + *cnt++; + XFlush(dpy); + return 1; /* short circuit watch_loop */ + } else { + return 0; + } + } + + if (got_keyboard_input) { + if (*cnt % ui_skip != 0) { + *cnt++; + return 1; /* short circuit watch_loop */ + } + /* otherwise continue with pointer input */ + } + + if (got_pointer_input) { + int eaten = 0, miss = 0, max_eat = 50; + int g, g_in; + double spin = 0.0, tm = 0.0; + double quick_spin_fac = 0.40; + double grind_spin_time = 0.175; + + + dtime(&tm); + g = g_in = got_pointer_input; + + /* + * Try for some "quick" pointer input processing. + * + * About as fast as we can, we try to process user input + * calling rfbProcessEvents or rfbCheckFds. We do this + * for a time on order of the last scan_for_updates() time, + * dt, but if we stop getting user input we break out. + * We will also break out if we have processed max_eat + * inputs. + * + * Note that rfbCheckFds() does not send any framebuffer + * updates, so is more what we want here, although it is + * likely they have all be sent already. + */ + while (1) { + rfbCheckFds(screen, 1000); + XFlush(dpy); + + spin += dtime(&tm); + + if (spin > quick_spin_fac * dt) { + /* get out if spin time comparable to last scan time */ + break; + } + if (got_pointer_input > g) { + g = got_pointer_input; + if (eaten++ < max_eat) { + continue; + } + } else { + miss++; + } + if (miss > 1) { /* 1 means out on 2nd miss */ + break; + } + } + + + /* + * Probably grinding with a lot of fb I/O if dt is + * this large. (need to do this more elegantly) + * + * Current idea is to spin our wheels here *not* processing + * any fb I/O, but still processing the user input. + * This user input goes to the X display and changes it, + * but we don't poll it while we "rest" here for a time + * on order of dt, the previous scan_for_updates() time. + * We also break out if we miss enough user input. + */ + if (dt > grind_spin_time) { + int i, ms, split = 30; + double shim; + + /* + * Break up our pause into 'split' steps. + * We get at most one input per step. + */ + shim = 0.75 * dt / split; + + ms = (int) (1000 * shim); + + /* cutoff how long the pause can be */ + if (split * ms > 300) { + ms = 300 / split; + } + + spin = 0.0; + tm = 0.0; + dtime(&tm); + + g = got_pointer_input; + miss = 0; + for (i=0; i g) { + XFlush(dpy); + miss = 0; + } else { + miss++; + } + g = got_pointer_input; + if (miss > 2) { + break; + } + if (1000 * spin > ms * split) { + break; + } + } + } + } + return 0; +} + +void print_help() { + char help[] = +"\n" +"x11vnc: allow VNC connections to real X11 displays.\n" +"\n" +"Typical usage is:\n" +"\n" +" Run this command in a shell on the remote machine \"far-host\":\n" +"\n" +" x11vnc -display :0\n" +"\n" +" Then run this in another window on the machine you are sitting at:\n" +"\n" +" vncviewer far-host:0\n" +"\n" +"Once x11vnc establishes connections with the X11 server and starts\n" +"listening as a VNC server it will print out a string: PORT=XXXX where\n" +"XXXX is typically 5900 (the default VNC port). One would next run something\n" +"like this on the local machine: \"vncviewer host:N\" where N is XXXX - 5900.\n" +"\n" +"By default x11vnc will not allow the screen to be shared and it will\n" +"exit as soon as a client disconnects. See -shared and -forever below\n" +"to override these protections.\n" +"\n" +"For additional info see: http://www.karlrunge.com/x11vnc/\n" +" and http://www.karlrunge.com/x11vnc/#faq\n" +"\n" +"Options:\n" +"\n" +"-display disp X11 server display to connect to, the X server process\n" +" must be running on same machine and support MIT-SHM.\n" +" Equivalent to setting the DISPLAY env. variable.\n" +"-id windowid Show the window corresponding to not the\n" +" entire display. Warning: bugs! new toplevels missed!...\n" +"-flashcmap In 8bpp indexed color, let the installed colormap flash\n" +" as the pointer moves from window to window (slow).\n" +"-notruecolor Force 8bpp indexed color even if it looks like TrueColor.\n" +"\n" +"-visual n Experimental option: probably does not do what you\n" +" think. It simply *forces* the visual used for the\n" +" framebuffer; this may be a bad thing... It is useful for\n" +" testing and for some workarounds. n may be a decimal\n" +" number, or 0x hex. Run xdpyinfo(1) for the values.\n" +" One may also use \"TrueColor\", etc. see \n" +" for a list. If the string ends in \":m\" for better\n" +" or for worse the visual depth is forced to be m.\n" +"\n" +"-viewonly All clients can only watch (default %s).\n" +"-shared VNC display is shared (default %s).\n" +"-forever Keep listening for more connections rather than exiting\n" +" as soon as the first client(s) disconnect. Same as -many\n" +"-connect string For use with \"vncviewer -listen\" reverse connections.\n" +" If string has the form \"host\" or \"host:port\"\n" +" the connection is made once at startup. Use commas\n" +" for a list. If string contains \"/\" it is a file to\n" +" periodically check for new hosts. The first line is\n" +" read and then file is truncated.\n" +"-vncconnect Monitor the VNC_CONNECT X property set by vncconnect(1).\n" +"-auth file Set the X authority file to be \"file\", equivalent to\n" +" setting the XAUTHORITY env. var to \"file\" before startup.\n" +"-allow addr1[,addr2..] Only allow client connections from IP addresses matching\n" +" the comma separated list of numerical addresses.\n" +" Can be a prefix, e.g. \"192.168.100.\" to match a\n" +" simple subnet, for more control build libvncserver with\n" +" libwrap support.\n" +"-localhost Same as -allow 127.0.0.1\n" +"-passwdfile filename Specify libvncserver -passwd via the first line of file\n" +" \"filename\" instead of via command line. Note: this\n" +" is a simple plaintext passwd, see also -rfbauth below.\n" +"-accept string Run a command (possibly to prompt the user at the\n" +" X11 display) to decide whether an incoming client\n" +" should be allowed to connect or not. \"string\" is\n" +" an external command run via system(3) (see below for\n" +" special cases). Be sure to quote \"string\" if it\n" +" contains spaces, etc. The RFB_CLIENT_IP environment\n" +" variable will be set to the incoming client IP number\n" +" and the port in RFB_CLIENT_PORT (or -1 if unavailable).\n" +" The x11vnc process id will be in RFB_X11VNC_PID and a\n" +" client id number in RFB_CLIENT_ID. If the external\n" +" command returns 0 the client is accepted, otherwise\n" +" the client is rejected. See below for an extension to\n" +" accept a client view-only.\n" +"\n" +" If \"string\" is \"popup\" then a builtin popup window\n" +" is used. The popup will time out after 120 seconds,\n" +" use \"popup:N\" to modify the timeout to N seconds\n" +" (use 0 for no timeout)\n" +"\n" +" If \"string\" is \"xmessage\" then an xmessage(1)\n" +" invocation is used for the command.\n" +"\n" +" Both \"popup\" and \"xmessage\" will present an option\n" +" for accepting the client \"View-Only\" (the client\n" +" can only watch). This option will not be presented if\n" +" -viewonly has been specified, in which case the entire\n" +" display is view only.\n" +"\n" +" If the user supplied command is prefixed with something\n" +" like \"yes:0,no:*,view:3 mycommand ...\" then this\n" +" associates the numerical command return code with\n" +" the actions: accept, reject, and accept-view-only,\n" +" respectively. Use \"*\" instead of a number to indicate\n" +" the default action (in case the command returns an\n" +" unexpected value). E.g. \"no:*\" is a good choice.\n" +"\n" +" Note that x11vnc blocks while the external command or\n" +" or popup is running (other clients may see no updates\n" +" during this period).\n" +"\n" +" More -accept tricks: use \"popupmouse\" to only allow\n" +" mouse clicks in the builtin popup to be recognized.\n" +" Similarly use \"popupkey\" to only recognize keystroke\n" +" responses. All 3 of the popup keywords can be followed\n" +" by +N+M to supply a position for the popup window.\n" +" The default is to center the popup window.\n" +"\n" +"-gone string As -accept string, except to run a user supplied command\n" +" when a client goes away (disconnects). Unlike -accept,\n" +" the command return code is not interpreted by x11vnc.\n" +"\n" +"-inetd Launched by inetd(1): stdio instead of listening socket.\n" +" Note: if you are not redirecting stderr to a log file\n" +" you must also specify the -q option.\n" +"\n" +"-noshm Do not use the MIT-SHM extension for the polling.\n" +" Remote displays can be polled this way: be careful this\n" +" can use large amounts of network bandwidth. This is\n" +" also of use if the local machine has a limited number\n" +" of shm segments and -onetile is not sufficient.\n" +"-flipbyteorder Sometimes needed if remotely polled host has different\n" +" endianness. Ignored unless -noshm is set.\n" +"-blackout string Black out rectangles on the screen. string is a comma\n" +" separated list of WxH+X+Y type geometries for each rect.\n" +"-xinerama If your screen is composed of multiple monitors\n" +" glued together via XINERAMA, and that screen is\n" +" non-rectangular this option will try to guess the areas\n" +" to black out (if your system has libXinerama).\n" +"\n" +"-o logfile Write stderr messages to file \"logfile\" instead of\n" +" to the terminal. Same as -logfile.\n" +"-q Be quiet by printing less informational output to\n" +" stderr. Same as -quiet.\n" +"-bg Go into the background after screen setup. Messages to\n" +" stderr are lost unless -o logfile is used. Something\n" +" like this could be useful in a script:\n" +" port=`ssh $host \"x11vnc -display :0 -bg\" | grep PORT`\n" +" port=`echo \"$port\" | sed -e 's/PORT=//'`\n" +" port=`expr $port - 5900`\n" +" vncviewer $host:$port\n" +"\n" +"-modtweak Handle AltGr/Shift modifiers for differing languages\n" +" between client and host (default %s).\n" +"-nomodtweak Send the keysym directly to the X server.\n" +"-remap string Read keysym remappings from file \"string\". Format is\n" +" one pair of keysyms per line (can be name or hex value).\n" +" \"string\" can also be of form: key1-key2,key3-key4...\n" +" To map a key to a button click, use the fake keysyms\n" +" \"Button1\", ..., etc. E.g. -remap Super_R-Button2\n" +"-nobell Do not watch for XBell events.\n" +"-nofb Ignore framebuffer: only process keyboard and pointer.\n" +"-nosel Do not manage exchange of X selection/cutbuffer.\n" +"-noprimary Do not poll the PRIMARY selection for changes and send\n" +" back to clients. PRIMARY is still set on received\n" +" changes, however.\n" +"\n" +"-nocursor Do not have the viewer show a local cursor.\n" +"-mouse Draw a 2nd cursor at the current X pointer position.\n" +"-mouseX As -mouse, but also draw an X on root background.\n" +"-X Shorthand for -mouseX -nocursor.\n" +"-xwarppointer Move the pointer with XWarpPointer instead of XTEST\n" +" (try as a workaround if pointer behaves poorly, e.g.\n" +" on touchscreens or other non-standard setups).\n" +"-cursorpos Send the X cursor position back to all vnc clients that\n" +" support the TightVNC CursorPosUpdates extension.\n" +"-buttonmap string String to remap mouse buttons. Format: IJK-LMN, this\n" +" maps buttons I -> L, etc., e.g. -buttonmap 13-31\n" +"\n" +" Button presses can also be mapped to keystrokes: replace\n" +" a button digit on the right of the dash with ::\n" +" or :+: etc. for multiple keys. For example,\n" +" if the viewing machine has a mouse-wheel (buttons 4 5)\n" +" but the x11vnc side does not, these will do scrolls:\n" +" -buttonmap 12345-123:Prior::Next:\n" +" -buttonmap 12345-123:Up+Up+Up::Down+Down+Down:\n" +"\n" +" If you include a modifier like \"Shift_L\" the\n" +" modifier's up/down state is toggled, e.g. to send\n" +" \"The\" use :Shift_L+t+Shift_L+h+e: (the 1st one is\n" +" shift down and the 2nd one is shift up). (note: the\n" +" initial state of the modifier is ignored and not reset)\n" +" To include button events use \"Button1\", ... etc.\n" +"\n" +"-nodragging Do not update the display during mouse dragging events\n" +" (mouse motion with a button held down). Greatly\n" +" improves response on slow setups, but you lose all\n" +" visual feedback for drags, text selection, and some\n" +" menu traversals.\n" +"-old_pointer Do not use the new pointer input handling mechanisms.\n" +" See check_input() and pointer() for details.\n" +"-input_skip n For the old pointer handling when non-threaded: try to\n" +" read n user input events before scanning display. n < 0\n" +" means to act as though there is always user input.\n" +"\n" +"-debug_pointer Print debugging output for every pointer event.\n" +"-debug_keyboard Print debugging output for every keyboard event.\n" +"\n" +"-defer time Time in ms to wait for updates before sending to client\n" +" [rfbDeferUpdateTime] (default %d).\n" +"-wait time Time in ms to pause between screen polls. Used to cut\n" +" down on load (default %d).\n" +"-nap Monitor activity and if low take longer naps between\n" +" polls to really cut down load when idle (default %s).\n" +"-sigpipe string Broken pipe (SIGPIPE) handling. \"string\" can be\n" +" \"ignore\" or \"exit\". For \"ignore\" libvncserver\n" +" will handle the abrupt loss of a client and continue,\n" +" for \"exit\" x11vnc will cleanup and exit at the 1st\n" +" broken connection. Default is \"ignore\".\n" +#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD +"-threads Whether or not to use the threaded libvncserver\n" +"-nothreads algorithm [rfbRunEventLoop] (default %s).\n" +#endif +"\n" +"-fs f If the fraction of changed tiles in a poll is greater\n" +" than f, the whole screen is updated (default %.2f).\n" +"-onetile Do not use the new copy_tiles() framebuffer mechanism,\n" +" just use 1 shm tile for polling. Same as -old_copytile.\n" +" Limits shm segments used to 3.\n" +"-gaps n Heuristic to fill in gaps in rows or cols of n or\n" +" less tiles. Used to improve text paging (default %d).\n" +"-grow n Heuristic to grow islands of changed tiles n or wider\n" +" by checking the tile near the boundary (default %d).\n" +"-fuzz n Tolerance in pixels to mark a tiles edges as changed\n" +" (default %d).\n" +"-hints Use krfb/x0rfbserver hints (glue changed adjacent\n" +" horizontal tiles into one big rectangle) (default %s).\n" +"-nohints Do not use hints; send each tile separately.\n" +"%s\n" +"\n" +"These options are passed to libvncserver:\n" +"\n" +; + fprintf(stderr, help, + view_only ? "on":"off", + shared ? "on":"off", + use_modifier_tweak ? "on":"off", + defer_update, + waitms, + take_naps ? "on":"off", +#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD + use_threads ? "on":"off", +#endif + fs_frac, + gaps_fill, + grow_fill, + tile_fuzz, + use_hints ? "on":"off", + "" + ); + + rfbUsage(); + exit(1); +} + +/* + * choose a desktop name + */ +#define MAXN 256 + +char *this_host() { + char host[MAXN]; +#ifdef LIBVNCSERVER_HAVE_GETHOSTNAME + if (gethostname(host, MAXN) == 0) { + return strdup(host); + } +#endif + return NULL; +} + +char *choose_title(char *display) { + static char title[(MAXN+10)]; + strcpy(title, "x11vnc"); + + if (display == NULL) { + display = getenv("DISPLAY"); + } + if (display == NULL) { + return title; + } + title[0] = '\0'; + if (display[0] == ':') { + if (this_host() != NULL) { + strncpy(title, this_host(), MAXN - strlen(title)); + } + } + strncat(title, display, MAXN - strlen(title)); + if (subwin) { + char *name; + if (XFetchName(dpy, window, &name)) { + strncat(title, " ", MAXN - strlen(title)); + strncat(title, name, MAXN - strlen(title)); + } + } + return title; +} + +/* + * check blacklist for OSs with tight shm limits. + */ +int limit_shm(void) { + struct utsname ut; + int limit = 0; + + if (uname(&ut) == -1) { + return 0; + } + if (!strcmp(ut.sysname, "SunOS")) { + char *r = ut.release; + if (*r == '5' && *(r+1) == '.') { + if (strchr("2345678", *(r+2)) != NULL) { + limit = 1; + } + } + } + if (limit && ! quiet) { + fprintf(stderr, "reducing shm usage on %s %s (adding " + "-onetile)\n", ut.sysname, ut.release); + } + return limit; +} + +int main(int argc, char** argv) { + + XImage *fb; + int i, op, ev, er, maj, min; + char *use_dpy = NULL; + char *auth_file = NULL; + char *arg, *visual_str = NULL; + char *logfile = NULL; + char *passwdfile = NULL; + int pw_loc = -1; + int dt = 0; + int bg = 0; + int got_rfbwait = 0; + int got_deferupdate = 0, got_defer = 0; + + /* used to pass args we do not know about to rfbGetScreen(): */ + int argc2 = 1; char *argv2[128]; + + argv2[0] = strdup(argv[0]); + + for (i=1; i < argc; i++) { + /* quick-n-dirty --option handling. */ + arg = argv[i]; + if (strstr(arg, "--") == arg) { + arg++; + } + + if (!strcmp(arg, "-display")) { + use_dpy = argv[++i]; + } else if (!strcmp(arg, "-id")) { + if (sscanf(argv[++i], "0x%x", &subwin) != 1) { + if (sscanf(argv[i], "%d", &subwin) != 1) { + fprintf(stderr, "bad -id arg: %s\n", + argv[i]); + exit(1); + } + } + } else if (!strcmp(arg, "-visual")) { + visual_str = argv[++i]; + } else if (!strcmp(arg, "-flashcmap")) { + flash_cmap = 1; + } else if (!strcmp(arg, "-notruecolor")) { + force_indexed_color = 1; + } else if (!strcmp(arg, "-viewonly")) { + view_only = 1; + } else if (!strcmp(arg, "-passwdfile")) { + passwdfile = argv[++i]; + } else if (!strcmp(arg, "-shared")) { + shared = 1; + } else if (!strcmp(arg, "-auth")) { + auth_file = argv[++i]; + } else if (!strcmp(arg, "-allow")) { + allow_list = argv[++i]; + } else if (!strcmp(arg, "-localhost")) { + allow_list = "127.0.0.1"; + } else if (!strcmp(arg, "-accept")) { + accept_cmd = argv[++i]; + } else if (!strcmp(arg, "-gone")) { + gone_cmd = argv[++i]; + } else if (!strcmp(arg, "-many") + || !strcmp(arg, "-forever")) { + connect_once = 0; + } else if (!strcmp(arg, "-connect")) { + i++; + if (strchr(arg, '/')) { + client_connect_file = argv[i]; + } else { + client_connect = strdup(argv[i]); + } + } else if (!strcmp(arg, "-vncconnect")) { + vnc_connect = 1; + } else if (!strcmp(arg, "-inetd")) { + inetd = 1; + } else if (!strcmp(arg, "-noshm")) { + using_shm = 0; + } else if (!strcmp(arg, "-flipbyteorder")) { + flip_byte_order = 1; + } else if (!strcmp(arg, "-modtweak")) { + use_modifier_tweak = 1; + } else if (!strcmp(arg, "-nomodtweak")) { + use_modifier_tweak = 0; + } else if (!strcmp(arg, "-remap")) { + remap_file = argv[++i]; + } else if (!strcmp(arg, "-blackout")) { + blackout_string = argv[++i]; + } else if (!strcmp(arg, "-xinerama")) { + xinerama = 1; + } else if (!strcmp(arg, "-nobell")) { + watch_bell = 0; + } else if (!strcmp(arg, "-nofb")) { + nofb = 1; + } else if (!strcmp(arg, "-nosel")) { + watch_selection = 0; + } else if (!strcmp(arg, "-noprimary")) { + watch_primary = 0; + } else if (!strcmp(arg, "-nocursor")) { + local_cursor = 0; + } else if (!strcmp(arg, "-mouse")) { + show_mouse = 1; + } else if (!strcmp(arg, "-mouseX")) { + show_mouse = 1; + show_root_cursor = 1; + } else if (!strcmp(arg, "-X")) { + show_mouse = 1; + show_root_cursor = 1; + local_cursor = 0; + } else if (!strcmp(arg, "-xwarppointer")) { + use_xwarppointer = 1; + } else if (!strcmp(arg, "-cursorpos")) { + cursor_pos = 1; + } else if (!strcmp(arg, "-buttonmap")) { + pointer_remap = argv[++i]; + } else if (!strcmp(arg, "-nodragging")) { + show_dragging = 0; + } else if (!strcmp(arg, "-input_skip")) { + ui_skip = atoi(argv[++i]); + if (! ui_skip) ui_skip = 1; + } else if (!strcmp(arg, "-old_pointer")) { + old_pointer = 1; + } else if (!strcmp(arg, "-onetile") + || !strcmp(arg, "-old_copytile")) { + single_copytile = 1; + } else if (!strcmp(arg, "-debug_pointer")) { + debug_pointer++; + } else if (!strcmp(arg, "-debug_keyboard")) { + debug_keyboard++; + } else if (!strcmp(arg, "-defer")) { + defer_update = atoi(argv[++i]); + got_defer = 1; + } else if (!strcmp(arg, "-wait")) { + waitms = atoi(argv[++i]); + } else if (!strcmp(arg, "-nap")) { + take_naps = 1; +#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD + } else if (!strcmp(arg, "-threads")) { + use_threads = 1; + } else if (!strcmp(arg, "-nothreads")) { + use_threads = 0; +#endif + } else if (!strcmp(arg, "-sigpipe")) { + if (!strcmp(argv[++i], "ignore")) { + sigpipe = 1; + } else if (!strcmp(argv[i], "exit")) { + sigpipe = 2; + } else if (!strcmp(argv[i], "skip")) { + sigpipe = 0; + } else { + fprintf(stderr, "bad -sigpipe arg: %s, must " + "be \"ignore\" or \"exit\"\n", argv[i]); + exit(1); + } + } else if (!strcmp(arg, "-fs")) { + fs_frac = atof(argv[++i]); + } else if (!strcmp(arg, "-gaps")) { + gaps_fill = atoi(argv[++i]); + } else if (!strcmp(arg, "-grow")) { + grow_fill = atoi(argv[++i]); + } else if (!strcmp(arg, "-fuzz")) { + tile_fuzz = atoi(argv[++i]); + } else if (!strcmp(arg, "-hints")) { + use_hints = 1; + } else if (!strcmp(arg, "-nohints")) { + use_hints = 0; + } else if (!strcmp(arg, "-h") || !strcmp(arg, "-help") + || !strcmp(arg, "-?")) { + print_help(); + } else if (!strcmp(arg, "-o") || !strcmp(arg, "-logfile")) { + logfile = argv[++i]; + } else if (!strcmp(arg, "-q") || !strcmp(arg, "-quiet")) { + quiet = 1; +#ifdef LIBVNCSERVER_HAVE_SETSID + } else if (!strcmp(arg, "-bg") || !strcmp(arg, "-background")) { + bg = 1; +#endif + } else { + if (!strcmp(arg, "-desktop")) { + dt = 1; + } + if (!strcmp(arg, "-passwd")) { + pw_loc = i; + } + if (!strcmp(arg, "-rfbwait")) { + got_rfbwait = 1; + } + if (!strcmp(arg, "-deferupdate")) { + got_deferupdate = 1; + } + if (!strcmp(arg, "-rfbport")) { + got_rfbport = 1; + } + if (!strcmp(arg, "-alwaysshared ")) { + got_alwaysshared = 1; + } + if (!strcmp(arg, "-nevershared")) { + got_nevershared = 1; + } + /* otherwise copy it for libvncserver use below. */ + if (argc2 < 100) { + argv2[argc2++] = strdup(arg); + } + } + } + if (logfile) { + int n; + if ((n = open(logfile, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) { + fprintf(stderr, "error opening logfile: %s\n", logfile); + perror("open"); + exit(1); + } + if (dup2(n, 2) < 0) { + fprintf(stderr, "dup2 failed\n"); + perror("dup2"); + exit(1); + } + if (n > 2) { + close(n); + } + } + if (! quiet && ! inetd) { + int i; + for (i=1; i < argc2; i++) { + fprintf(stderr, "passing arg to libvncserver: %s\n", + argv2[i]); + if (!strcmp(argv2[i], "-passwd")) { + i++; + } + } + } + + + /* + * If -passwd was used, clear it out of argv. This does not + * work on all UNIX, have to use execvp() in general... + */ + if (pw_loc > 0) { + char *p = argv[pw_loc]; + while (*p != '\0') { + *p++ = '\0'; + } + if (pw_loc+1 < argc) { + p = argv[pw_loc+1]; + while (*p != '\0') { + *p++ = '\0'; + } + } + } else if (passwdfile) { + char line[512]; + FILE *in; + in = fopen(passwdfile, "r"); + if (in == NULL) { + fprintf(stderr, "cannot open passwdfile: %s\n", + passwdfile); + perror("fopen"); + exit(1); + } + if (fgets(line, 512, in) != NULL) { + line[strlen(line)-1] = '\0'; + argv2[argc2++] = "-passwd"; + argv2[argc2++] = strdup(line); + } else { + fprintf(stderr, "cannot read passwdfile: %s\n", + passwdfile); + perror("fgets"); + exit(1); + } + } + + /* fixup settings that do not make sense */ + + if (use_threads && nofb && cursor_pos) { + if (! quiet) { + fprintf(stderr, "disabling -threads under -nofb " + "-cursorpos\n"); + } + use_threads = 0; + } + if (tile_fuzz < 1) { + tile_fuzz = 1; + } + if (waitms < 0) { + waitms = 0; + } + if (inetd) { + shared = 0; + connect_once = 1; + bg = 0; + } + + /* increase rfbwait if threaded */ + if (use_threads && ! got_rfbwait) { + argv2[argc2++] = "-rfbwait"; + argv2[argc2++] = "604800000"; /* one week... */ + } + + /* check for OS with small shm limits */ + if (using_shm && ! single_copytile) { + if (limit_shm()) { + single_copytile = 1; + } + } + + if (nofb && ! got_deferupdate && ! got_defer) { + /* reduce defer time under -nofb */ + defer_update = defer_update_nofb; + } + if (! got_deferupdate) { + char tmp[40]; + /* XXX not working yet in libvncserver */ + sprintf(tmp, "%d", defer_update); + argv2[argc2++] = "-deferupdate"; + argv2[argc2++] = strdup(tmp); + } + if (debug_pointer || debug_keyboard) { + if (bg || quiet) { + fprintf(stderr, "disabling -bg/-q under -debug_pointer" + "/-debug_keyboard\n"); + bg = 0; + quiet = 0; + } + } + + if (! quiet) { + fprintf(stderr, "\n"); + fprintf(stderr, "display: %s\n", use_dpy ? use_dpy + : "null"); + fprintf(stderr, "subwin: 0x%x\n", subwin); + fprintf(stderr, "visual: %s\n", visual_str ? visual_str + : "null"); + fprintf(stderr, "flashcmap: %d\n", flash_cmap); + fprintf(stderr, "force_idx: %d\n", force_indexed_color); + fprintf(stderr, "viewonly: %d\n", view_only); + fprintf(stderr, "shared: %d\n", shared); + fprintf(stderr, "authfile: %s\n", auth_file ? auth_file + : "null"); + fprintf(stderr, "passfile: %s\n", passwdfile ? passwdfile + : "null"); + fprintf(stderr, "logfile: %s\n", logfile ? logfile + : "null"); + fprintf(stderr, "allow: %s\n", allow_list ? allow_list + : "null"); + fprintf(stderr, "accept: %s\n", accept_cmd ? accept_cmd + : "null"); + fprintf(stderr, "gone: %s\n", gone_cmd ? gone_cmd + : "null"); + fprintf(stderr, "conn_once: %d\n", connect_once); + fprintf(stderr, "connect: %s\n", client_connect + ? client_connect : "null"); + fprintf(stderr, "connectfile %s\n", client_connect_file + ? client_connect_file : "null"); + fprintf(stderr, "vnc_conn: %d\n", vnc_connect); + fprintf(stderr, "inetd: %d\n", inetd); + fprintf(stderr, "using_shm: %d\n", using_shm); + fprintf(stderr, "flipbytes: %d\n", flip_byte_order); + fprintf(stderr, "mod_tweak: %d\n", use_modifier_tweak); + fprintf(stderr, "remap: %s\n", remap_file ? remap_file + : "null"); + fprintf(stderr, "blackout: %s\n", blackout_string + ? blackout_string : "null"); + fprintf(stderr, "xinerama: %d\n", xinerama); + fprintf(stderr, "watchbell: %d\n", watch_bell); + fprintf(stderr, "nofb: %d\n", nofb); + fprintf(stderr, "watchsel: %d\n", watch_selection); + fprintf(stderr, "watchprim: %d\n", watch_primary); + fprintf(stderr, "loc_curs: %d\n", local_cursor); + fprintf(stderr, "mouse: %d\n", show_mouse); + fprintf(stderr, "root_curs: %d\n", show_root_cursor); + fprintf(stderr, "xwarpptr: %d\n", use_xwarppointer); + fprintf(stderr, "cursorpos: %d\n", cursor_pos); + fprintf(stderr, "buttonmap: %s\n", pointer_remap + ? pointer_remap : "null"); + fprintf(stderr, "dragging: %d\n", show_dragging); + fprintf(stderr, "inputskip: %d\n", ui_skip); + fprintf(stderr, "old_ptr: %d\n", old_pointer); + fprintf(stderr, "onetile: %d\n", single_copytile); + fprintf(stderr, "debug_ptr: %d\n", debug_pointer); + fprintf(stderr, "debug_key: %d\n", debug_keyboard); + fprintf(stderr, "defer: %d\n", defer_update); + fprintf(stderr, "waitms: %d\n", waitms); + fprintf(stderr, "take_naps: %d\n", take_naps); + fprintf(stderr, "sigpipe: %d\n", sigpipe); + fprintf(stderr, "threads: %d\n", use_threads); + fprintf(stderr, "fs_frac: %.2f\n", fs_frac); + fprintf(stderr, "gaps_fill: %d\n", gaps_fill); + fprintf(stderr, "grow_fill: %d\n", grow_fill); + fprintf(stderr, "tile_fuzz: %d\n", tile_fuzz); + fprintf(stderr, "use_hints: %d\n", use_hints); + fprintf(stderr, "bg: %d\n", bg); + fprintf(stderr, "%s\n", lastmod); + } else { + rfbLogEnable(0); + } + + /* open the X display: */ + X_INIT; + if (auth_file) { + char *tmp; + int len = strlen("XAUTHORITY=") + strlen(auth_file) + 1; + tmp = (char *) malloc((size_t) len); + sprintf(tmp, "XAUTHORITY=%s", auth_file); + putenv(tmp); + } + if (use_dpy) { + dpy = XOpenDisplay(use_dpy); + } else if ( (use_dpy = getenv("DISPLAY")) ) { + dpy = XOpenDisplay(use_dpy); + } else { + dpy = XOpenDisplay(""); + } + + if (! dpy) { + fprintf(stderr, "XOpenDisplay failed (%s)\n", + use_dpy ? use_dpy:"null"); + exit(1); + } else if (use_dpy) { + if (! quiet) fprintf(stderr, "Using X display %s\n", use_dpy); + } else { + if (! quiet) fprintf(stderr, "Using default X display.\n"); + } + + /* check for XTEST */ + if (! XTestQueryExtension(dpy, &ev, &er, &maj, &min)) { + fprintf(stderr, "Display does not support XTest extension.\n"); + exit(1); + } + + /* check for MIT-SHM */ + if (! nofb && ! XShmQueryExtension(dpy)) { + if (! using_shm) { + if (! quiet) { + fprintf(stderr, "warning: display does not " + "support XShm.\n"); + } + } else { + fprintf(stderr, "Display does not support XShm " + "extension (must be local).\n"); + exit(1); + } + } + + if (visual_str != NULL) { + set_visual(visual_str); + } +#ifdef LIBVNCSERVER_HAVE_XKEYBOARD + /* check for XKEYBOARD */ + if (watch_bell) { + if (! XkbQueryExtension(dpy, &op, &ev, &er, &maj, &min)) { + if (! quiet) { + fprintf(stderr, "warning: disabling bell.\n"); + } + watch_bell = 0; + } else { + initialize_watch_bell(); + } + } +#endif + + /* + * Window managers will often grab the display during resize, etc. + * To avoid deadlock (our user resize input is not processed) + * we tell the server to process our requests during all grabs: + */ + XTestGrabControl(dpy, True); + + scr = DefaultScreen(dpy); + rootwin = RootWindow(dpy, scr); + + /* set up parameters for subwin or non-subwin cases: */ + if (! subwin) { + window = rootwin; + dpy_x = DisplayWidth(dpy, scr); + dpy_y = DisplayHeight(dpy, scr); + off_x = 0; + off_y = 0; + visual = DefaultVisual(dpy, scr); + } else { + /* experiment to share just one window */ + XWindowAttributes attr; + + window = (Window) subwin; + if ( ! XGetWindowAttributes(dpy, window, &attr) ) { + fprintf(stderr, "bad window: 0x%lx\n", window); + exit(1); + } + dpy_x = attr.width; + dpy_y = attr.height; + visual = attr.visual; + + /* show_mouse has some segv crashes as well */ + if (show_root_cursor) { + show_root_cursor = 0; + if (! quiet) { + fprintf(stderr, "disabling root cursor drawing" + " for subwindow\n"); + } + } + + set_offset(); + } + + /* initialize depth to reasonable value */ + depth = DefaultDepth(dpy, scr); + + /* + * User asked for non-default visual, this is not working well but it + * does some useful things... What should it do in general? + */ + if (visual_id) { + XVisualInfo vinfo_tmpl, *vinfo; + int n; + + vinfo_tmpl.visualid = visual_id; + vinfo = XGetVisualInfo(dpy, VisualIDMask, &vinfo_tmpl, &n); + if (vinfo == NULL || n == 0) { + fprintf(stderr, "could not match visual_id: 0x%x\n", + (int) visual_id); + exit(1); + } + visual = vinfo->visual; + depth = vinfo->depth; + if (visual_depth) { + depth = visual_depth; /* force it */ + } + if (! quiet) { + fprintf(stderr, "vis id: 0x%x\n", + (int) vinfo->visualid); + fprintf(stderr, "vis scr: %d\n", vinfo->screen); + fprintf(stderr, "vis depth %d\n", vinfo->depth); + fprintf(stderr, "vis class %d\n", vinfo->class); + fprintf(stderr, "vis rmask 0x%lx\n", vinfo->red_mask); + fprintf(stderr, "vis gmask 0x%lx\n", vinfo->green_mask); + fprintf(stderr, "vis bmask 0x%lx\n", vinfo->blue_mask); + fprintf(stderr, "vis cmap_sz %d\n", vinfo->colormap_size); + fprintf(stderr, "vis b/rgb %d\n", vinfo->bits_per_rgb); + } + + XFree(vinfo); + } + + + if (nofb || visual_id) { + fb = XCreateImage(dpy, visual, depth, ZPixmap, 0, NULL, + dpy_x, dpy_y, BitmapPad(dpy), 0); + /* + * For -nofb we do not allocate the framebuffer, so we + * can save a few MB of memory. + */ + if (! nofb) { + fb->data = (char *) malloc(fb->bytes_per_line * + fb->height); + } + } else { + fb = XGetImage(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, + ZPixmap); + if (! quiet) { + fprintf(stderr, "Read initial data from X display into" + " framebuffer.\n"); + } + } + if (fb->bits_per_pixel == 24 && ! quiet) { + fprintf(stderr, "warning: 24 bpp may have poor" + " performance.\n"); + } + + if (! dt) { + static char str[] = "-desktop"; + argv2[argc2++] = str; + argv2[argc2++] = choose_title(use_dpy); + } + + /* + * n.b. we do not have to X_LOCK any X11 calls until watch_loop() + * is called since we are single-threaded until then. + */ + + initialize_screen(&argc2, argv2, fb); + + initialize_tiles(); + + /* rectangular blackout regions */ + if (blackout_string != NULL) { + initialize_blackout(blackout_string); + } + if (xinerama) { + initialize_xinerama(); + } + if (blackouts) { + blackout_tiles(); + } + + initialize_shm(); /* also creates XImages when using_shm = 0 */ + + set_signals(); + + if (blackouts) { /* blackout fb as needed. */ + copy_screen(); + } + + if (use_modifier_tweak) { + initialize_modtweak(); + } + if (remap_file != NULL) { + initialize_remap(remap_file); + } + initialize_pointer_map(); + + if (! inetd) { + if (! screen->rfbPort || screen->rfbListenSock < 0) { + rfbLog("Error: could not obtain listening port.\n"); + clean_up_exit(1); + } + } + if (! quiet) { + rfbLog("screen setup finished.\n"); + } + if (screen->rfbPort) { + char *host = this_host(); + int port = screen->rfbPort; + if (host != NULL) { + /* note that vncviewer special cases 5900-5999 */ + if (inetd) { + ; /* should not occur */ + } else if (quiet) { + if (port >= 5900) { + fprintf(stderr, "The VNC desktop is " + "%s:%d\n", host, port - 5900); + } else { + fprintf(stderr, "The VNC desktop is " + "%s:%d\n", host, port); + } + } else if (port >= 5900) { + rfbLog("The VNC desktop is %s:%d\n", host, + port - 5900); + if (port >= 6000) { + rfbLog("possible aliases: %s:%d, " + "%s::%d\n", host, port, host, port); + } + } else { + rfbLog("The VNC desktop is %s:%d\n", host, + port); + rfbLog("possible alias: %s::%d\n", + host, port); + } + } + fflush(stderr); + fprintf(stdout, "PORT=%d\n", screen->rfbPort); + fflush(stdout); + } + +#if defined(LIBVNCSERVER_HAVE_FORK) && defined(LIBVNCSERVER_HAVE_SETSID) + if (bg) { + /* fork into the background now */ + int p, n; + if ((p = fork()) > 0) { + exit(0); + } else if (p == -1) { + fprintf(stderr, "could not fork\n"); + perror("fork"); + clean_up_exit(1); + } + if (setsid() == -1) { + fprintf(stderr, "setsid failed\n"); + perror("setsid"); + clean_up_exit(1); + } + /* adjust our stdio */ + n = open("/dev/null", O_RDONLY); + dup2(n, 0); + dup2(n, 1); + if (! logfile) { + dup2(n, 2); + } + if (n > 2) { + close(n); + } + } +#endif + + watch_loop(); + + return(0); +} diff --git a/zlib.c b/zlib.c deleted file mode 100644 index 9905810..0000000 --- a/zlib.c +++ /dev/null @@ -1,302 +0,0 @@ -/* - * zlib.c - * - * Routines to implement zlib based encoding (deflate). - */ - -/* - * Copyright (C) 2000 Tridia Corporation. All Rights Reserved. - * Copyright (C) 1999 AT&T Laboratories Cambridge. 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. - * - * For the latest source code, please check: - * - * http://www.developVNC.org/ - * - * or send email to feedback@developvnc.org. - */ - -#include - -/* - * zlibBeforeBuf contains pixel data in the client's format. - * zlibAfterBuf contains the zlib (deflated) encoding version. - * If the zlib compressed/encoded version is - * larger than the raw data or if it exceeds zlibAfterBufSize then - * raw encoding is used instead. - */ - -static int zlibBeforeBufSize = 0; -static char *zlibBeforeBuf = NULL; - -static int zlibAfterBufSize = 0; -static char *zlibAfterBuf = NULL; -static int zlibAfterBufLen; - -/* - * rfbSendOneRectEncodingZlib - send a given rectangle using one Zlib - * rectangle encoding. - */ - -rfbBool -rfbSendOneRectEncodingZlib(cl, x, y, w, h) - rfbClientPtr cl; - int x, y, w, h; -{ - rfbFramebufferUpdateRectHeader rect; - rfbZlibHeader hdr; - int deflateResult; - int previousOut; - int i; - char *fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y) - + (x * (cl->screen->bitsPerPixel / 8))); - - int maxRawSize; - int maxCompSize; - - maxRawSize = (cl->screen->width * cl->screen->height - * (cl->format.bitsPerPixel / 8)); - - if (zlibBeforeBufSize < maxRawSize) { - zlibBeforeBufSize = maxRawSize; - if (zlibBeforeBuf == NULL) - zlibBeforeBuf = (char *)malloc(zlibBeforeBufSize); - else - zlibBeforeBuf = (char *)realloc(zlibBeforeBuf, zlibBeforeBufSize); - } - - /* zlib compression is not useful for very small data sets. - * So, we just send these raw without any compression. - */ - if (( w * h * (cl->screen->bitsPerPixel / 8)) < - VNC_ENCODE_ZLIB_MIN_COMP_SIZE ) { - - int result; - - /* The translation function (used also by the in raw encoding) - * requires 4/2/1 byte alignment in the output buffer (which is - * updateBuf for the raw encoding) based on the bitsPerPixel of - * the viewer/client. This prevents SIGBUS errors on some - * architectures like SPARC, PARISC... - */ - if (( cl->format.bitsPerPixel > 8 ) && - ( cl->ublen % ( cl->format.bitsPerPixel / 8 )) != 0 ) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - result = rfbSendRectEncodingRaw(cl, x, y, w, h); - - return result; - - } - - /* - * zlib requires output buffer to be slightly larger than the input - * buffer, in the worst case. - */ - maxCompSize = maxRawSize + (( maxRawSize + 99 ) / 100 ) + 12; - - if (zlibAfterBufSize < maxCompSize) { - zlibAfterBufSize = maxCompSize; - if (zlibAfterBuf == NULL) - zlibAfterBuf = (char *)malloc(zlibAfterBufSize); - else - zlibAfterBuf = (char *)realloc(zlibAfterBuf, zlibAfterBufSize); - } - - /* - * Convert pixel data to client format. - */ - (*cl->translateFn)(cl->translateLookupTable, &cl->screen->rfbServerFormat, - &cl->format, fbptr, zlibBeforeBuf, - cl->screen->paddedWidthInBytes, w, h); - - cl->compStream.next_in = ( Bytef * )zlibBeforeBuf; - cl->compStream.avail_in = w * h * (cl->format.bitsPerPixel / 8); - cl->compStream.next_out = ( Bytef * )zlibAfterBuf; - cl->compStream.avail_out = maxCompSize; - cl->compStream.data_type = Z_BINARY; - - /* Initialize the deflation state. */ - if ( cl->compStreamInited == FALSE ) { - - cl->compStream.total_in = 0; - cl->compStream.total_out = 0; - cl->compStream.zalloc = Z_NULL; - cl->compStream.zfree = Z_NULL; - cl->compStream.opaque = Z_NULL; - - deflateInit2( &(cl->compStream), - cl->zlibCompressLevel, - Z_DEFLATED, - MAX_WBITS, - MAX_MEM_LEVEL, - Z_DEFAULT_STRATEGY ); - /* deflateInit( &(cl->compStream), Z_BEST_COMPRESSION ); */ - /* deflateInit( &(cl->compStream), Z_BEST_SPEED ); */ - cl->compStreamInited = TRUE; - - } - - previousOut = cl->compStream.total_out; - - /* Perform the compression here. */ - deflateResult = deflate( &(cl->compStream), Z_SYNC_FLUSH ); - - /* Find the total size of the resulting compressed data. */ - zlibAfterBufLen = cl->compStream.total_out - previousOut; - - if ( deflateResult != Z_OK ) { - rfbErr("zlib deflation error: %s\n", cl->compStream.msg); - return FALSE; - } - - /* Note that it is not possible to switch zlib parameters based on - * the results of the compression pass. The reason is - * that we rely on the compressor and decompressor states being - * in sync. Compressing and then discarding the results would - * cause lose of synchronization. - */ - - /* Update statics */ - cl->rfbRectanglesSent[rfbEncodingZlib]++; - cl->rfbBytesSent[rfbEncodingZlib] += (sz_rfbFramebufferUpdateRectHeader - + sz_rfbZlibHeader + zlibAfterBufLen); - - if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader - > UPDATE_BUF_SIZE) - { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - rect.r.x = Swap16IfLE(x); - rect.r.y = Swap16IfLE(y); - rect.r.w = Swap16IfLE(w); - rect.r.h = Swap16IfLE(h); - rect.encoding = Swap32IfLE(rfbEncodingZlib); - - memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, - sz_rfbFramebufferUpdateRectHeader); - cl->ublen += sz_rfbFramebufferUpdateRectHeader; - - hdr.nBytes = Swap32IfLE(zlibAfterBufLen); - - memcpy(&cl->updateBuf[cl->ublen], (char *)&hdr, sz_rfbZlibHeader); - cl->ublen += sz_rfbZlibHeader; - - for (i = 0; i < zlibAfterBufLen;) { - - int bytesToCopy = UPDATE_BUF_SIZE - cl->ublen; - - if (i + bytesToCopy > zlibAfterBufLen) { - bytesToCopy = zlibAfterBufLen - i; - } - - memcpy(&cl->updateBuf[cl->ublen], &zlibAfterBuf[i], bytesToCopy); - - cl->ublen += bytesToCopy; - i += bytesToCopy; - - if (cl->ublen == UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - } - - return TRUE; - -} - - -/* - * rfbSendRectEncodingZlib - send a given rectangle using one or more - * Zlib encoding rectangles. - */ - -rfbBool -rfbSendRectEncodingZlib(cl, x, y, w, h) - rfbClientPtr cl; - int x, y, w, h; -{ - int maxLines; - int linesRemaining; - rfbRectangle partialRect; - - partialRect.x = x; - partialRect.y = y; - partialRect.w = w; - partialRect.h = h; - - /* Determine maximum pixel/scan lines allowed per rectangle. */ - maxLines = ( ZLIB_MAX_SIZE(w) / w ); - - /* Initialize number of scan lines left to do. */ - linesRemaining = h; - - /* Loop until all work is done. */ - while ( linesRemaining > 0 ) { - - int linesToComp; - - if ( maxLines < linesRemaining ) - linesToComp = maxLines; - else - linesToComp = linesRemaining; - - partialRect.h = linesToComp; - - /* Encode (compress) and send the next rectangle. */ - if ( ! rfbSendOneRectEncodingZlib( cl, - partialRect.x, - partialRect.y, - partialRect.w, - partialRect.h )) { - - return FALSE; - } - - /* Technically, flushing the buffer here is not extrememly - * efficient. However, this improves the overall throughput - * of the system over very slow networks. By flushing - * the buffer with every maximum size zlib rectangle, we - * improve the pipelining usage of the server CPU, network, - * and viewer CPU components. Insuring that these components - * are working in parallel actually improves the performance - * seen by the user. - * Since, zlib is most useful for slow networks, this flush - * is appropriate for the desired behavior of the zlib encoding. - */ - if (( cl->ublen > 0 ) && - ( linesToComp == maxLines )) { - if (!rfbSendUpdateBuf(cl)) { - - return FALSE; - } - } - - /* Update remaining and incremental rectangle location. */ - linesRemaining -= linesToComp; - partialRect.y += linesToComp; - - } - - return TRUE; - -} - diff --git a/zrle.c b/zrle.c deleted file mode 100644 index 6ab933e..0000000 --- a/zrle.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. - * Copyright (C) 2003 Sun Microsystems, Inc. - * - * 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. - */ - -/* - * zrle.c - * - * Routines to implement Zlib Run-length Encoding (ZRLE). - */ - -#include "rfb/rfb.h" -#include "zrleoutstream.h" - - -#define GET_IMAGE_INTO_BUF(tx,ty,tw,th,buf) \ -{ char *fbptr = (cl->screen->frameBuffer \ - + (cl->screen->paddedWidthInBytes * ty) \ - + (tx * (cl->screen->bitsPerPixel / 8))); \ - \ - (*cl->translateFn)(cl->translateLookupTable, &cl->screen->rfbServerFormat,\ - &cl->format, fbptr, (char*)buf, \ - cl->screen->paddedWidthInBytes, tw, th); } - -#define EXTRA_ARGS , rfbClientPtr cl - -#define BPP 8 -#include -#undef BPP -#define BPP 16 -#include -#undef BPP -#define BPP 32 -#include -#define CPIXEL 24A -#include -#undef CPIXEL -#define CPIXEL 24B -#include -#undef CPIXEL -#undef BPP - - -/* - * zrleBeforeBuf contains pixel data in the client's format. It must be at - * least one pixel bigger than the largest tile of pixel data, since the - * ZRLE encoding algorithm writes to the position one past the end of the pixel - * data. - */ - -static char zrleBeforeBuf[rfbZRLETileWidth * rfbZRLETileHeight * 4 + 4]; - - - -/* - * rfbSendRectEncodingZRLE - send a given rectangle using ZRLE encoding. - */ - - -rfbBool rfbSendRectEncodingZRLE(rfbClientPtr cl, int x, int y, int w, int h) -{ - zrleOutStream* zos; - rfbFramebufferUpdateRectHeader rect; - rfbZRLEHeader hdr; - int i; - - if (!cl->zrleData) - cl->zrleData = zrleOutStreamNew(); - zos = cl->zrleData; - zos->in.ptr = zos->in.start; - zos->out.ptr = zos->out.start; - - switch (cl->format.bitsPerPixel) { - - case 8: - zrleEncode8( x, y, w, h, zos, zrleBeforeBuf, cl); - break; - - case 16: - zrleEncode16(x, y, w, h, zos, zrleBeforeBuf, cl); - break; - - case 32: { - rfbBool fitsInLS3Bytes - = ((cl->format.redMax << cl->format.redShift) < (1<<24) && - (cl->format.greenMax << cl->format.greenShift) < (1<<24) && - (cl->format.blueMax << cl->format.blueShift) < (1<<24)); - - rfbBool fitsInMS3Bytes = (cl->format.redShift > 7 && - cl->format.greenShift > 7 && - cl->format.blueShift > 7); - - if ((fitsInLS3Bytes && !cl->format.bigEndian) || - (fitsInMS3Bytes && cl->format.bigEndian)) - { - zrleEncode24A(x, y, w, h, zos, zrleBeforeBuf, cl); - } - else if ((fitsInLS3Bytes && cl->format.bigEndian) || - (fitsInMS3Bytes && !cl->format.bigEndian)) - { - zrleEncode24B(x, y, w, h, zos, zrleBeforeBuf, cl); - } - else - { - zrleEncode32(x, y, w, h, zos, zrleBeforeBuf, cl); - } - } - break; - } - - cl->rfbRectanglesSent[rfbEncodingZRLE]++; - cl->rfbBytesSent[rfbEncodingZRLE] += (sz_rfbFramebufferUpdateRectHeader - + sz_rfbZRLEHeader + ZRLE_BUFFER_LENGTH(&zos->out)); - - if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbZRLEHeader - > UPDATE_BUF_SIZE) - { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - rect.r.x = Swap16IfLE(x); - rect.r.y = Swap16IfLE(y); - rect.r.w = Swap16IfLE(w); - rect.r.h = Swap16IfLE(h); - rect.encoding = Swap32IfLE(rfbEncodingZRLE); - - memcpy(cl->updateBuf+cl->ublen, (char *)&rect, - sz_rfbFramebufferUpdateRectHeader); - cl->ublen += sz_rfbFramebufferUpdateRectHeader; - - hdr.length = Swap32IfLE(ZRLE_BUFFER_LENGTH(&zos->out)); - - memcpy(cl->updateBuf+cl->ublen, (char *)&hdr, sz_rfbZRLEHeader); - cl->ublen += sz_rfbZRLEHeader; - - /* copy into updateBuf and send from there. Maybe should send directly? */ - - for (i = 0; i < ZRLE_BUFFER_LENGTH(&zos->out);) { - - int bytesToCopy = UPDATE_BUF_SIZE - cl->ublen; - - if (i + bytesToCopy > ZRLE_BUFFER_LENGTH(&zos->out)) { - bytesToCopy = ZRLE_BUFFER_LENGTH(&zos->out) - i; - } - - memcpy(cl->updateBuf+cl->ublen, (uint8_t*)zos->out.start + i, bytesToCopy); - - cl->ublen += bytesToCopy; - i += bytesToCopy; - - if (cl->ublen == UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - } - - return TRUE; -} - - -void FreeZrleData(rfbClientPtr cl) -{ - if (cl->zrleData) - zrleOutStreamFree(cl->zrleData); - cl->zrleData = NULL; -} - diff --git a/zrleencodetemplate.c b/zrleencodetemplate.c deleted file mode 100644 index a1772ae..0000000 --- a/zrleencodetemplate.c +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. - * Copyright (C) 2003 Sun Microsystems, Inc. - * - * 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. - */ - -/* - * Before including this file, you must define a number of CPP macros. - * - * BPP should be 8, 16 or 32 depending on the bits per pixel. - * GET_IMAGE_INTO_BUF should be some code which gets a rectangle of pixel data - * into the given buffer. EXTRA_ARGS can be defined to pass any other - * arguments needed by GET_IMAGE_INTO_BUF. - * - * Note that the buf argument to ZRLE_ENCODE needs to be at least one pixel - * bigger than the largest tile of pixel data, since the ZRLE encoding - * algorithm writes to the position one past the end of the pixel data. - */ - -#include "zrleoutstream.h" -#include "zrlepalettehelper.h" -#include - -/* __RFB_CONCAT2 concatenates its two arguments. __RFB_CONCAT2E does the same - but also expands its arguments if they are macros */ - -#ifndef __RFB_CONCAT2E -#define __RFB_CONCAT2(a,b) a##b -#define __RFB_CONCAT2E(a,b) __RFB_CONCAT2(a,b) -#endif - -#ifdef CPIXEL -#define PIXEL_T __RFB_CONCAT2E(zrle_U,BPP) -#define zrleOutStreamWRITE_PIXEL __RFB_CONCAT2E(zrleOutStreamWriteOpaque,CPIXEL) -#define ZRLE_ENCODE __RFB_CONCAT2E(zrleEncode,CPIXEL) -#define ZRLE_ENCODE_TILE __RFB_CONCAT2E(zrleEncodeTile,CPIXEL) -#define BPPOUT 24 -#else -#define PIXEL_T __RFB_CONCAT2E(zrle_U,BPP) -#define zrleOutStreamWRITE_PIXEL __RFB_CONCAT2E(zrleOutStreamWriteOpaque,BPP) -#define ZRLE_ENCODE __RFB_CONCAT2E(zrleEncode,BPP) -#define ZRLE_ENCODE_TILE __RFB_CONCAT2E(zrleEncodeTile,BPP) -#define BPPOUT BPP -#endif - -#ifndef ZRLE_ONCE -#define ZRLE_ONCE - -static const int bitsPerPackedPixel[] = { - 0, 1, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 -}; - -static zrlePaletteHelper paletteHelper; - -#endif /* ZRLE_ONCE */ - -void ZRLE_ENCODE_TILE (PIXEL_T* data, int w, int h, zrleOutStream* os); - -void ZRLE_ENCODE (int x, int y, int w, int h, - zrleOutStream* os, void* buf - EXTRA_ARGS - ) -{ - int ty; - for (ty = y; ty < y+h; ty += rfbZRLETileHeight) { - int tx, th = rfbZRLETileHeight; - if (th > y+h-ty) th = y+h-ty; - for (tx = x; tx < x+w; tx += rfbZRLETileWidth) { - int tw = rfbZRLETileWidth; - if (tw > x+w-tx) tw = x+w-tx; - - GET_IMAGE_INTO_BUF(tx,ty,tw,th,buf); - - ZRLE_ENCODE_TILE((PIXEL_T*)buf, tw, th, os); - } - } - zrleOutStreamFlush(os); -} - - -void ZRLE_ENCODE_TILE (PIXEL_T* data, int w, int h, zrleOutStream* os) -{ - /* First find the palette and the number of runs */ - - zrlePaletteHelper *ph; - - int runs = 0; - int singlePixels = 0; - - rfbBool useRle; - rfbBool usePalette; - - int estimatedBytes; - int plainRleBytes; - int i; - - PIXEL_T* ptr = data; - PIXEL_T* end = ptr + h * w; - *end = ~*(end-1); /* one past the end is different so the while loop ends */ - - ph = &paletteHelper; - zrlePaletteHelperInit(ph); - - while (ptr < end) { - PIXEL_T pix = *ptr; - if (*++ptr != pix) { - singlePixels++; - } else { - while (*++ptr == pix) ; - runs++; - } - zrlePaletteHelperInsert(ph, pix); - } - - /* Solid tile is a special case */ - - if (ph->size == 1) { - zrleOutStreamWriteU8(os, 1); - zrleOutStreamWRITE_PIXEL(os, ph->palette[0]); - return; - } - - // Try to work out whether to use RLE and/or a palette. We do this by - // estimating the number of bytes which will be generated and picking the - // method which results in the fewest bytes. Of course this may not result - // in the fewest bytes after compression... - - useRle = FALSE; - usePalette = FALSE; - - estimatedBytes = w * h * (BPPOUT/8); // start assuming raw - - plainRleBytes = ((BPPOUT/8)+1) * (runs + singlePixels); - - if (plainRleBytes < estimatedBytes) { - useRle = TRUE; - estimatedBytes = plainRleBytes; - } - - if (ph->size < 128) { - int paletteRleBytes = (BPPOUT/8) * ph->size + 2 * runs + singlePixels; - - if (paletteRleBytes < estimatedBytes) { - useRle = TRUE; - usePalette = TRUE; - estimatedBytes = paletteRleBytes; - } - - if (ph->size < 17) { - int packedBytes = ((BPPOUT/8) * ph->size + - w * h * bitsPerPackedPixel[ph->size-1] / 8); - - if (packedBytes < estimatedBytes) { - useRle = FALSE; - usePalette = TRUE; - estimatedBytes = packedBytes; - } - } - } - - if (!usePalette) ph->size = 0; - - zrleOutStreamWriteU8(os, (useRle ? 128 : 0) | ph->size); - - for (i = 0; i < ph->size; i++) { - zrleOutStreamWRITE_PIXEL(os, ph->palette[i]); - } - - if (useRle) { - - PIXEL_T* ptr = data; - PIXEL_T* end = ptr + w * h; - PIXEL_T* runStart; - PIXEL_T pix; - while (ptr < end) { - int len; - runStart = ptr; - pix = *ptr++; - while (*ptr == pix && ptr < end) - ptr++; - len = ptr - runStart; - if (len <= 2 && usePalette) { - int index = zrlePaletteHelperLookup(ph, pix); - if (len == 2) - zrleOutStreamWriteU8(os, index); - zrleOutStreamWriteU8(os, index); - continue; - } - if (usePalette) { - int index = zrlePaletteHelperLookup(ph, pix); - zrleOutStreamWriteU8(os, index | 128); - } else { - zrleOutStreamWRITE_PIXEL(os, pix); - } - len -= 1; - while (len >= 255) { - zrleOutStreamWriteU8(os, 255); - len -= 255; - } - zrleOutStreamWriteU8(os, len); - } - - } else { - - // no RLE - - if (usePalette) { - int bppp; - PIXEL_T* ptr = data; - - // packed pixels - - assert (ph->size < 17); - - bppp = bitsPerPackedPixel[ph->size-1]; - - for (i = 0; i < h; i++) { - zrle_U8 nbits = 0; - zrle_U8 byte = 0; - - PIXEL_T* eol = ptr + w; - - while (ptr < eol) { - PIXEL_T pix = *ptr++; - zrle_U8 index = zrlePaletteHelperLookup(ph, pix); - byte = (byte << bppp) | index; - nbits += bppp; - if (nbits >= 8) { - zrleOutStreamWriteU8(os, byte); - nbits = 0; - } - } - if (nbits > 0) { - byte <<= 8 - nbits; - zrleOutStreamWriteU8(os, byte); - } - } - } else { - - // raw - -#ifdef CPIXEL - PIXEL_T *ptr; - for (ptr = data; ptr < data+w*h; ptr++) { - zrleOutStreamWRITE_PIXEL(os, *ptr); - } -#else - zrleOutStreamWriteBytes(os, (zrle_U8 *)data, w*h*(BPP/8)); -#endif - } - } -} - -#undef PIXEL_T -#undef zrleOutStreamWRITE_PIXEL -#undef ZRLE_ENCODE -#undef ZRLE_ENCODE_TILE -#undef BPPOUT diff --git a/zrleoutstream.c b/zrleoutstream.c deleted file mode 100644 index d22d649..0000000 --- a/zrleoutstream.c +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. - * Copyright (C) 2003 Sun Microsystems, Inc. - * - * 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. - */ - -#include "zrleoutstream.h" -#include - -#define ZRLE_IN_BUFFER_SIZE 16384 -#define ZRLE_OUT_BUFFER_SIZE 1024 -#undef ZRLE_DEBUG - -static rfbBool zrleBufferAlloc(zrleBuffer *buffer, int size) -{ - buffer->ptr = buffer->start = malloc(size); - if (buffer->start == NULL) { - buffer->end = NULL; - return FALSE; - } - - buffer->end = buffer->start + size; - - return TRUE; -} - -static void zrleBufferFree(zrleBuffer *buffer) -{ - if (buffer->start) - free(buffer->start); - buffer->start = buffer->ptr = buffer->end = NULL; -} - -static rfbBool zrleBufferGrow(zrleBuffer *buffer, int size) -{ - int offset; - - size += buffer->end - buffer->start; - offset = ZRLE_BUFFER_LENGTH (buffer); - - buffer->start = realloc(buffer->start, size); - if (!buffer->start) { - return FALSE; - } - - buffer->end = buffer->start + size; - buffer->ptr = buffer->start + offset; - - return TRUE; -} - -zrleOutStream *zrleOutStreamNew(void) -{ - zrleOutStream *os; - - os = malloc(sizeof(zrleOutStream)); - if (os == NULL) - return NULL; - - if (!zrleBufferAlloc(&os->in, ZRLE_IN_BUFFER_SIZE)) { - free(os); - return NULL; - } - - if (!zrleBufferAlloc(&os->out, ZRLE_OUT_BUFFER_SIZE)) { - zrleBufferFree(&os->in); - free(os); - return NULL; - } - - os->zs.zalloc = Z_NULL; - os->zs.zfree = Z_NULL; - os->zs.opaque = Z_NULL; - if (deflateInit(&os->zs, Z_DEFAULT_COMPRESSION) != Z_OK) { - zrleBufferFree(&os->in); - free(os); - return NULL; - } - - return os; -} - -void zrleOutStreamFree (zrleOutStream *os) -{ - deflateEnd(&os->zs); - zrleBufferFree(&os->in); - zrleBufferFree(&os->out); - free(os); -} - -rfbBool zrleOutStreamFlush(zrleOutStream *os) -{ - os->zs.next_in = os->in.start; - os->zs.avail_in = ZRLE_BUFFER_LENGTH (&os->in); - -#ifdef ZRLE_DEBUG - rfbLog("zrleOutStreamFlush: avail_in %d\n", os->zs.avail_in); -#endif - - while (os->zs.avail_in != 0) { - do { - int ret; - - if (os->out.ptr >= os->out.end && - !zrleBufferGrow(&os->out, os->out.end - os->out.start)) { - rfbLog("zrleOutStreamFlush: failed to grow output buffer\n"); - return FALSE; - } - - os->zs.next_out = os->out.ptr; - os->zs.avail_out = os->out.end - os->out.ptr; - -#ifdef ZRLE_DEBUG - rfbLog("zrleOutStreamFlush: calling deflate, avail_in %d, avail_out %d\n", - os->zs.avail_in, os->zs.avail_out); -#endif - - if ((ret = deflate(&os->zs, Z_SYNC_FLUSH)) != Z_OK) { - rfbLog("zrleOutStreamFlush: deflate failed with error code %d\n", ret); - return FALSE; - } - -#ifdef ZRLE_DEBUG - rfbLog("zrleOutStreamFlush: after deflate: %d bytes\n", - os->zs.next_out - os->out.ptr); -#endif - - os->out.ptr = os->zs.next_out; - } while (os->zs.avail_out == 0); - } - - os->in.ptr = os->in.start; - - return TRUE; -} - -static int zrleOutStreamOverrun(zrleOutStream *os, - int size) -{ -#ifdef ZRLE_DEBUG - rfbLog("zrleOutStreamOverrun\n"); -#endif - - while (os->in.end - os->in.ptr < size && os->in.ptr > os->in.start) { - os->zs.next_in = os->in.start; - os->zs.avail_in = ZRLE_BUFFER_LENGTH (&os->in); - - do { - int ret; - - if (os->out.ptr >= os->out.end && - !zrleBufferGrow(&os->out, os->out.end - os->out.start)) { - rfbLog("zrleOutStreamOverrun: failed to grow output buffer\n"); - return FALSE; - } - - os->zs.next_out = os->out.ptr; - os->zs.avail_out = os->out.end - os->out.ptr; - -#ifdef ZRLE_DEBUG - rfbLog("zrleOutStreamOverrun: calling deflate, avail_in %d, avail_out %d\n", - os->zs.avail_in, os->zs.avail_out); -#endif - - if ((ret = deflate(&os->zs, 0)) != Z_OK) { - rfbLog("zrleOutStreamOverrun: deflate failed with error code %d\n", ret); - return 0; - } - -#ifdef ZRLE_DEBUG - rfbLog("zrleOutStreamOverrun: after deflate: %d bytes\n", - os->zs.next_out - os->out.ptr); -#endif - - os->out.ptr = os->zs.next_out; - } while (os->zs.avail_out == 0); - - /* output buffer not full */ - - if (os->zs.avail_in == 0) { - os->in.ptr = os->in.start; - } else { - /* but didn't consume all the data? try shifting what's left to the - * start of the buffer. - */ - rfbLog("zrleOutStreamOverrun: out buf not full, but in data not consumed\n"); - memmove(os->in.start, os->zs.next_in, os->in.ptr - os->zs.next_in); - os->in.ptr -= os->zs.next_in - os->in.start; - } - } - - if (size > os->in.end - os->in.ptr) - size = os->in.end - os->in.ptr; - - return size; -} - -static inline int zrleOutStreamCheck(zrleOutStream *os, - int size) -{ - if (os->in.ptr + size > os->in.end) { - return zrleOutStreamOverrun(os, size); - } - return size; -} - -void zrleOutStreamWriteBytes(zrleOutStream *os, - const zrle_U8 *data, - int length) -{ - const zrle_U8* dataEnd = data + length; - while (data < dataEnd) { - int n = zrleOutStreamCheck(os, dataEnd - data); - memcpy(os->in.ptr, data, n); - os->in.ptr += n; - data += n; - } -} - -void zrleOutStreamWriteU8(zrleOutStream *os, zrle_U8 u) -{ - zrleOutStreamCheck(os, 1); - *os->in.ptr++ = u; -} - -void zrleOutStreamWriteOpaque8(zrleOutStream *os, zrle_U8 u) -{ - zrleOutStreamCheck(os, 1); - *os->in.ptr++ = u; -} - -void zrleOutStreamWriteOpaque16 (zrleOutStream *os, zrle_U16 u) -{ - zrleOutStreamCheck(os, 2); - *os->in.ptr++ = ((zrle_U8*)&u)[0]; - *os->in.ptr++ = ((zrle_U8*)&u)[1]; -} - -void zrleOutStreamWriteOpaque32 (zrleOutStream *os, zrle_U32 u) -{ - zrleOutStreamCheck(os, 4); - *os->in.ptr++ = ((zrle_U8*)&u)[0]; - *os->in.ptr++ = ((zrle_U8*)&u)[1]; - *os->in.ptr++ = ((zrle_U8*)&u)[2]; - *os->in.ptr++ = ((zrle_U8*)&u)[3]; -} - -void zrleOutStreamWriteOpaque24A(zrleOutStream *os, zrle_U32 u) -{ - zrleOutStreamCheck(os, 3); - *os->in.ptr++ = ((zrle_U8*)&u)[0]; - *os->in.ptr++ = ((zrle_U8*)&u)[1]; - *os->in.ptr++ = ((zrle_U8*)&u)[2]; -} - -void zrleOutStreamWriteOpaque24B(zrleOutStream *os, zrle_U32 u) -{ - zrleOutStreamCheck(os, 3); - *os->in.ptr++ = ((zrle_U8*)&u)[1]; - *os->in.ptr++ = ((zrle_U8*)&u)[2]; - *os->in.ptr++ = ((zrle_U8*)&u)[3]; -} diff --git a/zrleoutstream.h b/zrleoutstream.h deleted file mode 100644 index 9e4fe51..0000000 --- a/zrleoutstream.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. - * Copyright (C) 2003 Sun Microsystems, Inc. - * - * 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. - */ - -#ifndef __ZRLE_OUT_STREAM_H__ -#define __ZRLE_OUT_STREAM_H__ - -#include -#include "zrletypes.h" -#include "rfb/rfb.h" - -typedef struct { - zrle_U8 *start; - zrle_U8 *ptr; - zrle_U8 *end; -} zrleBuffer; - -typedef struct { - zrleBuffer in; - zrleBuffer out; - - z_stream zs; -} zrleOutStream; - -#define ZRLE_BUFFER_LENGTH(b) ((b)->ptr - (b)->start) - -zrleOutStream *zrleOutStreamNew (void); -void zrleOutStreamFree (zrleOutStream *os); -rfbBool zrleOutStreamFlush (zrleOutStream *os); -void zrleOutStreamWriteBytes (zrleOutStream *os, - const zrle_U8 *data, - int length); -void zrleOutStreamWriteU8 (zrleOutStream *os, - zrle_U8 u); -void zrleOutStreamWriteOpaque8 (zrleOutStream *os, - zrle_U8 u); -void zrleOutStreamWriteOpaque16 (zrleOutStream *os, - zrle_U16 u); -void zrleOutStreamWriteOpaque32 (zrleOutStream *os, - zrle_U32 u); -void zrleOutStreamWriteOpaque24A(zrleOutStream *os, - zrle_U32 u); -void zrleOutStreamWriteOpaque24B(zrleOutStream *os, - zrle_U32 u); - -#endif /* __ZRLE_OUT_STREAM_H__ */ diff --git a/zrlepalettehelper.c b/zrlepalettehelper.c deleted file mode 100644 index d758a26..0000000 --- a/zrlepalettehelper.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. - * Copyright (C) 2003 Sun Microsystems, Inc. - * - * 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. - */ - -#include "zrlepalettehelper.h" -#include -#include - -#define ZRLE_HASH(pix) (((pix) ^ ((pix) >> 17)) & 4095) - -void zrlePaletteHelperInit(zrlePaletteHelper *helper) -{ - memset(helper->palette, 0, sizeof(helper->palette)); - memset(helper->index, 255, sizeof(helper->index)); - memset(helper->key, 0, sizeof(helper->key)); - helper->size = 0; -} - -void zrlePaletteHelperInsert(zrlePaletteHelper *helper, zrle_U32 pix) -{ - if (helper->size < ZRLE_PALETTE_MAX_SIZE) { - int i = ZRLE_HASH(pix); - - while (helper->index[i] != 255 && helper->key[i] != pix) - i++; - if (helper->index[i] != 255) return; - - helper->index[i] = helper->size; - helper->key[i] = pix; - helper->palette[helper->size] = pix; - } - helper->size++; -} - -int zrlePaletteHelperLookup(zrlePaletteHelper *helper, zrle_U32 pix) -{ - int i = ZRLE_HASH(pix); - - assert(helper->size <= ZRLE_PALETTE_MAX_SIZE); - - while (helper->index[i] != 255 && helper->key[i] != pix) - i++; - if (helper->index[i] != 255) return helper->index[i]; - - return -1; -} diff --git a/zrlepalettehelper.h b/zrlepalettehelper.h deleted file mode 100644 index e1213d1..0000000 --- a/zrlepalettehelper.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2002 RealVNC Ltd. All Rights Reserved. - * Copyright (C) 2003 Sun Microsystems, Inc. - * - * 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. - */ - -/* - * The PaletteHelper class helps us build up the palette from pixel data by - * storing a reverse index using a simple hash-table - */ - -#ifndef __ZRLE_PALETTE_HELPER_H__ -#define __ZRLE_PALETTE_HELPER_H__ - -#include "zrletypes.h" - -#define ZRLE_PALETTE_MAX_SIZE 127 - -typedef struct { - zrle_U32 palette[ZRLE_PALETTE_MAX_SIZE]; - zrle_U8 index[ZRLE_PALETTE_MAX_SIZE + 4096]; - zrle_U32 key[ZRLE_PALETTE_MAX_SIZE + 4096]; - int size; -} zrlePaletteHelper; - -void zrlePaletteHelperInit (zrlePaletteHelper *helper); -void zrlePaletteHelperInsert(zrlePaletteHelper *helper, - zrle_U32 pix); -int zrlePaletteHelperLookup(zrlePaletteHelper *helper, - zrle_U32 pix); - -#endif /* __ZRLE_PALETTE_HELPER_H__ */ diff --git a/zrletypes.h b/zrletypes.h deleted file mode 100644 index 0df42c9..0000000 --- a/zrletypes.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2002 RealVNC Ltd. 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. - */ - -#ifndef __ZRLE_TYPES_H__ -#define __ZRLE_TYPES_H__ - -typedef unsigned char zrle_U8; -typedef unsigned short zrle_U16; -typedef unsigned int zrle_U32; -typedef signed char zrle_S8; -typedef signed short zrle_S16; -typedef signed int zrle_S32; - -#endif /* __ZRLE_TYPES_H__ */ -- cgit v1.2.1