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

#include "vncserv.h"
#include "proto.h"
#include "vncauth.h"
#include "pixtrans.h"
#include "hextile.h"
#include "areas.h"


// when converting pixels from server format to client format, we
// need a temporary buffer to store the result
#define RAWSTRIPWIDTH         256
#define RAWSTRIPHEIGHT        16
#define TEMPBUFFERSIZE        (RAWSTRIPWIDTH*RAWSTRIPHEIGHT)


static char pixelconvert[TEMPBUFFERSIZE*4];


#define VNCSERVSTATE_IDLE                         0
#define VNCSERVSTATE_WAITING_FOR_PROTOCOL_VERSION 1
#define VNCSERVSTATE_WAITING_FOR_AUTH             2
#define VNCSERVSTATE_WAITING_FOR_CLIENT_INIT      3
#define VNCSERVSTATE_READY                        10



static void serv_error(vncserv *serv, const char *msg);
static void set_state(vncserv *serv, int state, int timeout_in_seconds);
static void fill_buffer(vncserv *serv, int max);
static void flush_buffer(vncserv *serv, int n);
static void read_set_pixel_format(vncserv *serv);
static void read_fix_colour_map_entries(vncserv *serv);
static void read_set_encodings(vncserv *serv);
static void read_framebuffer_update_request(vncserv *serv);
static void read_key_event(vncserv *serv);
static void read_pointer_event(vncserv *serv);
static void read_client_cut_text(vncserv *serv);
static void send_area(vncserv *serv, area *ar);
static void write_data(vncserv *serv, void *bytes, int n);
static void mark_data(vncserv *serv);
static void area_poll(vncserv *serv);



vncserv *vncserver_create(void *framebuffer, int sizex, int sizey, int bpp, int bpl, char *name, vncservio *io, void *meta, const char *password) {
  // create new vnc server

  vncserv *serv;

  if (!io)    return NULL;

  if (!framebuffer) {
    if (io->error)
      io->error(NULL, "No framebuffer specified", NULL);
    return NULL;
  }

  if (sizex <= 0 || sizey <= 0 || sizex > 2048 || sizey > 2048 || (bpp != 4 && bpp != 8 && bpp != 16)) {
    if (io->error)
      io->error(NULL, "Illegal framebuffer layout", NULL);
    return NULL;
  }

  if (!io->write)
    if (io->error)
      io->error(NULL, "io.write() not specified - will cause problems later on...", NULL);

  serv = (vncserv *)malloc(sizeof(vncserv));
  if (!serv) {
    if (io->error) {
      char t[100];
      sprintf(t, "Failed to malloc() %d bytes", sizeof(vncserv));
      io->error(NULL, t, NULL);
    }
    return NULL;
  }
  memset(serv, 0, sizeof(vncserv));
  memcpy(&serv->io, io, sizeof(vncservio));
  serv->framebuffer = framebuffer;
  serv->sizex = sizex;
  serv->sizey = sizey;
  serv->sourcebpp = bpp;
  serv->bpl = bpl;
  if (name) {
    strncpy(serv->name, name, 255);
    serv->name[255] = '\0';
  } else
    strcpy(serv->name, "VNC server");
  if (password && password[0]) {
    strcpy(serv->password, password);
    serv->usepassword = 1;
  } else
    serv->usepassword = 0;
  serv->meta = meta;
  serv->bytesinbuffer = 0;
  serv->buffersize = DEFAULTBUFFERSIZE;
  serv->buffer = malloc(serv->buffersize);
  if (!serv->buffer) {
    free(serv);
    if (io->error) {
      char t[100];
      sprintf(t, "Failed to malloc() %d bytes", serv->buffersize);
      io->error(NULL, t, NULL);
    }
    return NULL;
  }
  serv->clientbpp = 16;
  serv->box[0].x = 0;
  serv->box[0].y = 0;
  serv->box[0].w = sizex;
  serv->box[0].h = sizey;
  reset_area(&serv->clientbox);
  reset_area(&serv->nextbox[0]);
  serv->hextile_supported =
    serv->hextiledithering_supported = serv->copy_supported = 0;
  serv->state = VNCSERVSTATE_IDLE;
  serv->statetimeout = 0;
  serv->started = 0;
  if (bpp == 8) {
    int i;
    for (i = 0; i < 256; i++) {
      int r, g, b;
      r = i & 7;       r = (r<<5) | (r<<2) | (r>>1);
      g = (i>>3) & 7;  g = (g<<5) | (g<<2) | (g>>1);
      b = (i>>6) & 3;  b = (b<<6) | (b<<4) | (b>>2) | b;
      serv->pal8bpp[i] = r | (g<<8) | (b<<16);
    }
  }
  return serv;
}

