Source of x11vnc.c


/* x11vnc.c -- a small clone of x0rfbserver by HexoNet, demonstrating the capabilities of LibVNCServer. */

/* Modified by rj@elilabs.com 15-Oct-2002 to tidy up source formatting and add comments. */


#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/extensions/XTest.h>

#ifndef NO_SHM
#include <X11/extensions/XShm.h>
#include <sys/shm.h>
#endif

#define KEYSYM_H
#undef Bool
#define KeySym RFBKeySym

#include "rfb.h"

#define min(i, j) ((i) < (j) ? (i) : (j))                         /* return the minimum of 2 numbers */

Display* dpy = 0;
int window;
int c = 0;
int blockLength = 32;
int tileX = 0;
int tileY = 0;
int tileWidth = 32;
int tileHeight = 32*2;
int dontTile = TRUE;
Bool gotInput = FALSE;
Bool viewOnly = FALSE;
Bool sharedMode = FALSE;

Bool disconnectAfterFirstClient = TRUE;

/* keyboard handling */
#define KBDDEBUG

char modifiers[0x100];
KeyCode keycodes[0x100];
KeyCode leftShiftCode;
KeyCode rightShiftCode;
KeyCode altGrCode;


void init_keycodes() {                                                 /* keyboard initialization stuff */
  KeySym key;
  KeySym *keymap;
  int i;
  int j;
  int minkey;
  int maxkey;
  int syms_per_keycode;

  memset(modifiers, -1, sizeof(modifiers));

  XDisplayKeycodes(dpy, &minkey, &maxkey);
  keymap = XGetKeyboardMapping(dpy, minkey, (maxkey - minkey + 1), &syms_per_keycode);

#ifdef KBDDEBUG
  fprintf(stderr, "minkey=%d, maxkey=%d, syms_per_keycode=%d\n", minkey, maxkey, syms_per_keycode);
#endif

  for (i = minkey; i <= maxkey; i++) {
    for (j = 0; j < syms_per_keycode; j++) {
      key = keymap[(i - minkey)*syms_per_keycode + j];

#ifdef KBDDEBUG
      fprintf(stderr, "keymap(i=0x%x, j=%d)==0x%lx\n", i, j, key);
#endif

      if (key >= ' ' && key < 0x100 && i == XKeysymToKeycode(dpy, key)) {
        keycodes[key] = i;
        modifiers[key] = j;

#ifdef KBDDEBUG
        fprintf(stderr, "key 0x%lx (%c): keycode=0x%x, modifier=%d\n", key, (char)key, i, j);
#endif

      }
    }
  }

  leftShiftCode = XKeysymToKeycode(dpy, XK_Shift_L);
  rightShiftCode = XKeysymToKeycode(dpy, XK_Shift_R);
  altGrCode = XKeysymToKeycode(dpy, XK_Mode_switch);

#ifdef KBDDEBUG
  fprintf(stderr, "leftShift=0x%x, rightShift=0x%x, altGr=0x%x\n", leftShiftCode, rightShiftCode, altGrCode);
#endif

  XFree ((char*) keymap);
}


static Bool shutDownServer = 0;


/* the hooks */

void clientGone(rfbClientPtr cl) {
  shutDownServer = -1;
}


enum rfbNewClientAction newClient(rfbClientPtr cl) {
  if (disconnectAfterFirstClient) {
    cl->clientGoneHook = clientGone;
  }
  if (viewOnly) {
    cl->clientData = (void*)-1;
  } else {
    cl->clientData = (void*)0;
  }
  return(RFB_CLIENT_ACCEPT);
}

#define LEFTSHIFT 1
#define RIGHTSHIFT 2
#define ALTGR 4
char ModifierState = 0;


/* This function adjusts the modifiers according to mod (as from modifiers) and ModifierState. */

void tweakModifiers(char mod, Bool down) {
  Bool isShift = ModifierState & (LEFTSHIFT | RIGHTSHIFT);

#ifdef KBDDEBUG
  fprintf(stderr, "tweakModifiers: 0x%x %s\n", mod, down?"down":"up");
#endif

  if (mod < 0) {
    return;
  }

  if (isShift && mod != 1) {
    if (ModifierState & LEFTSHIFT) {
      XTestFakeKeyEvent(dpy, leftShiftCode, !down, CurrentTime);
    }
    if (ModifierState & RIGHTSHIFT) {
      XTestFakeKeyEvent(dpy, rightShiftCode, !down, CurrentTime);
    }
  }

  if (!isShift && mod == 1) {
    XTestFakeKeyEvent(dpy, leftShiftCode, down, CurrentTime);
  }

  if (ModifierState & ALTGR && mod != 2) {
    XTestFakeKeyEvent(dpy, altGrCode, !down, CurrentTime);
  }

  if (!(ModifierState & ALTGR) && mod == 2) {
    XTestFakeKeyEvent(dpy, altGrCode, down, CurrentTime);
  }
}


