From af85e5fee34f7998fe3f06c799b8364a9ddbebce Mon Sep 17 00:00:00 2001
From: dscho <dscho>
Date: Tue, 22 Oct 2002 08:46:27 +0000
Subject: updated to vnc-3.3.4 (ZRLE encoding)

---
 Makefile               |  28 ++++-
 corre.c                |  15 ++-
 cvs_update_anonymously |   3 +-
 httpd.c                |  39 +++----
 main.c                 |   1 -
 rfb.h                  |  10 +-
 rfbproto.h             |  16 +++
 rfbserver.c            |  23 ++++
 stats.c                |  10 +-
 zrle.cc                | 180 ++++++++++++++++++++++++++++
 zrleDecode.h           | 241 ++++++++++++++++++++++++++++++++++++++
 zrleEncode.h           | 310 +++++++++++++++++++++++++++++++++++++++++++++++++
 12 files changed, 836 insertions(+), 40 deletions(-)
 create mode 100644 zrle.cc
 create mode 100644 zrleDecode.h
 create mode 100644 zrleEncode.h

diff --git a/Makefile b/Makefile
index 0c5590a..8d28dfb 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,10 @@
 INCLUDES=-I.
 VNCSERVERLIB=-L. -lvncserver -L/usr/local/lib -lz -ljpeg
 
-# for Solaris
+CXX=g++
 CC=gcc
+
+# for Solaris
 #EXTRALIBS=-lsocket -lnsl -L/usr/X/lib
 
 # for FreeBSD
@@ -18,11 +20,8 @@ FLAG24 = -DALLOW24BPP
 
 OPTFLAGS=-g -Wall -pedantic
 #OPTFLAGS=-O2 -Wall
-CFLAGS=$(OPTFLAGS) $(PTHREADDEF) $(FLAG24) $(INCLUDES) $(EXTRAINCLUDES) -DBACKCHANNEL
 RANLIB=ranlib
 
-LIBS=$(LDFLAGS) $(VNCSERVERLIB) $(PTHREADLIB) $(EXTRALIBS)
-
 # for Mac OS X
 OSX_LIBS = -framework ApplicationServices -framework Carbon -framework IOKit
 
@@ -30,14 +29,31 @@ OSX_LIBS = -framework ApplicationServices -framework Carbon -framework IOKit
 #XLIBS =  -L/usr/X11R6/lib -lXtst -lXext -lX11
 XLIBS =  -L/usr/X11R6/lib -L/usr/lib32 -lXtst -lXext -lX11
 
+ifdef CXX
+
+ZRLE_SRCS=zrle.cc rdr/FdInStream.cxx rdr/FdOutStream.cxx rdr/InStream.cxx \
+	rdr/NullOutStream.cxx rdr/ZlibInStream.cxx rdr/ZlibOutStream.cxx
+ZRLE_OBJS=zrle.o rdr/FdInStream.o rdr/FdOutStream.o rdr/InStream.o \
+	rdr/NullOutStream.o rdr/ZlibInStream.o rdr/ZlibOutStream.o
+ZRLE_DEF=-DHAVE_ZRLE
+
+%.o: %.cxx
+	$(CXX) $(CXXFLAGS) -c -o $@ $<
+
+endif
+
+CFLAGS=$(OPTFLAGS) $(PTHREADDEF) $(FLAG24) $(INCLUDES) $(EXTRAINCLUDES) $(ZRLE_DEF) -DBACKCHANNEL
+CXXFLAGS=$(OPTFLAGS) $(PTHREADDEF) $(FLAG24) $(INCLUDES) $(EXTRAINCLUDES) $(ZRLE_DEF) -DBACKCHANNEL
+LIBS=$(LDFLAGS) $(VNCSERVERLIB) $(PTHREADLIB) $(EXTRALIBS)
+
 SOURCES=main.c rfbserver.c sraRegion.c auth.c sockets.c \
 	stats.c corre.c hextile.c rre.c translate.c cutpaste.c \
 	zlib.c tight.c httpd.c cursor.c font.c \
