Commit 0fc57f20 authored by dscho's avatar dscho

first alpha version of libvncclient

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