void keyboard(Bool down, KeySym keySym, rfbClientPtr cl) {
  if ((int)(cl->clientData) == -1) {
    return;                                                         /* viewOnly */
  }

#define ADJUSTMOD(sym, state)      \
  if (keySym == sym) {             \
    if (down) {                    \
      ModifierState |= state;      \
    } else {                       \
      ModifierState &= ~state;     \
    }                              \
  }

  ADJUSTMOD(XK_Shift_L, LEFTSHIFT)
  ADJUSTMOD(XK_Shift_R, RIGHTSHIFT)
  ADJUSTMOD(XK_Mode_switch, ALTGR)

#ifdef KBDDEBUG
    fprintf(stderr, "keyboard: down=%s, keySym=0x%lx (%s), ModState=0x%x\n",
            down ? "down" : "up", keySym, XKeysymToString(keySym), ModifierState);
#endif

  if (keySym >= ' ' && keySym < 0x100) {
    KeyCode k;
    if (down) {
      tweakModifiers(modifiers[keySym], True);
    }
    k = keycodes[keySym];
    if (k != NoSymbol) {
      XTestFakeKeyEvent(dpy, k, down, CurrentTime);
      gotInput = TRUE;
    }
    if (down) {
      tweakModifiers(modifiers[keySym], False);
    }
    gotInput = TRUE;
  } else {
    KeyCode k = XKeysymToKeycode(dpy, keySym);
    if (k != NoSymbol) {
      XTestFakeKeyEvent(dpy, k, down, CurrentTime);
      gotInput = TRUE;
    }
  }
}


void mouse(                                                         /* update real X-server with vncviewer's mouse data */
           int buttonMask,                                          /* state of remote mouse buttons */
           int x,                                                  /* remote mouse x position */
           int y,                                                  /* remote mouse y position */
           rfbClientPtr cl) {                                         /* the client vncviewer's state vector */

  static int oldButtonMask = 0;                                         /* remember previous button status */
  int m;                                                         /* mask for a single mouse button */
  int i;                                                         /* loop index */

  if ((int)cl->clientData == -1) {                                 /* view only -- remote cannot control? */
    return;                                                         /* yes, skip setting mouse data */
  }

  XTestFakeMotionEvent(dpy,                                          /* stuff remote mouse data into real X-server */
                       0,                                          /* should this be -1 for multi-headed displays? */
                       x,
                       y,
                       CurrentTime);

  for (i = 0,                                                         /* for each button on the remote mouse */
         m = 1;
       i < 5;
       i++,
         m <<= 1) {

    if ((oldButtonMask & m) != (buttonMask & m)) {                 /* has button changed since we last looked at it? */

      XTestFakeButtonEvent(dpy,                                         /* yes, stuff new button data into X-server */
                           i + 1,
                           (buttonMask & m) ? True : False,
                           CurrentTime);
    }
  }

  oldButtonMask = buttonMask;                                         /* remember button status for next time */
  gotInput = TRUE;                                                 /* flag that we got some new input */
}


/* the X11 interaction */

#ifndef NO_SHM
Bool useSHM = TRUE;
XShmSegmentInfo shminfo;
#else
Bool useSHM = FALSE;
#endif