-	draw.c selbox.c d3des.c vncauth.c cargs.c
+	draw.c selbox.c d3des.c vncauth.c cargs.c $(ZRLE_SRCS)
 OBJS=main.o rfbserver.o sraRegion.o auth.o sockets.o \
 	stats.o corre.o hextile.o rre.o translate.o cutpaste.o \
 	zlib.o tight.o httpd.o cursor.o font.o \
-	draw.o selbox.o d3des.o vncauth.o cargs.o
+	draw.o selbox.o d3des.o vncauth.o cargs.o $(ZRLE_OBJS)
 INSTALLHEADER=rfb.h rfbproto.h sraRegion.h keysym.h
 
 all: example pnmshow storepasswd
diff --git a/corre.c b/corre.c
index d6329a1..d866712 100644
--- a/corre.c
+++ b/corre.c
@@ -6,6 +6,7 @@
  */
 
 /*
+ *  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.
@@ -63,17 +64,15 @@ rfbSendRectEncodingCoRRE(cl, x, y, w, h)
     int x, y, w, h;
 {
     if (h > cl->correMaxHeight) {
-        rfbSendRectEncodingCoRRE(cl, x, y, w, cl->correMaxHeight  );
-        rfbSendRectEncodingCoRRE(cl, x, y + cl->correMaxHeight, w,
-                                 h - cl->correMaxHeight);
-        return FALSE;
+        return (rfbSendRectEncodingCoRRE(cl, x, y, w, cl->correMaxHeight) &&
+		rfbSendRectEncodingCoRRE(cl, x, y + cl->correMaxHeight, w,
+					 h - cl->correMaxHeight));
     }
 
     if (w > cl->correMaxWidth) {
-        rfbSendRectEncodingCoRRE(cl, x, y, cl->correMaxWidth, h);
-        rfbSendRectEncodingCoRRE(cl, x + cl->correMaxWidth, y,
-                                 w - cl->correMaxWidth, h);
-        return FALSE;
+        return (rfbSendRectEncodingCoRRE(cl, x, y, cl->correMaxWidth, h) &&
+		rfbSendRectEncodingCoRRE(cl, x + cl->correMaxWidth, y,
+					 w - cl->correMaxWidth, h));
     }
 
     rfbSendSmallRectEncodingCoRRE(cl, x, y, w, h);
diff --git a/cvs_update_anonymously b/cvs_update_anonymously
index 26f2014..47bfab3 100644
--- a/cvs_update_anonymously
+++ b/cvs_update_anonymously
@@ -1,8 +1,9 @@
 if [ a"$1" = adiff ]; then
   cmd=diff
+  shift
 else
   cmd=update
 fi
 
-cvs -z3 -d :pserver:anonymous@cvs.libvncserver.sf.net:/cvsroot/libvncserver $cmd
+cvs -z3 -d :pserver:anonymous@cvs.libvncserver.sf.net:/cvsroot/libvncserver $cmd "$@"
 
diff --git a/httpd.c b/httpd.c
index 3baedce..5207c72 100644
--- a/httpd.c
+++ b/httpd.c
@@ -3,6 +3,7 @@
  */
 
 /*
+ *  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
@@ -47,30 +48,21 @@
 
 #include "rfb.h"
 
-#define NOT_FOUND_STR "HTTP/1.0 404 Not found\n\n" \
+#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\n\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\n\n"
+#define OK_STR "HTTP/1.0 200 OK\nContent-Type: text/html\r\n\r\n"
 
 static void httpProcessInput();
 static Bool compareAndSkip(char **ptr, const char *str);
 static Bool parseParams(const char *request, char *result, int max_bytes);
 static Bool validateString(char *str);
 
-/*
-int httpPort = 0;
-char *httpDir = NULL;
-
-int httpListenSock = -1;
-int httpSock = -1;
-FILE* httpFP = NULL;
-*/
-
 #define BUF_SIZE 32768
 
 static char buf[BUF_SIZE];
@@ -163,8 +155,10 @@ httpCheckFds(rfbScreenInfoPtr rfbScreen)
 	  rfbLog("Rejected HTTP connection from client %s\n",
 		 inet_ntoa(addr.sin_addr));
 #else