int vncserver_set_buffer_size(vncserv *serv, int size) {
// set buffer size
  char *p;

  // never decrease buffer size
  if (serv->buffersize >= size)      return 0;
  // get new buffer
  p = malloc(size);
  if (!p) {
    char t[100];
    sprintf(t, "Failed to malloc() %d bytes", size);
    serv_error(serv, t);
    return 1;
  }
  // copy buffer contents to new buffer
  if (serv->bytesinbuffer)   memcpy(p, serv->buffer, serv->bytesinbuffer);
  // free old buffer
  free(serv->buffer);
  serv->buffer = p;
  return 0;
}


void vncserv_set_8bpp_palette(vncserv *serv, const unsigned int *palette) {
  memcpy(serv->pal8bpp, palette, 4*256);
}

void vncserv_started(vncserv *serv) {
  // tell the server that the connection is open
  serv->started = 1;
  write_data(serv, (void *)"RFB 003.003\n", 12);
  set_state(serv, VNCSERVSTATE_WAITING_FOR_PROTOCOL_VERSION, 60);
}

int vncserv_poll(vncserv *serv, int timeout_in_milliseconds) {
  // poll the server, should be called as often as possible
  int timeout;

  if (!serv->started)   return VNCSERV_OK;
  timeout = clock(/* assumed to be 100 Hz clock */) + timeout_in_milliseconds/10;
  do {
    if (clock() > serv->statetimeout) {
      serv_error(serv, "Timeout whilst waiting for client data");
      return VNCSERV_FATAL_ERROR;
    }

    switch (serv->state) {
    case VNCSERVSTATE_IDLE:
      break;

    case VNCSERVSTATE_WAITING_FOR_PROTOCOL_VERSION:
      if (serv->bytesinbuffer < 12)   fill_buffer(serv, 12-serv->bytesinbuffer);
      if (serv->bytesinbuffer > 12) {
        serv_error(serv, "Too much data received from client");
        return VNCSERV_FATAL_ERROR;
      }
      if (serv->bytesinbuffer == 12) {
        // validate protocol version
        // HBP - somethings missing here!
        // remove the message
        flush_buffer(serv, -1);
        if (serv->usepassword) {
          int i;
          CARD32 authtype = Swap32IfLE(2);
          // send auth message
          write_data(serv, (void *)&authtype, 4);
          // generate random challenge
          for (i = 0; i < 16; i++) {
            int c;
            c = rand();
            c ^= clock();
            c ^= ((int)serv)>>i;
            serv->challenge[i] = c &255;
          }
          write_data(serv, serv->challenge, 16);
          set_state(serv, VNCSERVSTATE_WAITING_FOR_AUTH, 60);
        } else {
          CARD32 authtype = Swap32IfLE(1);
          // send auth message
          write_data(serv, (void *)&authtype, 4);
          set_state(serv, VNCSERVSTATE_WAITING_FOR_CLIENT_INIT, 60);
        }
      }
      break;

    case VNCSERVSTATE_WAITING_FOR_AUTH:
      if (serv->bytesinbuffer < 16)   fill_buffer(serv, 16-serv->bytesinbuffer);
      if (serv->bytesinbuffer > 16) {
        serv_error(serv, "Too much data received from client");
        return VNCSERV_FATAL_ERROR;
      }
      if (serv->bytesinbuffer == 16) {
        // validate encoded string
        vncEncryptBytes((unsigned char *)serv->challenge, serv->password);
        if (memcmp(serv->challenge, serv->buffer, 16)) {
          CARD32 failed = Swap32IfLE(1);
          write_data(serv, (void *)&failed, 4);
          serv_error(serv, "Authentication failed");
          return VNCSERV_FATAL_ERROR;
        } else {
          CARD32 ok = Swap32IfLE(0);
          write_data(serv, (void *)&ok, 4);
          set_state(serv, VNCSERVSTATE_WAITING_FOR_CLIENT_INIT, 60);
        }
        flush_buffer(serv, -1);
      }
      break;

    case VNCSERVSTATE_WAITING_FOR_CLIENT_INIT:
      if (serv->bytesinbuffer < 1)   fill_buffer(serv, 1-serv->bytesinbuffer);
      if (serv->bytesinbuffer > 1) {
        serv_error(serv, "Too much data received from client");
        return VNCSERV_FATAL_ERROR;
      }
      if (serv->bytesinbuffer == 1) {
        CARD32 len;
        CARD16 x, y;
        struct {
          CARD8 bpp;
          CARD8 depth;
          CARD8 bigendian;
          CARD8 truecolour;
          CARD16 redmax;
          CARD16 greenmax;
          CARD16 bluemax;
          CARD8 redshift;
          CARD8 greenshift;
          CARD8 blueshift;
          CARD8 pad13, pad14, pad15;
        } data;
        int shift[3], max[3];

        serv->sharedflag = serv->buffer[0];
        flush_buffer(serv, -1);

        // send server init
        // send size
        x = Swap16IfLE(serv->sizex);
        y = Swap16IfLE(serv->sizey);
        write_data(serv, &x, 2);
        write_data(serv, &y, 2);
        // send info
        data.bpp = serv->sourcebpp;
        data.bigendian = 0;
        data.truecolour = 1;
        switch (data.bpp) {
        case 16:
          shift[0] = 0;
          shift[1] = 5;
          shift[2] = 10;
          max[0] = max[1] = max[2] = 31;
          data.depth = 15;
          break;
        case 8:
          data.depth = 8;
          shift[0] = 0;
          shift[1] = 3;
          shift[2] = 6;
          max[0] = max[1] = 7;
          max[2] = 3;
          break;
        case 4:
          data.bpp = data.depth = 8;
          shift[0] = 0;
          shift[1] = 3;
          shift[2] = 6;
          max[0] = max[1] = 7;
          max[2] = 3;
          break;
        }
        data.redmax     = Swap16IfLE(max[0]);
        data.greenmax   = Swap16IfLE(max[1]);
        data.bluemax    = Swap16IfLE(max[2]);
        data.redshift   = shift[0];
        data.greenshift = shift[1];
        data.blueshift  = shift[2];
        data.pad13 = data.pad14 = data.pad15 = 0;
        write_data(serv, &data, 16);
        // send namelength and name
        len = Swap32IfLE(strlen(serv->name));
        write_data(serv, &len, 4);
        write_data(serv, serv->name, strlen(serv->name));
        set_state(serv, VNCSERVSTATE_READY, 1000);

        pixtrans_build(serv, max, shift, data.depth);
      }
      break;

    case VNCSERVSTATE_READY:
      fill_buffer(serv, serv->buffersize-serv->bytesinbuffer);
      if (serv->bytesinbuffer >= 1)
        switch (serv->buffer[0]) {
        case 0:
          read_set_pixel_format(serv);
          break;
        case 1:
          read_fix_colour_map_entries(serv);
          break;
        case 2:
          read_set_encodings(serv);
          break;
        case 3:
          read_framebuffer_update_request(serv);
          break;
        case 4:
          read_key_event(serv);
          break;
        case 5:
          read_pointer_event(serv);
          break;
        case 6:
          read_client_cut_text(serv);
          break;
        }
      area_poll(serv);
      break;
    }
  } while ((clock() < timeout) && !serv->errorhasoccured);
  return serv->errorhasoccured;
}