void getImage(                                                         /* get an X11 display image, shared or not */
              int bpp,                                                 /* bits per pexel */
              Display* dpy,                                         /* the X display */
              int xscreen,                                         /* and screen */
              XImage** i,                                         /* the X11 image */
              int x,                                                 /* its location */
              int y,
              int width,                                         /* and size */
              int height) {

  if (width <= 0) {                                                 /* set display width and height if not already set */
    width = DisplayWidth(dpy, xscreen);
  }
  if (height <= 0) {
    height=DisplayHeight(dpy, xscreen);
  }

  if (useSHM && bpp > 0) {                                         /* if using X11 MIT shared memory extension */

    static Bool firstTime = TRUE;                                 /* initialize first time thru */
    if (firstTime) {
      firstTime = FALSE;

      *i = XShmCreateImage(dpy,                                         /* try to create an X11 shared memory image */
                           DefaultVisual(dpy, xscreen),
                           bpp,
                           ZPixmap,
                           NULL,
                           &shminfo,
                           width, height);

      if (*i == 0) {                                                 /* if could not create shared memory image */
        useSHM = FALSE;                                                 /* say no shared memory */

        getImage(bpp,                                                 /* and try again */
                 dpy,
                 xscreen,
                 i,
                 x,
                 y,
                 width,
                 height);

        return;
      }

      shminfo.shmid = shmget(IPC_PRIVATE,                         /* now get the data that is there from the shared memory */
                             (*i)->bytes_per_line * (*i)->height,
                             IPC_CREAT | 0777);

      shminfo.shmaddr = (*i)->data                                 /* set the shared memory address */
        = (char *) shmat(shminfo.shmid, 0, 0);

      shminfo.readOnly = False;                                         /* read-write mode */

      XShmAttach(dpy, &shminfo);                                 /* hook it up and make it live! */
    }

    if (x == 0                                                         /* full screen request? */
        && y == 0
        && width == DisplayWidth(dpy, xscreen)
        && height == DisplayHeight(dpy, xscreen)) {

      XShmGetImage(dpy,                                                 /* yes, get the whole shared image */
                   window,
                   *i,
                   0,
                   0,
                   AllPlanes);

    } else {

      XGetSubImage(dpy,                                                 /* no, just get a sub-image (FIXME Not shared?) */
                   window,
                   x,
                   y,
                   width,
                   height,
                   AllPlanes,
                   ZPixmap,
                   *i,
                   0,
                   0);

    }

  } else {                                                         /* not using X11 MIT shared memory extension at all */

    *i = XGetImage(dpy,                                                 /* just get it the slow way (FIXME How about subimages?) */
                   window,
                   x,
                   y,
                   width,
                   height,
                   AllPlanes,
                   ZPixmap);
  }
}


void checkForImageUpdates(
                          rfbScreenInfoPtr s,                         /* the live X-server's framebuffer, et al */
                          char* b,                                 /* our saved framebuffer */
                          int rowstride,                         /* width of a row in bytes */
                          int x,                                 /* position */
                          int y,
                          int width,                                 /* and size */
                          int height) {

  Bool changed;                                                         /* changed flag */
  int i;                                                         /* horiz index */
  int j;                                                         /* vert index */
  int k;                                                         /* byte index for compare loop */
  int l1;                                                         /* location 1 -- offset in our saved framebuffer */
  int l2;                                                         /* location 2 -- offset in original X-server frame buffer */
  int x1;                                                         /* horiz end of region of interest */
  int y1;                                                         /* vert end of region of interest */
  int bpp = s->bitsPerPixel/8;

  for (j = 0; j < height; j += blockLength) {                         /* vertical scan loop by blocks */
    for (i = 0; i < width; i += blockLength) {                         /* horiz scan loop by blocks */
      y1 = min(j + blockLength, height);                         /* bottom of block */
      x1 = min(i + blockLength, width);                                 /* right edge of block (blocks are square, so height == width) */
      y1 *= rowstride;                                                 /* width of an entire row in bytes */
      x1 *= bpp;                                                 /* bytes per pixel */
      changed = FALSE;                                                 /* TRUE if anything has changed since last scan */

      for (l1 = j*rowstride,                                         /* sweep vertically thru a block */
             l2 = (j + y)*(s->paddedWidthInBytes) + x*bpp;
           (l1 < y1) && !changed;
           l1 += rowstride,
             l2 += s->paddedWidthInBytes) {

        for (k = i*bpp; (k < x1) && !changed; k++) {                 /* sweep horizontally thru a block */

          if (s->frameBuffer[l2 + k] != b[l1 + k]) {                 /* different? */
            changed=TRUE;                                         /* yes, say so! */
          }
        }
      }

      if (changed) {                                                 /* was anything different? */

        for (l1 += i*bpp - rowstride,                                 /* yes */
               l2 += i*bpp - s->paddedWidthInBytes;
             l1 < y1;
             l1 += rowstride,
               l2 += s->paddedWidthInBytes) {

          memcpy(s->frameBuffer+l2, b+l1, x1-i*bpp);                 /* copy new stuff to our private frameBuffer */
        }

        rfbMarkRectAsModified(s,                                 /* mark it as changed so we can send it to the vncviewer */
                              x+i,
                              y+j,
                              x+i+blockLength,
                              y+j+blockLength);
      }
    }
  }
}


