summaryrefslogtreecommitdiffstats
path: root/libvncclient
diff options
context:
space:
mode:
Diffstat (limited to 'libvncclient')
-rw-r--r--libvncclient/Makefile17
-rw-r--r--libvncclient/corre.c70
-rw-r--r--libvncclient/cursor.c175
-rw-r--r--libvncclient/hextile.c120
-rw-r--r--libvncclient/listen.c94
-rw-r--r--libvncclient/rfbproto.c1052
-rw-r--r--libvncclient/rre.c68
-rw-r--r--libvncclient/sockets.c424
-rw-r--r--libvncclient/tight.c606
-rw-r--r--libvncclient/vncviewer.c240
-rw-r--r--libvncclient/zlib.c158
11 files changed, 3024 insertions, 0 deletions
diff --git a/libvncclient/Makefile b/libvncclient/Makefile
new file mode 100644
index 0000000..026b5b2
--- /dev/null
+++ b/libvncclient/Makefile
@@ -0,0 +1,17 @@
+CFLAGS=-g -I.. -I. -Wall
+LDLIBS=-lz -ljpeg
+
+OBJS=cursor.o listen.o rfbproto.o sockets.o vncviewer.o ../libvncserver.a
+
+all: libvncclient.a
+
+rfbproto.o: rfbproto.c corre.c hextile.c rre.c tight.c zlib.c
+
+$(OBJS): ../rfb/rfbclient.h
+
+libvncclient.a: $(OBJS)
+ $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+clean:
+ rm *.o *.a
+
diff --git a/libvncclient/corre.c b/libvncclient/corre.c
new file mode 100644
index 0000000..d6d3d0d
--- /dev/null
+++ b/libvncclient/corre.c
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+/*
+ * corre.c - handle CoRRE encoding.
+ *
+ * This file shouldn't be compiled directly. It is included multiple times by
+ * rfbproto.c, each time with a different definition of the macro BPP. For
+ * each value of BPP, this file defines a function which handles a CoRRE
+ * encoded rectangle with BPP bits per pixel.
+ */
+
+#define HandleCoRREBPP CONCAT2E(HandleCoRRE,BPP)
+#define CARDBPP CONCAT3E(uint,BPP,_t)
+
+static Bool
+HandleCoRREBPP (rfbClient* client, int rx, int ry, int rw, int rh)
+{
+ rfbRREHeader hdr;
+ int i;
+ CARDBPP pix;
+ uint8_t *ptr;
+ int x, y, w, h;
+
+ if (!ReadFromRFBServer(client, (char *)&hdr, sz_rfbRREHeader))
+ return FALSE;
+
+ hdr.nSubrects = Swap32IfLE(hdr.nSubrects);
+
+ if (!ReadFromRFBServer(client, (char *)&pix, sizeof(pix)))
+ return FALSE;
+
+ FillRectangle(client, rx, ry, rw, rh, pix);
+
+ if (!ReadFromRFBServer(client, client->buffer, hdr.nSubrects * (4 + (BPP / 8))))
+ return FALSE;
+
+ ptr = (uint8_t *)client->buffer;
+
+ for (i = 0; i < hdr.nSubrects; i++) {
+ pix = *(CARDBPP *)ptr;
+ ptr += BPP/8;
+ x = *ptr++;
+ y = *ptr++;
+ w = *ptr++;
+ h = *ptr++;
+
+ FillRectangle(client, rx+x, ry+y, w, h, pix);
+ }
+
+ return TRUE;
+}
+
+#undef CARDBPP
diff --git a/libvncclient/cursor.c b/libvncclient/cursor.c
new file mode 100644
index 0000000..f6a7816
--- /dev/null
+++ b/libvncclient/cursor.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2001,2002 Constantin Kaplinsky. 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.
+ */
+
+/*
+ * cursor.c - code to support cursor shape updates (XCursor and
+ * RichCursor preudo-encodings).
+ */
+
+#include <rfb/rfbclient.h>
+
+
+#define OPER_SAVE 0
+#define OPER_RESTORE 1
+
+#define RGB24_TO_PIXEL(bpp,r,g,b) \
+ ((((uint##bpp##_t)(r) & 0xFF) * client->format.redMax + 127) / 255 \
+ << client->format.redShift | \
+ (((uint##bpp##_t)(g) & 0xFF) * client->format.greenMax + 127) / 255 \
+ << client->format.greenShift | \
+ (((uint##bpp##_t)(b) & 0xFF) * client->format.blueMax + 127) / 255 \
+ << client->format.blueShift)
+
+
+/*********************************************************************
+ * HandleCursorShape(). Support for XCursor and RichCursor shape
+ * updates. We emulate cursor operating on the frame buffer (that is
+ * why we call it "software cursor").
+ ********************************************************************/
+
+Bool HandleCursorShape(rfbClient* client,int xhot, int yhot, int width, int height, uint32_t enc)
+{
+ int bytesPerPixel;
+ size_t bytesPerRow, bytesMaskData;
+ rfbXCursorColors rgb;
+ uint32_t colors[2];
+ char *buf;
+ uint8_t *ptr;
+ int x, y, b;
+
+ bytesPerPixel = client->format.bitsPerPixel / 8;
+ bytesPerRow = (width + 7) / 8;
+ bytesMaskData = bytesPerRow * height;
+
+ if (width * height == 0)
+ return TRUE;
+
+ /* Allocate memory for pixel data and temporary mask data. */
+ if(client->rcSource)
+ free(client->rcSource);
+
+ client->rcSource = malloc(width * height * bytesPerPixel);
+ if (client->rcSource == NULL)
+ return FALSE;
+
+ buf = malloc(bytesMaskData);
+ if (buf == NULL) {
+ free(client->rcSource);
+ client->rcSource = NULL;
+ return FALSE;
+ }
+
+ /* Read and decode cursor pixel data, depending on the encoding type. */
+
+ if (enc == rfbEncodingXCursor) {
+ /* Read and convert background and foreground colors. */
+ if (!ReadFromRFBServer(client, (char *)&rgb, sz_rfbXCursorColors)) {
+ free(client->rcSource);
+ client->rcSource = NULL;
+ free(buf);
+ return FALSE;
+ }
+ colors[0] = RGB24_TO_PIXEL(32, rgb.backRed, rgb.backGreen, rgb.backBlue);
+ colors[1] = RGB24_TO_PIXEL(32, rgb.foreRed, rgb.foreGreen, rgb.foreBlue);
+
+ /* Read 1bpp pixel data into a temporary buffer. */
+ if (!ReadFromRFBServer(client, buf, bytesMaskData)) {
+ free(client->rcSource);
+ client->rcSource = NULL;
+ free(buf);
+ return FALSE;
+ }
+
+ /* Convert 1bpp data to byte-wide color indices. */
+ ptr = client->rcSource;
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width / 8; x++) {
+ for (b = 7; b >= 0; b--) {
+ *ptr = buf[y * bytesPerRow + x] >> b & 1;
+ ptr += bytesPerPixel;
+ }
+ }
+ for (b = 7; b > 7 - width % 8; b--) {
+ *ptr = buf[y * bytesPerRow + x] >> b & 1;
+ ptr += bytesPerPixel;
+ }
+ }
+
+ /* Convert indices into the actual pixel values. */
+ switch (bytesPerPixel) {
+ case 1:
+ for (x = 0; x < width * height; x++)
+ client->rcSource[x] = (uint8_t)colors[client->rcSource[x]];
+ break;
+ case 2:
+ for (x = 0; x < width * height; x++)
+ ((uint16_t *)client->rcSource)[x] = (uint16_t)colors[client->rcSource[x * 2]];
+ break;
+ case 4:
+ for (x = 0; x < width * height; x++)
+ ((uint32_t *)client->rcSource)[x] = colors[client->rcSource[x * 4]];
+ break;
+ }
+
+ } else { /* enc == rfbEncodingRichCursor */
+
+ if (!ReadFromRFBServer(client, (char *)client->rcSource, width * height * bytesPerPixel)) {
+ free(client->rcSource);
+ client->rcSource = NULL;
+ free(buf);
+ return FALSE;
+ }
+
+ }
+
+ /* Read and decode mask data. */
+
+ if (!ReadFromRFBServer(client, buf, bytesMaskData)) {
+ free(client->rcSource);
+ client->rcSource = NULL;
+ free(buf);
+ return FALSE;
+ }
+
+ client->rcMask = malloc(width * height);
+ if (client->rcMask == NULL) {
+ free(client->rcSource);
+ client->rcSource = NULL;
+ free(buf);
+ return FALSE;
+ }
+
+ ptr = client->rcMask;
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width / 8; x++) {
+ for (b = 7; b >= 0; b--) {
+ *ptr++ = buf[y * bytesPerRow + x] >> b & 1;
+ }
+ }
+ for (b = 7; b > 7 - width % 8; b--) {
+ *ptr++ = buf[y * bytesPerRow + x] >> b & 1;
+ }
+ }
+
+ free(buf);
+
+ return TRUE;
+}
+
+
diff --git a/libvncclient/hextile.c b/libvncclient/hextile.c
new file mode 100644
index 0000000..e09fbc9
--- /dev/null
+++ b/libvncclient/hextile.c
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+/*
+ * hextile.c - handle hextile encoding.
+ *
+ * This file shouldn't be compiled directly. It is included multiple times by
+ * rfbproto.c, each time with a different definition of the macro BPP. For
+ * each value of BPP, this file defines a function which handles a hextile
+ * encoded rectangle with BPP bits per pixel.
+ */
+
+#define HandleHextileBPP CONCAT2E(HandleHextile,BPP)
+#define CARDBPP CONCAT3E(uint,BPP,_t)
+#define GET_PIXEL CONCAT2E(GET_PIXEL,BPP)
+
+static Bool
+HandleHextileBPP (rfbClient* client, int rx, int ry, int rw, int rh)
+{
+ CARDBPP bg, fg;
+ int i;
+ uint8_t *ptr;
+ int x, y, w, h;
+ int sx, sy, sw, sh;
+ uint8_t subencoding;
+ uint8_t nSubrects;
+
+ 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 (!ReadFromRFBServer(client, (char *)&subencoding, 1))
+ return FALSE;
+
+ if (subencoding & rfbHextileRaw) {
+ if (!ReadFromRFBServer(client, client->buffer, w * h * (BPP / 8)))
+ return FALSE;
+
+ CopyRectangle(client, client->buffer, x, y, w, h);
+
+ continue;
+ }
+
+ if (subencoding & rfbHextileBackgroundSpecified)
+ if (!ReadFromRFBServer(client, (char *)&bg, sizeof(bg)))
+ return FALSE;
+
+ FillRectangle(client, x, y, w, h, fg);
+
+ if (subencoding & rfbHextileForegroundSpecified)
+ if (!ReadFromRFBServer(client, (char *)&fg, sizeof(fg)))
+ return FALSE;
+
+ if (!(subencoding & rfbHextileAnySubrects)) {
+ continue;
+ }
+
+ if (!ReadFromRFBServer(client, (char *)&nSubrects, 1))
+ return FALSE;
+
+ ptr = (uint8_t*)client->buffer;
+
+ if (subencoding & rfbHextileSubrectsColoured) {
+ if (!ReadFromRFBServer(client, client->buffer, nSubrects * (2 + (BPP / 8))))
+ return FALSE;
+
+ for (i = 0; i < nSubrects; i++) {
+ GET_PIXEL(fg, ptr);
+ sx = rfbHextileExtractX(*ptr);
+ sy = rfbHextileExtractY(*ptr);
+ ptr++;
+ sw = rfbHextileExtractW(*ptr);
+ sh = rfbHextileExtractH(*ptr);
+ ptr++;
+
+ FillRectangle(client, x+sx, y+sy, sw, sh, fg);
+ }
+
+ } else {
+ if (!ReadFromRFBServer(client, client->buffer, nSubrects * 2))
+ return FALSE;
+
+ for (i = 0; i < nSubrects; i++) {
+ sx = rfbHextileExtractX(*ptr);
+ sy = rfbHextileExtractY(*ptr);
+ ptr++;
+ sw = rfbHextileExtractW(*ptr);
+ sh = rfbHextileExtractH(*ptr);
+ ptr++;
+
+ FillRectangle(client, x+sx, y+sy, sw, sh, fg);
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+#undef CARDBPP
diff --git a/libvncclient/listen.c b/libvncclient/listen.c
new file mode 100644
index 0000000..c11aef7
--- /dev/null
+++ b/libvncclient/listen.c
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+/*
+ * listen.c - listen for incoming connections
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/utsname.h>
+#include <rfb/rfbclient.h>
+
+/*
+ * listenForIncomingConnections() - listen for incoming connections from
+ * servers, and fork a new process to deal with each connection.
+ */
+
+void
+listenForIncomingConnections(rfbClient* client)
+{
+ int listenSocket;
+ fd_set fds;
+
+ client->listenSpecified = TRUE;
+
+ listenSocket = ListenAtTcpPort(client->listenPort);
+
+ if ((listenSocket < 0)) exit(1);
+
+ fprintf(stderr,"%s -listen: Listening on port %d\n",
+ client->programName,client->listenPort);
+ fprintf(stderr,"%s -listen: Command line errors are not reported until "
+ "a connection comes in.\n", client->programName);
+
+ while (TRUE) {
+
+ /* reap any zombies */
+ int status, pid;
+ while ((pid= wait3(&status, WNOHANG, (struct rusage *)0))>0);
+
+ /* TODO: callback for discard any events (like X11 events) */
+
+ FD_ZERO(&fds);
+
+ FD_SET(listenSocket, &fds);
+
+ select(FD_SETSIZE, &fds, NULL, NULL, NULL);
+
+ if (FD_ISSET(listenSocket, &fds)) {
+ client->sock = AcceptTcpConnection(listenSocket);
+ if (client->sock < 0) exit(1);
+ if (!SetNonBlocking(client->sock)) exit(1);
+
+ /* Now fork off a new process to deal with it... */
+
+ switch (fork()) {
+
+ case -1:
+ perror("fork");
+ exit(1);
+
+ case 0:
+ /* child - return to caller */
+ close(listenSocket);
+ return;
+
+ default:
+ /* parent - go round and listen again */
+ close(client->sock);
+ break;
+ }
+ }
+ }
+}
+
+
diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c
new file mode 100644
index 0000000..e654fcc
--- /dev/null
+++ b/libvncclient/rfbproto.c
@@ -0,0 +1,1052 @@
+/*
+ * Copyright (C) 2000-2002 Constantin Kaplinsky. All Rights Reserved.
+ * 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.
+ */
+
+/*
+ * rfbproto.c - functions to deal with client side of RFB protocol.
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <pwd.h>
+#include <rfb/rfbclient.h>
+#ifdef HAVE_LIBZ
+#include <zlib.h>
+#endif
+#ifdef HAVE_LIBJPEG
+#include <jpeglib.h>
+#endif
+
+void FillRectangle(rfbClient* client, int x, int y, int w, int h, uint32_t colour) {
+ int i,j;
+
+#define FILL_RECT(BPP) \
+ for(j=y*client->width;j<(y+h)*client->width;j+=client->width) \
+ for(i=x;i<x+w;i++) \
+ ((uint##BPP##_t*)client->frameBuffer)[j+i]=colour;
+
+ switch(client->format.bitsPerPixel) {
+ case 8: FILL_RECT(8); break;
+ case 16: FILL_RECT(16); break;
+ case 32: FILL_RECT(32); break;
+ default:
+ fprintf(stderr,"Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel);
+ }
+}
+
+void CopyRectangle(rfbClient* client, uint8_t* buffer, int x, int y, int w, int h) {
+ int i,j;
+
+#define COPY_RECT(BPP) \
+ { \
+ uint##BPP##_t* _buffer=(uint##BPP##_t*)buffer; \
+ for(j=y*client->width;j<(y+h)*client->width;j+=client->width) { \
+ for(i=x;i<x+w;i++,_buffer++) \
+ ((uint##BPP##_t*)client->frameBuffer)[j+i]=*_buffer; \
+ } \
+ }
+
+ switch(client->format.bitsPerPixel) {
+ case 8: COPY_RECT(8); break;
+ case 16: COPY_RECT(16); break;
+ case 32: COPY_RECT(32); break;
+ default:
+ fprintf(stderr,"Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel);
+ }
+}
+
+/* TODO: test */
+void CopyRectangleFromRectangle(rfbClient* client, int src_x, int src_y, int w, int h, int dest_x, int dest_y) {
+ int i,j;
+
+#define COPY_RECT_FROM_RECT(BPP) \
+ { \
+ uint##BPP##_t* _buffer=((uint##BPP##_t*)client->frameBuffer)+(src_y-dest_y)*client->width+src_x-dest_x; \
+ for(j=dest_y*client->width;j<(dest_y+h)*client->width;j+=client->width) { \
+ for(i=dest_x;i<dest_x+w;i++) \
+ ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \
+ } \
+ }
+
+ switch(client->format.bitsPerPixel) {
+ case 8: COPY_RECT_FROM_RECT(8); break;
+ case 16: COPY_RECT_FROM_RECT(16); break;
+ case 32: COPY_RECT_FROM_RECT(32); break;
+ default:
+ fprintf(stderr,"Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel);
+ }
+}
+
+static Bool HandleRRE8(rfbClient* client, int rx, int ry, int rw, int rh);
+static Bool HandleRRE16(rfbClient* client, int rx, int ry, int rw, int rh);
+static Bool HandleRRE32(rfbClient* client, int rx, int ry, int rw, int rh);
+static Bool HandleCoRRE8(rfbClient* client, int rx, int ry, int rw, int rh);
+static Bool HandleCoRRE16(rfbClient* client, int rx, int ry, int rw, int rh);
+static Bool HandleCoRRE32(rfbClient* client, int rx, int ry, int rw, int rh);
+static Bool HandleHextile8(rfbClient* client, int rx, int ry, int rw, int rh);
+static Bool HandleHextile16(rfbClient* client, int rx, int ry, int rw, int rh);
+static Bool HandleHextile32(rfbClient* client, int rx, int ry, int rw, int rh);
+static Bool HandleZlib8(rfbClient* client, int rx, int ry, int rw, int rh);
+static Bool HandleZlib16(rfbClient* client, int rx, int ry, int rw, int rh);
+static Bool HandleZlib32(rfbClient* client, int rx, int ry, int rw, int rh);
+static Bool HandleTight8(rfbClient* client, int rx, int ry, int rw, int rh);
+static Bool HandleTight16(rfbClient* client, int rx, int ry, int rw, int rh);
+static Bool HandleTight32(rfbClient* client, int rx, int ry, int rw, int rh);
+
+static long ReadCompactLen (rfbClient* client);
+
+static void JpegInitSource(j_decompress_ptr cinfo);
+static boolean JpegFillInputBuffer(j_decompress_ptr cinfo);
+static void JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes);
+static void JpegTermSource(j_decompress_ptr cinfo);
+static void JpegSetSrcManager(j_decompress_ptr cinfo, uint8_t *compressedData,
+ int compressedLen);
+
+/* The zlib encoding requires expansion/decompression/deflation of the
+ compressed data in the "buffer" above into another, result buffer.
+ However, the size of the result buffer can be determined precisely
+ based on the bitsPerPixel, height and width of the rectangle. We
+ allocate this buffer one time to be the full size of the buffer. */
+
+static int raw_buffer_size = -1;
+static char *raw_buffer;
+
+static z_stream decompStream;
+static Bool decompStreamInited = FALSE;
+
+
+/*
+ * Variables for the ``tight'' encoding implementation.
+ */
+
+/* Separate buffer for compressed data. */
+#define ZLIB_BUFFER_SIZE 512
+static char zlib_buffer[ZLIB_BUFFER_SIZE];
+
+/* Four independent compression streams for zlib library. */
+static z_stream zlibStream[4];
+static Bool zlibStreamActive[4] = {
+ FALSE, FALSE, FALSE, FALSE
+};
+
+/* Filter stuff. Should be initialized by filter initialization code. */
+static Bool cutZeros;
+static int rectWidth, rectColors;
+static char tightPalette[256*4];
+static uint8_t tightPrevRow[2048*3*sizeof(uint16_t)];
+
+/* JPEG decoder state. */
+static Bool jpegError;
+
+
+/*
+ * ConnectToRFBServer.
+ */
+
+Bool
+ConnectToRFBServer(rfbClient* client,const char *hostname, int port)
+{
+ unsigned int host;
+
+ if (!StringToIPAddr(hostname, &host)) {
+ fprintf(stderr,"Couldn't convert '%s' to host address\n", hostname);
+ return FALSE;
+ }
+
+ client->sock = ConnectToTcpAddr(host, port);
+
+ if (client->sock < 0) {
+ fprintf(stderr,"Unable to connect to VNC server\n");
+ return FALSE;
+ }
+
+ return SetNonBlocking(client->sock);
+}
+
+static void rfbEncryptBytes(unsigned char *bytes, char *passwd);
+
+/*
+ * InitialiseRFBConnection.
+ */
+
+Bool
+InitialiseRFBConnection(rfbClient* client)
+{
+ rfbProtocolVersionMsg pv;
+ int major,minor;
+ uint32_t authScheme, reasonLen, authResult;
+ char *reason;
+ uint8_t challenge[CHALLENGESIZE];
+ char *passwd;
+ int i;
+ rfbClientInitMsg ci;
+
+ /* if the connection is immediately closed, don't report anything, so
+ that pmw's monitor can make test connections */
+
+ if (client->listenSpecified)
+ errorMessageOnReadFailure = FALSE;
+
+ if (!ReadFromRFBServer(client, pv, sz_rfbProtocolVersionMsg)) return FALSE;
+
+ errorMessageOnReadFailure = TRUE;
+
+ pv[sz_rfbProtocolVersionMsg] = 0;
+
+ if (sscanf(pv,rfbProtocolVersionFormat,&major,&minor) != 2) {
+ fprintf(stderr,"Not a valid VNC server\n");
+ return FALSE;
+ }
+
+ fprintf(stderr,"VNC server supports protocol version %d.%d (viewer %d.%d)\n",
+ major, minor, rfbProtocolMajorVersion, rfbProtocolMinorVersion);
+
+ major = rfbProtocolMajorVersion;
+ minor = rfbProtocolMinorVersion;
+
+ sprintf(pv,rfbProtocolVersionFormat,major,minor);
+
+ if (!WriteExact(client, pv, sz_rfbProtocolVersionMsg)) return FALSE;
+
+ if (!ReadFromRFBServer(client, (char *)&authScheme, 4)) return FALSE;
+
+ authScheme = Swap32IfLE(authScheme);
+
+ switch (authScheme) {
+
+ case rfbConnFailed:
+ if (!ReadFromRFBServer(client, (char *)&reasonLen, 4)) return FALSE;
+ reasonLen = Swap32IfLE(reasonLen);
+
+ reason = malloc(reasonLen);
+
+ if (!ReadFromRFBServer(client, reason, reasonLen)) return FALSE;
+
+ fprintf(stderr,"VNC connection failed: %.*s\n",(int)reasonLen, reason);
+ return FALSE;
+
+ case rfbNoAuth:
+ fprintf(stderr,"No authentication needed\n");
+ break;
+
+ case rfbVncAuth:
+ if (!ReadFromRFBServer(client, (char *)challenge, CHALLENGESIZE)) return FALSE;
+
+ if (client->GetPassword)
+ passwd = client->GetPassword(client);
+
+ if ((!passwd) || (strlen(passwd) == 0)) {
+ fprintf(stderr,"Reading password failed\n");
+ return FALSE;
+ }
+ if (strlen(passwd) > 8) {
+ passwd[8] = '\0';
+ }
+
+ rfbEncryptBytes(challenge, passwd);
+
+ /* Lose the password from memory */
+ for (i = strlen(passwd); i >= 0; i--) {
+ passwd[i] = '\0';
+ }
+
+ if (!WriteExact(client, (char *)challenge, CHALLENGESIZE)) return FALSE;
+
+ if (!ReadFromRFBServer(client, (char *)&authResult, 4)) return FALSE;
+
+ authResult = Swap32IfLE(authResult);
+
+ switch (authResult) {
+ case rfbVncAuthOK:
+ fprintf(stderr,"VNC authentication succeeded\n");
+ break;
+ case rfbVncAuthFailed:
+ fprintf(stderr,"VNC authentication failed\n");
+ return FALSE;
+ case rfbVncAuthTooMany:
+ fprintf(stderr,"VNC authentication failed - too many tries\n");
+ return FALSE;
+ default:
+ fprintf(stderr,"Unknown VNC authentication result: %d\n",
+ (int)authResult);
+ return FALSE;
+ }
+ break;
+
+ default:
+ fprintf(stderr,"Unknown authentication scheme from VNC server: %d\n",
+ (int)authScheme);
+ return FALSE;
+ }
+
+ ci.shared = (client->appData.shareDesktop ? 1 : 0);
+
+ if (!WriteExact(client, (char *)&ci, sz_rfbClientInitMsg)) return FALSE;
+
+ if (!ReadFromRFBServer(client, (char *)&client->si, sz_rfbServerInitMsg)) return FALSE;
+
+ client->si.framebufferWidth = Swap16IfLE(client->si.framebufferWidth);
+ client->si.framebufferHeight = Swap16IfLE(client->si.framebufferHeight);
+ client->si.format.redMax = Swap16IfLE(client->si.format.redMax);
+ client->si.format.greenMax = Swap16IfLE(client->si.format.greenMax);
+ client->si.format.blueMax = Swap16IfLE(client->si.format.blueMax);
+ client->si.nameLength = Swap32IfLE(client->si.nameLength);
+
+ client->desktopName = malloc(client->si.nameLength + 1);
+ if (!client->desktopName) {
+ fprintf(stderr, "Error allocating memory for desktop name, %lu bytes\n",
+ (unsigned long)client->si.nameLength);
+ return FALSE;
+ }
+
+ if (!ReadFromRFBServer(client, client->desktopName, client->si.nameLength)) return FALSE;
+
+ client->desktopName[client->si.nameLength] = 0;
+
+ fprintf(stderr,"Desktop name \"%s\"\n",client->desktopName);
+
+ fprintf(stderr,"Connected to VNC server, using protocol version %d.%d\n",
+ rfbProtocolMajorVersion, rfbProtocolMinorVersion);
+
+ fprintf(stderr,"VNC server default format:\n");
+ PrintPixelFormat(&client->si.format);
+
+ return TRUE;
+}
+
+
+/*
+ * SetFormatAndEncodings.
+ */
+
+Bool
+SetFormatAndEncodings(rfbClient* client)
+{
+ rfbSetPixelFormatMsg spf;
+ char buf[sz_rfbSetEncodingsMsg + MAX_ENCODINGS * 4];
+ rfbSetEncodingsMsg *se = (rfbSetEncodingsMsg *)buf;
+ uint32_t *encs = (uint32_t *)(&buf[sz_rfbSetEncodingsMsg]);
+ int len = 0;
+ Bool requestCompressLevel = FALSE;
+ Bool requestQualityLevel = FALSE;
+ Bool requestLastRectEncoding = FALSE;
+
+ spf.type = rfbSetPixelFormat;
+ spf.format = client->format;
+ spf.format.redMax = Swap16IfLE(spf.format.redMax);
+ spf.format.greenMax = Swap16IfLE(spf.format.greenMax);
+ spf.format.blueMax = Swap16IfLE(spf.format.blueMax);
+
+ if (!WriteExact(client, (char *)&spf, sz_rfbSetPixelFormatMsg))
+ return FALSE;
+
+ se->type = rfbSetEncodings;
+ se->nEncodings = 0;
+
+ if (client->appData.encodingsString) {
+ const char *encStr = client->appData.encodingsString;
+ int encStrLen;
+ do {
+ const char *nextEncStr = strchr(encStr, ' ');
+ if (nextEncStr) {
+ encStrLen = nextEncStr - encStr;
+ nextEncStr++;
+ } else {
+ encStrLen = strlen(encStr);
+ }
+
+ if (strncasecmp(encStr,"raw",encStrLen) == 0) {
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRaw);
+ } else if (strncasecmp(encStr,"copyrect",encStrLen) == 0) {
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCopyRect);
+ } else if (strncasecmp(encStr,"tight",encStrLen) == 0) {
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingTight);
+ requestLastRectEncoding = TRUE;
+ if (client->appData.compressLevel >= 0 && client->appData.compressLevel <= 9)
+ requestCompressLevel = TRUE;
+ if (client->appData.enableJPEG)
+ requestQualityLevel = TRUE;
+ } else if (strncasecmp(encStr,"hextile",encStrLen) == 0) {
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingHextile);
+ } else if (strncasecmp(encStr,"zlib",encStrLen) == 0) {
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingZlib);
+ if (client->appData.compressLevel >= 0 && client->appData.compressLevel <= 9)
+ requestCompressLevel = TRUE;
+ } else if (strncasecmp(encStr,"corre",encStrLen) == 0) {
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCoRRE);
+ } else if (strncasecmp(encStr,"rre",encStrLen) == 0) {
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRRE);
+ } else {
+ fprintf(stderr,"Unknown encoding '%.*s'\n",encStrLen,encStr);
+ }
+
+ encStr = nextEncStr;
+ } while (encStr && se->nEncodings < MAX_ENCODINGS);
+
+ if (se->nEncodings < MAX_ENCODINGS && requestCompressLevel) {
+ encs[se->nEncodings++] = Swap32IfLE(client->appData.compressLevel +
+ rfbEncodingCompressLevel0);
+ }
+
+ if (se->nEncodings < MAX_ENCODINGS && requestQualityLevel) {
+ if (client->appData.qualityLevel < 0 || client->appData.qualityLevel > 9)
+ client->appData.qualityLevel = 5;
+ encs[se->nEncodings++] = Swap32IfLE(client->appData.qualityLevel +
+ rfbEncodingQualityLevel0);
+ }
+
+ if (client->appData.useRemoteCursor) {
+ if (se->nEncodings < MAX_ENCODINGS)
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingXCursor);
+ if (se->nEncodings < MAX_ENCODINGS)
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRichCursor);
+ if (se->nEncodings < MAX_ENCODINGS)
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingPointerPos);
+ }
+
+ if (se->nEncodings < MAX_ENCODINGS && requestLastRectEncoding) {
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingLastRect);
+ }
+ }
+ else {
+ if (SameMachine(client->sock)) {
+ /* TODO:
+ if (!tunnelSpecified) {
+ */
+ fprintf(stderr,"Same machine: preferring raw encoding\n");
+ /* TODO: */
+ //encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRaw);
+ /*
+ } else {
+ fprintf(stderr,"Tunneling active: preferring tight encoding\n");
+ }
+ */
+ }
+
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCopyRect);
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingTight);
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingHextile);
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingZlib);
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCoRRE);
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRRE);
+
+ if (client->appData.compressLevel >= 0 && client->appData.compressLevel <= 9) {
+ encs[se->nEncodings++] = Swap32IfLE(client->appData.compressLevel +
+ rfbEncodingCompressLevel0);
+ } else /* if (!tunnelSpecified) */ {
+ /* If -tunnel option was provided, we assume that server machine is
+ not in the local network so we use default compression level for
+ tight encoding instead of fast compression. Thus we are
+ requesting level 1 compression only if tunneling is not used. */
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCompressLevel1);
+ }
+
+ if (client->appData.enableJPEG) {
+ if (client->appData.qualityLevel < 0 || client->appData.qualityLevel > 9)
+ client->appData.qualityLevel = 5;
+ encs[se->nEncodings++] = Swap32IfLE(client->appData.qualityLevel +
+ rfbEncodingQualityLevel0);
+ }
+
+ if (client->appData.useRemoteCursor) {
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingXCursor);
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRichCursor);
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingPointerPos);
+ }
+
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingLastRect);
+ }
+
+ len = sz_rfbSetEncodingsMsg + se->nEncodings * 4;
+
+ se->nEncodings = Swap16IfLE(se->nEncodings);
+
+ if (!WriteExact(client, buf, len)) return FALSE;
+
+ return TRUE;
+}
+
+
+/*
+ * SendIncrementalFramebufferUpdateRequest.
+ */
+
+Bool
+SendIncrementalFramebufferUpdateRequest(rfbClient* client)
+{
+ return SendFramebufferUpdateRequest(client, 0, 0, client->si.framebufferWidth,
+ client->si.framebufferHeight, TRUE);
+}
+
+
+/*
+ * SendFramebufferUpdateRequest.
+ */
+
+Bool
+SendFramebufferUpdateRequest(rfbClient* client, int x, int y, int w, int h, Bool incremental)
+{
+ rfbFramebufferUpdateRequestMsg fur;
+
+ fur.type = rfbFramebufferUpdateRequest;
+ fur.incremental = incremental ? 1 : 0;
+ fur.x = Swap16IfLE(x);
+ fur.y = Swap16IfLE(y);
+ fur.w = Swap16IfLE(w);
+ fur.h = Swap16IfLE(h);
+
+ if (!WriteExact(client, (char *)&fur, sz_rfbFramebufferUpdateRequestMsg))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+/*
+ * SendPointerEvent.
+ */
+
+Bool
+SendPointerEvent(rfbClient* client,int x, int y, int buttonMask)
+{
+ rfbPointerEventMsg pe;
+
+ pe.type = rfbPointerEvent;
+ pe.buttonMask = buttonMask;
+ if (x < 0) x = 0;
+ if (y < 0) y = 0;
+
+ pe.x = Swap16IfLE(x);
+ pe.y = Swap16IfLE(y);
+ return WriteExact(client, (char *)&pe, sz_rfbPointerEventMsg);
+}
+
+
+/*
+ * SendKeyEvent.
+ */
+
+Bool
+SendKeyEvent(rfbClient* client, uint32_t key, Bool down)
+{
+ rfbKeyEventMsg ke;
+
+ ke.type = rfbKeyEvent;
+ ke.down = down ? 1 : 0;
+ ke.key = Swap32IfLE(key);
+ return WriteExact(client, (char *)&ke, sz_rfbKeyEventMsg);
+}
+
+
+/*
+ * SendClientCutText.
+ */
+
+Bool
+SendClientCutText(rfbClient* client, char *str, int len)
+{
+ rfbClientCutTextMsg cct;
+
+ if (client->serverCutText)
+ free(client->serverCutText);
+ client->serverCutText = NULL;
+
+ cct.type = rfbClientCutText;
+ cct.length = Swap32IfLE(len);
+ return (WriteExact(client, (char *)&cct, sz_rfbClientCutTextMsg) &&
+ WriteExact(client, str, len));
+}
+
+
+
+/*
+ * HandleRFBServerMessage.
+ */
+
+Bool
+HandleRFBServerMessage(rfbClient* client)
+{
+ rfbServerToClientMsg msg;
+
+ if (!ReadFromRFBServer(client, (char *)&msg, 1))
+ return FALSE;
+
+ switch (msg.type) {
+
+ case rfbSetColourMapEntries:
+ {
+ /* TODO:
+ int i;
+ uint16_t rgb[3];
+ XColor xc;
+
+ if (!ReadFromRFBServer(client, ((char *)&msg) + 1,
+ sz_rfbSetColourMapEntriesMsg - 1))
+ return FALSE;
+
+ msg.scme.firstColour = Swap16IfLE(msg.scme.firstColour);
+ msg.scme.nColours = Swap16IfLE(msg.scme.nColours);
+
+ for (i = 0; i < msg.scme.nColours; i++) {
+ if (!ReadFromRFBServer(client, (char *)rgb, 6))
+ return FALSE;
+ xc.pixel = msg.scme.firstColour + i;
+ xc.red = Swap16IfLE(rgb[0]);
+ xc.green = Swap16IfLE(rgb[1]);
+ xc.blue = Swap16IfLE(rgb[2]);
+ xc.flags = DoRed|DoGreen|DoBlue;
+ XStoreColor(dpy, cmap, &xc);
+ }
+ */
+
+ break;
+ }
+
+ case rfbFramebufferUpdate:
+ {
+ rfbFramebufferUpdateRectHeader rect;
+ int linesToRead;
+ int bytesPerLine;
+ int i;
+
+ if (!ReadFromRFBServer(client, ((char *)&msg.fu) + 1,
+ sz_rfbFramebufferUpdateMsg - 1))
+ return FALSE;
+
+ msg.fu.nRects = Swap16IfLE(msg.fu.nRects);
+
+ for (i = 0; i < msg.fu.nRects; i++) {
+ if (!ReadFromRFBServer(client, (char *)&rect, sz_rfbFramebufferUpdateRectHeader))
+ return FALSE;
+
+ rect.encoding = Swap32IfLE(rect.encoding);
+ if (rect.encoding == rfbEncodingLastRect)
+ break;
+
+ rect.r.x = Swap16IfLE(rect.r.x);
+ rect.r.y = Swap16IfLE(rect.r.y);
+ rect.r.w = Swap16IfLE(rect.r.w);
+ rect.r.h = Swap16IfLE(rect.r.h);
+
+ if (rect.encoding == rfbEncodingXCursor ||
+ rect.encoding == rfbEncodingRichCursor) {
+ if (!HandleCursorShape(client,
+ rect.r.x, rect.r.y, rect.r.w, rect.r.h,
+ rect.encoding)) {
+ return FALSE;
+ }
+ continue;
+ }
+
+ if (rect.encoding == rfbEncodingPointerPos) {
+ if (!client->HandleCursorPos(client,rect.r.x, rect.r.y)) {
+ return FALSE;
+ }
+ continue;
+ }
+
+ if ((rect.r.x + rect.r.w > client->si.framebufferWidth) ||
+ (rect.r.y + rect.r.h > client->si.framebufferHeight))
+ {
+ fprintf(stderr,"Rect too large: %dx%d at (%d, %d)\n",
+ rect.r.w, rect.r.h, rect.r.x, rect.r.y);
+ return FALSE;
+ }
+
+ if (rect.r.h * rect.r.w == 0) {
+ fprintf(stderr,"Zero size rect - ignoring\n");
+ continue;
+ }
+
+ /* If RichCursor encoding is used, we should prevent collisions
+ between framebuffer updates and cursor drawing operations. */
+ client->SoftCursorLockArea(client, rect.r.x, rect.r.y, rect.r.w, rect.r.h);
+
+ switch (rect.encoding) {
+
+ case rfbEncodingRaw:
+
+ bytesPerLine = rect.r.w * client->format.bitsPerPixel / 8;
+ linesToRead = BUFFER_SIZE / bytesPerLine;
+
+ while (rect.r.h > 0) {
+ if (linesToRead > rect.r.h)
+ linesToRead = rect.r.h;
+
+ if (!ReadFromRFBServer(client, client->buffer,bytesPerLine * linesToRead))
+ return FALSE;
+
+ CopyRectangle(client, client->buffer,
+ rect.r.x, rect.r.y, rect.r.w,linesToRead);
+
+ rect.r.h -= linesToRead;
+ rect.r.y += linesToRead;
+
+ }
+ break;
+
+ case rfbEncodingCopyRect:
+ {
+ rfbCopyRect cr;
+
+ if (!ReadFromRFBServer(client, (char *)&cr, sz_rfbCopyRect))
+ return FALSE;
+
+ cr.srcX = Swap16IfLE(cr.srcX);
+ cr.srcY = Swap16IfLE(cr.srcY);
+
+ /* If RichCursor encoding is used, we should extend our
+ "cursor lock area" (previously set to destination
+ rectangle) to the source rectangle as well. */
+ client->SoftCursorLockArea(client,
+ cr.srcX, cr.srcY, rect.r.w, rect.r.h);
+
+ CopyRectangleFromRectangle(client,
+ cr.srcX, cr.srcY, rect.r.w, rect.r.h,
+ rect.r.x, rect.r.y);
+
+ break;
+ }
+
+ case rfbEncodingRRE:
+ {
+ switch (client->format.bitsPerPixel) {
+ case 8:
+ if (!HandleRRE8(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return FALSE;
+ break;
+ case 16:
+ if (!HandleRRE16(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return FALSE;
+ break;
+ case 32:
+ if (!HandleRRE32(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return FALSE;
+ break;
+ }
+ break;
+ }
+
+ case rfbEncodingCoRRE:
+ {
+ switch (client->format.bitsPerPixel) {
+ case 8:
+ if (!HandleCoRRE8(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return FALSE;
+ break;
+ case 16:
+ if (!HandleCoRRE16(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return FALSE;
+ break;
+ case 32:
+ if (!HandleCoRRE32(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return FALSE;
+ break;
+ }
+ break;
+ }
+
+ case rfbEncodingHextile:
+ {
+ switch (client->format.bitsPerPixel) {
+ case 8:
+ if (!HandleHextile8(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return FALSE;
+ break;
+ case 16:
+ if (!HandleHextile16(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return FALSE;
+ break;
+ case 32:
+ if (!HandleHextile32(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return FALSE;
+ break;
+ }
+ break;
+ }
+
+ case rfbEncodingZlib:
+ {
+ switch (client->format.bitsPerPixel) {
+ case 8:
+ if (!HandleZlib8(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return FALSE;
+ break;
+ case 16:
+ if (!HandleZlib16(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return FALSE;
+ break;
+ case 32:
+ if (!HandleZlib32(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return FALSE;
+ break;
+ }
+ break;
+ }
+
+ case rfbEncodingTight:
+ {
+ switch (client->format.bitsPerPixel) {
+ case 8:
+ if (!HandleTight8(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return FALSE;
+ break;
+ case 16:
+ if (!HandleTight16(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return FALSE;
+ break;
+ case 32:
+ if (!HandleTight32(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return FALSE;
+ break;
+ }
+ break;
+ }
+
+ default:
+ fprintf(stderr,"Unknown rect encoding %d\n",
+ (int)rect.encoding);
+ return FALSE;
+ }
+
+ /* Now we may discard "soft cursor locks". */
+ client->SoftCursorUnlockScreen(client);
+
+ client->FramebufferUpdateReceived(client, rect.r.x, rect.r.y, rect.r.w, rect.r.h);
+ }
+
+#ifdef MITSHM
+ /* if using shared memory PutImage, make sure that the X server has
+ updated its framebuffer before we reuse the shared memory. This is
+ mainly to avoid copyrect using invalid screen contents - not sure
+ if we'd need it otherwise. */
+
+ if (client->appData.useShm)
+ XSync(dpy, FALSE);
+#endif
+
+ if (!SendIncrementalFramebufferUpdateRequest(client))
+ return FALSE;
+
+ break;
+ }
+
+ case rfbBell:
+ {
+ client->Bell(client);
+
+ break;
+ }
+
+ case rfbServerCutText:
+ {
+ if (!ReadFromRFBServer(client, ((char *)&msg) + 1,
+ sz_rfbServerCutTextMsg - 1))
+ return FALSE;
+
+ msg.sct.length = Swap32IfLE(msg.sct.length);
+
+ if (client->serverCutText)
+ free(client->serverCutText);
+
+ client->serverCutText = malloc(msg.sct.length+1);
+
+ if (!ReadFromRFBServer(client, client->serverCutText, msg.sct.length))
+ return FALSE;
+
+ client->serverCutText[msg.sct.length] = 0;
+
+ client->newServerCutText = TRUE;
+
+ break;
+ }
+
+ default:
+ fprintf(stderr,"Unknown message type %d from VNC server\n",msg.type);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+#define GET_PIXEL8(pix, ptr) ((pix) = *(ptr)++)
+
+#define GET_PIXEL16(pix, ptr) (((uint8_t*)&(pix))[0] = *(ptr)++, \
+ ((uint8_t*)&(pix))[1] = *(ptr)++)
+
+#define GET_PIXEL32(pix, ptr) (((uint8_t*)&(pix))[0] = *(ptr)++, \
+ ((uint8_t*)&(pix))[1] = *(ptr)++, \
+ ((uint8_t*)&(pix))[2] = *(ptr)++, \
+ ((uint8_t*)&(pix))[3] = *(ptr)++)
+
+/* CONCAT2 concatenates its two arguments. CONCAT2E does the same but also
+ expands its arguments if they are macros */
+
+#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 BPP 8
+#include "rre.c"
+#include "corre.c"
+#include "hextile.c"
+#include "zlib.c"
+#include "tight.c"
+#undef BPP
+#define BPP 16
+#include "rre.c"
+#include "corre.c"
+#include "hextile.c"
+#include "zlib.c"
+#include "tight.c"
+#undef BPP
+#define BPP 32
+#include "rre.c"
+#include "corre.c"
+#include "hextile.c"
+#include "zlib.c"
+#include "tight.c"
+#undef BPP
+
+
+/*
+ * PrintPixelFormat.
+ */
+
+void
+PrintPixelFormat(format)
+ rfbPixelFormat *format;
+{
+ if (format->bitsPerPixel == 1) {
+ fprintf(stderr," Single bit per pixel.\n");
+ fprintf(stderr,
+ " %s significant bit in each byte is leftmost on the screen.\n",
+ (format->bigEndian ? "Most" : "Least"));
+ } else {
+ fprintf(stderr," %d bits per pixel.\n",format->bitsPerPixel);
+ if (format->bitsPerPixel != 8) {
+ fprintf(stderr," %s significant byte first in each pixel.\n",
+ (format->bigEndian ? "Most" : "Least"));
+ }
+ if (format->trueColour) {
+ fprintf(stderr," TRUE colour: max red %d green %d blue %d",
+ format->redMax, format->greenMax, format->blueMax);
+ fprintf(stderr,", shift red %d green %d blue %d\n",
+ format->redShift, format->greenShift, format->blueShift);
+ } else {
+ fprintf(stderr," Colour map (not true colour).\n");
+ }
+ }
+}
+
+static long
+ReadCompactLen (rfbClient* client)
+{
+ long len;
+ uint8_t b;
+
+ if (!ReadFromRFBServer(client, (char *)&b, 1))
+ return -1;
+ len = (int)b & 0x7F;
+ if (b & 0x80) {
+ if (!ReadFromRFBServer(client, (char *)&b, 1))
+ return -1;
+ len |= ((int)b & 0x7F) << 7;
+ if (b & 0x80) {
+ if (!ReadFromRFBServer(client, (char *)&b, 1))
+ return -1;
+ len |= ((int)b & 0xFF) << 14;
+ }
+ }
+ return len;
+}
+
+/*
+ * JPEG source manager functions for JPEG decompression in Tight decoder.
+ */
+
+static struct jpeg_source_mgr jpegSrcManager;
+static JOCTET *jpegBufferPtr;
+static size_t jpegBufferLen;
+
+static void
+JpegInitSource(j_decompress_ptr cinfo)
+{
+ jpegError = FALSE;
+}
+
+static boolean
+JpegFillInputBuffer(j_decompress_ptr cinfo)
+{
+ jpegError = TRUE;
+ jpegSrcManager.bytes_in_buffer = jpegBufferLen;
+ jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr;
+
+ return TRUE;
+}
+
+static void
+JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes)
+{
+ if (num_bytes < 0 || num_bytes > jpegSrcManager.bytes_in_buffer) {
+ jpegError = TRUE;
+ jpegSrcManager.bytes_in_buffer = jpegBufferLen;
+ jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr;
+ } else {
+ jpegSrcManager.next_input_byte += (size_t) num_bytes;
+ jpegSrcManager.bytes_in_buffer -= (size_t) num_bytes;
+ }
+}
+
+static void
+JpegTermSource(j_decompress_ptr cinfo)
+{
+ /* No work necessary here. */
+}
+
+static void
+JpegSetSrcManager(j_decompress_ptr cinfo, uint8_t *compressedData,
+ int compressedLen)
+{
+ jpegBufferPtr = (JOCTET *)compressedData;
+ jpegBufferLen = (size_t)compressedLen;
+
+ jpegSrcManager.init_source = JpegInitSource;
+ jpegSrcManager.fill_input_buffer = JpegFillInputBuffer;
+ jpegSrcManager.skip_input_data = JpegSkipInputData;
+ jpegSrcManager.resync_to_restart = jpeg_resync_to_restart;
+ jpegSrcManager.term_source = JpegTermSource;
+ jpegSrcManager.next_input_byte = jpegBufferPtr;
+ jpegSrcManager.bytes_in_buffer = jpegBufferLen;
+
+ cinfo->src = &jpegSrcManager;
+}
+
+/* avoid name clashes with LibVNCServer */
+
+#define vncEncryptBytes rfbEncryptBytes
+#define vncEncryptAndStorePasswd rfbEncryptAndStorePasswdUnused
+#define vncDecryptPasswdFromFile rfbDecryptPasswdFromFileUnused
+#define vncRandomBytes rfbRandomBytesUnused
+
+#include "../vncauth.c"
+#include "../d3des.c"
diff --git a/libvncclient/rre.c b/libvncclient/rre.c
new file mode 100644
index 0000000..fba3767
--- /dev/null
+++ b/libvncclient/rre.c
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+/*
+ * rre.c - handle RRE encoding.
+ *
+ * This file shouldn't be compiled directly. It is included multiple times by
+ * rfbproto.c, each time with a different definition of the macro BPP. For
+ * each value of BPP, this file defines a function which handles an RRE
+ * encoded rectangle with BPP bits per pixel.
+ */
+
+#define HandleRREBPP CONCAT2E(HandleRRE,BPP)
+#define CARDBPP CONCAT3E(uint,BPP,_t)
+
+static Bool
+HandleRREBPP (rfbClient* client, int rx, int ry, int rw, int rh)
+{
+ rfbRREHeader hdr;
+ int i;
+ CARDBPP pix;
+ rfbRectangle subrect;
+
+ if (!ReadFromRFBServer(client, (char *)&hdr, sz_rfbRREHeader))
+ return FALSE;
+
+ hdr.nSubrects = Swap32IfLE(hdr.nSubrects);
+
+ if (!ReadFromRFBServer(client, (char *)&pix, sizeof(pix)))
+ return FALSE;
+
+ FillRectangle(client, rx, ry, rw, rh, pix);
+
+ for (i = 0; i < hdr.nSubrects; i++) {
+ if (!ReadFromRFBServer(client, (char *)&pix, sizeof(pix)))
+ return FALSE;
+
+ if (!ReadFromRFBServer(client, (char *)&subrect, sz_rfbRectangle))
+ return FALSE;
+
+ subrect.x = Swap16IfLE(subrect.x);
+ subrect.y = Swap16IfLE(subrect.y);
+ subrect.w = Swap16IfLE(subrect.w);
+ subrect.h = Swap16IfLE(subrect.h);
+
+ FillRectangle(client, rx+subrect.x, ry+subrect.y, subrect.w, subrect.h, pix);
+ }
+
+ return TRUE;
+}
+
+#undef CARDBPP
diff --git a/libvncclient/sockets.c b/libvncclient/sockets.c
new file mode 100644
index 0000000..5e6cd69
--- /dev/null
+++ b/libvncclient/sockets.c
@@ -0,0 +1,424 @@
+/*
+ * 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.
+ */
+
+/*
+ * sockets.c - functions to deal with sockets.
+ */
+
+#include <unistd.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <rfb/rfbclient.h>
+
+void PrintInHex(char *buf, int len);
+
+Bool errorMessageOnReadFailure = TRUE;
+
+#define BUF_SIZE 8192
+static char buf[BUF_SIZE];
+static char *bufoutptr = buf;
+static int buffered = 0;
+
+/*
+ * ReadFromRFBServer is called whenever we want to read some data from the RFB
+ * server. It is non-trivial for two reasons:
+ *
+ * 1. For efficiency it performs some intelligent buffering, avoiding invoking
+ * the read() system call too often. For small chunks of data, it simply
+ * copies the data out of an internal buffer. For large amounts of data it
+ * reads directly into the buffer provided by the caller.
+ *
+ * 2. Whenever read() would block, it invokes the Xt event dispatching
+ * mechanism to process X events. In fact, this is the only place these
+ * events are processed, as there is no XtAppMainLoop in the program.
+ */
+
+Bool
+ReadFromRFBServer(rfbClient* client, char *out, unsigned int n)
+{
+ if (n <= buffered) {
+ memcpy(out, bufoutptr, n);
+ bufoutptr += n;
+ buffered -= n;
+ return TRUE;
+ }
+
+ memcpy(out, bufoutptr, buffered);
+
+ out += buffered;
+ n -= buffered;
+
+ bufoutptr = buf;
+ buffered = 0;
+
+ if (n <= BUF_SIZE) {
+
+ while (buffered < n) {
+ int i = read(client->sock, buf + buffered, BUF_SIZE - buffered);
+ if (i <= 0) {
+ if (i < 0) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN) {
+ /* TODO:
+ ProcessXtEvents();
+ */
+ i = 0;
+ } else {
+ perror("read");
+ return FALSE;
+ }
+ } else {
+ if (errorMessageOnReadFailure) {
+ fprintf(stderr,"VNC server closed connection\n");
+ }
+ return FALSE;
+ }
+ }
+ buffered += i;
+ }
+
+ memcpy(out, bufoutptr, n);
+ bufoutptr += n;
+ buffered -= n;
+ return TRUE;
+
+ } else {
+
+ while (n > 0) {
+ int i = read(client->sock, out, n);
+ if (i <= 0) {
+ if (i < 0) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN) {
+ /* TODO:
+ ProcessXtEvents();
+ */
+ i = 0;
+ } else {
+ perror("read");
+ return FALSE;
+ }
+ } else {
+ if (errorMessageOnReadFailure) {
+ fprintf(stderr,"VNC server closed connection\n");
+ }
+ return FALSE;
+ }
+ }
+ out += i;
+ n -= i;
+ }
+
+ return TRUE;
+ }
+}
+
+
+/*
+ * Write an exact number of bytes, and don't return until you've sent them.
+ */
+
+Bool
+WriteExact(rfbClient* client, char *buf, int n)
+{
+ fd_set fds;
+ int i = 0;
+ int j;
+
+ while (i < n) {
+ j = write(client->sock, buf + i, (n - i));
+ if (j <= 0) {
+ if (j < 0) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN) {
+ FD_ZERO(&fds);
+ FD_SET(client->sock,&fds);
+
+ if (select(client->sock+1, NULL, &fds, NULL, NULL) <= 0) {
+ perror("select");
+ return FALSE;
+ }
+ j = 0;
+ } else {
+ perror("write");
+ return FALSE;
+ }
+ } else {
+ fprintf(stderr,"write failed\n");
+ return FALSE;
+ }
+ }
+ i += j;
+ }
+ return TRUE;
+}
+
+
+/*
+ * ConnectToTcpAddr connects to the given TCP port.
+ */
+
+int
+ConnectToTcpAddr(unsigned int host, int port)
+{
+ int sock;
+ struct sockaddr_in addr;
+ int one = 1;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = host;
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0) {
+ perror("ConnectToTcpAddr: socket");
+ return -1;
+ }
+
+ if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("ConnectToTcpAddr: connect");
+ close(sock);
+ return -1;
+ }
+
+ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+ (char *)&one, sizeof(one)) < 0) {
+ perror("ConnectToTcpAddr: setsockopt");
+ close(sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+
+
+/*
+ * FindFreeTcpPort tries to find unused TCP port in the range
+ * (TUNNEL_PORT_OFFSET, TUNNEL_PORT_OFFSET + 99]. Returns 0 on failure.
+ */
+
+int
+FindFreeTcpPort(void)
+{
+ int sock, port;
+ struct sockaddr_in addr;
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0) {
+ perror(": FindFreeTcpPort: socket");
+ return 0;
+ }
+
+ for (port = TUNNEL_PORT_OFFSET + 99; port > TUNNEL_PORT_OFFSET; port--) {
+ addr.sin_port = htons((unsigned short)port);
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
+ close(sock);
+ return port;
+ }
+ }
+
+ close(sock);
+ return 0;
+}
+
+
+/*
+ * ListenAtTcpPort starts listening at the given TCP port.
+ */
+
+int
+ListenAtTcpPort(int port)
+{
+ int sock;
+ struct sockaddr_in addr;
+ int one = 1;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = INADDR_ANY;
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0) {
+ perror("ListenAtTcpPort: socket");
+ return -1;
+ }
+
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (const char *)&one, sizeof(one)) < 0) {
+ perror("ListenAtTcpPort: setsockopt");
+ close(sock);
+ return -1;
+ }
+
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("ListenAtTcpPort: bind");
+ close(sock);
+ return -1;
+ }
+
+ if (listen(sock, 5) < 0) {
+ perror("ListenAtTcpPort: listen");
+ close(sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+
+/*
+ * AcceptTcpConnection accepts a TCP connection.
+ */
+
+int
+AcceptTcpConnection(int listenSock)
+{
+ int sock;
+ struct sockaddr_in addr;
+ int addrlen = sizeof(addr);
+ int one = 1;
+
+ sock = accept(listenSock, (struct sockaddr *) &addr, &addrlen);
+ if (sock < 0) {
+ perror("AcceptTcpConnection: accept");
+ return -1;
+ }
+
+ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+ (char *)&one, sizeof(one)) < 0) {
+ perror("AcceptTcpConnection: setsockopt");
+ close(sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+
+/*
+ * SetNonBlocking sets a socket into non-blocking mode.
+ */
+
+Bool
+SetNonBlocking(int sock)
+{
+ if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
+ perror("AcceptTcpConnection: fcntl");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/*
+ * StringToIPAddr - convert a host string to an IP address.
+ */
+
+Bool
+StringToIPAddr(const char *str, unsigned int *addr)
+{
+ struct hostent *hp;
+
+ if (strcmp(str,"") == 0) {
+ *addr = 0; /* local */
+ return TRUE;
+ }
+
+ *addr = inet_addr(str);
+
+ if (*addr != -1)
+ return TRUE;
+
+ hp = gethostbyname(str);
+
+ if (hp) {
+ *addr = *(unsigned int *)hp->h_addr;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/*
+ * Test if the other end of a socket is on the same machine.
+ */
+
+Bool
+SameMachine(int sock)
+{
+ struct sockaddr_in peeraddr, myaddr;
+ int addrlen = sizeof(struct sockaddr_in);
+
+ getpeername(sock, (struct sockaddr *)&peeraddr, &addrlen);
+ getsockname(sock, (struct sockaddr *)&myaddr, &addrlen);
+
+ return (peeraddr.sin_addr.s_addr == myaddr.sin_addr.s_addr);
+}
+
+
+/*
+ * Print out the contents of a packet for debugging.
+ */
+
+void
+PrintInHex(char *buf, int len)
+{
+ int i, j;
+ char c, str[17];
+
+ str[16] = 0;
+
+ fprintf(stderr,"ReadExact: ");
+
+ for (i = 0; i < len; i++)
+ {
+ if ((i % 16 == 0) && (i != 0)) {
+ fprintf(stderr," ");
+ }
+ c = buf[i];
+ str[i % 16] = (((c > 31) && (c < 127)) ? c : '.');
+ fprintf(stderr,"%02x ",(unsigned char)c);
+ if ((i % 4) == 3)
+ fprintf(stderr," ");
+ if ((i % 16) == 15)
+ {
+ fprintf(stderr,"%s\n",str);
+ }
+ }
+ if ((i % 16) != 0)
+ {
+ for (j = i % 16; j < 16; j++)
+ {
+ fprintf(stderr," ");
+ if ((j % 4) == 3) fprintf(stderr," ");
+ }
+ str[i % 16] = 0;
+ fprintf(stderr,"%s\n",str);
+ }
+
+ fflush(stderr);
+}
diff --git a/libvncclient/tight.c b/libvncclient/tight.c
new file mode 100644
index 0000000..c858392
--- /dev/null
+++ b/libvncclient/tight.c
@@ -0,0 +1,606 @@
+/*
+ * Copyright (C) 2000, 2001 Const Kaplinsky. 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.
+ */
+
+/*
+ * tight.c - handle ``tight'' encoding.
+ *
+ * This file shouldn't be compiled directly. It is included multiple
+ * times by rfbproto.c, each time with a different definition of the
+ * macro BPP. For each value of BPP, this file defines a function
+ * which handles a tight-encoded rectangle with BPP bits per pixel.
+ *
+ */
+
+#define TIGHT_MIN_TO_COMPRESS 12
+
+#define CARDBPP CONCAT3E(uint,BPP,_t)
+#define filterPtrBPP CONCAT2E(filterPtr,BPP)
+
+#define HandleTightBPP CONCAT2E(HandleTight,BPP)
+#define InitFilterCopyBPP CONCAT2E(InitFilterCopy,BPP)
+#define InitFilterPaletteBPP CONCAT2E(InitFilterPalette,BPP)
+#define InitFilterGradientBPP CONCAT2E(InitFilterGradient,BPP)
+#define FilterCopyBPP CONCAT2E(FilterCopy,BPP)
+#define FilterPaletteBPP CONCAT2E(FilterPalette,BPP)
+#define FilterGradientBPP CONCAT2E(FilterGradient,BPP)
+
+#if BPP != 8
+#define DecompressJpegRectBPP CONCAT2E(DecompressJpegRect,BPP)
+#endif
+
+#ifndef RGB_TO_PIXEL
+
+#define RGB_TO_PIXEL(bpp,r,g,b) \
+ (((CARD##bpp)(r) & client->format.redMax) << client->format.redShift | \
+ ((CARD##bpp)(g) & client->format.greenMax) << client->format.greenShift | \
+ ((CARD##bpp)(b) & client->format.blueMax) << client->format.blueShift)
+
+#define RGB24_TO_PIXEL(bpp,r,g,b) \
+ ((((CARD##bpp)(r) & 0xFF) * client->format.redMax + 127) / 255 \
+ << client->format.redShift | \
+ (((CARD##bpp)(g) & 0xFF) * client->format.greenMax + 127) / 255 \
+ << client->format.greenShift | \
+ (((CARD##bpp)(b) & 0xFF) * client->format.blueMax + 127) / 255 \
+ << client->format.blueShift)
+
+#define RGB24_TO_PIXEL32(r,g,b) \
+ (((uint32_t)(r) & 0xFF) << client->format.redShift | \
+ ((uint32_t)(g) & 0xFF) << client->format.greenShift | \
+ ((uint32_t)(b) & 0xFF) << client->format.blueShift)
+
+#endif
+
+/* Type declarations */
+
+typedef void (*filterPtrBPP)(rfbClient* client, int, CARDBPP *);
+
+/* Prototypes */
+
+static int InitFilterCopyBPP (rfbClient* client, int rw, int rh);
+static int InitFilterPaletteBPP (rfbClient* client, int rw, int rh);
+static int InitFilterGradientBPP (rfbClient* client, int rw, int rh);
+static void FilterCopyBPP (rfbClient* client, int numRows, CARDBPP *destBuffer);
+static void FilterPaletteBPP (rfbClient* client, int numRows, CARDBPP *destBuffer);
+static void FilterGradientBPP (rfbClient* client, int numRows, CARDBPP *destBuffer);
+
+#if BPP != 8
+static Bool DecompressJpegRectBPP(rfbClient* client, int x, int y, int w, int h);
+#endif
+
+/* Definitions */
+
+static Bool
+HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh)
+{
+ CARDBPP fill_colour;
+ uint8_t comp_ctl;
+ uint8_t filter_id;
+ filterPtrBPP filterFn;
+ z_streamp zs;
+ char *buffer2;
+ int err, stream_id, compressedLen, bitsPixel;
+ int bufferSize, rowSize, numRows, portionLen, rowsProcessed, extraBytes;
+
+ if (!ReadFromRFBServer(client, (char *)&comp_ctl, 1))
+ return FALSE;
+
+ /* Flush zlib streams if we are told by the server to do so. */
+ for (stream_id = 0; stream_id < 4; stream_id++) {
+ if ((comp_ctl & 1) && zlibStreamActive[stream_id]) {
+ if (inflateEnd (&zlibStream[stream_id]) != Z_OK &&
+ zlibStream[stream_id].msg != NULL)
+ fprintf(stderr, "inflateEnd: %s\n", zlibStream[stream_id].msg);
+ zlibStreamActive[stream_id] = FALSE;
+ }
+ comp_ctl >>= 1;
+ }
+
+ /* Handle solid rectangles. */
+ if (comp_ctl == rfbTightFill) {
+#if BPP == 32
+ if (client->format.depth == 24 && client->format.redMax == 0xFF &&
+ client->format.greenMax == 0xFF && client->format.blueMax == 0xFF) {
+ if (!ReadFromRFBServer(client, client->buffer, 3))
+ return FALSE;
+ fill_colour = RGB24_TO_PIXEL32(client->buffer[0], client->buffer[1], client->buffer[2]);
+ } else {
+ if (!ReadFromRFBServer(client, (char*)&fill_colour, sizeof(fill_colour)))
+ return FALSE;
+ }
+#else
+ if (!ReadFromRFBServer(client, (char*)&fill_colour, sizeof(fill_colour)))
+ return FALSE;
+#endif
+
+ FillRectangle(client, rx, ry, rw, rh, fill_colour);
+
+ return TRUE;
+ }
+
+#if BPP == 8
+ if (comp_ctl == rfbTightJpeg) {
+ fprintf(stderr, "Tight encoding: JPEG is not supported in 8 bpp mode.\n");
+ return FALSE;
+ }
+#else
+ if (comp_ctl == rfbTightJpeg) {
+ return DecompressJpegRectBPP(client, rx, ry, rw, rh);
+ }
+#endif
+
+ /* Quit on unsupported subencoding value. */
+ if (comp_ctl > rfbTightMaxSubencoding) {
+ fprintf(stderr, "Tight encoding: bad subencoding value received.\n");
+ return FALSE;
+ }
+
+ /*
+ * Here primary compression mode handling begins.
+ * Data was processed with optional filter + zlib compression.
+ */
+
+ /* First, we should identify a filter to use. */
+ if ((comp_ctl & rfbTightExplicitFilter) != 0) {
+ if (!ReadFromRFBServer(client, (char*)&filter_id, 1))
+ return FALSE;
+
+ switch (filter_id) {
+ case rfbTightFilterCopy:
+ filterFn = FilterCopyBPP;
+ bitsPixel = InitFilterCopyBPP(client, rw, rh);
+ break;
+ case rfbTightFilterPalette:
+ filterFn = FilterPaletteBPP;
+ bitsPixel = InitFilterPaletteBPP(client, rw, rh);
+ break;
+ case rfbTightFilterGradient:
+ filterFn = FilterGradientBPP;
+ bitsPixel = InitFilterGradientBPP(client, rw, rh);
+ break;
+ default:
+ fprintf(stderr, "Tight encoding: unknown filter code received.\n");
+ return FALSE;
+ }
+ } else {
+ filterFn = FilterCopyBPP;
+ bitsPixel = InitFilterCopyBPP(client, rw, rh);
+ }
+ if (bitsPixel == 0) {
+ fprintf(stderr, "Tight encoding: error receiving palette.\n");
+ return FALSE;
+ }
+
+ /* Determine if the data should be decompressed or just copied. */
+ rowSize = (rw * bitsPixel + 7) / 8;
+ if (rh * rowSize < TIGHT_MIN_TO_COMPRESS) {
+ if (!ReadFromRFBServer(client, (char*)client->buffer, rh * rowSize))
+ return FALSE;
+
+ buffer2 = &client->buffer[TIGHT_MIN_TO_COMPRESS * 4];
+ filterFn(client, rh, (CARDBPP *)buffer2);
+
+ CopyRectangle(client, buffer2, rx, ry, rw, rh);
+
+ return TRUE;
+ }
+
+ /* Read the length (1..3 bytes) of compressed data following. */
+ compressedLen = (int)ReadCompactLen(client);
+ if (compressedLen <= 0) {
+ fprintf(stderr, "Incorrect data received from the server.\n");
+ return FALSE;
+ }
+
+ /* Now let's initialize compression stream if needed. */
+ stream_id = comp_ctl & 0x03;
+ zs = &zlibStream[stream_id];
+ if (!zlibStreamActive[stream_id]) {
+ zs->zalloc = Z_NULL;
+ zs->zfree = Z_NULL;
+ zs->opaque = Z_NULL;
+ err = inflateInit(zs);
+ if (err != Z_OK) {
+ if (zs->msg != NULL)
+ fprintf(stderr, "InflateInit error: %s.\n", zs->msg);
+ return FALSE;
+ }
+ zlibStreamActive[stream_id] = TRUE;
+ }
+
+ /* Read, decode and draw actual pixel data in a loop. */
+
+ bufferSize = BUFFER_SIZE * bitsPixel / (bitsPixel + BPP) & 0xFFFFFFFC;
+ buffer2 = &client->buffer[bufferSize];
+ if (rowSize > bufferSize) {
+ /* Should be impossible when BUFFER_SIZE >= 16384 */
+ fprintf(stderr, "Internal error: incorrect buffer size.\n");
+ return FALSE;
+ }
+
+ rowsProcessed = 0;
+ extraBytes = 0;
+
+ while (compressedLen > 0) {
+ if (compressedLen > ZLIB_BUFFER_SIZE)
+ portionLen = ZLIB_BUFFER_SIZE;
+ else
+ portionLen = compressedLen;
+
+ if (!ReadFromRFBServer(client, (char*)zlib_buffer, portionLen))
+ return FALSE;
+
+ compressedLen -= portionLen;
+
+ zs->next_in = (Bytef *)zlib_buffer;
+ zs->avail_in = portionLen;
+
+ do {
+ zs->next_out = (Bytef *)&client->buffer[extraBytes];
+ zs->avail_out = bufferSize - extraBytes;
+
+ err = inflate(zs, Z_SYNC_FLUSH);
+ if (err == Z_BUF_ERROR) /* Input exhausted -- no problem. */
+ break;
+ if (err != Z_OK && err != Z_STREAM_END) {
+ if (zs->msg != NULL) {
+ fprintf(stderr, "Inflate error: %s.\n", zs->msg);
+ } else {
+ fprintf(stderr, "Inflate error: %d.\n", err);
+ }
+ return FALSE;
+ }
+
+ numRows = (bufferSize - zs->avail_out) / rowSize;
+
+ filterFn(client, numRows, (CARDBPP *)buffer2);
+
+ extraBytes = bufferSize - zs->avail_out - numRows * rowSize;
+ if (extraBytes > 0)
+ memcpy(client->buffer, &client->buffer[numRows * rowSize], extraBytes);
+
+ CopyRectangle(client, buffer2, rx, ry+rowsProcessed, rw, numRows);
+
+ rowsProcessed += numRows;
+ }
+ while (zs->avail_out == 0);
+ }
+
+ if (rowsProcessed != rh) {
+ fprintf(stderr, "Incorrect number of scan lines after decompression.\n");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*----------------------------------------------------------------------------
+ *
+ * Filter stuff.
+ *
+ */
+
+/*
+ The following variables are defined in rfbproto.c:
+ static Bool cutZeros;
+ static int rectWidth, rectColors;
+ static uint8_t tightPalette[256*4];
+ static uint8_t tightPrevRow[2048*3*sizeof(CARD16)];
+*/
+
+static int
+InitFilterCopyBPP (rfbClient* client, int rw, int rh)
+{
+ rectWidth = rw;
+
+#if BPP == 32
+ if (client->format.depth == 24 && client->format.redMax == 0xFF &&
+ client->format.greenMax == 0xFF && client->format.blueMax == 0xFF) {
+ cutZeros = TRUE;
+ return 24;
+ } else {
+ cutZeros = FALSE;
+ }
+#endif
+
+ return BPP;
+}
+
+static void
+FilterCopyBPP (rfbClient* client, int numRows, CARDBPP *dst)
+{
+
+#if BPP == 32
+ int x, y;
+
+ if (cutZeros) {
+ for (y = 0; y < numRows; y++) {
+ for (x = 0; x < rectWidth; x++) {
+ dst[y*rectWidth+x] =
+ RGB24_TO_PIXEL32(client->buffer[(y*rectWidth+x)*3],
+ client->buffer[(y*rectWidth+x)*3+1],
+ client->buffer[(y*rectWidth+x)*3+2]);
+ }
+ }
+ return;
+ }
+#endif
+
+ memcpy (dst, client->buffer, numRows * rectWidth * (BPP / 8));
+}
+
+static int
+InitFilterGradientBPP (rfbClient* client, int rw, int rh)
+{
+ int bits;
+
+ bits = InitFilterCopyBPP(client, rw, rh);
+ if (cutZeros)
+ memset(tightPrevRow, 0, rw * 3);
+ else
+ memset(tightPrevRow, 0, rw * 3 * sizeof(uint16_t));
+
+ return bits;
+}
+
+#if BPP == 32
+
+static void
+FilterGradient24 (rfbClient* client, int numRows, uint32_t *dst)
+{
+ int x, y, c;
+ uint8_t thisRow[2048*3];
+ uint8_t pix[3];
+ int est[3];
+
+ for (y = 0; y < numRows; y++) {
+
+ /* First pixel in a row */
+ for (c = 0; c < 3; c++) {
+ pix[c] = tightPrevRow[c] + client->buffer[y*rectWidth*3+c];
+ thisRow[c] = pix[c];
+ }
+ dst[y*rectWidth] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]);
+
+ /* Remaining pixels of a row */
+ for (x = 1; x < rectWidth; x++) {
+ for (c = 0; c < 3; c++) {
+ est[c] = (int)tightPrevRow[x*3+c] + (int)pix[c] -
+ (int)tightPrevRow[(x-1)*3+c];
+ if (est[c] > 0xFF) {
+ est[c] = 0xFF;
+ } else if (est[c] < 0x00) {
+ est[c] = 0x00;
+ }
+ pix[c] = (uint8_t)est[c] + client->buffer[(y*rectWidth+x)*3+c];
+ thisRow[x*3+c] = pix[c];
+ }
+ dst[y*rectWidth+x] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]);
+ }
+
+ memcpy(tightPrevRow, thisRow, rectWidth * 3);
+ }
+}
+
+#endif
+
+static void
+FilterGradientBPP (rfbClient* client, int numRows, CARDBPP *dst)
+{
+ int x, y, c;
+ CARDBPP *src = (CARDBPP *)client->buffer;
+ uint16_t *thatRow = (uint16_t *)tightPrevRow;
+ uint16_t thisRow[2048*3];
+ uint16_t pix[3];
+ uint16_t max[3];
+ int shift[3];
+ int est[3];
+
+#if BPP == 32
+ if (cutZeros) {
+ FilterGradient24(client, numRows, dst);
+ return;
+ }
+#endif
+
+ max[0] = client->format.redMax;
+ max[1] = client->format.greenMax;
+ max[2] = client->format.blueMax;
+
+ shift[0] = client->format.redShift;
+ shift[1] = client->format.greenShift;
+ shift[2] = client->format.blueShift;
+
+ for (y = 0; y < numRows; y++) {
+
+ /* First pixel in a row */
+ for (c = 0; c < 3; c++) {
+ pix[c] = (uint16_t)(((src[y*rectWidth] >> shift[c]) + thatRow[c]) & max[c]);
+ thisRow[c] = pix[c];
+ }
+ dst[y*rectWidth] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]);
+
+ /* Remaining pixels of a row */
+ for (x = 1; x < rectWidth; x++) {
+ for (c = 0; c < 3; c++) {
+ est[c] = (int)thatRow[x*3+c] + (int)pix[c] - (int)thatRow[(x-1)*3+c];
+ if (est[c] > (int)max[c]) {
+ est[c] = (int)max[c];
+ } else if (est[c] < 0) {
+ est[c] = 0;
+ }
+ pix[c] = (uint16_t)(((src[y*rectWidth+x] >> shift[c]) + est[c]) & max[c]);
+ thisRow[x*3+c] = pix[c];
+ }
+ dst[y*rectWidth+x] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]);
+ }
+ memcpy(thatRow, thisRow, rectWidth * 3 * sizeof(uint16_t));
+ }
+}
+
+static int
+InitFilterPaletteBPP (rfbClient* client, int rw, int rh)
+{
+ uint8_t numColors;
+#if BPP == 32
+ int i;
+ CARDBPP *palette = (CARDBPP *)tightPalette;
+#endif
+
+ rectWidth = rw;
+
+ if (!ReadFromRFBServer(client, (char*)&numColors, 1))
+ return 0;
+
+ rectColors = (int)numColors;
+ if (++rectColors < 2)
+ return 0;
+
+#if BPP == 32
+ if (client->format.depth == 24 && client->format.redMax == 0xFF &&
+ client->format.greenMax == 0xFF && client->format.blueMax == 0xFF) {
+ if (!ReadFromRFBServer(client, (char*)&tightPalette, rectColors * 3))
+ return 0;
+ for (i = rectColors - 1; i >= 0; i--) {
+ palette[i] = RGB24_TO_PIXEL32(tightPalette[i*3],
+ tightPalette[i*3+1],
+ tightPalette[i*3+2]);
+ }
+ return (rectColors == 2) ? 1 : 8;
+ }
+#endif
+
+ if (!ReadFromRFBServer(client, (char*)&tightPalette, rectColors * (BPP / 8)))
+ return 0;
+
+ return (rectColors == 2) ? 1 : 8;
+}
+
+static void
+FilterPaletteBPP (rfbClient* client, int numRows, CARDBPP *dst)
+{
+ int x, y, b, w;
+ uint8_t *src = (uint8_t *)client->buffer;
+ CARDBPP *palette = (CARDBPP *)tightPalette;
+
+ if (rectColors == 2) {
+ w = (rectWidth + 7) / 8;
+ for (y = 0; y < numRows; y++) {
+ for (x = 0; x < rectWidth / 8; x++) {
+ for (b = 7; b >= 0; b--)
+ dst[y*rectWidth+x*8+7-b] = palette[src[y*w+x] >> b & 1];
+ }
+ for (b = 7; b >= 8 - rectWidth % 8; b--) {
+ dst[y*rectWidth+x*8+7-b] = palette[src[y*w+x] >> b & 1];
+ }
+ }
+ } else {
+ for (y = 0; y < numRows; y++)
+ for (x = 0; x < rectWidth; x++)
+ dst[y*rectWidth+x] = palette[(int)src[y*rectWidth+x]];
+ }
+}
+
+#if BPP != 8
+
+/*----------------------------------------------------------------------------
+ *
+ * JPEG decompression.
+ *
+ */
+
+/*
+ The following variables are defined in rfbproto.c:
+ static Bool jpegError;
+ static struct jpeg_source_mgr jpegSrcManager;
+ static JOCTET *jpegBufferPtr;
+ static size_t *jpegBufferLen;
+*/
+
+static Bool
+DecompressJpegRectBPP(rfbClient* client, int x, int y, int w, int h)
+{
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ int compressedLen;
+ uint8_t *compressedData;
+ CARDBPP *pixelPtr;
+ JSAMPROW rowPointer[1];
+ int dx, dy;
+
+ compressedLen = (int)ReadCompactLen(client);
+ if (compressedLen <= 0) {
+ fprintf(stderr, "Incorrect data received from the server.\n");
+ return FALSE;
+ }
+
+ compressedData = malloc(compressedLen);
+ if (compressedData == NULL) {
+ fprintf(stderr, "Memory allocation error.\n");
+ return FALSE;
+ }
+
+ if (!ReadFromRFBServer(client, (char*)compressedData, compressedLen)) {
+ free(compressedData);
+ return FALSE;
+ }
+
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_decompress(&cinfo);
+
+ JpegSetSrcManager(&cinfo, compressedData, compressedLen);
+
+ jpeg_read_header(&cinfo, TRUE);
+ cinfo.out_color_space = JCS_RGB;
+
+ jpeg_start_decompress(&cinfo);
+ if (cinfo.output_width != w || cinfo.output_height != h ||
+ cinfo.output_components != 3) {
+ fprintf(stderr, "Tight Encoding: Wrong JPEG data received.\n");
+ jpeg_destroy_decompress(&cinfo);
+ free(compressedData);
+ return FALSE;
+ }
+
+ rowPointer[0] = (JSAMPROW)client->buffer;
+ dy = 0;
+ while (cinfo.output_scanline < cinfo.output_height) {
+ jpeg_read_scanlines(&cinfo, rowPointer, 1);
+ if (jpegError) {
+ break;
+ }
+ pixelPtr = (CARDBPP *)&client->buffer[BUFFER_SIZE / 2];
+ for (dx = 0; dx < w; dx++) {
+ *pixelPtr++ =
+ RGB24_TO_PIXEL(BPP, client->buffer[dx*3], client->buffer[dx*3+1], client->buffer[dx*3+2]);
+ }
+ CopyRectangle(client, &client->buffer[BUFFER_SIZE / 2], x, y + dy, w, 1);
+ dy++;
+ }
+
+ if (!jpegError)
+ jpeg_finish_decompress(&cinfo);
+
+ jpeg_destroy_decompress(&cinfo);
+ free(compressedData);
+
+ return !jpegError;
+}
+
+#endif
+
diff --git a/libvncclient/vncviewer.c b/libvncclient/vncviewer.c
new file mode 100644
index 0000000..3c9c13c
--- /dev/null
+++ b/libvncclient/vncviewer.c
@@ -0,0 +1,240 @@
+/*
+ * 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.
+ */
+
+/*
+ * vncviewer.c - the Xt-based VNC viewer.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <rfb/rfbclient.h>
+
+static void Dummy(rfbClient* client) {
+}
+static Bool DummyPoint(rfbClient* client, int x, int y) {
+ return TRUE;
+}
+static void DummyRect(rfbClient* client, int x, int y, int w, int h) {
+}
+static char* NoPassword(rfbClient* client) {
+ return "";
+}
+static Bool MallocFrameBuffer(rfbClient* client) {
+ if(client->frameBuffer)
+ free(client->frameBuffer);
+ client->frameBuffer=malloc(client->width*client->height*client->format.bitsPerPixel/8);
+ return client->frameBuffer?TRUE:FALSE;
+}
+
+rfbClient* rfbGetClient(int* argc,char** argv,
+ int bitsPerSample,int samplesPerPixel,
+ int bytesPerPixel) {
+ rfbClient* client=(rfbClient*)calloc(sizeof(rfbClient),1);
+ client->programName = argv[0];
+ client->endianTest = 1;
+
+ client->format.bitsPerPixel = bytesPerPixel*8;
+ client->format.depth = bitsPerSample*samplesPerPixel;
+ client->format.bigEndian = *(char *)&client->endianTest?FALSE:TRUE;
+ client->format.trueColour = TRUE;
+
+ if (client->format.bitsPerPixel == 8) {
+ client->format.redMax = 7;
+ client->format.greenMax = 7;
+ client->format.blueMax = 3;
+ client->format.redShift = 0;
+ client->format.greenShift = 3;
+ client->format.blueShift = 6;
+ } else {
+ client->format.redMax = (1 << bitsPerSample) - 1;
+ client->format.greenMax = (1 << bitsPerSample) - 1;
+ client->format.blueMax = (1 << bitsPerSample) - 1;
+ if(!client->format.bigEndian) {
+ client->format.redShift = 0;
+ client->format.greenShift = bitsPerSample;
+ client->format.blueShift = bitsPerSample * 2;
+ } else {
+ if(client->format.bitsPerPixel==8*3) {
+ client->format.redShift = bitsPerSample*2;
+ client->format.greenShift = bitsPerSample*1;
+ client->format.blueShift = 0;
+ } else {
+ client->format.redShift = bitsPerSample*3;
+ client->format.greenShift = bitsPerSample*2;
+ client->format.blueShift = bitsPerSample;
+ }
+ }
+ }
+
+ client->HandleCursorPos = DummyPoint;
+ client->SoftCursorLockArea = DummyRect;
+ client->SoftCursorUnlockScreen = Dummy;
+ client->FramebufferUpdateReceived = DummyRect;
+ client->GetPassword = NoPassword;
+ client->MallocFrameBuffer = MallocFrameBuffer;
+ client->Bell = Dummy;
+
+ return client;
+}
+
+void PrintRect(rfbClient* client, int x, int y, int w, int h) {
+ fprintf(stderr,"Received an update for %d,%d,%d,%d.\n",x,y,w,h);
+}
+
+void SaveFramebufferAsPGM(rfbClient* client, int x, int y, int w, int h) {
+ static time_t t=0,t1;
+ FILE* f;
+ int i,j;
+ int bpp=client->format.bitsPerPixel/8;
+ int row_stride=client->width*bpp;
+
+ /* save one picture only if the last is older than 2 seconds */
+ t1=time(0);
+ if(t1-t>2)
+ t=t1;
+ else
+ return;
+
+ /* assert bpp=4 */
+ if(bpp!=4) {
+ fprintf(stderr,"bpp = %d (!=4)\n",bpp);
+ return;
+ }
+
+ f=fopen("/tmp/framebuffer.ppm","wb");
+
+ fprintf(f,"P6\n# %s\n%d %d\n255\n",client->desktopName,client->width,client->height);
+ for(j=0;j<client->height*row_stride;j+=row_stride)
+ for(i=0;i<client->width*bpp;i+=bpp) {
+ if(client->format.bigEndian) {
+ fputc(client->frameBuffer[j+i+bpp-1],f);
+ fputc(client->frameBuffer[j+i+bpp-2],f);
+ fputc(client->frameBuffer[j+i+bpp-3],f);
+ } else {
+ fputc(client->frameBuffer[j+i+bpp+0],f);
+ fputc(client->frameBuffer[j+i+bpp+1],f);
+ fputc(client->frameBuffer[j+i+bpp+2],f);
+ }
+ }
+ fclose(f);
+}
+
+void
+vncEncryptBytes(unsigned char *bytes, char *passwd);
+
+int
+main(int argc, char **argv)
+{
+ int i;
+ rfbClient* client = rfbGetClient(&argc,argv,8,3,4);
+ const char* vncServerHost="";
+ int vncServerPort=5900;
+
+ char buf1[]="pass",buf2[]="pass";
+ vncEncryptBytes(buf1,buf2);
+
+ client->FramebufferUpdateReceived = PrintRect;
+ client->FramebufferUpdateReceived = SaveFramebufferAsPGM;
+
+ /* The -listen option is used to make us a daemon process which listens for
+ incoming connections from servers, rather than actively connecting to a
+ given server. The -tunnel and -via options are useful to create
+ connections tunneled via SSH port forwarding. We must test for the
+ -listen option before invoking any Xt functions - this is because we use
+ forking, and Xt doesn't seem to cope with forking very well. For -listen
+ option, when a successful incoming connection has been accepted,
+ listenForIncomingConnections() returns, setting the listenSpecified
+ flag. */
+
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-listen") == 0) {
+ listenForIncomingConnections(client);
+ break;
+ } else {
+ char* colon=strchr(argv[i],':');
+
+ vncServerHost=argv[i];
+ if(colon) {
+ *colon=0;
+ vncServerPort=atoi(colon+1);
+ } else
+ vncServerPort=0;
+ vncServerPort+=5900;
+ }
+
+ /* TODO:
+ if (strcmp(argv[i], "-tunnel") == 0 || strcmp(argv[i], "-via") == 0) {
+ if (!createTunnel(&argc, argv, i))
+ exit(1);
+ break;
+ }
+ */
+ }
+
+ /* Call the main Xt initialisation function. It parses command-line options,
+ generating appropriate resource specs, and makes a connection to the X
+ display. */
+
+ /* TODO: cmdline args
+ toplevel = XtVaAppInitialize(&appContext, "Vncviewer",
+ cmdLineOptions, numCmdLineOptions,
+ &argc, argv, fallback_resources,
+ XtNborderWidth, 0, NULL);
+
+ dpy = XtDisplay(toplevel);
+ */
+
+ /* Interpret resource specs and process any remaining command-line arguments
+ (i.e. the VNC server name). If the server name isn't specified on the
+ command line, getArgsAndResources() will pop up a dialog box and wait
+ for one to be entered. */
+
+ /*
+ GetArgsAndResources(argc, argv);
+ */
+
+ /* Unless we accepted an incoming connection, make a TCP connection to the
+ given VNC server */
+
+ if (!client->listenSpecified) {
+ if (!ConnectToRFBServer(client,vncServerHost, vncServerPort)) exit(1);
+ }
+
+ /* Initialise the VNC connection, including reading the password */
+
+ if (!InitialiseRFBConnection(client)) exit(1);
+
+ SetFormatAndEncodings(client);
+
+ client->width=client->si.framebufferWidth;
+ client->height=client->si.framebufferHeight;
+ client->MallocFrameBuffer(client);
+ SendFramebufferUpdateRequest(client,0,0,client->width,client->height,FALSE);
+
+ /* Now enter the main loop, processing VNC messages. X events will
+ automatically be processed whenever the VNC connection is idle. */
+
+ while (1) {
+ if (!HandleRFBServerMessage(client))
+ break;
+ }
+
+ return 0;
+}
diff --git a/libvncclient/zlib.c b/libvncclient/zlib.c
new file mode 100644
index 0000000..1fa67b5
--- /dev/null
+++ b/libvncclient/zlib.c
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ */
+
+/*
+ * zlib.c - handle zlib encoding.
+ *
+ * This file shouldn't be compiled directly. It is included multiple times by
+ * rfbproto.c, each time with a different definition of the macro BPP. For
+ * each value of BPP, this file defines a function which handles an zlib
+ * encoded rectangle with BPP bits per pixel.
+ */
+
+#define HandleZlibBPP CONCAT2E(HandleZlib,BPP)
+#define CARDBPP CONCAT2E(uint,BPP,_t)
+
+static Bool
+HandleZlibBPP (rfbClient* client, int rx, int ry, int rw, int rh)
+{
+ rfbZlibHeader hdr;
+ int remaining;
+ int inflateResult;
+ int toRead;
+
+ /* First make sure we have a large enough raw buffer to hold the
+ * decompressed data. In practice, with a fixed BPP, fixed frame
+ * buffer size and the first update containing the entire frame
+ * buffer, this buffer allocation should only happen once, on the
+ * first update.
+ */
+ if ( raw_buffer_size < (( rw * rh ) * ( BPP / 8 ))) {
+
+ if ( raw_buffer != NULL ) {
+
+ free( raw_buffer );
+
+ }
+
+ raw_buffer_size = (( rw * rh ) * ( BPP / 8 ));
+ raw_buffer = (char*) malloc( raw_buffer_size );
+
+ }
+
+ if (!ReadFromRFBServer(client, (char *)&hdr, sz_rfbZlibHeader))
+ return FALSE;
+
+ remaining = Swap32IfLE(hdr.nBytes);
+
+ /* Need to initialize the decompressor state. */
+ decompStream.next_in = ( Bytef * )client->buffer;
+ decompStream.avail_in = 0;
+ decompStream.next_out = ( Bytef * )raw_buffer;
+ decompStream.avail_out = raw_buffer_size;
+ decompStream.data_type = Z_BINARY;
+
+ /* Initialize the decompression stream structures on the first invocation. */
+ if ( decompStreamInited == FALSE ) {
+
+ inflateResult = inflateInit( &decompStream );
+
+ if ( inflateResult != Z_OK ) {
+ fprintf(stderr,
+ "inflateInit returned error: %d, msg: %s\n",
+ inflateResult,
+ decompStream.msg);
+ return FALSE;
+ }
+
+ decompStreamInited = TRUE;
+
+ }
+
+ inflateResult = Z_OK;
+
+ /* Process buffer full of data until no more to process, or
+ * some type of inflater error, or Z_STREAM_END.
+ */
+ while (( remaining > 0 ) &&
+ ( inflateResult == Z_OK )) {
+
+ if ( remaining > BUFFER_SIZE ) {
+ toRead = BUFFER_SIZE;
+ }
+ else {
+ toRead = remaining;
+ }
+
+ /* Fill the buffer, obtaining data from the server. */
+ if (!ReadFromRFBServer(client, client->buffer,toRead))
+ return FALSE;
+
+ decompStream.next_in = ( Bytef * )client->buffer;
+ decompStream.avail_in = toRead;
+
+ /* Need to uncompress buffer full. */
+ inflateResult = inflate( &decompStream, Z_SYNC_FLUSH );
+
+ /* We never supply a dictionary for compression. */
+ if ( inflateResult == Z_NEED_DICT ) {
+ fprintf(stderr,"zlib inflate needs a dictionary!\n");
+ return FALSE;
+ }
+ if ( inflateResult < 0 ) {
+ fprintf(stderr,
+ "zlib inflate returned error: %d, msg: %s\n",
+ inflateResult,
+ decompStream.msg);
+ return FALSE;
+ }
+
+ /* Result buffer allocated to be at least large enough. We should
+ * never run out of space!
+ */
+ if (( decompStream.avail_in > 0 ) &&
+ ( decompStream.avail_out <= 0 )) {
+ fprintf(stderr,"zlib inflate ran out of space!\n");
+ return FALSE;
+ }
+
+ remaining -= toRead;
+
+ } /* while ( remaining > 0 ) */
+
+ if ( inflateResult == Z_OK ) {
+
+ /* Put the uncompressed contents of the update on the screen. */
+ CopyRectangle(client, raw_buffer, rx, ry, rw, rh);
+ }
+ else {
+
+ fprintf(stderr,
+ "zlib inflate returned error: %d, msg: %s\n",
+ inflateResult,
+ decompStream.msg);
+ return FALSE;
+
+ }
+
+ return TRUE;
+}
+
+#undef CARDBPP