/*
* Copyright (C) 2002 RealVNC Ltd.
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
*
* This is free software; you can redistribute it and/or modify
* 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 "vncviewer.h"
#include "rfb/vncauth.h"
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);
static Bool HandleCoRRE8(int rx, int ry, int rw, int rh);
static Bool HandleCoRRE16(int rx, int ry, int rw, int rh);
static Bool HandleCoRRE32(int rx, int ry, int rw, int rh);
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);
char *desktopName;
rfbPixelFormat myFormat;
Bool pendingFormatChange = False;
/*
* Macro to compare pixel formats.
*/
#define PF_EQ(x,y) \
((x.bitsPerPixel == y.bitsPerPixel) && \
(x.depth == y.depth) && \
((x.bigEndian == y.bigEndian) || (x.bitsPerPixel == 8)) && \
(x.trueColour == y.trueColour) && \
(!x.trueColour || ((x.redMax == y.redMax) && \
(x.greenMax == y.greenMax) && \
(x.blueMax == y.blueMax) && \
(x.redShift == y.redShift) && \
(x.greenShift == y.greenShift) && \
(x.blueShift == y.blueShift))))
int currentEncoding = rfbEncodingZRLE;
Bool pendingEncodingChange = False;
int supportedEncodings[] = {
rfbEncodingZRLE, rfbEncodingHextile, rfbEncodingCoRRE, rfbEncodingRRE,
rfbEncodingRaw
};
#define NUM_SUPPORTED_ENCODINGS (sizeof(supportedEncodings)/sizeof(int))
rfbServerInitMsg si;
char *serverCutText = NULL;
Bool newServerCutText = False;
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 */
#define BUFFER_SIZE (640*480)
static char buffer[BUFFER_SIZE];
/*
* InitialiseRFBConnection.
*/
Bool
InitialiseRFBConnection()
{
rfbProtocolVersionMsg pv;
int major,minor;
CARD32 authScheme, reasonLen, authResult;
char *reason;
CARD8 challenge[CHALLENGESIZE];
char *passwd;
int i;
rfbClientInitMsg ci;
if (!ReadFromRFBServer(pv, sz_rfbProtocolVersionMsg)) return False;
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 (!WriteToRFBServer(pv, sz_rfbProtocolVersionMsg)) return False;
if (!ReadFromRFBServer((char *)&authScheme, 4)) return False;
authScheme = Swap32IfLE(authScheme);
switch (authScheme) {
case rfbConnFailed:
if (!ReadFromRFBServer((char *)&reasonLen, 4)) return False;
reasonLen = Swap32IfLE(reasonLen);
reason = malloc(reasonLen);
if (!ReadFromRFBServer(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((char *)challenge, CHALLENGESIZE)) return False;
if (appData.passwordFile) {
passwd = vncDecryptPasswdFromFile(appData.passwordFile);
if (!passwd) {
fprintf(stderr,"Cannot read valid password from file \"%s\"\n",
appData.passwordFile);
return False;
}
} else if (appData.passwordDialog) {
passwd = DoPasswordDialog();
} else {
passwd = getpass("Password: ");
}
if ((!passwd) || (strlen(passwd) == 0)) {
fprintf(stderr,"Reading password failed\n");
return False;
}
if (strlen(passwd) > 8) {
passwd[8] = '\0';
}
vncEncryptBytes(challenge, passwd);
/* Lose the password from memory */
for (i = strlen(passwd); i >= 0; i--) {
passwd[i] = '\0';
}
if (!WriteToRFBServer((char *)challenge, CHALLENGESIZE)) return False;
if (!ReadFromRFBServer((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 = (appData.shareDesktop ? 1 : 0);
if (!WriteToRFBServer((char *)&ci, sz_rfbClientInitMsg)) return False;
if (!ReadFromRFBServer((char *)&si, sz_rfbServerInitMsg)) return False;
si.framebufferWidth = Swap16IfLE(si.framebufferWidth);
si.framebufferHeight = Swap16IfLE(si.framebufferHeight);
si.format.redMax = Swap16IfLE(si.format.redMax);
si.format.greenMax = Swap16IfLE(si.format.greenMax);
si.format.blueMax = Swap16IfLE(si.format.blueMax);
si.nameLength = Swap32IfLE(si.nameLength);
desktopName = malloc(si.nameLength + 1);
if (!ReadFromRFBServer(desktopName, si.nameLength)) return False;
desktopName[si.nameLength] = 0;
fprintf(stderr,"Desktop name \"%s\"\n",desktopName);
fprintf(stderr,"Connected to VNC server, using protocol version %d.%d\n",
rfbProtocolMajorVersion, rfbProtocolMinorVersion);
fprintf(stderr,"VNC server default format:\n");
PrintPixelFormat(&si.format);
return True;
}
Bool SendFramebufferUpdateRequest(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 (!WriteToRFBServer((char *)&fur, sz_rfbFramebufferUpdateRequestMsg))
return False;
return True;
}
Bool SendSetPixelFormat()
{
rfbSetPixelFormatMsg spf;
spf.type = rfbSetPixelFormat;
spf.format = myFormat;
spf.format.redMax = Swap16IfLE(spf.format.redMax);
spf.format.greenMax = Swap16IfLE(spf.format.greenMax);
spf.format.blueMax = Swap16IfLE(spf.format.blueMax);
return WriteToRFBServer((char *)&spf, sz_rfbSetPixelFormatMsg);
}
Bool SendSetEncodings()
{
char buf[sz_rfbSetEncodingsMsg + MAX_ENCODINGS * 4];
rfbSetEncodingsMsg *se = (rfbSetEncodingsMsg *)buf;
CARD32 *encs = (CARD32 *)(&buf[sz_rfbSetEncodingsMsg]);
int len = 0;
int i;
se->type = rfbSetEncodings;
se->nEncodings = 0;
if (appData.encodingsString) {
char *encStr = appData.encodingsString;
int encStrLen;
do {
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,"hextile",encStrLen) == 0) {
encs[se->nEncodings++] = Swap32IfLE(rfbEncodingHextile);
} 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 if (strncasecmp(encStr,"zrle",encStrLen) == 0) {
encs[se->nEncodings++] = Swap32IfLE(rfbEncodingZRLE);
} else {
fprintf(stderr,"Unknown encoding '%.*s'\n",encStrLen,encStr);
}
encStr = nextEncStr;
} while (encStr && se->nEncodings < MAX_ENCODINGS);
} else {
encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCopyRect);
encs[se->nEncodings++] = Swap32IfLE(currentEncoding);
for (i = 0; i < NUM_SUPPORTED_ENCODINGS; i++) {
if (supportedEncodings[i] != currentEncoding)
encs[se->nEncodings++] = Swap32IfLE(supportedEncodings[i]);
}
}
len = sz_rfbSetEncodingsMsg + se->nEncodings * 4;
se->nEncodings = Swap16IfLE(se->nEncodings);
return WriteToRFBServer(buf, len);
}
/*
* SendPointerEvent.
*/
Bool
SendPointerEvent(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 WriteToRFBServer((char *)&pe, sz_rfbPointerEventMsg);
}
/*
* SendKeyEvent.
*/
Bool
SendKeyEvent(CARD32 key, Bool down)
{
rfbKeyEventMsg ke;
ke.type = rfbKeyEvent;
ke.down = down ? 1 : 0;
ke.key = Swap32IfLE(key);
return WriteToRFBServer((char *)&ke, sz_rfbKeyEventMsg);
}
/*
* SendClientCutText.
*/
Bool
SendClientCutText(char *str, int len)
{
rfbClientCutTextMsg cct;
if (serverCutText)
free(serverCutText);
serverCutText = NULL;
cct.type = rfbClientCutText;
cct.length = Swap32IfLE(len);
return (WriteToRFBServer((char *)&cct, sz_rfbClientCutTextMsg) &&
WriteToRFBServer(str, len));
}
void AutoSelectFormatAndEncodings()
{
/* Above 16Mbps (timing for at least a second), same machine, switch to raw
Above 10Mbps, switch to hextile
Below 4Mbps, switch to ZRLE
Above 1Mbps, switch from 8bit */
int kbitsPerSecond = KbitsPerSecond();
if (!appData.encodingsString) {
if (kbitsPerSecond > 16000 && sameMachine && TimeWaitedIn100us() >= 10000)
{
if (currentEncoding != rfbEncodingRaw) {
fprintf(stderr,"Throughput %d kbit/s - changing to Raw\n",
kbitsPerSecond);
currentEncoding = rfbEncodingRaw;
pendingEncodingChange = True;
}
} else if (kbitsPerSecond > 10000) {
if (currentEncoding != rfbEncodingHextile) {
fprintf(stderr,"Throughput %d kbit/s - changing to Hextile\n",
kbitsPerSecond);
currentEncoding = rfbEncodingHextile;
pendingEncodingChange = True;
}
} else if (kbitsPerSecond < 4000) {
if (currentEncoding != rfbEncodingZRLE) {
fprintf(stderr,"Throughput %d kbit/s - changing to ZRLE\n",
kbitsPerSecond);
currentEncoding = rfbEncodingZRLE;
pendingEncodingChange = True;
}
}
}
if (kbitsPerSecond > 1000) {
if (appData.useBGR233 && vis->class == TrueColor) {
/* only makes sense if using a TrueColor visual */
fprintf(stderr,"Throughput %d kbit/s - changing from 8bit\n",
kbitsPerSecond);
appData.useBGR233 = False;
pendingFormatChange = True;
}
}
}
int updateNeededX1, updateNeededY1, updateNeededX2, updateNeededY2;
Bool updateNeeded = False;
Bool updateDefinitelyExpected = False;
void UpdateNeeded(int x, int y, int w, int h)
{
if (!updateNeeded) {
updateNeededX1 = x;
updateNeededY1 = y;
updateNeededX2 = x+w;
updateNeededY2 = y+h;
updateNeeded = True;
} else {
if (x < updateNeededX1) updateNeededX1 = x;
if (y < updateNeededY1) updateNeededY1 = y;
if (x+w > updateNeededX2) updateNeededX2 = x+w;
if (y+h > updateNeededY2) updateNeededY2 = y+h;
}
}
Bool CheckUpdateNeeded()
{
if (updateNeeded && !updateDefinitelyExpected) {
if (!SendFramebufferUpdateRequest(updateNeededX1, updateNeededY1,
updateNeededX2-updateNeededX1,
updateNeededY2-updateNeededY1, False))
return False;
updateDefinitelyExpected = True;
updateNeeded = False;
}
return True;
}
Bool RequestNewUpdate()
{
updateDefinitelyExpected = False;
if (pendingEncodingChange) {
pendingEncodingChange = False;
if (!SendSetEncodings()) return False;
}
if (pendingFormatChange)
{
rfbPixelFormat oldFormat = myFormat;
pendingFormatChange = False;
SetMyFormatFromVisual();
if (!PF_EQ(oldFormat, myFormat)) {
if (!SendSetPixelFormat()) return False;
UpdateNeeded(0, 0, si.framebufferWidth, si.framebufferHeight);
}
}
if (updateNeeded) {
if (!CheckUpdateNeeded()) return False;
} else {
if (!SendFramebufferUpdateRequest(0, 0, si.framebufferWidth,
si.framebufferHeight, True))
return False;
}
return True;
}
/*
* HandleRFBServerMessage.
*/
Bool
HandleRFBServerMessage()
{
rfbServerToClientMsg msg;
if (!ReadFromRFBServer((char *)&msg, 1))
return False;
switch (msg.type) {
case rfbSetColourMapEntries:
{
int i;
CARD16 rgb[3];
XColor xc;
if (!ReadFromRFBServer(((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((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 (appData.autoDetect)
StartTiming();
if (!ReadFromRFBServer(((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((char *)&rect, sz_rfbFramebufferUpdateRectHeader))
return False;
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);
rect.encoding = Swap32IfLE(rect.encoding);
if ((rect.r.x + rect.r.w > si.framebufferWidth) ||
(rect.r.y + rect.r.h > 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;
}
switch (rect.encoding) {
case rfbEncodingRaw:
bytesPerLine = rect.r.w * myFormat.bitsPerPixel / 8;
linesToRead = BUFFER_SIZE / bytesPerLine;
while (rect.r.h > 0) {
if (linesToRead > rect.r.h)
linesToRead = rect.r.h;
if (!ReadFromRFBServer(buffer,bytesPerLine * linesToRead))
return False;
CopyDataToScreen(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((char *)&cr, sz_rfbCopyRect))
return False;
cr.srcX = Swap16IfLE(cr.srcX);
cr.srcY = Swap16IfLE(cr.srcY);
CopyRectangularArea(cr.srcX, cr.srcY, rect.r.w, rect.r.h, rect.r.x, rect.r.y);
break;
}
case rfbEncodingRRE:
{
switch (myFormat.bitsPerPixel) {
case 8:
if (!HandleRRE8(rect.r.x,rect.r.y,rect.r.w,rect.r.h))
return False;
break;
case 16:
if (!HandleRRE16(rect.r.x,rect.r.y,rect.r.w,rect.r.h))
return False;
break;
case 32:
if (!HandleRRE32(rect.r.x,rect.r.y,rect.r.w,rect.r.h))
return False;
break;
}
break;
}
case rfbEncodingCoRRE:
{
switch (myFormat.bitsPerPixel) {
case 8:
if (!HandleCoRRE8(rect.r.x,rect.r.y,rect.r.w,rect.r.h))
return False;
break;
case 16:
if (!HandleCoRRE16(rect.r.x,rect.r.y,rect.r.w,rect.r.h))
return False;
break;
case 32:
if (!HandleCoRRE32(rect.r.x,rect.r.y,rect.r.w,rect.r.h))
return False;
break;
}
break;
}
case rfbEncodingHextile:
{
switch (myFormat.bitsPerPixel) {
case 8:
if (!HandleHextile8(rect.r.x,rect.r.y,rect.r.w,rect.r.h))
return False;
break;
case 16:
if (!HandleHextile16(rect.r.x,rect.r.y,rect.r.w,rect.r.h))
return False;
break;
case 32:
if (!HandleHextile32(rect.r.x,rect.r.y,rect.r.w,rect.r.h))
return False;
break;
}
break;
}
case rfbEncodingZRLE:
if (!zrleDecode(rect.r.x,rect.r.y,rect.r.w,rect.r.h))
return False;
break;
default:
fprintf(stderr,"Unknown rect encoding %d\n",
(int)rect.encoding);
return False;
}
}
if (appData.autoDetect) {
StopTiming();
AutoSelectFormatAndEncodings();
}
#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 (appData.useShm)
XSync(dpy, False);
#endif
if (!RequestNewUpdate()) return False;
break;
}
case rfbBell:
XBell(dpy,100);
break;
case rfbServerCutText:
{
if (!ReadFromRFBServer(((char *)&msg) + 1,
sz_rfbServerCutTextMsg - 1))
return False;
msg.sct.length = Swap32IfLE(msg.sct.length);
if (serverCutText)
free(serverCutText);
serverCutText = malloc(msg.sct.length+1);
if (!ReadFromRFBServer(serverCutText, msg.sct.length))
return False;
serverCutText[msg.sct.length] = 0;
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) (((CARD8*)&(pix))[0] = *(ptr)++, \
((CARD8*)&(pix))[1] = *(ptr)++)
#define GET_PIXEL32(pix, ptr) (((CARD8*)&(pix))[0] = *(ptr)++, \
((CARD8*)&(pix))[1] = *(ptr)++, \
((CARD8*)&(pix))[2] = *(ptr)++, \
((CARD8*)&(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 BPP 8
#include "rre.c"
#include "corre.c"
#include "hextile.c"
#undef BPP
#define BPP 16
#include "rre.c"
#include "corre.c"
#include "hextile.c"
#undef BPP
#define BPP 32
#include "rre.c"
#include "corre.c"
#include "hextile.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");
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1