int probeX;
int probeY;


void probeScreen(                                                 /* check X-server for changed and mark changed rectangles */
                 rfbScreenInfoPtr s,                                 /* the live X-server et al */
                 int xscreen) {                                         /* the X screen of that server */

  int i;                                                         /* horizontal loop index */
  int j;                                                         /* vertical loop index */
  int j1;                                                         /* row index for buffer copy */
  int bpp = s->rfbServerFormat.bitsPerPixel/8;                         /* bytes per pixel == sizeof(pixel) */
  int rstride = s->paddedWidthInBytes;                                 /* row stride -- how wide each full row is in bytes */
  XImage* im;                                                         /* pointer to the X11 image */

#if 1
  probeX++;                                                         /* sequential scan */
  if(probeX>=tileWidth) {
    probeX=0;
    probeY++;
    if(probeY>=tileHeight) {
      probeY=0;
    }
  }
#else
  probeX = rand() % tileWidth;                                         /* random scan */
  probeY= rand() % tileHeight;
#endif

  for (j = probeY; j < s->height; j += tileHeight) {                 /* sweep vertically */
    for (i = 0 /*probeX*/; i < s->width; i += tileWidth) {       /* and horizontally within each vertical sweep */

      im = XGetImage(dpy,                                         /* get a live image of the X-server's screen */
                     window,
                     i,
                     j,
                     tileWidth /*1*/,
                     1,
                     AllPlanes,
                     ZPixmap);

      if (memcmp(im->data,                                         /* is the X11 framebuffer different from our old copy? */
                 s->frameBuffer + i*bpp + j*rstride,
                 tileWidth*bpp)) {

                                                                 /* yes, do an update */
        int x = i /* -probeX */;
        int w = (x + tileWidth > s->width) ? s->width-x : tileWidth;
        int y = j - probeY;
        int h = (y + tileHeight > s->height) ? s->height-y : tileHeight;

        XDestroyImage(im);                                         /* kill the old image */
        im = XGetImage(dpy, window, x, y, w, h, AllPlanes, ZPixmap); /* and get a new one */

        for (j1 = 0; j1 < h; j1++) {                                 /* save it in our copy of the framebuffer */
          memcpy(s->frameBuffer + x*bpp + (y + j1)*rstride,
                 im->data + j1*im->bytes_per_line,
                 bpp*w);
        }

        XDestroyImage(im);                                         /* get rid of live image -- we have our copy now */
        rfbMarkRectAsModified(s, x, y, x+w, y+h);

      } else {
        XDestroyImage(im);                                         /* no differences found, discard framebuffer image */
      }
    }
  }
}


/* #include "1instance.c" */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>

typedef struct {
  char* filename; /* this file is the pipe (set by user) */
  char is_server; /* this is set by open_control_file */
  int fd; /* this is set by open_control_file */
} single_instance_struct;

/* returns fd, is_server is set to -1 if server, 0 if client */
int open_control_file(single_instance_struct* str)
{
  struct stat buf;

  if(stat(str->filename,&buf)) {
    mkfifo(str->filename,128|256);
    str->is_server=-1;
    str->fd=open(str->filename,O_NONBLOCK|O_RDONLY);
  } else {
    str->fd=open(str->filename,O_NONBLOCK|O_WRONLY);
    if(errno==ENXIO) {
      str->is_server=-1;
      str->fd=open(str->filename,O_NONBLOCK|O_RDONLY);
    } else
      str->is_server=0;
  }

  return(str->fd);
}

void delete_control_file(single_instance_struct* str)
{
  remove(str->filename);
}

void close_control_file(single_instance_struct* str)
{
  close(str->fd);
}

typedef void (*event_dispatcher)(char* message);

//#if defined(__linux__) && defined(NEED_TIMEVAL)
//struct timeval {
//  long int tv_sec,tv_usec;
//};
//#endif