void vncserv_framebuffer_area_moved(vncserv *serv, area *from, area *to) {
  area both;
  areas_union(from, to, &both);
  vncserv_framebuffer_changed(serv, &both);
}

void vncserv_framebuffer_changed(vncserv *serv, area *serverarea) {
  // called whenever part of the screen has changed
  area ar;

  copy_area(&ar, serverarea);
  // validate area
  if (ar.x <  0 || ar.y <  0)   return;
  if (ar.w <= 0 || ar.h <= 0)   return;
  if (ar.x + ar.w > serv->sizex || ar.y + ar.h > serv->sizey)   return;

  if (area_is_empty(&serv->box[0])) {
    // easy - the new area is the only area
    copy_area(&serv->box[0], &ar);

  } else {
    // quick and easy hack - simply get the largest area covering both areas...
    int s1, s2, s12, waste;
    s1 = ar.w*ar.h;
    s2 = serv->box[0].w*serv->box[0].h;
    areas_union(&ar, &serv->box[0], &serv->box[0]);
    s12 = serv->box[0].w*serv->box[0].h;
    waste = s12 - s1 - s2;
//    fprintf(stderr, "Need to join two areas %d pixels (%d %%) waste\n", waste, (100*waste)/(s1+s2));
  }

  if (area_is_empty(&serv->clientbox))   return;

  if (!area_is_empty(&serv->nextbox[0]))    return;

  // check if the changed area and client area touch at all
  if (!areas_intersect(&serv->clientbox, &serv->box[0]))   return;

  if (area_encloses(&serv->box[0], &serv->clientbox)) {
    // client area completely within changed area, so just send client area
    copy_area(&ar, &serv->clientbox);

  } else if (area_encloses(&serv->clientbox, &serv->box[0])) {
    // changed area completely within client area, so send entire changed area
    copy_area(&ar, &serv->box[0]);

  } else {
    // changed area and client area overlap in some way, send union
    areas_union(&serv->clientbox, &serv->box[0], &ar);

  }
  reset_area(&serv->clientbox);
  reset_area(&serv->box[0]);
  copy_area(&serv->nextbox[0], &ar);
}