-	if ((rfbScreen->httpFP = fdopen(rfbScreen->httpSock, "r+")) == NULL) {
-	    rfbLogPerror("httpCheckFds: fdopen");
+	flags = fcntl(rfbScreen->httpSock, F_SETFL);
+
+	if (flags < 0 || fcntl(rfbScreen->httpSock, F_SETFL, flags | O_NONBLOCK) == -1) {
+	    rfbLogPerror("httpCheckFds: fcntl");
 #endif
 	    close(rfbScreen->httpSock);
 	    rfbScreen->httpSock = -1;
@@ -187,9 +181,6 @@ httpCheckFds(rfbScreenInfoPtr rfbScreen)
 static void
 httpCloseSock(rfbScreenInfoPtr rfbScreen)
 {
-    fclose(rfbScreen->httpFP);
-    rfbScreen->httpFP = NULL;
-    /*RemoveEnabledDevice(httpSock);*/
     rfbScreen->httpSock = -1;
 }
 
@@ -231,7 +222,15 @@ httpProcessInput(rfbScreenInfoPtr rfbScreen)
 
     /* Read data from the HTTP client until we get a complete request. */
     while (1) {
-	ssize_t got = read (rfbScreen->httpSock, buf + buf_filled,
+	ssize_t got;
+
+        if (buf_filled > sizeof (buf)) {
+	    rfbLog("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) {
@@ -271,8 +270,6 @@ httpProcessInput(rfbScreenInfoPtr rfbScreen)
 	    rfbLog("httpd: client asked for CONNECT\n");
 	    WriteExact(&cl,PROXY_OK_STR,strlen(PROXY_OK_STR));
 	    rfbNewClientConnection(rfbScreen,rfbScreen->httpSock);
-	    // don't fclose(rfbScreen->httpFP), because this would kill the connection
-	    rfbScreen->httpFP = NULL;
 	    rfbScreen->httpSock = -1;
 	    return;
 	}
@@ -281,8 +278,6 @@ httpProcessInput(rfbScreenInfoPtr rfbScreen)
 	    rfbLog("httpd: client asked for /proxied.connection\n");
 	    WriteExact(&cl,PROXY_OK_STR,strlen(PROXY_OK_STR));
 	    rfbNewClientConnection(rfbScreen,rfbScreen->httpSock);
-	    // don't fclose(rfbScreen->httpFP), because this would kill the connection
-	    rfbScreen->httpFP = NULL;
 	    rfbScreen->httpSock = -1;
 	    return;
 	}	   
diff --git a/main.c b/main.c
index adc231d..044e3c6 100644
--- a/main.c
+++ b/main.c
@@ -549,7 +549,6 @@ rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv,
    rfbScreen->httpDir=NULL;
    rfbScreen->httpListenSock=-1;
    rfbScreen->httpSock=-1;
-   rfbScreen->httpFP=NULL;
 
    rfbScreen->desktopName = "LibVNCServer";
    rfbScreen->rfbAlwaysShared = FALSE;
diff --git a/rfb.h b/rfb.h
index fcf7b09..7e096f0 100644
--- a/rfb.h
+++ b/rfb.h
@@ -6,6 +6,7 @@
  */
 
 /*
+ *  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.
@@ -297,7 +298,6 @@ typedef struct _rfbScreenInfo
     char* httpDir;
     SOCKET httpListenSock;
     SOCKET httpSock;
-    FILE* httpFP;
 
     PasswordCheckProcPtr passwordCheck;
     void* rfbAuthPasswdData;
@@ -409,6 +409,9 @@ typedef struct _rfbClientRec {
     Bool useCopyRect;
     int preferredEncoding;
     int correMaxWidth, correMaxHeight;
+#ifdef HAVE_ZRLE
+    void* zrleData;
+#endif
 
     /* The following member is only used during VNC authentication */
     CARD8 authChallenge[CHALLENGESIZE];
@@ -716,6 +719,11 @@ extern void rfbSetCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr c,Bool freeOld)
 /* cursor handling for the pointer */
 extern void defaultPtrAddEvent(int buttonMask,int x,int y,rfbClientPtr cl);
 
+/* zrle.c */
+
+extern Bool rfbSendRectEncodingZRLE(rfbClientPtr cl, int x, int y, int w,int h);
+extern void FreeZrleData(rfbClientPtr cl);
+
 /* stats.c */
 
 extern void rfbResetStats(rfbClientPtr cl);
diff --git a/rfbproto.h b/rfbproto.h
index f839019..e84f6a0 100644
--- a/rfbproto.h
+++ b/rfbproto.h
@@ -302,6 +302,7 @@ typedef struct {
 #ifdef BACKCHANNEL
 #define rfbEncodingBackChannel 15
 #endif
+#define rfbEncodingZRLE 16
 
 /*
  * Special encoding numbers:
@@ -559,6 +560,21 @@ typedef struct {
  */
 
 
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * ZRLE - encoding combining Zlib compression, tiling, palettisation and
+ * run-length encoding.
+ */
+
+typedef struct {
+    CARD32 length;
+} rfbZRLEHeader;
+
+#define sz_rfbZRLEHeader 4
+
+#define rfbZRLETileWidth 64
+#define rfbZRLETileHeight 64
+
+
 /*-----------------------------------------------------------------------------
  * SetColourMapEntries - these messages are only sent if the pixel
  * format uses a "colour map" (i.e. trueColour false) and the client has not
diff --git a/rfbserver.c b/rfbserver.c
index 7ba360e..51769be 100644
--- a/rfbserver.c
+++ b/rfbserver.c
@@ -3,6 +3,7 @@
  */
 
 /*
+ *  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.
@@ -257,6 +258,7 @@ rfbNewTCPOrUDPClient(rfbScreen,sock,isUDP)
       cl->preferredEncoding = rfbEncodingRaw;
       cl->correMaxWidth = 48;
       cl->correMaxHeight = 48;
+      cl->zrleData = 0;
 
       cl->copyRegion = sraRgnCreate();
       cl->copyDX = 0;
@@ -372,6 +374,10 @@ rfbClientConnectionGone(cl)
     if (cl->next)
         cl->next->prev = cl->prev;
 
+#ifdef HAVE_ZRLE
+    FreeZrleData(cl);
+#endif
+
 #ifdef HAVE_PTHREADS
     LOCK(cl->refCountMutex);
     if(cl->refCount) {
@@ -786,6 +792,15 @@ rfbProcessClientNormalMessage(cl)
 		    cl->enableBackChannel = TRUE;
 		}
 		break;
+#endif
+#ifdef HAVE_ZRLE
+           case rfbEncodingZRLE:
+               if (cl->preferredEncoding == -1) {
+                   cl->preferredEncoding = enc;
+                   rfbLog("Using ZRLE encoding for client %s\n",
+                          cl->host);
+               }
+               break;
 #endif
             default:
 		if ( enc >= (CARD32)rfbEncodingCompressLevel0 &&
@@ -1187,6 +1202,14 @@ rfbSendFramebufferUpdate(cl, givenUpdateRegion)
 		return FALSE;
 	    }
 	    break;
+#ifdef HAVE_ZRLE
+       case rfbEncodingZRLE:
+           if (!rfbSendRectEncodingZRLE(cl, x, y, w, h)) {
+	       sraRgnDestroy(updateRegion);
+               return FALSE;
+           }
+           break;
+#endif
         }
     }
     sraRgnReleaseIterator(i);
diff --git a/stats.c b/stats.c
index a0b5ccc..a941741 100644
--- a/stats.c
+++ b/stats.c
@@ -3,6 +3,7 @@
  */
 
 /*
+ *  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.
@@ -29,7 +30,14 @@
 
 static const char* encNames[] = {
     "raw", "copyRect", "RRE", "[encoding 3]", "CoRRE", "hextile",
-    "zlib", "tight", "[encoding 8]", "[encoding 9]"
+    "zlib", "tight", "[encoding 8]", "[encoding 9]", "[encoding 10]",
+    "[encoding 11]", "[encoding 12]", "[encoding 13]", "[encoding 14]",
+#ifdef BACKCHANNEL
+    "BackChannel",
+#else
+    "[encoding 15]",
+#endif
+    "ZRLE", "[encoding 17]", "[encoding 18]", "[encoding 19]", "[encoding 20]"
 };
 
 
diff --git a/zrle.cc b/zrle.cc
new file mode 100644
index 0000000..7839677
--- /dev/null
+++ b/zrle.cc
@@ -0,0 +1,180 @@
+//
+// 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.
+//
+
+//
+// zrle.cc
+//
+// Routines to implement Zlib Run-length Encoding (ZRLE).
+//
+
+#include <stdio.h>
+extern "C" {
+#include "rfb.h"
+}
+#include <rdr/MemOutStream.h>
+#include <rdr/ZlibOutStream.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 <zrleEncode.h>
+#undef BPP
+#define BPP 16
+#include <zrleEncode.h>
+#undef BPP
+#define BPP 32
+#include <zrleEncode.h>
+#define CPIXEL 24A
+#include <zrleEncode.h>
+#undef CPIXEL
+#define CPIXEL 24B
+#include <zrleEncode.h>
+#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];
+
+static rdr::MemOutStream mos;
+
+
+/*
+ * rfbSendRectEncodingZRLE - send a given rectangle using ZRLE encoding.
+ */
+
+
+Bool rfbSendRectEncodingZRLE(rfbClientPtr cl, int x, int y, int w, int h)
+{
+  if (!cl->zrleData) cl->zrleData = new rdr::ZlibOutStream;
+  rdr::ZlibOutStream* zos = (rdr::ZlibOutStream*)cl->zrleData;
+  mos.clear();
+
+  switch (cl->format.bitsPerPixel) {
+
+  case 8:
+    zrleEncode8( x, y, w, h, &mos, zos, zrleBeforeBuf, cl);
+    break;
+
+  case 16:
+    zrleEncode16(x, y, w, h, &mos, zos, zrleBeforeBuf, cl);
+    break;
+
+  case 32:
+    bool 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));
+
+    bool 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, &mos, zos, zrleBeforeBuf, cl);
+    }
+    else if ((fitsInLS3Bytes && cl->format.bigEndian) ||
+             (fitsInMS3Bytes && !cl->format.bigEndian))
+    {
+      zrleEncode24B(x, y, w, h, &mos, zos, zrleBeforeBuf, cl);
+    }
+    else
+    {
+      zrleEncode32(x, y, w, h, &mos, zos, zrleBeforeBuf, cl);
+    }
+    break;
+  }
+
+  cl->rfbRectanglesSent[rfbEncodingZRLE]++;
+  cl->rfbBytesSent[rfbEncodingZRLE] += (sz_rfbFramebufferUpdateRectHeader
+                                        + sz_rfbZRLEHeader + mos.length());
+
+  if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbZRLEHeader
+      > UPDATE_BUF_SIZE)
+    {
+      if (!rfbSendUpdateBuf(cl))
+        return FALSE;
+    }
+
+  rfbFramebufferUpdateRectHeader rect;
+  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;
+
+  rfbZRLEHeader hdr;
+
+  hdr.length = Swap32IfLE(mos.length());
+
+  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 (int i = 0; i < mos.length();) {
+
+    int bytesToCopy = UPDATE_BUF_SIZE - cl->ublen;
+
+    if (i + bytesToCopy > mos.length()) {
+      bytesToCopy = mos.length() - i;
+    }
+
+    memcpy(cl->updateBuf+cl->ublen, (CARD8*)mos.data() + i, bytesToCopy);
+
+    cl->ublen += bytesToCopy;
+    i += bytesToCopy;
+
+    if (cl->ublen == UPDATE_BUF_SIZE) {
+      if (!rfbSendUpdateBuf(cl))
+        return FALSE;
+    }
+  }
+
+  return TRUE;
+}
+
+
+void FreeZrleData(rfbClientPtr cl)
+{
+  delete (rdr::ZlibOutStream*)cl->zrleData;
+}
+
diff --git a/zrleDecode.h b/zrleDecode.h
new file mode 100644
index 0000000..8ddb5c8
--- /dev/null
+++ b/zrleDecode.h
@@ -0,0 +1,241 @@
+//
+// 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.
+
+//
+// zrleDecode.h - zrle decoding function.
+//
+// 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.
+// FILL_RECT
+// IMAGE_RECT
+
+#include <rdr/ZlibInStream.h>
+#include <rdr/InStream.h>
+#include <assert.h>
+
+using namespace rdr;
+
+/* __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(rdr::U,BPP)
+#define READ_PIXEL __RFB_CONCAT2E(readOpaque,CPIXEL)
+#define ZRLE_DECODE_BPP __RFB_CONCAT2E(zrleDecode,CPIXEL)
+#else
+#define PIXEL_T __RFB_CONCAT2E(rdr::U,BPP)
+#define READ_PIXEL __RFB_CONCAT2E(readOpaque,BPP)
+#define ZRLE_DECODE_BPP __RFB_CONCAT2E(zrleDecode,BPP)
+#endif
+
+void ZRLE_DECODE_BPP (int x, int y, int w, int h, rdr::InStream* is,
+                      rdr::ZlibInStream* zis, PIXEL_T* buf)
+{
+  int length = is->readU32();
+  zis->setUnderlying(is, length);
+
+  for (int ty = y; ty < y+h; ty += rfbZRLETileHeight) {
+    int th = rfbZRLETileHeight;
+    if (th > y+h-ty) th = y+h-ty;
+    for (int tx = x; tx < x+w; tx += rfbZRLETileWidth) {
+      int tw = rfbZRLETileWidth;
+      if (tw > x+w-tx) tw = x+w-tx;
+
+      int mode = zis->readU8();
+      bool rle = mode & 128;
+      int palSize = mode & 127;
+      PIXEL_T palette[128];
+
+      //        fprintf(stderr,"rle %d palSize %d\n",rle,palSize);
+
+      for (int i = 0; i < palSize; i++) {
+        palette[i] = zis->READ_PIXEL();
+      }
+
+      if (palSize == 1) {
+        PIXEL_T pix = palette[0];
+        FILL_RECT(tx,ty,tw,th,pix);
+        continue;
+      }
+
+      if (!rle) {
+        if (palSize == 0) {
+
+          // raw
+
+#ifdef CPIXEL
+          for (PIXEL_T* ptr = buf; ptr < buf+tw*th; ptr++) {
+            *ptr = zis->READ_PIXEL();
+          }
+#else
+          zis->readBytes(buf, tw * th * (BPP / 8));
+#endif
+
+        } else {
+
+          // packed pixels
+          int bppp = ((palSize > 16) ? 8 :
+                      ((palSize > 4) ? 4 : ((palSize > 2) ? 2 : 1)));
+
+          PIXEL_T* ptr = buf;
+
+          for (int i = 0; i < th; i++) {
+            PIXEL_T* eol = ptr + tw;
+            U8 byte = 0;
+            U8 nbits = 0;
+
+            while (ptr < eol) {
+              if (nbits == 0) {
+                byte = zis->readU8();
+                nbits = 8;
+              }
+              nbits -= bppp;
+              U8 index = (byte >> nbits) & ((1 << bppp) - 1) & 127;
+              *ptr++ = palette[index];
+            }
+          }
+        }
+
+#ifdef FAVOUR_FILL_RECT
+       //fprintf(stderr,"copying data to screen %dx%d at %d,%d\n",tw,th,tx,ty);
+        IMAGE_RECT(tx,ty,tw,th,buf);
+#endif
+
+      } else {
+
+        if (palSize == 0) {
+
+          // plain RLE
+
+          PIXEL_T* ptr = buf;
+          PIXEL_T* end = ptr + th * tw;	    
+          while (ptr < end) {
+            PIXEL_T pix = zis->READ_PIXEL();
+            int len = 1;
+            int b;
+            do {
+              b = zis->readU8();
+              len += b;
+            } while (b == 255);
+
+            assert(len <= end - ptr);
+
+#ifdef FAVOUR_FILL_RECT
+            int i = ptr - buf;
+            ptr += len;
+
+            int runX = i % tw;
+            int runY = i / tw;
+
+            if (runX + len > tw) {
+              if (runX != 0) {
+                FILL_RECT(tx+runX, ty+runY, tw-runX, 1, pix);
+                len -= tw-runX;
+                runX = 0;
+                runY++;
+              }
+
+              if (len > tw) {
+                FILL_RECT(tx, ty+runY, tw, len/tw, pix);
+                runY += len / tw;
+                len = len % tw;
+              }
+            }
+
+            if (len != 0) {
+              FILL_RECT(tx+runX, ty+runY, len, 1, pix);
+            }
+#else
+            while (len-- > 0) *ptr++ = pix;
+#endif
+
+          }
+        } else {
+
+          // palette RLE
+
+          PIXEL_T* ptr = buf;
+          PIXEL_T* end = ptr + th * tw;
+          while (ptr < end) {
+            int index = zis->readU8();
+            int len = 1;
+            if (index & 128) {
+              int b;
+              do {
+                b = zis->readU8();
+                len += b;
+              } while (b == 255);
+
+              assert(len <= end - ptr);
+            }
+
+            index &= 127;
+
+            PIXEL_T pix = palette[index];
+
+#ifdef FAVOUR_FILL_RECT
+            int i = ptr - buf;
+            ptr += len;
+
+            int runX = i % tw;
+            int runY = i / tw;
+
+            if (runX + len > tw) {
+              if (runX != 0) {
+                FILL_RECT(tx+runX, ty+runY, tw-runX, 1, pix);
+                len -= tw-runX;
+                runX = 0;
+                runY++;
+              }
+
+              if (len > tw) {
+                FILL_RECT(tx, ty+runY, tw, len/tw, pix);
+                runY += len / tw;
+                len = len % tw;
+              }
+            }
+
+            if (len != 0) {
+              FILL_RECT(tx+runX, ty+runY, len, 1, pix);
+            }
+#else
+            while (len-- > 0) *ptr++ = pix;
+#endif
+          }
+        }
+      }
+
+#ifndef FAVOUR_FILL_RECT
+      //fprintf(stderr,"copying data to screen %dx%d at %d,%d\n",tw,th,tx,ty);
+      IMAGE_RECT(tx,ty,tw,th,buf);
+#endif
+    }
+  }
+
+  zis->reset();
+}
+
+#undef ZRLE_DECODE_BPP
+#undef READ_PIXEL
+#undef PIXEL_T
diff --git a/zrleEncode.h b/zrleEncode.h
new file mode 100644
index 0000000..6d58a5a
--- /dev/null
+++ b/zrleEncode.h
@@ -0,0 +1,310 @@
+//
+// 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.
+
+//
+// zrleEncode.h - zrle encoding function.
+//
+// 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 <rdr/OutStream.h>
+#include <assert.h>
+
+using namespace rdr;
+
+/* __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(rdr::U,BPP)
+#define WRITE_PIXEL __RFB_CONCAT2E(writeOpaque,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(rdr::U,BPP)
+#define WRITE_PIXEL __RFB_CONCAT2E(writeOpaque,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
+};
+
+// The PaletteHelper class helps us build up the palette from pixel data by
+// storing a reverse index using a simple hash-table
+
+class PaletteHelper {
+public:
+  enum { MAX_SIZE = 127 };
+
+  PaletteHelper()
+  {
+    memset(index, 255, sizeof(index));
+    size = 0;
+  }
+
+  inline int hash(rdr::U32 pix)
+  {
+    return (pix ^ (pix >> 17)) & 4095;
+  }
+
+  inline void insert(rdr::U32 pix)
+  {
+    if (size < MAX_SIZE) {
+      int i = hash(pix);
+      while (index[i] != 255 && key[i] != pix)
+        i++;
+      if (index[i] != 255) return;
+
+      index[i] = size;
+      key[i] = pix;
+      palette[size] = pix;
+    }
+    size++;
+  }
+
+  inline int lookup(rdr::U32 pix)
+  {
+    assert(size <= MAX_SIZE);
+    int i = hash(pix);
+    while (index[i] != 255 && key[i] != pix)
+      i++;
+    if (index[i] != 255) return index[i];
+    return -1;
+  }
+
+  rdr::U32 palette[MAX_SIZE];
+  rdr::U8 index[4096+MAX_SIZE];
+  rdr::U32 key[4096+MAX_SIZE];
+  int size;
+};
+#endif
+
+void ZRLE_ENCODE_TILE (PIXEL_T* data, int w, int h, rdr::OutStream* os);
+
+void ZRLE_ENCODE (int x, int y, int w, int h, rdr::OutStream* os,
+                  rdr::ZlibOutStream* zos, void* buf
+                  EXTRA_ARGS
+                  )
+{
+  zos->setUnderlying(os);
+
+  for (int ty = y; ty < y+h; ty += rfbZRLETileHeight) {
+    int th = rfbZRLETileHeight;
+    if (th > y+h-ty) th = y+h-ty;
+    for (int 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, zos);
+    }
+  }
+  zos->flush();
+}
+
+
+void ZRLE_ENCODE_TILE (PIXEL_T* data, int w, int h, rdr::OutStream* os)
+{
+  // First find the palette and the number of runs
+
+  PaletteHelper ph;
+
+  int runs = 0;
+  int singlePixels = 0;
+
+  PIXEL_T* ptr = data;
+  PIXEL_T* end = ptr + h * w;
+  *end = ~*(end-1); // one past the end is different so the while loop ends
+
+  while (ptr < end) {
+    PIXEL_T pix = *ptr;
+    if (*++ptr != pix) {
+      singlePixels++;
+    } else {
+      while (*++ptr == pix) ;
+      runs++;
+    }
+    ph.insert(pix);
+  }
+
+  //fprintf(stderr,"runs %d, single pixels %d, paletteSize %d\n",
+  //        runs, singlePixels, ph.size);
+
+  // Solid tile is a special case
+
+  if (ph.size == 1) {
+    os->writeU8(1);
+    os->WRITE_PIXEL(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...
+
+  bool useRle = false;
+  bool usePalette = false;
+
+  int estimatedBytes = w * h * (BPPOUT/8); // start assuming raw
+
+  int 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;
+
+  os->writeU8((useRle ? 128 : 0) | ph.size);
+
+  for (int i = 0; i < ph.size; i++) {
+    os->WRITE_PIXEL(ph.palette[i]);
+  }
+
+  if (useRle) {
+
+    PIXEL_T* ptr = data;
+    PIXEL_T* end = ptr + w * h;
+    PIXEL_T* runStart;
+    PIXEL_T pix;
+    while (ptr < end) {
+      runStart = ptr;
+      pix = *ptr++;
+      while (*ptr == pix && ptr < end)
+        ptr++;
+      int len = ptr - runStart;
+      if (len <= 2 && usePalette) {
+        int index = ph.lookup(pix);
+        if (len == 2)
+          os->writeU8(index);
+        os->writeU8(index);
+        continue;
+      }
+      if (usePalette) {
+        int index = ph.lookup(pix);
+        os->writeU8(index | 128);
+      } else {
+        os->WRITE_PIXEL(pix);
+      }
+      len -= 1;
+      while (len >= 255) {
+        os->writeU8(255);
+        len -= 255;
+      }
+      os->writeU8(len);
+    }
+
+  } else {
+
+    // no RLE
+
+    if (usePalette) {
+
+      // packed pixels
+
+      assert (ph.size < 17);
+
+      int bppp = bitsPerPackedPixel[ph.size-1];
+
+      PIXEL_T* ptr = data;
+
+      for (int i = 0; i < h; i++) {
+        U8 nbits = 0;
+        U8 byte = 0;
+
+        PIXEL_T* eol = ptr + w;
+
+        while (ptr < eol) {
+          PIXEL_T pix = *ptr++;
+          U8 index = ph.lookup(pix);
+          byte = (byte << bppp) | index;
+          nbits += bppp;
+          if (nbits >= 8) {
+            os->writeU8(byte);
+            nbits = 0;
+          }
+        }
+        if (nbits > 0) {
+          byte <<= 8 - nbits;
+          os->writeU8(byte);
+        }
+      }
+    } else {
+
+      // raw
+
+#ifdef CPIXEL
+      for (PIXEL_T* ptr = data; ptr < data+w*h; ptr++) {
+        os->WRITE_PIXEL(*ptr);
+      }
+#else
+      os->writeBytes(data, w*h*(BPP/8));
+#endif
+    }
+  }
+}
+
+#undef PIXEL_T
+#undef WRITE_PIXEL
+#undef ZRLE_ENCODE
+#undef ZRLE_ENCODE_TILE
+#undef BPPOUT
-- 
cgit v1.2.3