int get_next_message(char* buffer,int len,single_instance_struct* str,int usecs)
{
  struct timeval tv;
  fd_set fdset;
  int num_fds;

  FD_ZERO(&fdset);
  FD_SET(str->fd,&fdset);
  tv.tv_sec=0;
  tv.tv_usec=usecs;

  num_fds=select(str->fd+1,&fdset,NULL,NULL,&tv);
  if(num_fds) {
    int reallen;

    reallen=read(str->fd,buffer,len);
    if(reallen==0) {
      close(str->fd);
      str->fd=open(str->filename,O_NONBLOCK|O_RDONLY);
      num_fds--;
    }
    buffer[reallen]=0;
#ifdef DEBUG_1INSTANCE
    if(reallen!=0) fprintf(stderr,"message received: %s.\n",buffer);
#endif
  }

  return(num_fds);
}

int dispatch_event(single_instance_struct* str,event_dispatcher dispatcher,int usecs)
{
  char buffer[1024];
  int num_fds;

  if((num_fds=get_next_message(buffer,1024,str,usecs)) && buffer[0])
    dispatcher(buffer);

  return(num_fds);
}

int loop_if_server(single_instance_struct* str,event_dispatcher dispatcher)
{
  open_control_file(str);
  if(str->is_server) {
    while(1)
      dispatch_event(str,dispatcher,50);
  }

  return(str->fd);
}

void send_message(single_instance_struct* str,char* message)
{
#ifdef DEBUG_1INSTANCE
  int i=
#endif
  write(str->fd,message,strlen(message));
#ifdef DEBUG_1INSTANCE
  fprintf(stderr,"send: %s => %d(%d)\n",message,i,strlen(message));
#endif
}

#ifdef DEBUG_MAIN

#include <stdio.h>
#include <stdlib.h>

single_instance_struct str1 = { "/tmp/1instance" };

void my_dispatcher(char* message)
{
#ifdef DEBUG_1INSTANCE
  fprintf(stderr,"Message arrived: %s.\n",message);
#endif
  if(!strcmp(message,"quit")) {
    delete_control_file(str1);
    exit(0);
  }
}

int main(int argc,char** argv)
{
  int i;

  loop_if_server(str1,my_dispatcher);

  for(i=1;i<argc;i++)
    send_event(str1,argv[i]);

  return(0);
}

#endif
/* END OF #include "1instance.c" */


/* ---------------- The Main Program ---------------- */

