added libvnc/ with RealVNC compatibility fix
[presencevnc] / libvnc / libvncserver / main.c
diff --git a/libvnc/libvncserver/main.c b/libvnc/libvncserver/main.c
new file mode 100644 (file)
index 0000000..52bd4e7
--- /dev/null
@@ -0,0 +1,1151 @@
+/*
+ *  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()
+{
+       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,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;
+            }
+           LOCK(cl->updateMutex);
+           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);
+        rfbSendFramebufferUpdate(cl, updateRegion);
+       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;
+
+       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);
+
+        if (cl->sock == -1) {
+            /* Client has disconnected. */
+            break;
+        }
+    }
+
+    /* 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->getKeyboardLedStateHook = 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);
+
+  rfbRRECleanup(screen);
+  rfbCoRRECleanup(screen);
+  rfbUltraCleanup(screen);
+#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;
+  struct timeval tv;
+  rfbBool result=FALSE;
+  extern rfbClientIteratorPtr
+    rfbGetClientIteratorWithClosed(rfbScreenInfoPtr rfbScreen);
+
+  if(usec<0)
+    usec=screen->deferUpdateTime*1000;
+
+  rfbCheckFds(screen,usec);
+  rfbHttpCheckFds(screen);
+#ifdef CORBA
+  corbaCheckFds(screen);
+#endif
+
+  i = rfbGetClientIteratorWithClosed(screen);
+  cl=rfbClientIteratorHead(i);
+  while(cl) {
+    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;
+        }
+      }
+    }
+    clPrev=cl;
+    cl=rfbClientIteratorNext(i);
+    if(clPrev->sock==-1) {
+      rfbClientConnectionGone(clPrev);
+      result=TRUE;
+    }
+  }
+  rfbReleaseClientIterator(i);
+
+  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);
+}