Index: vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/Imakefile
diff -u vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/Imakefile:1.1.1.1 vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/Imakefile:1.2
--- vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/Imakefile:1.1.1.1	Sun Jun 11 20:00:52 2000
+++ vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/Imakefile	Thu Aug 17 17:35:31 2000
@@ -3,11 +3,11 @@
 
 SRCS = init.c sockets.c kbdptr.c cmap.c draw.c cutpaste.c \
        dispcur.c sprite.c rfbserver.c translate.c httpd.c auth.c \
-       rre.c corre.c stats.c hextile.c
+       rre.c corre.c stats.c hextile.c tight.c
 
 OBJS = init.o sockets.o kbdptr.o cmap.o draw.o cutpaste.o \
        dispcur.o sprite.o rfbserver.o translate.o httpd.o auth.o \
-       rre.o corre.o stats.o hextile.o
+       rre.o corre.o stats.o hextile.o tight.o
 
 #include <vnclibs.def>
 INCLUDES = -I. -I$(XBUILDINCDIR) -I$(FONTINCSRC) -I$(XINCLUDESRC) \
Index: vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/hextile.c
diff -u vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/hextile.c:1.1.1.1 vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/hextile.c:1.2
--- vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/hextile.c:1.1.1.1	Sun Jun 11 20:00:52 2000
+++ vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/hextile.c	Wed Oct  4 05:25:46 2000
@@ -120,7 +120,7 @@
 	    if (ry+rh - y < 16)						      \
 		h = ry+rh - y;						      \
 									      \
-	    if ((ublen + 1 + 16*16*(bpp/8)) > UPDATE_BUF_SIZE) {	      \
+	    if ((ublen + 1 + (2 + 16*16)*(bpp/8)) > UPDATE_BUF_SIZE) {	      \
 		if (!rfbSendUpdateBuf(cl))				      \
 		    return FALSE;					      \
 	    }								      \
Index: vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/init.c
diff -u vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/init.c:1.1.1.1 vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/init.c:1.2
--- vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/init.c:1.1.1.1	Sun Jun 11 20:00:52 2000
+++ vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/init.c	Mon Sep 25 19:59:16 2000
@@ -259,6 +259,11 @@
 	return 1;
     }
 
+    if (strcmp(argv[i], "-lazytight") == 0) {
+	rfbTightDisableGradient = TRUE;
+	return 1;
+    }
+
     if (strcmp(argv[i], "-desktop") == 0) {	/* -desktop desktop-name */
 	if (i + 1 >= argc) UseMsg();
 	desktopName = argv[i+1];
@@ -744,6 +749,8 @@
     ErrorF("-deferupdate time      time in ms to defer updates "
 							     "(default 40)\n");
     ErrorF("-economictranslate     less memory-hungry translation\n");
+    ErrorF("-lazytight             disable \"gradient\" filter in tight "
+                                                                "encoding\n");
     ErrorF("-desktop name          VNC desktop name (default x11)\n");
     ErrorF("-alwaysshared          always treat new clients as shared\n");
     ErrorF("-nevershared           never treat new clients as shared\n");
Index: vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/rfb.h
diff -u vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/rfb.h:1.1.1.1 vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/rfb.h:1.7
--- vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/rfb.h:1.1.1.1	Sun Jun 11 20:00:52 2000
+++ vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/rfb.h	Mon Sep 25 19:59:17 2000
@@ -27,6 +27,7 @@
 #include "osdep.h"
 #include <rfbproto.h>
 #include <vncauth.h>
+#include <zlib.h>
 
 #define MAX_ENCODINGS 10
 
@@ -207,6 +208,11 @@
     int rfbKeyEventsRcvd;
     int rfbPointerEventsRcvd;
 
+    /* tight encoding -- preserve zlib streams' state for each client */
+
+    z_stream zsStruct[4];
+    Bool zsActive[4];
+
     struct rfbClientRec *next;
 
 } rfbClientRec, *rfbClientPtr;
@@ -440,6 +446,15 @@
 
 extern Bool rfbSendRectEncodingHextile(rfbClientPtr cl, int x, int y, int w,
 				       int h);
+
+
+/* tight.c */
+
+#define TIGHT_MAX_RECT_SIZE  65536
+
+extern Bool rfbTightDisableGradient;
+
+extern Bool rfbSendRectEncodingTight(rfbClientPtr cl, int x,int y,int w,int h);
 
 
 /* stats.c */
Index: vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/rfbserver.c
diff -u vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/rfbserver.c:1.1.1.1 vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/rfbserver.c:1.4
--- vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/rfbserver.c:1.1.1.1	Sun Jun 11 20:00:52 2000
+++ vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/rfbserver.c	Wed Aug 30 18:15:38 2000
@@ -118,6 +118,7 @@
     BoxRec box;
     struct sockaddr_in addr;
     int addrlen = sizeof(struct sockaddr_in);
+    int i;
 
     if (rfbClientHead == NULL) {
 	/* no other clients - make sure we don't think any keys are pressed */
@@ -163,6 +164,9 @@
     cl->translateFn = rfbTranslateNone;
     cl->translateLookupTable = NULL;
 
+    for (i = 0; i < 4; i++)
+        cl->zsActive[i] = FALSE;
+
     cl->next = rfbClientHead;
     rfbClientHead = cl;
 
@@ -191,6 +195,7 @@
     int sock;
 {
     rfbClientPtr cl, prev;
+    int i;
 
     for (prev = NULL, cl = rfbClientHead; cl; prev = cl, cl = cl->next) {
 	if (sock == cl->sock)
@@ -205,6 +210,11 @@
     rfbLog("Client %s gone\n",cl->host);
     free(cl->host);
 
+    for (i = 0; i < 4; i++) {
+        if (cl->zsActive[i])
+            deflateEnd(&cl->zsStruct[i]);
+    }
+
     if (pointerClient == cl)
 	pointerClient = NULL;
 
@@ -554,6 +564,13 @@
 			   cl->host);
 		}
 		break;
+	    case rfbEncodingTight:
+		if (cl->preferredEncoding == -1) {
+		    cl->preferredEncoding = enc;
+		    rfbLog("Using tight encoding for client %s\n",
+			   cl->host);
+		}
+		break;
 	    default:
 		rfbLog("rfbProcessClientNormalMessage: ignoring unknown "
 		       "encoding type %d\n", (int)enc);
@@ -827,6 +844,23 @@
 	    nUpdateRegionRects += (((w-1) / cl->correMaxWidth + 1)
 				     * ((h-1) / cl->correMaxHeight + 1));
 	}
+    } else if (cl->preferredEncoding == rfbEncodingTight) {
+	nUpdateRegionRects = 0;
+
+	for (i = 0; i < REGION_NUM_RECTS(&updateRegion); i++) {
+	    int x = REGION_RECTS(&updateRegion)[i].x1;
+	    int y = REGION_RECTS(&updateRegion)[i].y1;
+	    int w = REGION_RECTS(&updateRegion)[i].x2 - x;
+	    int h = REGION_RECTS(&updateRegion)[i].y2 - y;
+            if (w > 2048 || w * h > TIGHT_MAX_RECT_SIZE) {
+                int subrectMaxWidth = (w > 2048) ? 2048 : w;
+                int subrectMaxHeight = TIGHT_MAX_RECT_SIZE / subrectMaxWidth;
+                nUpdateRegionRects += (((w - 1) / 2048 + 1)
+                                       * ((h - 1) / subrectMaxHeight + 1));
+            } else {
+                nUpdateRegionRects++;
+            }
+	}
     } else {
 	nUpdateRegionRects = REGION_NUM_RECTS(&updateRegion);
     }
@@ -876,6 +910,12 @@
 	    break;
 	case rfbEncodingHextile:
 	    if (!rfbSendRectEncodingHextile(cl, x, y, w, h)) {
+		REGION_UNINIT(pScreen,&updateRegion);
+		return FALSE;
+	    }
+	    break;
+	case rfbEncodingTight:
+	    if (!rfbSendRectEncodingTight(cl, x, y, w, h)) {
 		REGION_UNINIT(pScreen,&updateRegion);
 		return FALSE;
 	    }
Index: vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/stats.c
diff -u vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/stats.c:1.1.1.1 vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/stats.c:1.2
--- vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/stats.c:1.1.1.1	Sun Jun 11 20:00:52 2000
+++ vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/stats.c	Thu Aug 17 23:51:17 2000
@@ -27,7 +27,7 @@
 
 static char* encNames[] = {
     "raw", "copyRect", "RRE", "[encoding 3]", "CoRRE", "hextile",
-    "[encoding 6]", "[encoding 7]", "[encoding 8]", "[encoding 9]"
+    "[encoding 6]", "tight", "[encoding 8]", "[encoding 9]"
 };
 
 