void vncserv_closedown(vncserv *serv) {
  // close down the server
  free(serv);
}

void vncserv_cut_text(vncserv *serv, char *text) {
  // send string to client
  struct {
    CARD8 type;
    CARD8 pad1, pad2, pad3;
    CARD32 len;
  } data;
  if (!serv->started)   return;

  data.type = 3;
  data.pad1 = data.pad2 = data.pad3 = 0;
  data.len = Swap32IfLE(strlen(text));
  write_data(serv, &data, 8);
  write_data(serv, text, strlen(text));
}

void vncserv_bell(vncserv *serv) {
  // ring the bell on the client
  CARD8 type;
  if (!serv->started)   return;
  type = 2;
  write_data(serv, &type, 1);
}

int vncserv_give_data(vncserv *serv, char *buffer, int bytes) {
  // write data directly into the input buffer - use only if io.read() not set
  int use;

  if (serv->io.read)   return 0;
  if (bytes <= 0)      return serv->buffersize - serv->bytesinbuffer;

  use = bytes;
  if (use + serv->bytesinbuffer > serv->buffersize)
    use = serv->buffersize - serv->bytesinbuffer;
  memcpy(serv->buffer + serv->bytesinbuffer, buffer, use);
  serv->bytesinbuffer += use;

  if (use == bytes)    return serv->buffersize - serv->bytesinbuffer;
  return bytes - use;
}

int vncserv_read_key(vncserv *serv, int *key, int *down, int *time) {
  if (serv->keyeventcount) {
    *key  = serv->keyevents[0].key;
    *down = serv->keyevents[0].down;
    *time = serv->keyevents[0].time;
    serv->keyeventcount--;
    if (serv->keyeventcount)
      memmove(serv->keyevents,
              serv->keyevents+1,
              sizeof(bufferedkey)*(KEYEVENTSTOBUFFER-1));
  }
  return serv->keyeventcount;
}

int vncserv_read_pointer(vncserv *serv, int *x, int *y, int *buttons, int *time) {
  if (serv->pointereventcount) {
    *x       = serv->pointerevents[0].x;
    *y       = serv->pointerevents[0].y;
    *buttons = serv->pointerevents[0].buttons;
    *time    = serv->pointerevents[0].time;
    serv->pointereventcount--;
    if (serv->pointereventcount)
      memmove(serv->pointerevents,
              serv->pointerevents+1,
              sizeof(bufferedptr)*(POINTEREVENTSTOBUFFER-1));
  }
  return serv->pointereventcount;
}

// ------------------------------------------------------------------------------

// report an error to the frontend
void serv_error(vncserv *serv, const char *msg) {
  if (serv->io.error)   serv->io.error(serv, msg, serv->meta);
}

void set_state(vncserv *serv, int state, int timeout_in_seconds) {
  serv->state = state;
  serv->statetimeout = clock() + timeout_in_seconds*100;
}

