diff options
author | dscho <dscho> | 2004-05-25 09:05:09 +0000 |
---|---|---|
committer | dscho <dscho> | 2004-05-25 09:05:09 +0000 |
commit | 876868553da8f69ed1a368688b6d01a8a7bc1a39 (patch) | |
tree | 0bf872ebeadb746eec0f4695230c8777680ece9f /libvncserver | |
parent | 0130bdb9d6c61e0446ab514816041f1b9283410a (diff) | |
download | libtdevnc-876868553da8f69ed1a368688b6d01a8a7bc1a39.tar.gz libtdevnc-876868553da8f69ed1a368688b6d01a8a7bc1a39.zip |
move the library into libvncserver/, x11vnc into x11vnc/
Diffstat (limited to 'libvncserver')
37 files changed, 12775 insertions, 0 deletions
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 <mcguirk@incompleteness.net>. + * 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 <rfb/rfb.h> + +/* + * 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 <jdeari01@longisland.poly.edu>. + */ + +/* + * LibVNCServer (C) 2001 Johannes E. Schindelin <Johannes.Schindelin@gmx.de> + * Original OSXvnc (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>. + * Original Xvnc (C) 1999 AT&T Laboratories Cambridge. + * All Rights Reserved. + * + * see GPL (latest version) for full details + */ + +#include <rfb/rfb.h> + +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 <arpa/inet.h> 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 <fcntl.h> 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 <inttypes.h> 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 <memory.h> 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 <netdb.h> header file. */ +#define HAVE_NETDB_H 1 + +/* Define to 1 if you have the <netinet/in.h> 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 <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> 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 <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> 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 <syslog.h> header file. */ +#define HAVE_SYSLOG_H 1 + +/* Define to 1 if you have the <sys/socket.h> header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/timeb.h> header file. */ +#define HAVE_SYS_TIMEB_H 1 + +/* Define to 1 if you have the <sys/time.h> header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the <unistd.h> 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 <vfork.h> 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 <sys/time.h> and <time.h>. */ +#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 <sys/types.h> does not define. */ +/* #undef pid_t */ + +/* Define to `unsigned' if <sys/types.h> 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 <mcguirk@incompleteness.net>. + * 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 <rfb/rfb.h> + +/* + * 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 [<bgcolour><subrect><subrect>...] where each + * <subrect> is [<colour><x><y><w><h>]. + */ + +#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<h; y++) { \ + line = data+(y*w); \ + for (x=0; x<w; x++) { \ + if (line[x] != bg) { \ + cl = line[x]; \ + hy = y-1; \ + hyflag = 1; \ + for (j=y; j<h; j++) { \ + seg = data+(j*w); \ + if (seg[x] != cl) {break;} \ + i = x; \ + while ((seg[i] == cl) && (i < w)) i += 1; \ + i -= 1; \ + if (j == y) vx = hx = i; \ + if (i < vx) vx = i; \ + if ((hyflag > 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; i++) { + counts[i] = 0; + } + + for (j=0; j<size; j++) { + k = (int)(((uint8_t *)data)[j]); + if (k >= 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 <rfb/rfb.h> + +/* + * 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;i<t;i++) + bitmap[i]=rfbReverseByte[(int)bitmap[i]]; +} + +/* Cursor creation. You "paint" a cursor and let these routines do the work */ + +rfbCursorPtr rfbMakeXCursor(int width,int height,char* cursorString,char* maskString) +{ + int i,j,w=(width+7)/8; + rfbCursorPtr cursor = (rfbCursorPtr)calloc(1,sizeof(rfbCursor)); + char* cp; + unsigned char bit; + + cursor->cleanup=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<height;j++) + for(i=0,bit=0x80;i<width;i++,bit=(bit&1)?0x80:bit>>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<height;j++) + for(i=0,bit=0x80;i<width;i++,bit=(bit&1)?0x80:bit>>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<height;j++) + for(i=w-1;i>=0;i--) { + c=source[j*w+i]; + if(j>0) c|=source[(j-1)*w+i]; + if(j<height-1) c|=source[(j+1)*w+i]; + + if(i>0 && (c&0x80)) + mask[j*w+i-1]|=0x01; + if(i<w-1 && (c&0x01)) + mask[j*w+i+1]|=0x80; + + mask[j*w+i]|=(c<<1)|c|(c>>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<<format->redShift| + cursor->backGreen<<format->greenShift|cursor->backBlue<<format->blueShift; + + for(j=0;j<cursor->height;j++) + for(i=0,bit=0x80;i<cursor->width;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<<format->redShift| + cursor->backGreen<<format->greenShift|cursor->backBlue<<format->blueShift; + foreground=cursor->foreRed<<format->redShift| + cursor->foreGreen<<format->greenShift|cursor->foreBlue<<format->blueShift; + + for(j=0;j<cursor->height;j++) + for(i=0,bit=0x80;i<cursor->height;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;j<y2;j++) + memcpy(s->frameBuffer+(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->underCursorBufferLen<bufSize) { + if(s->underCursorBuffer!=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;j<y2;j++) { + char* dest=s->underCursorBuffer+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;j<y2;j++) + for(i=0;i<x2;i++) + if((c->mask[(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;j<cursor->height;j++) { + for(i=0,i1=0,bit=0x80;i1<cursor->width;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;i1<cursor->width;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 <mcguirk@incompleteness.net>. + * 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 <rfb/rfb.h> + + +/* + * 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 <rfb/rfb.h> + +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;j<y2;j++) + for(i=x1;i<x2;i++) + memcpy(s->frameBuffer+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)<abs(y1-y2)) { + if(y1>y2) + SWAPPOINTS + for(i=y1;i<=y2;i++) + SETPIXEL(x1+(i-y1)*(x2-x1)/(y2-y1),i); + /* TODO: Maybe make this more intelligently? */ + if(x2<x1) { i=x1; x1=x2; x2=i; } + rfbMarkRectAsModified(s,x1,y1,x2+1,y2+1); + } else { + if(x1>x2) + 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<y1) { i=y1; y1=y2; y2=i; } + rfbMarkRectAsModified(s,x1,y1,x2+1,y2+1); + } +} diff --git a/libvncserver/font.c b/libvncserver/font.c new file mode 100755 index 0000000..423d415 --- /dev/null +++ b/libvncserver/font.c @@ -0,0 +1,192 @@ +#include <rfb/rfb.h> + +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;j<height;j++) { + for(i=0;i<width;i++) { + if((i&7)==0) { + d=*data; + data++; + } + if(d&0x80) + memcpy(rfbScreen->frameBuffer+(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<y+height) height-=y+height-y2; + if(x2<x+width) { extra_bytes+=(x1+width)/8-(x+width-x2+7)/8; width-=x+width-x2; } + + d=*data; + for(j=y1;j<height;j++) { + if((x1&7)!=0) + d=data[-1]; /* TODO: check if in this case extra_bytes is correct! */ + for(i=x1;i<width;i++) { + if((i&7)==0) { + d=*data; + data++; + } + /* if(x+i>=x1 && x+i<x2 && y+j>=y1 && y+j<y2) */ { + if(d&0x80) { + memcpy(rfbScreen->frameBuffer+(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 <mcguirk@incompleteness.net>. + * 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 <rfb/rfb.h> + +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<h; y++) { \ + line = data+(y*w); \ + for (x=0; x<w; x++) { \ + if (line[x] != bg) { \ + cl2 = line[x]; \ + hy = y-1; \ + hyflag = 1; \ + for (j=y; j<h; j++) { \ + seg = data+(j*w); \ + if (seg[x] != cl2) {break;} \ + i = x; \ + while ((seg[i] == cl2) && (i < w)) i += 1; \ + i -= 1; \ + if (j == y) vx = hx = i; \ + if (i < vx) vx = i; \ + if ((hyflag > 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 <rfb/rfb.h> + +#include <ctype.h> +#ifdef LIBVNCSERVER_HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef LIBVNCSERVER_HAVE_FCNTL_H +#include <fcntl.h> +#endif +#include <errno.h> + +#ifdef WIN32 +#include <winsock.h> +#define close closesocket +#else +#ifdef LIBVNCSERVER_HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#ifdef LIBVNCSERVER_HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef LIBVNCSERVER_HAVE_NETINET_IN_H +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <arpa/inet.h> +#endif +#include <pwd.h> +#endif + +#ifdef USE_LIBWRAP +#include <tcpd.h> +#endif + +#define NOT_FOUND_STR "HTTP/1.0 404 Not found\r\n\r\n" \ + "<HEAD><TITLE>File Not Found</TITLE></HEAD>\n" \ + "<BODY><H1>File Not Found</H1></BODY>\n" + +#define INVALID_REQUEST_STR "HTTP/1.0 400 Invalid Request\r\n\r\n" \ + "<HEAD><TITLE>Invalid Request</TITLE></HEAD>\n" \ + "<BODY><H1>Invalid request</H1></BODY>\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 <param> 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, + "<PARAM NAME=\"%s\" VALUE=\"%s\">\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 <Johannes.Schindelin@gmx.de> + * Original OSXvnc (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>. + * Original Xvnc (C) 1999 AT&T Laboratories Cambridge. + * All Rights Reserved. + * + * see GPL (latest version) for full details + */ + +#include <rfb/rfb.h> +#include <rfb/rfbregion.h> + +#include <stdarg.h> +#include <errno.h> + +#ifndef false +#define false 0 +#define true -1 +#endif + +#ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#ifndef WIN32 +#include <sys/socket.h> +#include <netinet/in.h> +#include <unistd.h> +#endif + +#include <signal.h> +#include <time.h> + +#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.y2;j++,out+=rowstride,in+=rowstride) + memmove(out,in,widthInBytes); + else { + out += rowstride*(rect.y2-rect.y1-1); + in += rowstride*(rect.y2-rect.y1-1); + for(j=rect.y2-1;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 <fcntl.h> +#include <conio.h> +#include <sys/timeb.h> + +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 <arpa/inet.h> 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 <fcntl.h> 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 <inttypes.h> 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 <memory.h> 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 <netdb.h> header file. */ +#define HAVE_NETDB_H 1 + +/* Define to 1 if you have the <netinet/in.h> 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 <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> 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 <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> 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 <syslog.h> header file. */ +#define HAVE_SYSLOG_H 1 + +/* Define to 1 if you have the <sys/socket.h> header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/timeb.h> header file. */ +#define HAVE_SYS_TIMEB_H 1 + +/* Define to 1 if you have the <sys/time.h> header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the <unistd.h> 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 <vfork.h> 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 <sys/time.h> and <time.h>. */ +#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 <sys/types.h> does not define. */ +/* #undef pid_t */ + +/* Define to `unsigned' if <sys/types.h> 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 <rfb/rfb.h> +#include <rfb/rfbregion.h> + +/* -=- 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->start<ymin) + ymin=vcurr->start; + if(vcurr->end>ymax) + ymax=vcurr->end; + + hcurr = vcurr->subspan->front._next; + while (hcurr != &(vcurr->subspan->back)) { + if(hcurr->start<xmin) + xmin=hcurr->start; + if(hcurr->end>xmax) + xmax=hcurr->end; + hcurr = hcurr->_next; + } + + vcurr = vcurr->_next; + } + + if(xmax<xmin || ymax<ymin) + return sraRgnCreate(); + + return sraRgnCreateRect(xmin,ymin,xmax,ymax); +} + +rfbBool +sraRgnPopRect(sraRegion *rgn, sraRect *rect, unsigned long flags) { + sraSpan *vcurr, *hcurr; + sraSpan *vend, *hend; + rfbBool right2left = flags & 2; + rfbBool bottom2top = flags & 1; + + /* - Pick correct order */ + if (bottom2top) { + vcurr = ((sraSpanList*)rgn)->back._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 <mcguirk@incompleteness.net>. + * 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 <string.h> +#include <rfb/rfb.h> +#include <rfb/rfbregion.h> + +#ifdef LIBVNCSERVER_HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#ifdef WIN32 +#define write(sock,buf,len) send(sock,buf,len,0) +#else +#ifdef LIBVNCSERVER_HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <pwd.h> +#ifdef LIBVNCSERVER_HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef LIBVNCSERVER_HAVE_NETINET_IN_H +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#endif +#endif + +#ifdef CORBA +#include <vncserverctrl.h> +#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.y1 || 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 <mcguirk@incompleteness.net>. + * 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 <rfb/rfb.h> + +/* + * 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 [<bgcolour><subrect><subrect>...] where each + * <subrect> is [<colour><x><y><w><h>]. + */ + +#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<h; y++) { \ + line = data+(y*w); \ + for (x=0; x<w; x++) { \ + if (line[x] != bg) { \ + cl = line[x]; \ + hy = y-1; \ + hyflag = 1; \ + for (j=y; j<h; j++) { \ + seg = data+(j*w); \ + if (seg[x] != cl) {break;} \ + i = x; \ + while ((seg[i] == cl) && (i < w)) i += 1; \ + i -= 1; \ + if (j == y) vx = hx = i; \ + if (i < vx) vx = i; \ + if ((hyflag > 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; i++) { + counts[i] = 0; + } + + for (j=0; j<size; j++) { + k = (int)(((uint8_t *)data)[j]); + if (k >= 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 <ctype.h> +#include <rfb/rfb.h> +#include <rfb/keysym.h> + +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+line<m->listSize) + 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(_index<m->displayStart || _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(_index<targetLine) + targetLine = _index; + else if(_index+m->pageH-targetLine>=m->listSize) + targetLine = _index+m->pageH-m->listSize; + delta = _index-(m->displayStart+targetLine); + + if(delta>-m->pageH && delta<m->pageH) { + 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;delta<lineEnd;delta++) + if(delta!=_index) + selPaintLine(m,delta,FALSE); + } + + m->selected = _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;i<m->selected && 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+1<m->listSize) + selSelect(m,curSel+1); + } else { + if(keySym==XK_Page_Down) { + if(curSel+m->pageH<m->listSize) + 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(y<m->okY && y>=m->okY-m->textH) { + if(x>=m->okBX && x<m->okBX+m->buttonWidth) { + if(!m->okInverted) + selPaintButtons(m,TRUE,FALSE); + if(buttonMask) + m->state = OK; + } else if(x>=m->cancelBX && x<m->cancelBX+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 && x<m->x2 && y>=m->y1 && y<m->y2) + 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-y1<selData.textH*2+3*border) + return(-1); + selData.xhot = -fx1; + selData.yhot = -fy2; + selData.x1 = x1+border; + selData.y1 = y1+border; + selData.y2 = y2-selData.textH-3*border; + selData.x2 = x2-2*border; + selData.pageH = (selData.y2-selData.y1)/selData.textH; + + i = rfbWidthOfString(font,okStr); + j = rfbWidthOfString(font,cancelStr); + selData.buttonWidth= k = 4*border+(i<j)?j:i; + selData.okBX = x1+(x2-x1-2*k)/3; + if(selData.okBX<x1+border) /* too narrow! */ + return(-1); + selData.cancelBX = x1+k+(x2-x1-2*k)*2/3; + selData.okX = selData.okBX+(k-i)/2; + selData.cancelX = selData.cancelBX+(k-j)/2; + selData.okY = y2-border; + + rfbUndrawCursor(rfbScreen); + frameBufferBackup = (char*)malloc(bpp*(x2-x1)*(y2-y1)); + + selData.state = SELECTING; + selData.screen = rfbScreen; + selData.font = font; + selData.list = list; + selData.colour = colour; + selData.backColour = backColour; + for(i=0;list[i];i++); + selData.selected = i; + selData.listSize = i; + selData.displayStart = i; + selData.lastButtons = 0; + selData.selChangedHook = selChangedHook; + + rfbScreen->screenData = &selData; + rfbScreen->kbdAddEvent = selKbdAddEvent; + rfbScreen->ptrAddEvent = selPtrAddEvent; + rfbScreen->getCursorPtr = selGetCursorPtr; + rfbScreen->displayHook = 0; + + /* backup screen */ + for(j=0;j<y2-y1;j++) + memcpy(frameBufferBackup+j*(x2-x1)*bpp, + rfbScreen->frameBuffer+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;j<y2-y1;j++) + memcpy(rfbScreen->frameBuffer+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 <mcguirk@incompleteness.net>. + * 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 <rfb/rfb.h> + +#ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H +#include <sys/types.h> +#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 <sys/time.h> +#endif +#ifdef LIBVNCSERVER_HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef LIBVNCSERVER_HAVE_NETINET_IN_H +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <arpa/inet.h> +#endif +#ifdef LIBVNCSERVER_HAVE_UNISTD_H +#include <unistd.h> +#endif +#endif + +#if defined(__linux__) && defined(NEED_TIMEVAL) +struct timeval +{ + long int tv_sec,tv_usec; +} +; +#endif + +#ifdef LIBVNCSERVER_HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#include <errno.h> + +#ifdef USE_LIBWRAP +#include <syslog.h> +#include <tcpd.h> +int allow_severity=LOG_INFO; +int deny_severity=LOG_WARNING; +#endif + +/*#ifndef WIN32 +int max(int i,int j) { return(i<j?j:i); } +#endif +*/ + +int rfbMaxClientWait = 20000; /* time (ms) after which we decide client has + gone away - needed to stop us hanging */ + +/* + * rfbInitSockets sets up the TCP and UDP sockets to listen for RFB + * connections. It does nothing if called again. + */ + +void +rfbInitSockets(rfbScreenInfoPtr rfbScreen) +{ + if (rfbScreen->socketInitDone) + 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 <mcguirk@incompleteness.net>. + * 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 <rfb/rfb.h> + +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 <mcguirk@incompleteness.net>. + * 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 <mcguirk@incompleteness.net>. + * 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 <mcguirk@incompleteness.net>. + * 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 <mcguirk@incompleteness.net>. + * 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 <mcguirk@incompleteness.net>. + * 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 <stdio.h>*/ +#include <rfb/rfb.h> + +#ifdef WIN32 +#define XMD_H +#undef FAR +#define NEEDFAR_POINTERS +#endif + +#include <jpeglib.h> + +/* 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 <mcguirk@incompleteness.net>. + * 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 <rfb/rfb.h> +#include <rfb/rfbregion.h> + +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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <rfb/rfbproto.h> +#include "d3des.h" + +#include <string.h> +#include <math.h> + +#ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef LIBVNCSERVER_HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +#include <time.h> + +#ifdef WIN32 +#define srandom srand +#define random rand +#else +#include <sys/time.h> +#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 <rfb/rfb.h> + +/* + * 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 <zrleencodetemplate.c> +#undef BPP +#define BPP 16 +#include <zrleencodetemplate.c> +#undef BPP +#define BPP 32 +#include <zrleencodetemplate.c> +#define CPIXEL 24A +#include <zrleencodetemplate.c> +#undef CPIXEL +#define CPIXEL 24B +#include <zrleencodetemplate.c> +#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 <assert.h> + +/* __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 <stdlib.h> + +#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 <zlib.h> +#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 <assert.h> +#include <string.h> + +#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__ */ |