1
0
mirror of https://github.com/KDE/krfb synced 2026-07-01 15:51:18 -07:00
Files
krfb/libvncserver/main.c
George Kiagiadakis 9f56633c0b Sync with libvncserver from git (git describe version: 0.9.8-10-g17ce0c5).
My patches in this local fork have been merged upstream, but they
have been merged after 0.9.8 and 0.9.9 hasn't been released yet,
so we cannot yet switch back to finding libvncserver externally.

So I am syncing now with basically what is 0.9.8 + my patches and a few bugfixes.

svn path=/trunk/KDE/kdenetwork/krfb/; revision=1258493
2011-10-11 19:12:14 +00:00

1178 lines
29 KiB
C

/*
* This file is called main.c, because it contains most of the new functions
* for use with LibVNCServer.
*
* LibVNCServer (C) 2001 Johannes E. Schindelin <Johannes.Schindelin@gmx.de>
* Original OSXvnc (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
* Original Xvnc (C) 1999 AT&T Laboratories Cambridge.
* All Rights Reserved.
*
* see GPL (latest version) for full details
*/
#ifdef __STRICT_ANSI__
#define _BSD_SOURCE
#endif
#include "rfb/rfb.h"
#include "rfb/rfbregion.h"
#include "private.h"
#include <stdarg.h>
#include <errno.h>
#ifndef false
#define false 0
#define true -1
#endif
#ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifndef WIN32
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#endif
#include <signal.h>
#include <time.h>
static int extMutex_initialized = 0;
static int logMutex_initialized = 0;
#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
static MUTEX(logMutex);
static MUTEX(extMutex);
#endif
static int rfbEnableLogging=1;
#ifdef LIBVNCSERVER_WORDS_BIGENDIAN
char rfbEndianTest = (1==0);
#else
char rfbEndianTest = (1==1);
#endif
/*
* Protocol extensions
*/
static rfbProtocolExtension* rfbExtensionHead = NULL;
/*
* This method registers a list of new extensions.
* It avoids same extension getting registered multiple times.
* The order is not preserved if multiple extensions are
* registered at one-go.
*/
void
rfbRegisterProtocolExtension(rfbProtocolExtension* extension)
{
rfbProtocolExtension *head = rfbExtensionHead, *next = NULL;
if(extension == NULL)
return;
next = extension->next;
if (! extMutex_initialized) {
INIT_MUTEX(extMutex);
extMutex_initialized = 1;
}
LOCK(extMutex);
while(head != NULL) {
if(head == extension) {
UNLOCK(extMutex);
rfbRegisterProtocolExtension(next);
return;
}
head = head->next;
}
extension->next = rfbExtensionHead;
rfbExtensionHead = extension;
UNLOCK(extMutex);
rfbRegisterProtocolExtension(next);
}
/*
* This method unregisters a list of extensions.
* These extensions won't be available for any new
* client connection.
*/
void
rfbUnregisterProtocolExtension(rfbProtocolExtension* extension)
{
rfbProtocolExtension *cur = NULL, *pre = NULL;
if(extension == NULL)
return;
if (! extMutex_initialized) {
INIT_MUTEX(extMutex);
extMutex_initialized = 1;
}
LOCK(extMutex);
if(rfbExtensionHead == extension) {
rfbExtensionHead = rfbExtensionHead->next;
UNLOCK(extMutex);
rfbUnregisterProtocolExtension(extension->next);
return;
}
cur = pre = rfbExtensionHead;
while(cur) {
if(cur == extension) {
pre->next = cur->next;
break;
}
pre = cur;
cur = cur->next;
}
UNLOCK(extMutex);
rfbUnregisterProtocolExtension(extension->next);
}
rfbProtocolExtension* rfbGetExtensionIterator()
{
if (! extMutex_initialized) {
INIT_MUTEX(extMutex);
extMutex_initialized = 1;
}
LOCK(extMutex);
return rfbExtensionHead;
}
void rfbReleaseExtensionIterator()
{
UNLOCK(extMutex);
}
rfbBool rfbEnableExtension(rfbClientPtr cl, rfbProtocolExtension* extension,
void* data)
{
rfbExtensionData* extData;
/* make sure extension is not yet enabled. */
for(extData = cl->extensions; extData; extData = extData->next)
if(extData->extension == extension)
return FALSE;
extData = calloc(sizeof(rfbExtensionData),1);
extData->extension = extension;
extData->data = data;
extData->next = cl->extensions;
cl->extensions = extData;
return TRUE;
}
rfbBool rfbDisableExtension(rfbClientPtr cl, rfbProtocolExtension* extension)
{
rfbExtensionData* extData;
rfbExtensionData* prevData = NULL;
for(extData = cl->extensions; extData; extData = extData->next) {
if(extData->extension == extension) {
if(extData->data)
free(extData->data);
if(prevData == NULL)
cl->extensions = extData->next;
else
prevData->next = extData->next;
return TRUE;
}
prevData = extData;
}
return FALSE;
}
void* rfbGetExtensionClientData(rfbClientPtr cl, rfbProtocolExtension* extension)
{
rfbExtensionData* data = cl->extensions;
while(data && data->extension != extension)
data = data->next;
if(data == NULL) {
rfbLog("Extension is not enabled !\n");
/* rfbCloseClient(cl); */
return NULL;
}
return data->data;
}
/*
* Logging
*/
void rfbLogEnable(int enabled) {
rfbEnableLogging=enabled;
}
/*
* rfbLog prints a time-stamped message to the log file (stderr).
*/
static void
rfbDefaultLog(const char *format, ...)
{
va_list args;
char buf[256];
time_t log_clock;
if(!rfbEnableLogging)
return;
if (! logMutex_initialized) {
INIT_MUTEX(logMutex);
logMutex_initialized = 1;
}
LOCK(logMutex);
va_start(args, format);
time(&log_clock);
strftime(buf, 255, "%d/%m/%Y %X ", localtime(&log_clock));
fprintf(stderr, "%s", buf);
vfprintf(stderr, format, args);
fflush(stderr);
va_end(args);
UNLOCK(logMutex);
}
rfbLogProc rfbLog=rfbDefaultLog;
rfbLogProc rfbErr=rfbDefaultLog;
void rfbLogPerror(const char *str)
{
rfbErr("%s: %s\n", str, strerror(errno));
}
void rfbScheduleCopyRegion(rfbScreenInfoPtr rfbScreen,sraRegionPtr copyRegion,int dx,int dy)
{
rfbClientIteratorPtr iterator;
rfbClientPtr cl;
iterator=rfbGetClientIterator(rfbScreen);
while((cl=rfbClientIteratorNext(iterator))) {
LOCK(cl->updateMutex);
if(cl->useCopyRect) {
sraRegionPtr modifiedRegionBackup;
if(!sraRgnEmpty(cl->copyRegion)) {
if(cl->copyDX!=dx || cl->copyDY!=dy) {
/* if a copyRegion was not yet executed, treat it as a
* modifiedRegion. The idea: in this case it could be
* source of the new copyRect or modified anyway. */
sraRgnOr(cl->modifiedRegion,cl->copyRegion);
sraRgnMakeEmpty(cl->copyRegion);
} else {
/* we have to set the intersection of the source of the copy
* and the old copy to modified. */
modifiedRegionBackup=sraRgnCreateRgn(copyRegion);
sraRgnOffset(modifiedRegionBackup,-dx,-dy);
sraRgnAnd(modifiedRegionBackup,cl->copyRegion);
sraRgnOr(cl->modifiedRegion,modifiedRegionBackup);
sraRgnDestroy(modifiedRegionBackup);
}
}
sraRgnOr(cl->copyRegion,copyRegion);
cl->copyDX = dx;
cl->copyDY = dy;
/* if there were modified regions, which are now copied,
* mark them as modified, because the source of these can be overlapped
* either by new modified or now copied regions. */
modifiedRegionBackup=sraRgnCreateRgn(cl->modifiedRegion);
sraRgnOffset(modifiedRegionBackup,dx,dy);
sraRgnAnd(modifiedRegionBackup,cl->copyRegion);
sraRgnOr(cl->modifiedRegion,modifiedRegionBackup);
sraRgnDestroy(modifiedRegionBackup);
if(!cl->enableCursorShapeUpdates) {
/*
* n.b. (dx, dy) is the vector pointing in the direction the
* copyrect displacement will take place. copyRegion is the
* destination rectangle (say), not the source rectangle.
*/
sraRegionPtr cursorRegion;
int x = cl->cursorX - cl->screen->cursor->xhot;
int y = cl->cursorY - cl->screen->cursor->yhot;
int w = cl->screen->cursor->width;
int h = cl->screen->cursor->height;
cursorRegion = sraRgnCreateRect(x, y, x + w, y + h);
sraRgnAnd(cursorRegion, cl->copyRegion);
if(!sraRgnEmpty(cursorRegion)) {
/*
* current cursor rect overlaps with the copy region *dest*,
* mark it as modified since we won't copy-rect stuff to it.
*/
sraRgnOr(cl->modifiedRegion, cursorRegion);
}
sraRgnDestroy(cursorRegion);
cursorRegion = sraRgnCreateRect(x, y, x + w, y + h);
/* displace it to check for overlap with copy region source: */
sraRgnOffset(cursorRegion, dx, dy);
sraRgnAnd(cursorRegion, cl->copyRegion);
if(!sraRgnEmpty(cursorRegion)) {
/*
* current cursor rect overlaps with the copy region *source*,
* mark the *displaced* cursorRegion as modified since we
* won't copyrect stuff to it.
*/
sraRgnOr(cl->modifiedRegion, cursorRegion);
}
sraRgnDestroy(cursorRegion);
}
} else {
sraRgnOr(cl->modifiedRegion,copyRegion);
}
TSIGNAL(cl->updateCond);
UNLOCK(cl->updateMutex);
}
rfbReleaseClientIterator(iterator);
}
void rfbDoCopyRegion(rfbScreenInfoPtr screen,sraRegionPtr copyRegion,int dx,int dy)
{
sraRectangleIterator* i;
sraRect rect;
int j,widthInBytes,bpp=screen->serverFormat.bitsPerPixel/8,
rowstride=screen->paddedWidthInBytes;
char *in,*out;
/* copy it, really */
i = sraRgnGetReverseIterator(copyRegion,dx<0,dy<0);
while(sraRgnIteratorNext(i,&rect)) {
widthInBytes = (rect.x2-rect.x1)*bpp;
out = screen->frameBuffer+rect.x1*bpp+rect.y1*rowstride;
in = screen->frameBuffer+(rect.x1-dx)*bpp+(rect.y1-dy)*rowstride;
if(dy<0)
for(j=rect.y1;j<rect.y2;j++,out+=rowstride,in+=rowstride)
memmove(out,in,widthInBytes);
else {
out += rowstride*(rect.y2-rect.y1-1);
in += rowstride*(rect.y2-rect.y1-1);
for(j=rect.y2-1;j>=rect.y1;j--,out-=rowstride,in-=rowstride)
memmove(out,in,widthInBytes);
}
}
sraRgnReleaseIterator(i);
rfbScheduleCopyRegion(screen,copyRegion,dx,dy);
}
void rfbDoCopyRect(rfbScreenInfoPtr screen,int x1,int y1,int x2,int y2,int dx,int dy)
{
sraRegionPtr region = sraRgnCreateRect(x1,y1,x2,y2);
rfbDoCopyRegion(screen,region,dx,dy);
sraRgnDestroy(region);
}
void rfbScheduleCopyRect(rfbScreenInfoPtr screen,int x1,int y1,int x2,int y2,int dx,int dy)
{
sraRegionPtr region = sraRgnCreateRect(x1,y1,x2,y2);
rfbScheduleCopyRegion(screen,region,dx,dy);
sraRgnDestroy(region);
}
void rfbMarkRegionAsModified(rfbScreenInfoPtr screen,sraRegionPtr modRegion)
{
rfbClientIteratorPtr iterator;
rfbClientPtr cl;
iterator=rfbGetClientIterator(screen);
while((cl=rfbClientIteratorNext(iterator))) {
LOCK(cl->updateMutex);
sraRgnOr(cl->modifiedRegion,modRegion);
TSIGNAL(cl->updateCond);
UNLOCK(cl->updateMutex);
}
rfbReleaseClientIterator(iterator);
}
void rfbScaledScreenUpdate(rfbScreenInfoPtr screen, int x1, int y1, int x2, int y2);
void rfbMarkRectAsModified(rfbScreenInfoPtr screen,int x1,int y1,int x2,int y2)
{
sraRegionPtr region;
int i;
if(x1>x2) { i=x1; x1=x2; x2=i; }
if(x1<0) x1=0;
if(x2>screen->width) x2=screen->width;
if(x1==x2) return;
if(y1>y2) { i=y1; y1=y2; y2=i; }
if(y1<0) y1=0;
if(y2>screen->height) y2=screen->height;
if(y1==y2) return;
/* update scaled copies for this rectangle */
rfbScaledScreenUpdate(screen,x1,y1,x2,y2);
region = sraRgnCreateRect(x1,y1,x2,y2);
rfbMarkRegionAsModified(screen,region);
sraRgnDestroy(region);
}
#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
#include <unistd.h>
static void *
clientOutput(void *data)
{
rfbClientPtr cl = (rfbClientPtr)data;
rfbBool haveUpdate;
sraRegion* updateRegion;
while (1) {
haveUpdate = false;
while (!haveUpdate) {
if (cl->sock == -1) {
/* Client has disconnected. */
return NULL;
}
if (cl->state != RFB_NORMAL || cl->onHold) {
/* just sleep until things get normal */
usleep(cl->screen->deferUpdateTime * 1000);
continue;
}
LOCK(cl->updateMutex);
if (sraRgnEmpty(cl->requestedRegion)) {
; /* always require a FB Update Request (otherwise can crash.) */
} else {
haveUpdate = FB_UPDATE_PENDING(cl);
if(!haveUpdate) {
updateRegion = sraRgnCreateRgn(cl->modifiedRegion);
haveUpdate = sraRgnAnd(updateRegion,cl->requestedRegion);
sraRgnDestroy(updateRegion);
}
}
if (!haveUpdate) {
WAIT(cl->updateCond, cl->updateMutex);
}
UNLOCK(cl->updateMutex);
}
/* OK, now, to save bandwidth, wait a little while for more
updates to come along. */
usleep(cl->screen->deferUpdateTime * 1000);
/* Now, get the region we're going to update, and remove
it from cl->modifiedRegion _before_ we send the update.
That way, if anything that overlaps the region we're sending
is updated, we'll be sure to do another update later. */
LOCK(cl->updateMutex);
updateRegion = sraRgnCreateRgn(cl->modifiedRegion);
UNLOCK(cl->updateMutex);
/* Now actually send the update. */
rfbIncrClientRef(cl);
LOCK(cl->sendMutex);
rfbSendFramebufferUpdate(cl, updateRegion);
UNLOCK(cl->sendMutex);
rfbDecrClientRef(cl);
sraRgnDestroy(updateRegion);
}
/* Not reached. */
return NULL;
}
static void *
clientInput(void *data)
{
rfbClientPtr cl = (rfbClientPtr)data;
pthread_t output_thread;
pthread_create(&output_thread, NULL, clientOutput, (void *)cl);
while (1) {
fd_set rfds, wfds, efds;
struct timeval tv;
int n;
if (cl->sock == -1) {
/* Client has disconnected. */
break;
}
FD_ZERO(&rfds);
FD_SET(cl->sock, &rfds);
FD_ZERO(&efds);
FD_SET(cl->sock, &efds);
/* Are we transferring a file in the background? */
FD_ZERO(&wfds);
if ((cl->fileTransfer.fd!=-1) && (cl->fileTransfer.sending==1))
FD_SET(cl->sock, &wfds);
tv.tv_sec = 60; /* 1 minute */
tv.tv_usec = 0;
n = select(cl->sock + 1, &rfds, &wfds, &efds, &tv);
if (n < 0) {
rfbLogPerror("ReadExact: select");
break;
}
if (n == 0) /* timeout */
{
rfbSendFileTransferChunk(cl);
continue;
}
/* We have some space on the transmit queue, send some data */
if (FD_ISSET(cl->sock, &wfds))
rfbSendFileTransferChunk(cl);
if (FD_ISSET(cl->sock, &rfds) || FD_ISSET(cl->sock, &efds))
rfbProcessClientMessage(cl);
}
/* Get rid of the output thread. */
LOCK(cl->updateMutex);
TSIGNAL(cl->updateCond);
UNLOCK(cl->updateMutex);
IF_PTHREADS(pthread_join(output_thread, NULL));
rfbClientConnectionGone(cl);
return NULL;
}
static void*
listenerRun(void *data)
{
rfbScreenInfoPtr screen=(rfbScreenInfoPtr)data;
int client_fd;
struct sockaddr_in peer;
rfbClientPtr cl;
socklen_t len;
len = sizeof(peer);
/* TODO: this thread wont die by restarting the server */
/* TODO: HTTP is not handled */
while ((client_fd = accept(screen->listenSock,
(struct sockaddr*)&peer, &len)) >= 0) {
cl = rfbNewClient(screen,client_fd);
len = sizeof(peer);
if (cl && !cl->onHold )
rfbStartOnHoldClient(cl);
}
return(NULL);
}
void
rfbStartOnHoldClient(rfbClientPtr cl)
{
pthread_create(&cl->client_thread, NULL, clientInput, (void *)cl);
}
#else
void
rfbStartOnHoldClient(rfbClientPtr cl)
{
cl->onHold = FALSE;
}
#endif
void
rfbRefuseOnHoldClient(rfbClientPtr cl)
{
rfbCloseClient(cl);
rfbClientConnectionGone(cl);
}
static void
rfbDefaultKbdAddEvent(rfbBool down, rfbKeySym keySym, rfbClientPtr cl)
{
}
void
rfbDefaultPtrAddEvent(int buttonMask, int x, int y, rfbClientPtr cl)
{
rfbClientIteratorPtr iterator;
rfbClientPtr other_client;
rfbScreenInfoPtr s = cl->screen;
if (x != s->cursorX || y != s->cursorY) {
LOCK(s->cursorMutex);
s->cursorX = x;
s->cursorY = y;
UNLOCK(s->cursorMutex);
/* The cursor was moved by this client, so don't send CursorPos. */
if (cl->enableCursorPosUpdates)
cl->cursorWasMoved = FALSE;
/* But inform all remaining clients about this cursor movement. */
iterator = rfbGetClientIterator(s);
while ((other_client = rfbClientIteratorNext(iterator)) != NULL) {
if (other_client != cl && other_client->enableCursorPosUpdates) {
other_client->cursorWasMoved = TRUE;
}
}
rfbReleaseClientIterator(iterator);
}
}
static void rfbDefaultSetXCutText(char* text, int len, rfbClientPtr cl)
{
}
/* TODO: add a nice VNC or RFB cursor */
#if defined(WIN32) || defined(sparc) || !defined(NO_STRICT_ANSI)
static rfbCursor myCursor =
{
FALSE, FALSE, FALSE, FALSE,
(unsigned char*)"\000\102\044\030\044\102\000",
(unsigned char*)"\347\347\176\074\176\347\347",
8, 7, 3, 3,
0, 0, 0,
0xffff, 0xffff, 0xffff,
NULL
};
#else
static rfbCursor myCursor =
{
cleanup: FALSE,
cleanupSource: FALSE,
cleanupMask: FALSE,
cleanupRichSource: FALSE,
source: "\000\102\044\030\044\102\000",
mask: "\347\347\176\074\176\347\347",
width: 8, height: 7, xhot: 3, yhot: 3,
foreRed: 0, foreGreen: 0, foreBlue: 0,
backRed: 0xffff, backGreen: 0xffff, backBlue: 0xffff,
richSource: NULL
};
#endif
static rfbCursorPtr rfbDefaultGetCursorPtr(rfbClientPtr cl)
{
return(cl->screen->cursor);
}
/* response is cl->authChallenge vncEncrypted with passwd */
static rfbBool rfbDefaultPasswordCheck(rfbClientPtr cl,const char* response,int len)
{
int i;
char *passwd=rfbDecryptPasswdFromFile(cl->screen->authPasswdData);
if(!passwd) {
rfbErr("Couldn't read password file: %s\n",cl->screen->authPasswdData);
return(FALSE);
}
rfbEncryptBytes(cl->authChallenge, passwd);
/* Lose the password from memory */
for (i = strlen(passwd); i >= 0; i--) {
passwd[i] = '\0';
}
free(passwd);
if (memcmp(cl->authChallenge, response, len) != 0) {
rfbErr("authProcessClientMessage: authentication failed from %s\n",
cl->host);
return(FALSE);
}
return(TRUE);
}
/* for this method, authPasswdData is really a pointer to an array
of char*'s, where the last pointer is 0. */
rfbBool rfbCheckPasswordByList(rfbClientPtr cl,const char* response,int len)
{
char **passwds;
int i=0;
for(passwds=(char**)cl->screen->authPasswdData;*passwds;passwds++,i++) {
uint8_t auth_tmp[CHALLENGESIZE];
memcpy((char *)auth_tmp, (char *)cl->authChallenge, CHALLENGESIZE);
rfbEncryptBytes(auth_tmp, *passwds);
if (memcmp(auth_tmp, response, len) == 0) {
if(i>=cl->screen->authPasswdFirstViewOnly)
cl->viewOnly=TRUE;
return(TRUE);
}
}
rfbErr("authProcessClientMessage: authentication failed from %s\n",
cl->host);
return(FALSE);
}
void rfbDoNothingWithClient(rfbClientPtr cl)
{
}
static enum rfbNewClientAction rfbDefaultNewClientHook(rfbClientPtr cl)
{
return RFB_CLIENT_ACCEPT;
}
/*
* Update server's pixel format in screenInfo structure. This
* function is called from rfbGetScreen() and rfbNewFramebuffer().
*/
static void rfbInitServerFormat(rfbScreenInfoPtr screen, int bitsPerSample)
{
rfbPixelFormat* format=&screen->serverFormat;
format->bitsPerPixel = screen->bitsPerPixel;
format->depth = screen->depth;
format->bigEndian = rfbEndianTest?FALSE:TRUE;
format->trueColour = TRUE;
screen->colourMap.count = 0;
screen->colourMap.is16 = 0;
screen->colourMap.data.bytes = NULL;
if (format->bitsPerPixel == 8) {
format->redMax = 7;
format->greenMax = 7;
format->blueMax = 3;
format->redShift = 0;
format->greenShift = 3;
format->blueShift = 6;
} else {
format->redMax = (1 << bitsPerSample) - 1;
format->greenMax = (1 << bitsPerSample) - 1;
format->blueMax = (1 << bitsPerSample) - 1;
if(rfbEndianTest) {
format->redShift = 0;
format->greenShift = bitsPerSample;
format->blueShift = bitsPerSample * 2;
} else {
if(format->bitsPerPixel==8*3) {
format->redShift = bitsPerSample*2;
format->greenShift = bitsPerSample*1;
format->blueShift = 0;
} else {
format->redShift = bitsPerSample*3;
format->greenShift = bitsPerSample*2;
format->blueShift = bitsPerSample;
}
}
}
}
rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv,
int width,int height,int bitsPerSample,int samplesPerPixel,
int bytesPerPixel)
{
rfbScreenInfoPtr screen=calloc(sizeof(rfbScreenInfo),1);
if (! logMutex_initialized) {
INIT_MUTEX(logMutex);
logMutex_initialized = 1;
}
if(width&3)
rfbErr("WARNING: Width (%d) is not a multiple of 4. VncViewer has problems with that.\n",width);
screen->autoPort=FALSE;
screen->clientHead=NULL;
screen->pointerClient=NULL;
screen->port=5900;
screen->socketState=RFB_SOCKET_INIT;
screen->inetdInitDone = FALSE;
screen->inetdSock=-1;
screen->udpSock=-1;
screen->udpSockConnected=FALSE;
screen->udpPort=0;
screen->udpClient=NULL;
screen->maxFd=0;
screen->listenSock=-1;
screen->httpInitDone=FALSE;
screen->httpEnableProxyConnect=FALSE;
screen->httpPort=0;
screen->httpDir=NULL;
screen->httpListenSock=-1;
screen->httpSock=-1;
screen->desktopName = "LibVNCServer";
screen->alwaysShared = FALSE;
screen->neverShared = FALSE;
screen->dontDisconnect = FALSE;
screen->authPasswdData = NULL;
screen->authPasswdFirstViewOnly = 1;
screen->width = width;
screen->height = height;
screen->bitsPerPixel = screen->depth = 8*bytesPerPixel;
screen->passwordCheck = rfbDefaultPasswordCheck;
screen->ignoreSIGPIPE = TRUE;
/* disable progressive updating per default */
screen->progressiveSliceHeight = 0;
screen->listenInterface = htonl(INADDR_ANY);
screen->deferUpdateTime=5;
screen->maxRectsPerUpdate=50;
screen->handleEventsEagerly = FALSE;
screen->protocolMajorVersion = rfbProtocolMajorVersion;
screen->protocolMinorVersion = rfbProtocolMinorVersion;
screen->permitFileTransfer = FALSE;
if(!rfbProcessArguments(screen,argc,argv)) {
free(screen);
return NULL;
}
#ifdef WIN32
{
DWORD dummy=255;
GetComputerName(screen->thisHost,&dummy);
}
#else
gethostname(screen->thisHost, 255);
#endif
screen->paddedWidthInBytes = width*bytesPerPixel;
/* format */
rfbInitServerFormat(screen, bitsPerSample);
/* cursor */
screen->cursorX=screen->cursorY=screen->underCursorBufferLen=0;
screen->underCursorBuffer=NULL;
screen->dontConvertRichCursorToXCursor = FALSE;
screen->cursor = &myCursor;
INIT_MUTEX(screen->cursorMutex);
IF_PTHREADS(screen->backgroundLoop = FALSE);
/* proc's and hook's */
screen->kbdAddEvent = rfbDefaultKbdAddEvent;
screen->kbdReleaseAllKeys = rfbDoNothingWithClient;
screen->ptrAddEvent = rfbDefaultPtrAddEvent;
screen->setXCutText = rfbDefaultSetXCutText;
screen->getCursorPtr = rfbDefaultGetCursorPtr;
screen->setTranslateFunction = rfbSetTranslateFunction;
screen->newClientHook = rfbDefaultNewClientHook;
screen->displayHook = NULL;
screen->displayFinishedHook = NULL;
screen->getKeyboardLedStateHook = NULL;
screen->xvpHook = NULL;
/* initialize client list and iterator mutex */
rfbClientListInit(screen);
return(screen);
}
/*
* Switch to another framebuffer (maybe of different size and color
* format). Clients supporting NewFBSize pseudo-encoding will change
* their local framebuffer dimensions if necessary.
* NOTE: Rich cursor data should be converted to new pixel format by
* the caller.
*/
void rfbNewFramebuffer(rfbScreenInfoPtr screen, char *framebuffer,
int width, int height,
int bitsPerSample, int samplesPerPixel,
int bytesPerPixel)
{
rfbPixelFormat old_format;
rfbBool format_changed = FALSE;
rfbClientIteratorPtr iterator;
rfbClientPtr cl;
/* Update information in the screenInfo structure */
old_format = screen->serverFormat;
if (width & 3)
rfbErr("WARNING: New width (%d) is not a multiple of 4.\n", width);
screen->width = width;
screen->height = height;
screen->bitsPerPixel = screen->depth = 8*bytesPerPixel;
screen->paddedWidthInBytes = width*bytesPerPixel;
rfbInitServerFormat(screen, bitsPerSample);
if (memcmp(&screen->serverFormat, &old_format,
sizeof(rfbPixelFormat)) != 0) {
format_changed = TRUE;
}
screen->frameBuffer = framebuffer;
/* Adjust pointer position if necessary */
if (screen->cursorX >= width)
screen->cursorX = width - 1;
if (screen->cursorY >= height)
screen->cursorY = height - 1;
/* For each client: */
iterator = rfbGetClientIterator(screen);
while ((cl = rfbClientIteratorNext(iterator)) != NULL) {
/* Re-install color translation tables if necessary */
if (format_changed)
screen->setTranslateFunction(cl);
/* Mark the screen contents as changed, and schedule sending
NewFBSize message if supported by this client. */
LOCK(cl->updateMutex);
sraRgnDestroy(cl->modifiedRegion);
cl->modifiedRegion = sraRgnCreateRect(0, 0, width, height);
sraRgnMakeEmpty(cl->copyRegion);
cl->copyDX = 0;
cl->copyDY = 0;
if (cl->useNewFBSize)
cl->newFBSizePending = TRUE;
TSIGNAL(cl->updateCond);
UNLOCK(cl->updateMutex);
}
rfbReleaseClientIterator(iterator);
}
/* hang up on all clients and free all reserved memory */
void rfbScreenCleanup(rfbScreenInfoPtr screen)
{
rfbClientIteratorPtr i=rfbGetClientIterator(screen);
rfbClientPtr cl,cl1=rfbClientIteratorNext(i);
while(cl1) {
cl=rfbClientIteratorNext(i);
rfbClientConnectionGone(cl1);
cl1=cl;
}
rfbReleaseClientIterator(i);
#define FREE_IF(x) if(screen->x) free(screen->x)
FREE_IF(colourMap.data.bytes);
FREE_IF(underCursorBuffer);
TINI_MUTEX(screen->cursorMutex);
if(screen->cursor && screen->cursor->cleanup)
rfbFreeCursor(screen->cursor);
#ifdef LIBVNCSERVER_HAVE_LIBZ
rfbZlibCleanup(screen);
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
rfbTightCleanup(screen);
#endif
/* free all 'scaled' versions of this screen */
while (screen->scaledScreenNext!=NULL)
{
rfbScreenInfoPtr ptr;
ptr = screen->scaledScreenNext;
screen->scaledScreenNext = ptr->scaledScreenNext;
free(ptr->frameBuffer);
free(ptr);
}
#endif
free(screen);
}
void rfbInitServer(rfbScreenInfoPtr screen)
{
#ifdef WIN32
WSADATA trash;
WSAStartup(MAKEWORD(2,2),&trash);
#endif
rfbInitSockets(screen);
rfbHttpInitSockets(screen);
#ifndef __MINGW32__
if(screen->ignoreSIGPIPE)
signal(SIGPIPE,SIG_IGN);
#endif
}
void rfbShutdownServer(rfbScreenInfoPtr screen,rfbBool disconnectClients) {
if(disconnectClients) {
rfbClientPtr cl;
rfbClientIteratorPtr iter = rfbGetClientIterator(screen);
while( (cl = rfbClientIteratorNext(iter)) )
if (cl->sock > -1)
/* we don't care about maxfd here, because the server goes away */
rfbCloseClient(cl);
rfbReleaseClientIterator(iter);
}
rfbShutdownSockets(screen);
rfbHttpShutdownSockets(screen);
}
#ifndef LIBVNCSERVER_HAVE_GETTIMEOFDAY
#include <fcntl.h>
#include <conio.h>
#include <sys/timeb.h>
void gettimeofday(struct timeval* tv,char* dummy)
{
SYSTEMTIME t;
GetSystemTime(&t);
tv->tv_sec=t.wHour*3600+t.wMinute*60+t.wSecond;
tv->tv_usec=t.wMilliseconds*1000;
}
#endif
rfbBool
rfbProcessEvents(rfbScreenInfoPtr screen,long usec)
{
rfbClientIteratorPtr i;
rfbClientPtr cl,clPrev;
rfbBool result=FALSE;
extern rfbClientIteratorPtr
rfbGetClientIteratorWithClosed(rfbScreenInfoPtr rfbScreen);
if(usec<0)
usec=screen->deferUpdateTime*1000;
rfbCheckFds(screen,usec);
rfbHttpCheckFds(screen);
i = rfbGetClientIteratorWithClosed(screen);
cl=rfbClientIteratorHead(i);
while(cl) {
result = rfbUpdateClient(cl);
clPrev=cl;
cl=rfbClientIteratorNext(i);
if(clPrev->sock==-1) {
rfbClientConnectionGone(clPrev);
result=TRUE;
}
}
rfbReleaseClientIterator(i);
return result;
}
rfbBool
rfbUpdateClient(rfbClientPtr cl)
{
struct timeval tv;
rfbBool result=FALSE;
rfbScreenInfoPtr screen = cl->screen;
if (cl->sock >= 0 && !cl->onHold && FB_UPDATE_PENDING(cl) &&
!sraRgnEmpty(cl->requestedRegion)) {
result=TRUE;
if(screen->deferUpdateTime == 0) {
rfbSendFramebufferUpdate(cl,cl->modifiedRegion);
} else if(cl->startDeferring.tv_usec == 0) {
gettimeofday(&cl->startDeferring,NULL);
if(cl->startDeferring.tv_usec == 0)
cl->startDeferring.tv_usec++;
} else {
gettimeofday(&tv,NULL);
if(tv.tv_sec < cl->startDeferring.tv_sec /* at midnight */
|| ((tv.tv_sec-cl->startDeferring.tv_sec)*1000
+(tv.tv_usec-cl->startDeferring.tv_usec)/1000)
> screen->deferUpdateTime) {
cl->startDeferring.tv_usec = 0;
rfbSendFramebufferUpdate(cl,cl->modifiedRegion);
}
}
}
if (!cl->viewOnly && cl->lastPtrX >= 0) {
if(cl->startPtrDeferring.tv_usec == 0) {
gettimeofday(&cl->startPtrDeferring,NULL);
if(cl->startPtrDeferring.tv_usec == 0)
cl->startPtrDeferring.tv_usec++;
} else {
struct timeval tv;
gettimeofday(&tv,NULL);
if(tv.tv_sec < cl->startPtrDeferring.tv_sec /* at midnight */
|| ((tv.tv_sec-cl->startPtrDeferring.tv_sec)*1000
+(tv.tv_usec-cl->startPtrDeferring.tv_usec)/1000)
> cl->screen->deferPtrUpdateTime) {
cl->startPtrDeferring.tv_usec = 0;
cl->screen->ptrAddEvent(cl->lastPtrButtons,
cl->lastPtrX,
cl->lastPtrY, cl);
cl->lastPtrX = -1;
}
}
}
return result;
}
rfbBool rfbIsActive(rfbScreenInfoPtr screenInfo) {
return screenInfo->socketState!=RFB_SOCKET_SHUTDOWN || screenInfo->clientHead!=NULL;
}
void rfbRunEventLoop(rfbScreenInfoPtr screen, long usec, rfbBool runInBackground)
{
if(runInBackground) {
#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
pthread_t listener_thread;
screen->backgroundLoop = TRUE;
pthread_create(&listener_thread, NULL, listenerRun, screen);
return;
#else
rfbErr("Can't run in background, because I don't have PThreads!\n");
return;
#endif
}
if(usec<0)
usec=screen->deferUpdateTime*1000;
while(rfbIsActive(screen))
rfbProcessEvents(screen,usec);
}