// try to fill the buffer with 'max' bytes from the frontend
void fill_buffer(vncserv *serv, int max) {
  int n;
  if (max <= 0)         return;
  if (!serv->io.read)   return;
  n = serv->io.read(serv, serv->buffer+serv->bytesinbuffer, max, serv->meta);
  if (n > 0)  serv->bytesinbuffer += n;
  if (n < 0)  serv->errorhasoccured = n;
}

// remove 'n' bytes from the buffer
void flush_buffer(vncserv *serv, int n) {
  if (n < 0)               serv->bytesinbuffer = 0;
  if (n <= 0)              return;
  if (n > serv->bytesinbuffer)  n = serv->bytesinbuffer;
  if (n < serv->bytesinbuffer)  memmove(serv->buffer, serv->buffer+n, serv->bytesinbuffer-n);
  serv->bytesinbuffer -= n;
}

// ------------------------------------------------------------------------------

// client sent a 'set pixel format' message, so read and process it
void read_set_pixel_format(vncserv *serv) {
  int shift[3], max[3];
  struct {
    CARD8 type;
    CARD8 pad1, pad2, pad3;
    CARD8 bpp;
    CARD8 depth;
    CARD8 bigendian;
    CARD8 truecolour;
    CARD16 redmax;
    CARD16 greenmax;
    CARD16 bluemax;
    CARD8 redshift;
    CARD8 greenshift;
    CARD8 blueshift;
    CARD8 pad17, pad18, pad19;
  } data;

  // make sure we have enough data in the buffer
  if (serv->bytesinbuffer < 20)           return;
  memcpy(&data, serv->buffer, 20);
  flush_buffer(serv, 20);

  // process the message
  serv->clientbpp = data.bpp;

  if (data.truecolour) {
    shift[0] = data.redshift;
    shift[1] = data.greenshift;
    shift[2] = data.blueshift;
    max[0] = Swap16IfLE(data.redmax);
    max[1] = Swap16IfLE(data.greenmax);
    max[2] = Swap16IfLE(data.bluemax);
    pixtrans_build(serv, max, shift, data.bpp);

  } else if (data.bpp == 8) {
    shift[0] = 0;
    shift[1] = 3;
    shift[2] = 6;
    max[0] = 7;
    max[1] = 7;
    max[2] = 3;
    pixtrans_build(serv, max, shift, data.bpp);
  }
}

// client sent 'fix colour map entries'
// just read and ignore it
void read_fix_colour_map_entries(vncserv *serv) {
  struct {
    CARD8 type;
    CARD8 pad1;
    CARD16 col1;
    CARD16 numcol;
  } data;
  int n;

  if (serv->bytesinbuffer < 4)           return;
  memcpy(&data, serv->buffer, 4);
  n = Swap16IfLE(data.numcol);
  if (serv->bytesinbuffer < 4 + 6*n)     return;
  flush_buffer(serv, 4 + 6*n);
}

// client has specified which encodings to use
void read_set_encodings(vncserv *serv) {
  struct {
    CARD8 type;
    CARD8 pad1;
    CARD16 numencodings;
  } data;
  int num, i;

  if (serv->bytesinbuffer < 4)           return;
  memcpy(&data, serv->buffer, 4);
  num = Swap16IfLE(data.numencodings);
  // don't process anything until all encodings have been read
  if (serv->bytesinbuffer < 4 + num*4)   return;

  for (i = 0; i < num; i++) {
    CARD32 enc;
    memcpy(&enc, serv->buffer + 4 + 4*i, 4);
    enc = Swap32IfLE(enc);
    // HBP some work on encoding priorities is needed...
    switch (enc) {
    case HEXTILE_ENCODING:
      serv->hextile_supported = 1;
      break;
    case HEXTILEDITHER8_ENCODING:
      serv->hextiledithering_supported = 1;
      break;
    case COPY_ENCODING:
      serv->copy_supported = 1;
      break;
    }
  }
  flush_buffer(serv, 4 + 4*num);
}

