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 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 #include +#include #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 +#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 [] :\n" + "usage: %s [] [:]\n" " %s [] -listen []\n" + " %s [] -tunnel :\n" "\n" " are standard Xt options, or:\n" " -shared\n" @@ -260,7 +276,7 @@ " -owncmap\n" " -truecolour\n" " -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 #include #include +#include +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 +#include +#include +#include + +/* + * "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 */