Index: vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/tight.c
diff -u /dev/null vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/tight.c:1.16
--- /dev/null	Thu Oct  5 17:44:09 2000
+++ vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/tight.c	Sat Sep 30 00:03:03 2000
@@ -0,0 +1,1070 @@
+/*
+ * tight.c
+ *
+ * Routines to implement Tight Encoding
+ */
+
+/*
+ *  Copyright (C) 2000 Const Kaplinsky.  All Rights Reserved.
+ *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+ *
+ *  This is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This software is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this software; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ *  USA.
+ */
+
+#include <stdio.h>
+#include "rfb.h"
+
+
+/* Note: The following constant should not be changed. */
+#define TIGHT_MIN_TO_COMPRESS 12
+
+/* May be set to TRUE with "-lazytight" Xvnc option. */
+Bool rfbTightDisableGradient = FALSE;
+
+
+typedef struct COLOR_LIST_s {
+    struct COLOR_LIST_s *next;
+    int idx;
+    CARD32 rgb;
+} COLOR_LIST;
+
+typedef struct PALETTE_ENTRY_s {
+    COLOR_LIST *listNode;
+    int numPixels;
+} PALETTE_ENTRY;
+
+typedef struct PALETTE_s {
+    PALETTE_ENTRY entry[256];
+    COLOR_LIST *hash[256];
+    COLOR_LIST list[256];
+} PALETTE;
+
+typedef struct PALETTE8_s {
+    CARD8 pixelValue[2];
+    CARD8 colorIdx[256];
+} PALETTE8;
+
+
+static BOOL usePixelFormat24;
+
+static int paletteMaxColors;
+static int paletteNumColors;
+static PALETTE palette;
+static PALETTE8 palette8;
+
+static int tightBeforeBufSize = 0;
+static char *tightBeforeBuf = NULL;
+
+static int tightAfterBufSize = 0;
+static char *tightAfterBuf = NULL;
+
+static int *prevRowBuf = NULL;
+
+
+static int SendSubrect(rfbClientPtr cl, int x, int y, int w, int h);
+static BOOL SendSolidRect(rfbClientPtr cl, int w, int h);
+static BOOL SendIndexedRect(rfbClientPtr cl, int w, int h);
+static BOOL SendFullColorRect(rfbClientPtr cl, int w, int h);
+static BOOL SendGradientRect(rfbClientPtr cl, int w, int h);
+static BOOL CompressData(rfbClientPtr cl, int streamId, int dataLen);
+
+static void FillPalette8(int w, int h);
+static void FillPalette16(int w, int h);
+static void FillPalette32(int w, int h);
+
+static void PaletteReset(void);
+static int PaletteFind(CARD32 rgb);
+static int PaletteInsert(CARD32 rgb, int numPixels);
+
+static void Pack24(char *buf, rfbPixelFormat *fmt, int count);
+
+static void EncodeIndexedRect8(CARD8 *buf, int w, int h);
+static void EncodeIndexedRect16(CARD8 *buf, int w, int h);
+static void EncodeIndexedRect32(CARD8 *buf, int w, int h);
+
+static void FilterGradient24(char *buf, rfbPixelFormat *fmt, int w, int h);
+static void FilterGradient16(CARD16 *buf, rfbPixelFormat *fmt, int w, int h);
+static void FilterGradient32(CARD32 *buf, rfbPixelFormat *fmt, int w, int h);
+
+static int DetectStillImage (rfbPixelFormat *fmt, int w, int h);
+static int DetectStillImage24 (rfbPixelFormat *fmt, int w, int h);
+static int DetectStillImage16 (rfbPixelFormat *fmt, int w, int h);
+static int DetectStillImage32 (rfbPixelFormat *fmt, int w, int h);
+
+/*
+ * Tight encoding implementation.
+ */
+
+Bool
+rfbSendRectEncodingTight(cl, x, y, w, h)
+    rfbClientPtr cl;
+    int x, y, w, h;
+{
+    int maxBeforeSize, maxAfterSize;
+    int subrectMaxWidth, subrectMaxHeight;
+    int dx, dy;
+    int rw, rh;
+
+    if ( cl->format.depth == 24 && cl->format.redMax == 0xFF &&
+         cl->format.greenMax == 0xFF && cl->format.blueMax == 0xFF ) {
+        usePixelFormat24 = TRUE;
+    } else {
+        usePixelFormat24 = FALSE;
+    }
+
+    maxBeforeSize = TIGHT_MAX_RECT_SIZE * (cl->format.bitsPerPixel / 8);
+    maxAfterSize = maxBeforeSize + (maxBeforeSize + 99) / 100 + 12;
+
+    if (tightBeforeBufSize < maxBeforeSize) {
+	tightBeforeBufSize = maxBeforeSize;
+	if (tightBeforeBuf == NULL)
+	    tightBeforeBuf = (char *)xalloc(tightBeforeBufSize);
+	else
+	    tightBeforeBuf = (char *)xrealloc(tightBeforeBuf,
+                                              tightBeforeBufSize);
+    }
+
+    if (tightAfterBufSize < maxAfterSize) {
+	tightAfterBufSize = maxAfterSize;
+	if (tightAfterBuf == NULL)
+	    tightAfterBuf = (char *)xalloc(tightAfterBufSize);
+	else
+	    tightAfterBuf = (char *)xrealloc(tightAfterBuf,
+                                             tightAfterBufSize);
+    }
+
+    if (w > 2048 || w * h > TIGHT_MAX_RECT_SIZE) {
+        subrectMaxWidth = (w > 2048) ? 2048 : w;
+        subrectMaxHeight = TIGHT_MAX_RECT_SIZE / subrectMaxWidth;
+
+        for (dy = 0; dy < h; dy += subrectMaxHeight) {
+            for (dx = 0; dx < w; dx += 2048) {
+                rw = (dx + 2048 < w) ? 2048 : w - dx;
+                rh = (dy + subrectMaxHeight < h) ? subrectMaxHeight : h - dy;
+                if (!SendSubrect(cl, x+dx, y+dy, rw, rh))
+                    return FALSE;
+            }
+        }
+    } else {
+        if (!SendSubrect(cl, x, y, w, h))
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+static int
+SendSubrect(cl, x, y, w, h)
+    rfbClientPtr cl;
+    int x, y, w, h;
+{
+    rfbFramebufferUpdateRectHeader rect;
+    char *fbptr;
+    int success = 0;
+
+    if (ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) {
+	if (!rfbSendUpdateBuf(cl))
+	    return FALSE;
+    }
+
+    rect.r.x = Swap16IfLE(x);
+    rect.r.y = Swap16IfLE(y);
+    rect.r.w = Swap16IfLE(w);
+    rect.r.h = Swap16IfLE(h);
+    rect.encoding = Swap32IfLE(rfbEncodingTight);
+
+    memcpy(&updateBuf[ublen], (char *)&rect,
+	   sz_rfbFramebufferUpdateRectHeader);
+    ublen += sz_rfbFramebufferUpdateRectHeader;
+
+    cl->rfbRectanglesSent[rfbEncodingTight]++;
+    cl->rfbBytesSent[rfbEncodingTight] += sz_rfbFramebufferUpdateRectHeader;
+
+    fbptr = (rfbScreen.pfbMemory + (rfbScreen.paddedWidthInBytes * y)
+             + (x * (rfbScreen.bitsPerPixel / 8)));
+
+    (*cl->translateFn)(cl->translateLookupTable, &rfbServerFormat,
+                       &cl->format, fbptr, tightBeforeBuf,
+                       rfbScreen.paddedWidthInBytes, w, h);
+
+    paletteMaxColors = w * h / 128;
+    switch (cl->format.bitsPerPixel) {
+    case 8:
+        FillPalette8(w, h);
+        break;
+    case 16:
+        FillPalette16(w, h);
+        break;
+    default:
+        FillPalette32(w, h);
+    }
+
+    switch (paletteNumColors) {
+    case 1:
+        /* Solid rectangle */
+        success = SendSolidRect(cl, w, h);
+        break;
+    case 0:
+        /* Truecolor image */
+        if (!rfbTightDisableGradient && DetectStillImage(&cl->format, w, h)) {
+            success = SendGradientRect(cl, w, h);
+        } else {
+            success = SendFullColorRect(cl, w, h);
+        }
+        break;
+    default:
+        /* Up to 256 different colors */
+        success = SendIndexedRect(cl, w, h);
+    }
+    return success;
+}
+
+
+/*
+ * Subencoding implementations.
+ */
+
+static BOOL
+SendSolidRect(cl, w, h)
+    rfbClientPtr cl;
+    int w, h;
+{
+    int len;
+
+    if (usePixelFormat24) {
+        Pack24(tightBeforeBuf, &cl->format, 1);
+        len = 3;
+    } else
+        len = cl->format.bitsPerPixel / 8;
+
+    if (ublen + 1 + len > UPDATE_BUF_SIZE) {
+	if (!rfbSendUpdateBuf(cl))
+	    return FALSE;
+    }
+
+    updateBuf[ublen++] = (char)(rfbTightFill << 4);
+    memcpy (&updateBuf[ublen], tightBeforeBuf, len);
+    ublen += len;
+
+    cl->rfbBytesSent[rfbEncodingTight] += len + 1;
+
+    return TRUE;
+}
+
+static BOOL
+SendIndexedRect(cl, w, h)
+    rfbClientPtr cl;
+    int w, h;
+{
+    int streamId, entryLen, dataLen;
+    int x, y, i, width_bytes;
+
+    if ( ublen + 6 + paletteNumColors * cl->format.bitsPerPixel / 8 >
+         UPDATE_BUF_SIZE ) {
+	if (!rfbSendUpdateBuf(cl))
+	    return FALSE;
+    }
+
+    /* Prepare tight encoding header. */
+    if (paletteNumColors == 2) {
+        streamId = 1;
+        dataLen = (w + 7) / 8;
+        dataLen *= h;
+    } else {
+        streamId = 2;
+        dataLen = w * h;
+    }
+    updateBuf[ublen++] = (streamId | rfbTightExplicitFilter) << 4;
+    updateBuf[ublen++] = rfbTightFilterPalette;
+    updateBuf[ublen++] = (char)(paletteNumColors - 1);
+
+    /* Prepare palette, convert image. */
+    switch (cl->format.bitsPerPixel) {
+    case 32:
+        for (i = 0; i < paletteNumColors; i++) {
+            ((CARD32 *)tightAfterBuf)[i] =
+                palette.entry[i].listNode->rgb;
+        }
+
+        if (usePixelFormat24) {
+            Pack24(tightAfterBuf, &cl->format, paletteNumColors);
+            entryLen = 3;
+        } else
+            entryLen = 4;
+
+        memcpy(&updateBuf[ublen], tightAfterBuf, paletteNumColors * entryLen);
+        ublen += paletteNumColors * entryLen;
+        cl->rfbBytesSent[rfbEncodingTight] += paletteNumColors * entryLen + 3;
+
+        EncodeIndexedRect32((CARD8 *)tightBeforeBuf, w, h);
+        break;
+
+    case 16:
+        for (i = 0; i < paletteNumColors; i++) {
+            ((CARD16 *)tightAfterBuf)[i] =
+                (CARD16)palette.entry[i].listNode->rgb;
+        }
+        memcpy(&updateBuf[ublen], tightAfterBuf, paletteNumColors * 2);
+        ublen += paletteNumColors * 2;
+        cl->rfbBytesSent[rfbEncodingTight] += paletteNumColors * 2 + 3;
+
+        EncodeIndexedRect16((CARD8 *)tightBeforeBuf, w, h);
+        break;
+
+    default:
+        memcpy (&updateBuf[ublen], palette8.pixelValue, paletteNumColors);
+        ublen += paletteNumColors;
+        cl->rfbBytesSent[rfbEncodingTight] += paletteNumColors + 3;
+
+        EncodeIndexedRect8((CARD8 *)tightBeforeBuf, w, h);
+    }
+
+    return CompressData(cl, streamId, dataLen);
+}
+
+static BOOL
+SendFullColorRect(cl, w, h)
+    rfbClientPtr cl;
+    int w, h;
+{
+    int len;
+
+    if (ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) {
+	if (!rfbSendUpdateBuf(cl))
+	    return FALSE;
+    }
+
+    updateBuf[ublen++] = 0x00;  /* stream id = 0, no flushing, no filter */
+    cl->rfbBytesSent[rfbEncodingTight]++;
+
+    if (usePixelFormat24) {
+        Pack24(tightBeforeBuf, &cl->format, w * h);
+        len = 3;
+    } else
+        len = cl->format.bitsPerPixel / 8;
+
+    return CompressData(cl, 0, w * h * len);
+}
+
+static BOOL
+SendGradientRect(cl, w, h)
+    rfbClientPtr cl;
+    int w, h;
+{
+    int len;
+
+    if (cl->format.bitsPerPixel == 8)
+        return SendFullColorRect(cl, w, h);
+
+    if (ublen + TIGHT_MIN_TO_COMPRESS + 2 > UPDATE_BUF_SIZE) {
+	if (!rfbSendUpdateBuf(cl))
+	    return FALSE;
+    }
+
+    if (prevRowBuf == NULL)
+        prevRowBuf = (int *)xalloc(2048 * 3 * sizeof(int));
+
+    updateBuf[ublen++] = (3 | rfbTightExplicitFilter) << 4;
+    updateBuf[ublen++] = rfbTightFilterGradient;
+    cl->rfbBytesSent[rfbEncodingTight] += 2;
+
+    if (usePixelFormat24) {
+        FilterGradient24(tightBeforeBuf, &cl->format, w, h);
+        len = 3;
+    } else if (cl->format.bitsPerPixel == 32) {
+        FilterGradient32((CARD32 *)tightBeforeBuf, &cl->format, w, h);
+        len = 4;
+    } else {
+        FilterGradient16((CARD16 *)tightBeforeBuf, &cl->format, w, h);
+        len = 2;
+    }
+
+    return CompressData(cl, 3, w * h * len);
+}
+
+static BOOL
+CompressData(cl, streamId, dataLen)
+    rfbClientPtr cl;
+    int streamId, dataLen;
+{
+    z_streamp pz;
+    int compressedLen, portionLen;
+    int i, err;
+
+    if (dataLen < TIGHT_MIN_TO_COMPRESS) {
+        memcpy(&updateBuf[ublen], tightBeforeBuf, dataLen);
+        ublen += dataLen;
+        cl->rfbBytesSent[rfbEncodingTight] += dataLen;
+        return TRUE;
+    }
+
+    pz = &cl->zsStruct[streamId];
+
+    /* Initialize compression stream. */
+    if (!cl->zsActive[streamId]) {
+        pz->zalloc = Z_NULL;
+        pz->zfree = Z_NULL;
+        pz->opaque = Z_NULL;
+
+        if (streamId == 3) {
+            err = deflateInit2 (pz, 6, Z_DEFLATED, MAX_WBITS,
+                                MAX_MEM_LEVEL, Z_FILTERED);
+        } else {
+            err = deflateInit2 (pz, 9, Z_DEFLATED, MAX_WBITS,
+                                MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+        }
+
+        if (err != Z_OK)
+            return FALSE;
+
+        cl->zsActive[streamId] = TRUE;
+    }
+
+    /* Actual compression. */
+    pz->next_in = (Bytef *)tightBeforeBuf;
+    pz->avail_in = dataLen;
+    pz->next_out = (Bytef *)tightAfterBuf;
+    pz->avail_out = tightAfterBufSize;
+
+    if ( deflate (pz, Z_SYNC_FLUSH) != Z_OK ||
+         pz->avail_in != 0 || pz->avail_out == 0 ) {
+        return FALSE;
+    }
+
+    compressedLen = (size_t)(tightAfterBufSize - pz->avail_out);
+    cl->rfbBytesSent[rfbEncodingTight] += compressedLen + 1;
+
+    /* Prepare compressed data size for sending. */
+    updateBuf[ublen++] = compressedLen & 0x7F;
+    if (compressedLen > 0x7F) {
+        updateBuf[ublen-1] |= 0x80;
+        updateBuf[ublen++] = compressedLen >> 7 & 0x7F;
+        cl->rfbBytesSent[rfbEncodingTight]++;
+        if (compressedLen > 0x3FFF) {
+            updateBuf[ublen-1] |= 0x80;
+            updateBuf[ublen++] = compressedLen >> 14 & 0xFF;
+            cl->rfbBytesSent[rfbEncodingTight]++;
+        }
+    }
+
+    /* Send update. */
+    for (i = 0; i < compressedLen; ) {
+        portionLen = compressedLen - i;
+        if (portionLen > UPDATE_BUF_SIZE - ublen)
+            portionLen = UPDATE_BUF_SIZE - ublen;
+
+        memcpy(&updateBuf[ublen], &tightAfterBuf[i], portionLen);
+
+        ublen += portionLen;
+        i += portionLen;
+
+        if (!rfbSendUpdateBuf(cl))
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+
+/*
+ * Code to determine how many different colors used in rectangle.
+ */
+
+static void
+FillPalette8(w, h)
+    int w, h;
+{
+    CARD8 *data = (CARD8 *)tightBeforeBuf;
+    CARD8 c0, c1;
+    int i, n0, n1;
+
+    paletteNumColors = 0;
+
+    c0 = data[0];
+    for (i = 1; i < w * h && data[i] == c0; i++);
+    if (i == w * h) {
+        paletteNumColors = 1;
+        return;                 /* Solid rectangle */
+    }
+
+    if (paletteMaxColors < 2)
+        return;
+
+    n0 = i;
+    c1 = data[i];
+    n1 = 0;
+    for (i++; i < w * h; i++) {
+        if (data[i] == c0) {
+            n0++;
+        } else if (data[i] == c1) {
+            n1++;
+        } else
+            break;
+    }
+    if (i == w * h) {
+        if (n1 > n0) {
+            palette8.pixelValue[0] = c0;
+            palette8.pixelValue[1] = c1;
+            palette8.colorIdx[c0] = 0;
+            palette8.colorIdx[c1] = 1;
+        } else {
+            palette8.pixelValue[0] = c1;
+            palette8.pixelValue[1] = c0;
+            palette8.colorIdx[c0] = 1;
+            palette8.colorIdx[c1] = 0;
+        }
+        paletteNumColors = 2;   /* Two colors */
+    }
+}
+
+#define DEFINE_FILL_PALETTE_FUNCTION(bpp)                               \
+                                                                        \
+static void                                                             \
+FillPalette##bpp(w, h)                                                  \
+    int w, h;                                                           \
+{                                                                       \
+    CARD##bpp *data = (CARD##bpp *)tightBeforeBuf;                      \
+    CARD##bpp c0, c1, ci;                                               \
+    int i, n0, n1, ni;                                                  \
+                                                                        \
+    PaletteReset();                                                     \
+                                                                        \
+    c0 = data[0];                                                       \
+    for (i = 1; i < w * h && data[i] == c0; i++);                       \
+    if (i == w * h) {                                                   \
+        paletteNumColors = 1;                                           \
+        return;                 /* Solid rectangle */                   \
+    }                                                                   \
+                                                                        \
+    if (paletteMaxColors < 2)                                           \
+        return;                                                         \
+                                                                        \
+    n0 = i;                                                             \
+    c1 = data[i];                                                       \
+    n1 = 0;                                                             \
+    for (i++; i < w * h; i++) {                                         \
+        ci = data[i];                                                   \
+        if (ci == c0) {                                                 \
+            n0++;                                                       \
+        } else if (ci == c1) {                                          \
+            n1++;                                                       \
+        } else                                                          \
+            break;                                                      \
+    }                                                                   \
+    PaletteInsert (c0, (CARD32)n0);                                     \
+    PaletteInsert (c1, (CARD32)n1);                                     \
+    if (i == w * h)                                                     \
+        return;                 /* Two colors */                        \
+                                                                        \
+    ni = 1;                                                             \
+    for (i++; i < w * h; i++) {                                         \
+        if (data[i] == ci) {                                            \
+            ni++;                                                       \
+        } else {                                                        \
+            if (!PaletteInsert (ci, (CARD32)ni))                        \
+                return;                                                 \
+            ci = data[i];                                               \
+            ni = 1;                                                     \
+        }                                                               \
+    }                                                                   \
+    PaletteInsert (ci, (CARD32)ni);                                     \
+}
+
+DEFINE_FILL_PALETTE_FUNCTION(16)
+DEFINE_FILL_PALETTE_FUNCTION(32)
+
+
+/*
+ * Functions to operate with palette structures.
+ */
+
+static void
+PaletteReset(void)
+{
+    int i;
+
+    paletteNumColors = 0;
+    for (i = 0; i < 256; i++)
+        palette.hash[i] = NULL;
+}
+
+static int
+PaletteFind(rgb)
+    CARD32 rgb;
+{
+    COLOR_LIST *pnode;
+
+    if (rgb & 0xFF000000) {
+        pnode = palette.hash[(int)((rgb >> 24) + (rgb >> 16) & 0xFF)];
+    } else {
+        pnode = palette.hash[(int)((rgb >> 8) + rgb & 0xFF)];
+    }
+
+    while (pnode != NULL) {
+        if (pnode->rgb == rgb)
+            return pnode->idx;
+        pnode = pnode->next;
+    }
+    return -1;
+}
+
+static int
+PaletteInsert(rgb, numPixels)
+    CARD32 rgb;
+    int numPixels;
+{
+    COLOR_LIST *pnode;
+    COLOR_LIST *prev_pnode = NULL;
+    int hash_key, idx, new_idx, count;
+
+    if (rgb & 0xFF000000) {
+        hash_key = (int)((rgb >> 24) + (rgb >> 16) & 0xFF);
+    } else {
+        hash_key = (int)((rgb >> 8) + rgb & 0xFF);
+    }
+    pnode = palette.hash[hash_key];
+
+    while (pnode != NULL) {
+        if (pnode->rgb == rgb) {
+            /* Such palette entry already exists. */
+            new_idx = idx = pnode->idx;
+            count = palette.entry[idx].numPixels + numPixels;
+            if (new_idx && palette.entry[new_idx-1].numPixels < count) {
+                do {
+                    palette.entry[new_idx] = palette.entry[new_idx-1];
+                    palette.entry[new_idx].listNode->idx = new_idx;
+                    new_idx--;
+                }
+                while (new_idx && palette.entry[new_idx-1].numPixels < count);
+                palette.entry[new_idx].listNode = pnode;
+                pnode->idx = new_idx;
+            }
+            palette.entry[new_idx].numPixels = count;
+            return paletteNumColors;
+        }
+        prev_pnode = pnode;
+        pnode = pnode->next;
+    }
+
+    /* Check if palette is full. */
+    if (paletteNumColors == 256 || paletteNumColors == paletteMaxColors) {
+        paletteNumColors = 0;
+        return 0;
+    }
+
+    /* Move palette entries with lesser pixel counts. */
+    for ( idx = paletteNumColors;
+          idx > 0 && palette.entry[idx-1].numPixels < numPixels;
+          idx-- ) {
+        palette.entry[idx] = palette.entry[idx-1];
+        palette.entry[idx].listNode->idx = idx;
+    }
+
+    /* Add new palette entry into the freed slot. */
+    pnode = &palette.list[paletteNumColors];
+    if (prev_pnode != NULL) {
+        prev_pnode->next = pnode;
+    } else {
+        palette.hash[hash_key] = pnode;
+    }
+    pnode->next = NULL;
+    pnode->idx = idx;
+    pnode->rgb = rgb;
+    palette.entry[idx].listNode = pnode;
+    palette.entry[idx].numPixels = numPixels;
+
+    return (++paletteNumColors);
+}
+
+
+/*
+ * Converting 32-bit color samples into 24-bit colors.
+ * Should be called only when redMax, greenMax and blueMax are 256.
+ * 8-bit samples assumed to be byte-aligned.
+ */
+
+static void Pack24(buf, fmt, count)
+    char *buf;
+    rfbPixelFormat *fmt;
+    int count;
+{
+    int i;
+    CARD32 pix;
+    int r_shift, g_shift, b_shift;
+
+    if (!rfbServerFormat.bigEndian == !fmt->bigEndian) {
+        r_shift = fmt->redShift;
+        g_shift = fmt->greenShift;
+        b_shift = fmt->blueShift;
+    } else {
+        r_shift = 24 - fmt->redShift;
+        g_shift = 24 - fmt->greenShift;
+        b_shift = 24 - fmt->blueShift;
+    }
+
+    for (i = 0; i < count; i++) {
+        pix = ((CARD32 *)buf)[i];
+        buf[i*3]   = (char)(pix >> r_shift);
+        buf[i*3+1] = (char)(pix >> g_shift);
+        buf[i*3+2] = (char)(pix >> b_shift);
+    }
+}
+
+
+/*
+ * Converting truecolor samples into palette indices.
+ */
+
+#define PaletteFind8(c)  palette8.colorIdx[(c)]
+#define PaletteFind16    PaletteFind
+#define PaletteFind32    PaletteFind
+
+#define DEFINE_IDX_ENCODE_FUNCTION(bpp)                                 \
+                                                                        \
+static void                                                             \
+EncodeIndexedRect##bpp(buf, w, h)                                       \
+    CARD8 *buf;                                                         \
+    int w, h;                                                           \
+{                                                                       \
+    int x, y, i, width_bytes;                                           \
+                                                                        \
+    if (paletteNumColors != 2) {                                        \
+      for (i = 0; i < w * h; i++)                                       \
+        buf[i] = (CARD8)PaletteFind(((CARD##bpp *)buf)[i]);             \
+      return;                                                           \
+    }                                                                   \
+                                                                        \
+    width_bytes = (w + 7) / 8;                                          \
+    for (y = 0; y < h; y++) {                                           \
+      for (x = 0; x < w / 8; x++) {                                     \
+        for (i = 0; i < 8; i++)                                         \
+          buf[y*width_bytes+x] = (buf[y*width_bytes+x] << 1) |          \
+            (PaletteFind##bpp (((CARD##bpp *)buf)[y*w+x*8+i]) & 1);     \
+      }                                                                 \
+      buf[y*width_bytes+x] = 0;                                         \
+      for (i = 0; i < w % 8; i++) {                                     \
+        buf[y*width_bytes+x] |=                                         \
+          (PaletteFind##bpp (((CARD##bpp *)buf)[y*w+x*8+i]) & 1) <<     \
+            (7 - i);                                                    \
+      }                                                                 \
+    }                                                                   \
+}
+
+DEFINE_IDX_ENCODE_FUNCTION(8)
+DEFINE_IDX_ENCODE_FUNCTION(16)
+DEFINE_IDX_ENCODE_FUNCTION(32)
+
+
+/*
+ * ``Gradient'' filter for 24-bit color samples.
+ * Should be called only when redMax, greenMax and blueMax are 256.
+ * 8-bit samples assumed to be byte-aligned.
+ */
+
+static void FilterGradient24(buf, fmt, w, h)
+    char *buf;
+    rfbPixelFormat *fmt;
+    int w, h;
+{
+    CARD32 *buf32;
+    CARD32 pix32;
+    int *prevRowPtr;
+    int shiftBits[3];
+    int pixHere[3], pixUpper[3], pixLeft[3], pixUpperLeft[3];
+    int prediction;
+    int x, y, c;
+
+    buf32 = (CARD32 *)buf;
+    memset (prevRowBuf, 0, w * 3 * sizeof(int));
+
+    if (!rfbServerFormat.bigEndian == !fmt->bigEndian) {
+        shiftBits[0] = fmt->redShift;
+        shiftBits[1] = fmt->greenShift;
+        shiftBits[2] = fmt->blueShift;
+    } else {
+        shiftBits[0] = 24 - fmt->redShift;
+        shiftBits[1] = 24 - fmt->greenShift;
+        shiftBits[2] = 24 - fmt->blueShift;
+    }
+
+    for (y = 0; y < h; y++) {
+        for (c = 0; c < 3; c++) {
+            pixUpper[c] = 0;
+            pixHere[c] = 0;
+        }
+        prevRowPtr = prevRowBuf;
+        for (x = 0; x < w; x++) {
+            pix32 = *buf32++;
+            for (c = 0; c < 3; c++) {
+                pixUpperLeft[c] = pixUpper[c];
+                pixLeft[c] = pixHere[c];
+                pixUpper[c] = *prevRowPtr;
+                pixHere[c] = (int)(pix32 >> shiftBits[c] & 0xFF);
+                *prevRowPtr++ = pixHere[c];
+
+                prediction = pixLeft[c] + pixUpper[c] - pixUpperLeft[c];
+                if (prediction < 0) {
+                    prediction = 0;
+                } else if (prediction > 0xFF) {
+                    prediction = 0xFF;
+                }
+                *buf++ = (char)(pixHere[c] - prediction);
+            }
+        }
+    }
+}
+
+
+/*
+ * ``Gradient'' filter for other color depths.
+ */
+
+#define DEFINE_GRADIENT_FILTER_FUNCTION(bpp)                             \
+                                                                         \
+static void FilterGradient##bpp(buf, fmt, w, h)                          \
+    CARD##bpp *buf;                                                      \
+    rfbPixelFormat *fmt;                                                 \
+    int w, h;                                                            \
+{                                                                        \
+    CARD##bpp pix, diff;                                                 \
+    BOOL endianMismatch;                                                 \
+    int *prevRowPtr;                                                     \
+    int maxColor[3], shiftBits[3];                                       \
+    int pixHere[3], pixUpper[3], pixLeft[3], pixUpperLeft[3];            \
+    int prediction;                                                      \
+    int x, y, c;                                                         \
+                                                                         \
+    memset (prevRowBuf, 0, w * 3 * sizeof(int));                         \
+                                                                         \
+    endianMismatch = (!rfbServerFormat.bigEndian != !fmt->bigEndian);    \
+                                                                         \
+    maxColor[0] = fmt->redMax;                                           \
+    maxColor[1] = fmt->greenMax;                                         \
+    maxColor[2] = fmt->blueMax;                                          \
+    shiftBits[0] = fmt->redShift;                                        \
+    shiftBits[1] = fmt->greenShift;                                      \
+    shiftBits[2] = fmt->blueShift;                                       \
+                                                                         \
+    for (y = 0; y < h; y++) {                                            \
+        for (c = 0; c < 3; c++) {                                        \
+            pixUpper[c] = 0;                                             \
+            pixHere[c] = 0;                                              \
+        }                                                                \
+        prevRowPtr = prevRowBuf;                                         \
+        for (x = 0; x < w; x++) {                                        \
+            pix = *buf;                                                  \
+            if (endianMismatch) {                                        \
+                pix = Swap##bpp(pix);                                    \
+            }                                                            \
+            diff = 0;                                                    \
+            for (c = 0; c < 3; c++) {                                    \
+                pixUpperLeft[c] = pixUpper[c];                           \
+                pixLeft[c] = pixHere[c];                                 \
+                pixUpper[c] = *prevRowPtr;                               \
+                pixHere[c] = (int)(pix >> shiftBits[c] & maxColor[c]);   \
+                *prevRowPtr++ = pixHere[c];                              \
+                                                                         \
+                prediction = pixLeft[c] + pixUpper[c] - pixUpperLeft[c]; \
+                if (prediction < 0) {                                    \
+                    prediction = 0;                                      \
+                } else if (prediction > maxColor[c]) {                   \
+                    prediction = maxColor[c];                            \
+                }                                                        \
+                diff |= ((pixHere[c] - prediction) & maxColor[c])        \
+                    << shiftBits[c];                                     \
+            }                                                            \
+            if (endianMismatch) {                                        \
+                diff = Swap##bpp(diff);                                  \
+            }                                                            \
+            *buf++ = diff;                                               \
+        }                                                                \
+    }                                                                    \
+}
+
+DEFINE_GRADIENT_FILTER_FUNCTION(16)
+DEFINE_GRADIENT_FILTER_FUNCTION(32)
+
+
+/*
+ * Code to guess if given rectangle is suitable for still image
+ * compression.
+ */
+
+#define DETECT_SUBROW_WIDTH   7
+#define DETECT_MIN_WIDTH      8
+#define DETECT_MIN_HEIGHT     8
+#define DETECT_MIN_SIZE    8192
+
+static int DetectStillImage (fmt, w, h)
+    rfbPixelFormat *fmt;
+    int w, h;
+{
+    if ( fmt->bitsPerPixel == 8 || w * h < DETECT_MIN_SIZE ||
+         w < DETECT_MIN_WIDTH || h < DETECT_MIN_HEIGHT ) {
+        return 0;
+    }
+
+    if (fmt->bitsPerPixel == 32) {
+        if (usePixelFormat24) {
+            return DetectStillImage24(fmt, w, h);
+        } else {
+            return DetectStillImage32(fmt, w, h);
+        }
+    } else {
+        return DetectStillImage16(fmt, w, h);
+    }
+}
+
+static int DetectStillImage24 (fmt, w, h)
+    rfbPixelFormat *fmt;
+    int w, h;
+{
+    int off;
+    int x, y, d, dx, c;
+    int diffStat[256];
+    int pixelCount = 0;
+    int pix, left[3];
+    unsigned long avgError;
+
+    /* If client is big-endian, color samples begin from the second
+       byte (offset 1) of a 32-bit pixel value. */
+    off = (fmt->bigEndian != 0);
+
+    memset(diffStat, 0, 256*sizeof(int));
+
+    y = 0, x = 0;
+    while (y < h && x < w) {
+        for (d = 0; d < h - y && d < w - x - DETECT_SUBROW_WIDTH; d++) {
+            for (c = 0; c < 3; c++) {
+                left[c] = (int)tightBeforeBuf[((y+d)*w+x+d)*4+off+c] & 0xFF;
+            }
+            for (dx = 1; dx <= DETECT_SUBROW_WIDTH; dx++) {
+                for (c = 0; c < 3; c++) {
+                    pix = (int)tightBeforeBuf[((y+d)*w+x+d+dx)*4+off+c] & 0xFF;
+                    diffStat[abs(pix - left[c])]++;
+                    left[c] = pix;
+                }
+                pixelCount++;
+            }
+        }
+        if (w > h) {
+            x += h;
+            y = 0;
+        } else {
+            x = 0;
+            y += w;
+        }
+    }
+
+    if (diffStat[0] * 33 / pixelCount >= 95)
+        return 0;
+
+    avgError = 0;
+    for (c = 1; c < 8; c++) {
+        avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c);
+        if (diffStat[c] == 0 || diffStat[c] > diffStat[c-1] * 2)
+            return 0;
+    }
+    for (; c < 256; c++) {
+        avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c);
+    }
+    avgError /= (pixelCount * 3 - diffStat[0]);
+
+    return (avgError < 500);
+}
+
+#define DEFINE_DETECT_FUNCTION(bpp)                                          \
+                                                                             \
+static int DetectStillImage##bpp (fmt, w, h)                                 \
+    rfbPixelFormat *fmt;                                                     \
+    int w, h;                                                                \
+{                                                                            \
+    BOOL endianMismatch;                                                     \
+    CARD##bpp pix;                                                           \
+    int maxColor[3], shiftBits[3];                                           \
+    int x, y, d, dx, c;                                                      \
+    int diffStat[256];                                                       \
+    int pixelCount = 0;                                                      \
+    int sample, sum, left[3];                                                \
+    unsigned long avgError;                                                  \
+                                                                             \
+    endianMismatch = (!rfbServerFormat.bigEndian != !fmt->bigEndian);        \
+                                                                             \
+    maxColor[0] = fmt->redMax;                                               \
+    maxColor[1] = fmt->greenMax;                                             \
+    maxColor[2] = fmt->blueMax;                                              \
+    shiftBits[0] = fmt->redShift;                                            \
+    shiftBits[1] = fmt->greenShift;                                          \
+    shiftBits[2] = fmt->blueShift;                                           \
+                                                                             \
+    memset(diffStat, 0, 256*sizeof(int));                                    \
+                                                                             \
+    y = 0, x = 0;                                                            \
+    while (y < h && x < w) {                                                 \
+        for (d = 0; d < h - y && d < w - x - DETECT_SUBROW_WIDTH; d++) {     \
+            pix = ((CARD##bpp *)tightBeforeBuf)[(y+d)*w+x+d];                \
+            if (endianMismatch) {                                            \
+                pix = Swap##bpp(pix);                                        \
+            }                                                                \
+            for (c = 0; c < 3; c++) {                                        \
+                left[c] = (int)(pix >> shiftBits[c] & maxColor[c]);          \
+            }                                                                \
+            for (dx = 1; dx <= DETECT_SUBROW_WIDTH; dx++) {                  \
+                pix = ((CARD##bpp *)tightBeforeBuf)[(y+d)*w+x+d+dx];         \
+                if (endianMismatch) {                                        \
+                    pix = Swap##bpp(pix);                                    \
+                }                                                            \
+                sum = 0;                                                     \
+                for (c = 0; c < 3; c++) {                                    \
+                    sample = (int)(pix >> shiftBits[c] & maxColor[c]);       \
+                    sum += abs(sample - left[c]);                            \
+                    left[c] = sample;                                        \
+                }                                                            \
+                if (sum > 255)                                               \
+                    sum = 255;                                               \
+                diffStat[sum]++;                                             \
+                pixelCount++;                                                \
+            }                                                                \
+        }                                                                    \
+        if (w > h) {                                                         \
+            x += h;                                                          \
+            y = 0;                                                           \
+        } else {                                                             \
+            x = 0;                                                           \
+            y += w;                                                          \
+        }                                                                    \
+    }                                                                        \
+                                                                             \
+    if ((diffStat[0] + diffStat[1]) * 100 / pixelCount >= 90)                \
+        return 0;                                                            \
+                                                                             \
+    avgError = 0;                                                            \
+    for (c = 1; c < 8; c++) {                                                \
+        avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c);     \
+        if (diffStat[c] == 0 || diffStat[c] > diffStat[c-1] * 2)             \
+            return 0;                                                        \
+    }                                                                        \
+    for (; c < 256; c++) {                                                   \
+        avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c);     \
+    }                                                                        \
+    avgError /= (pixelCount - diffStat[0]);                                  \
+                                                                             \
+    return (avgError < 200);                                                 \
+}
+
+DEFINE_DETECT_FUNCTION(16)
+DEFINE_DETECT_FUNCTION(32)
+
Index: vnc_unixsrc/include/rfbproto.h
diff -u vnc_unixsrc/include/rfbproto.h:1.1.1.1 vnc_unixsrc/include/rfbproto.h:1.7
--- vnc_unixsrc/include/rfbproto.h:1.1.1.1	Sun Jun 11 20:00:53 2000
+++ vnc_unixsrc/include/rfbproto.h	Fri Sep  1 17:04:43 2000
@@ -289,6 +289,7 @@
 #define rfbEncodingRRE 2
 #define rfbEncodingCoRRE 4
 #define rfbEncodingHextile 5
+#define rfbEncodingTight 7
 
 
 
@@ -435,6 +436,20 @@
 #define rfbHextileExtractY(byte) ((byte) & 0xf)
 #define rfbHextileExtractW(byte) (((byte) >> 4) + 1)
 #define rfbHextileExtractH(byte) (((byte) & 0xf) + 1)
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * ``Tight'' Encoding.  FIXME: Add more documentation.
+ */
+
+#define rfbTightExplicitFilter         0x04
+#define rfbTightFill                   0x08
+#define rfbTightMaxSubencoding         0x08
+
+/* Filters to improve compression efficiency */
+#define rfbTightFilterCopy             0x00
+#define rfbTightFilterPalette          0x01
+#define rfbTightFilterGradient         0x02
 
 
 /*-----------------------------------------------------------------------------
Index: vnc_unixsrc/vncviewer/Imakefile
diff -u vnc_unixsrc/vncviewer/Imakefile:1.1.1.1 vnc_unixsrc/vncviewer/Imakefile:1.3
--- vnc_unixsrc/vncviewer/Imakefile:1.1.1.1	Sun Jun 11 20:00:53 2000
+++ vnc_unixsrc/vncviewer/Imakefile	Tue Aug  8 05:46:53 2000
@@ -1,3 +1,4 @@
+XCOMM (FIXME) Use internal copy of zlib library?
 
 #ifdef SunArchitecture
 CC = gcc
@@ -12,11 +13,12 @@
 DEFINES = -DMITSHM
 #endif
 
-INCLUDES = -I../include -I.
+INCLUDES = -I../include -I. -I/usr/include
 VNCAUTH_LIB = ../libvncauth/libvncauth.a
+ZLIB_LIB = /usr/lib/libz.a
 
-DEPLIBS = XawClientDepLibs $(VNCAUTH_LIB)
-LOCAL_LIBRARIES = XawClientLibs $(VNCAUTH_LIB)
+DEPLIBS = XawClientDepLibs $(VNCAUTH_LIB) $(ZLIB_LIB)
+LOCAL_LIBRARIES = XawClientLibs $(VNCAUTH_LIB) $(ZLIB_LIB)
 
 SRCS = \
   argsresources.c \
@@ -31,6 +33,7 @@
   selection.c \
   shm.c \
   sockets.c \
+  tunnel.c \
   vncviewer.c
 
 OBJS = $(SRCS:.c=.o)
Index: vnc_unixsrc/vncviewer/argsresources.c
diff -u vnc_unixsrc/vncviewer/argsresources.c:1.1.1.1 vnc_unixsrc/vncviewer/argsresources.c:1.3
--- vnc_unixsrc/vncviewer/argsresources.c:1.1.1.1	Sun Jun 11 20:00:53 2000
+++ vnc_unixsrc/vncviewer/argsresources.c	Tue Aug  8 16:34:06 2000
@@ -238,17 +238,33 @@
 
 
 /*
+ * removeArgs() is used to remove some of command line arguments.
+ */
+
+void
+removeArgs(int *argc, char** argv, int idx, int nargs)
+{
+  int i;
+  if ((idx+nargs) > *argc) return;
+  for (i = idx+nargs; i < *argc; i++) {
+    argv[i-nargs] = argv[i];
+  }
+  *argc -= nargs;
+}
+
+/*
  * usage() prints out the usage message.
  */
 
 void
-usage()
+usage(void)
 {
   fprintf(stderr,"\n"
-	  "VNC viewer version 3.3.3r1\n"
+	  "VNC viewer version 3.3.3r1 with SSH tunneling support\n"
 	  "\n"
-	  "usage: %s [<options>] <host>:<display#>\n"
+	  "usage: %s [<options>] [<host>:]<display#>\n"
 	  "       %s [<options>] -listen [<display#>]\n"
+	  "       %s [<options>] -tunnel <host>:<display#>\n"
 	  "\n"
 	  "<options> are standard Xt options, or:\n"
 	  "              -shared\n"
@@ -260,7 +276,7 @@
 	  "              -owncmap\n"
 	  "              -truecolour\n"
 	  "              -depth <depth>\n"
-	  ,programName,programName);
+	  ,programName,programName,programName);
   exit(1);
 }
 
Index: vnc_unixsrc/vncviewer/listen.c
diff -u vnc_unixsrc/vncviewer/listen.c:1.1.1.1 vnc_unixsrc/vncviewer/listen.c:1.2
--- vnc_unixsrc/vncviewer/listen.c:1.1.1.1	Sun Jun 11 20:00:53 2000
+++ vnc_unixsrc/vncviewer/listen.c	Tue Aug  8 16:34:06 2000
@@ -40,17 +40,6 @@
 static void flashDisplay(Display *d, char *user);
 static Bool AllXEventsPredicate(Display *d, XEvent *ev, char *arg);
 
-static void
-removeArgs(int *argc, char** argv, int idx, int nargs)
-{
-  int i;
-  if ((idx+nargs) > *argc) return;
-  for (i = idx+nargs; i < *argc; i++) {
-    argv[i-nargs] = argv[i];
-  }
-  *argc -= nargs;
-}
-
 /*
  * listenForIncomingConnections() - listen for incoming connections from
  * servers, and fork a new process to deal with each connection.  We must do
Index: vnc_unixsrc/vncviewer/rfbproto.c
diff -u vnc_unixsrc/vncviewer/rfbproto.c:1.1.1.1 vnc_unixsrc/vncviewer/rfbproto.c:1.8
--- vnc_unixsrc/vncviewer/rfbproto.c:1.1.1.1	Sun Jun 11 20:00:53 2000
+++ vnc_unixsrc/vncviewer/rfbproto.c	Thu Sep  7 11:49:19 2000
@@ -26,7 +26,10 @@
 #include <pwd.h>
 #include <vncviewer.h>
 #include <vncauth.h>
+#include <zlib.h>
 
+static long ReadCompactLen (void);
+
 static Bool HandleRRE8(int rx, int ry, int rw, int rh);
 static Bool HandleRRE16(int rx, int ry, int rw, int rh);
 static Bool HandleRRE32(int rx, int ry, int rw, int rh);
@@ -36,6 +39,9 @@
 static Bool HandleHextile8(int rx, int ry, int rw, int rh);
 static Bool HandleHextile16(int rx, int ry, int rw, int rh);
 static Bool HandleHextile32(int rx, int ry, int rw, int rh);
+static Bool HandleTight8(int rx, int ry, int rw, int rh);
+static Bool HandleTight16(int rx, int ry, int rw, int rh);
+static Bool HandleTight32(int rx, int ry, int rw, int rh);
 
 int rfbsock;
 char *desktopName;
@@ -47,15 +53,37 @@
 int endianTest = 1;
 
 
-/* note that the CoRRE encoding uses this buffer and assumes it is big enough
-   to hold 255 * 255 * 32 bits -> 260100 bytes.  640*480 = 307200 bytes */
-/* also hextile assumes it is big enough to hold 16 * 16 * 32 bits */
+/* Note that the CoRRE encoding uses this buffer and assumes it is big enough
+   to hold 255 * 255 * 32 bits -> 260100 bytes.  640*480 = 307200 bytes.
+   Hextile also assumes it is big enough to hold 16 * 16 * 32 bits.
+   Tight encoding assumes BUFFER_SIZE is at least 16384 bytes. */
 
 #define BUFFER_SIZE (640*480)
 static char buffer[BUFFER_SIZE];
 
 
 /*
+ * Variables for the ``tight'' encoding implementation.
+ */
+
+/* Separate buffer for compressed data. */
+#define ZLIB_BUFFER_SIZE 4096
+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 CARD8 tightPrevRow[2048*3*sizeof(CARD16)];
+
+
+/*
  * ConnectToRFBServer.
  */
 
@@ -276,6 +304,8 @@
 	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);
       } else if (strncasecmp(encStr,"hextile",encStrLen) == 0) {
 	encs[se->nEncodings++] = Swap32IfLE(rfbEncodingHextile);
       } else if (strncasecmp(encStr,"corre",encStrLen) == 0) {
@@ -298,6 +328,7 @@
     encs[se->nEncodings++] = Swap32IfLE(rfbEncodingHextile);
     encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCoRRE);
     encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRRE);
+    encs[se->nEncodings++] = Swap32IfLE(rfbEncodingTight);
   }
 
   len = sz_rfbSetEncodingsMsg + se->nEncodings * 4;
@@ -589,6 +620,25 @@
 	break;
       }
 
+      case rfbEncodingTight:
+      {
+	switch (myFormat.bitsPerPixel) {
+	case 8:
+	  if (!HandleTight8(rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+	    return False;
+	  break;
+	case 16:
+	  if (!HandleTight16(rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+	    return False;
+	  break;
+	case 32:
+	  if (!HandleTight32(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);
@@ -613,7 +663,7 @@
   }
 
   case rfbBell:
-    XBell(dpy,100);
+    XBell(dpy,0);
     break;
 
   case rfbServerCutText:
@@ -668,16 +718,19 @@
 #include "rre.c"
 #include "corre.c"
 #include "hextile.c"
+#include "tight.c"
 #undef BPP
 #define BPP 16
 #include "rre.c"
 #include "corre.c"
 #include "hextile.c"
+#include "tight.c"
 #undef BPP
 #define BPP 32
 #include "rre.c"
 #include "corre.c"
 #include "hextile.c"
+#include "tight.c"
 #undef BPP
 
 
@@ -710,3 +763,26 @@
     }
   }
 }
+
+static long
+ReadCompactLen (void)
+{
+  long len;
+  CARD8 b;
+
+  if (!ReadFromRFBServer((char *)&b, 1))
+    return -1;
+  len = (int)b & 0x7F;
+  if (b & 0x80) {
+    if (!ReadFromRFBServer((char *)&b, 1))
+      return -1;
+    len |= ((int)b & 0x7F) << 7;
+    if (b & 0x80) {
+      if (!ReadFromRFBServer((char *)&b, 1))
+        return -1;
+      len |= ((int)b & 0xFF) << 14;
+    }
+  }
+  return len;
+}
+
Index: vnc_unixsrc/vncviewer/sockets.c
diff -u vnc_unixsrc/vncviewer/sockets.c:1.1.1.1 vnc_unixsrc/vncviewer/sockets.c:1.2
--- vnc_unixsrc/vncviewer/sockets.c:1.1.1.1	Sun Jun 11 20:00:53 2000
+++ vnc_unixsrc/vncviewer/sockets.c	Tue Aug  8 05:46:53 2000
@@ -235,6 +235,40 @@
 
 
 /*
+ * 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) {
+    fprintf(stderr,programName);
+    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.
  */
 
@@ -257,7 +291,7 @@
   }
 
   if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
-		 (const char *)&one, sizeof(one)) < 0) {
+                 (const char *)&one, sizeof(one)) < 0) {
     fprintf(stderr,programName);
     perror(": ListenAtTcpPort: setsockopt");
     close(sock);
Index: vnc_unixsrc/vncviewer/tight.c
diff -u /dev/null vnc_unixsrc/vncviewer/tight.c:1.17
--- /dev/null	Thu Oct  5 17:44:10 2000
+++ vnc_unixsrc/vncviewer/tight.c	Wed Sep 27 23:08:56 2000
@@ -0,0 +1,494 @@
+/*
+ *  Copyright (C) 2000 Const Kaplinsky.  All Rights Reserved.
+ *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+ *
+ *  This is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This software is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this software; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ *  USA.
+ */
+
+/*
+ * 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 an zlib encoded rectangle with BPP bits per pixel.
+ *
+ */
+
+#define TIGHT_MIN_TO_COMPRESS 12
+
+#define CARDBPP CONCAT2E(CARD,BPP)
+#define filterPtrBPP CONCAT2E(filterPtr,BPP)
+#define RGB_TO_PIXELBPP CONCAT2E(RGB_TO_PIXEL,BPP)
+#define RGB24_TO_PIXELBPP CONCAT2E(RGB24_TO_PIXEL,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)
+
+#ifndef RGB_TO_PIXEL
+
+#define RGB_TO_PIXEL(bpp,r,g,b)                                              \
+  ((CARD##bpp)(r) & myFormat.redMax) << myFormat.redShift |                  \
+  ((CARD##bpp)(g) & myFormat.greenMax) << myFormat.greenShift |              \
+  ((CARD##bpp)(b) & myFormat.blueMax) << myFormat.blueShift;
+
+#define RGB24_TO_PIXEL32(r,g,b)                                              \
+  ((CARD32)(r) & 0xFF) << myFormat.redShift |                                \
+  ((CARD32)(g) & 0xFF) << myFormat.greenShift |                              \
+  ((CARD32)(b) & 0xFF) << myFormat.blueShift;
+
+#endif
+
+/* Type declarations */
+
+typedef void (*filterPtrBPP)(int, CARDBPP *);
+
+/* Prototypes */
+
+static int InitFilterCopyBPP (int rw, int rh);
+static int InitFilterPaletteBPP (int rw, int rh);
+static int InitFilterGradientBPP (int rw, int rh);
+static void FilterCopyBPP (int numRows, CARDBPP *destBuffer);
+static void FilterPaletteBPP (int numRows, CARDBPP *destBuffer);
+static void FilterGradientBPP (int numRows, CARDBPP *destBuffer);
+
+/* Definitions */
+
+static Bool
+HandleTightBPP (int rx, int ry, int rw, int rh)
+{
+  CARDBPP fill_colour;
+  XGCValues gcv;
+  CARD8 comp_ctl;
+  CARD8 filter_id;
+  filterPtrBPP filterFn;
+  z_streamp zs;
+  char *buffer2;
+  int err, stream_id, compressedLen, bitsPixel;
+  int bufferSize, rowSize, numRows, portionLen, rowsProcessed, extraBytes;
+  CARDBPP *rawData;
+
+  if (!ReadFromRFBServer((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 (myFormat.depth == 24 && myFormat.redMax == 0xFF &&
+        myFormat.greenMax == 0xFF && myFormat.blueMax == 0xFF) {
+      if (!ReadFromRFBServer(buffer, 3))
+        return False;
+      fill_colour = RGB24_TO_PIXEL32(buffer[0], buffer[1], buffer[2]);
+    } else {
+      if (!ReadFromRFBServer((char*)&fill_colour, sizeof(fill_colour)))
+        return False;
+    }
+#else
+    if (!ReadFromRFBServer((char*)&fill_colour, sizeof(fill_colour)))
+        return False;
+#endif
+
+#if (BPP == 8)
+    gcv.foreground = (appData.useBGR233) ?
+      BGR233ToPixel[fill_colour] : fill_colour;
+#else
+    gcv.foreground = fill_colour;
+#endif
+
+    XChangeGC(dpy, gc, GCForeground, &gcv);
+    XFillRectangle(dpy, desktopWin, gc, rx, ry, rw, rh);
+    return True;
+  }
+
+  /* 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((char*)&filter_id, 1))
+      return False;
+
+    switch (filter_id) {
+    case rfbTightFilterCopy:
+      filterFn = FilterCopyBPP;
+      bitsPixel = InitFilterCopyBPP(rw, rh);
+      break;
+    case rfbTightFilterPalette:
+      filterFn = FilterPaletteBPP;
+      bitsPixel = InitFilterPaletteBPP(rw, rh);
+      break;
+    case rfbTightFilterGradient:
+      filterFn = FilterGradientBPP;
+      bitsPixel = InitFilterGradientBPP(rw, rh);
+      break;
+    delault:
+      fprintf(stderr, "Tight encoding: unknown filter code received.\n");
+      return False;
+    }
+  } else {
+    filterFn = FilterCopyBPP;
+    bitsPixel = InitFilterCopyBPP(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((char*)buffer, rh * rowSize))
+      return False;
+
+    buffer2 = &buffer[TIGHT_MIN_TO_COMPRESS * 4];
+    filterFn(rh, (CARDBPP *)buffer2);
+    CopyDataToScreen(buffer2, rx, ry, rw, rh);
+
+    return True;
+  }
+
+  /* Read the length (1..3 bytes) of compressed data following. */
+  compressedLen = ReadCompactLen();
+  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 = &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((char*)zlib_buffer, portionLen))
+      return False;
+
+    compressedLen -= portionLen;
+
+    zs->next_in = (Bytef *)zlib_buffer;
+    zs->avail_in = portionLen;
+
+    do {
+      zs->next_out = (Bytef *)&buffer[extraBytes];
+      zs->avail_out = bufferSize - extraBytes;
+
+      err = inflate(zs, Z_SYNC_FLUSH);
+      if (err != Z_OK && err != Z_STREAM_END) {
+        if (zs->msg != NULL)
+          fprintf(stderr, "Inflate error: %s.\n", zs->msg);
+        return False;
+      }
+
+      numRows = (bufferSize - zs->avail_out) / rowSize;
+
+      filterFn(numRows, (CARDBPP *)buffer2);
+
+      extraBytes = bufferSize - zs->avail_out - numRows * rowSize;
+      if (extraBytes > 0)
+        memcpy(buffer, &buffer[numRows * rowSize], extraBytes);
+
+      CopyDataToScreen(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 CARD8 tightPalette[256*4];
+     static CARD8 tightPrevRow[2048*3*sizeof(CARD16)];
+*/
+
+static int
+InitFilterCopyBPP (int rw, int rh)
+{
+  rectWidth = rw;
+
+#if BPP == 32
+  if (myFormat.depth == 24 && myFormat.redMax == 0xFF &&
+      myFormat.greenMax == 0xFF && myFormat.blueMax == 0xFF) {
+    cutZeros = True;
+    return 24;
+  } else {
+    cutZeros = False;
+  }
+#endif
+
+  return BPP;
+}
+
+static void
+FilterCopyBPP (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(buffer[(y*rectWidth+x)*3],
+                           buffer[(y*rectWidth+x)*3+1],
+                           buffer[(y*rectWidth+x)*3+2]);
+      }
+    }
+    return;
+  }
+#endif
+
+  memcpy (dst, buffer, numRows * rectWidth * (BPP / 8));
+}
+
+static int
+InitFilterGradientBPP (int rw, int rh)
+{
+  int bits;
+
+  bits = InitFilterCopyBPP(rw, rh);
+  if (cutZeros)
+    memset(tightPrevRow, 0, rw * 3);
+  else
+    memset(tightPrevRow, 0, rw * 3 * sizeof(CARD16));
+
+  return bits;
+}
+
+#if BPP == 32
+
+static void
+FilterGradient24 (int numRows, CARD32 *dst)
+{
+  int x, y, c;
+  CARD8 thisRow[2048*3];
+  CARD8 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] + 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] = (CARD8)est[c] + 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 (int numRows, CARDBPP *dst)
+{
+  int x, y, c;
+  CARDBPP *src = (CARDBPP *)buffer;
+  CARD16 *thatRow = (CARD16 *)tightPrevRow;
+  CARD16 thisRow[2048*3];
+  CARD16 pix[3];
+  CARD16 max[3];
+  int shift[3];
+  int est[3];
+
+#if BPP == 32
+  if (cutZeros) {
+    FilterGradient24(numRows, dst);
+    return;
+  }
+#endif
+
+  max[0] = myFormat.redMax;
+  max[1] = myFormat.greenMax;
+  max[2] = myFormat.blueMax;
+
+  shift[0] = myFormat.redShift;
+  shift[1] = myFormat.greenShift;
+  shift[2] = myFormat.blueShift;
+
+  for (y = 0; y < numRows; y++) {
+
+    /* First pixel in a row */
+    for (c = 0; c < 3; c++) {
+      pix[c] = (CARD16)((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] = (CARD16)((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(CARD16));
+  }
+}
+
+static int
+InitFilterPaletteBPP (int rw, int rh)
+{
+  int i;
+  CARD8 numColors;
+  CARDBPP *palette = (CARDBPP *)tightPalette;
+
+  rectWidth = rw;
+
+  if (!ReadFromRFBServer((char*)&numColors, 1))
+    return 0;
+
+  rectColors = (int)numColors;
+  if (++rectColors < 2)
+    return 0;
+
+#if BPP == 32
+  if (myFormat.depth == 24 && myFormat.redMax == 0xFF &&
+      myFormat.greenMax == 0xFF && myFormat.blueMax == 0xFF) {
+    if (!ReadFromRFBServer((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((char*)&tightPalette, rectColors * (BPP / 8)))
+    return 0;
+
+  return (rectColors == 2) ? 1 : 8;
+}
+
+static void
+FilterPaletteBPP (int numRows, CARDBPP *dst)
+{
+  int x, y, b, w;
+  CARD8 *src = (CARD8 *)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]];
+  }
+}
+
Index: vnc_unixsrc/vncviewer/tunnel.c
diff -u /dev/null vnc_unixsrc/vncviewer/tunnel.c:1.2
--- /dev/null	Thu Oct  5 17:44:10 2000
+++ vnc_unixsrc/vncviewer/tunnel.c	Tue Aug  8 16:34:06 2000
@@ -0,0 +1,189 @@
+/*
+ *  Copyright (C) 1999 Const Kaplinsky.  All Rights Reserved.
+ *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+ *
+ *  This is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This software is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this software; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ *  USA.
+ */
+
+/*
+ * tunnel.c - tunneling support (e.g. for using standalone SSH installation)
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <vncviewer.h>
+
+/*
+ * "Hostname:display" pair in the command line will be substituted by
+ * this fake argument when tunneling is used.
+ */
+static char lastArgv[32];
+
+
+static void processTunnelArgs (char **remoteHost, int *remotePort,
+                               int localPort, int *pargc, char **argv,
+                               int tunnelArgIndex);
+static char *getCmdPattern (void);
+static Bool fillCmdPattern (char *result, char *pattern, char *remoteHost,
+                            char *remotePort, char *localPort);
+static Bool runCommand (char *cmd);
+
+
+Bool
+createTunnel(int *pargc, char **argv, int tunnelArgIndex)
+{
+  char *pattern;
+  char cmd[1024];
+  int localPort, remotePort;
+  char localPortStr[8];
+  char remotePortStr[8];
+  char *remoteHost;
+
+  pattern = getCmdPattern();
+  if (!pattern)
+    return False;
+
+  localPort = FindFreeTcpPort();
+  if (localPort == 0)
+    return False;
+
+  processTunnelArgs (&remoteHost, &remotePort, localPort,
+                     pargc, argv, tunnelArgIndex);
+
+  sprintf (localPortStr, "%hu", (unsigned short)localPort);
+  sprintf (remotePortStr, "%hu", (unsigned short)remotePort);
+
+  if (!fillCmdPattern(cmd, pattern, remoteHost, remotePortStr, localPortStr))
+    return False;
+
+  if (!runCommand(cmd))
+    return False;
+
+  return True;
+}
+
+static void
+processTunnelArgs (char **remoteHost, int *remotePort, int localPort,
+                   int *pargc, char **argv, int tunnelArgIndex)
+{
+  char *pdisplay;
+  int port;
+
+  if (tunnelArgIndex >= *pargc - 1)
+    usage();
+
+  pdisplay = strchr(argv[*pargc - 1], ':');
+  if (pdisplay == NULL || pdisplay == argv[*pargc - 1])
+    usage();
+
+  *pdisplay++ = '\0';
+  if (strspn(pdisplay, "0123456789") != strlen(pdisplay))
+    usage();
+
+  *remotePort = atoi(pdisplay);
+  if (*remotePort < 100)
+    *remotePort += SERVER_PORT_OFFSET;
+
+  sprintf(lastArgv, "localhost:%hu", (unsigned short)localPort);
+
+  *remoteHost = argv[*pargc - 1];
+  argv[*pargc - 1] = lastArgv;
+
+  removeArgs(pargc, argv, tunnelArgIndex, 1);
+}
+
+static char *
+getCmdPattern (void)
+{
+  struct stat st;
+  char *pattern;
+
+  pattern = getenv("VNC_TUNNEL_CMD");
+  if (pattern == NULL) {
+    if ( stat(DEFAULT_SSH_CMD, &st) != 0 ||
+         !(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) ) {
+      fprintf(stderr, "%s: Cannot establish SSH tunnel: missing %s binary.\n",
+              programName, DEFAULT_SSH_CMD);
+      return NULL;
+    }
+    pattern = DEFAULT_TUNNEL_CMD;
+  }
+
+  return pattern;
+}
+
+/* Note: in fillCmdPattern() result points to a 1024-byte buffer */
+
+static Bool
+fillCmdPattern (char *result, char *pattern, char *remoteHost,
+                char *remotePort, char *localPort)
+{
+  int i, j;
+  Bool H_found = False, R_found = False, L_found = False;
+
+  for (i=0, j=0; pattern[i] && j<1023; i++, j++) {
+    if (pattern[i] == '%') {
+      switch (pattern[++i]) {
+      case 'H':
+        strncpy(&result[j], remoteHost, 1024 - j);
+        j += strlen(remoteHost) - 1;
+        H_found = True;
+        continue;
+      case 'R':
+        strncpy(&result[j], remotePort, 1024 - j);
+        j += strlen(remotePort) - 1;
+        R_found = True;
+        continue;
+      case 'L':
+        strncpy(&result[j], localPort, 1024 - j);
+        j += strlen(localPort) - 1;
+        L_found = True;
+        continue;
+      case '\0':
+        i--;
+        continue;
+      }
+    }
+    result[j] = pattern[i];
+  }
+
+  if (pattern[i]) {
+    fprintf(stderr, "%s: Tunneling command is too long.\n", programName);
+    return False;
+  }
+
+  if (!H_found || !R_found || !L_found) {
+    fprintf(stderr, "%s: %%H, %%R or %%L absent in tunneling command.\n",
+            programName);
+    return False;
+  }
+
+  result[j] = '\0';
+  return True;
+}
+
+static Bool
+runCommand (char *cmd)
+{
+  if (system(cmd) != 0) {
+    fprintf(stderr, "%s: Tunneling command failed: %s.\n",
+            programName, cmd);
+    return False;
+  }
+  return True;
+}
+
Index: vnc_unixsrc/vncviewer/vncviewer.c
diff -u vnc_unixsrc/vncviewer/vncviewer.c:1.1.1.1 vnc_unixsrc/vncviewer/vncviewer.c:1.3
--- vnc_unixsrc/vncviewer/vncviewer.c:1.1.1.1	Sun Jun 11 20:00:53 2000
+++ vnc_unixsrc/vncviewer/vncviewer.c	Tue Aug  8 16:34:06 2000
@@ -35,18 +35,24 @@
   int i;
   programName = argv[0];
 
-  /* 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.  We must test for this option before invoking any Xt
-     functions - this is because we deal with each incoming connection by
-     forking, and Xt doesn't seem to cope with forking very well.  When a
-     successful incoming connection has been accepted,
-     listenForIncomingConnections() returns, setting the listenSpecified
-     flag. */
+  /* 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 option is
+     useful to create connection tunneled via SSH port forwarding. We
+     must test for these options 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(&argc, argv, i);
+      break;
+    }
+    if (strcmp(argv[i], "-tunnel") == 0) {
+      if (!createTunnel(&argc, argv, i))
+        exit(1);
       break;
     }
   }
Index: vnc_unixsrc/vncviewer/vncviewer.h
diff -u vnc_unixsrc/vncviewer/vncviewer.h:1.1.1.1 vnc_unixsrc/vncviewer/vncviewer.h:1.3
--- vnc_unixsrc/vncviewer/vncviewer.h:1.1.1.1	Sun Jun 11 20:00:53 2000
+++ vnc_unixsrc/vncviewer/vncviewer.h	Tue Aug  8 16:34:06 2000
@@ -50,9 +50,14 @@
 
 #define FLASH_PORT_OFFSET 5400
 #define LISTEN_PORT_OFFSET 5500
+#define TUNNEL_PORT_OFFSET 5500
 #define SERVER_PORT_OFFSET 5900
 
+#define DEFAULT_SSH_CMD "/usr/bin/ssh"
+#define DEFAULT_TUNNEL_CMD DEFAULT_SSH_CMD \
+                           " -f -L %L:%H:%R %H sleep 20"
 
+
 /* argsresources.c */
 
 typedef struct {
@@ -100,6 +105,8 @@
 extern XrmOptionDescRec cmdLineOptions[];
 extern int numCmdLineOptions;
 
+extern void removeArgs(int *argc, char** argv, int idx, int nargs);
+extern void usage(void);
 extern void GetArgsAndResources(int argc, char **argv);
 
 /* colour.c */
@@ -214,6 +221,7 @@
 
 extern Bool ReadFromRFBServer(char *out, unsigned int n);
 extern Bool WriteExact(int sock, char *buf, int n);
+extern int FindFreeTcpPort(void);
 extern int ListenAtTcpPort(int port);
 extern int ConnectToTcpAddr(unsigned int host, int port);
 extern int AcceptTcpConnection(int listenSock);
@@ -221,6 +229,10 @@
 
 extern int StringToIPAddr(const char *str, unsigned int *addr);
 extern Bool SameMachine(int sock);
+
+/* tunnel.c */
+
+extern Bool createTunnel(int *argc, char **argv, int tunnelArgIndex);
 
 /* vncviewer.c */
 