// client sent update request
void read_framebuffer_update_request(vncserv *serv) {
  struct {
    CARD8 type;
    CARD8 incremental;
    CARD16 minx;
    CARD16 miny;
    CARD16 width;
    CARD16 height;
  } data;
  int x, y, w, h, x1, y1;
  area client;

  if (serv->bytesinbuffer < 10)          return;
  memcpy(&data, serv->buffer, 10);
  flush_buffer(serv, 10);
  x = Swap16IfLE(data.minx);
  y = Swap16IfLE(data.miny);
  w = Swap16IfLE(data.width);
  h = Swap16IfLE(data.height);

  x1 = x + w;
  y1 = y + h;

  // make sure the area is valid
  if (x < 0)  x = 0;
  if (y < 0)  y = 0;
  if (x1 > serv->sizex)  x1 = serv->sizex;
  if (y1 > serv->sizey)  y1 = serv->sizey;
  if (x >= x1 || y >= y1)   return;
  set_area(&client, x, y, x1-x, y1-y);

  if (data.incremental) {
    // send any changed area within the new client area
    if (!area_is_empty(&serv->box[0])) {
      if (area_encloses(&client, &serv->box[0])) {
        // new client area covers the changed area, so send changed area
        //send_area(serv, &serv->box[0]);
        areas_union(&serv->box[0], &serv->nextbox[0], &serv->nextbox[0]);
        // changed area is no longer changed
        reset_area(&serv->box[0]);

      } else if (!areas_intersect(&client, &serv->box[0])) {
        // nothing changed within new client area

      } else if (area_encloses(&serv->box[0], &client)) {
        // new client area completely inside the changed area, send new client area
        //send_area(serv, &client);
        areas_union(&client, &serv->nextbox[0], &serv->nextbox[0]);

      } else {
        // new client area partly covers changed area, send new client area
        //send_area(serv, &client);
        areas_union(&client, &serv->nextbox[0], &serv->nextbox[0]);
      }

    } else if (area_is_empty(&serv->clientbox)) {
      // if there's no changed area, and no old client area...
      copy_area(&serv->clientbox, &client);

    } else {
      // if there's no changed area, make client area from union of
      // old client area and new client area
      areas_union(&client, &serv->clientbox, &serv->clientbox);

    }
  } else {
    // send the entire user area
    copy_area(&serv->nextbox[0], &client);
    //send_area(serv, &client);
    // mark the area as unchanged
    // quick and easy hack ...
    //if (area_encloses(&serv->box[0], &client)) // ?? is this correct ??
      reset_area(&serv->box[0]);        // clear changed area
  }
}

// cliet sent key event
void read_key_event(vncserv *serv) {
  struct {
    CARD8 type;
    CARD8 downflag;
    CARD8 pad2, pad3;
    CARD32 key;
  } data;
  int key;

  if (serv->bytesinbuffer < 8)          return;
  memcpy(&data, serv->buffer, 8);
  flush_buffer(serv, 8);
  key = Swap32IfLE(data.key);
  if (serv->io.keypress)
    serv->io.keypress(serv, key, !!data.downflag, serv->meta);
  else
    if (serv->keyeventcount < KEYEVENTSTOBUFFER) {
      serv->keyevents[serv->keyeventcount].key = key;
      serv->keyevents[serv->keyeventcount].down = data.downflag;
      serv->keyevents[serv->keyeventcount].time = clock();
      serv->keyeventcount++;
    }
}

// client sent pointer event
void read_pointer_event(vncserv *serv) {
  struct {
    CARD8 type;
    CARD8 buttons;
    CARD16 x, y;
  } data;
  int x, y;

  if (serv->bytesinbuffer < 6)          return;
  memcpy(&data, serv->buffer, 6);
  flush_buffer(serv, 6);
  x = Swap16IfLE(data.x);
  y = Swap16IfLE(data.y);
  if (serv->io.pointer)
    serv->io.pointer(serv, x, y, data.buttons, serv->meta);
  else
    if (serv->pointereventcount < POINTEREVENTSTOBUFFER) {
      if (serv->pointereventcount == POINTEREVENTSTOBUFFER-1) {
        // don't save dublicates
        int px, py, pbuttons;
        px = serv->pointerevents[serv->pointereventcount-1].x;
        py = serv->pointerevents[serv->pointereventcount-1].y;
        pbuttons = serv->pointerevents[serv->pointereventcount-1].buttons;
        if (x == px && y == py && data.buttons == pbuttons)  return;
      }
      serv->pointerevents[serv->pointereventcount].x = x;
      serv->pointerevents[serv->pointereventcount].y = y;
      serv->pointerevents[serv->pointereventcount].buttons = data.buttons;
      serv->pointerevents[serv->pointereventcount].time = clock();
      serv->pointereventcount++;
    }
}