int main(int argc, char** argv) {

  XImage *framebufferImage;
  char* backupImage;
  int xscreen;
  int i;
  rfbScreenInfoPtr screen;
  int maxMsecsToConnect = 5000;                                         /* a maximum of 5 seconds to connect */
  // FIXME maxMsecsToConnect is out of calibration.  On linux intel PC, this is 20 sec, not 5 sec.  What is wrong?

  int updateCounter;                                                 /* about every 50 ms a screen update should be made. */
  // FIXME Is this a scan of the X-server framebuffer to see if an update needs to be sent to the vncviewer?

  char message[1024];                                                 /* message buffer to send to "single_instance" below */
  single_instance_struct single_instance = { "/tmp/x11vnc_control" }; /* FIXME what is this?  some kind of message passing thing? */
  open_control_file(&single_instance);


  /* ---------------- Parse The Command Line Arguments In Reverse Order... ---------------- */

  for (i = argc - 1; i > 0; i--) {                                 /* for each argument on the command line... */

    /* FIXME Each of these options needs to be documented!!! */

    if (i < argc - 1 && !strcmp(argv[i], "-toggleviewonly")) {         /* -toggleviewonly */
      sprintf(message, "t%s", argv[i + 1]);
      send_message(&single_instance, message);
      exit(0);

    } else if (!strcmp(argv[i], "-listclients")) {                 /* -listclients */
      fprintf(stderr, "list clients\n");
      send_message(&single_instance, "l");
      exit(0);

    } else

#ifdef BACKCHANNEL
      if (i < argc - 1 && !strcmp(argv[i], "-backchannel")) {         /* -backchannel */
        sprintf(message, "b%s", argv[i+1]);
        send_message(&single_instance, message);
        exit(0);

      } else
#endif

        if (i < argc - 1 && strcmp(argv[i], "-display")==0) {         /* -display */
          fprintf(stderr, "Using display %s\n", argv[i + 1]);
          dpy = XOpenDisplay(argv[i + 1]);
          if (dpy == 0) {
            fprintf(stderr, "Couldn't connect to display \"%s\".\n", argv[i + 1]);
            exit(1);

          }

        } else if (i < argc - 1 && strcmp(argv[i], "-wait4client") == 0) { /* -wait4client */
          maxMsecsToConnect = atoi(argv[i + 1]);

        } else if (i < argc - 1 && strcmp(argv[i], "-update") == 0) { /* -update */
          updateCounter = atoi(argv[i + 1]);

        } else if (strcmp(argv[i], "-noshm") == 0) {                 /* -noshm */
          useSHM = FALSE;

        } else if (strcmp(argv[i], "-runforever") == 0) {         /* -runforever */
          disconnectAfterFirstClient = FALSE;

        } else if (strcmp(argv[i], "-tile") == 0) {                 /* -tile */
          dontTile = FALSE;

        } else if (strcmp(argv[i], "-viewonly") == 0) {                 /* viewonly */
          viewOnly = TRUE;

        } else if (strcmp(argv[i], "-shared") == 0) {                 /* -shared */
          sharedMode = TRUE;
        }
  }

  /* ---------------- End Of Parsing Command Line Arguments ---------------- */


  updateCounter = dontTile ? 20 : 1;                                 /* FIXME This overrides the setting of -update */

  if (dpy == 0) {                                                 /* open default DISPLAY if none already opened */
    dpy = XOpenDisplay("");
  }
  if (dpy == 0) {                                                 /* could not open default either */
    fprintf(stderr, "Couldn't open display!\n");
    exit(2);                                                         /* DIE! */
  }

  xscreen = DefaultScreen(dpy);                                         /* default screen of selected DISPLAY */
  window = RootWindow(dpy, xscreen);                                 /* root window of that screen */

  init_keycodes();                                                 /* initialize the keyboard */

  getImage(0, dpy, xscreen, &framebufferImage, 0, 0, -1, -1);         /* get the initial state of the live X-server's framebuffer */

  screen = rfbGetScreen(&argc, argv,
                        framebufferImage->width,
                        framebufferImage->height,
                        framebufferImage->bits_per_pixel,
                        8,
                        framebufferImage->bits_per_pixel/8);

  screen->paddedWidthInBytes = framebufferImage->bytes_per_line;

  screen->rfbServerFormat.bitsPerPixel = framebufferImage->bits_per_pixel;
  screen->rfbServerFormat.depth = framebufferImage->depth;
  // rfbEndianTest = framebufferImage->bitmap_bit_order != MSBFirst;
  screen->rfbServerFormat.trueColour = TRUE;

  if (screen->rfbServerFormat.bitsPerPixel == 8) {
    if (CellsOfScreen(ScreenOfDisplay(dpy, xscreen))) {
      XColor color[256];
      int i;
      screen->colourMap.count = 256;
      screen->rfbServerFormat.trueColour = FALSE;
      screen->colourMap.is16 = TRUE;
      for (i = 0; i < 256; i++) {
        color[i].pixel=i;
      }
      XQueryColors(dpy, DefaultColormap(dpy, xscreen), color, 256);
      screen->colourMap.data.shorts = (short*)malloc(3*sizeof(short)*screen->colourMap.count);
      for (i = 0; i < screen->colourMap.count; i++) {
        screen->colourMap.data.shorts[i*3 + 0] = color[i].red;
        screen->colourMap.data.shorts[i*3 + 1] = color[i].green;
        screen->colourMap.data.shorts[i*3 + 2] = color[i].blue;
      }
    } else {
      screen->rfbServerFormat.redShift = 0;
      screen->rfbServerFormat.greenShift = 2;
      screen->rfbServerFormat.blueShift = 5;
      screen->rfbServerFormat.redMax   = 3;
      screen->rfbServerFormat.greenMax = 7;
      screen->rfbServerFormat.blueMax  = 3;
    }
  } else {
    screen->rfbServerFormat.redShift = 0;
    if (framebufferImage->red_mask) {
      while (!(framebufferImage->red_mask & (1 << screen->rfbServerFormat.redShift))) {
        screen->rfbServerFormat.redShift++;
      }
    }
    screen->rfbServerFormat.greenShift = 0;
    if (framebufferImage->green_mask) {
      while (!(framebufferImage->green_mask & (1 << screen->rfbServerFormat.greenShift))) {
        screen->rfbServerFormat.greenShift++;
      }
    }
    screen->rfbServerFormat.blueShift = 0;
    if (framebufferImage->blue_mask) {
      while (!(framebufferImage->blue_mask & (1 << screen->rfbServerFormat.blueShift))) {
        screen->rfbServerFormat.blueShift++;
      }
    }
    screen->rfbServerFormat.redMax   = framebufferImage->red_mask   >> screen->rfbServerFormat.redShift;
    screen->rfbServerFormat.greenMax = framebufferImage->green_mask >> screen->rfbServerFormat.greenShift;
    screen->rfbServerFormat.blueMax  = framebufferImage->blue_mask  >> screen->rfbServerFormat.blueShift;
  }

  backupImage = malloc(screen->height*screen->paddedWidthInBytes);
  memcpy(backupImage, framebufferImage->data, screen->height*screen->paddedWidthInBytes);

  screen->frameBuffer = backupImage;
  screen->cursor = 0;
  screen->newClientHook = newClient;

  screen->kbdAddEvent = keyboard;
  screen->ptrAddEvent = mouse;

  if (sharedMode) {
    screen->rfbAlwaysShared = TRUE;
  }

  screen->rfbDeferUpdateTime = 1;
  updateCounter /= screen->rfbDeferUpdateTime;

  rfbInitServer(screen);

  c = 0;
  for(;;) {
    if (screen->rfbClientHead) {
      maxMsecsToConnect = 1<<16;
    } else {
      maxMsecsToConnect -= screen->rfbDeferUpdateTime;
      if (maxMsecsToConnect < 0) {
        fprintf(stderr, "Maximum time to connect reached. Exiting.\n");
        XTestDiscard(dpy);
        exit(2);
      }
    }

    if (get_next_message(message, 1024, &single_instance, 50)) {
      if (message[0] == 'l' && message[1] == 0) {
        rfbClientPtr cl;
        int i;
        for (i = 0,
               cl = screen->rfbClientHead;
             cl;
             cl = cl->next,
               i++) {
          fprintf(stderr, "%02d: %s\n", i, cl->host);
        }
      } else if (message[0] == 't') {
        rfbClientPtr cl;
        for (cl = screen->rfbClientHead; cl; cl = cl->next) {
          if (!strcmp(message+1, cl->host)) {
            cl->clientData=(void*)((cl->clientData==0)?-1:0);
            break;
          }
        }
      }

#ifdef BACKCHANNEL
      else if (message[0]=='b')
        rfbSendBackChannel(screen, message+1, strlen(message+1));
#endif

    }

    rfbProcessEvents(screen, -1);
    if (shutDownServer) {
      free(backupImage);
      rfbScreenCleanup(screen);
      XFree(dpy);
      exit(0);
    }

    if (dontTile) {
      if (gotInput) {
        gotInput = FALSE;
        c = updateCounter;
      } else if (screen->rfbClientHead && c++ > updateCounter) {
        c=0;
        if (!useSHM) {
          framebufferImage->f.destroy_image(framebufferImage);
        }

        getImage(screen->rfbServerFormat.bitsPerPixel,
                 dpy,
                 xscreen,
                 &framebufferImage,
                 0,
                 0,
                 screen->width,
                 screen->height);

        checkForImageUpdates(screen,
                             framebufferImage->data,
                             framebufferImage->bytes_per_line,
                             0,
                             0,
                             screen->width,
                             screen->height);

      }
    } else if (c++ > updateCounter) {
      c = 0;
      probeScreen(screen, xscreen);
    }

#ifdef WRITE_SNAPS
    {
      int i;
      int j;
      int r;
      int g;
      int b;
      FILE* f = fopen("test.pnm", "wb");
      fprintf(f, "P6\n%d %d\n255\n", screen->width, screen->height);
      for (j = 0; j < screen->height; j++) {
        for (i = 0; i < screen->width; i++) {
          r = framebufferImage->data[j*screen->paddedWidthInBytes + i*2];
          fputc(((r >> screen->rfbServerFormat.redShift)   & screen->rfbServerFormat.redMax)   * 255/screen->rfbServerFormat.redMax,   f);
          fputc(((r >> screen->rfbServerFormat.greenShift) & screen->rfbServerFormat.greenMax) * 255/screen->rfbServerFormat.greenMax, f);
          fputc(((r >> screen->rfbServerFormat.blueShift)  & screen->rfbServerFormat.blueMax)  * 255/screen->rfbServerFormat.blueMax,  f);
        }
      }
      fclose(f);
    }
#endif

  }
  return(0);
}