// client sent text
void read_client_cut_text(vncserv *serv) {
  struct {
    CARD8 type;
    CARD8 pad1, pad2, pad3;
    CARD32 len;
  } data;
  int len;

  if (serv->bytesinbuffer < 8)          return;
  memcpy(&data, serv->buffer, 8);
  len = Swap32IfLE(data.len);
  if (serv->bytesinbuffer < 8+len)      return;

  if (serv->io.cuttext)    serv->io.cuttext(serv, serv->buffer+8, len, serv->meta);

  flush_buffer(serv, 8+len);
}

// ------------------------------------------------------------------------------

void send_area_raw(vncserv *serv, area *ar) {
  int y, x, nx, ny, width, height;
  struct {
    CARD8 type;
    CARD8 pad1;
    CARD16 n;
  } data;
  struct {
    CARD16 x;
    CARD16 y;
    CARD16 w;
    CARD16 h;
    CARD32 coding;
  } rect;

  if (ar->w*ar->h < TEMPBUFFERSIZE) {
    // can be in the temp buffer in one go...
    data.type = 0;
    data.pad1 = 0;
    data.n    = Swap16IfLE(1);
    write_data(serv, &data, 4);

    rect.x = Swap16IfLE(ar->x);
    rect.y = Swap16IfLE(ar->y);
    rect.w = Swap16IfLE(ar->w);
    rect.h = Swap16IfLE(ar->h);
    rect.coding = 0; // raw
    write_data(serv, &rect, 12);

    pixtrans_do_it(serv, ar->x, ar->y, ar->w, ar->h, pixelconvert);
    // send strip
    write_data(serv, pixelconvert, ar->w*ar->h*serv->clientbpp/8);
    return;
  }

  // divide the area into big stripes
  if (ar->w < RAWSTRIPWIDTH)
    width = ar->w;
  else
    width = RAWSTRIPWIDTH;;
  height = TEMPBUFFERSIZE/width;

  nx = (ar->w + width-1)/width;
  ny = (ar->h + height-1)/height;

  data.type = 0;
  data.pad1 = 0;
  data.n    = Swap16IfLE(nx*ny);
  write_data(serv, &data, 4);

  for (x = 0; x < ar->w; x += width) {
    int w;
    w = width;
    if (x + w > ar->w)   w = ar->w - x;
    rect.x = Swap16IfLE(ar->x + x);
    rect.w = Swap16IfLE(w);

    for (y = 0; y < ar->h; y += height) {
      int h;
      h = height;
      if (y + h > ar->h)   h = ar->h - y;
      rect.y = Swap16IfLE(ar->y + y);
      rect.h = Swap16IfLE(h);
      rect.coding = 0; // raw
      write_data(serv, &rect, 12);
      pixtrans_do_it(serv, ar->x + x, ar->y + y, w, h, pixelconvert);
      // send strip
      write_data(serv, pixelconvert, w*h*serv->clientbpp/8);
    }
  }
}

void send_area(vncserv *serv, area *ar) {

  if (area_is_empty(ar))          return;
  if (ar->w == 0 || ar->h == 0)   return;

  if (serv->hextile_supported)
    hextile_encode_and_send(serv, ar->x, ar->y, ar->w, ar->h);
  else
    send_area_raw(serv, ar);

  mark_data(serv);
}

void write_data(vncserv *serv, void *data, int n) {
  if (serv->io.write) {
    int err;
    err = serv->io.write(serv, data, n, serv->meta);
    if (err != VNCSERV_OK)   serv->errorhasoccured = err;
  }
}

void mark_data(vncserv *serv) {
  if (serv->io.setmark) {
    int err;
    err = serv->io.setmark(serv, serv->meta);
    if (err != VNCSERV_OK)   serv->errorhasoccured = err;
  }
}

void area_poll(vncserv *serv) {
  if (serv->io.getmark(serv, serv->meta) == 0)
  {
    if (!area_is_empty(&serv->nextbox[0])) {
      send_area(serv, &serv->nextbox[0]);
      reset_area(&serv->nextbox[0]);
    }
  }
}
