Initial revision


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@2 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/rfb/Blacklist.cxx b/rfb/Blacklist.cxx
new file mode 100644
index 0000000..4c4f95b
--- /dev/null
+++ b/rfb/Blacklist.cxx
@@ -0,0 +1,86 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <rfb/Blacklist.h>
+#include <rfb/Configuration.h>
+
+using namespace rfb;
+
+IntParameter Blacklist::threshold("BlacklistThreshold",
+                              "The number of unauthenticated connection attempts allowed from any "
+                              "individual host before that host is black-listed",
+                              5);
+IntParameter Blacklist::initialTimeout("BlacklistTimeout",
+                              "The initial timeout applied when a host is first black-listed.  "
+                              "The host cannot re-attempt a connection until the timeout expires.",
+                              10);
+
+
+Blacklist::Blacklist() {
+}
+
+Blacklist::~Blacklist() {
+  // Free the map keys
+  BlacklistMap::iterator i;
+  for (i=blm.begin(); i!=blm.end(); i++) {
+    strFree((char*)(*i).first);
+  }
+}
+
+bool Blacklist::isBlackmarked(const char* name) {
+  BlacklistMap::iterator i = blm.find(name);
+  if (i == blm.end()) {
+    // Entry is not already black-marked.
+    // Create the entry unmarked, unblocked,
+    // with suitable defaults set.
+    BlacklistInfo bi;
+    bi.marks = 1;
+    bi.blockUntil = 0;
+    bi.blockTimeout = initialTimeout;
+    blm[strDup(name)] = bi;
+    i = blm.find(name);
+  }
+
+  // Entry exists - has it reached the threshold yet?
+  if ((*i).second.marks >= threshold) {
+    // Yes - entry is blocked - has the timeout expired?        
+    time_t now = time(0);
+    if (now >= (*i).second.blockUntil) {
+      // Timeout has expired.  Reset timeout and allow
+      // a re-try.
+      (*i).second.blockUntil = now + (*i).second.blockTimeout;
+      (*i).second.blockTimeout = (*i).second.blockTimeout * 2;
+      return false;
+    }
+    // Blocked and timeout still in effect - reject!
+    return true;
+  }
+
+  // We haven't reached the threshold yet.
+  // Increment the black-mark counter but allow
+  // the entry to pass.
+  (*i).second.marks++;
+  return false;
+}
+
+void Blacklist::clearBlackmark(const char* name) {
+  BlacklistMap::iterator i = blm.find(name);
+  if (i != blm.end()) {
+    strFree((char*)(*i).first);
+    blm.erase(i);
+  }
+}
diff --git a/rfb/Blacklist.h b/rfb/Blacklist.h
new file mode 100644
index 0000000..4df7ec8
--- /dev/null
+++ b/rfb/Blacklist.h
@@ -0,0 +1,91 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+//
+// Blacklist.h - Handling of black-listed entities.
+// Just keeps a table mapping strings to timing information, including
+// how many times the entry has been black-listed and when to next
+// put it on probation (e.g. allow a connection in from the host, and
+// re-blacklist it if that fails). 
+//
+
+#ifndef __RFB_BLACKLIST_H__
+#define __RFB_BLACKLIST_H__
+
+#include <string.h>
+#include <time.h>
+#include <map>
+
+#include <rfb/Configuration.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+  //
+  // -=- Blacklist handler
+  //
+  // Parameters include a threshold after which to blacklist the named
+  // host, and a timeout after which to re-consider them.
+  //
+  // Threshold means that isBlackmarked can be called that number of times
+  // before it will return true.
+  //
+  // Timeout means that after that many seconds, the next call to isBlackmarked
+  // will return false.  At the same time, the timeout is doubled, so that the
+  // next calls will fail, until the timeout expires again or clearBlackmark is
+  // called.
+  //
+  // When clearBlackMark is called, the corresponding entry is completely
+  // removed, causing the next isBlackmarked call to return false.
+
+  // KNOWN BUG:  Client can keep making rejected requests, thus increasing
+  // their timeout.  If client does this for 30 years, timeout may wrap round
+  // to a very small value again.
+
+  // THIS CLASS IS NOT THREAD-SAFE!
+
+  class Blacklist {
+  public:
+    Blacklist();
+    ~Blacklist();
+
+    bool isBlackmarked(const char* name);
+    void clearBlackmark(const char* name);
+
+    static IntParameter threshold;
+    static IntParameter initialTimeout;
+
+  protected:
+    struct ltStr {
+      bool operator()(const char* s1, const char* s2) const {
+        return strcmp(s1, s2) < 0;
+      };
+    };
+    struct BlacklistInfo {
+      int marks;
+      time_t blockUntil;
+      unsigned int blockTimeout;
+    };
+    typedef std::map<const char*,BlacklistInfo,ltStr> BlacklistMap;
+    BlacklistMap blm;
+  };
+
+}
+
+#endif
+
diff --git a/rfb/CConnection.cxx b/rfb/CConnection.cxx
new file mode 100644
index 0000000..c6a3eed
--- /dev/null
+++ b/rfb/CConnection.cxx
@@ -0,0 +1,291 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <rfb/Exception.h>
+#include <rfb/CMsgReaderV3.h>
+#include <rfb/CMsgWriterV3.h>
+#include <rfb/CSecurity.h>
+#include <rfb/secTypes.h>
+#include <rfb/CConnection.h>
+#include <rfb/util.h>
+
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+
+static LogWriter vlog("CConnection");
+
+CConnection::CConnection()
+  : is(0), os(0), reader_(0), writer_(0),
+    shared(false), security(0), nSecTypes(0), clientSecTypeOrder(false),
+    state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false)
+{
+}
+
+CConnection::~CConnection()
+{
+  if (security) security->destroy();
+  deleteReaderAndWriter();
+}
+
+void CConnection::setServerName(const char* serverName_) {
+  serverName.buf = strDup(serverName_);
+}
+
+void CConnection::deleteReaderAndWriter()
+{
+  delete reader_;
+  reader_ = 0;
+  delete writer_;
+  writer_ = 0;
+}
+
+void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
+{
+  is = is_;
+  os = os_;
+}
+
+void CConnection::addSecType(rdr::U8 secType)
+{
+  if (nSecTypes == maxSecTypes)
+    throw Exception("too many security types");
+  secTypes[nSecTypes++] = secType;
+}
+
+void CConnection::setClientSecTypeOrder(bool clientOrder) {
+  clientSecTypeOrder = clientOrder;
+}
+
+void CConnection::initialiseProtocol()
+{
+  state_ = RFBSTATE_PROTOCOL_VERSION;
+}
+
+void CConnection::processMsg()
+{
+  switch (state_) {
+
+  case RFBSTATE_PROTOCOL_VERSION: processVersionMsg();       break;
+  case RFBSTATE_SECURITY_TYPES:   processSecurityTypesMsg(); break;
+  case RFBSTATE_SECURITY:         processSecurityMsg();      break;
+  case RFBSTATE_SECURITY_RESULT:  processSecurityResultMsg(); break;
+  case RFBSTATE_INITIALISATION:   processInitMsg();          break;
+  case RFBSTATE_NORMAL:           reader_->readMsg();        break;
+  case RFBSTATE_UNINITIALISED:
+    throw Exception("CConnection::processMsg: not initialised yet?");
+  default:
+    throw Exception("CConnection::processMsg: invalid state");
+  }
+}
+
+void CConnection::processVersionMsg()
+{
+  vlog.debug("reading protocol version");
+  bool done;
+  if (!cp.readVersion(is, &done)) {
+    state_ = RFBSTATE_INVALID;
+    throw Exception("reading version failed: not an RFB server?");
+  }
+  if (!done) return;
+
+  vlog.info("Server supports RFB protocol version %d.%d",
+            cp.majorVersion, cp.minorVersion);
+
+  // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
+  if (cp.beforeVersion(3,3)) {
+    char msg[256];
+    sprintf(msg,"Server gave unsupported RFB protocol version %d.%d",
+            cp.majorVersion, cp.minorVersion);
+    vlog.error(msg);
+    state_ = RFBSTATE_INVALID;
+    throw Exception(msg);
+  } else if (useProtocol3_3 || cp.beforeVersion(3,7)) {
+    cp.setVersion(3,3);
+  } else if (cp.afterVersion(3,8)) {
+    cp.setVersion(3,8);
+  }
+
+  cp.writeVersion(os);
+  state_ = RFBSTATE_SECURITY_TYPES;
+
+  vlog.info("Using RFB protocol version %d.%d",
+            cp.majorVersion, cp.minorVersion);
+}
+
+
+void CConnection::processSecurityTypesMsg()
+{
+  vlog.debug("processing security types message");
+
+  int secType = secTypeInvalid;
+
+  if (cp.isVersion(3,3)) {
+
+    // legacy 3.3 server may only offer "vnc authentication" or "none"
+
+    secType = is->readU32();
+    if (secType == secTypeInvalid) {
+      throwConnFailedException();
+
+    } else if (secType == secTypeNone || secType == secTypeVncAuth) {
+      int j;
+      for (j = 0; j < nSecTypes; j++)
+        if (secTypes[j] == secType) break;
+      if (j == nSecTypes)
+        secType = secTypeInvalid;
+    } else {
+      vlog.error("Unknown 3.3 security type %d", secType);
+      throw Exception("Unknown 3.3 security type");
+    }
+
+  } else {
+
+    // >=3.7 server will offer us a list
+
+    int nServerSecTypes = is->readU8();
+    if (nServerSecTypes == 0)
+      throwConnFailedException();
+
+    int secTypePos = nSecTypes;
+    for (int i = 0; i < nServerSecTypes; i++) {
+      rdr::U8 serverSecType = is->readU8();
+      vlog.debug("Server offers security type %s(%d)",
+                 secTypeName(serverSecType),serverSecType);
+
+      // If we haven't already chosen a secType, try this one
+      // If we are using the client's preference for types,
+      // we keep trying types, to find the one that matches and
+      // which appears first in the client's list of supported types.
+      if (secType == secTypeInvalid || clientSecTypeOrder) {
+        for (int j = 0; j < nSecTypes; j++) {
+          if (secTypes[j] == serverSecType && j < secTypePos) {
+            secType = secTypes[j];
+            secTypePos = j;
+            break;
+          }
+        }
+        // NB: Continue reading the remaining server secTypes, but ignore them
+      }
+    }
+
+    // Inform the server of our decision
+    if (secType != secTypeInvalid) {
+      os->writeU8(secType);
+      os->flush();
+      vlog.debug("Choosing security type %s(%d)",secTypeName(secType),secType);
+    }
+  }
+
+  if (secType == secTypeInvalid) {
+    state_ = RFBSTATE_INVALID;
+    vlog.error("No matching security types");
+    throw Exception("No matching security types");
+  }
+
+  state_ = RFBSTATE_SECURITY;
+  security = getCSecurity(secType);
+  processSecurityMsg();
+}
+
+void CConnection::processSecurityMsg()
+{
+  vlog.debug("processing security message");
+  bool done;
+  if (!security->processMsg(this, &done))
+    throwAuthFailureException();
+  if (done) {
+    state_ = RFBSTATE_SECURITY_RESULT;
+    processSecurityResultMsg();
+  }
+}
+
+void CConnection::processSecurityResultMsg()
+{
+  vlog.debug("processing security result message");
+  int result;
+  if (cp.beforeVersion(3,8) && security->getType() == secTypeNone) {
+    result = secResultOK;
+  } else {
+    if (!is->checkNoWait(1)) return;
+    result = is->readU32();
+  }
+  switch (result) {
+  case secResultOK:
+    securityCompleted();
+    break;
+  case secResultFailed:
+    vlog.debug("auth failed");
+    throwAuthFailureException();
+  case secResultTooMany:
+    vlog.debug("auth failed - too many tries");
+    throwAuthFailureException();
+  default:
+    vlog.error("unknown security result");
+    throwAuthFailureException();
+  };
+}
+
+void CConnection::processInitMsg()
+{
+  vlog.debug("reading server initialisation");
+  reader_->readServerInit();
+}
+
+void CConnection::throwAuthFailureException()
+{
+  CharArray reason;
+  vlog.debug("state=%d, ver=%d.%d", state(), cp.majorVersion, cp.minorVersion);
+  if (state()==RFBSTATE_SECURITY_RESULT && !cp.beforeVersion(3,8)) {
+    reason.buf = is->readString();
+  } else {
+    reason.buf = strDup("Authentication failure");
+  }
+  state_ = RFBSTATE_INVALID;
+  vlog.error(reason.buf);
+  throw AuthFailureException(reason.buf);
+}
+
+void CConnection::throwConnFailedException()
+{
+  state_ = RFBSTATE_INVALID;
+  CharArray reason;
+  reason.buf = is->readString();
+  throw ConnFailedException(reason.buf);
+}
+
+void CConnection::securityCompleted()
+{
+  state_ = RFBSTATE_INITIALISATION;
+  reader_ = new CMsgReaderV3(this, is);
+  writer_ = new CMsgWriterV3(&cp, os);
+  vlog.debug("Authentication success!");
+  authSuccess();
+  writer_->writeClientInit(shared);
+}
+
+void CConnection::authSuccess()
+{
+}
+
+void CConnection::serverInit()
+{
+  state_ = RFBSTATE_NORMAL;
+  vlog.debug("initialisation done");
+}
diff --git a/rfb/CConnection.h b/rfb/CConnection.h
new file mode 100644
index 0000000..480fca3
--- /dev/null
+++ b/rfb/CConnection.h
@@ -0,0 +1,178 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// CConnection - class on the client side representing a connection to a
+// server.  A derived class should override methods appropriately.
+//
+
+#ifndef __RFB_CCONNECTION_H__
+#define __RFB_CCONNECTION_H__
+
+#include <rdr/InStream.h>
+#include <rdr/OutStream.h>
+#include <rfb/CMsgHandler.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+  class CMsgReader;
+  class CMsgWriter;
+  class CSecurity;
+  class IdentityVerifier;
+
+  class CConnection : public CMsgHandler {
+  public:
+
+    CConnection();
+    virtual ~CConnection();
+
+    // ***
+    void setServerName(const char* serverName_);
+
+    // Methods to initialise the connection
+
+    // setStreams() sets the streams to be used for the connection.  These must
+    // be set before initialiseProtocol() and processMsg() are called.  The
+    // CSecurity object may call setStreams() again to provide alternative
+    // streams over which the RFB protocol is sent (i.e. encrypting/decrypting
+    // streams).  Ownership of the streams remains with the caller
+    // (i.e. SConnection will not delete them).
+    void setStreams(rdr::InStream* is, rdr::OutStream* os);
+
+    // addSecType() should be called once for each security type which the
+    // client supports.  The order in which they're added is such that the
+    // first one is most preferred.
+    void addSecType(rdr::U8 secType);
+
+    // setClientSecTypeOrder() determines whether the client should obey
+    // the server's security type preference, by picking the first server security
+    // type that the client supports, or whether it should pick the first type
+    // that the server supports, from the client-supported list of types.
+    void setClientSecTypeOrder(bool clientOrder);
+
+    // setShared sets the value of the shared flag which will be sent to the
+    // server upon initialisation.
+    void setShared(bool s) { shared = s; }
+
+    // setProtocol3_3 configures whether or not the CConnection should
+    // only ever support protocol version 3.3
+    void setProtocol3_3(bool s) {useProtocol3_3 = s;}
+
+    // initialiseProtocol() should be called once the streams and security
+    // types are set.  Subsequently, processMsg() should be called whenever
+    // there is data to read on the InStream.
+    void initialiseProtocol();
+
+    // processMsg() should be called whenever there is either:
+    // - data available on the underlying network stream
+    //   In this case, processMsg may return without processing an RFB message,
+    //   if the available data does not result in an RFB message being ready
+    //   to handle. e.g. if data is encrypted.
+    // NB: This makes it safe to call processMsg() in response to select()
+    // - data available on the CConnection's current InStream
+    //   In this case, processMsg should always process the available RFB
+    //   message before returning.
+    // NB: In either case, you must have called initialiseProtocol() first.
+    void processMsg();
+
+
+    // Methods to be overridden in a derived class
+
+    // getCSecurity() gets the CSecurity object for the given type.  The type
+    // is guaranteed to be one of the secTypes passed in to addSecType().  The
+    // CSecurity object's destroy() method will be called by the CConnection
+    // from its destructor.
+    virtual CSecurity* getCSecurity(int secType)=0;
+
+    // getCurrentCSecurity() gets the CSecurity instance used for this connection.
+    const CSecurity* getCurrentCSecurity() const {return security;} 
+
+    // getIdVerifier() returns the identity verifier associated with the connection.
+    // Ownership of the IdentityVerifier is retained by the CConnection instance.
+    virtual IdentityVerifier* getIdentityVerifier() {return 0;}
+
+    // authSuccess() is called when authentication has succeeded.
+    virtual void authSuccess();
+
+    // serverInit() is called when the ServerInit message is received.  The
+    // derived class must call on to CConnection::serverInit().
+    virtual void serverInit();
+
+
+    // Other methods
+
+    // deleteReaderAndWriter() deletes the reader and writer associated with
+    // this connection.  This may be useful if you want to delete the streams
+    // before deleting the SConnection to make sure that no attempt by the
+    // SConnection is made to read or write.
+    // XXX Do we really need this at all???
+    void deleteReaderAndWriter();
+
+    CMsgReader* reader() { return reader_; }
+    CMsgWriter* writer() { return writer_; }
+
+    rdr::InStream* getInStream() { return is; }
+    rdr::OutStream* getOutStream() { return os; }
+
+    char* getServerName() {return strDup(serverName.buf);}
+
+    enum stateEnum {
+      RFBSTATE_UNINITIALISED,
+      RFBSTATE_PROTOCOL_VERSION,
+      RFBSTATE_SECURITY_TYPES,
+      RFBSTATE_SECURITY,
+      RFBSTATE_SECURITY_RESULT,
+      RFBSTATE_INITIALISATION,
+      RFBSTATE_NORMAL,
+      RFBSTATE_INVALID
+    };
+
+    stateEnum state() { return state_; }
+
+  protected:
+    void setState(stateEnum s) { state_ = s; }
+
+  private:
+    void processVersionMsg();
+    void processSecurityTypesMsg();
+    void processSecurityMsg();
+    void processSecurityResultMsg();
+    void processInitMsg();
+    void throwAuthFailureException();
+    void throwConnFailedException();
+    void securityCompleted();
+
+    rdr::InStream* is;
+    rdr::OutStream* os;
+    CMsgReader* reader_;
+    CMsgWriter* writer_;
+    bool deleteStreamsWhenDone;
+    bool shared;
+    CSecurity* security;
+    enum { maxSecTypes = 8 };
+    int nSecTypes;
+    rdr::U8 secTypes[maxSecTypes];
+    bool clientSecTypeOrder;
+    stateEnum state_;
+
+    CharArray serverName;
+
+    bool useProtocol3_3;
+  };
+}
+#endif
diff --git a/rfb/CMsgHandler.cxx b/rfb/CMsgHandler.cxx
new file mode 100644
index 0000000..1010916
--- /dev/null
+++ b/rfb/CMsgHandler.cxx
@@ -0,0 +1,99 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <rfb/Exception.h>
+#include <rfb/CMsgHandler.h>
+
+using namespace rfb;
+
+CMsgHandler::CMsgHandler()
+{
+}
+
+CMsgHandler::~CMsgHandler()
+{
+}
+
+void CMsgHandler::setDesktopSize(int width, int height)
+{
+  cp.width = width;
+  cp.height = height;
+}
+
+void CMsgHandler::setCursor(const Point& hotspot, const Point& size, void* data, void* mask)
+{
+}
+
+void CMsgHandler::setPixelFormat(const PixelFormat& pf)
+{
+  cp.setPF(pf);
+}
+
+void CMsgHandler::setName(const char* name)
+{
+  cp.setName(name);
+}
+
+void CMsgHandler::serverInit()
+{
+  throw Exception("CMsgHandler::serverInit called");
+}
+
+void CMsgHandler::framebufferUpdateStart()
+{
+}
+
+void CMsgHandler::framebufferUpdateEnd()
+{
+}
+
+void CMsgHandler::beginRect(const Rect& r, unsigned int encoding)
+{
+}
+
+void CMsgHandler::endRect(const Rect& r, unsigned int encoding)
+{
+}
+
+
+void CMsgHandler::setColourMapEntries(int firstColour, int nColours,
+                                      rdr::U16* rgbs)
+{
+  throw Exception("CMsgHandler::setColourMapEntries called");
+}
+
+void CMsgHandler::bell()
+{
+}
+
+void CMsgHandler::serverCutText(const char* str, int len)
+{
+}
+
+void CMsgHandler::fillRect(const Rect& r, Pixel pix)
+{
+}
+
+void CMsgHandler::imageRect(const Rect& r, void* pixels)
+{
+}
+
+void CMsgHandler::copyRect(const Rect& r, int srcX, int srcY)
+{
+}
+
+
diff --git a/rfb/CMsgHandler.h b/rfb/CMsgHandler.h
new file mode 100644
index 0000000..7a69f14
--- /dev/null
+++ b/rfb/CMsgHandler.h
@@ -0,0 +1,62 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// CMsgHandler - class to handle incoming messages on the client side.
+//
+
+#ifndef __RFB_CMSGHANDLER_H__
+#define __RFB_CMSGHANDLER_H__
+
+#include <rdr/types.h>
+#include <rfb/Pixel.h>
+#include <rfb/ConnParams.h>
+#include <rfb/Rect.h>
+
+namespace rdr { class InStream; }
+
+namespace rfb {
+
+  class CMsgHandler {
+  public:
+    CMsgHandler();
+    virtual ~CMsgHandler();
+
+    virtual void setDesktopSize(int w, int h);
+    virtual void setCursor(const Point& hotspot, const Point& size, void* data, void* mask);
+    virtual void setPixelFormat(const PixelFormat& pf);
+    virtual void setName(const char* name);
+    virtual void serverInit();
+
+    virtual void framebufferUpdateStart();
+    virtual void framebufferUpdateEnd();
+    virtual void beginRect(const Rect& r, unsigned int encoding);
+    virtual void endRect(const Rect& r, unsigned int encoding);
+
+    virtual void setColourMapEntries(int firstColour, int nColours,
+				     rdr::U16* rgbs);
+    virtual void bell();
+    virtual void serverCutText(const char* str, int len);
+
+    virtual void fillRect(const Rect& r, Pixel pix);
+    virtual void imageRect(const Rect& r, void* pixels);
+    virtual void copyRect(const Rect& r, int srcX, int srcY);
+
+    ConnParams cp;
+  };
+}
+#endif
diff --git a/rfb/CMsgReader.cxx b/rfb/CMsgReader.cxx
new file mode 100644
index 0000000..46973eb
--- /dev/null
+++ b/rfb/CMsgReader.cxx
@@ -0,0 +1,167 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <stdio.h>
+#include <rdr/InStream.h>
+#include <rfb/Exception.h>
+#include <rfb/util.h>
+#include <rfb/CMsgHandler.h>
+#include <rfb/CMsgReader.h>
+
+using namespace rfb;
+
+CMsgReader::CMsgReader(CMsgHandler* handler_, rdr::InStream* is_)
+  : imageBufIdealSize(0), handler(handler_), is(is_),
+    imageBuf(0), imageBufSize(0)
+{
+  for (unsigned int i = 0; i <= encodingMax; i++) {
+    decoders[i] = 0;
+  }
+}
+
+CMsgReader::~CMsgReader()
+{
+  for (unsigned int i = 0; i <= encodingMax; i++) {
+    delete decoders[i];
+  }
+  delete [] imageBuf;
+}
+
+void CMsgReader::endMsg()
+{
+}
+
+void CMsgReader::readSetColourMapEntries()
+{
+  is->skip(1);
+  int firstColour = is->readU16();
+  int nColours = is->readU16();
+  rdr::U16Array rgbs(nColours * 3);
+  for (int i = 0; i < nColours * 3; i++)
+    rgbs.buf[i] = is->readU16();
+  endMsg();
+  handler->setColourMapEntries(firstColour, nColours, rgbs.buf);
+}
+
+void CMsgReader::readBell()
+{
+  endMsg();
+  handler->bell();
+}
+
+void CMsgReader::readServerCutText()
+{
+  is->skip(3);
+  int len = is->readU32();
+  if (len > 256*1024) {
+    is->skip(len);
+    fprintf(stderr,"cut text too long (%d bytes) - ignoring\n",len);
+    return;
+  }
+  CharArray ca(len+1);
+  ca.buf[len] = 0;
+  is->readBytes(ca.buf, len);
+  endMsg();
+  handler->serverCutText(ca.buf, len);
+}
+
+void CMsgReader::readFramebufferUpdateStart()
+{
+  endMsg();
+  handler->framebufferUpdateStart();
+}
+
+void CMsgReader::readFramebufferUpdateEnd()
+{
+  endMsg();
+  handler->framebufferUpdateEnd();
+}
+
+void CMsgReader::readRect(const Rect& r, unsigned int encoding)
+{
+  if ((r.br.x > handler->cp.width) || (r.br.y > handler->cp.height)) {
+    fprintf(stderr, "Rect too big: %dx%d at %d,%d exceeds %dx%d\n",
+	    r.width(), r.height(), r.tl.x, r.tl.y,
+            handler->cp.width, handler->cp.height);
+    throw Exception("Rect too big");
+  }
+
+  if (r.is_empty())
+    fprintf(stderr, "Warning: zero size rect\n");
+
+  handler->beginRect(r, encoding);
+
+  if (encoding == encodingCopyRect) {
+    readCopyRect(r);
+  } else {
+    if (!decoders[encoding]) {
+      decoders[encoding] = Decoder::createDecoder(encoding, this);
+      if (!decoders[encoding]) {
+        fprintf(stderr, "Unknown rect encoding %d\n", encoding);
+        throw Exception("Unknown rect encoding");
+      }
+    }
+    decoders[encoding]->readRect(r, handler);
+  }
+
+  handler->endRect(r, encoding);
+}
+
+void CMsgReader::readCopyRect(const Rect& r)
+{
+  int srcX = is->readU16();
+  int srcY = is->readU16();
+  handler->copyRect(r, srcX, srcY);
+}
+
+void CMsgReader::readSetCursor(const Point& hotspot, const Point& size)
+{
+  int data_len = size.x * size.y * (handler->cp.pf().bpp/8);
+  int mask_len = ((size.x+7)/8) * size.y;
+  rdr::U8Array data(data_len);
+  rdr::U8Array mask(mask_len);
+
+  is->readBytes(data.buf, data_len);
+  is->readBytes(mask.buf, mask_len);
+
+  handler->setCursor(hotspot, size, data.buf, mask.buf);
+}
+
+rdr::U8* CMsgReader::getImageBuf(int required, int requested, int* nPixels)
+{
+  int requiredBytes = required * (handler->cp.pf().bpp / 8);
+  int requestedBytes = requested * (handler->cp.pf().bpp / 8);
+  int size = requestedBytes;
+  if (size > imageBufIdealSize) size = imageBufIdealSize;
+
+  if (size < requiredBytes)
+    size = requiredBytes;
+
+  if (imageBufSize < size) {
+    imageBufSize = size;
+    delete [] imageBuf;
+    imageBuf = new rdr::U8[imageBufSize];
+  }
+  if (nPixels)
+    *nPixels = imageBufSize / (handler->cp.pf().bpp / 8);
+  return imageBuf;
+}
+
+int CMsgReader::bpp()
+{
+  return handler->cp.pf().bpp;
+}
diff --git a/rfb/CMsgReader.h b/rfb/CMsgReader.h
new file mode 100644
index 0000000..8b4638c
--- /dev/null
+++ b/rfb/CMsgReader.h
@@ -0,0 +1,75 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// CMsgReader - class for reading RFB messages on the server side
+// (i.e. messages from client to server).
+//
+
+#ifndef __RFB_CMSGREADER_H__
+#define __RFB_CMSGREADER_H__
+
+#include <rdr/types.h>
+#include <rfb/encodings.h>
+#include <rfb/Decoder.h>
+
+namespace rdr { class InStream; }
+
+namespace rfb {
+  class CMsgHandler;
+  struct Rect;
+
+  class CMsgReader {
+  public:
+    virtual ~CMsgReader();
+
+    virtual void readServerInit()=0;
+
+    // readMsg() reads a message, calling the handler as appropriate.
+    virtual void readMsg()=0;
+
+    rdr::InStream* getInStream() { return is; }
+    rdr::U8* getImageBuf(int required, int requested=0, int* nPixels=0);
+    int bpp();
+
+    int imageBufIdealSize;
+
+  protected:
+    virtual void readSetColourMapEntries();
+    virtual void readBell();
+    virtual void readServerCutText();
+
+    virtual void endMsg();
+
+    virtual void readFramebufferUpdateStart();
+    virtual void readFramebufferUpdateEnd();
+    virtual void readRect(const Rect& r, unsigned int encoding);
+
+    virtual void readCopyRect(const Rect& r);
+
+    virtual void readSetCursor(const Point& hotspot, const Point& size);
+
+    CMsgReader(CMsgHandler* handler, rdr::InStream* is);
+
+    CMsgHandler* handler;
+    rdr::InStream* is;
+    Decoder* decoders[encodingMax+1];
+    rdr::U8* imageBuf;
+    int imageBufSize;
+  };
+}
+#endif
diff --git a/rfb/CMsgReaderV3.cxx b/rfb/CMsgReaderV3.cxx
new file mode 100644
index 0000000..1f974fd
--- /dev/null
+++ b/rfb/CMsgReaderV3.cxx
@@ -0,0 +1,97 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <rfb/PixelFormat.h>
+#include <rfb/msgTypes.h>
+#include <rfb/Exception.h>
+#include <rdr/InStream.h>
+#include <rfb/CMsgReaderV3.h>
+#include <rfb/CMsgHandler.h>
+
+using namespace rfb;
+
+CMsgReaderV3::CMsgReaderV3(CMsgHandler* handler, rdr::InStream* is)
+  : CMsgReader(handler, is), nUpdateRectsLeft(0)
+{
+}
+
+CMsgReaderV3::~CMsgReaderV3()
+{
+}
+
+void CMsgReaderV3::readServerInit()
+{
+  int width = is->readU16();
+  int height = is->readU16();
+  handler->setDesktopSize(width, height);
+  PixelFormat pf;
+  pf.read(is);
+  handler->setPixelFormat(pf);
+  char* name = is->readString();
+  handler->setName(name);
+  delete [] name;
+  endMsg();
+  handler->serverInit();
+}
+
+void CMsgReaderV3::readMsg()
+{
+  if (nUpdateRectsLeft == 0) {
+
+    int type = is->readU8();
+    switch (type) {
+    case msgTypeFramebufferUpdate:   readFramebufferUpdate(); break;
+    case msgTypeSetColourMapEntries: readSetColourMapEntries(); break;
+    case msgTypeBell:                readBell(); break;
+    case msgTypeServerCutText:       readServerCutText(); break;
+    default:
+      fprintf(stderr, "unknown message type %d\n", type);
+      throw Exception("unknown message type");
+    }
+
+  } else {
+
+    int x = is->readU16();
+    int y = is->readU16();
+    int w = is->readU16();
+    int h = is->readU16();
+    unsigned int encoding = is->readU32();
+
+    switch (encoding) {
+    case pseudoEncodingDesktopSize:
+      handler->setDesktopSize(w, h);
+      break;
+    case pseudoEncodingCursor:
+      readSetCursor(Point(x, y), Point(w, h));
+      break;
+    default:
+      readRect(Rect(x, y, x+w, y+h), encoding);
+      break;
+    };
+
+    nUpdateRectsLeft--;
+    if (nUpdateRectsLeft == 0) handler->framebufferUpdateEnd();
+  }
+}
+
+void CMsgReaderV3::readFramebufferUpdate()
+{
+  is->skip(1);
+  nUpdateRectsLeft = is->readU16();
+  endMsg();
+  handler->framebufferUpdateStart();
+}
diff --git a/rfb/CMsgReaderV3.h b/rfb/CMsgReaderV3.h
new file mode 100644
index 0000000..93c8c6a
--- /dev/null
+++ b/rfb/CMsgReaderV3.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_CMSGREADERV3_H__
+#define __RFB_CMSGREADERV3_H__
+
+#include <rfb/CMsgReader.h>
+
+namespace rfb {
+  class CMsgReaderV3 : public CMsgReader {
+  public:
+    CMsgReaderV3(CMsgHandler* handler, rdr::InStream* is);
+    virtual ~CMsgReaderV3();
+    virtual void readServerInit();
+    virtual void readMsg();
+  private:
+    void readFramebufferUpdate();
+    int nUpdateRectsLeft;
+  };
+}
+#endif
diff --git a/rfb/CMsgWriter.cxx b/rfb/CMsgWriter.cxx
new file mode 100644
index 0000000..a7e4fb9
--- /dev/null
+++ b/rfb/CMsgWriter.cxx
@@ -0,0 +1,130 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <stdio.h>
+#include <rdr/OutStream.h>
+#include <rfb/msgTypes.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/Rect.h>
+#include <rfb/ConnParams.h>
+#include <rfb/Decoder.h>
+#include <rfb/CMsgWriter.h>
+
+using namespace rfb;
+
+CMsgWriter::CMsgWriter(ConnParams* cp_, rdr::OutStream* os_)
+  : cp(cp_), os(os_)
+{
+}
+
+CMsgWriter::~CMsgWriter()
+{
+}
+
+void CMsgWriter::writeSetPixelFormat(const PixelFormat& pf)
+{
+  startMsg(msgTypeSetPixelFormat);                                 
+  os->pad(3);
+  pf.write(os);
+  endMsg();
+}
+
+void CMsgWriter::writeSetEncodings(int nEncodings, rdr::U32* encodings)
+{
+  startMsg(msgTypeSetEncodings);
+  os->skip(1);
+  os->writeU16(nEncodings);
+  for (int i = 0; i < nEncodings; i++)
+    os->writeU32(encodings[i]);
+  endMsg();
+}
+
+// Ask for encodings based on which decoders are supported.  Assumes higher
+// encoding numbers are more desirable.
+
+void CMsgWriter::writeSetEncodings(int preferredEncoding, bool useCopyRect)
+{
+  int nEncodings = 0;
+  rdr::U32 encodings[encodingMax+2];
+  if (cp->supportsLocalCursor)
+    encodings[nEncodings++] = pseudoEncodingCursor;
+  if (cp->supportsDesktopResize)
+    encodings[nEncodings++] = pseudoEncodingDesktopSize;
+  if (Decoder::supported(preferredEncoding)) {
+    encodings[nEncodings++] = preferredEncoding;
+  }
+  if (useCopyRect) {
+    encodings[nEncodings++] = encodingCopyRect;
+  }
+  for (int i = encodingMax; i >= 0; i--) {
+    if (i != preferredEncoding && Decoder::supported(i)) {
+      encodings[nEncodings++] = i;
+    }
+  }
+  writeSetEncodings(nEncodings, encodings);
+}
+  
+void CMsgWriter::writeFramebufferUpdateRequest(const Rect& r, bool incremental)
+{
+  startMsg(msgTypeFramebufferUpdateRequest);
+  os->writeU8(incremental);
+  os->writeU16(r.tl.x);
+  os->writeU16(r.tl.y);
+  os->writeU16(r.width());
+  os->writeU16(r.height());
+  endMsg();
+}
+
+
+void CMsgWriter::writeKeyEvent(rdr::U32 key, bool down)
+{
+  startMsg(msgTypeKeyEvent);
+  os->writeU8(down);
+  os->pad(2);
+  os->writeU32(key);
+  endMsg();
+}
+
+
+void CMsgWriter::writePointerEvent(int x, int y, int buttonMask)
+{
+  if (x < 0) x = 0;
+  if (y < 0) y = 0;
+  if (x >= cp->width) x = cp->width - 1;
+  if (y >= cp->height) y = cp->height - 1;
+
+  startMsg(msgTypePointerEvent);
+  os->writeU8(buttonMask);
+  os->writeU16(x);
+  os->writeU16(y);
+  endMsg();
+}
+
+
+void CMsgWriter::writeClientCutText(const char* str, int len)
+{
+  startMsg(msgTypeClientCutText);
+  os->pad(3);
+  os->writeU32(len);
+  os->writeBytes(str, len);
+  endMsg();
+}
+
+void CMsgWriter::setOutStream(rdr::OutStream* os_)
+{
+  os = os_;
+}
diff --git a/rfb/CMsgWriter.h b/rfb/CMsgWriter.h
new file mode 100644
index 0000000..8d6e373
--- /dev/null
+++ b/rfb/CMsgWriter.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// CMsgWriter - class for writing RFB messages on the server side.
+//
+
+#ifndef __RFB_CMSGWRITER_H__
+#define __RFB_CMSGWRITER_H__
+
+#include <rdr/types.h>
+
+namespace rdr { class OutStream; }
+
+namespace rfb {
+
+  class PixelFormat;
+  class ConnParams;
+  struct Rect;
+
+  class CMsgWriter {
+  public:
+    virtual ~CMsgWriter();
+
+    virtual void writeClientInit(bool shared)=0;
+
+    virtual void writeSetPixelFormat(const PixelFormat& pf);
+    virtual void writeSetEncodings(int nEncodings, rdr::U32* encodings);
+    virtual void writeSetEncodings(int preferredEncoding, bool useCopyRect);
+    virtual void writeFramebufferUpdateRequest(const Rect& r,bool incremental);
+    virtual void writeKeyEvent(rdr::U32 key, bool down);
+    virtual void writePointerEvent(int x, int y, int buttonMask);
+    virtual void writeClientCutText(const char* str, int len);
+
+    virtual void startMsg(int type)=0;
+    virtual void endMsg()=0;
+
+    virtual void setOutStream(rdr::OutStream* os);
+
+    ConnParams* getConnParams() { return cp; }
+    rdr::OutStream* getOutStream() { return os; }
+
+  protected:
+    CMsgWriter(ConnParams* cp, rdr::OutStream* os);
+
+    ConnParams* cp;
+    rdr::OutStream* os;
+  };
+}
+#endif
diff --git a/rfb/CMsgWriterV3.cxx b/rfb/CMsgWriterV3.cxx
new file mode 100644
index 0000000..a6ad237
--- /dev/null
+++ b/rfb/CMsgWriterV3.cxx
@@ -0,0 +1,49 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <rdr/OutStream.h>
+#include <rfb/msgTypes.h>
+#include <rfb/Exception.h>
+#include <rfb/ConnParams.h>
+#include <rfb/CMsgWriterV3.h>
+
+using namespace rfb;
+
+CMsgWriterV3::CMsgWriterV3(ConnParams* cp, rdr::OutStream* os)
+  : CMsgWriter(cp, os)
+{
+}
+
+CMsgWriterV3::~CMsgWriterV3()
+{
+}
+
+void CMsgWriterV3::writeClientInit(bool shared)
+{
+  os->writeU8(shared);
+  endMsg();
+}
+
+void CMsgWriterV3::startMsg(int type)
+{
+  os->writeU8(type);
+}
+
+void CMsgWriterV3::endMsg()
+{
+  os->flush();
+}
diff --git a/rfb/CMsgWriterV3.h b/rfb/CMsgWriterV3.h
new file mode 100644
index 0000000..0cf6157
--- /dev/null
+++ b/rfb/CMsgWriterV3.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_CMSGWRITERV3_H__
+#define __RFB_CMSGWRITERV3_H__
+
+#include <rfb/CMsgWriter.h>
+
+namespace rfb {
+  class CMsgWriterV3 : public CMsgWriter {
+  public:
+    CMsgWriterV3(ConnParams* cp, rdr::OutStream* os);
+    virtual ~CMsgWriterV3();
+
+    virtual void writeClientInit(bool shared);
+    virtual void startMsg(int type);
+    virtual void endMsg();
+
+  };
+}
+#endif
diff --git a/rfb/CSecurity.h b/rfb/CSecurity.h
new file mode 100644
index 0000000..639d550
--- /dev/null
+++ b/rfb/CSecurity.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// CSecurity - class on the client side for handling security handshaking.  A
+// derived class for a particular security type overrides the processMsg()
+// method.  processMsg() is called first when the security type has been
+// decided on, and will keep being called whenever there is data to read from
+// the server until either it returns false, indicating authentication/security
+// failure, or it returns with done set to true, to indicate success.
+//
+// Note that the first time processMsg() is called, there is no guarantee that
+// there is any data to read from the CConnection's InStream, but subsequent
+// calls guarantee there is at least one byte which can be read without
+// blocking.
+//
+// description is a string describing the level of encryption applied to the
+// session, or null if no encryption will be used.
+
+#ifndef __RFB_CSECURITY_H__
+#define __RFB_CSECURITY_H__
+
+namespace rfb {
+  class CConnection;
+  class CSecurity {
+  public:
+    virtual ~CSecurity() {}
+    virtual bool processMsg(CConnection* cc, bool* done)=0;
+    virtual void destroy() { delete this; }
+    virtual int getType() const = 0;
+    virtual const char* description() const = 0;
+  };
+}
+#endif
diff --git a/rfb/CSecurityNone.h b/rfb/CSecurityNone.h
new file mode 100644
index 0000000..23b36ce
--- /dev/null
+++ b/rfb/CSecurityNone.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// CSecurityNone.h
+//
+
+#ifndef __CSECURITYNONE_H__
+#define __CSECURITYNONE_H__
+
+#include <rfb/CSecurity.h>
+
+namespace rfb {
+
+  class CSecurityNone : public CSecurity {
+  public:
+    virtual bool processMsg(CConnection* cc, bool* done) {
+      *done = true; return true;
+    }
+    virtual int getType() const {return secTypeNone;}
+    virtual const char* description() const {return "No Encryption";}
+  };
+}
+#endif
diff --git a/rfb/CSecurityVncAuth.cxx b/rfb/CSecurityVncAuth.cxx
new file mode 100644
index 0000000..3d6c87c
--- /dev/null
+++ b/rfb/CSecurityVncAuth.cxx
@@ -0,0 +1,63 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// CSecurityVncAuth
+//
+
+#include <string.h>
+#include <stdio.h>
+#include <rfb/CConnection.h>
+#include <rfb/UserPasswdGetter.h>
+#include <rfb/vncAuth.h>
+#include <rfb/CSecurityVncAuth.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+
+using namespace rfb;
+
+static LogWriter vlog("VncAuth");
+
+CSecurityVncAuth::CSecurityVncAuth(UserPasswdGetter* upg_)
+  : upg(upg_)
+{
+}
+
+CSecurityVncAuth::~CSecurityVncAuth()
+{
+}
+
+bool CSecurityVncAuth::processMsg(CConnection* cc, bool* done)
+{
+  *done = false;
+  rdr::InStream* is = cc->getInStream();
+  rdr::OutStream* os = cc->getOutStream();
+
+  rdr::U8 challenge[vncAuthChallengeSize];
+  is->readBytes(challenge, vncAuthChallengeSize);
+  CharArray passwd;
+  if (!upg->getUserPasswd(0, &passwd.buf)) {
+    vlog.error("Getting password failed");
+    return false;
+  }
+  vncAuthEncryptChallenge(challenge, passwd.buf);
+  memset(passwd.buf, 0, strlen(passwd.buf));
+  os->writeBytes(challenge, vncAuthChallengeSize);
+  os->flush();
+  *done = true;
+  return true;
+}
diff --git a/rfb/CSecurityVncAuth.h b/rfb/CSecurityVncAuth.h
new file mode 100644
index 0000000..bfa40b3
--- /dev/null
+++ b/rfb/CSecurityVncAuth.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_CSECURITYVNCAUTH_H__
+#define __RFB_CSECURITYVNCAUTH_H__
+
+#include <rfb/CSecurity.h>
+#include <rfb/secTypes.h>
+#include <rfb/vncAuth.h>
+
+namespace rfb {
+
+  class UserPasswdGetter;
+
+  class CSecurityVncAuth : public CSecurity {
+  public:
+    CSecurityVncAuth(UserPasswdGetter* pg);
+    virtual ~CSecurityVncAuth();
+    virtual bool processMsg(CConnection* cc, bool* done);
+    virtual int getType() const {return secTypeVncAuth;};
+    virtual const char* description() const {return "No Encryption";}
+  private:
+    UserPasswdGetter* upg;
+  };
+}
+#endif
diff --git a/rfb/ColourCube.h b/rfb/ColourCube.h
new file mode 100644
index 0000000..904256c
--- /dev/null
+++ b/rfb/ColourCube.h
@@ -0,0 +1,96 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// ColourCube - structure to represent a colour cube.  The colour cube consists
+// of its dimensions (nRed x nGreen x nBlue) and a table mapping an (r,g,b)
+// triple to a pixel value.
+//
+// A colour cube is used in two cases.  The first is internally in a viewer
+// when it cannot use a trueColour format, nor can it have exclusive access to
+// a writable colour map.  This is most notably the case for an X viewer
+// wishing to use a PseudoColor X server's default colormap.
+//
+// The second use is on the server side when a client has asked for a colour
+// map and the server is trueColour.  Instead of setting an uneven trueColour
+// format like bgr233, it can set the client's colour map up with a 6x6x6
+// colour cube.  For this use the colour cube table has a null mapping, which
+// makes it easy to perform the reverse lookup operation from pixel value to
+// r,g,b values.
+
+#ifndef __RFB_COLOURCUBE_H__
+#define __RFB_COLOURCUBE_H__
+
+#include <rfb/Pixel.h>
+#include <rfb/ColourMap.h>
+
+namespace rfb {
+
+  class ColourCube : public ColourMap {
+  public:
+    ColourCube(int nr, int ng, int nb, Pixel* table_=0)
+      : nRed(nr), nGreen(ng), nBlue(nb), table(table_), deleteTable(false)
+    {
+      if (!table) {
+        table = new Pixel[size()];
+        deleteTable = true;
+        // set a null mapping by default
+        for (int i = 0; i < size(); i++)
+          table[i] = i;
+      }
+    }
+
+    ColourCube() : deleteTable(false) {}
+
+    virtual ~ColourCube() {
+      if (deleteTable) delete [] table;
+    }
+
+    void set(int r, int g, int b, Pixel p) {
+      table[(r * nGreen + g) * nBlue + b] = p;
+    }
+
+    Pixel lookup(int r, int g, int b) const {
+      return table[(r * nGreen + g) * nBlue + b];
+    }
+
+    int size()      const { return nRed*nGreen*nBlue; }
+    int redMult()   const { return nGreen*nBlue; }
+    int greenMult() const { return nBlue; }
+    int blueMult()  const { return 1; }
+
+    // ColourMap lookup() method.  Note that this only works when the table has
+    // the default null mapping.
+    virtual void lookup(int i, int* r, int* g, int* b) {
+      if (i >= size()) return;
+      *b = i % nBlue;
+      i /= nBlue;
+      *g = i % nGreen;
+      *r = i / nGreen;
+      *r = (*r * 65535 + (nRed-1)   / 2) / (nRed-1);
+      *g = (*g * 65535 + (nGreen-1) / 2) / (nGreen-1);
+      *b = (*b * 65535 + (nBlue-1)  / 2) / (nBlue-1);
+    }
+
+    int nRed;
+    int nGreen;
+    int nBlue;
+    Pixel* table;
+    bool deleteTable;
+  };
+}
+#endif
diff --git a/rfb/ColourMap.h b/rfb/ColourMap.h
new file mode 100644
index 0000000..ee7783d
--- /dev/null
+++ b/rfb/ColourMap.h
@@ -0,0 +1,34 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_COLOURMAP_H__
+#define __RFB_COLOURMAP_H__
+namespace rfb {
+  struct Colour {
+    Colour() : r(0), g(0), b(0) {}
+    Colour(int r_, int g_, int b_) : r(r_), g(g_), b(b_) {}
+    int r, g, b;
+    bool operator==(const Colour& c) const {return c.r == r && c.g == g && c.b == b;}
+    bool operator!=(const Colour& c) const {return !(c == *this);}
+  };
+
+  class ColourMap {
+  public:
+    virtual void lookup(int index, int* r, int* g, int* b)=0;
+  };
+}
+#endif
diff --git a/rfb/ComparingUpdateTracker.cxx b/rfb/ComparingUpdateTracker.cxx
new file mode 100644
index 0000000..0c44d85
--- /dev/null
+++ b/rfb/ComparingUpdateTracker.cxx
@@ -0,0 +1,151 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <stdio.h>
+#include <vector>
+#include <rdr/types.h>
+#include <rfb/Exception.h>
+#include <rfb/ComparingUpdateTracker.h>
+
+using namespace rfb;
+
+ComparingUpdateTracker::ComparingUpdateTracker(PixelBuffer* buffer)
+  : SimpleUpdateTracker(true), fb(buffer),
+    oldFb(fb->getPF(), 0, 0), firstCompare(true)
+{
+    changed.assign_union(fb->getRect());
+}
+
+ComparingUpdateTracker::~ComparingUpdateTracker()
+{
+}
+
+
+void ComparingUpdateTracker::flush_update(UpdateInfo* info,
+                                          const Region& cliprgn, int maxArea)
+{
+  throw rfb::Exception("flush_update(UpdateInfo*) not implemented");
+}
+
+void ComparingUpdateTracker::flush_update(UpdateTracker &ut,
+                                          const Region &cliprgn)
+{
+  throw rfb::Exception("flush_update(UpdateTracker&) not implemented");
+}
+
+
+#define BLOCK_SIZE 16
+
+void ComparingUpdateTracker::compare()
+{
+  std::vector<Rect> rects;
+  std::vector<Rect>::iterator i;
+
+  if (firstCompare) {
+    // NB: We leave the change region untouched on this iteration,
+    // since in effect the entire framebuffer has changed.
+    oldFb.setSize(fb->width(), fb->height());
+    for (int y=0; y<fb->height(); y+=BLOCK_SIZE) {
+      Rect pos(0, y, fb->width(), min(fb->height(), y+BLOCK_SIZE));
+      int srcStride;
+      const rdr::U8* srcData = fb->getPixelsR(pos, &srcStride);
+      oldFb.imageRect(pos, srcData, srcStride);
+    }
+    firstCompare = false;
+  } else {
+    copied.get_rects(&rects, copy_delta.x<=0, copy_delta.y<=0);
+    for (i = rects.begin(); i != rects.end(); i++)
+      oldFb.copyRect(*i, copy_delta);
+
+    Region to_check = changed.union_(copied);
+    to_check.get_rects(&rects);
+
+    Region newChanged;
+    for (i = rects.begin(); i != rects.end(); i++)
+      compareRect(*i, &newChanged);
+
+    copied.assign_subtract(newChanged);
+    changed = newChanged;
+  }
+}
+
+void ComparingUpdateTracker::compareRect(const Rect& r, Region* newChanged)
+{
+  if (!r.enclosed_by(fb->getRect())) {
+    fprintf(stderr,"ComparingUpdateTracker: rect outside fb (%d,%d-%d,%d)\n", r.tl.x, r.tl.y, r.br.x, r.br.y);
+    return;
+  }
+
+  int bytesPerPixel = fb->getPF().bpp/8;
+  int oldStride;
+  rdr::U8* oldData = oldFb.getPixelsRW(r, &oldStride);
+  int oldStrideBytes = oldStride * bytesPerPixel;
+
+  std::vector<Rect> changedBlocks;
+
+  for (int blockTop = r.tl.y; blockTop < r.br.y; blockTop += BLOCK_SIZE)
+  {
+    // Get a strip of the source buffer
+    Rect pos(r.tl.x, blockTop, r.br.x, min(r.br.y, blockTop+BLOCK_SIZE));
+    int fbStride;
+    const rdr::U8* newBlockPtr = fb->getPixelsR(pos, &fbStride);
+    int newStrideBytes = fbStride * bytesPerPixel;
+
+    rdr::U8* oldBlockPtr = oldData;
+    int blockBottom = min(blockTop+BLOCK_SIZE, r.br.y);
+
+    for (int blockLeft = r.tl.x; blockLeft < r.br.x; blockLeft += BLOCK_SIZE)
+    {
+      const rdr::U8* newPtr = newBlockPtr;
+      rdr::U8* oldPtr = oldBlockPtr;
+
+      int blockRight = min(blockLeft+BLOCK_SIZE, r.br.x);
+      int blockWidthInBytes = (blockRight-blockLeft) * bytesPerPixel;
+
+      for (int y = blockTop; y < blockBottom; y++)
+      {
+        if (memcmp(oldPtr, newPtr, blockWidthInBytes) != 0)
+        {
+          // A block has changed - copy the remainder to the oldFb
+          changedBlocks.push_back(Rect(blockLeft, blockTop,
+                                       blockRight, blockBottom));
+          for (int y2 = y; y2 < blockBottom; y2++)
+          {
+            memcpy(oldPtr, newPtr, blockWidthInBytes);
+            newPtr += newStrideBytes;
+            oldPtr += oldStrideBytes;
+          }
+          break;
+        }
+
+        newPtr += newStrideBytes;
+        oldPtr += oldStrideBytes;
+      }
+
+      oldBlockPtr += blockWidthInBytes;
+      newBlockPtr += blockWidthInBytes;
+    }
+
+    oldData += oldStrideBytes * BLOCK_SIZE;
+  }
+
+  if (!changedBlocks.empty()) {
+    Region temp;
+    temp.setOrderedRects(changedBlocks);
+    newChanged->assign_union(temp);
+  }
+}
diff --git a/rfb/ComparingUpdateTracker.h b/rfb/ComparingUpdateTracker.h
new file mode 100644
index 0000000..bec93d1
--- /dev/null
+++ b/rfb/ComparingUpdateTracker.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#ifndef __RFB_COMPARINGUPDATETRACKER_H__
+#define __RFB_COMPARINGUPDATETRACKER_H__
+
+#include <rfb/UpdateTracker.h>
+
+namespace rfb {
+
+  class ComparingUpdateTracker : public SimpleUpdateTracker {
+  public:
+    ComparingUpdateTracker(PixelBuffer* buffer);
+    ~ComparingUpdateTracker();
+
+    // compare() does the comparison and reduces its changed and copied regions
+    // as appropriate.
+
+    virtual void compare();
+
+    virtual void flush_update(UpdateInfo* info, const Region& cliprgn,
+                              int maxArea);
+    virtual void flush_update(UpdateTracker &info, const Region &cliprgn);
+  private:
+    void compareRect(const Rect& r, Region* newchanged);
+    PixelBuffer* fb;
+    ManagedPixelBuffer oldFb;
+    bool firstCompare;
+  };
+
+}
+#endif
diff --git a/rfb/Configuration.cxx b/rfb/Configuration.cxx
new file mode 100644
index 0000000..048fb77
--- /dev/null
+++ b/rfb/Configuration.cxx
@@ -0,0 +1,410 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- Configuration.cxx
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#ifdef WIN32
+#define strcasecmp _stricmp
+#define strncasecmp _strnicmp
+#endif
+
+#include <rfb/util.h>
+#include <rfb/Configuration.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Exception.h>
+
+#ifdef WIN32
+
+// Under Win32, we use these routines from several threads,
+// so we must provide suitable locking.
+#include <rfb/Threading.h>
+
+static rfb::Mutex configLock;
+
+#endif
+
+#include <rdr/HexOutStream.h>
+#include <rdr/HexInStream.h>
+
+using namespace rfb;
+
+static LogWriter vlog("Config");
+
+
+// -=- Configuration
+
+VoidParameter* Configuration::head = 0;
+
+bool Configuration::setParam(const char* n, const char* v, bool immutable) {
+  return setParam(n, strlen(n), v, immutable);
+}
+
+bool Configuration::setParam(const char* name, int len,
+                             const char* val, bool immutable)
+{
+  VoidParameter* current = head;
+  while (current) {
+    if ((int)strlen(current->getName()) == len &&
+        strncasecmp(current->getName(), name, len) == 0)
+    {
+      bool b = current->setParam(val);
+      if (b && immutable)
+        current->setImmutable();
+      return b;
+    }
+    current = current->_next;
+  }
+  return false;
+}
+
+bool Configuration::setParam(const char* config, bool immutable) {
+  bool hyphen = false;
+  if (config[0] == '-') {
+    hyphen = true;
+    config++;
+    if (config[0] == '-') config++; // allow gnu-style --<option>
+  }
+  const char* equal = strchr(config, '=');
+  if (equal) {
+    return setParam(config, equal-config, equal+1, immutable);
+  } else if (hyphen) {
+    VoidParameter* current = head;
+    while (current) {
+      if (strcasecmp(current->getName(), config) == 0) {
+        bool b = current->setParam();
+        if (b && immutable)
+          current->setImmutable();
+        return b;
+      }
+      current = current->_next;
+    }
+  }    
+  return false;
+}
+
+VoidParameter* Configuration::getParam(const char* param)
+{
+  VoidParameter* current = head;
+  while (current) {
+    if (strcasecmp(current->getName(), param) == 0)
+      return current;
+    current = current->_next;
+  }
+  return 0;
+}
+
+void Configuration::listParams(int width, int nameWidth) {
+  VoidParameter* current = head;
+  while (current) {
+    char* def_str = current->getDefaultStr();
+    const char* desc = current->getDescription();
+    fprintf(stderr,"  %-*s -", nameWidth, current->getName());
+    int column = strlen(current->getName());
+    if (column < nameWidth) column = nameWidth;
+    column += 4;
+    while (true) {
+      const char* s = strchr(desc, ' ');
+      int wordLen;
+      if (s) wordLen = s-desc;
+      else wordLen = strlen(desc);
+
+      if (column + wordLen + 1 > width) {
+        fprintf(stderr,"\n%*s",nameWidth+4,"");
+        column = nameWidth+4;
+      }
+      fprintf(stderr," %.*s",wordLen,desc);
+      column += wordLen + 1;
+      desc += wordLen + 1;
+      if (!s) break;
+    }
+
+    if (def_str) {
+      if (column + (int)strlen(def_str) + 11 > width)
+        fprintf(stderr,"\n%*s",nameWidth+4,"");
+      fprintf(stderr," (default=%s)\n",def_str);
+      strFree(def_str);
+    } else {
+      fprintf(stderr,"\n");
+    }
+    current = current->_next;
+  }
+}
+
+// -=- VoidParameter
+
+VoidParameter::VoidParameter(const char* name_, const char* desc_)
+  : immutable(false), name(name_), description(desc_) {
+  _next = Configuration::head;
+  Configuration::head = this;
+}
+
+VoidParameter::~VoidParameter() {
+}
+
+const char*
+VoidParameter::getName() const {
+  return name;
+}
+
+const char*
+VoidParameter::getDescription() const {
+  return description;
+}
+
+bool VoidParameter::setParam() {
+  return false;
+}
+
+bool VoidParameter::isBool() const {
+  return false;
+}
+
+void
+VoidParameter::setImmutable() {
+  vlog.debug("set immutable %s", getName());
+  immutable = true;
+}
+
+// -=- AliasParameter
+
+AliasParameter::AliasParameter(const char* name_, const char* desc_,
+                               VoidParameter* param_)
+  : VoidParameter(name_, desc_), param(param_) {
+}
+
+bool
+AliasParameter::setParam(const char* v) {
+  return param->setParam(v);
+}
+
+bool AliasParameter::setParam() {
+  return param->setParam();
+}
+
+char*
+AliasParameter::getDefaultStr() const {
+  return 0;
+}
+
+char* AliasParameter::getValueStr() const {
+  return param->getValueStr();
+}
+
+bool AliasParameter::isBool() const {
+  return param->isBool();
+}
+
+// -=- BoolParameter
+
+BoolParameter::BoolParameter(const char* name_, const char* desc_, bool v)
+: VoidParameter(name_, desc_), value(v), def_value(v) {
+}
+
+bool
+BoolParameter::setParam(const char* v) {
+  if (immutable) return true;
+
+  if (*v == 0 || strcasecmp(v, "1") == 0 || strcasecmp(v, "on") == 0
+      || strcasecmp(v, "true") == 0 || strcasecmp(v, "yes") == 0)
+    value = 1;
+  else if (strcasecmp(v, "0") == 0 || strcasecmp(v, "off") == 0
+           || strcasecmp(v, "false") == 0 || strcasecmp(v, "no") == 0)
+    value = 0;
+  else {
+    vlog.error("Bool parameter %s: invalid value '%s'", getName(), v);
+    return false;
+  }
+
+  vlog.debug("set %s(Bool) to %s(%d)", getName(), v, value);
+  return true;
+}
+
+bool BoolParameter::setParam() {
+  setParam(true);
+  return true;
+}
+
+void BoolParameter::setParam(bool b) {
+  if (immutable) return;
+  value = b;
+  vlog.debug("set %s(Bool) to %d", getName(), value);
+}
+
+char*
+BoolParameter::getDefaultStr() const {
+  char* result = new char[8];
+  sprintf(result, "%d", (int)def_value);
+  return result;
+}
+
+char* BoolParameter::getValueStr() const {
+  char* result = new char[8];
+  sprintf(result, "%d", (int)value);
+  return result;
+}
+
+bool BoolParameter::isBool() const {
+  return true;
+}
+
+BoolParameter::operator bool() const {
+  return value;
+}
+
+// -=- IntParameter
+
+IntParameter::IntParameter(const char* name_, const char* desc_, int v)
+: VoidParameter(name_, desc_), value(v), def_value(v) {
+}
+
+bool
+IntParameter::setParam(const char* v) {
+  if (immutable) return true;
+  vlog.debug("set %s(Int) to %s", getName(), v);
+  value = atoi(v);
+  return true;
+}
+
+bool
+IntParameter::setParam(int v) {
+  if (immutable) return true;
+  vlog.debug("set %s(Int) to %d", getName(), v);
+  value = v;
+  return true;
+}
+
+char*
+IntParameter::getDefaultStr() const {
+  char* result = new char[16];
+  sprintf(result, "%d", def_value);
+  return result;
+}
+
+char* IntParameter::getValueStr() const {
+  char* result = new char[16];
+  sprintf(result, "%d", value);
+  return result;
+}
+
+IntParameter::operator int() const {
+  return value;
+}
+
+// -=- StringParameter
+
+StringParameter::StringParameter(const char* name_, const char* desc_,
+                                 const char* v)
+  : VoidParameter(name_, desc_), value(strDup(v)), def_value(v)
+{
+  if (!v) {
+    fprintf(stderr,"Default value <null> for %s not allowed\n",name_);
+    throw rfb::Exception("Default value <null> not allowed");
+  }
+}
+
+StringParameter::~StringParameter() {
+  strFree(value);
+}
+
+bool StringParameter::setParam(const char* v) {
+#ifdef WIN32
+  Lock l(configLock);
+#endif
+  if (immutable) return true;
+  if (!v)
+    throw rfb::Exception("setParam(<null>) not allowed");
+  vlog.debug("set %s(String) to %s", getName(), v);
+  strFree(value);
+  value = strDup(v);
+  return value != 0;
+}
+
+char* StringParameter::getDefaultStr() const {
+  return strDup(def_value);
+}
+
+char* StringParameter::getValueStr() const {
+#ifdef WIN32
+  Lock l(configLock);
+#endif
+  return strDup(value);
+}
+
+// -=- BinaryParameter
+
+BinaryParameter::BinaryParameter(const char* name_, const char* desc_, const void* v, int l)
+: VoidParameter(name_, desc_), value(0), length(0), def_value((char*)v), def_length(l) {
+  if (l) {
+    value = new char[l];
+    length = l;
+    memcpy(value, v, l);
+  }
+}
+BinaryParameter::~BinaryParameter() {
+  if (value)
+    delete [] value;
+}
+
+bool BinaryParameter::setParam(const char* v) {
+#ifdef WIN32
+  Lock l(configLock);
+#endif
+  if (immutable) return true;
+  vlog.debug("set %s(Binary) to %s", getName(), v);
+  return rdr::HexInStream::hexStrToBin(v, &value, &length);
+}
+
+void BinaryParameter::setParam(const void* v, int len) {
+#ifdef WIN32
+  Lock l(configLock);
+#endif
+  if (immutable) return;
+  vlog.debug("set %s(Binary)", getName());
+  delete [] value; value = 0;
+  if (len) {
+    value = new char[len];
+    length = len;
+    memcpy(value, v, len);
+  }
+}
+
+char* BinaryParameter::getDefaultStr() const {
+  return rdr::HexOutStream::binToHexStr(def_value, def_length);
+}
+
+char* BinaryParameter::getValueStr() const {
+#ifdef WIN32
+  Lock l(configLock);
+#endif
+  return rdr::HexOutStream::binToHexStr(value, length);
+}
+
+void BinaryParameter::getData(void** data_, int* length_) const {
+#ifdef WIN32
+  Lock l(configLock);
+#endif
+  if (length_) *length_ = length;
+  if (data_) {
+    *data_ = new char[length];
+    memcpy(*data_, value, length);
+  }
+}
diff --git a/rfb/Configuration.h b/rfb/Configuration.h
new file mode 100644
index 0000000..1b37ac9
--- /dev/null
+++ b/rfb/Configuration.h
@@ -0,0 +1,167 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- Configuration.h
+//
+// This header defines a set of classes used to represent configuration
+// parameters of different types.  Instances of the different parameter
+// types are associated with instances of the Configuration class, and
+// are each given a unique name.  The Configuration class provides a
+// generic API through which parameters may be located by name and their
+// value set, thus removing the need to write platform-specific code.
+// Simply defining a new parameter and associating it with a Configuration
+// will allow it to be configured by the user.
+//
+// The Configuration class is used to allow multiple distinct configurations
+// to co-exist at the same time.  A process serving several desktops, for
+// instance, can create a Configuration instance for each, to allow them
+// to be configured independently, from the command-line, registry, etc.
+
+#ifndef __RFB_CONFIGURATION_H__
+#define __RFB_CONFIGURATION_H__
+
+namespace rfb {
+  class VoidParameter;
+
+  // -=- Configuration
+  //     Class used to access parameters.
+
+  class Configuration {
+  public:
+    // - Set named parameter to value
+    static bool setParam(const char* param, const char* value, bool immutable=false);
+
+    // - Set parameter to value (separated by "=")
+    static bool setParam(const char* config, bool immutable=false);
+
+    // - Set named parameter to value, with name truncated at len
+    static bool setParam(const char* name, int len,
+                         const char* val, bool immutable);
+
+    // - Get named parameter
+    static VoidParameter* getParam(const char* param);
+
+    static void listParams(int width=79, int nameWidth=10);
+
+    static VoidParameter* head;
+  };
+
+  // -=- VoidParameter
+  //     Configuration parameter base-class.
+
+  class VoidParameter {
+  public:
+    VoidParameter(const char* name_, const char* desc_);
+    virtual  ~VoidParameter();
+    const char* getName() const;
+    const char* getDescription() const;
+
+    virtual bool setParam(const char* value)  = 0;
+    virtual bool setParam();
+    virtual char* getDefaultStr() const = 0;
+    virtual char* getValueStr() const = 0;
+    virtual bool isBool() const;
+
+    virtual void setImmutable();
+
+    VoidParameter* _next;
+  protected:
+    bool immutable;
+    const char* name;
+    const char* description;
+  };
+
+  class AliasParameter : public VoidParameter {
+  public:
+    AliasParameter(const char* name_, const char* desc_,VoidParameter* param_);
+    virtual bool setParam(const char* value);
+    virtual bool setParam();
+    virtual char* getDefaultStr() const;
+    virtual char* getValueStr() const;
+    virtual bool isBool() const;
+  private:
+    VoidParameter* param;
+  };
+
+  class BoolParameter : public VoidParameter {
+  public:
+    BoolParameter(const char* name_, const char* desc_, bool v);
+    virtual bool setParam(const char* value);
+    virtual bool setParam();
+    virtual void setParam(bool b);
+    virtual char* getDefaultStr() const;
+    virtual char* getValueStr() const;
+    virtual bool isBool() const;
+    operator bool() const;
+  protected:
+    bool value;
+    bool def_value;
+  };
+
+  class IntParameter : public VoidParameter {
+  public:
+    IntParameter(const char* name_, const char* desc_, int v);
+    virtual bool setParam(const char* value);
+    virtual bool setParam(int v);
+    virtual char* getDefaultStr() const;
+    virtual char* getValueStr() const;
+    operator int() const;
+  protected:
+    int value;
+    int def_value;
+  };
+
+  class StringParameter : public VoidParameter {
+  public:
+    // StringParameter contains a null-terminated string, which CANNOT
+    // be Null, and so neither can the default value!
+    StringParameter(const char* name_, const char* desc_, const char* v);
+    virtual ~StringParameter();
+    virtual bool setParam(const char* value);
+    virtual char* getDefaultStr() const;
+    virtual char* getValueStr() const;
+
+    // getData() returns a copy of the data - it must be delete[]d by the
+    // caller.
+    char* getData() const { return getValueStr(); }
+  protected:
+    char* value;
+    const char* def_value;
+  };
+
+  class BinaryParameter : public VoidParameter {
+  public:
+    BinaryParameter(const char* name_, const char* desc_, const void* v, int l);
+    virtual ~BinaryParameter();
+    virtual bool setParam(const char* value);
+    virtual void setParam(const void* v, int l);
+    virtual char* getDefaultStr() const;
+    virtual char* getValueStr() const;
+
+    void getData(void** data, int* length) const;
+
+  protected:
+    char* value;
+    int length;
+    char* def_value;
+    int def_length;
+  };
+
+};
+
+#endif // __RFB_CONFIGURATION_H__
diff --git a/rfb/ConnParams.cxx b/rfb/ConnParams.cxx
new file mode 100644
index 0000000..9552940
--- /dev/null
+++ b/rfb/ConnParams.cxx
@@ -0,0 +1,104 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <rdr/InStream.h>
+#include <rdr/OutStream.h>
+#include <rfb/Exception.h>
+#include <rfb/encodings.h>
+#include <rfb/Encoder.h>
+#include <rfb/ConnParams.h>
+#include <rfb/util.h>
+
+using namespace rfb;
+
+ConnParams::ConnParams()
+  : majorVersion(0), minorVersion(0), width(0), height(0), useCopyRect(false),
+    supportsLocalCursor(false), supportsDesktopResize(false),
+    name_(0), nEncodings_(0), encodings_(0),
+    currentEncoding_(encodingRaw), verStrPos(0)
+{
+  setName("");
+}
+
+ConnParams::~ConnParams()
+{
+  delete [] name_;
+  delete [] encodings_;
+}
+
+bool ConnParams::readVersion(rdr::InStream* is, bool* done)
+{
+  if (verStrPos >= 12) return false;
+  while (is->checkNoWait(1) && verStrPos < 12) {
+    verStr[verStrPos++] = is->readU8();
+  }
+
+  if (verStrPos < 12) {
+    *done = false;
+    return true;
+  }
+  *done = true;
+  verStr[12] = 0;
+  return (sscanf(verStr, "RFB %03d.%03d\n", &majorVersion,&minorVersion) == 2);
+}
+
+void ConnParams::writeVersion(rdr::OutStream* os)
+{
+  char str[13];
+  sprintf(str, "RFB %03d.%03d\n", majorVersion, minorVersion);
+  os->writeBytes(str, 12);
+  os->flush();
+}
+
+void ConnParams::setPF(const PixelFormat& pf)
+{
+  pf_ = pf;
+
+  if (pf.bpp != 8 && pf.bpp != 16 && pf.bpp != 32)
+    throw Exception("setPF: not 8, 16 or 32 bpp?");
+}
+
+void ConnParams::setName(const char* name)
+{
+  delete [] name_;
+  name_ = strDup(name);
+}
+
+void ConnParams::setEncodings(int nEncodings, const rdr::U32* encodings)
+{
+  if (nEncodings > nEncodings_) {
+    delete [] encodings_;
+    encodings_ = new rdr::U32[nEncodings];
+  }
+  nEncodings_ = nEncodings;
+  useCopyRect = false;
+  supportsLocalCursor = false;
+  currentEncoding_ = encodingRaw;
+
+  for (int i = nEncodings-1; i >= 0; i--) {
+    encodings_[i] = encodings[i];
+
+    if (encodings[i] == encodingCopyRect)
+      useCopyRect = true;
+    else if (encodings[i] == pseudoEncodingCursor)
+      supportsLocalCursor = true;
+    else if (encodings[i] == pseudoEncodingDesktopSize)
+      supportsDesktopResize = true;
+    else if (encodings[i] <= encodingMax && Encoder::supported(encodings[i]))
+      currentEncoding_ = encodings[i];
+  }
+}
diff --git a/rfb/ConnParams.h b/rfb/ConnParams.h
new file mode 100644
index 0000000..fb96a7a
--- /dev/null
+++ b/rfb/ConnParams.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// ConnParams - structure containing the connection parameters.
+//
+
+#ifndef __RFB_CONNPARAMS_H__
+#define __RFB_CONNPARAMS_H__
+
+#include <rdr/types.h>
+#include <rfb/PixelFormat.h>
+
+namespace rdr { class InStream; }
+
+namespace rfb {
+
+  class ConnParams {
+  public:
+    ConnParams();
+    ~ConnParams();
+
+    bool readVersion(rdr::InStream* is, bool* done);
+    void writeVersion(rdr::OutStream* os);
+
+    int majorVersion;
+    int minorVersion;
+
+    void setVersion(int major, int minor) {
+      majorVersion = major; minorVersion = minor;
+    }
+    bool isVersion(int major, int minor) {
+      return majorVersion == major && minorVersion == minor;
+    }
+    bool beforeVersion(int major, int minor) {
+      return (majorVersion < major ||
+              (majorVersion == major && minorVersion < minor));
+    }
+    bool afterVersion(int major, int minor) {
+      return !beforeVersion(major,minor+1);
+    }
+
+    int width;
+    int height;
+
+    const PixelFormat& pf() { return pf_; }
+    void setPF(const PixelFormat& pf);
+
+    const char* name() { return name_; }
+    void setName(const char* name);
+
+    rdr::U32 currentEncoding() { return currentEncoding_; }
+    int nEncodings() { return nEncodings_; }
+    const rdr::U32* encodings() { return encodings_; }
+    void setEncodings(int nEncodings, const rdr::U32* encodings);
+    bool useCopyRect;
+
+    bool supportsLocalCursor;
+    bool supportsDesktopResize;
+
+  private:
+
+    PixelFormat pf_;
+    char* name_;
+    int nEncodings_;
+    rdr::U32* encodings_;
+    int currentEncoding_;
+    char verStr[13];
+    int verStrPos;
+  };
+}
+#endif
diff --git a/rfb/Cursor.cxx b/rfb/Cursor.cxx
new file mode 100644
index 0000000..b50d925
--- /dev/null
+++ b/rfb/Cursor.cxx
@@ -0,0 +1,178 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <string.h>
+#include <rfb/Cursor.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+
+static LogWriter vlog("Cursor");
+
+void Cursor::setSize(int w, int h) {
+  int oldMaskLen = maskLen();
+  ManagedPixelBuffer::setSize(w, h);
+  if (maskLen() > oldMaskLen) {
+    delete [] mask.buf;
+    mask.buf = new rdr::U8[maskLen()];
+  }
+}
+
+void Cursor::drawOutline(const Pixel& c)
+{
+  Cursor outlined;
+
+  // Create a mirror of the existing cursor
+  outlined.setPF(getPF());
+  outlined.setSize(width(), height());
+  outlined.hotspot = hotspot;
+
+  // Clear the mirror's background to the outline colour
+  outlined.fillRect(getRect(), c);
+
+  // Blit the existing cursor, using its mask
+  outlined.maskRect(getRect(), data, mask.buf);
+
+  // Now just adjust the mask to add the outline.  The outline pixels
+  // will already be the right colour. :)
+  int maskBytesPerRow = (width() + 7) / 8;
+  for (int y = 0; y < height(); y++) {
+    for (int byte=0; byte<maskBytesPerRow; byte++) {
+      rdr::U8 m8 = mask.buf[y*maskBytesPerRow + byte];
+
+      // Handle above & below outline
+      if (y > 0) m8 |= mask.buf[(y-1)*maskBytesPerRow + byte];
+      if (y < height()-1) m8 |= mask.buf[(y+1)*maskBytesPerRow + byte];
+
+      // Left outline
+      m8 |= mask.buf[y*maskBytesPerRow + byte] << 1;
+      if (byte < maskBytesPerRow-1)
+        m8 |= (mask.buf[y*maskBytesPerRow + byte + 1] >> 7) & 1;
+
+      // Right outline
+      m8 |= mask.buf[y*maskBytesPerRow + byte] >> 1;
+      if (byte > 0)
+        m8 |= (mask.buf[y*maskBytesPerRow + byte - 1] << 7) & 128;
+
+      outlined.mask.buf[y*maskBytesPerRow + byte] = m8;
+    }
+  }
+
+  // Replace the existing cursor & mask with the new one
+  delete [] data;
+  delete [] mask.buf;
+  data = outlined.data; outlined.data = 0;
+  mask.buf = outlined.mask.buf; outlined.mask.buf = 0;
+}
+
+rdr::U8* Cursor::getBitmap(Pixel* pix0, Pixel* pix1)
+{
+  bool gotPix0 = false;
+  bool gotPix1 = false;
+  rdr::U8Array source(maskLen());
+  memset(source.buf, 0, maskLen());
+
+  int maskBytesPerRow = (width() + 7) / 8;
+  for (int y = 0; y < height(); y++) {
+    for (int x = 0; x < width(); x++) {
+      int byte = y * maskBytesPerRow + x / 8;
+      int bit = 7 - x % 8;
+      if (mask.buf[byte] & (1 << bit)) {
+        Pixel pix=0;
+        switch (getPF().bpp) {
+        case 8:  pix = ((rdr::U8*) data)[y * width() + x]; break;
+        case 16: pix = ((rdr::U16*)data)[y * width() + x]; break;
+        case 32: pix = ((rdr::U32*)data)[y * width() + x]; break;
+        }
+        if (!gotPix0 || pix == *pix0) {
+          gotPix0 = true;
+          *pix0 = pix;
+        } else if (!gotPix1 || pix == *pix1) {
+          gotPix1 = true;
+          *pix1 = pix;
+          source.buf[byte] |= (1 << bit);
+        } else {
+          // not a bitmap
+          return 0;
+        }
+      }
+    }
+  }
+  return source.takeBuf();
+}
+
+// crop() determines the "busy" rectangle for the cursor - the minimum bounding
+// rectangle containing actual pixels.  This isn't the most efficient algorithm
+// but it's short.  For sanity, we make sure that the busy rectangle always
+// includes the hotspot (the hotspot is unsigned on the wire so otherwise it
+// would cause problems if it was above or left of the actual pixels)
+
+void Cursor::crop()
+{
+  Rect busy = getRect().intersect(Rect(hotspot.x, hotspot.y,
+                                       hotspot.x+1, hotspot.y+1));
+  int maskBytesPerRow = (width() + 7) / 8;
+  int x, y;
+  for (y = 0; y < height(); y++) {
+    for (x = 0; x < width(); x++) {
+      int byte = y * maskBytesPerRow + x / 8;
+      int bit = 7 - x % 8;
+      if (mask.buf[byte] & (1 << bit)) {
+        if (x < busy.tl.x) busy.tl.x = x;
+        if (x+1 > busy.br.x) busy.br.x = x+1;
+        if (y < busy.tl.y) busy.tl.y = y;
+        if (y+1 > busy.br.y) busy.br.y = y+1;
+      }
+    }
+  }
+
+  if (width() == busy.width() && height() == busy.height()) return;
+
+  vlog.debug("cropping %dx%d to %dx%d", width(), height(),
+             busy.width(), busy.height());
+
+  // Copy the pixel data
+  int newDataLen = busy.area() * (getPF().bpp/8);
+  rdr::U8* newData = new rdr::U8[newDataLen];
+  getImage(newData, busy);
+
+  // Copy the mask
+  int newMaskBytesPerRow = (busy.width()+7)/8;
+  int newMaskLen = newMaskBytesPerRow * busy.height();
+  rdr::U8* newMask = new rdr::U8[newMaskLen];
+  memset(newMask, 0, newMaskLen);
+  for (y = 0; y < busy.height(); y++) {
+    int newByte, newBit;
+    for (x = 0; x < busy.width(); x++) {
+      int oldByte = (y+busy.tl.y) * maskBytesPerRow + (x+busy.tl.x) / 8;
+      int oldBit = 7 - (x+busy.tl.x) % 8;
+      newByte = y * newMaskBytesPerRow + x / 8;
+      newBit = 7 - x % 8;
+      if (mask.buf[oldByte] & (1 << oldBit))
+        newMask[newByte] |= (1 << newBit);
+    }
+  }
+
+  // Set the size and data to the new, cropped cursor.
+  setSize(busy.width(), busy.height());
+  hotspot = hotspot.subtract(busy.tl);
+  delete [] data;
+  delete [] mask.buf;
+  datasize = newDataLen;
+  data = newData;
+  mask.buf = newMask;
+}
diff --git a/rfb/Cursor.h b/rfb/Cursor.h
new file mode 100644
index 0000000..0f18775
--- /dev/null
+++ b/rfb/Cursor.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// Cursor - structure containing information describing
+//          the current cursor shape
+//
+
+#ifndef __RFB_CURSOR_H__
+#define __RFB_CURSOR_H__
+
+#include <rfb/PixelBuffer.h>
+
+namespace rfb {
+
+  class Cursor : public ManagedPixelBuffer {
+  public:
+    Cursor() {}
+    rdr::U8Array mask;
+    Point hotspot;
+
+    int maskLen() { return (width() + 7) / 8 * height(); }
+
+    // setSize() resizes the cursor.  The contents of the data and mask are
+    // undefined after this call.
+    virtual void setSize(int w, int h);
+
+    // drawOutline() adds an outline to the cursor in the given colour.
+    void drawOutline(const Pixel& c);
+
+    // getBitmap() tests whether the cursor is monochrome, and if so returns a
+    // bitmap together with background and foreground colours.  The size and
+    // layout of the bitmap are the same as the mask.
+    rdr::U8* getBitmap(Pixel* pix0, Pixel* pix1);
+
+    // crop() crops the cursor down to the smallest possible size, based on the
+    // mask.
+    void crop();
+  };
+
+}
+#endif
diff --git a/rfb/Decoder.cxx b/rfb/Decoder.cxx
new file mode 100644
index 0000000..816cb6e
--- /dev/null
+++ b/rfb/Decoder.cxx
@@ -0,0 +1,68 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <stdio.h>
+#include <rfb/Exception.h>
+#include <rfb/Decoder.h>
+#include <rfb/RawDecoder.h>
+#include <rfb/RREDecoder.h>
+#include <rfb/HextileDecoder.h>
+#include <rfb/ZRLEDecoder.h>
+
+using namespace rfb;
+
+Decoder::~Decoder()
+{
+}
+
+DecoderCreateFnType Decoder::createFns[encodingMax+1] = { 0 };
+
+bool Decoder::supported(unsigned int encoding)
+{
+  return encoding <= encodingMax && createFns[encoding];
+}
+
+Decoder* Decoder::createDecoder(unsigned int encoding, CMsgReader* reader)
+{
+  if (encoding <= encodingMax && createFns[encoding])
+    return (*createFns[encoding])(reader);
+  return 0;
+}
+
+void Decoder::registerDecoder(unsigned int encoding,
+                              DecoderCreateFnType createFn)
+{
+  if (encoding > encodingMax)
+    throw Exception("Decoder::registerDecoder: encoding out of range");
+
+  if (createFns[encoding])
+    fprintf(stderr,"Replacing existing decoder for encoding %s (%d)\n",
+            encodingName(encoding), encoding);
+  createFns[encoding] = createFn;
+}
+
+int DecoderInit::count = 0;
+
+DecoderInit::DecoderInit()
+{
+  if (count++ != 0) return;
+
+  Decoder::registerDecoder(encodingRaw, RawDecoder::create);
+  Decoder::registerDecoder(encodingRRE, RREDecoder::create);
+  Decoder::registerDecoder(encodingHextile, HextileDecoder::create);
+  Decoder::registerDecoder(encodingZRLE, ZRLEDecoder::create);
+}
diff --git a/rfb/Decoder.h b/rfb/Decoder.h
new file mode 100644
index 0000000..914b26a
--- /dev/null
+++ b/rfb/Decoder.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_DECODER_H__
+#define __RFB_DECODER_H__
+
+#include <rfb/Rect.h>
+#include <rfb/encodings.h>
+
+namespace rfb {
+  class CMsgReader;
+  class CMsgHandler;
+  class Decoder;
+  typedef Decoder* (*DecoderCreateFnType)(CMsgReader*);
+
+  class Decoder {
+  public:
+    virtual ~Decoder();
+    virtual void readRect(const Rect& r, CMsgHandler* handler)=0;
+
+    static bool supported(unsigned int encoding);
+    static Decoder* createDecoder(unsigned int encoding, CMsgReader* reader);
+    static void registerDecoder(unsigned int encoding,
+                                DecoderCreateFnType createFn);
+  private:
+    static DecoderCreateFnType createFns[encodingMax+1];
+  };
+
+  class DecoderInit {
+    static int count;
+  public:
+    DecoderInit();
+  };
+
+  static DecoderInit decoderInitObj;
+}
+
+#endif
diff --git a/rfb/Encoder.cxx b/rfb/Encoder.cxx
new file mode 100644
index 0000000..f2d6d3d
--- /dev/null
+++ b/rfb/Encoder.cxx
@@ -0,0 +1,75 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <stdio.h>
+#include <rfb/Exception.h>
+#include <rfb/Encoder.h>
+#include <rfb/RawEncoder.h>
+#include <rfb/RREEncoder.h>
+#include <rfb/HextileEncoder.h>
+#include <rfb/ZRLEEncoder.h>
+
+using namespace rfb;
+
+Encoder::~Encoder()
+{
+}
+
+EncoderCreateFnType Encoder::createFns[encodingMax+1] = { 0 };
+
+bool Encoder::supported(unsigned int encoding)
+{
+  return encoding <= encodingMax && createFns[encoding];
+}
+
+Encoder* Encoder::createEncoder(unsigned int encoding, SMsgWriter* writer)
+{
+  if (encoding <= encodingMax && createFns[encoding])
+    return (*createFns[encoding])(writer);
+  return 0;
+}
+
+void Encoder::registerEncoder(unsigned int encoding,
+                              EncoderCreateFnType createFn)
+{
+  if (encoding > encodingMax)
+    throw Exception("Encoder::registerEncoder: encoding out of range");
+
+  if (createFns[encoding])
+    fprintf(stderr,"Replacing existing encoder for encoding %s (%d)\n",
+            encodingName(encoding), encoding);
+  createFns[encoding] = createFn;
+}
+
+void Encoder::unregisterEncoder(unsigned int encoding)
+{
+  if (encoding > encodingMax)
+    throw Exception("Encoder::unregisterEncoder: encoding out of range");
+  createFns[encoding] = 0;
+}
+
+int EncoderInit::count = 0;
+
+EncoderInit::EncoderInit()
+{
+  if (count++ != 0) return;
+
+  Encoder::registerEncoder(encodingRaw, RawEncoder::create);
+  Encoder::registerEncoder(encodingRRE, RREEncoder::create);
+  Encoder::registerEncoder(encodingHextile, HextileEncoder::create);
+  Encoder::registerEncoder(encodingZRLE, ZRLEEncoder::create);
+}
diff --git a/rfb/Encoder.h b/rfb/Encoder.h
new file mode 100644
index 0000000..7795d90
--- /dev/null
+++ b/rfb/Encoder.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_ENCODER_H__
+#define __RFB_ENCODER_H__
+
+#include <rfb/Rect.h>
+#include <rfb/encodings.h>
+
+namespace rfb {
+  class SMsgWriter;
+  class Encoder;
+  class ImageGetter;
+  typedef Encoder* (*EncoderCreateFnType)(SMsgWriter*);
+
+  class Encoder {
+  public:
+    virtual ~Encoder();
+
+    // writeRect() tries to write the given rectangle.  If it is unable to
+    // write the whole rectangle it returns false and sets actual to the actual
+    // rectangle which was updated.
+    virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual)=0;
+
+    static bool supported(unsigned int encoding);
+    static Encoder* createEncoder(unsigned int encoding, SMsgWriter* writer);
+    static void registerEncoder(unsigned int encoding,
+                                EncoderCreateFnType createFn);
+    static void unregisterEncoder(unsigned int encoding);
+  private:
+    static EncoderCreateFnType createFns[encodingMax+1];
+  };
+
+  class EncoderInit {
+    static int count;
+  public:
+    EncoderInit();
+  };
+
+  static EncoderInit encoderInitObj;
+}
+
+#endif
diff --git a/rfb/Exception.h b/rfb/Exception.h
new file mode 100644
index 0000000..aa98271
--- /dev/null
+++ b/rfb/Exception.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_EXCEPTION_H__
+#define __RFB_EXCEPTION_H__
+
+#include <rdr/Exception.h>
+
+namespace rfb {
+  struct Exception : public rdr::Exception {
+    Exception(const char* s=0, const char* e="rfb::Exception")
+      : rdr::Exception(s,e) {}
+  };
+  struct AuthFailureException : public Exception {
+    AuthFailureException(const char* s="Authentication failure")
+      : Exception(s,"rfb::AuthFailureException") {}
+  };
+  struct ConnFailedException : public Exception {
+    ConnFailedException(const char* s="Connection failed")
+      : Exception(s,"rfb::ConnFailedException") {}
+  };
+}
+#endif
diff --git a/rfb/HTTPServer.cxx b/rfb/HTTPServer.cxx
new file mode 100644
index 0000000..55e6909
--- /dev/null
+++ b/rfb/HTTPServer.cxx
@@ -0,0 +1,386 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#include <rfb/HTTPServer.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+#include <rdr/MemOutStream.h>
+#include <time.h>
+
+// *** Shouldn't really link against this - only for ClientWaitTimeMillis
+//     and IdleTimeout
+#include <rfb/ServerCore.h>
+
+#ifdef WIN32
+#define strcasecmp _stricmp
+#endif
+
+
+using namespace rfb;
+using namespace rdr;
+
+static LogWriter vlog("HTTPServer");
+
+
+//
+// -=- LineReader
+//     Helper class which is repeatedly called until a line has been read
+//     (lines end in \n or \r\n).
+//     Returns true when line complete, and resets internal state so that
+//     next read() call will start reading a new line.
+//     Only one buffer is kept - process line before reading next line!
+//
+
+class LineReader : public CharArray {
+public:
+  LineReader(InStream& is_, int l)
+    : CharArray(l), is(is_), pos(0), len(l), bufferOverrun(false) {}
+
+  // Returns true if line complete, false otherwise
+  bool read() {
+    while (is.checkNoWait(1)) {
+      char c = is.readU8();
+
+      if (c == '\n') {
+        if (pos && (buf[pos-1] == '\r'))
+          pos--;
+        bufferOverrun = false;
+        buf[pos++] = 0;
+        pos = 0;
+        return true;
+      }
+
+      if (pos == (len-1)) {
+        bufferOverrun = true;
+        buf[pos] = 0;
+        return true;
+      }
+
+      buf[pos++] = c;
+    }
+
+    return false;
+  }
+  bool didBufferOverrun() const {return bufferOverrun;}
+protected:
+  InStream& is;
+  int pos, len;
+  bool bufferOverrun;
+};
+
+
+//
+// -=- HTTPServer::Session
+//     Manages the internal state for an HTTP session.
+//     processHTTP returns true when request has completed,
+//     indicating that socket & session data can be deleted.
+//
+
+class rfb::HTTPServer::Session {
+public:
+  Session(network::Socket& s, rfb::HTTPServer& srv)
+    : contentType(0), line(s.inStream(), 256), sock(s),
+      server(srv), state(ReadRequestLine), lastActive(time(0)) {
+  }
+  ~Session() {
+  }
+
+  void writeResponse(int result, const char* text);
+  bool writeResponse(int code);
+
+  bool processHTTP();
+
+  network::Socket* getSock() const {return &sock;}
+
+  int checkIdleTimeout();
+protected:
+  CharArray uri;
+  const char* contentType;
+  LineReader line;
+  network::Socket& sock;
+  rfb::HTTPServer& server;
+  enum {ReadRequestLine, ReadHeaders, WriteResponse} state;
+  enum {GetRequest, HeadRequest} request;
+  time_t lastActive;
+};
+
+
+// - Internal helper routines
+
+void
+copyStream(InStream& is, OutStream& os) {
+  try {
+    while (1) {
+      os.writeU8(is.readU8());
+    }
+  } catch (rdr::EndOfStream) {
+  }
+}
+
+void writeLine(OutStream& os, const char* text) {
+  os.writeBytes(text, strlen(text));
+  os.writeBytes("\r\n", 2);
+}
+
+
+// - Write an HTTP-compliant response to the client
+
+void
+HTTPServer::Session::writeResponse(int result, const char* text) {
+  char buffer[1024];
+  if (strlen(text) > 512)
+    throw new rdr::Exception("Internal error - HTTP response text too big");
+  sprintf(buffer, "%s %d %s", "HTTP/1.1", result, text);
+  OutStream& os=sock.outStream();
+  writeLine(os, buffer);
+  writeLine(os, "Server: RealVNC/4.0");
+  writeLine(os, "Connection: close");
+  os.writeBytes("Content-Type: ", 14);
+  if (result == 200) {
+    if (!contentType)
+      contentType = guessContentType(uri.buf, "text/html");
+    os.writeBytes(contentType, strlen(contentType));
+  } else {
+    os.writeBytes("text/html", 9);
+  }
+  os.writeBytes("\r\n", 2);
+  writeLine(os, "");
+  if (result != 200) {
+    writeLine(os, "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">");
+    writeLine(os, "<HTML><HEAD>");
+    sprintf(buffer, "<TITLE>%d %s</TITLE>", result, text);
+    writeLine(os, buffer);
+    writeLine(os, "</HEAD><BODY><H1>");
+    writeLine(os, text);
+    writeLine(os, "</H1></BODY></HTML>");
+    sock.outStream().flush();
+  }
+}
+
+bool
+HTTPServer::Session::writeResponse(int code) {
+  switch (code) {
+  case 200: writeResponse(code, "OK"); break;
+  case 400: writeResponse(code, "Bad Request"); break;
+  case 404: writeResponse(code, "Not Found"); break;
+  case 501: writeResponse(code, "Not Implemented"); break;
+  default: writeResponse(500, "Unknown Error"); break;
+  };
+
+  // This return code is passed straight out of processHTTP().
+  // true indicates that the request has been completely processed.
+  return true;
+}
+
+// - Main HTTP request processing routine
+
+bool
+HTTPServer::Session::processHTTP() {
+  lastActive = time(0);
+
+  while (sock.inStream().checkNoWait(1)) {
+
+    switch (state) {
+
+      // Reading the Request-Line
+    case ReadRequestLine:
+
+      // Either read a line, or run out of incoming data
+      if (!line.read())
+        return false;
+
+      // We have read a line!  Skip it if it's blank
+      if (strlen(line.buf) == 0)
+        continue;
+
+      // The line contains a request to process.
+      {
+        char method[16], path[128], version[16];
+        int matched = sscanf(line.buf, "%15s%127s%15s",
+          method, path, version);
+        if (matched != 3)
+          return writeResponse(400);
+
+        // Store the required "method"
+        if (strcmp(method, "GET") == 0)
+          request = GetRequest;
+        else if (strcmp(method, "HEAD") == 0)
+          request = HeadRequest;
+        else
+          return writeResponse(501);
+
+        // Store the URI to the "document"
+        uri.buf = strDup(path);
+      }
+
+      // Move on to reading the request headers
+      state = ReadHeaders;
+      break;
+
+      // Reading the request headers
+    case ReadHeaders:
+
+      // Try to read a line
+      if (!line.read())
+        return false;
+
+      // Skip headers until we hit a blank line
+      if (strlen(line.buf) != 0)
+        continue;
+
+      // Headers ended - write the response!
+      {
+        CharArray address(sock.getPeerAddress());
+        vlog.info("getting %s for %s", uri.buf, address.buf);
+        InStream* data = server.getFile(uri.buf, &contentType);
+        if (!data)
+          return writeResponse(404);
+
+        try {
+          writeResponse(200);
+          if (request == GetRequest)
+            copyStream(*data, sock.outStream());
+          sock.outStream().flush();
+        } catch (rdr::Exception& e) {
+          vlog.error("error writing HTTP document:%s", e.str());
+        }
+        delete data;
+      }
+
+      // The operation is complete!
+      return true;
+
+    default:
+      throw rdr::Exception("invalid HTTPSession state!");
+    };
+
+  }
+
+  // Indicate that we're still processing the HTTP request.
+  return false;
+}
+
+int HTTPServer::Session::checkIdleTimeout() {
+  time_t now = time(0);
+  int timeout = (lastActive + rfb::Server::idleTimeout) - now;
+  if (timeout > 0)
+    return timeout * 1000;
+  sock.shutdown();
+  return 0;
+}
+
+// -=- Constructor / destructor
+
+HTTPServer::HTTPServer() {
+}
+
+HTTPServer::~HTTPServer() {
+  std::list<Session*>::iterator i;
+  for (i=sessions.begin(); i!=sessions.end(); i++) {
+    delete (*i)->getSock();
+    delete *i;
+  }
+}
+
+
+// -=- SocketServer interface implementation
+
+void
+HTTPServer::addClient(network::Socket* sock) {
+  Session* s = new Session(*sock, *this);
+  if (!s) {
+    sock->shutdown();
+  } else {
+    sock->inStream().setTimeout(rfb::Server::clientWaitTimeMillis);
+    sock->outStream().setTimeout(rfb::Server::clientWaitTimeMillis);
+    sessions.push_front(s);
+  }
+}
+
+bool
+HTTPServer::processSocketEvent(network::Socket* sock) {
+  std::list<Session*>::iterator i;
+  for (i=sessions.begin(); i!=sessions.end(); i++) {
+    if ((*i)->getSock() == sock) {
+      try {
+        if ((*i)->processHTTP()) {
+          vlog.info("completed HTTP request");
+          delete *i;
+          sessions.erase(i);
+          break;
+        }
+        return true;
+      } catch (rdr::Exception& e) {
+        vlog.error("untrapped: %s", e.str());
+        delete *i;
+        sessions.erase(i);
+        break;
+      }
+    }
+  }
+  delete sock;
+  return false;
+}
+
+void HTTPServer::getSockets(std::list<network::Socket*>* sockets)
+{
+  sockets->clear();
+  std::list<Session*>::iterator ci;
+  for (ci = sessions.begin(); ci != sessions.end(); ci++) {
+    sockets->push_back((*ci)->getSock());
+  }
+}
+
+int HTTPServer::checkTimeouts() {
+  std::list<Session*>::iterator ci;
+  int timeout = 0;
+  for (ci = sessions.begin(); ci != sessions.end(); ci++) {
+    soonestTimeout(&timeout, (*ci)->checkIdleTimeout());
+  }
+  return timeout;
+}
+
+
+// -=- Default getFile implementation
+
+InStream*
+HTTPServer::getFile(const char* name, const char** contentType) {
+  return 0;
+}
+
+const char*
+HTTPServer::guessContentType(const char* name, const char* defType) {
+  CharArray file, ext;
+  if (!strSplit(name, '.', &file.buf, &ext.buf))
+    return defType;
+  if (strcasecmp(ext.buf, "html") == 0 ||
+    strcasecmp(ext.buf, "htm") == 0) {
+    return "text/html";
+  } else if (strcasecmp(ext.buf, "txt") == 0) {
+    return "text/plain";
+  } else if (strcasecmp(ext.buf, "gif") == 0) {
+    return "image/gif";
+  } else if (strcasecmp(ext.buf, "jpg") == 0) {
+    return "image/jpeg";
+  } else if (strcasecmp(ext.buf, "jar") == 0) {
+    return "application/java-archive";
+  } else if (strcasecmp(ext.buf, "exe") == 0) {
+    return "application/octet-stream";
+  }
+  return defType;
+}
diff --git a/rfb/HTTPServer.h b/rfb/HTTPServer.h
new file mode 100644
index 0000000..9431195
--- /dev/null
+++ b/rfb/HTTPServer.h
@@ -0,0 +1,112 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- HTTPServer.h
+
+// Single-threaded HTTP server implementation.
+// All I/O is handled by the processSocketEvent routine,
+// which is called by the main-loop of the VNC server whenever
+// there is an event on an HTTP socket.
+
+#ifndef __RFB_HTTP_SERVER_H__
+#define __RFB_HTTP_SERVER_H__
+
+#include <list>
+
+#include <rdr/MemInStream.h>
+#include <rfb/UpdateTracker.h>
+#include <rfb/Configuration.h>
+#include <network/Socket.h>
+
+namespace rfb {
+
+  class HTTPServer : public network::SocketServer {
+  public:
+    // -=- Constructors
+
+    // - HTTPServer(files)
+    //   Create an HTTP server which will use the getFile method
+    //   to satisfy HTTP GET requests.
+    HTTPServer();
+
+    virtual ~HTTPServer();
+
+    // -=- Client management
+
+    // - Run a client connection on the supplied socket
+    //   This causes the server to perform HTTP protocol on the
+    //   supplied socket.
+    //   The socket will be closed if protocol initialisation
+    //   fails.
+    virtual void addClient(network::Socket* sock);
+
+    // -=- Event processing methods
+
+    // - Process an input event on a particular Socket
+    //   The platform-specific side of the server implementation calls
+    //   this method whenever data arrives on one of the active
+    //   network sockets.
+    //   The method returns true if the Socket is still in use by the
+    //   server, or false if it is no longer required and should be
+    //   deleted.
+    //   NB:  If false is returned then the Socket is deleted and must
+    //   not be accessed again!
+
+    virtual bool processSocketEvent(network::Socket* sock);
+
+    // - Check for socket timeouts
+    virtual int checkTimeouts();
+
+    // getSockets() gets a list of sockets.  This can be used to generate an
+    // fd_set for calling select().
+
+    virtual void getSockets(std::list<network::Socket*>* sockets);
+
+
+    // -=- File interface
+
+    // - getFile is passed the path portion of a URL and returns an
+    //   InStream containing the data to return.  If the requested
+    //   file is available then the contentType should be set to the
+    //   type of the file, or left untouched if the file type is to
+    //   be determined automatically by HTTPServer.
+    //   If the file is not available then null is returned.
+    //   Overridden getFile functions should call the default version
+    //   if they do not recognise a path name.
+    //   NB: The caller assumes ownership of the returned InStream.
+    //   NB: The contentType is statically allocated by the getFile impl.
+    //   NB: contentType is *guaranteed* to be valid when getFile is called.
+
+    virtual rdr::InStream* getFile(const char* name, const char** contentType);
+
+    // - guessContentType is passed the name of a file and returns the
+    //   name of an HTTP content type, based on the file's extension.  If
+    //   the extension isn't recognised then defType is returned.  This can
+    //   be used from getFile to easily default to the supplied contentType,
+    //   or by passing zero in to determine whether a type is recognised or not.
+
+    static const char* guessContentType(const char* name, const char* defType);
+
+  protected:
+    class Session;
+    std::list<Session*> sessions;
+  };
+}
+
+#endif
+
diff --git a/rfb/HextileDecoder.cxx b/rfb/HextileDecoder.cxx
new file mode 100644
index 0000000..97c7ca7
--- /dev/null
+++ b/rfb/HextileDecoder.cxx
@@ -0,0 +1,59 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <rfb/CMsgReader.h>
+#include <rfb/CMsgHandler.h>
+#include <rfb/HextileDecoder.h>
+
+using namespace rfb;
+
+#define EXTRA_ARGS CMsgHandler* handler
+#define FILL_RECT(r, p) handler->fillRect(r, p)
+#define IMAGE_RECT(r, p) handler->imageRect(r, p)
+#define BPP 8
+#include <rfb/hextileDecode.h>
+#undef BPP
+#define BPP 16
+#include <rfb/hextileDecode.h>
+#undef BPP
+#define BPP 32
+#include <rfb/hextileDecode.h>
+#undef BPP
+
+Decoder* HextileDecoder::create(CMsgReader* reader)
+{
+  return new HextileDecoder(reader);
+}
+
+HextileDecoder::HextileDecoder(CMsgReader* reader_) : reader(reader_)
+{
+}
+
+HextileDecoder::~HextileDecoder()
+{
+}
+
+void HextileDecoder::readRect(const Rect& r, CMsgHandler* handler)
+{
+  rdr::InStream* is = reader->getInStream();
+  rdr::U8* buf = reader->getImageBuf(16 * 16 * 4);
+  switch (reader->bpp()) {
+  case 8:  hextileDecode8 (r, is, (rdr::U8*) buf, handler); break;
+  case 16: hextileDecode16(r, is, (rdr::U16*)buf, handler); break;
+  case 32: hextileDecode32(r, is, (rdr::U32*)buf, handler); break;
+  }
+}
diff --git a/rfb/HextileDecoder.h b/rfb/HextileDecoder.h
new file mode 100644
index 0000000..718bd38
--- /dev/null
+++ b/rfb/HextileDecoder.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_HEXTILEDECODER_H__
+#define __RFB_HEXTILEDECODER_H__
+
+#include <rfb/Decoder.h>
+
+namespace rfb {
+
+  class HextileDecoder : public Decoder {
+  public:
+    static Decoder* create(CMsgReader* reader);
+    virtual void readRect(const Rect& r, CMsgHandler* handler);
+    virtual ~HextileDecoder();
+  private:
+    HextileDecoder(CMsgReader* reader);
+    CMsgReader* reader;
+  };
+}
+#endif
diff --git a/rfb/HextileEncoder.cxx b/rfb/HextileEncoder.cxx
new file mode 100644
index 0000000..736bd5f
--- /dev/null
+++ b/rfb/HextileEncoder.cxx
@@ -0,0 +1,61 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <rfb/ImageGetter.h>
+#include <rfb/encodings.h>
+#include <rfb/SMsgWriter.h>
+#include <rfb/HextileEncoder.h>
+
+using namespace rfb;
+
+#define EXTRA_ARGS ImageGetter* ig
+#define GET_IMAGE_INTO_BUF(r,buf) ig->getImage(buf, r);
+#define BPP 8
+#include <rfb/hextileEncode.h>
+#undef BPP
+#define BPP 16
+#include <rfb/hextileEncode.h>
+#undef BPP
+#define BPP 32
+#include <rfb/hextileEncode.h>
+#undef BPP
+
+Encoder* HextileEncoder::create(SMsgWriter* writer)
+{
+  return new HextileEncoder(writer);
+}
+
+HextileEncoder::HextileEncoder(SMsgWriter* writer_) : writer(writer_)
+{
+}
+
+HextileEncoder::~HextileEncoder()
+{
+}
+
+bool HextileEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
+{
+  writer->startRect(r, encodingHextile);
+  rdr::OutStream* os = writer->getOutStream();
+  switch (writer->bpp()) {
+  case 8:  hextileEncode8(r, os, ig);  break;
+  case 16: hextileEncode16(r, os, ig); break;
+  case 32: hextileEncode32(r, os, ig); break;
+  }
+  writer->endRect();
+  return true;
+}
diff --git a/rfb/HextileEncoder.h b/rfb/HextileEncoder.h
new file mode 100644
index 0000000..f09ead8
--- /dev/null
+++ b/rfb/HextileEncoder.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_HEXTILEENCODER_H__
+#define __RFB_HEXTILEENCODER_H__
+
+#include <rfb/Encoder.h>
+
+namespace rfb {
+
+  class HextileEncoder : public Encoder {
+  public:
+    static Encoder* create(SMsgWriter* writer);
+    virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual);
+    virtual ~HextileEncoder();
+  private:
+    HextileEncoder(SMsgWriter* writer);
+    SMsgWriter* writer;
+  };
+}
+#endif
diff --git a/rfb/Hostname.h b/rfb/Hostname.h
new file mode 100644
index 0000000..bdff474
--- /dev/null
+++ b/rfb/Hostname.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#ifndef __RFB_HOSTNAME_H__
+#define __RFB_HOSTNAME_H__
+
+#include <rfb/util.h>
+
+namespace rfb {
+
+  void getHostAndPort(const char* hi, char** host, int* port, int basePort=5900) {
+    CharArray portBuf;
+    CharArray hostBuf;
+    if (hi[0] == '[') {
+      if (!strSplit(&hi[1], ']', &hostBuf.buf, &portBuf.buf))
+        throw rdr::Exception("unmatched [ in host");
+    } else {
+      portBuf.buf = strDup(hi);
+    }
+    if (strSplit(portBuf.buf, ':', hostBuf.buf ? 0 : &hostBuf.buf, &portBuf.buf)) {
+      if (portBuf.buf[0] == ':') {
+        *port = atoi(&portBuf.buf[1]);
+      } else {
+        *port = atoi(portBuf.buf);
+        if (*port < 100) *port += basePort;
+      }
+    } else {
+      *port = basePort;
+    }
+    if (strlen(hostBuf.buf) == 0)
+      *host = strDup("localhost");
+    else
+      *host = hostBuf.takeBuf();
+  }
+
+};
+
+#endif // __RFB_HOSTNAME_H__
diff --git a/rfb/ImageGetter.h b/rfb/ImageGetter.h
new file mode 100644
index 0000000..b550a12
--- /dev/null
+++ b/rfb/ImageGetter.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_IMAGEGETTER_H__
+#define __RFB_IMAGEGETTER_H__
+
+#include <rfb/Rect.h>
+
+namespace rfb {
+  class ImageGetter {
+  public:
+    virtual void getImage(void* imageBuf,
+                          const Rect& r, int stride=0) = 0;
+  };
+}
+#endif
diff --git a/rfb/LogWriter.cxx b/rfb/LogWriter.cxx
new file mode 100644
index 0000000..6f267f1
--- /dev/null
+++ b/rfb/LogWriter.cxx
@@ -0,0 +1,137 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- LogWriter.cxx - client-side logging interface
+
+#include <string.h>
+#ifdef WIN32
+#define strcasecmp _stricmp
+#endif
+
+#include <rfb/LogWriter.h>
+#include <rfb/Configuration.h>
+#include <rfb/util.h>
+#include <stdlib.h>
+
+rfb::LogParameter rfb::logParams;
+
+using namespace rfb;
+
+
+LogWriter::LogWriter(const char* name) : m_name(name), m_level(0), m_log(0), m_next(log_writers) {
+  log_writers = this;
+}
+
+LogWriter::~LogWriter() {
+  // *** Should remove this logger here!
+}
+
+void LogWriter::setLog(Logger *logger) {
+  m_log = logger;
+}
+
+void LogWriter::setLevel(int level) {
+  m_level = level;
+}
+
+void
+LogWriter::listLogWriters(int width) {
+  // *** make this respect width...
+  LogWriter* current = log_writers;
+  printf("  ");
+  while (current) {
+    printf("%s", current->m_name);
+    current = current->m_next;
+    if (current) printf(", ");
+  }
+  printf("\n");
+}
+
+LogWriter* LogWriter::log_writers;
+
+LogWriter*
+LogWriter::getLogWriter(const char* name) {
+  LogWriter* current = log_writers;
+  while (current) {
+    if (strcasecmp(name, current->m_name) == 0) return current;
+      current = current->m_next;
+    }
+  return 0;
+}
+
+bool LogWriter::setLogParams(const char* params) {
+  CharArray logwriterName, loggerName, logLevel;
+  if (!strSplit(params, ':', &logwriterName.buf, &loggerName.buf) ||
+    !strSplit(loggerName.buf, ':', &loggerName.buf, &logLevel.buf)) {
+    fprintf(stderr,"failed to parse log params:%s\n",params);
+    return false;
+  }
+  int level = atoi(logLevel.buf);
+  Logger* logger = 0;
+  if (strcmp("", loggerName.buf) != 0) {
+    logger = Logger::getLogger(loggerName.buf);
+    if (!logger) fprintf(stderr,"no logger found! %s\n",loggerName.buf);
+  }
+  if (strcmp("*", logwriterName.buf) == 0) {
+    LogWriter* current = log_writers;
+    while (current) {
+      current->setLog(logger);
+      current->setLevel(level);
+      current = current->m_next;
+    }
+    return true;
+  } else {
+    LogWriter* logwriter = getLogWriter(logwriterName.buf);
+    if (!logwriter) {
+      fprintf(stderr,"no logwriter found! %s\n",logwriterName.buf);
+    } else {
+      logwriter->setLog(logger);
+      logwriter->setLevel(level);
+      return true;
+    }
+  }
+  return false;
+}
+
+
+LogParameter::LogParameter()
+  : StringParameter("Log",
+    "Specifies which log output should be directed to "
+    "which target logger, and the level of output to log. "
+    "Format is <log>:<target>:<level>[, ...].",
+    "") {
+}
+
+bool LogParameter::setParam(const char* v) {
+  if (immutable) return true;
+  LogWriter::setLogParams("*::0");
+  StringParameter::setParam(v);
+  CharArray logParam;
+  CharArray params(getData());
+  while (params.buf) {
+    strSplit(params.buf, ',', &logParam.buf, &params.buf);
+    if (strlen(logParam.buf) && !LogWriter::setLogParams(logParam.buf))
+      return false;
+  }
+  return true;
+}
+
+void LogParameter::setDefault(const char* d) {
+  def_value = d;
+  setParam(def_value);
+}
diff --git a/rfb/LogWriter.h b/rfb/LogWriter.h
new file mode 100644
index 0000000..58e81f2
--- /dev/null
+++ b/rfb/LogWriter.h
@@ -0,0 +1,106 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- LogWriter.h - The Log writer class.
+
+#ifndef __RFB_LOG_WRITER_H__
+#define __RFB_LOG_WRITER_H__
+
+#include <stdarg.h>
+#include <rfb/Logger.h>
+#include <rfb/Configuration.h>
+
+// Each log writer instance has a unique textual name,
+// and is attached to a particular Log instance and
+// is assigned a particular log level.
+
+#define DEF_LOGFUNCTION(name, level) \
+  inline void name(const char* fmt, ...) { \
+    if (m_log && (level <= m_level)) {     \
+      va_list ap; va_start(ap, fmt);       \
+      m_log->write(level, m_name, fmt, ap);\
+      va_end(ap);                          \
+    }                                      \
+  }
+
+namespace rfb {
+
+  class LogWriter;
+
+  class LogWriter {
+  public:
+    LogWriter(const char* name);
+    ~LogWriter();
+
+    const char *getName() {return m_name;}
+
+    void setLog(Logger *logger);
+    void setLevel(int level);
+
+    inline void write(int level, const char* format, ...) {
+      if (m_log && (level <= m_level)) {
+        va_list ap;
+        va_start(ap, format);
+        m_log->write(level, m_name, format, ap);
+        va_end(ap);
+      }
+    }
+
+    DEF_LOGFUNCTION(error, 0)
+    DEF_LOGFUNCTION(status, 10)
+    DEF_LOGFUNCTION(info, 30)
+    DEF_LOGFUNCTION(debug, 100)
+
+    // -=- DIAGNOSTIC & HELPER ROUTINES
+
+    static void listLogWriters(int width=79);
+
+    // -=- CLASS FIELDS & FUNCTIONS
+
+    static LogWriter* log_writers;
+
+    static LogWriter* getLogWriter(const char* name);
+
+    static bool setLogParams(const char* params);
+
+  private:
+    const char* m_name;
+    int m_level;
+    Logger* m_log;
+    LogWriter* m_next;
+  };
+
+  class LogParameter : public StringParameter {
+  public:
+    LogParameter();
+    virtual bool setParam(const char* v);
+
+    // Call this to set a suitable default value.
+    // Can't use the normal default mechanism for
+    // this because there is no guarantee on C++
+    // constructor ordering - some LogWriters may
+    // not exist when LogParameter gets constructed.
+    // NB: The default value must exist for the
+    //     lifetime of the process!
+    void setDefault(const char* v);
+  };
+  extern LogParameter logParams;
+
+};
+
+#endif // __RFB_LOG_WRITER_H__
diff --git a/rfb/Logger.cxx b/rfb/Logger.cxx
new file mode 100644
index 0000000..84b8f55
--- /dev/null
+++ b/rfb/Logger.cxx
@@ -0,0 +1,107 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- Logger.cxx - support for the Logger and LogWriter classes
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef WIN32
+#define strcasecmp _stricmp
+#define vsnprintf _vsnprintf
+#define HAVE_VSNPRINTF
+#endif
+
+#include <rfb/Logger.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+
+using namespace rfb;
+
+#ifndef HAVE_VSNPRINTF
+static int vsnprintf(char *str, size_t n, const char *format, va_list ap)
+{
+  str[0] = 0;
+  FILE* fp = fopen("/dev/null","w");
+  if (!fp) return 0;
+  int len = vfprintf(fp, format, ap);
+  if (len <= 0) return 0;
+  fclose(fp);
+
+  CharArray s(len+1);
+  vsprintf(s.buf, format, ap);
+
+  if (len > (int)n-1) len = n-1;
+  memcpy(str, s.buf, len);
+  str[len] = 0;
+  return len;
+}
+#endif
+
+
+Logger* Logger::loggers = 0;
+
+Logger::Logger(const char* name) : registered(false), m_name(name), m_next(0) {
+}
+
+Logger::~Logger() {
+  // *** Should remove this logger here!
+}
+
+void Logger::write(int level, const char *logname, const char* format,
+                   va_list ap)
+{
+  // - Format the supplied data, and pass it to the
+  //   actual log_message function
+  //   The log level is included as a hint for loggers capable of representing
+  //   different log levels in some way.
+  char buf1[4096];
+  vsnprintf(buf1, sizeof(buf1)-1, format, ap);
+  buf1[sizeof(buf1)-1] = 0;
+  write(level, logname, buf1);
+}
+
+void
+Logger::registerLogger() {
+  if (!registered) {
+    registered = true;
+    m_next = loggers;
+    loggers=this;
+  }
+}
+    
+Logger*
+Logger::getLogger(const char* name) {
+  Logger* current = loggers;
+  while (current) {
+    if (strcasecmp(name, current->m_name) == 0) return current;
+    current = current->m_next;
+  }
+  return 0;
+}
+
+void
+Logger::listLoggers() {
+  Logger* current = loggers;
+  while (current) {
+    printf("  %s\n", current->m_name);
+    current = current->m_next;
+  }
+}
+
+
diff --git a/rfb/Logger.h b/rfb/Logger.h
new file mode 100644
index 0000000..4233964
--- /dev/null
+++ b/rfb/Logger.h
@@ -0,0 +1,70 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- Logger.h - The Logger class.
+
+#ifndef __RFB_LOGGER_H__
+#define __RFB_LOGGER_H__
+
+#include <stdarg.h>
+#include <stdio.h>
+
+// Each log writer instance has a unique textual name,
+// and is attached to a particular Logger instance and
+// is assigned a particular log level.
+
+namespace rfb {
+
+  class Logger {
+  public:
+
+    // -=- Create / Destroy a logger
+
+    Logger(const char* name);
+    virtual ~Logger();
+
+    // -=- Get the name of a logger
+
+    const char *getName() {return m_name;}
+
+    // -=- Write data to a log
+
+    virtual void write(int level, const char *logname, const char *text) = 0;
+    void write(int level, const char *logname, const char* format, va_list ap);
+
+    // -=- Register a logger
+
+    void registerLogger();
+
+    // -=- CLASS FIELDS & FUNCTIONS
+
+    static Logger* loggers;
+
+    static Logger* getLogger(const char* name);
+
+    static void listLoggers();
+
+  private:
+    bool registered;
+    const char *m_name;
+    Logger *m_next;
+  };
+
+};
+
+#endif // __RFB_LOGGER_H__
diff --git a/rfb/Logger_file.cxx b/rfb/Logger_file.cxx
new file mode 100644
index 0000000..ac249b3
--- /dev/null
+++ b/rfb/Logger_file.cxx
@@ -0,0 +1,123 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- Logger_file.cxx - Logger instance for a file
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <rfb/util.h>
+#include <rfb/Logger_file.h>
+#include <rfb/Threading.h>
+
+using namespace rfb;
+
+
+// If threading is available then protect the write() operation
+// from concurrent accesses
+#ifdef __RFB_THREADING_IMPL
+static Mutex logLock;
+#endif
+
+
+Logger_File::Logger_File(const char* loggerName)
+  : Logger(loggerName), indent(13), width(79), m_filename(0), m_file(0),
+    m_lastLogTime(0)
+{
+}
+
+Logger_File::~Logger_File()
+{
+  closeFile();
+}
+
+void Logger_File::write(int level, const char *logname, const char *message)
+{
+#ifdef __RFB_THREADING_IMPL
+  Lock l(logLock);
+#endif
+  if (!m_file) {
+    if (!m_filename) return;
+    m_file = fopen(m_filename, "w+");
+    if (!m_file) return;
+  }
+
+#ifndef _WIN32_WCE
+  time_t current = time(0);
+  if (current != m_lastLogTime) {
+    m_lastLogTime = current;
+    fprintf(m_file, "\n%s", ctime(&m_lastLogTime));
+  }
+#endif
+
+  fprintf(m_file," %s:", logname);
+  int column = strlen(logname) + 2;
+  if (column < indent) {
+    fprintf(m_file,"%*s",indent-column,"");
+    column = indent;
+  }
+  while (true) {
+    const char* s = strchr(message, ' ');
+    int wordLen;
+    if (s) wordLen = s-message;
+    else wordLen = strlen(message);
+
+    if (column + wordLen + 1 > width) {
+      fprintf(m_file,"\n%*s",indent,"");
+      column = indent;
+    }
+    fprintf(m_file," %.*s",wordLen,message);
+    column += wordLen + 1;
+    message += wordLen + 1;
+    if (!s) break;
+  }
+  fprintf(m_file,"\n");
+  fflush(m_file);
+}
+
+void Logger_File::setFilename(const char* filename)
+{
+  closeFile();
+  m_filename = strDup(filename);
+}
+
+void Logger_File::setFile(FILE* file)
+{
+  closeFile();
+  m_file = file;
+}
+
+void Logger_File::closeFile()
+{
+  if (m_filename) {
+    if (m_file) {
+      fclose(m_file);
+      m_file = 0;
+    }
+    strFree(m_filename);
+    m_filename = 0;
+  }
+}
+
+static Logger_File logger("file");
+
+bool rfb::initFileLogger(const char* filename) {
+  logger.setFilename(filename);
+  logger.registerLogger();
+  return true;
+}
diff --git a/rfb/Logger_file.h b/rfb/Logger_file.h
new file mode 100644
index 0000000..30c3f40
--- /dev/null
+++ b/rfb/Logger_file.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- Logger_file - log to a file
+
+#ifndef __RFB_LOGGER_FILE_H__
+#define __RFB_LOGGER_FILE_H__
+
+#include <time.h>
+#include <rfb/Logger.h>
+
+namespace rfb {
+
+  class Logger_File : public Logger {
+  public:
+    Logger_File(const char* loggerName);
+    ~Logger_File();
+
+    virtual void write(int level, const char *logname, const char *message);
+    void setFilename(const char* filename);
+    void setFile(FILE* file);
+
+    int indent;
+    int width;
+
+  protected:
+    void closeFile();
+    char* m_filename;
+    FILE* m_file;
+    time_t m_lastLogTime;
+  };
+
+  bool initFileLogger(const char* filename);
+};
+
+#endif
diff --git a/rfb/Logger_stdio.cxx b/rfb/Logger_stdio.cxx
new file mode 100644
index 0000000..ac9556e
--- /dev/null
+++ b/rfb/Logger_stdio.cxx
@@ -0,0 +1,32 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- Logger_stdio.cxx - Logger instances for stderr and stdout
+
+#include <rfb/Logger_stdio.h>
+
+using namespace rfb;
+
+static Logger_StdIO logStdErr("stderr", stderr);
+static Logger_StdIO logStdOut("stdout", stdout);
+
+bool rfb::initStdIOLoggers() {
+  logStdErr.registerLogger();
+  logStdOut.registerLogger();
+  return true;
+}
diff --git a/rfb/Logger_stdio.h b/rfb/Logger_stdio.h
new file mode 100644
index 0000000..68f795f
--- /dev/null
+++ b/rfb/Logger_stdio.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- Logger_stdio - standard output logger instances
+
+#ifndef __RFB_LOGGER_STDIO_H__
+#define __RFB_LOGGER_STDIO_H__
+
+#include <rfb/Logger_file.h>
+
+namespace rfb {
+
+  class Logger_StdIO : public Logger_File {
+  public:
+    Logger_StdIO(const char *name, FILE* file) : Logger_File(name) {
+      setFile(file);
+    }
+  };
+
+  bool initStdIOLoggers();
+
+};
+
+#endif
diff --git a/rfb/Makefile.in b/rfb/Makefile.in
new file mode 100644
index 0000000..50bc04c
--- /dev/null
+++ b/rfb/Makefile.in
@@ -0,0 +1,66 @@
+
+CXXSRCS = \
+  Blacklist.cxx \
+  CConnection.cxx \
+  CMsgHandler.cxx \
+  CMsgReader.cxx \
+  CMsgReaderV3.cxx \
+  CMsgWriter.cxx \
+  CMsgWriterV3.cxx \
+  CSecurityVncAuth.cxx \
+  ComparingUpdateTracker.cxx \
+  Configuration.cxx \
+  ConnParams.cxx \
+  Cursor.cxx \
+  Decoder.cxx \
+  Encoder.cxx \
+  HTTPServer.cxx \
+  HextileDecoder.cxx \
+  HextileEncoder.cxx \
+  LogWriter.cxx \
+  Logger.cxx \
+  Logger_file.cxx \
+  Logger_stdio.cxx \
+  PixelBuffer.cxx \
+  PixelFormat.cxx \
+  RREEncoder.cxx \
+  RREDecoder.cxx \
+  RawDecoder.cxx \
+  RawEncoder.cxx \
+  Region.cxx \
+  SConnection.cxx \
+  SMsgHandler.cxx \
+  SMsgReader.cxx \
+  SMsgReaderV3.cxx \
+  SMsgWriter.cxx \
+  SMsgWriterV3.cxx \
+  ServerCore.cxx \
+  SSecurityFactoryStandard.cxx \
+  SSecurityVncAuth.cxx \
+  TransImageGetter.cxx \
+  UpdateTracker.cxx \
+  VNCSConnectionST.cxx \
+  VNCServerST.cxx \
+  ZRLEEncoder.cxx \
+  ZRLEDecoder.cxx \
+  encodings.cxx \
+  secTypes.cxx \
+  util.cxx \
+  vncAuth.cxx
+
+SRCS = d3des.c $(CXXSRCS)
+
+OBJS = d3des.o $(CXXSRCS:.cxx=.o)
+
+DIR_CPPFLAGS = -I$(top_srcdir) @VSNPRINTF_DEFINE@
+
+library = librfb.a
+
+all:: $(library)
+
+$(library): $(OBJS)
+	rm -f $(library)
+	$(AR) $(library) $(OBJS)
+	$(RANLIB) $(library)
+
+# followed by boilerplate.mk
diff --git a/rfb/Pixel.h b/rfb/Pixel.h
new file mode 100644
index 0000000..2b1aaf0
--- /dev/null
+++ b/rfb/Pixel.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_PIXEL_H__
+#define __RFB_PIXEL_H__
+
+#include <rdr/types.h>
+
+namespace rfb {
+  typedef rdr::U32 Pixel; // must be big enough to hold any pixel value
+}
+#endif
diff --git a/rfb/PixelBuffer.cxx b/rfb/PixelBuffer.cxx
new file mode 100644
index 0000000..fcad227
--- /dev/null
+++ b/rfb/PixelBuffer.cxx
@@ -0,0 +1,309 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- PixelBuffer.cxx
+//
+// The PixelBuffer class encapsulates the PixelFormat and dimensions
+// of a block of pixel data.
+
+#include <rfb/Exception.h>
+#include <rfb/LogWriter.h>
+#include <rfb/PixelBuffer.h>
+
+using namespace rfb;
+using namespace rdr;
+
+static LogWriter vlog("PixelBuffer");
+
+
+// -=- Generic pixel buffer class
+
+PixelBuffer::PixelBuffer(const PixelFormat& pf, int w, int h, ColourMap* cm)
+  : format(pf), width_(w), height_(h), colourmap(cm) {}
+PixelBuffer::PixelBuffer() : width_(0), height_(0), colourmap(0) {}
+
+PixelBuffer::~PixelBuffer() {}
+
+
+void PixelBuffer::setPF(const PixelFormat &pf) {format = pf;}
+const PixelFormat& PixelBuffer::getPF() const {return format;}
+ColourMap* PixelBuffer::getColourMap() const {return colourmap;}
+
+
+void
+PixelBuffer::getImage(void* imageBuf, const Rect& r, int outStride) {
+  int inStride;
+  const U8* data = getPixelsR(r, &inStride);
+  // We assume that the specified rectangle is pre-clipped to the buffer
+  int bytesPerPixel = format.bpp/8;
+  int inBytesPerRow = inStride * bytesPerPixel;
+  if (!outStride) outStride = r.width();
+  int outBytesPerRow = outStride * bytesPerPixel;
+  int bytesPerMemCpy = r.width() * bytesPerPixel;
+  U8* imageBufPos = (U8*)imageBuf;
+  const U8* end = data + (inBytesPerRow * r.height());
+  while (data < end) {
+    memcpy(imageBufPos, data, bytesPerMemCpy);
+    imageBufPos += outBytesPerRow;
+    data += inBytesPerRow;
+  }
+}
+
+/* ***
+Pixel PixelBuffer::getPixel(const Point& p) {
+  int stride;
+  Rect r = Rect(p.x, p.y, p.x+1, p.y+1);
+  switch(format.bpp) {
+  case 8: return *((rdr::U8*)getDataAt(r, &stride));
+  case 16: return *((rdr::U16*)getDataAt(r, &stride));
+  case 32: return *((rdr::U32*)getDataAt(r, &stride));
+  default: return 0;
+  };
+}
+*/
+
+
+FullFramePixelBuffer::FullFramePixelBuffer(const PixelFormat& pf, int w, int h,
+                                           rdr::U8* data_, ColourMap* cm)
+  : PixelBuffer(pf, w, h, cm), data(data_)
+{
+}
+
+FullFramePixelBuffer::FullFramePixelBuffer() : data(0) {}
+
+FullFramePixelBuffer::~FullFramePixelBuffer() {}
+
+
+int FullFramePixelBuffer::getStride() const { return width(); }
+
+rdr::U8* FullFramePixelBuffer::getPixelsRW(const Rect& r, int* stride)
+{
+  *stride = getStride();
+  return &data[(r.tl.x + (r.tl.y * *stride)) * format.bpp/8];
+}
+
+
+void FullFramePixelBuffer::fillRect(const Rect& r, Pixel pix) {
+  int stride;
+  U8* data = getPixelsRW(r, &stride);
+  int bytesPerPixel = getPF().bpp/8;
+  int bytesPerRow = bytesPerPixel * stride;
+  int bytesPerFill = bytesPerPixel * r.width();
+
+  U8* end = data + (bytesPerRow * r.height());
+  while (data < end) {
+    switch (bytesPerPixel) {
+    case 1:
+      memset(data, pix, bytesPerFill);
+      break;
+    case 2:
+      {
+        U16* optr = (U16*)data;
+        U16* eol = optr + r.width();
+        while (optr < eol)
+          *optr++ = pix;
+      }
+      break;
+    case 4:
+      {
+        U32* optr = (U32*)data;
+        U32* eol = optr + r.width();
+        while (optr < eol)
+          *optr++ = pix;
+      }
+      break;
+    }
+    data += bytesPerRow;
+  }
+}
+
+void FullFramePixelBuffer::imageRect(const Rect& r, const void* pixels, int srcStride) {
+  int bytesPerPixel = getPF().bpp/8;
+  int destStride;
+  U8* dest = getPixelsRW(r, &destStride);
+  int bytesPerDestRow = bytesPerPixel * destStride;
+  if (!srcStride) srcStride = r.width();
+  int bytesPerSrcRow = bytesPerPixel * srcStride;
+  int bytesPerFill = bytesPerPixel * r.width();
+  const U8* src = (const U8*)pixels;
+  U8* end = dest + (bytesPerDestRow * r.height());
+  while (dest < end) {
+    memcpy(dest, src, bytesPerFill);
+    dest += bytesPerDestRow;
+    src += bytesPerSrcRow;
+  }
+}
+
+void FullFramePixelBuffer::maskRect(const Rect& r, const void* pixels, const void* mask_) {
+  Rect cr = getRect().intersect(r);
+  if (cr.is_empty()) return;
+  int stride;
+  U8* data = getPixelsRW(cr, &stride);
+  U8* mask = (U8*) mask_;
+  int w = cr.width();
+  int h = cr.height();
+  int bpp = getPF().bpp;
+  int pixelStride = r.width();
+  int maskStride = (r.width() + 7) / 8;
+
+  Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
+  mask += offset.y * maskStride;
+  for (int y = 0; y < h; y++) {
+    int cy = offset.y + y;
+    for (int x = 0; x < w; x++) {
+      int cx = offset.x + x;
+      U8* byte = mask + (cx / 8);
+      int bit = 7 - cx % 8;
+      if ((*byte) & (1 << bit)) {
+        switch (bpp) {
+        case 8:
+          ((U8*)data)[y * stride + x] = ((U8*)pixels)[cy * pixelStride + cx];
+          break;
+        case 16:
+          ((U16*)data)[y * stride + x] = ((U16*)pixels)[cy * pixelStride + cx];
+          break;
+        case 32:
+          ((U32*)data)[y * stride + x] = ((U32*)pixels)[cy * pixelStride + cx];
+          break;
+        }
+      }
+    }
+    mask += maskStride;
+  }
+}
+
+void FullFramePixelBuffer::maskRect(const Rect& r, Pixel pixel, const void* mask_) {
+  Rect cr = getRect().intersect(r);
+  if (cr.is_empty()) return;
+  int stride;
+  U8* data = getPixelsRW(cr, &stride);
+  U8* mask = (U8*) mask_;
+  int w = cr.width();
+  int h = cr.height();
+  int bpp = getPF().bpp;
+  int maskStride = (r.width() + 7) / 8;
+
+  Point offset = Point(cr.tl.x-r.tl.x, cr.tl.y-r.tl.y);
+  mask += offset.y * maskStride;
+  for (int y = 0; y < h; y++) {
+    for (int x = 0; x < w; x++) {
+      int cx = offset.x + x;
+      U8* byte = mask + (cx / 8);
+      int bit = 7 - cx % 8;
+      if ((*byte) & (1 << bit)) {
+        switch (bpp) {
+        case 8:
+          ((U8*)data)[y * stride + x] = pixel;
+          break;
+        case 16:
+          ((U16*)data)[y * stride + x] = pixel;
+          break;
+        case 32:
+          ((U32*)data)[y * stride + x] = pixel;
+          break;
+        }
+      }
+    }
+    mask += maskStride;
+  }
+}
+
+void FullFramePixelBuffer::copyRect(const Rect &rect, const Point &move_by_delta) {
+  int stride;
+  U8* data = getPixelsRW(getRect(), &stride);
+  // We assume that the specified rectangle is pre-clipped to the buffer
+  unsigned int bytesPerPixel, bytesPerRow, bytesPerMemCpy;
+  Rect srect = rect.translate(move_by_delta.negate());
+  bytesPerPixel = getPF().bpp/8;
+  bytesPerRow = stride * bytesPerPixel;
+  bytesPerMemCpy = rect.width() * bytesPerPixel;
+  if (move_by_delta.y <= 0) {
+    U8* dest = data + rect.tl.x*bytesPerPixel + rect.tl.y*bytesPerRow;
+    U8* src = data + srect.tl.x*bytesPerPixel + srect.tl.y*bytesPerRow;
+    for (int i=rect.tl.y; i<rect.br.y; i++) {
+      memmove(dest, src, bytesPerMemCpy);
+      dest += bytesPerRow;
+      src += bytesPerRow;
+    }
+  } else {
+    U8* dest = data + rect.tl.x*bytesPerPixel + (rect.br.y-1)*bytesPerRow;
+    U8* src = data + srect.tl.x*bytesPerPixel + (srect.br.y-1)*bytesPerRow;
+    for (int i=rect.tl.y; i<rect.br.y; i++) {
+      memmove(dest, src, bytesPerMemCpy);
+      dest -= bytesPerRow;
+      src -= bytesPerRow;
+    }
+  }
+}
+
+
+// -=- Managed pixel buffer class
+// Automatically allocates enough space for the specified format & area
+
+ManagedPixelBuffer::ManagedPixelBuffer()
+  : datasize(0), own_colourmap(false)
+{
+  checkDataSize();
+};
+
+ManagedPixelBuffer::ManagedPixelBuffer(const PixelFormat& pf, int w, int h)
+  : FullFramePixelBuffer(pf, w, h, 0, 0), datasize(0), own_colourmap(false)
+{
+  checkDataSize();
+};
+
+ManagedPixelBuffer::~ManagedPixelBuffer() {
+  if (data) delete [] data;
+  if (colourmap && own_colourmap) delete colourmap;
+};
+
+
+void
+ManagedPixelBuffer::setPF(const PixelFormat &pf) {
+  format = pf; checkDataSize();
+};
+void
+ManagedPixelBuffer::setSize(int w, int h) {
+  width_ = w; height_ = h; checkDataSize();
+};
+
+
+void
+ManagedPixelBuffer::setColourMap(ColourMap* cm, bool own_cm) {
+  if (colourmap && own_colourmap) delete colourmap;
+  colourmap = cm;
+  own_colourmap = own_cm;
+}
+
+inline void
+ManagedPixelBuffer::checkDataSize() {
+  unsigned long new_datasize = width_ * height_ * (format.bpp/8);
+  if (datasize < new_datasize) {
+    vlog.debug("reallocating managed buffer (%dx%d)", width_, height_);
+    if (data) {
+      delete [] data;
+      datasize = 0; data = 0;
+    }
+    if (new_datasize) {
+      data = new U8[new_datasize];
+      if (!data)
+        throw Exception("rfb::ManagedPixelBuffer unable to allocate buffer");
+      datasize = new_datasize;
+    }
+  }
+};
diff --git a/rfb/PixelBuffer.h b/rfb/PixelBuffer.h
new file mode 100644
index 0000000..2ba105a
--- /dev/null
+++ b/rfb/PixelBuffer.h
@@ -0,0 +1,172 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- PixelBuffer.h
+//
+// The PixelBuffer class encapsulates the PixelFormat and dimensions
+// of a block of pixel data.
+
+#ifndef __RFB_PIXEL_BUFFER_H__
+#define __RFB_PIXEL_BUFFER_H__
+
+#include <rfb/ImageGetter.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/ColourMap.h>
+#include <rfb/Rect.h>
+#include <rfb/Pixel.h>
+
+namespace rfb {
+
+  class Region;
+
+  class PixelBuffer : public ImageGetter {
+  public:
+    PixelBuffer(const PixelFormat& pf, int width, int height, ColourMap* cm);
+    virtual ~PixelBuffer();
+
+    ///////////////////////////////////////////////
+    // Format / Layout
+    //
+
+    // Set/get pixel format & colourmap
+    virtual void setPF(const PixelFormat &pf);
+    virtual const PixelFormat &getPF() const;
+    virtual ColourMap* getColourMap() const;
+
+    // Get width, height and number of pixels
+    int width()  const { return width_; }
+    int height() const { return height_; }
+    int area() const { return width_ * height_; }
+
+    // Get rectangle encompassing this buffer
+    //   Top-left of rectangle is either at (0,0), or the specified point.
+    Rect getRect() const { return Rect(0, 0, width_, height_); }
+    Rect getRect(const Point& pos) const {
+      return Rect(pos, pos.translate(Point(width_, height_)));
+    }
+
+    ///////////////////////////////////////////////
+    // Access to pixel data
+    //
+
+    // Get a pointer into the buffer
+    //   The pointer is to the top-left pixel of the specified Rect.
+    //   The buffer stride (in pixels) is returned.
+    virtual const rdr::U8* getPixelsR(const Rect& r, int* stride) = 0;
+
+    // Get pixel data for a given part of the buffer
+    //   Data is copied into the supplied buffer, with the specified
+    //   stride.
+    virtual void getImage(void* imageBuf, const Rect& r, int stride=0);
+
+    // Get the data at (x,y) as a Pixel.
+    //   VERY INEFFICIENT!!!
+    // *** Pixel getPixel(const Point& p);
+
+    ///////////////////////////////////////////////
+    // Framebuffer update methods
+    //
+
+    // Ensure that the specified rectangle of buffer is up to date.
+    //   Overridden by derived classes implementing framebuffer access
+    //   to copy the required display data into place.
+    virtual void grabRegion(const Region& region) {}
+
+  protected:
+    PixelBuffer();
+    PixelFormat format;
+    int width_, height_;
+    ColourMap* colourmap;
+  };
+
+  // FullFramePixelBuffer
+
+  class FullFramePixelBuffer : public PixelBuffer {
+  public:
+    FullFramePixelBuffer(const PixelFormat& pf, int width, int height,
+                         rdr::U8* data_, ColourMap* cm);
+    virtual ~FullFramePixelBuffer();
+
+    // - Get the number of pixels per row in the actual pixel buffer data area
+    //   This may in some cases NOT be the same as width().
+    virtual int getStride() const;
+
+    // Get a pointer to specified pixel data
+    virtual rdr::U8* getPixelsRW(const Rect& r, int* stride);
+    virtual const rdr::U8* getPixelsR(const Rect& r, int* stride) {
+      return getPixelsRW(r, stride);
+    }
+
+    ///////////////////////////////////////////////
+    // Basic rendering operations
+    // These operations DO NOT clip to the pixelbuffer area, or trap overruns.
+
+    // Fill a rectangle
+    virtual void fillRect(const Rect &dest, Pixel pix);
+
+    // Copy pixel data to the buffer
+    virtual void imageRect(const Rect &dest, const void* pixels, int stride=0);
+
+    // Copy pixel data from one PixelBuffer location to another
+    virtual void copyRect(const Rect &dest, const Point &move_by_delta);
+
+    // Copy pixel data to the buffer through a mask
+    //   pixels is a pointer to the pixel to be copied to r.tl.
+    //   maskPos specifies the pixel offset in the mask to start from.
+    //   mask_ is a pointer to the mask bits at (0,0).
+    //   pStride and mStride are the strides of the pixel and mask buffers.
+    virtual void maskRect(const Rect& r, const void* pixels, const void* mask_);
+
+    //   pixel is the Pixel value to be used where mask_ is set
+    virtual void maskRect(const Rect& r, Pixel pixel, const void* mask_);
+
+    // *** Should this be visible?
+    rdr::U8* data;
+
+  protected:
+    FullFramePixelBuffer();
+  };
+
+  // -=- Managed pixel buffer class
+  // Automatically allocates enough space for the specified format & area
+
+  class ManagedPixelBuffer : public FullFramePixelBuffer {
+  public:
+    ManagedPixelBuffer();
+    ManagedPixelBuffer(const PixelFormat& pf, int width, int height);
+    virtual ~ManagedPixelBuffer();
+
+    // Manage the pixel buffer layout
+    virtual void setPF(const PixelFormat &pf);
+    virtual void setSize(int w, int h);
+
+    // Assign a colour map to the buffer
+    virtual void setColourMap(ColourMap* cm, bool own_cm);
+
+    // Return the total number of bytes of pixel data in the buffer
+    int dataLen() const { return width_ * height_ * (format.bpp/8); }
+
+  protected:
+    unsigned long datasize;
+    bool own_colourmap;
+    void checkDataSize();
+  };
+
+};
+
+#endif // __RFB_PIXEL_BUFFER_H__
diff --git a/rfb/PixelFormat.cxx b/rfb/PixelFormat.cxx
new file mode 100644
index 0000000..683b53a
--- /dev/null
+++ b/rfb/PixelFormat.cxx
@@ -0,0 +1,238 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <rdr/InStream.h>
+#include <rdr/OutStream.h>
+#include <rfb/PixelFormat.h>
+
+#ifdef _WIN32
+#define strcasecmp _stricmp
+#endif
+
+using namespace rfb;
+
+PixelFormat::PixelFormat(int b, int d, bool e, bool t,
+                         int rm, int gm, int bm, int rs, int gs, int bs)
+  : bpp(b), depth(d), bigEndian(e), trueColour(t),
+    redMax(rm), greenMax(gm), blueMax(bm),
+    redShift(rs), greenShift(gs), blueShift(bs)
+{
+}
+
+PixelFormat::PixelFormat()
+  : bpp(8), depth(8), bigEndian(false), trueColour(true),
+    redMax(7), greenMax(7), blueMax(3),
+    redShift(0), greenShift(3), blueShift(6)
+{
+}
+
+bool PixelFormat::equal(const PixelFormat& other) const
+{
+  return (bpp == other.bpp &&
+          depth == other.depth &&
+          (bigEndian == other.bigEndian || bpp == 8) &&
+          trueColour == other.trueColour &&
+          (!trueColour || (redMax == other.redMax &&
+                           greenMax == other.greenMax &&
+                           blueMax == other.blueMax &&
+                           redShift == other.redShift &&
+                           greenShift == other.greenShift &&
+                           blueShift == other.blueShift)));
+}
+
+void PixelFormat::read(rdr::InStream* is)
+{
+  bpp = is->readU8();
+  depth = is->readU8();
+  bigEndian = is->readU8();
+  trueColour = is->readU8();
+  redMax = is->readU16();
+  greenMax = is->readU16();
+  blueMax = is->readU16();
+  redShift = is->readU8();
+  greenShift = is->readU8();
+  blueShift = is->readU8();
+  is->skip(3);
+}
+
+void PixelFormat::write(rdr::OutStream* os) const
+{
+  os->writeU8(bpp);
+  os->writeU8(depth);
+  os->writeU8(bigEndian);
+  os->writeU8(trueColour);
+  os->writeU16(redMax);
+  os->writeU16(greenMax);
+  os->writeU16(blueMax);
+  os->writeU8(redShift);
+  os->writeU8(greenShift);
+  os->writeU8(blueShift);
+  os->pad(3);
+}
+
+Pixel PixelFormat::pixelFromRGB(rdr::U16 red, rdr::U16 green, rdr::U16 blue,
+                                ColourMap* cm) const
+{
+  if (trueColour) {
+    rdr::U32 r = ((rdr::U32)red   * redMax   + 32767) / 65535;
+    rdr::U32 g = ((rdr::U32)green * greenMax + 32767) / 65535;
+    rdr::U32 b = ((rdr::U32)blue  * blueMax  + 32767) / 65535;
+
+    return (r << redShift) | (g << greenShift) | (b << blueShift);
+  } else if (cm) {
+    // Try to find the closest pixel by Cartesian distance
+    int colours = 1 << depth;
+    int diff = 256 * 256 * 4;
+    int col = 0;
+    for (int i=0; i<colours; i++) {
+      int r, g, b;
+      cm->lookup(i, &r, &g, &b);
+      int rd = (r-red) >> 8;
+      int gd = (g-green) >> 8;
+      int bd = (b-blue) >> 8;
+      int d = rd*rd + gd*gd + bd*bd;
+      if (d < diff) {
+        col = i;
+        diff = d;
+      }
+    }
+    return col;
+  }
+  // XXX just return 0 for colour map?
+  return 0;
+}
+
+
+void PixelFormat::rgbFromPixel(Pixel p, ColourMap* cm, Colour* rgb) const
+{
+  if (trueColour) {
+    rgb->r = (((p >> redShift  ) & redMax  ) * 65535 + redMax  /2) / redMax;
+    rgb->g = (((p >> greenShift) & greenMax) * 65535 + greenMax/2) / greenMax;
+    rgb->b = (((p >> blueShift ) & blueMax ) * 65535 + blueMax /2) / blueMax;
+  } else {
+    cm->lookup(p, &rgb->r, &rgb->g, &rgb->b);
+  }
+}
+
+
+void PixelFormat::print(char* str, int len) const
+{
+  // Unfortunately snprintf is not widely available so we build the string up
+  // using strncat - not pretty, but should be safe against buffer overruns.
+
+  char num[20];
+  if (len < 1) return;
+  str[0] = 0;
+  strncat(str, "depth ", len-1-strlen(str));
+  sprintf(num,"%d",depth);
+  strncat(str, num, len-1-strlen(str));
+  strncat(str, " (", len-1-strlen(str));
+  sprintf(num,"%d",bpp);
+  strncat(str, num, len-1-strlen(str));
+  strncat(str, "bpp)", len-1-strlen(str));
+  if (bpp != 8) {
+    if (bigEndian)
+      strncat(str, " big-endian", len-1-strlen(str));
+    else
+      strncat(str, " little-endian", len-1-strlen(str));
+  }
+
+  if (!trueColour) {
+    strncat(str, " colour-map", len-1-strlen(str));
+    return;
+  }
+
+  if (blueShift == 0 && greenShift > blueShift && redShift > greenShift &&
+      blueMax  == (1 << greenShift) - 1 &&
+      greenMax == (1 << (redShift-greenShift)) - 1 &&
+      redMax   == (1 << (depth-redShift)) - 1)
+  {
+    strncat(str, " rgb", len-1-strlen(str));
+    sprintf(num,"%d",depth-redShift);
+    strncat(str, num, len-1-strlen(str));
+    sprintf(num,"%d",redShift-greenShift);
+    strncat(str, num, len-1-strlen(str));
+    sprintf(num,"%d",greenShift);
+    strncat(str, num, len-1-strlen(str));
+    return;
+  }
+
+  if (redShift == 0 && greenShift > redShift && blueShift > greenShift &&
+      redMax   == (1 << greenShift) - 1 &&
+      greenMax == (1 << (blueShift-greenShift)) - 1 &&
+      blueMax  == (1 << (depth-blueShift)) - 1)
+  {
+    strncat(str, " bgr", len-1-strlen(str));
+    sprintf(num,"%d",depth-blueShift);
+    strncat(str, num, len-1-strlen(str));
+    sprintf(num,"%d",blueShift-greenShift);
+    strncat(str, num, len-1-strlen(str));
+    sprintf(num,"%d",greenShift);
+    strncat(str, num, len-1-strlen(str));
+    return;
+  }
+
+  strncat(str, " rgb max ", len-1-strlen(str));
+  sprintf(num,"%d,",redMax);
+  strncat(str, num, len-1-strlen(str));
+  sprintf(num,"%d,",greenMax);
+  strncat(str, num, len-1-strlen(str));
+  sprintf(num,"%d",blueMax);
+  strncat(str, num, len-1-strlen(str));
+  strncat(str, " shift ", len-1-strlen(str));
+  sprintf(num,"%d,",redShift);
+  strncat(str, num, len-1-strlen(str));
+  sprintf(num,"%d,",greenShift);
+  strncat(str, num, len-1-strlen(str));
+  sprintf(num,"%d",blueShift);
+  strncat(str, num, len-1-strlen(str));
+}
+
+
+bool PixelFormat::parse(const char* str)
+{
+  char rgbbgr[4];
+  int bits1, bits2, bits3;
+  if (sscanf(str, "%3s%1d%1d%1d", rgbbgr, &bits1, &bits2, &bits3) < 4)
+    return false;
+  
+  depth = bits1 + bits2 + bits3;
+  bpp = depth <= 8 ? 8 : ((depth <= 16) ? 16 : 32);
+  trueColour = true;
+  rdr::U32 endianTest = 1;
+  bigEndian = (*(rdr::U8*)&endianTest == 0);
+
+  greenShift = bits3;
+  greenMax = (1 << bits2) - 1;
+
+  if (strcasecmp(rgbbgr, "bgr") == 0) {
+    redShift = 0;
+    redMax = (1 << bits3) - 1;
+    blueShift = bits3 + bits2;
+    blueMax = (1 << bits1) - 1;
+  } else if (strcasecmp(rgbbgr, "rgb") == 0) {
+    blueShift = 0;
+    blueMax = (1 << bits3) - 1;
+    redShift = bits3 + bits2;
+    redMax = (1 << bits1) - 1;
+  } else {
+    return false;
+  }
+  return true;
+}
diff --git a/rfb/PixelFormat.h b/rfb/PixelFormat.h
new file mode 100644
index 0000000..0f2edd2
--- /dev/null
+++ b/rfb/PixelFormat.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// PixelFormat - structure to represent a pixel format.  Also has useful
+// methods for reading & writing to streams, etc.
+//
+
+#ifndef __RFB_PIXELFORMAT_H__
+#define __RFB_PIXELFORMAT_H__
+
+#include <rfb/Pixel.h>
+#include <rfb/ColourMap.h>
+
+namespace rdr { class InStream; class OutStream; }
+
+namespace rfb {
+
+  class PixelFormat {
+  public:
+    PixelFormat(int b, int d, bool e, bool t,
+                int rm=0, int gm=0, int bm=0, int rs=0, int gs=0, int bs=0);
+    PixelFormat();
+    bool equal(const PixelFormat& other) const;
+    void read(rdr::InStream* is);
+    void write(rdr::OutStream* os) const;
+    Pixel pixelFromRGB(rdr::U16 red, rdr::U16 green, rdr::U16 blue, ColourMap* cm=0) const;
+    void rgbFromPixel(Pixel pix, ColourMap* cm, Colour* rgb) const;
+    void print(char* str, int len) const;
+    bool parse(const char* str);
+
+    int bpp;
+    int depth;
+    bool bigEndian;
+    bool trueColour;
+    int redMax;
+    int greenMax;
+    int blueMax;
+    int redShift;
+    int greenShift;
+    int blueShift;
+  };
+}
+#endif
diff --git a/rfb/RREDecoder.cxx b/rfb/RREDecoder.cxx
new file mode 100644
index 0000000..c613dbb
--- /dev/null
+++ b/rfb/RREDecoder.cxx
@@ -0,0 +1,58 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <rfb/CMsgReader.h>
+#include <rfb/CMsgHandler.h>
+#include <rfb/RREDecoder.h>
+
+using namespace rfb;
+
+#define EXTRA_ARGS CMsgHandler* handler
+#define FILL_RECT(r, p) handler->fillRect(r, p)
+#define IMAGE_RECT(r, p) handler->imageRect(r, p)
+#define BPP 8
+#include <rfb/rreDecode.h>
+#undef BPP
+#define BPP 16
+#include <rfb/rreDecode.h>
+#undef BPP
+#define BPP 32
+#include <rfb/rreDecode.h>
+#undef BPP
+
+Decoder* RREDecoder::create(CMsgReader* reader)
+{
+  return new RREDecoder(reader);
+}
+
+RREDecoder::RREDecoder(CMsgReader* reader_) : reader(reader_)
+{
+}
+
+RREDecoder::~RREDecoder()
+{
+}
+
+void RREDecoder::readRect(const Rect& r, CMsgHandler* handler)
+{
+  rdr::InStream* is = reader->getInStream();
+  switch (reader->bpp()) {
+  case 8:  rreDecode8 (r, is, handler); break;
+  case 16: rreDecode16(r, is, handler); break;
+  case 32: rreDecode32(r, is, handler); break;
+  }
+}
diff --git a/rfb/RREDecoder.h b/rfb/RREDecoder.h
new file mode 100644
index 0000000..75a5e85
--- /dev/null
+++ b/rfb/RREDecoder.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_RREDECODER_H__
+#define __RFB_RREDECODER_H__
+
+#include <rfb/Decoder.h>
+
+namespace rfb {
+
+  class RREDecoder : public Decoder {
+  public:
+    static Decoder* create(CMsgReader* reader);
+    virtual void readRect(const Rect& r, CMsgHandler* handler);
+    virtual ~RREDecoder();
+  private:
+    RREDecoder(CMsgReader* reader);
+    CMsgReader* reader;
+  };
+}
+#endif
diff --git a/rfb/RREEncoder.cxx b/rfb/RREEncoder.cxx
new file mode 100644
index 0000000..612a869
--- /dev/null
+++ b/rfb/RREEncoder.cxx
@@ -0,0 +1,75 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <rdr/OutStream.h>
+#include <rfb/ImageGetter.h>
+#include <rfb/encodings.h>
+#include <rfb/SMsgWriter.h>
+#include <rfb/RREEncoder.h>
+
+using namespace rfb;
+
+#define BPP 8
+#include <rfb/rreEncode.h>
+#undef BPP
+#define BPP 16
+#include <rfb/rreEncode.h>
+#undef BPP
+#define BPP 32
+#include <rfb/rreEncode.h>
+#undef BPP
+
+Encoder* RREEncoder::create(SMsgWriter* writer)
+{
+  return new RREEncoder(writer);
+}
+
+RREEncoder::RREEncoder(SMsgWriter* writer_) : writer(writer_)
+{
+}
+
+RREEncoder::~RREEncoder()
+{
+}
+
+bool RREEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
+{
+  int w = r.width();
+  int h = r.height();
+  rdr::U8* imageBuf = writer->getImageBuf(w*h);
+  ig->getImage(imageBuf, r);
+
+  mos.clear();
+
+  int nSubrects = -1;
+  switch (writer->bpp()) {
+  case 8:  nSubrects = rreEncode8(imageBuf, w, h, &mos);  break;
+  case 16: nSubrects = rreEncode16(imageBuf, w, h, &mos); break;
+  case 32: nSubrects = rreEncode32(imageBuf, w, h, &mos); break;
+  }
+  
+  if (nSubrects < 0) {
+    return writer->writeRect(r, encodingRaw, ig, actual);
+  }
+
+  writer->startRect(r, encodingRRE);
+  rdr::OutStream* os = writer->getOutStream();
+  os->writeU32(nSubrects);
+  os->writeBytes(mos.data(), mos.length());
+  writer->endRect();
+  return true;
+}
diff --git a/rfb/RREEncoder.h b/rfb/RREEncoder.h
new file mode 100644
index 0000000..40b203e
--- /dev/null
+++ b/rfb/RREEncoder.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_RREENCODER_H__
+#define __RFB_RREENCODER_H__
+
+#include <rdr/MemOutStream.h>
+#include <rfb/Encoder.h>
+
+namespace rfb {
+
+  class RREEncoder : public Encoder {
+  public:
+    static Encoder* create(SMsgWriter* writer);
+    virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual);
+    virtual ~RREEncoder();
+  private:
+    RREEncoder(SMsgWriter* writer);
+    SMsgWriter* writer;
+    rdr::MemOutStream mos;
+  };
+}
+#endif
diff --git a/rfb/RawDecoder.cxx b/rfb/RawDecoder.cxx
new file mode 100644
index 0000000..5a5d62b
--- /dev/null
+++ b/rfb/RawDecoder.cxx
@@ -0,0 +1,55 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <rdr/InStream.h>
+#include <rfb/CMsgReader.h>
+#include <rfb/CMsgHandler.h>
+#include <rfb/RawDecoder.h>
+
+using namespace rfb;
+
+Decoder* RawDecoder::create(CMsgReader* reader)
+{
+  return new RawDecoder(reader);
+}
+
+RawDecoder::RawDecoder(CMsgReader* reader_) : reader(reader_)
+{
+}
+
+RawDecoder::~RawDecoder()
+{
+}
+
+void RawDecoder::readRect(const Rect& r, CMsgHandler* handler)
+{
+  int x = r.tl.x;
+  int y = r.tl.y;
+  int w = r.width();
+  int h = r.height();
+  int nPixels;
+  rdr::U8* imageBuf = reader->getImageBuf(w, w*h, &nPixels);
+  int bytesPerRow = w * (reader->bpp() / 8);
+  while (h > 0) {
+    int nRows = nPixels / w;
+    if (nRows > h) nRows = h;
+    reader->getInStream()->readBytes(imageBuf, nRows * bytesPerRow);
+    handler->imageRect(Rect(x, y, x+w, y+nRows), imageBuf);
+    h -= nRows;
+    y += nRows;
+  }
+}
diff --git a/rfb/RawDecoder.h b/rfb/RawDecoder.h
new file mode 100644
index 0000000..b3dd9b7
--- /dev/null
+++ b/rfb/RawDecoder.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_RAWDECODER_H__
+#define __RFB_RAWDECODER_H__
+
+#include <rfb/Decoder.h>
+
+namespace rfb {
+
+  class RawDecoder : public Decoder {
+  public:
+    static Decoder* create(CMsgReader* reader);
+    virtual void readRect(const Rect& r, CMsgHandler* handler);
+    virtual ~RawDecoder();
+  private:
+    RawDecoder(CMsgReader* reader);
+    CMsgReader* reader;
+  };
+}
+#endif
diff --git a/rfb/RawEncoder.cxx b/rfb/RawEncoder.cxx
new file mode 100644
index 0000000..d758ec6
--- /dev/null
+++ b/rfb/RawEncoder.cxx
@@ -0,0 +1,59 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <rdr/OutStream.h>
+#include <rfb/ImageGetter.h>
+#include <rfb/encodings.h>
+#include <rfb/SMsgWriter.h>
+#include <rfb/RawEncoder.h>
+
+using namespace rfb;
+
+Encoder* RawEncoder::create(SMsgWriter* writer)
+{
+  return new RawEncoder(writer);
+}
+
+RawEncoder::RawEncoder(SMsgWriter* writer_) : writer(writer_)
+{
+}
+
+RawEncoder::~RawEncoder()
+{
+}
+
+bool RawEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
+{
+  int x = r.tl.x;
+  int y = r.tl.y;
+  int w = r.width();
+  int h = r.height();
+  int nPixels;
+  rdr::U8* imageBuf = writer->getImageBuf(w, w*h, &nPixels);
+  int bytesPerRow = w * (writer->bpp() / 8);
+  writer->startRect(r, encodingRaw);
+  while (h > 0) {
+    int nRows = nPixels / w;
+    if (nRows > h) nRows = h;
+    ig->getImage(imageBuf, Rect(x, y, x+w, y+nRows));
+    writer->getOutStream()->writeBytes(imageBuf, nRows * bytesPerRow);
+    h -= nRows;
+    y += nRows;
+  }
+  writer->endRect();
+  return true;
+}
diff --git a/rfb/RawEncoder.h b/rfb/RawEncoder.h
new file mode 100644
index 0000000..c8b6a62
--- /dev/null
+++ b/rfb/RawEncoder.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_RAWENCODER_H__
+#define __RFB_RAWENCODER_H__
+
+#include <rfb/Encoder.h>
+
+namespace rfb {
+
+  class RawEncoder : public Encoder {
+  public:
+    static Encoder* create(SMsgWriter* writer);
+    virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual);
+    virtual ~RawEncoder();
+  private:
+    RawEncoder(SMsgWriter* writer);
+    SMsgWriter* writer;
+  };
+}
+#endif
diff --git a/rfb/Rect.h b/rfb/Rect.h
new file mode 100644
index 0000000..ee43e66
--- /dev/null
+++ b/rfb/Rect.h
@@ -0,0 +1,112 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// rfb::Rect and rfb::Point structures
+
+#ifndef __RFB_RECT_INCLUDED__
+#define __RFB_RECT_INCLUDED__
+
+#ifndef max
+#define max(a,b)            (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef min
+#define min(a,b)            (((a) < (b)) ? (a) : (b))
+#endif
+
+namespace rfb {
+
+  // rfb::Point
+  //
+  // Represents a point in 2D space, by X and Y coordinates.
+  // Can also be used to represent a delta, or offset, between
+  // two Points.
+  // Functions are provided to allow Points to be compared for
+  // equality and translated by a supplied offset.
+  // Functions are also provided to negate offset Points.
+
+  struct Point {
+    Point() : x(0), y(0) {}
+    Point(int x_, int y_) : x(x_), y(y_) {}
+    inline Point negate() const {return Point(-x, -y);}
+    inline bool equals(const Point &p) const {return x==p.x && y==p.y;}
+    inline Point translate(const Point &p) const {return Point(x+p.x, y+p.y);}
+    inline Point subtract(const Point &p) const {return Point(x-p.x, y-p.y);}
+    int x, y;
+  };
+
+  // rfb::Rect
+  //
+  // Represents a rectangular region defined by its top-left (tl)
+  // and bottom-right (br) Points.
+  // Rects may be compared for equality, checked to determine whether
+  // or not they are empty, cleared (made empty), or intersected with
+  // one another.  The bounding rectangle of two existing Rects
+  // may be calculated, as may the area of a Rect.
+  // Rects may also be translated, in the same way as Points, by
+  // an offset specified in a Point structure.
+
+  struct Rect {
+    Rect() {}
+    Rect(Point tl_, Point br_) : tl(tl_), br(br_) {}
+    Rect(int x1, int y1, int x2, int y2) : tl(x1, y1), br(x2, y2) {}
+    inline void setXYWH(int x, int y, int w, int h) {
+      tl.x = x; tl.y = y; br.x = x+w; br.y = y+h;
+    }
+    inline Rect intersect(const Rect &r) const {
+      Rect result;
+      result.tl.x = max(tl.x, r.tl.x);
+      result.tl.y = max(tl.y, r.tl.y);
+      result.br.x = max(min(br.x, r.br.x), result.tl.x);
+      result.br.y = max(min(br.y, r.br.y), result.tl.y);
+      return result;
+    }
+    inline Rect union_boundary(const Rect &r) const {
+      if (r.is_empty()) return *this;
+      if (is_empty()) return r;
+      Rect result;
+      result.tl.x = min(tl.x, r.tl.x);
+      result.tl.y = min(tl.y, r.tl.y);
+      result.br.x = max(br.x, r.br.x);
+      result.br.y = max(br.y, r.br.y);
+      return result;
+    }
+    inline Rect translate(const Point &p) const {
+      return Rect(tl.translate(p), br.translate(p));
+    }
+    inline bool equals(const Rect &r) const {return r.tl.equals(tl) && r.br.equals(br);}
+    inline bool is_empty() const {return (tl.x >= br.x) || (tl.y >= br.y);}
+    inline void clear() {tl = Point(); br = Point();}
+    inline bool enclosed_by(const Rect &r) const {
+      return (tl.x>=r.tl.x) && (tl.y>=r.tl.y) && (br.x<=r.br.x) && (br.y<=r.br.y);
+    }
+    inline bool overlaps(const Rect &r) const {
+      return tl.x < r.br.x && tl.y < r.br.y && br.x > r.tl.x && br.y > r.tl.y;
+    }
+    inline unsigned int area() const {return is_empty() ? 0 : (br.x-tl.x)*(br.y-tl.y);}
+    inline Point dimensions() const {return Point(width(), height());}
+    inline int width() const {return br.x-tl.x;}
+    inline int height() const {return br.y-tl.y;}
+    inline bool contains(const Point &p) const {
+      return (tl.x<=p.x) && (tl.y<=p.y) && (br.x>p.x) && (br.y>p.y);
+    }
+    Point tl;
+    Point br;
+  };
+}
+#endif // __RFB_RECT_INCLUDED__
diff --git a/rfb/Region.cxx b/rfb/Region.cxx
new file mode 100644
index 0000000..bbcc892
--- /dev/null
+++ b/rfb/Region.cxx
@@ -0,0 +1,248 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// Cross-platform Region class based on the X11 region implementation.  Note
+// that for efficiency this code manipulates the Xlib region structure
+// directly.  Apart from the layout of the structure, there is one other key
+// assumption made: a Region returned from XCreateRegion must always have its
+// rects member allocated so that there is space for at least one rectangle.
+//
+
+#include <rfb/Region.h>
+#include <Xregion/Xregion.h>
+#include <Xregion/region.h>
+#include <assert.h>
+#include <stdio.h>
+
+// A _RectRegion must never be passed as a return parameter to the Xlib region
+// operations.  This is because for efficiency its "rects" member has not been
+// allocated with Xmalloc.  It is however safe to pass it as an input
+// parameter.
+
+class _RectRegion {
+public:
+  _RectRegion(const rfb::Rect& r) {
+    region.rects = &region.extents;
+    region.numRects = 1;
+    region.extents.x1 = r.tl.x;
+    region.extents.y1 = r.tl.y;
+    region.extents.x2 = r.br.x;
+    region.extents.y2 = r.br.y;
+    region.size = 1;
+    if (r.is_empty())
+      region.numRects = 0;
+  }
+  REGION region;
+};
+
+
+rfb::Region::Region() {
+  xrgn = XCreateRegion();
+  assert(xrgn);
+}
+
+rfb::Region::Region(const Rect& r) {
+  xrgn = XCreateRegion();
+  assert(xrgn);
+  reset(r);
+}
+
+rfb::Region::Region(const rfb::Region& r) {
+  xrgn = XCreateRegion();
+  assert(xrgn);
+  XUnionRegion(xrgn, r.xrgn, xrgn);
+}
+
+rfb::Region::~Region() {
+  XDestroyRegion(xrgn);
+}
+
+rfb::Region& rfb::Region::operator=(const rfb::Region& r) {
+  clear();
+  XUnionRegion(xrgn, r.xrgn, xrgn);
+  return *this;
+}
+
+void rfb::Region::clear() {
+  xrgn->numRects = 0;
+  xrgn->extents.x1 = 0;
+  xrgn->extents.y1 = 0;
+  xrgn->extents.x2 = 0;
+  xrgn->extents.y2 = 0;
+}
+
+void rfb::Region::reset(const Rect& r) {
+  if (r.is_empty()) {
+    clear();
+  } else {
+    xrgn->numRects = 1;
+    xrgn->rects[0].x1 = xrgn->extents.x1 = r.tl.x;
+    xrgn->rects[0].y1 = xrgn->extents.y1 = r.tl.y;
+    xrgn->rects[0].x2 = xrgn->extents.x2 = r.br.x;
+    xrgn->rects[0].y2 = xrgn->extents.y2 = r.br.y;
+  }
+}
+
+void rfb::Region::translate(const Point& delta) {
+  XOffsetRegion(xrgn, delta.x, delta.y);
+}
+
+void rfb::Region::setOrderedRects(const std::vector<Rect>& rects) {
+  clear();
+  std::vector<Rect>::const_iterator i;
+  for (i=rects.begin(); i != rects.end(); i++) {
+    _RectRegion rr(*i);
+    XUnionRegion(xrgn, &rr.region, xrgn);
+  }
+}
+
+void rfb::Region::setExtentsAndOrderedRects(const ShortRect* extents,
+                                            int nRects, const ShortRect* rects)
+{
+  if (xrgn->size < nRects)
+  {
+    BOX* prevRects = xrgn->rects;
+    xrgn->rects = (BOX*)Xrealloc((char*)xrgn->rects, nRects * sizeof(BOX));
+    if (!xrgn->rects) {
+      fprintf(stderr,"Xrealloc failed\n");
+      Xfree(prevRects);
+      return;
+    }
+    xrgn->size = nRects;
+  }
+
+  xrgn->numRects = nRects;
+  xrgn->extents.x1 = extents->x1;
+  xrgn->extents.y1 = extents->y1;
+  xrgn->extents.x2 = extents->x2;
+  xrgn->extents.y2 = extents->y2;
+  for (int i = 0; i < nRects; i++) {
+    xrgn->rects[i].x1 = rects[i].x1;
+    xrgn->rects[i].y1 = rects[i].y1;
+    xrgn->rects[i].x2 = rects[i].x2;
+    xrgn->rects[i].y2 = rects[i].y2;
+  }
+}
+
+void rfb::Region::copyFrom(const rfb::Region& r) {
+  XUnionRegion(r.xrgn, r.xrgn, xrgn);
+}
+
+void rfb::Region::assign_intersect(const rfb::Region& r) {
+  XIntersectRegion(xrgn, r.xrgn, xrgn);
+}
+
+void rfb::Region::assign_union(const rfb::Region& r) {
+  XUnionRegion(xrgn, r.xrgn, xrgn);
+}
+
+void rfb::Region::assign_subtract(const rfb::Region& r) {
+  XSubtractRegion(xrgn, r.xrgn, xrgn);
+}
+
+rfb::Region rfb::Region::intersect(const rfb::Region& r) const {
+  rfb::Region ret;
+  XIntersectRegion(xrgn, r.xrgn, ret.xrgn);
+  return ret;
+}
+
+rfb::Region rfb::Region::union_(const rfb::Region& r) const {
+  rfb::Region ret;
+  XUnionRegion(xrgn, r.xrgn, ret.xrgn);
+  return ret;
+}
+
+rfb::Region rfb::Region::subtract(const rfb::Region& r) const {
+  rfb::Region ret;
+  XSubtractRegion(xrgn, r.xrgn, ret.xrgn);
+  return ret;
+}
+
+bool rfb::Region::equals(const rfb::Region& r) const {
+  return XEqualRegion(xrgn, r.xrgn);
+}
+
+int rfb::Region::numRects() const {
+  return xrgn->numRects;
+}
+
+bool rfb::Region::get_rects(std::vector<Rect>* rects,
+                            bool left2right, bool topdown, int maxArea) const
+{
+  int nRects = xrgn->numRects;
+  int xInc = left2right ? 1 : -1;
+  int yInc = topdown ? 1 : -1;
+  int i = topdown ? 0 : nRects-1;
+  rects->clear();
+  rects->reserve(nRects);
+
+  while (nRects > 0) {
+    int firstInNextBand = i;
+    int nRectsInBand = 0;
+
+    while (nRects > 0 && xrgn->rects[firstInNextBand].y1 == xrgn->rects[i].y1)
+    {
+      firstInNextBand += yInc;
+      nRects--;
+      nRectsInBand++;
+    }
+
+    if (xInc != yInc)
+      i = firstInNextBand - yInc;
+
+    while (nRectsInBand > 0) {
+      int y = xrgn->rects[i].y1;
+      int h = maxArea / (xrgn->rects[i].x2 - xrgn->rects[i].x1);
+      if (!h) h = xrgn->rects[i].y2 - y;
+      do {
+        if (h > xrgn->rects[i].y2 - y)
+          h = xrgn->rects[i].y2 - y;
+        Rect r(xrgn->rects[i].x1, y, xrgn->rects[i].x2, y+h);
+        rects->push_back(r);
+        y += h;
+      } while (y < xrgn->rects[i].y2);
+      i += xInc;
+      nRectsInBand--;
+    }
+
+    i = firstInNextBand;
+  }
+
+  return !rects->empty();
+}
+
+rfb::Rect rfb::Region::get_bounding_rect() const {
+  return Rect(xrgn->extents.x1, xrgn->extents.y1,
+              xrgn->extents.x2, xrgn->extents.y2);
+}
+
+
+void rfb::Region::debug_print(const char* prefix) const
+{
+  fprintf(stderr,"%s num rects %3ld extents %3d,%3d %3dx%3d\n",
+          prefix, xrgn->numRects, xrgn->extents.x1, xrgn->extents.y1,
+          xrgn->extents.x2-xrgn->extents.x1,
+          xrgn->extents.y2-xrgn->extents.y1);
+
+  for (int i = 0; i < xrgn->numRects; i++) {
+    fprintf(stderr,"    rect %3d,%3d %3dx%3d\n",
+            xrgn->rects[i].x1, xrgn->rects[i].y1,
+            xrgn->rects[i].x2-xrgn->rects[i].x1,
+            xrgn->rects[i].y2-xrgn->rects[i].y1);
+  }
+}
diff --git a/rfb/Region.h b/rfb/Region.h
new file mode 100644
index 0000000..8fb9889
--- /dev/null
+++ b/rfb/Region.h
@@ -0,0 +1,84 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// Cross-platform Region class based on the X11 region implementation
+
+#ifndef __RFB_REGION_INCLUDED__
+#define __RFB_REGION_INCLUDED__
+
+#include <rfb/Rect.h>
+#include <vector>
+
+struct _XRegion;
+
+namespace rfb {
+
+  struct ShortRect {
+    short x1, y1, x2, y2;
+  };
+
+  class Region {
+  public:
+    // Create an empty region
+    Region();
+    // Create a rectangular region
+    Region(const Rect& r);
+
+    Region(const Region& r);
+    Region &operator=(const Region& src);
+
+    ~Region();
+
+    // the following methods alter the region in place:
+
+    void clear();
+    void reset(const Rect& r);
+    void translate(const rfb::Point& delta);
+    void setOrderedRects(const std::vector<Rect>& rects);
+    void setExtentsAndOrderedRects(const ShortRect* extents, int nRects,
+                                   const ShortRect* rects);
+    void copyFrom(const Region& r);
+
+    void assign_intersect(const Region& r);
+    void assign_union(const Region& r);
+    void assign_subtract(const Region& r);
+
+    // the following three operations return a new region:
+
+    Region intersect(const Region& r) const;
+    Region union_(const Region& r) const;
+    Region subtract(const Region& r) const;
+
+    bool equals(const Region& b) const;
+    int numRects() const;
+    bool is_empty() const { return numRects() == 0; }
+
+    bool get_rects(std::vector<Rect>* rects, bool left2right=true,
+                   bool topdown=true, int maxArea=0) const;
+    Rect get_bounding_rect() const;
+
+    void debug_print(const char *prefix) const;
+
+  protected:
+
+    struct _XRegion* xrgn;
+  };
+
+};
+
+#endif // __RFB_REGION_INCLUDED__
diff --git a/rfb/SConnection.cxx b/rfb/SConnection.cxx
new file mode 100644
index 0000000..e969ed8
--- /dev/null
+++ b/rfb/SConnection.cxx
@@ -0,0 +1,330 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <rfb/Exception.h>
+#include <rfb/secTypes.h>
+#include <rfb/SMsgReaderV3.h>
+#include <rfb/SMsgWriterV3.h>
+#include <rfb/SSecurity.h>
+#include <rfb/SConnection.h>
+#include <rfb/ServerCore.h>
+
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+
+static LogWriter vlog("SConnection");
+
+// AccessRights values
+const SConnection::AccessRights SConnection::AccessView       = 0x0001;
+const SConnection::AccessRights SConnection::AccessKeyEvents  = 0x0002;
+const SConnection::AccessRights SConnection::AccessPtrEvents  = 0x0004;
+const SConnection::AccessRights SConnection::AccessCutText    = 0x0008;
+const SConnection::AccessRights SConnection::AccessDefault    = 0x03ff;
+const SConnection::AccessRights SConnection::AccessNoQuery    = 0x0400;
+const SConnection::AccessRights SConnection::AccessFull       = 0xffff;
+
+
+SConnection::SConnection()
+  : readyForSetColourMapEntries(false),
+    is(0), os(0), reader_(0), writer_(0),
+    nSecTypes(0), security(0), state_(RFBSTATE_UNINITIALISED)
+{
+  defaultMajorVersion = 3;
+  defaultMinorVersion = 8;
+  if (rfb::Server::protocol3_3)
+    defaultMinorVersion = 3;
+
+  cp.setVersion(defaultMajorVersion, defaultMinorVersion);
+}
+
+SConnection::~SConnection()
+{
+  if (security) security->destroy();
+  deleteReaderAndWriter();
+}
+
+void SConnection::deleteReaderAndWriter()
+{
+  delete reader_;
+  reader_ = 0;
+  delete writer_;
+  writer_ = 0;
+}
+
+void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
+{
+  is = is_;
+  os = os_;
+}
+
+void SConnection::addSecType(rdr::U8 secType)
+{
+  if (nSecTypes == maxSecTypes)
+    throw Exception("too many security types");
+  secTypes[nSecTypes++] = secType;
+  vlog.debug("Offering security type %s(%d)",
+             secTypeName(secType),secType);
+}
+
+void SConnection::initialiseProtocol()
+{
+  cp.writeVersion(os);
+  state_ = RFBSTATE_PROTOCOL_VERSION;
+}
+
+void SConnection::processMsg()
+{
+  switch (state_) {
+  case RFBSTATE_PROTOCOL_VERSION: processVersionMsg();      break;
+  case RFBSTATE_SECURITY_TYPE:    processSecurityTypeMsg(); break;
+  case RFBSTATE_SECURITY:         processSecurityMsg();     break;
+  case RFBSTATE_INITIALISATION:   processInitMsg();         break;
+  case RFBSTATE_NORMAL:           reader_->readMsg();       break;
+  case RFBSTATE_QUERYING:
+    throw Exception("SConnection::processMsg: bogus data from client while "
+                    "querying");
+  case RFBSTATE_UNINITIALISED:
+    throw Exception("SConnection::processMsg: not initialised yet?");
+  default:
+    throw Exception("SConnection::processMsg: invalid state");
+  }
+}
+
+void SConnection::processVersionMsg()
+{
+  vlog.debug("reading protocol version");
+  bool done;
+  if (!cp.readVersion(is, &done)) {
+    state_ = RFBSTATE_INVALID;
+    throw Exception("reading version failed: not an RFB client?");
+  }
+  if (!done) return;
+
+  vlog.info("Client needs protocol version %d.%d",
+            cp.majorVersion, cp.minorVersion);
+
+  if (cp.majorVersion != 3) {
+    // unknown protocol version
+    char msg[256];
+    sprintf(msg,"Error: client needs protocol version %d.%d, server has %d.%d",
+            cp.majorVersion, cp.minorVersion,
+            defaultMajorVersion, defaultMinorVersion);
+    throwConnFailedException(msg);
+  }
+
+  if (cp.minorVersion != 3 && cp.minorVersion != 7 && cp.minorVersion != 8) {
+    vlog.error("Client uses unofficial protocol version %d.%d",
+               cp.majorVersion,cp.minorVersion);
+    if (cp.minorVersion >= 8)
+      cp.minorVersion = 8;
+    else if (cp.minorVersion == 7)
+      cp.minorVersion = 7;
+    else
+      cp.minorVersion = 3;
+    vlog.error("Assuming compatibility with version %d.%d",
+               cp.majorVersion,cp.minorVersion);
+  }
+
+  versionReceived();
+
+  if (cp.isVersion(3,3)) {
+
+    // cope with legacy 3.3 client only if "no authentication" or "vnc
+    // authentication" is supported.
+
+    int i;
+    for (i = 0; i < nSecTypes; i++) {
+      if (secTypes[i] == secTypeNone || secTypes[i] == secTypeVncAuth) break;
+    }
+    if (i == nSecTypes) {
+      char msg[256];
+      sprintf(msg,"No supported security type for %d.%d client",
+              cp.majorVersion, cp.minorVersion);
+      throwConnFailedException(msg);
+    }
+
+    os->writeU32(secTypes[i]);
+    if (secTypes[i] == secTypeNone) os->flush();
+    state_ = RFBSTATE_SECURITY;
+    security = getSSecurity(secTypes[i]);
+    processSecurityMsg();
+    return;
+  }
+
+  // list supported security types for >=3.7 clients
+
+  if (nSecTypes == 0)
+    throwConnFailedException("No supported security types");
+
+  os->writeU8(nSecTypes);
+  os->writeBytes(secTypes, nSecTypes);
+  os->flush();
+  state_ = RFBSTATE_SECURITY_TYPE;
+}
+
+
+void SConnection::processSecurityTypeMsg()
+{
+  vlog.debug("processing security type message");
+  int secType = is->readU8();
+  vlog.info("Client requests security type %s(%d)",
+            secTypeName(secType),secType);
+  int i;
+  for (i = 0; i < nSecTypes; i++) {
+    if (secType == secTypes[i]) break;
+  }
+  if (i == nSecTypes) {
+    char msg[256];
+    sprintf(msg,"Security type %s(%d) from client not supported",
+            secTypeName(secType),secType);
+    throwConnFailedException(msg);
+  }
+  state_ = RFBSTATE_SECURITY;
+  security = getSSecurity(secType);
+  processSecurityMsg();
+}
+
+void SConnection::processSecurityMsg()
+{
+  vlog.debug("processing security message");
+  bool done;
+  bool ok = security->processMsg(this, &done);
+  if (done) {
+    state_ = RFBSTATE_QUERYING;
+    if (ok) {
+      queryConnection(security->getUserName());
+    } else {
+      const char* failureMsg = security->failureMessage();
+      if (!failureMsg) failureMsg = "Authentication failure";
+      approveConnection(false, failureMsg);
+    }
+  }
+  if (!ok) {
+    state_ = RFBSTATE_INVALID;
+    authFailure();
+    throw AuthFailureException();
+  }
+}
+
+void SConnection::processInitMsg()
+{
+  vlog.debug("reading client initialisation");
+  reader_->readClientInit();
+}
+
+void SConnection::throwConnFailedException(const char* msg)
+{
+  vlog.info(msg);
+  if (state_ == RFBSTATE_PROTOCOL_VERSION) {
+    if (cp.majorVersion == 3 && cp.minorVersion == 3) {
+      os->writeU32(0);
+      os->writeString(msg);
+      os->flush();
+    } else {
+      os->writeU8(0);
+      os->writeString(msg);
+      os->flush();
+    }
+  }
+  state_ = RFBSTATE_INVALID;
+  throw ConnFailedException(msg);
+}
+
+void SConnection::writeConnFailedFromScratch(const char* msg,
+                                             rdr::OutStream* os)
+{
+  os->writeBytes("RFB 003.003\n", 12);
+  os->writeU32(0);
+  os->writeString(msg);
+  os->flush();
+}
+
+void SConnection::versionReceived()
+{
+}
+
+void SConnection::authSuccess()
+{
+}
+
+void SConnection::authFailure()
+{
+}
+
+void SConnection::queryConnection(const char* userName)
+{
+  approveConnection(true);
+}
+
+void SConnection::approveConnection(bool accept, const char* reason)
+{
+  if (state_ != RFBSTATE_QUERYING)
+    throw Exception("SConnection::approveConnection: invalid state");
+
+  if (!reason) reason = "Authentication failure";
+
+  if (!cp.beforeVersion(3,8) || security->getType() != secTypeNone) {
+    if (accept) {
+      os->writeU32(secResultOK);
+    } else {
+      os->writeU32(secResultFailed);
+      if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
+        os->writeString(reason);
+    }
+    os->flush();
+  }
+
+  if (accept) {
+    state_ = RFBSTATE_INITIALISATION;
+    reader_ = new SMsgReaderV3(this, is);
+    writer_ = new SMsgWriterV3(&cp, os);
+    authSuccess();
+  } else {
+    state_ = RFBSTATE_INVALID;
+    authFailure();
+    throw AuthFailureException(reason);
+  }
+}
+
+void SConnection::setInitialColourMap()
+{
+}
+
+void SConnection::clientInit(bool shared)
+{
+  writer_->writeServerInit();
+  state_ = RFBSTATE_NORMAL;
+}
+
+void SConnection::setPixelFormat(const PixelFormat& pf)
+{
+  SMsgHandler::setPixelFormat(pf);
+  readyForSetColourMapEntries = true;
+}
+
+void SConnection::framebufferUpdateRequest(const Rect& r, bool incremental)
+{
+  if (!readyForSetColourMapEntries) {
+    readyForSetColourMapEntries = true;
+    if (!cp.pf().trueColour) {
+      setInitialColourMap();
+    }
+  }
+}
diff --git a/rfb/SConnection.h b/rfb/SConnection.h
new file mode 100644
index 0000000..19453c6
--- /dev/null
+++ b/rfb/SConnection.h
@@ -0,0 +1,207 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// SConnection - class on the server side representing a connection to a
+// client.  A derived class should override methods appropriately.
+//
+
+#ifndef __RFB_SCONNECTION_H__
+#define __RFB_SCONNECTION_H__
+
+#include <rdr/InStream.h>
+#include <rdr/OutStream.h>
+#include <rfb/SMsgHandler.h>
+
+namespace rfb {
+
+  class SMsgReader;
+  class SMsgWriter;
+  class SSecurity;
+
+  class SConnection : public SMsgHandler {
+  public:
+
+    SConnection();
+    virtual ~SConnection();
+
+    // Methods to initialise the connection
+
+    // setStreams() sets the streams to be used for the connection.  These must
+    // be set before initialiseProtocol() and processMsg() are called.  The
+    // SSecurity object may call setStreams() again to provide alternative
+    // streams over which the RFB protocol is sent (i.e. encrypting/decrypting
+    // streams).  Ownership of the streams remains with the caller
+    // (i.e. SConnection will not delete them).
+    void setStreams(rdr::InStream* is, rdr::OutStream* os);
+
+    // addSecType() should be called once for each security type which the
+    // server supports to this client.
+    void addSecType(rdr::U8 secType);
+
+    // initialiseProtocol() should be called once the streams and security
+    // types are set.  Subsequently, processMsg() should be called whenever
+    // there is data to read on the InStream.
+    void initialiseProtocol();
+
+    // processMsg() should be called whenever there is data to read on the
+    // InStream.  You must have called initialiseProtocol() first.
+    void processMsg();
+
+    // approveConnection() is called to either accept or reject the connection.
+    // If accept is false, the reason string gives the reason for the
+    // rejection.  It can either be called directly from queryConnection() or
+    // later, after queryConnection() has returned.  It can only be called when
+    // in state RFBSTATE_QUERYING.  On rejection, an AuthFailureException is
+    // thrown, so this must be handled appropriately by the caller.
+    void approveConnection(bool accept, const char* reason=0);
+
+
+    // Methods to be overridden in a derived class
+
+    // versionReceived() indicates that the version number has just been read
+    // from the client.  The version will already have been "cooked"
+    // to deal with unknown/bogus viewer protocol numbers.
+    virtual void versionReceived();
+
+    // getSSecurity() gets the SSecurity object for the given type.  The type
+    // is guaranteed to be one of the secTypes passed in to addSecType().  The
+    // SSecurity object's destroy() method will be called by the SConnection
+    // from its destructor.
+    virtual SSecurity* getSSecurity(int secType)=0;
+
+    // authSuccess() is called when authentication has succeeded.
+    virtual void authSuccess();
+
+    // authFailure() is called when authentication has failed.  This method is
+    // not normally overridden since an exception is thrown anyway.
+    virtual void authFailure();
+
+    // queryConnection() is called when authentication has succeeded, but
+    // before informing the client.  It can be overridden to query a local user
+    // to accept the incoming connection, for example.  The userName argument
+    // is the name of the user making the connection, or null (note that the
+    // storage for userName is owned by the caller).  The connection must be
+    // accepted or rejected by calling approveConnection(), either directly
+    // from queryConnection() or some time later.
+    virtual void queryConnection(const char* userName);
+
+    // clientInit() is called when the ClientInit message is received.  The
+    // derived class must call on to SConnection::clientInit().
+    virtual void clientInit(bool shared);
+
+    // setPixelFormat() is called when a SetPixelFormat message is received.
+    // The derived class must call on to SConnection::setPixelFormat().
+    virtual void setPixelFormat(const PixelFormat& pf);
+
+    // framebufferUpdateRequest() is called when a FramebufferUpdateRequest
+    // message is received.  The derived class must call on to
+    // SConnection::framebufferUpdateRequest().
+    virtual void framebufferUpdateRequest(const Rect& r, bool incremental);
+
+    // setInitialColourMap() is called when the client needs an initial
+    // SetColourMapEntries message.  In fact this only happens when the client
+    // accepts the server's default pixel format and it uses a colour map.
+    virtual void setInitialColourMap();
+
+    // setAccessRights() allows a security package to limit the access rights
+    // of a VNCSConnectionST to the server.  How the access rights are treated
+    // is up to the derived class.
+
+    typedef rdr::U16 AccessRights;
+    static const AccessRights AccessView;         // View display contents
+    static const AccessRights AccessKeyEvents;    // Send key events
+    static const AccessRights AccessPtrEvents;    // Send pointer events
+    static const AccessRights AccessCutText;      // Send/receive clipboard events
+    static const AccessRights AccessDefault;      // The default rights, INCLUDING FUTURE ONES
+    static const AccessRights AccessNoQuery;      // Connect without local user accepting
+    static const AccessRights AccessFull;         // All of the available AND FUTURE rights
+    virtual void setAccessRights(AccessRights ar) = 0;
+
+    // Other methods
+
+    // authenticated() returns true if the client has authenticated
+    // successfully.
+    bool authenticated() { return (state_ == RFBSTATE_INITIALISATION ||
+                                   state_ == RFBSTATE_NORMAL); }
+
+    // deleteReaderAndWriter() deletes the reader and writer associated with
+    // this connection.  This may be useful if you want to delete the streams
+    // before deleting the SConnection to make sure that no attempt by the
+    // SConnection is made to read or write.
+    // XXX Do we really need this at all???
+    void deleteReaderAndWriter();
+
+    // throwConnFailedException() prints a message to the log, sends a conn
+    // failed message to the client (if possible) and throws a
+    // ConnFailedException.
+    void throwConnFailedException(const char* msg);
+
+    // writeConnFailedFromScratch() sends a conn failed message to an OutStream
+    // without the need to negotiate the protocol version first.  It actually
+    // does this by assuming that the client will understand version 3.3 of the
+    // protocol.
+    static void writeConnFailedFromScratch(const char* msg,
+                                           rdr::OutStream* os);
+
+    SMsgReader* reader() { return reader_; }
+    SMsgWriter* writer() { return writer_; }
+
+    rdr::InStream* getInStream() { return is; }
+    rdr::OutStream* getOutStream() { return os; }
+
+    enum stateEnum {
+      RFBSTATE_UNINITIALISED,
+      RFBSTATE_PROTOCOL_VERSION,
+      RFBSTATE_SECURITY_TYPE,
+      RFBSTATE_SECURITY,
+      RFBSTATE_QUERYING,
+      RFBSTATE_INITIALISATION,
+      RFBSTATE_NORMAL,
+      RFBSTATE_CLOSING,
+      RFBSTATE_INVALID
+    };
+
+    stateEnum state() { return state_; }
+
+    // ssecurity() returns a pointer to this connection's SSecurity object, if any
+    const SSecurity* ssecurity() const { return security; }
+
+  protected:
+    void setState(stateEnum s) { state_ = s; }
+
+    bool readyForSetColourMapEntries;
+
+  private:
+    void processVersionMsg();
+    void processSecurityTypeMsg();
+    void processSecurityMsg();
+    void processInitMsg();
+
+    int defaultMajorVersion, defaultMinorVersion;
+    rdr::InStream* is;
+    rdr::OutStream* os;
+    SMsgReader* reader_;
+    SMsgWriter* writer_;
+    enum { maxSecTypes = 8 };
+    int nSecTypes;
+    rdr::U8 secTypes[maxSecTypes];
+    SSecurity* security;
+    stateEnum state_;
+  };
+}
+#endif
diff --git a/rfb/SDesktop.h b/rfb/SDesktop.h
new file mode 100644
index 0000000..eb17a52
--- /dev/null
+++ b/rfb/SDesktop.h
@@ -0,0 +1,123 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+/////////////////////////////////////////////////////////////////////////////
+
+// SDesktop is an interface implemented by back-ends, on which callbacks are
+// made by the VNCServer as appropriate for pointer and keyboard events, etc.
+// SDesktop objects are always created before the VNCServer - the SDesktop
+// will be passed a pointer to the VNCServer in the start() call.  If a more
+// implementation-specific pointer to the VNCServer is required then this
+// can be provided to the SDesktop via an implementation-specific method.
+//
+// An SDesktop usually has an associated PixelBuffer which it tells the
+// VNCServer via the VNCServer's setPixelBuffer() method.  It can do this at
+// any time, but the PixelBuffer MUST be valid by the time the call to start()
+// returns.  The PixelBuffer may be set to null again if desired when stop() is
+// called.  Note that start() and stop() are guaranteed to be called
+// alternately; there should never be two calls to start() without an
+// intervening stop() and vice-versa.
+//
+
+#ifndef __RFB_SDESKTOP_H__
+#define __RFB_SDESKTOP_H__
+
+#include <rfb/PixelBuffer.h>
+#include <rfb/VNCServer.h>
+#include <rfb/Exception.h>
+
+namespace rfb {
+
+  class VNCServer;
+
+  class SDesktop {
+  public:
+    // start() is called by the server when the first client authenticates
+    // successfully, and can be used to begin any expensive tasks which are not
+    // needed when there are no clients.  A valid PixelBuffer must have been
+    // set via the VNCServer's setPixelBuffer() method by the time this call
+    // returns.
+
+    virtual void start(VNCServer* vs) {}
+
+    // stop() is called by the server when there are no longer any
+    // authenticated clients, and therefore the desktop can cease any
+    // expensive tasks.  No further calls to the VNCServer passed to start()
+    // can be made once stop has returned.
+
+    virtual void stop() {}
+
+    // pointerEvent(), keyEvent() and clientCutText() are called in response to
+    // the relevant RFB protocol messages from clients.
+
+    virtual void pointerEvent(const Point& pos, rdr::U8 buttonmask) {}
+    virtual void keyEvent(rdr::U32 key, bool down) {}
+    virtual void clientCutText(const char* str, int len) {}
+
+    // framebufferUpdateRequest() is called to let the desktop know that at
+    // least one client has become ready for an update.  Desktops can check
+    // whether there are clients ready at any time by calling the VNCServer's
+    // clientsReadyForUpdate() method.
+
+    virtual void framebufferUpdateRequest() {}
+
+    // getFbSize() returns the current dimensions of the framebuffer.
+    // This can be called even while the SDesktop is not start()ed.
+
+    virtual Point getFbSize() = 0;
+
+  protected:
+    virtual ~SDesktop() {}
+  };
+
+  // -=- SStaticDesktop
+  //     Trivial implementation of the SDesktop interface, which provides
+  //     dummy input handlers and event processing routine, and exports
+  //     a plain black desktop of the specified format.
+  class SStaticDesktop : public SDesktop {
+  public:
+    SStaticDesktop(const Point& size) : server(0), buffer(0) {
+      PixelFormat pf;
+      buffer = new ManagedPixelBuffer(pf, size.x, size.y);
+      if (buffer) memset(buffer->data, 0, (pf.bpp/8) * (size.x*size.y));
+    }
+    SStaticDesktop(const Point& size, const PixelFormat& pf) : buffer(0) {
+      buffer = new ManagedPixelBuffer(pf, size.x, size.y);
+      if (buffer) memset(buffer->data, 0, (pf.bpp/8) * (size.x*size.y));
+    }
+    virtual ~SStaticDesktop() {
+      if (buffer) delete buffer;
+    }
+
+    virtual void start(VNCServer* vs) {
+      server = vs;
+      server->setPixelBuffer(buffer);
+    }
+    virtual void stop() {
+      server->setPixelBuffer(0);
+      server = 0;
+    }
+
+  protected:
+    VNCServer* server;
+    ManagedPixelBuffer* buffer;
+  };
+
+};
+
+#endif // __RFB_SDESKTOP_H__
diff --git a/rfb/SMsgHandler.cxx b/rfb/SMsgHandler.cxx
new file mode 100644
index 0000000..d6a139c
--- /dev/null
+++ b/rfb/SMsgHandler.cxx
@@ -0,0 +1,64 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <rfb/Exception.h>
+#include <rfb/SMsgHandler.h>
+
+using namespace rfb;
+
+SMsgHandler::SMsgHandler()
+{
+}
+
+SMsgHandler::~SMsgHandler()
+{
+}
+
+void SMsgHandler::clientInit(bool shared)
+{
+}
+
+void SMsgHandler::setPixelFormat(const PixelFormat& pf)
+{
+  cp.setPF(pf);
+}
+
+void SMsgHandler::setEncodings(int nEncodings, rdr::U32* encodings)
+{
+  cp.setEncodings(nEncodings, encodings);
+  supportsLocalCursor();
+}
+
+void SMsgHandler::framebufferUpdateRequest(const Rect& r, bool incremental)
+{
+}
+
+void SMsgHandler::keyEvent(rdr::U32 key, bool down)
+{
+}
+
+void SMsgHandler::pointerEvent(int x, int y, int buttonMask)
+{
+}
+
+void SMsgHandler::clientCutText(const char* str, int len)
+{
+}
+
+void SMsgHandler::supportsLocalCursor()
+{
+}
diff --git a/rfb/SMsgHandler.h b/rfb/SMsgHandler.h
new file mode 100644
index 0000000..f326ad4
--- /dev/null
+++ b/rfb/SMsgHandler.h
@@ -0,0 +1,62 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// SMsgHandler - class to handle incoming messages on the server side.
+//
+
+#ifndef __RFB_SMSGHANDLER_H__
+#define __RFB_SMSGHANDLER_H__
+
+#include <rdr/types.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/ConnParams.h>
+#include <rfb/Rect.h>
+
+namespace rdr { class InStream; }
+
+namespace rfb {
+
+  class SMsgHandler {
+  public:
+    SMsgHandler();
+    virtual ~SMsgHandler();
+
+    // The following methods are called as corresponding messages are read.  A
+    // derived class should override these methods as desired.  Note that for
+    // the setPixelFormat() and setEncodings() methods, a derived class must
+    // call on to SMsgHandler's methods.
+
+    virtual void clientInit(bool shared);
+
+    virtual void setPixelFormat(const PixelFormat& pf);
+    virtual void setEncodings(int nEncodings, rdr::U32* encodings);
+    virtual void framebufferUpdateRequest(const Rect& r, bool incremental);
+    virtual void keyEvent(rdr::U32 key, bool down);
+    virtual void pointerEvent(int x, int y, int buttonMask);
+    virtual void clientCutText(const char* str, int len);
+
+    // supportsLocalCursor() is called whenever the status of
+    // cp.supportsLocalCursor has changed.  At the moment this happens on a
+    // setEncodings message, but in the future this may be due to a message
+    // specially for this purpose.
+    virtual void supportsLocalCursor();
+
+    ConnParams cp;
+  };
+}
+#endif
diff --git a/rfb/SMsgReader.cxx b/rfb/SMsgReader.cxx
new file mode 100644
index 0000000..2939aa1
--- /dev/null
+++ b/rfb/SMsgReader.cxx
@@ -0,0 +1,105 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <stdio.h>
+#include <rdr/InStream.h>
+#include <rfb/Exception.h>
+#include <rfb/util.h>
+#include <rfb/SMsgHandler.h>
+#include <rfb/SMsgReader.h>
+
+using namespace rfb;
+
+SMsgReader::SMsgReader(SMsgHandler* handler_, rdr::InStream* is_)
+  : handler(handler_), is(is_)
+{
+}
+
+SMsgReader::~SMsgReader()
+{
+}
+
+void SMsgReader::endMsg()
+{
+}
+
+void SMsgReader::readSetPixelFormat()
+{
+  is->skip(3);
+  PixelFormat pf;
+  pf.read(is);
+  endMsg();
+  handler->setPixelFormat(pf);
+}
+
+void SMsgReader::readSetEncodings()
+{
+  is->skip(1);
+  int nEncodings = is->readU16();
+  rdr::U32* encodings = new rdr::U32[nEncodings];
+  for (int i = 0; i < nEncodings; i++)
+    encodings[i] = is->readU32();
+  endMsg();
+  handler->setEncodings(nEncodings, encodings);
+  delete [] encodings;
+}
+
+void SMsgReader::readFramebufferUpdateRequest()
+{
+  bool inc = is->readU8();
+  int x = is->readU16();
+  int y = is->readU16();
+  int w = is->readU16();
+  int h = is->readU16();
+  endMsg();
+  handler->framebufferUpdateRequest(Rect(x, y, x+w, y+h), inc);
+}
+
+void SMsgReader::readKeyEvent()
+{
+  bool down = is->readU8();
+  is->skip(2);
+  rdr::U32 key = is->readU32();
+  endMsg();
+  handler->keyEvent(key, down);
+}
+
+void SMsgReader::readPointerEvent()
+{
+  int mask = is->readU8();
+  int x = is->readU16();
+  int y = is->readU16();
+  endMsg();
+  handler->pointerEvent(x, y, mask);
+}
+
+
+void SMsgReader::readClientCutText()
+{
+  is->skip(3);
+  int len = is->readU32();
+  if (len > 256*1024) {
+    is->skip(len);
+    fprintf(stderr,"cut text too long (%d bytes) - ignoring\n",len);
+    return;
+  }
+  CharArray ca(len+1);
+  ca.buf[len] = 0;
+  is->readBytes(ca.buf, len);
+  endMsg();
+  handler->clientCutText(ca.buf, len);
+}
diff --git a/rfb/SMsgReader.h b/rfb/SMsgReader.h
new file mode 100644
index 0000000..4d26938
--- /dev/null
+++ b/rfb/SMsgReader.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// SMsgReader - class for reading RFB messages on the server side
+// (i.e. messages from client to server).
+//
+
+#ifndef __RFB_SMSGREADER_H__
+#define __RFB_SMSGREADER_H__
+
+namespace rdr { class InStream; }
+
+namespace rfb {
+  class SMsgHandler;
+
+  class SMsgReader {
+  public:
+    virtual ~SMsgReader();
+
+    virtual void readClientInit()=0;
+
+    // readMsg() reads a message, calling the handler as appropriate.
+    virtual void readMsg()=0;
+
+    rdr::InStream* getInStream() { return is; }
+
+  protected:
+    virtual void readSetPixelFormat();
+    virtual void readSetEncodings();
+    virtual void readFramebufferUpdateRequest();
+    virtual void readKeyEvent();
+    virtual void readPointerEvent();
+    virtual void readClientCutText();
+    virtual void endMsg();
+
+    SMsgReader(SMsgHandler* handler, rdr::InStream* is);
+
+    SMsgHandler* handler;
+    rdr::InStream* is;
+  };
+}
+#endif
diff --git a/rfb/SMsgReaderV3.cxx b/rfb/SMsgReaderV3.cxx
new file mode 100644
index 0000000..e5ae744
--- /dev/null
+++ b/rfb/SMsgReaderV3.cxx
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <rfb/PixelFormat.h>
+#include <rfb/msgTypes.h>
+#include <rfb/Exception.h>
+#include <rdr/InStream.h>
+#include <rfb/SMsgReaderV3.h>
+#include <rfb/SMsgHandler.h>
+
+using namespace rfb;
+
+SMsgReaderV3::SMsgReaderV3(SMsgHandler* handler, rdr::InStream* is)
+  : SMsgReader(handler, is)
+{
+}
+
+SMsgReaderV3::~SMsgReaderV3()
+{
+}
+
+void SMsgReaderV3::readClientInit()
+{
+  bool shared = is->readU8();
+  endMsg();
+  handler->clientInit(shared);
+}
+
+void SMsgReaderV3::readMsg()
+{
+  int msgType = is->readU8();
+  switch (msgType) {
+  case msgTypeSetPixelFormat:           readSetPixelFormat(); break;
+  case msgTypeSetEncodings:             readSetEncodings(); break;
+  case msgTypeFramebufferUpdateRequest: readFramebufferUpdateRequest(); break;
+  case msgTypeKeyEvent:                 readKeyEvent(); break;
+  case msgTypePointerEvent:             readPointerEvent(); break;
+  case msgTypeClientCutText:            readClientCutText(); break;
+  default:
+    fprintf(stderr, "unknown message type %d\n", msgType);
+    throw Exception("unknown message type");
+  }
+}
diff --git a/rfb/SMsgReaderV3.h b/rfb/SMsgReaderV3.h
new file mode 100644
index 0000000..28cc7a6
--- /dev/null
+++ b/rfb/SMsgReaderV3.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_SMSGREADERV3_H__
+#define __RFB_SMSGREADERV3_H__
+
+#include <rfb/SMsgReader.h>
+
+namespace rfb {
+  class SMsgReaderV3 : public SMsgReader {
+  public:
+    SMsgReaderV3(SMsgHandler* handler, rdr::InStream* is);
+    virtual ~SMsgReaderV3();
+    virtual void readClientInit();
+    virtual void readMsg();
+  };
+}
+#endif
diff --git a/rfb/SMsgWriter.cxx b/rfb/SMsgWriter.cxx
new file mode 100644
index 0000000..ac74382
--- /dev/null
+++ b/rfb/SMsgWriter.cxx
@@ -0,0 +1,180 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <stdio.h>
+#include <assert.h>
+#include <rdr/OutStream.h>
+#include <rfb/msgTypes.h>
+#include <rfb/ColourMap.h>
+#include <rfb/ConnParams.h>
+#include <rfb/UpdateTracker.h>
+#include <rfb/SMsgWriter.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+
+static LogWriter vlog("SMsgWriter");
+
+SMsgWriter::SMsgWriter(ConnParams* cp_, rdr::OutStream* os_)
+  : imageBufIdealSize(0), cp(cp_), os(os_), lenBeforeRect(0),
+    currentEncoding(0), updatesSent(0), rawBytesEquivalent(0),
+    imageBuf(0), imageBufSize(0)
+{
+  for (unsigned int i = 0; i <= encodingMax; i++) {
+    encoders[i] = 0;
+    bytesSent[i] = 0;
+    rectsSent[i] = 0;
+  }
+}
+
+SMsgWriter::~SMsgWriter()
+{
+  vlog.info("framebuffer updates %d",updatesSent);
+  int bytes = 0;
+  for (unsigned int i = 0; i <= encodingMax; i++) {
+    delete encoders[i];
+    if (i != encodingCopyRect)
+      bytes += bytesSent[i];
+    if (rectsSent[i])
+      vlog.info("  %s rects %d, bytes %d",
+                encodingName(i), rectsSent[i], bytesSent[i]);
+  }
+  vlog.info("  raw bytes equivalent %d, compression ratio %f",
+          rawBytesEquivalent, (double)rawBytesEquivalent / bytes);
+  delete [] imageBuf;
+}
+
+void SMsgWriter::writeSetColourMapEntries(int firstColour, int nColours,
+                                          ColourMap* cm)
+{
+  startMsg(msgTypeSetColourMapEntries);
+  os->pad(1);
+  os->writeU16(firstColour);
+  os->writeU16(nColours);
+  for (int i = firstColour; i < firstColour+nColours; i++) {
+    int r, g, b;
+    cm->lookup(i, &r, &g, &b);
+    os->writeU16(r);
+    os->writeU16(g);
+    os->writeU16(b);
+  }
+  endMsg();
+}
+
+void SMsgWriter::writeBell()
+{
+  startMsg(msgTypeBell);
+  endMsg();
+}
+
+void SMsgWriter::writeServerCutText(const char* str, int len)
+{
+  startMsg(msgTypeServerCutText);
+  os->pad(3);
+  os->writeU32(len);
+  os->writeBytes(str, len);
+  endMsg();
+}
+
+void SMsgWriter::writeFramebufferUpdate(const UpdateInfo& ui, ImageGetter* ig,
+                                        Region* updatedRegion)
+{
+  writeFramebufferUpdateStart(ui.numRects());
+  writeRects(ui, ig, updatedRegion);
+  writeFramebufferUpdateEnd();
+}
+
+void SMsgWriter::writeRects(const UpdateInfo& ui, ImageGetter* ig,
+                            Region* updatedRegion)
+{
+  std::vector<Rect> rects;
+  std::vector<Rect>::const_iterator i;
+  updatedRegion->copyFrom(ui.changed);
+  updatedRegion->assign_union(ui.copied);
+
+  ui.copied.get_rects(&rects, ui.copy_delta.x <= 0, ui.copy_delta.y <= 0);
+  for (i = rects.begin(); i != rects.end(); i++)
+    writeCopyRect(*i, i->tl.x - ui.copy_delta.x, i->tl.y - ui.copy_delta.y);
+
+  ui.changed.get_rects(&rects);
+  for (i = rects.begin(); i != rects.end(); i++) {
+    Rect actual;
+    if (!writeRect(*i, ig, &actual)) {
+      updatedRegion->assign_subtract(*i);
+      updatedRegion->assign_union(actual);
+    }
+  }
+}
+
+
+bool SMsgWriter::needFakeUpdate()
+{
+  return false;
+}
+
+bool SMsgWriter::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
+{
+  return writeRect(r, cp->currentEncoding(), ig, actual);
+}
+
+bool SMsgWriter::writeRect(const Rect& r, unsigned int encoding,
+                           ImageGetter* ig, Rect* actual)
+{
+  if (!encoders[encoding]) {
+    encoders[encoding] = Encoder::createEncoder(encoding, this);
+    assert(encoders[encoding]);
+  }
+  return encoders[encoding]->writeRect(r, ig, actual);
+}
+
+void SMsgWriter::writeCopyRect(const Rect& r, int srcX, int srcY)
+{
+  startRect(r,encodingCopyRect);
+  os->writeU16(srcX);
+  os->writeU16(srcY);
+  endRect();
+}
+
+void SMsgWriter::setOutStream(rdr::OutStream* os_)
+{
+  os = os_;
+}
+
+rdr::U8* SMsgWriter::getImageBuf(int required, int requested, int* nPixels)
+{
+  int requiredBytes = required * (cp->pf().bpp / 8);
+  int requestedBytes = requested * (cp->pf().bpp / 8);
+  int size = requestedBytes;
+  if (size > imageBufIdealSize) size = imageBufIdealSize;
+
+  if (size < requiredBytes)
+    size = requiredBytes;
+
+  if (imageBufSize < size) {
+    imageBufSize = size;
+    delete [] imageBuf;
+    imageBuf = new rdr::U8[imageBufSize];
+  }
+  if (nPixels)
+    *nPixels = imageBufSize / (cp->pf().bpp / 8);
+  return imageBuf;
+}
+
+int SMsgWriter::bpp()
+{
+  return cp->pf().bpp;
+}
diff --git a/rfb/SMsgWriter.h b/rfb/SMsgWriter.h
new file mode 100644
index 0000000..6eba068
--- /dev/null
+++ b/rfb/SMsgWriter.h
@@ -0,0 +1,156 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// SMsgWriter - class for writing RFB messages on the server side.
+//
+
+#ifndef __RFB_SMSGWRITER_H__
+#define __RFB_SMSGWRITER_H__
+
+#include <rdr/types.h>
+#include <rfb/encodings.h>
+#include <rfb/Encoder.h>
+
+namespace rdr { class OutStream; }
+
+namespace rfb {
+
+  class PixelFormat;
+  class ConnParams;
+  class ImageGetter;
+  class ColourMap;
+  class Region;
+  class UpdateInfo;
+
+  class WriteSetCursorCallback {
+  public:
+    virtual void writeSetCursorCallback() = 0;
+  };
+
+  class SMsgWriter {
+  public:
+    virtual ~SMsgWriter();
+
+    // writeServerInit() must only be called at the appropriate time in the
+    // protocol initialisation.
+    virtual void writeServerInit()=0;
+
+    // Methods to write normal protocol messages
+
+    // writeSetColourMapEntries() writes a setColourMapEntries message, using
+    // the given ColourMap object to lookup the RGB values of the given range
+    // of colours.
+    virtual void writeSetColourMapEntries(int firstColour, int nColours,
+                                          ColourMap* cm);
+
+    // writeBell() and writeServerCutText() do the obvious thing.
+    virtual void writeBell();
+    virtual void writeServerCutText(const char* str, int len);
+
+    // writeSetDesktopSize() on a V3 writer won't actually write immediately,
+    // but will write the relevant pseudo-rectangle as part of the next update.
+    virtual bool writeSetDesktopSize()=0;
+
+    // Like setDestkopSize, we can't just write out a setCursor message
+    // immediately on a V3 writer.  Instead of calling writeSetCursor()
+    // directly, you must call cursorChange(), and then invoke writeSetCursor()
+    // in response to the writeSetCursorCallback() callback.  For a V3 writer
+    // this will happen when the next update is sent.
+    virtual void cursorChange(WriteSetCursorCallback* cb)=0;
+    virtual void writeSetCursor(int width, int height, int hotspotX,
+                                int hotspotY, void* data, void* mask)=0;
+
+    // needFakeUpdate() returns true when an immediate update is needed in
+    // order to flush out setDesktopSize or setCursor pseudo-rectangles to the
+    // client.
+    virtual bool needFakeUpdate();
+
+    // writeFramebufferUpdate() writes a framebuffer update using the given
+    // UpdateInfo and ImageGetter.  On a V3 writer this may have
+    // pseudo-rectangles for setDesktopSize and setCursor added to it, and so
+    // may invoke writeSetCursorCallback().
+    virtual void writeFramebufferUpdate(const UpdateInfo& ui, ImageGetter* ig,
+                                        Region* updatedRegion);
+
+    // writeRects() accepts an UpdateInfo (changed & copied regions) and an
+    // ImageGetter to fetch pixels from.  It then calls writeCopyRect() and
+    // writeRect() as appropriate.  writeFramebufferUpdateStart() must be used
+    // before the first writeRects() call and writeFrameBufferUpdateEnd() after
+    // the last one.  It returns the actual region sent to the client, which
+    // may be smaller than the update passed in.
+    virtual void writeRects(const UpdateInfo& update, ImageGetter* ig,
+                            Region* updatedRegion);
+
+    // To construct a framebuffer update you can call
+    // writeFramebufferUpdateStart(), followed by a number of writeCopyRect()s
+    // and writeRect()s, finishing with writeFramebufferUpdateEnd().  If you
+    // know the exact number of rectangles ahead of time you can specify it to
+    // writeFramebufferUpdateStart() which can be more efficient.
+    virtual void writeFramebufferUpdateStart(int nRects)=0;
+    virtual void writeFramebufferUpdateStart()=0;
+    virtual void writeFramebufferUpdateEnd()=0;
+
+    // writeRect() tries to write the given rectangle.  If it is unable to
+    // write the whole rectangle it returns false and sets actual to the actual
+    // rectangle which was updated.
+    virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual);
+    virtual bool writeRect(const Rect& r, unsigned int encoding,
+                           ImageGetter* ig, Rect* actual);
+
+    virtual void writeCopyRect(const Rect& r, int srcX, int srcY);
+
+    virtual void startRect(const Rect& r, unsigned int enc)=0;
+    virtual void endRect()=0;
+
+    // setOutStream() changes the OutStream on the fly.
+    virtual void setOutStream(rdr::OutStream* os);
+
+    ConnParams* getConnParams() { return cp; }
+    rdr::OutStream* getOutStream() { return os; }
+    rdr::U8* getImageBuf(int required, int requested=0, int* nPixels=0);
+    int bpp();
+
+    int getUpdatesSent()           { return updatesSent; }
+    int getRectsSent(int encoding) { return rectsSent[encoding]; }
+    int getBytesSent(int encoding) { return bytesSent[encoding]; }
+    int getRawBytesEquivalent()    { return rawBytesEquivalent; }
+
+    int imageBufIdealSize;
+
+  protected:
+    SMsgWriter(ConnParams* cp, rdr::OutStream* os);
+
+    virtual void startMsg(int type)=0;
+    virtual void endMsg()=0;
+
+    ConnParams* cp;
+    rdr::OutStream* os;
+
+    Encoder* encoders[encodingMax+1];
+    int lenBeforeRect;
+    unsigned int currentEncoding;
+    int updatesSent;
+    int bytesSent[encodingMax+1];
+    int rectsSent[encodingMax+1];
+    int rawBytesEquivalent;
+
+    rdr::U8* imageBuf;
+    int imageBufSize;
+  };
+}
+#endif
diff --git a/rfb/SMsgWriterV3.cxx b/rfb/SMsgWriterV3.cxx
new file mode 100644
index 0000000..20a7280
--- /dev/null
+++ b/rfb/SMsgWriterV3.cxx
@@ -0,0 +1,173 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <rdr/OutStream.h>
+#include <rdr/MemOutStream.h>
+#include <rfb/msgTypes.h>
+#include <rfb/Exception.h>
+#include <rfb/ConnParams.h>
+#include <rfb/SMsgWriterV3.h>
+
+using namespace rfb;
+
+SMsgWriterV3::SMsgWriterV3(ConnParams* cp, rdr::OutStream* os)
+  : SMsgWriter(cp, os), updateOS(0), realOS(os), nRectsInUpdate(0),
+    nRectsInHeader(0), wsccb(0),
+    needSetDesktopSize(false)
+{
+}
+
+SMsgWriterV3::~SMsgWriterV3()
+{
+  delete updateOS;
+}
+
+void SMsgWriterV3::writeServerInit()
+{
+  os->writeU16(cp->width);
+  os->writeU16(cp->height);
+  cp->pf().write(os);
+  os->writeString(cp->name());
+  endMsg();
+}
+
+void SMsgWriterV3::startMsg(int type)
+{
+  if (os != realOS)
+    throw Exception("startMsg called while writing an update?");
+
+  os->writeU8(type);
+}
+
+void SMsgWriterV3::endMsg()
+{
+  os->flush();
+}
+
+bool SMsgWriterV3::writeSetDesktopSize() {
+  if (!cp->supportsDesktopResize) return false;
+  needSetDesktopSize = true;
+  return true;
+}
+
+void SMsgWriterV3::cursorChange(WriteSetCursorCallback* cb)
+{
+  wsccb = cb;
+}
+
+void SMsgWriterV3::writeSetCursor(int width, int height, int hotspotX,
+                                  int hotspotY, void* data, void* mask)
+{
+  if (!wsccb) return;
+  if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+    throw Exception("SMsgWriterV3::writeSetCursor: nRects out of sync");
+  os->writeS16(hotspotX);
+  os->writeS16(hotspotY);
+  os->writeU16(width);
+  os->writeU16(height);
+  os->writeU32(pseudoEncodingCursor);
+  os->writeBytes(data, width * height * (cp->pf().bpp/8));
+  os->writeBytes(mask, (width+7)/8 * height);
+}
+
+void SMsgWriterV3::writeFramebufferUpdateStart(int nRects)
+{
+  startMsg(msgTypeFramebufferUpdate);
+  os->pad(1);
+  if (wsccb) nRects++;
+  if (needSetDesktopSize) nRects++;
+  os->writeU16(nRects);
+  nRectsInUpdate = 0;
+  nRectsInHeader = nRects;
+  if (wsccb) {
+    wsccb->writeSetCursorCallback();
+    wsccb = 0;
+  }
+}
+
+void SMsgWriterV3::writeFramebufferUpdateStart()
+{
+  nRectsInUpdate = nRectsInHeader = 0;
+  if (!updateOS)
+    updateOS = new rdr::MemOutStream;
+  os = updateOS;
+}
+
+void SMsgWriterV3::writeFramebufferUpdateEnd()
+{
+  if (needSetDesktopSize) {
+    if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+      throw Exception("SMsgWriterV3 setDesktopSize: nRects out of sync");
+    os->writeS16(0);
+    os->writeS16(0);
+    os->writeU16(cp->width);
+    os->writeU16(cp->height);
+    os->writeU32(pseudoEncodingDesktopSize);
+    needSetDesktopSize = false;
+  }
+
+  if (nRectsInUpdate != nRectsInHeader && nRectsInHeader)
+    throw Exception("SMsgWriterV3::writeFramebufferUpdateEnd: "
+                    "nRects out of sync");
+  if (os == updateOS) {
+    os = realOS;
+    startMsg(msgTypeFramebufferUpdate);
+    os->pad(1);
+    os->writeU16(nRectsInUpdate);
+    os->writeBytes(updateOS->data(), updateOS->length());
+    updateOS->clear();
+  }
+
+  updatesSent++;
+  endMsg();
+}
+
+bool SMsgWriterV3::needFakeUpdate()
+{
+  return wsccb || needSetDesktopSize;
+}
+
+void SMsgWriterV3::startRect(const Rect& r, unsigned int encoding)
+{
+  if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
+    throw Exception("SMsgWriterV3::startRect: nRects out of sync");
+
+  currentEncoding = encoding;
+  lenBeforeRect = os->length();
+  if (encoding != encodingCopyRect)
+    rawBytesEquivalent += 12 + r.width() * r.height() * (bpp()/8);
+
+  os->writeS16(r.tl.x);
+  os->writeS16(r.tl.y);
+  os->writeU16(r.width());
+  os->writeU16(r.height());
+  os->writeU32(encoding);
+}
+
+void SMsgWriterV3::endRect()
+{
+  if (currentEncoding <= encodingMax) {
+    bytesSent[currentEncoding] += os->length() - lenBeforeRect;
+    rectsSent[currentEncoding]++;
+  }
+}
+
+void SMsgWriterV3::setOutStream(rdr::OutStream* os_)
+{
+  SMsgWriter::setOutStream(os_);
+  realOS = os;
+}
diff --git a/rfb/SMsgWriterV3.h b/rfb/SMsgWriterV3.h
new file mode 100644
index 0000000..3881061
--- /dev/null
+++ b/rfb/SMsgWriterV3.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_SMSGWRITERV3_H__
+#define __RFB_SMSGWRITERV3_H__
+
+#include <rfb/SMsgWriter.h>
+
+namespace rdr { class MemOutStream; }
+
+namespace rfb {
+  class SMsgWriterV3 : public SMsgWriter {
+  public:
+    SMsgWriterV3(ConnParams* cp, rdr::OutStream* os);
+    virtual ~SMsgWriterV3();
+
+    virtual void writeServerInit();
+    virtual void startMsg(int type);
+    virtual void endMsg();
+    virtual bool writeSetDesktopSize();
+    virtual void cursorChange(WriteSetCursorCallback* cb);
+    virtual void writeSetCursor(int width, int height, int hotspotX,
+                                int hotspotY, void* data, void* mask);
+    virtual void writeFramebufferUpdateStart(int nRects);
+    virtual void writeFramebufferUpdateStart();
+    virtual void writeFramebufferUpdateEnd();
+    virtual bool needFakeUpdate();
+    virtual void startRect(const Rect& r, unsigned int encoding);
+    virtual void endRect();
+
+    virtual void setOutStream(rdr::OutStream* os);
+
+  private:
+    rdr::MemOutStream* updateOS;
+    rdr::OutStream* realOS;
+    int nRectsInUpdate;
+    int nRectsInHeader;
+    WriteSetCursorCallback* wsccb;
+    bool needSetDesktopSize;
+  };
+}
+#endif
diff --git a/rfb/SSecurity.h b/rfb/SSecurity.h
new file mode 100644
index 0000000..2ca5344
--- /dev/null
+++ b/rfb/SSecurity.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// SSecurity - class on the server side for handling security handshaking.  A
+// derived class for a particular security type overrides the processMsg()
+// method.  processMsg() is called first when the security type has been
+// decided on, and will keep being called whenever there is data to read from
+// the client until either it returns false, indicating authentication/security
+// failure, or it returns with done set to true, to indicate success.
+//
+// processMsg() must never block (or at least must never block until the client
+// has been authenticated) - this is to prevent denial of service attacks.
+// Note that the first time processMsg() is called, there is no guarantee that
+// there is any data to read from the SConnection's InStream, but subsequent
+// calls guarantee there is at least one byte which can be read without
+// blocking.
+//
+// getType() should return the secType value corresponding to the SSecurity
+// implementation.
+//
+// failureMessage_.buf can be set to a string which will be passed to the client
+// if processMsg returns false, to indicate the reason for the failure.
+
+#ifndef __RFB_SSECURITY_H__
+#define __RFB_SSECURITY_H__
+
+#include <rfb/util.h>
+
+namespace rfb {
+
+  class SConnection;
+
+  class SSecurity {
+  public:
+    virtual ~SSecurity() {}
+    virtual bool processMsg(SConnection* sc, bool* done)=0;
+    virtual void destroy() { delete this; }
+    virtual int getType() const = 0;
+
+    // getUserName() gets the name of the user attempting authentication.  The
+    // storage is owned by the SSecurity object, so a copy must be taken if
+    // necessary.  Null may be returned to indicate that there is no user name
+    // for this security type.
+    virtual const char* getUserName() const = 0;
+
+    virtual const char* failureMessage() {return failureMessage_.buf;}
+  protected:
+    CharArray failureMessage_;
+  };
+
+  // SSecurityFactory creates new SSecurity instances for
+  // particular security types.
+  // The instances must be destroyed by calling destroy()
+  // on them when done.
+  class SSecurityFactory {
+  public:
+    virtual ~SSecurityFactory() {}
+    virtual SSecurity* getSSecurity(int secType, bool noAuth=false)=0;
+  };
+
+}
+#endif
diff --git a/rfb/SSecurityFactoryStandard.cxx b/rfb/SSecurityFactoryStandard.cxx
new file mode 100644
index 0000000..e3a40aa
--- /dev/null
+++ b/rfb/SSecurityFactoryStandard.cxx
@@ -0,0 +1,101 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// SSecurityFactoryStandard
+//
+
+#include <rfb/secTypes.h>
+#include <rfb/SSecurityNone.h>
+#include <rfb/Configuration.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Exception.h>
+#include <rfb/SSecurityFactoryStandard.h>
+
+using namespace rfb;
+
+static LogWriter vlog("SSecurityFactoryStandard");
+
+VncAuthPasswdParameter* SSecurityFactoryStandard::vncAuthPasswd = 0;
+
+
+SSecurity* SSecurityFactoryStandard::getSSecurity(int secType, bool noAuth) {
+  switch (secType) {
+  case secTypeNone:    return new SSecurityNone();
+  case secTypeVncAuth:
+    if (!vncAuthPasswd)
+      throw rdr::Exception("No VncAuthPasswdParameter defined!");
+    return new SSecurityVncAuth(vncAuthPasswd);
+  default:
+    throw Exception("Unsupported secType?");
+  }
+}
+
+VncAuthPasswdParameter::VncAuthPasswdParameter() {
+  if (SSecurityFactoryStandard::vncAuthPasswd)
+    throw rdr::Exception("duplicate VncAuthPasswdParameter!");
+  SSecurityFactoryStandard::vncAuthPasswd = this;
+}
+
+
+VncAuthPasswdConfigParameter::VncAuthPasswdConfigParameter()
+: passwdParam("Password",
+   "Obfuscated binary encoding of the password which clients must supply to "
+   "access the server", 0, 0) {
+}
+
+char* VncAuthPasswdConfigParameter::getVncAuthPasswd() {
+  CharArray obfuscated;
+  int len;
+  passwdParam.getData((void**)&obfuscated.buf, &len);
+  printf("vnc password len=%d\n", len); // ***
+  if (len == 8) {
+    CharArray password(9);
+    memcpy(password.buf, obfuscated.buf, 8);
+    vncAuthUnobfuscatePasswd(password.buf);
+    return password.takeBuf();
+  }
+  return 0;
+}
+
+
+VncAuthPasswdFileParameter::VncAuthPasswdFileParameter()
+  : param("PasswordFile", "Password file for VNC authentication", "") {
+}
+
+char* VncAuthPasswdFileParameter::getVncAuthPasswd() {
+  CharArray fname(param.getData());
+  if (!fname.buf[0]) {
+    vlog.error("passwordFile parameter not set");
+    return 0;
+  }
+  FILE* fp = fopen(fname.buf, "r");
+  if (!fp) {
+    vlog.error("opening password file '%s' failed",fname.buf);
+    return 0;
+  }
+  CharArray passwd(9);
+  int len = fread(passwd.buf, 1, 9, fp);
+  fclose(fp);
+  if (len != 8) {
+    vlog.error("password file '%s' is the wrong length",fname.buf);
+    return 0;
+  }
+  vncAuthUnobfuscatePasswd(passwd.buf);
+  return passwd.takeBuf();
+}
+
diff --git a/rfb/SSecurityFactoryStandard.h b/rfb/SSecurityFactoryStandard.h
new file mode 100644
index 0000000..5fced04
--- /dev/null
+++ b/rfb/SSecurityFactoryStandard.h
@@ -0,0 +1,75 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+//
+// SSecurityFactoryStandard - implementation of the SSecurityFactory
+// interface.
+//
+// Server implementations must define an instance of a
+// VncAuthPasswdParameter-based class somewhere.  Any class based on
+// VncAuthPasswdParameter will automatically register itself as the
+// password parameter to be used by the Standard factory.
+//
+// Two implementations are provided here:
+//
+// VncAuthPasswdConfigParameter - reads the password from the Binary
+//                                parameter "Password".
+// VncAuthPasswdFileParameter   - reads the password from the file named
+//                                in the String parameter "PasswordFile".
+//
+// This factory supports only the "None" and "VncAuth" security types.
+//
+
+#ifndef __RFB_SSECURITYFACTORY_STANDARD_H__
+#define __RFB_SSECURITYFACTORY_STANDARD_H__
+
+#include <rfb/SSecurityVncAuth.h>
+#include <rfb/Configuration.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+  class VncAuthPasswdParameter : public VncAuthPasswdGetter {
+  public:
+    VncAuthPasswdParameter();
+    virtual ~VncAuthPasswdParameter() {}
+  };
+
+  class SSecurityFactoryStandard : public SSecurityFactory {
+  public:
+    virtual SSecurity* getSSecurity(int secType, bool noAuth);
+    static VncAuthPasswdParameter* vncAuthPasswd;
+  };
+
+  class VncAuthPasswdConfigParameter : public VncAuthPasswdParameter {
+  public:
+    VncAuthPasswdConfigParameter();
+    virtual char* getVncAuthPasswd();
+  protected:
+    BinaryParameter passwdParam;
+  };
+
+  class VncAuthPasswdFileParameter : public VncAuthPasswdParameter {
+  public:
+    VncAuthPasswdFileParameter();
+    virtual char* getVncAuthPasswd();
+    StringParameter param;
+  };
+
+}
+#endif
diff --git a/rfb/SSecurityNone.h b/rfb/SSecurityNone.h
new file mode 100644
index 0000000..09b2db4
--- /dev/null
+++ b/rfb/SSecurityNone.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// SSecurityNone.h
+//
+
+#ifndef __SSECURITYNONE_H__
+#define __SSECURITYNONE_H__
+
+#include <rfb/SSecurity.h>
+
+namespace rfb {
+
+  class SSecurityNone : public SSecurity {
+  public:
+    virtual bool processMsg(SConnection* sc, bool* done) {
+      *done = true; return true;
+    }
+    virtual int getType() const {return secTypeNone;}
+    virtual const char* getUserName() const {return 0;}
+  };
+}
+#endif
diff --git a/rfb/SSecurityVncAuth.cxx b/rfb/SSecurityVncAuth.cxx
new file mode 100644
index 0000000..532d1a6
--- /dev/null
+++ b/rfb/SSecurityVncAuth.cxx
@@ -0,0 +1,83 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// SSecurityVncAuth
+//
+
+#include <rfb/SSecurityVncAuth.h>
+#include <rdr/RandomStream.h>
+#include <rfb/SConnection.h>
+#include <rfb/vncAuth.h>
+#include <rfb/Configuration.h>
+#include <rfb/LogWriter.h>
+#include <rfb/util.h>
+#include <string.h>
+#include <stdio.h>
+
+using namespace rfb;
+
+static LogWriter vlog("VncAuth");
+
+
+SSecurityVncAuth::SSecurityVncAuth(VncAuthPasswdGetter* pg_)
+  : sentChallenge(false), responsePos(0), pg(pg_)
+{
+}
+
+bool SSecurityVncAuth::processMsg(SConnection* sc, bool* done)
+{
+  *done = false;
+  rdr::InStream* is = sc->getInStream();
+  rdr::OutStream* os = sc->getOutStream();
+
+  if (!sentChallenge) {
+    rdr::RandomStream rs;
+    rs.readBytes(challenge, vncAuthChallengeSize);
+    os->writeBytes(challenge, vncAuthChallengeSize);
+    os->flush();
+    sentChallenge = true;
+    return true;
+  }
+
+  if (responsePos >= vncAuthChallengeSize) return false;
+  while (is->checkNoWait(1) && responsePos < vncAuthChallengeSize) {
+    response[responsePos++] = is->readU8();
+  }
+
+  if (responsePos < vncAuthChallengeSize) return true;
+
+  CharArray passwd(pg->getVncAuthPasswd());
+
+  // Beyond this point, there is no more VNCAuth protocol to perform.
+  *done = true;
+
+  if (!passwd.buf) {
+    failureMessage_.buf = strDup("No password configured for VNC Auth");
+    vlog.error(failureMessage_.buf);
+    return false;
+  }
+
+  vncAuthEncryptChallenge(challenge, passwd.buf);
+  memset(passwd.buf, 0, strlen(passwd.buf));
+
+  if (memcmp(challenge, response, vncAuthChallengeSize) != 0) {
+    return false;
+  }
+
+  return true;
+}
diff --git a/rfb/SSecurityVncAuth.h b/rfb/SSecurityVncAuth.h
new file mode 100644
index 0000000..edbd720
--- /dev/null
+++ b/rfb/SSecurityVncAuth.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+// SSecurityVncAuth - legacy VNC authentication protocol.
+// The getPasswd call can be overridden if you wish to store
+// the VncAuth password in an implementation-specific place.
+// Otherwise, the password is read from a BinaryParameter
+// called Password.
+
+#ifndef __RFB_SSECURITYVNCAUTH_H__
+#define __RFB_SSECURITYVNCAUTH_H__
+
+#include <rfb/SSecurity.h>
+#include <rfb/secTypes.h>
+#include <rfb/vncAuth.h>
+
+namespace rfb {
+
+  class VncAuthPasswdGetter {
+  public:
+    // getPasswd() returns a string or null if unsuccessful.  The
+    // SSecurityVncAuth object delete[]s the string when done.
+    virtual char* getVncAuthPasswd()=0;
+  };
+
+  class SSecurityVncAuth : public SSecurity {
+  public:
+    SSecurityVncAuth(VncAuthPasswdGetter* pg);
+    virtual bool processMsg(SConnection* sc, bool* done);
+    virtual int getType() const {return secTypeVncAuth;}
+    virtual const char* getUserName() const {return 0;}
+  private:
+    rdr::U8 challenge[vncAuthChallengeSize];
+    rdr::U8 response[vncAuthChallengeSize];
+    bool sentChallenge;
+    int responsePos;
+    VncAuthPasswdGetter* pg;
+  };
+}
+#endif
diff --git a/rfb/ServerCore.cxx b/rfb/ServerCore.cxx
new file mode 100644
index 0000000..7a20c56
--- /dev/null
+++ b/rfb/ServerCore.cxx
@@ -0,0 +1,95 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- ServerCore.cxx
+
+// This header will define the Server interface, from which ServerMT and
+// ServerST will be derived.
+
+#include <string.h>
+#include <rfb/util.h>
+#include <rfb/ServerCore.h>
+#include <rfb/vncAuth.h>
+
+rfb::IntParameter rfb::Server::idleTimeout
+("IdleTimeout",
+ "The number of seconds after which an idle VNC connection will be dropped",
+ 3600);
+rfb::IntParameter rfb::Server::clientWaitTimeMillis
+("ClientWaitTimeMillis",
+ "The number of milliseconds to wait for a client which is no longer "
+ "responding",
+ 20000);
+rfb::StringParameter rfb::Server::sec_types
+("SecurityTypes",
+ "Specify which security scheme to use for incoming connections (None, VncAuth)",
+ "VncAuth");
+rfb::StringParameter rfb::Server::rev_sec_types
+("ReverseSecurityTypes",
+ "Specify encryption scheme to use for reverse connections (None)",
+ "None");
+rfb::BoolParameter rfb::Server::compareFB
+("CompareFB",
+ "Perform pixel comparison on framebuffer to reduce unnecessary updates",
+ true);
+rfb::BoolParameter rfb::Server::protocol3_3
+("Protocol3.3",
+ "Always use protocol version 3.3 for backwards compatibility with "
+ "badly-behaved clients",
+ false);
+rfb::BoolParameter rfb::Server::alwaysShared
+("AlwaysShared",
+ "Always treat incoming connections as shared, regardless of the client-"
+ "specified setting",
+ false);
+rfb::BoolParameter rfb::Server::neverShared
+("NeverShared",
+ "Never treat incoming connections as shared, regardless of the client-"
+ "specified setting",
+ false);
+rfb::BoolParameter rfb::Server::disconnectClients
+("DisconnectClients",
+ "Disconnect existing clients if an incoming connection is non-shared. "
+ "If combined with NeverShared then new connections will be refused "
+ "while there is a client active",
+ true);
+rfb::BoolParameter rfb::Server::acceptKeyEvents
+("AcceptKeyEvents",
+ "Accept key press and release events from clients.",
+ true);
+rfb::BoolParameter rfb::Server::acceptPointerEvents
+("AcceptPointerEvents",
+ "Accept pointer press and release events from clients.",
+ true);
+rfb::BoolParameter rfb::Server::acceptCutText
+("AcceptCutText",
+ "Accept clipboard updates from clients.",
+ true);
+rfb::BoolParameter rfb::Server::sendCutText
+("SendCutText",
+ "Send clipboard changes to clients.",
+ true);
+rfb::BoolParameter rfb::Server::queryConnect
+("QueryConnect",
+ "Prompt the local user to accept or reject incoming connections.",
+ false);
+rfb::IntParameter rfb::Server::blacklistLevel
+("BlacklistLevel",
+ "When to test whether particular host should be blacklisted.  (0 = Never, "
+ "1 = Test before authentication, 2 = Test on connect)",
+ 1);
diff --git a/rfb/ServerCore.h b/rfb/ServerCore.h
new file mode 100644
index 0000000..74d7443
--- /dev/null
+++ b/rfb/ServerCore.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- ServerCore.h
+
+// This header will define the Server interface, from which ServerMT and
+// ServerST will be derived.
+
+#ifndef __RFB_SERVER_CORE_H__
+#define __RFB_SERVER_CORE_H__
+
+#include <rfb/Configuration.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+  class Server {
+  public:
+
+    static IntParameter idleTimeout;
+    static IntParameter clientWaitTimeMillis;
+    static StringParameter sec_types;
+    static StringParameter rev_sec_types;
+    static BoolParameter compareFB;
+    static BoolParameter protocol3_3;
+    static BoolParameter alwaysShared;
+    static BoolParameter neverShared;
+    static BoolParameter disconnectClients;
+    static BoolParameter acceptKeyEvents;
+    static BoolParameter acceptPointerEvents;
+    static BoolParameter acceptCutText;
+    static BoolParameter sendCutText;
+    static BoolParameter queryConnect;
+    static IntParameter blacklistLevel;
+
+  };
+
+};
+
+#endif // __RFB_SERVER_CORE_H__
+
diff --git a/rfb/Threading.h b/rfb/Threading.h
new file mode 100644
index 0000000..effc436
--- /dev/null
+++ b/rfb/Threading.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- Threading.h
+// General purpose threading interface.
+// If the current platform supports threading then __RFB_THREADING_IMPL
+// will be defined after this header has been included.
+
+#ifndef __RFB_THREADING_H__
+#define __RFB_THREADING_H__
+
+#ifdef WIN32
+#include <rfb/win32/Threading_win32.h>
+#endif
+
+#endif // __RFB_THREADING_H__
diff --git a/rfb/TransImageGetter.cxx b/rfb/TransImageGetter.cxx
new file mode 100644
index 0000000..693b18c
--- /dev/null
+++ b/rfb/TransImageGetter.cxx
@@ -0,0 +1,278 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/Exception.h>
+#include <rfb/ConnParams.h>
+#include <rfb/SMsgWriter.h>
+#include <rfb/ColourMap.h>
+#include <rfb/TrueColourMap.h>
+#include <rfb/PixelBuffer.h>
+#include <rfb/ColourCube.h>
+#include <rfb/TransImageGetter.h>
+
+using namespace rfb;
+
+const PixelFormat bgr233PF(8, 8, false, true, 7, 7, 3, 0, 3, 6);
+
+static void noTransFn(void* table_,
+                      const PixelFormat& inPF, void* inPtr, int inStride,
+                      const PixelFormat& outPF, void* outPtr, int outStride,
+                      int width, int height)
+{
+  rdr::U8* ip = (rdr::U8*)inPtr;
+  rdr::U8* op = (rdr::U8*)outPtr;
+  int inStrideBytes = inStride * (inPF.bpp/8);
+  int outStrideBytes = outStride * (outPF.bpp/8);
+  int widthBytes = width * (outPF.bpp/8);
+
+  while (height > 0) {
+    memcpy(op, ip, widthBytes);
+    ip += inStrideBytes;
+    op += outStrideBytes;
+    height--;
+  }
+}
+
+#define BPPOUT 8
+#include "transInitTempl.h"
+#define BPPIN 8
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 16
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 32
+#include "transTempl.h"
+#undef BPPIN
+#undef BPPOUT
+
+#define BPPOUT 16
+#include "transInitTempl.h"
+#define BPPIN 8
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 16
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 32
+#include "transTempl.h"
+#undef BPPIN
+#undef BPPOUT
+
+#define BPPOUT 32
+#include "transInitTempl.h"
+#define BPPIN 8
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 16
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 32
+#include "transTempl.h"
+#undef BPPIN
+#undef BPPOUT
+
+
+// Translation functions.  Note that transSimple* is only used for 8/16bpp and
+// transRGB* is used for 16/32bpp
+
+static transFnType transSimpleFns[][3] = {
+  { transSimple8to8,  transSimple8to16,  transSimple8to32 },
+  { transSimple16to8, transSimple16to16, transSimple16to32 },
+};
+static transFnType transRGBFns[][3] = {
+  { transRGB16to8, transRGB16to16, transRGB16to32 },
+  { transRGB32to8, transRGB32to16, transRGB32to32 }
+};
+static transFnType transRGBCubeFns[][3] = {
+  { transRGBCube16to8, transRGBCube16to16, transRGBCube16to32 },
+  { transRGBCube32to8, transRGBCube32to16, transRGBCube32to32 }
+};
+
+// Table initialisation functions.
+
+typedef void (*initCMtoTCFnType)(rdr::U8** tablep, const PixelFormat& inPF,
+                                 ColourMap* cm, const PixelFormat& outPF);
+typedef void (*initTCtoTCFnType)(rdr::U8** tablep, const PixelFormat& inPF,
+                                 const PixelFormat& outPF);
+typedef void (*initCMtoCubeFnType)(rdr::U8** tablep, const PixelFormat& inPF,
+                                   ColourMap* cm, ColourCube* cube);
+typedef void (*initTCtoCubeFnType)(rdr::U8** tablep, const PixelFormat& inPF,
+                                   ColourCube* cube);
+
+
+static initCMtoTCFnType initSimpleCMtoTCFns[] = {
+    initSimpleCMtoTC8, initSimpleCMtoTC16, initSimpleCMtoTC32
+};
+
+static initTCtoTCFnType initSimpleTCtoTCFns[] = {
+    initSimpleTCtoTC8, initSimpleTCtoTC16, initSimpleTCtoTC32
+};
+
+static initCMtoCubeFnType initSimpleCMtoCubeFns[] = {
+    initSimpleCMtoCube8, initSimpleCMtoCube16, initSimpleCMtoCube32
+};
+
+static initTCtoCubeFnType initSimpleTCtoCubeFns[] = {
+    initSimpleTCtoCube8, initSimpleTCtoCube16, initSimpleTCtoCube32
+};
+
+static initTCtoTCFnType initRGBTCtoTCFns[] = {
+    initRGBTCtoTC8, initRGBTCtoTC16, initRGBTCtoTC32
+};
+
+static initTCtoCubeFnType initRGBTCtoCubeFns[] = {
+    initRGBTCtoCube8, initRGBTCtoCube16, initRGBTCtoCube32
+};
+
+
+TransImageGetter::TransImageGetter(bool econ)
+  : economic(econ), pb(0), table(0), transFn(0), cube(0)
+{
+}
+
+TransImageGetter::~TransImageGetter()
+{
+  delete [] table;
+}
+
+void TransImageGetter::init(PixelBuffer* pb_, const PixelFormat& out,
+                            SMsgWriter* writer, ColourCube* cube_)
+{
+  pb = pb_;
+  outPF = out;
+  transFn = 0;
+  cube = cube_;
+  const PixelFormat& inPF = pb->getPF();
+
+  if ((inPF.bpp != 8) && (inPF.bpp != 16) && (inPF.bpp != 32))
+    throw Exception("TransImageGetter: bpp in not 8, 16 or 32");
+
+  if ((outPF.bpp != 8) && (outPF.bpp != 16) && (outPF.bpp != 32))
+    throw Exception("TransImageGetter: bpp out not 8, 16 or 32");
+
+  if (!outPF.trueColour) {
+    if (outPF.bpp != 8)
+      throw Exception("TransImageGetter: outPF has colour map but not 8bpp");
+
+    if (!inPF.trueColour) {
+      if (inPF.bpp != 8)
+        throw Exception("TransImageGetter: inPF has colourMap but not 8bpp");
+
+      // CM to CM/Cube
+
+      if (cube) {
+        transFn = transSimpleFns[inPF.bpp/16][outPF.bpp/16];
+        (*initSimpleCMtoCubeFns[outPF.bpp/16]) (&table, inPF,
+                                                pb->getColourMap(), cube);
+      } else {
+        transFn = noTransFn;
+        setColourMapEntries(0, 256, writer);
+      }
+      return;
+    }
+
+    // TC to CM/Cube
+
+    ColourCube defaultCube(6,6,6);
+    if (!cube) cube = &defaultCube;
+
+    if ((inPF.bpp > 16) || (economic && (inPF.bpp == 16))) {
+      transFn = transRGBCubeFns[inPF.bpp/32][outPF.bpp/16];
+      (*initRGBTCtoCubeFns[outPF.bpp/16]) (&table, inPF, cube);
+    } else {
+      transFn = transSimpleFns[inPF.bpp/16][outPF.bpp/16];
+      (*initSimpleTCtoCubeFns[outPF.bpp/16]) (&table, inPF, cube);
+    }
+
+    if (cube != &defaultCube)
+      return;
+
+    if (writer) writer->writeSetColourMapEntries(0, 216, cube);
+    cube = 0;
+    return;
+  }
+
+  if (inPF.equal(outPF)) {
+    transFn = noTransFn;
+    return;
+  }
+
+  if (!inPF.trueColour) {
+
+    // CM to TC
+
+    if (inPF.bpp != 8)
+      throw Exception("TransImageGetter: inPF has colourMap but not 8bpp");
+    transFn = transSimpleFns[inPF.bpp/16][outPF.bpp/16];
+    (*initSimpleCMtoTCFns[outPF.bpp/16]) (&table, inPF, pb->getColourMap(),
+                                          outPF);
+    return;
+  }
+
+  // TC to TC
+
+  if ((inPF.bpp > 16) || (economic && (inPF.bpp == 16))) {
+    transFn = transRGBFns[inPF.bpp/32][outPF.bpp/16];
+    (*initRGBTCtoTCFns[outPF.bpp/16]) (&table, inPF, outPF);
+  } else {
+    transFn = transSimpleFns[inPF.bpp/16][outPF.bpp/16];
+    (*initSimpleTCtoTCFns[outPF.bpp/16]) (&table, inPF, outPF);
+  }
+}
+
+void TransImageGetter::setColourMapEntries(int firstCol, int nCols,
+                                           SMsgWriter* writer)
+{
+  if (nCols == 0)
+    nCols = (1 << pb->getPF().depth) - firstCol;
+  if (pb->getPF().trueColour) return; // shouldn't be called in this case
+
+  if (outPF.trueColour) {
+    (*initSimpleCMtoTCFns[outPF.bpp/16]) (&table, pb->getPF(),
+                                          pb->getColourMap(), outPF);
+  } else if (cube) {
+    (*initSimpleCMtoCubeFns[outPF.bpp/16]) (&table, pb->getPF(),
+                                            pb->getColourMap(), cube);
+  } else if (writer && pb->getColourMap()) {
+    writer->writeSetColourMapEntries(firstCol, nCols, pb->getColourMap());
+  }
+}
+
+void TransImageGetter::getImage(void* outPtr, const Rect& r, int outStride)
+{
+  if (!transFn)
+    throw Exception("TransImageGetter: not initialised yet");
+
+  int inStride;
+  const rdr::U8* inPtr = pb->getPixelsR(r.translate(offset.negate()), &inStride);
+
+  if (!outStride) outStride = r.width();
+
+  (*transFn)(table, pb->getPF(), (void*)inPtr, inStride,
+             outPF, outPtr, outStride, r.width(), r.height());
+}
+
+void TransImageGetter::translatePixels(void* inPtr, void* outPtr,
+                                       int nPixels) const
+{
+  (*transFn)(table, pb->getPF(), inPtr, nPixels,
+             outPF, outPtr, nPixels, nPixels, 1);
+}
diff --git a/rfb/TransImageGetter.h b/rfb/TransImageGetter.h
new file mode 100644
index 0000000..60ab069
--- /dev/null
+++ b/rfb/TransImageGetter.h
@@ -0,0 +1,104 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// TransImageGetter - class to perform translation between pixel formats,
+// implementing the ImageGetter interface.
+//
+
+#ifndef __RFB_TRANSIMAGEGETTER_H__
+#define __RFB_TRANSIMAGEGETTER_H__
+
+#include <rfb/Rect.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/ImageGetter.h>
+
+namespace rfb {
+  typedef void (*transFnType)(void* table_,
+                              const PixelFormat& inPF, void* inPtr,
+                              int inStride,
+                              const PixelFormat& outPF, void* outPtr,
+                              int outStride, int width, int height);
+
+  class SMsgWriter;
+  class ColourMap;
+  class PixelBuffer;
+  class ColourCube;
+
+  class TransImageGetter : public ImageGetter {
+  public:
+
+    TransImageGetter(bool econ=false);
+    virtual ~TransImageGetter();
+
+    // init() is called to initialise the translation tables.  The PixelBuffer
+    // argument gives the source data and format details, outPF gives the
+    // client's pixel format.  If the client has a colour map, then the writer
+    // argument is used to send a SetColourMapEntries message to the client.
+
+    void init(PixelBuffer* pb, const PixelFormat& outPF, SMsgWriter* writer=0,
+              ColourCube* cube=0);
+
+    // setColourMapEntries() is called when the PixelBuffer has a colour map
+    // which has changed.  firstColour and nColours specify which part of the
+    // colour map has changed.  If nColours is 0, this means the rest of the
+    // colour map.  The PixelBuffer previously passed to init() must have a
+    // valid ColourMap object.  If the client also has a colour map, then the
+    // writer argument is used to send a SetColourMapEntries message to the
+    // client.  If the client is true colour then instead we update the
+    // internal translation table - in this case the caller should also make
+    // sure that the client receives an update of the relevant parts of the
+    // framebuffer (the simplest thing to do is just update the whole
+    // framebuffer, though it is possible to be smarter than this).
+
+    void setColourMapEntries(int firstColour, int nColours,
+                             SMsgWriter* writer=0);
+
+    // getImage() gets the given rectangle of data from the PixelBuffer,
+    // translates it into the client's pixel format and puts it in the buffer
+    // pointed to by the outPtr argument.  The optional outStride argument can
+    // be used where padding is required between the output scanlines (the
+    // padding will be outStride-r.width() pixels).
+    void getImage(void* outPtr, const Rect& r, int outStride=0);
+
+    // translatePixels() translates the given number of pixels from inPtr,
+    // putting it into the buffer pointed to by outPtr.  The pixels at inPtr
+    // should be in the same format as the PixelBuffer, and the translated
+    // pixels will be in the format previously given by the outPF argument to
+    // init().  Note that this call does not use the PixelBuffer's pixel data.
+    void translatePixels(void* inPtr, void* outPtr, int nPixels) const;
+
+    // setPixelBuffer() changes the pixel buffer to be used.  The new pixel
+    // buffer MUST have the same pixel format as the old one - if not you
+    // should call init() instead.
+    void setPixelBuffer(PixelBuffer* pb_) { pb = pb_; }
+
+    // setOffset() sets an offset which is subtracted from the coordinates of
+    // the rectangle given to getImage().
+    void setOffset(const Point& offset_) { offset = offset_; }
+
+  private:
+    bool economic;
+    PixelBuffer* pb;
+    PixelFormat outPF;
+    rdr::U8* table;
+    transFnType transFn;
+    ColourCube* cube;
+    Point offset;
+  };
+}
+#endif
diff --git a/rfb/TrueColourMap.h b/rfb/TrueColourMap.h
new file mode 100644
index 0000000..c0d4907
--- /dev/null
+++ b/rfb/TrueColourMap.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_TRUECOLOURMAP_H__
+#define __RFB_TRUECOLOURMAP_H__
+
+#include <rfb/ColourMap.h>
+
+namespace rfb {
+
+  class TrueColourMap : public ColourMap {
+  public:
+    TrueColourMap(const PixelFormat& pf_) : pf(pf_) {}
+
+    virtual void lookup(int i, int* r, int* g, int* b)
+    {
+      *r = (((i >> pf.redShift  ) & pf.redMax)
+            * 65535 + pf.redMax/2) / pf.redMax;
+      *g = (((i >> pf.greenShift) & pf.greenMax)
+            * 65535 + pf.greenMax/2) / pf.greenMax;
+      *b = (((i >> pf.blueShift) & pf.blueMax)
+            * 65535 + pf.blueMax/2) / pf.blueMax;
+    }
+  private:
+    PixelFormat pf;
+  };
+}
+#endif
diff --git a/rfb/UpdateTracker.cxx b/rfb/UpdateTracker.cxx
new file mode 100644
index 0000000..cc0fb10
--- /dev/null
+++ b/rfb/UpdateTracker.cxx
@@ -0,0 +1,172 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- rfbUpdateTracker.cpp
+//
+// Tracks updated regions and a region-copy event, too
+//
+
+#include <assert.h>
+
+#include <rfb/UpdateTracker.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+
+static LogWriter vlog("UpdateTracker");
+
+// -=- ClippedUpdateTracker
+
+void ClippedUpdateTracker::add_changed(const Region &region) {
+  child.add_changed(region.intersect(cliprgn));
+}
+
+void ClippedUpdateTracker::add_copied(const Region &dest, const Point &delta) {
+  // Clip the destination to the display area
+  Region clipdest = dest.intersect(cliprgn);
+  if (clipdest.is_empty())  return;
+
+  // Clip the source to the screen
+  Region tmp = clipdest;
+  tmp.translate(delta.negate());
+  tmp.assign_intersect(cliprgn);
+  if (!tmp.is_empty()) {
+    // Translate the source back to a destination region
+    tmp.translate(delta);
+
+    // Pass the copy region to the child tracker
+    child.add_copied(tmp, delta);
+  }
+
+  // And add any bits that we had to remove to the changed region
+  tmp = clipdest.subtract(tmp);
+  if (!tmp.is_empty()) {
+    child.add_changed(tmp);
+  }
+}
+
+// SimpleUpdateTracker
+
+SimpleUpdateTracker::SimpleUpdateTracker(bool use_copyrect) {
+  copy_enabled = use_copyrect;
+}
+
+SimpleUpdateTracker::~SimpleUpdateTracker() {
+}
+
+void SimpleUpdateTracker::enable_copyrect(bool enable) {
+  if (!enable && copy_enabled) {
+    add_changed(copied);
+    copied.clear();
+  }
+  copy_enabled=enable;
+}
+
+void SimpleUpdateTracker::add_changed(const Region &region) {
+  changed.assign_union(region);
+}
+
+void SimpleUpdateTracker::add_copied(const Region &dest, const Point &delta) {
+  // Do we support copyrect?
+  if (!copy_enabled) {
+    add_changed(dest);
+    return;
+  }
+
+  // Is there anything to do?
+  if (dest.is_empty()) return;
+
+  // Calculate whether any of this copy can be treated as a continuation
+  // of an earlier one
+  Region src = dest;
+  src.translate(delta.negate());
+  Region overlap = src.intersect(copied);
+
+  if (overlap.is_empty()) {
+    // There is no overlap
+
+    Rect newbr = dest.get_bounding_rect();
+    Rect oldbr = copied.get_bounding_rect();
+    if (oldbr.area() > newbr.area()) {
+      // Old copyrect is (probably) bigger - use it
+      changed.assign_union(dest);
+    } else {
+      // New copyrect is probably bigger
+      // Use the new one
+      // But be careful not to copy stuff that still needs
+      // to be updated.
+      Region invalid_src = src.intersect(changed);
+      invalid_src.translate(delta);
+      changed.assign_union(invalid_src);
+      changed.assign_union(copied);
+      copied = dest;
+      copy_delta = delta;
+    }
+    return;
+  }
+
+  Region invalid_src = overlap.intersect(changed);
+  invalid_src.translate(delta);
+  changed.assign_union(invalid_src);
+  
+  overlap.translate(delta);
+
+  Region nonoverlapped_copied = dest.union_(copied).subtract(overlap);
+  changed.assign_union(nonoverlapped_copied);
+
+  copied = overlap;
+  copy_delta = copy_delta.translate(delta);
+
+  return;
+}
+
+void SimpleUpdateTracker::subtract(const Region& region) {
+  copied.assign_subtract(region);
+  changed.assign_subtract(region);
+}
+
+void SimpleUpdateTracker::get_update(UpdateInfo* info, const Region& clip)
+{
+  copied.assign_subtract(changed);
+  info->changed = changed.intersect(clip);
+  info->copied = copied.intersect(clip);
+  info->copy_delta = copy_delta;
+}
+
+void SimpleUpdateTracker::flush_update(UpdateTracker &info,
+                                       const Region &cliprgn)
+{
+  Region copied_clipped = copied.intersect(cliprgn);
+  Region changed_clipped = changed.intersect(cliprgn);
+  copied.assign_subtract(copied_clipped);
+  changed.assign_subtract(changed_clipped);
+  if (!copied_clipped.is_empty()) {
+    info.add_copied(copied_clipped, copy_delta);
+  }
+  if (!changed_clipped.is_empty())
+    info.add_changed(changed_clipped);
+}
+
+void SimpleUpdateTracker::get_update(UpdateTracker &to) const {
+  if (!copied.is_empty()) {
+    to.add_copied(copied, copy_delta);
+  }
+  if (!changed.is_empty()) {
+    to.add_changed(changed);
+  }
+}
diff --git a/rfb/UpdateTracker.h b/rfb/UpdateTracker.h
new file mode 100644
index 0000000..5015a25
--- /dev/null
+++ b/rfb/UpdateTracker.h
@@ -0,0 +1,107 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#ifndef __RFB_UPDATETRACKER_INCLUDED__
+#define __RFB_UPDATETRACKER_INCLUDED__
+
+#include <rfb/Rect.h>
+#include <rfb/Region.h>
+#include <rfb/PixelBuffer.h>
+
+namespace rfb {
+
+  class UpdateInfo {
+  public:
+    Region changed;
+    Region copied;
+    Point copy_delta;
+    bool is_empty() const {
+      return copied.is_empty() && changed.is_empty();
+    }
+    int numRects() const {
+      return copied.numRects() + changed.numRects();
+    }
+  };
+
+  class UpdateTracker {
+  public:
+    UpdateTracker() {};
+    virtual ~UpdateTracker() {};
+
+    virtual void add_changed(const Region &region) = 0;
+    virtual void add_copied(const Region &dest, const Point &delta) = 0;
+  };
+
+  class ClippedUpdateTracker : public UpdateTracker {
+  public:
+    ClippedUpdateTracker(UpdateTracker &child_) : child(child_) {};
+    ClippedUpdateTracker(UpdateTracker &child_,
+      const Region &cliprgn_) : child(child_), cliprgn(cliprgn_) {};
+    virtual ~ClippedUpdateTracker() {};
+
+    virtual void set_clip_region(const Region cliprgn_) {cliprgn = cliprgn_;};
+
+    virtual void add_changed(const Region &region);
+    virtual void add_copied(const Region &dest, const Point &delta);
+  protected:
+    UpdateTracker &child;
+    Region cliprgn;
+  };
+
+  class SimpleUpdateTracker : public UpdateTracker {
+  public:
+    SimpleUpdateTracker(bool use_copyrect=false);
+    virtual ~SimpleUpdateTracker();
+
+    virtual void enable_copyrect(bool enable);
+
+    virtual void add_changed(const Region &region);
+    virtual void add_copied(const Region &dest, const Point &delta);
+    virtual void subtract(const Region& region);
+
+    // Fill the supplied UpdateInfo structure with update information
+    virtual void get_update(UpdateInfo* info, const Region& cliprgn);
+
+    // Pass the current updates to the supplied tracker
+    virtual void get_update(UpdateTracker &to) const;
+
+    // Also removes the updates that are returned from this update tracker
+    virtual void flush_update(UpdateTracker &to, const Region &cliprgn);
+
+
+    // Get the changed/copied regions
+    const Region& get_changed() const {return changed;}
+    const Region& get_copied() const {return copied;}
+    const Point& get_delta() const {return copy_delta;}
+
+    // Move the entire update region by an offset
+    void translate(const Point& p) {changed.translate(p); copied.translate(p);}
+
+    virtual bool is_empty() const {return changed.is_empty() && copied.is_empty();}
+
+    virtual void clear() {changed.clear(); copied.clear();};
+  protected:
+    Region changed;
+    Region copied;
+    Point copy_delta;
+    bool copy_enabled;
+  };
+
+}
+
+#endif // __RFB_UPDATETRACKER_INCLUDED__
diff --git a/rfb/UserPasswdGetter.h b/rfb/UserPasswdGetter.h
new file mode 100644
index 0000000..c242ed0
--- /dev/null
+++ b/rfb/UserPasswdGetter.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_USERPASSWDGETTER_H__
+#define __RFB_USERPASSWDGETTER_H__
+namespace rfb {
+  class UserPasswdGetter {
+  public:
+    // getUserPasswd gets the username and password.  This might
+    // involve a dialog, getpass(), etc.  The user buffer pointer
+    // can be null, in which case no user name will be retrieved.
+    // The caller MUST delete [] the result(s) iff the
+    // call succeeds (returns true), and ignore them if failed.
+    virtual bool getUserPasswd(char** user, char** password)=0;
+  };
+}
+#endif
diff --git a/rfb/VNCSConnectionST.cxx b/rfb/VNCSConnectionST.cxx
new file mode 100644
index 0000000..3ae378e
--- /dev/null
+++ b/rfb/VNCSConnectionST.cxx
@@ -0,0 +1,629 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#include <rfb/VNCSConnectionST.h>
+#include <rfb/LogWriter.h>
+#include <rfb/secTypes.h>
+#include <rfb/ServerCore.h>
+#include <rfb/ComparingUpdateTracker.h>
+#define XK_MISCELLANY
+#define XK_XKB_KEYS
+#include <rfb/keysymdef.h>
+
+using namespace rfb;
+
+static LogWriter vlog("VNCSConnST");
+
+VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
+                                   bool reverse)
+  : sock(s), reverseConnection(reverse), server(server_),
+    image_getter(server->useEconomicTranslate),
+    drawRenderedCursor(false), removeRenderedCursor(false),
+    pointerEventTime(0), accessRights(AccessDefault)
+{
+  setStreams(&sock->inStream(), &sock->outStream());
+  peerEndpoint.buf = sock->getPeerEndpoint();
+  VNCServerST::connectionsLog.write(1,"accepted: %s", peerEndpoint.buf);
+
+  setSocketTimeouts();
+  lastEventTime = time(0);
+
+  // Initialise security
+  CharArray sec_types_str;
+  if (reverseConnection)
+    sec_types_str.buf = rfb::Server::rev_sec_types.getData();
+  else
+    sec_types_str.buf = rfb::Server::sec_types.getData();
+  std::list<int> sec_types = parseSecTypes(sec_types_str.buf);
+  std::list<int>::iterator i;
+  for (i=sec_types.begin(); i!=sec_types.end(); i++) {
+    addSecType(*i);
+  }
+
+  server->clients.push_front(this);
+}
+
+
+VNCSConnectionST::~VNCSConnectionST()
+{
+  // If we reach here then VNCServerST is deleting us!
+  VNCServerST::connectionsLog.write(1,"closed: %s (%s)",
+                                    peerEndpoint.buf, closeReason.buf);
+
+  // Release any keys the client still had pressed
+  std::set<rdr::U32>::iterator i;
+  for (i=pressedKeys.begin(); i!=pressedKeys.end(); i++)
+    server->desktop->keyEvent(*i, false);
+  if (server->pointerClient == this)
+    server->pointerClient = 0;
+
+  // Remove this client from the server
+  server->clients.remove(this);
+}
+
+
+// Methods called from VNCServerST
+
+bool VNCSConnectionST::init()
+{
+  try {
+    initialiseProtocol();
+  } catch (rdr::Exception& e) {
+    close(e.str());
+    return false;
+  }
+  return true;
+}
+
+void VNCSConnectionST::close(const char* reason)
+{
+  // Log the reason for the close
+  if (!closeReason.buf)
+    closeReason.buf = strDup(reason);
+  else
+    vlog.debug("second close: %s (%s)", peerEndpoint.buf, reason);
+
+  // Just shutdown the socket.  This will cause processMessages to
+  // eventually fail, causing us and our socket to be deleted.
+  sock->shutdown();
+  setState(RFBSTATE_CLOSING);
+}
+
+
+bool VNCSConnectionST::processMessages()
+{
+  if (state() == RFBSTATE_CLOSING) return false;
+  try {
+    // - Now set appropriate socket timeouts and process data
+    setSocketTimeouts();
+    bool clientsReadyBefore = server->clientsReadyForUpdate();
+
+    while (getInStream()->checkNoWait(1)) {
+      processMsg();
+    }
+
+    if (!clientsReadyBefore && !requested.is_empty())
+      server->desktop->framebufferUpdateRequest();
+
+    return true;
+
+  } catch (rdr::EndOfStream&) {
+    close("Clean disconnection");
+  } catch (rdr::Exception &e) {
+    close(e.str());
+  }
+  return false;
+}
+
+void VNCSConnectionST::writeFramebufferUpdateOrClose()
+{
+  try {
+    writeFramebufferUpdate();
+  } catch(rdr::Exception &e) {
+    close(e.str());
+  }
+}
+
+void VNCSConnectionST::pixelBufferChange()
+{
+  try {
+    if (!authenticated()) return;
+    if (cp.width && cp.height && (server->pb->width() != cp.width ||
+                                  server->pb->height() != cp.height))
+    {
+      // We need to clip the next update to the new size, but also add any
+      // extra bits if it's bigger.  If we wanted to do this exactly, something
+      // like the code below would do it, but at the moment we just update the
+      // entire new size.  However, we do need to clip the renderedCursorRect
+      // because that might be added to updates in writeFramebufferUpdate().
+
+      //updates.intersect(server->pb->getRect());
+      //
+      //if (server->pb->width() > cp.width)
+      //  updates.add_changed(Rect(cp.width, 0, server->pb->width(),
+      //                           server->pb->height()));
+      //if (server->pb->height() > cp.height)
+      //  updates.add_changed(Rect(0, cp.height, cp.width,
+      //                           server->pb->height()));
+
+      renderedCursorRect = renderedCursorRect.intersect(server->pb->getRect());
+
+      cp.width = server->pb->width();
+      cp.height = server->pb->height();
+      if (!writer()->writeSetDesktopSize()) {
+        close("Client does not support desktop resize");
+        return;
+      }
+    }
+    // Just update the whole screen at the moment because we're too lazy to
+    // work out what's actually changed.
+    updates.clear();
+    updates.add_changed(server->pb->getRect());
+    vlog.debug("pixel buffer changed - re-initialising image getter");
+    image_getter.init(server->pb, cp.pf(), writer());
+    if (writer()->needFakeUpdate())
+      writeFramebufferUpdate();
+  } catch(rdr::Exception &e) {
+    close(e.str());
+  }
+}
+
+void VNCSConnectionST::setColourMapEntriesOrClose(int firstColour,int nColours)
+{
+  try {
+    setColourMapEntries(firstColour, nColours);
+  } catch(rdr::Exception& e) {
+    close(e.str());
+  }
+}
+
+void VNCSConnectionST::bell()
+{
+  try {
+    if (state() == RFBSTATE_NORMAL) writer()->writeBell();
+  } catch(rdr::Exception& e) {
+    close(e.str());
+  }
+}
+
+void VNCSConnectionST::serverCutText(const char *str, int len)
+{
+  try {
+    if (!(accessRights & AccessCutText)) return;
+    if (!rfb::Server::sendCutText) return;
+    if (state() == RFBSTATE_NORMAL)
+      writer()->writeServerCutText(str, len);
+  } catch(rdr::Exception& e) {
+    close(e.str());
+  }
+}
+
+void VNCSConnectionST::setCursorOrClose()
+{
+  try {
+    setCursor();
+  } catch(rdr::Exception& e) {
+    close(e.str());
+  }
+}
+
+
+int VNCSConnectionST::checkIdleTimeout()
+{
+  int idleTimeout = rfb::Server::idleTimeout;
+  if (idleTimeout == 0) return 0;
+  if (state() != RFBSTATE_NORMAL && idleTimeout < 15)
+    idleTimeout = 15; // minimum of 15 seconds while authenticating
+  time_t now = time(0);
+  if (now < lastEventTime) {
+    // Someone must have set the time backwards.  Set lastEventTime so that the
+    // idleTimeout will count from now.
+    vlog.info("Time has gone backwards - resetting idle timeout");
+    lastEventTime = now;
+  }
+  int timeLeft = lastEventTime + idleTimeout - now;
+  if (timeLeft < -60) {
+    // Our callback is over a minute late - someone must have set the time
+    // forwards.  Set lastEventTime so that the idleTimeout will count from
+    // now.
+    vlog.info("Time has gone forwards - resetting idle timeout");
+    lastEventTime = now;
+    return idleTimeout;
+  }
+  if (timeLeft <= 0) {
+    close("Idle timeout");
+    return 0;
+  }
+  return timeLeft * 1000;
+}
+
+// renderedCursorChange() is called whenever the server-side rendered cursor
+// changes shape or position.  It ensures that the next update will clean up
+// the old rendered cursor and if necessary draw the new rendered cursor.
+
+void VNCSConnectionST::renderedCursorChange()
+{
+  if (state() != RFBSTATE_NORMAL) return;
+  removeRenderedCursor = true;
+  if (needRenderedCursor())
+    drawRenderedCursor = true;
+}
+
+// needRenderedCursor() returns true if this client needs the server-side
+// rendered cursor.  This may be because it does not support local cursor or
+// because the current cursor position has not been set by this client.
+// Unfortunately we can't know for sure when the current cursor position has
+// been set by this client.  We guess that this is the case when the current
+// cursor position is the same as the last pointer event from this client, or
+// if it is a very short time since this client's last pointer event (up to a
+// second).  [ Ideally we should do finer-grained timing here and make the time
+// configurable, but I don't think it's that important. ]
+
+bool VNCSConnectionST::needRenderedCursor()
+{
+  return (state() == RFBSTATE_NORMAL
+          && (!cp.supportsLocalCursor
+              || (!server->cursorPos.equals(pointerEventPos) &&
+                  (time(0) - pointerEventTime) > 0)));
+}
+
+
+void VNCSConnectionST::approveConnectionOrClose(bool accept,
+                                                const char* reason)
+{
+  try {
+    approveConnection(accept, reason);
+  } catch (rdr::Exception& e) {
+    close(e.str());
+  }
+}
+
+
+
+// -=- Callbacks from SConnection
+
+void VNCSConnectionST::versionReceived() {
+  CharArray address(sock->getPeerAddress());
+  if ((rfb::Server::blacklistLevel == 1) &&
+      server->blHosts->isBlackmarked(address.buf)) {
+    server->connectionsLog.error("blacklisted: %s", address.buf);
+    throwConnFailedException("Too many security failures");
+  }
+}
+
+SSecurity* VNCSConnectionST::getSSecurity(int secType) {
+  if (!server->securityFactory)
+    throw rdr::Exception("no SSecurityFactory registered!");
+  return server->securityFactory->getSSecurity(secType, reverseConnection);
+}
+
+void VNCSConnectionST::authSuccess()
+{
+  lastEventTime = time(0);
+
+  // - Authentication succeeded - clear from blacklist
+  CharArray name; name.buf = sock->getPeerAddress();
+  server->blHosts->clearBlackmark(name.buf);
+
+  server->startDesktop();
+
+  // - Set the connection parameters appropriately
+  cp.width = server->pb->width();
+  cp.height = server->pb->height();
+  cp.setName(server->getName());
+  
+  // - Set the default pixel format
+  cp.setPF(server->pb->getPF());
+  char buffer[256];
+  cp.pf().print(buffer, 256);
+  vlog.info("Server default pixel format %s", buffer);
+  image_getter.init(server->pb, cp.pf(), 0);
+
+  // - Mark the entire display as "dirty"
+  updates.add_changed(server->pb->getRect());
+}
+
+void VNCSConnectionST::queryConnection(const char* userName)
+{
+  // - Does the client have the right to bypass the query?
+  if (reverseConnection || !rfb::Server::queryConnect ||
+      (accessRights & AccessNoQuery))
+  {
+    approveConnection(true);
+    return;
+  }
+
+  CharArray reason;
+  VNCServerST::queryResult qr = server->queryConnection(sock, userName,
+                                                        &reason.buf);
+  if (qr == VNCServerST::PENDING) return;
+  approveConnection(qr == VNCServerST::ACCEPT, reason.buf);
+}
+
+void VNCSConnectionST::clientInit(bool shared)
+{
+  lastEventTime = time(0);
+  if (rfb::Server::alwaysShared || reverseConnection) shared = true;
+  if (rfb::Server::neverShared) shared = false;
+  if (!shared) {
+    if (rfb::Server::disconnectClients) {
+      // - Close all the other connected clients
+      vlog.debug("non-shared connection - closing clients");
+      server->closeClients("Non-shared connection requested", getSock());
+    } else {
+      // - Refuse this connection if there are existing clients, in addition to this one
+      if (server->authClientCount() > 1) {
+        close("Server is already in use");
+        return;
+      }
+    }
+  }
+  SConnection::clientInit(shared);
+}
+
+void VNCSConnectionST::setPixelFormat(const PixelFormat& pf)
+{
+  SConnection::setPixelFormat(pf);
+  char buffer[256];
+  pf.print(buffer, 256);
+  vlog.info("Client pixel format %s", buffer);
+  image_getter.init(server->pb, pf, writer());
+  setCursor();
+}
+
+void VNCSConnectionST::pointerEvent(int x, int y, int buttonMask)
+{
+  pointerEventTime = lastEventTime = time(0);
+  if (!(accessRights & AccessPtrEvents)) return;
+  if (!rfb::Server::acceptPointerEvents) return;
+  if (!server->pointerClient || server->pointerClient == this) {
+    pointerEventPos = Point(x, y);
+    if (buttonMask)
+      server->pointerClient = this;
+    else
+      server->pointerClient = 0;
+    server->desktop->pointerEvent(pointerEventPos, buttonMask);
+  }
+}
+
+
+class VNCSConnectionSTShiftPresser {
+public:
+  VNCSConnectionSTShiftPresser(SDesktop* desktop_)
+    : desktop(desktop_), pressed(false) {}
+  ~VNCSConnectionSTShiftPresser() {
+    if (pressed) { desktop->keyEvent(XK_Shift_L, false); }
+  }
+  void press() {
+    desktop->keyEvent(XK_Shift_L, true);
+    pressed = true;
+  }
+  SDesktop* desktop;
+  bool pressed;
+};
+
+// keyEvent() - record in the pressedKeys which keys were pressed.  Allow
+// multiple down events (for autorepeat), but only allow a single up event.
+void VNCSConnectionST::keyEvent(rdr::U32 key, bool down) {
+  lastEventTime = time(0);
+  if (!(accessRights & AccessKeyEvents)) return;
+  if (!rfb::Server::acceptKeyEvents) return;
+
+  // Turn ISO_Left_Tab into shifted Tab.
+  VNCSConnectionSTShiftPresser shiftPresser(server->desktop);
+  if (key == XK_ISO_Left_Tab) {
+    if (pressedKeys.find(XK_Shift_L) == pressedKeys.end() &&
+        pressedKeys.find(XK_Shift_R) == pressedKeys.end())
+      shiftPresser.press();
+    key = XK_Tab;
+  }
+
+  if (down) {
+    pressedKeys.insert(key);
+  } else {
+    if (!pressedKeys.erase(key)) return;
+  }
+  server->desktop->keyEvent(key, down);
+}
+
+void VNCSConnectionST::clientCutText(const char* str, int len)
+{
+  if (!(accessRights & AccessCutText)) return;
+  if (!rfb::Server::acceptCutText) return;
+  server->desktop->clientCutText(str, len);
+}
+
+void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
+{
+  if (!(accessRights & AccessView)) return;
+
+  SConnection::framebufferUpdateRequest(r, incremental);
+
+  Region reqRgn(r);
+  requested.assign_union(reqRgn);
+
+  if (!incremental) {
+    // Non-incremental update - treat as if area requested has changed
+    updates.add_changed(reqRgn);
+    server->comparer->add_changed(reqRgn);
+  }
+
+  writeFramebufferUpdate();
+}
+
+void VNCSConnectionST::setInitialColourMap()
+{
+  setColourMapEntries(0, 0);
+}
+
+// supportsLocalCursor() is called whenever the status of
+// cp.supportsLocalCursor has changed.  If the client does now support local
+// cursor, we make sure that the old server-side rendered cursor is cleaned up
+// and the cursor is sent to the client.
+
+void VNCSConnectionST::supportsLocalCursor()
+{
+  if (cp.supportsLocalCursor) {
+    removeRenderedCursor = true;
+    drawRenderedCursor = false;
+    setCursor();
+  }
+}
+
+void VNCSConnectionST::writeSetCursorCallback()
+{
+  rdr::U8* transData = writer()->getImageBuf(server->cursor.area());
+  image_getter.translatePixels(server->cursor.data, transData,
+                               server->cursor.area());
+
+  writer()->writeSetCursor(server->cursor.width(),
+                           server->cursor.height(),
+                           server->cursor.hotspot.x,
+                           server->cursor.hotspot.y,
+                           transData, server->cursor.mask.buf);
+}
+
+
+void VNCSConnectionST::writeFramebufferUpdate()
+{
+  if (state() != RFBSTATE_NORMAL || requested.is_empty()) return;
+
+  server->checkUpdate();
+
+  // If the previous position of the rendered cursor overlaps the source of the
+  // copy, then when the copy happens the corresponding rectangle in the
+  // destination will be wrong, so add it to the changed region.
+
+  if (!updates.get_copied().is_empty() && !renderedCursorRect.is_empty()) {
+    Rect bogusCopiedCursor = (renderedCursorRect.translate(updates.get_delta())
+                              .intersect(server->pb->getRect()));
+    if (!updates.get_copied().intersect(bogusCopiedCursor).is_empty()) {
+      updates.add_changed(bogusCopiedCursor);
+    }
+  }
+
+  // If we need to remove the old rendered cursor, just add the rectangle to
+  // the changed region.
+
+  if (removeRenderedCursor) {
+    updates.add_changed(renderedCursorRect);
+    renderedCursorRect.clear();
+    removeRenderedCursor = false;
+  }
+
+  // Return if there is nothing to send the client.
+
+  if (updates.is_empty() && !writer()->needFakeUpdate() && !drawRenderedCursor)
+    return;
+
+  // If the client needs a server-side rendered cursor, work out the cursor
+  // rectangle.  If it's empty then don't bother drawing it, but if it overlaps
+  // with the update region, we need to draw the rendered cursor regardless of
+  // whether it has changed.
+
+  if (needRenderedCursor()) {
+    renderedCursorRect
+      = (server->renderedCursor.getRect(server->renderedCursorTL)
+         .intersect(requested.get_bounding_rect()));
+
+    if (renderedCursorRect.is_empty()) {
+      drawRenderedCursor = false;
+    } else if (!updates.get_changed().union_(updates.get_copied())
+        .intersect(renderedCursorRect).is_empty()) {
+      drawRenderedCursor = true;
+    }
+
+    // We could remove the new cursor rect from updates here.  It's not clear
+    // whether this is worth it.  If we do remove it, then we won't draw over
+    // the same bit of screen twice, but we have the overhead of a more complex
+    // region.
+
+    //if (drawRenderedCursor)
+    //  updates.subtract(renderedCursorRect);
+  }
+
+  UpdateInfo update;
+  updates.enable_copyrect(cp.useCopyRect);
+  updates.get_update(&update, requested);
+  if (!update.is_empty() || writer()->needFakeUpdate() || drawRenderedCursor) {
+    int nRects = update.numRects() + (drawRenderedCursor ? 1 : 0);
+    writer()->writeFramebufferUpdateStart(nRects);
+    Region updatedRegion;
+    writer()->writeRects(update, &image_getter, &updatedRegion);
+    updates.subtract(updatedRegion);
+    if (drawRenderedCursor)
+      writeRenderedCursorRect();
+    writer()->writeFramebufferUpdateEnd();
+    requested.clear();
+  }
+}
+
+
+// writeRenderedCursorRect() writes a single rectangle drawing the rendered
+// cursor on the client.
+
+void VNCSConnectionST::writeRenderedCursorRect()
+{
+  image_getter.setPixelBuffer(&server->renderedCursor);
+  image_getter.setOffset(server->renderedCursorTL);
+
+  Rect actual;
+  writer()->writeRect(renderedCursorRect, &image_getter, &actual);
+
+  image_getter.setPixelBuffer(server->pb);
+  image_getter.setOffset(Point(0,0));
+
+  drawRenderedCursor = false;
+}
+
+void VNCSConnectionST::setColourMapEntries(int firstColour, int nColours)
+{
+  if (!readyForSetColourMapEntries) return;
+  if (server->pb->getPF().trueColour) return;
+
+  image_getter.setColourMapEntries(firstColour, nColours, writer());
+
+  if (cp.pf().trueColour) {
+    updates.add_changed(server->pb->getRect());
+  }
+}
+
+
+// setCursor() is called whenever the cursor has changed shape or pixel format.
+// If the client supports local cursor then it will arrange for the cursor to
+// be sent to the client.
+
+void VNCSConnectionST::setCursor()
+{
+  if (state() != RFBSTATE_NORMAL || !cp.supportsLocalCursor) return;
+  writer()->cursorChange(this);
+  if (writer()->needFakeUpdate())
+    writeFramebufferUpdate();
+}
+
+void VNCSConnectionST::setSocketTimeouts()
+{
+  int timeoutms = rfb::Server::clientWaitTimeMillis;
+  if (timeoutms == 0 || timeoutms > rfb::Server::idleTimeout * 1000) {
+    timeoutms = rfb::Server::idleTimeout * 1000;
+    if (timeoutms == 0)
+      timeoutms = -1;
+  }
+  sock->inStream().setTimeout(timeoutms);
+  sock->outStream().setTimeout(timeoutms);
+}
diff --git a/rfb/VNCSConnectionST.h b/rfb/VNCSConnectionST.h
new file mode 100644
index 0000000..ba480e5
--- /dev/null
+++ b/rfb/VNCSConnectionST.h
@@ -0,0 +1,168 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+//
+// VNCSConnectionST is our derived class of SConnection for VNCServerST - there
+// is one for each connected client.  We think of VNCSConnectionST as part of
+// the VNCServerST implementation, so its methods are allowed full access to
+// members of VNCServerST.
+//
+
+#ifndef __RFB_VNCSCONNECTIONST_H__
+#define __RFB_VNCSCONNECTIONST_H__
+
+#include <set>
+#include <rfb/SConnection.h>
+#include <rfb/SMsgWriter.h>
+#include <rfb/TransImageGetter.h>
+#include <rfb/VNCServerST.h>
+
+namespace rfb {
+  class VNCSConnectionST : public SConnection,
+                           public WriteSetCursorCallback {
+  public:
+    VNCSConnectionST(VNCServerST* server_, network::Socket* s, bool reverse);
+    virtual ~VNCSConnectionST();
+
+    // Methods called from VNCServerST.  None of these methods ever knowingly
+    // throw an exception.
+
+    // Unless otherwise stated, the SConnectionST may not be valid after any of
+    // these methods are called, since they catch exceptions and may have
+    // called close() which deletes the object.
+
+    // init() must be called to initialise the protocol.  If it fails it
+    // returns false, and close() will have been called.
+    bool init();
+
+    // close() shuts down the socket to the client and deletes the
+    // SConnectionST object.
+    void close(const char* reason);
+
+    // processMessages() processes incoming messages from the client, invoking
+    // various callbacks as a result.  It continues to process messages until
+    // reading might block.  Returns true if the client is still valid &
+    // active, or false if it has disconnected or an error has occurred.
+    bool processMessages();
+
+    void writeFramebufferUpdateOrClose();
+    void pixelBufferChange();
+    void setColourMapEntriesOrClose(int firstColour, int nColours);
+    void bell();
+    void serverCutText(const char *str, int len);
+    void setCursorOrClose();
+
+    // checkIdleTimeout() returns the number of milliseconds left until the
+    // idle timeout expires.  If it has expired, the connection is closed and
+    // zero is returned.  Zero is also returned if there is no idle timeout.
+    int checkIdleTimeout();
+
+    // The following methods never throw exceptions nor do they ever delete the
+    // SConnectionST object.
+
+    // renderedCursorChange() is called whenever the server-side rendered
+    // cursor changes shape or position.  It ensures that the next update will
+    // clean up the old rendered cursor and if necessary draw the new rendered
+    // cursor.
+    void renderedCursorChange();
+
+    // needRenderedCursor() returns true if this client needs the server-side
+    // rendered cursor.  This may be because it does not support local cursor
+    // or because the current cursor position has not been set by this client.
+    bool needRenderedCursor();
+
+    network::Socket* getSock() { return sock; }
+    bool readyForUpdate() { return !requested.is_empty(); }
+    void add_changed(const Region& region) { updates.add_changed(region); }
+    void add_copied(const Region& dest, const Point& delta) {
+      updates.add_copied(dest, delta);
+    }
+
+    const char* getPeerEndpoint() const {return peerEndpoint.buf;}
+
+    // approveConnectionOrClose() is called some time after
+    // VNCServerST::queryConnection() has returned with PENDING to accept or
+    // reject the connection.  The accept argument should be true for
+    // acceptance, or false for rejection, in which case a string reason may
+    // also be given.
+
+    void approveConnectionOrClose(bool accept, const char* reason);
+
+  private:
+    // SConnection callbacks
+
+    // These methods are invoked as callbacks from processMsg().  Note that
+    // none of these methods should call any of the above methods which may
+    // delete the SConnectionST object.
+
+    virtual void versionReceived();
+    virtual SSecurity* getSSecurity(int secType);
+    virtual void authSuccess();
+    virtual void queryConnection(const char* userName);
+    virtual void clientInit(bool shared);
+    virtual void setPixelFormat(const PixelFormat& pf);
+    virtual void pointerEvent(int x, int y, int buttonMask);
+    virtual void keyEvent(rdr::U32 key, bool down);
+    virtual void framebufferUpdateRequest(const Rect& r, bool incremental);
+    virtual void clientCutText(const char* str, int len);
+    virtual void setInitialColourMap();
+    virtual void supportsLocalCursor();
+
+    // setAccessRights() allows a security package to limit the access rights
+    // of a VNCSConnectioST to the server.  These access rights are applied
+    // such that the actual rights granted are the minimum of the server's
+    // default access settings and the connection's access settings.
+    virtual void setAccessRights(AccessRights ar) {accessRights=ar;}
+
+    // WriteSetCursorCallback
+    virtual void writeSetCursorCallback();
+
+    // Internal methods
+
+    // writeFramebufferUpdate() attempts to write a framebuffer update to the
+    // client.
+
+    void writeFramebufferUpdate();
+
+    void writeRenderedCursorRect();
+    void setColourMapEntries(int firstColour, int nColours);
+    void setCursor();
+    void setSocketTimeouts();
+
+    network::Socket* sock;
+    CharArray peerEndpoint;
+    bool reverseConnection;
+    VNCServerST* server;
+    SimpleUpdateTracker updates;
+    TransImageGetter image_getter;
+    Region requested;
+    bool drawRenderedCursor, removeRenderedCursor;
+    Rect renderedCursorRect;
+
+    std::set<rdr::U32> pressedKeys;
+
+    time_t lastEventTime;
+    time_t pointerEventTime;
+    Point pointerEventPos;
+
+    AccessRights accessRights;
+
+    CharArray closeReason;
+  };
+}
+#endif
diff --git a/rfb/VNCServer.h b/rfb/VNCServer.h
new file mode 100644
index 0000000..e80044c
--- /dev/null
+++ b/rfb/VNCServer.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// VNCServer - abstract interface implemented by the RFB library.  The back-end
+// code calls the relevant methods as appropriate.
+
+#ifndef __RFB_VNCSERVER_H__
+#define __RFB_VNCSERVER_H__
+
+#include <rfb/UpdateTracker.h>
+#include <rfb/SSecurity.h>
+
+namespace rfb {
+
+  class VNCServer : public UpdateTracker {
+  public:
+
+    // setPixelBuffer() tells the server to use the given pixel buffer.  If
+    // this differs in size from the previous pixel buffer, this may result in
+    // protocol messages being sent, or clients being disconnected.
+    virtual void setPixelBuffer(PixelBuffer* pb) = 0;
+
+    // setColourMapEntries() tells the server that some entries in the colour
+    // map have changed.  The server will retrieve them via the PixelBuffer's
+    // ColourMap object.  This may result in protocol messages being sent.
+    // If nColours is 0, this means the rest of the colour map.
+    virtual void setColourMapEntries(int firstColour=0, int nColours=0) = 0;
+
+    // serverCutText() tells the server that the cut text has changed.  This
+    // will normally be sent to all clients.
+    virtual void serverCutText(const char* str, int len) = 0;
+
+    // bell() tells the server that it should make all clients make a bell sound.
+    virtual void bell() = 0;
+
+    // clientsReadyForUpdate() returns true if there is at least one client
+    // waiting for an update, false if no clients are ready.
+    virtual bool clientsReadyForUpdate() = 0;
+
+    // - Close all currently-connected clients, by calling
+    //   their close() method with the supplied reason.
+    virtual void closeClients(const char* reason) = 0;
+
+    // tryUpdate() causes the server to attempt to send updates to any waiting
+    // clients.
+    virtual void tryUpdate() = 0;
+
+    // setCursor() tells the server that the cursor has changed.  The
+    // cursorData argument contains width*height pixel values in the pixel
+    // buffer's format.  The mask argument is a bitmask with a 1-bit meaning
+    // the corresponding pixel in cursorData is valid.  The mask consists of
+    // left-to-right, top-to-bottom scanlines, where each scanline is padded to
+    // a whole number of bytes [(width+7)/8].  Within each byte the most
+    // significant bit represents the leftmost pixel, and the bytes are simply
+    // in left-to-right order.  The server takes its own copy of the data in
+    // cursorData and mask.
+    virtual void setCursor(int width, int height, int hotspotX, int hotspotY,
+                           void* cursorData, void* mask) = 0;
+
+    // setCursorPos() tells the server the current position of the cursor.
+    virtual void setCursorPos(int x, int y) = 0;
+
+    // setSSecurityFactory() tells the server which factory to use when
+    // attempting to authenticate connections.
+    virtual void setSSecurityFactory(SSecurityFactory* f) = 0;
+
+    // setName() tells the server what desktop title to supply to clients
+    virtual void setName(const char* name) = 0;
+  };
+}
+#endif
diff --git a/rfb/VNCServerST.cxx b/rfb/VNCServerST.cxx
new file mode 100644
index 0000000..b3f9e88
--- /dev/null
+++ b/rfb/VNCServerST.cxx
@@ -0,0 +1,430 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- Single-Threaded VNC Server implementation
+
+
+// Note about how sockets get closed:
+//
+// Closing sockets to clients is non-trivial because the code which calls
+// VNCServerST must explicitly know about all the sockets (so that it can block
+// on them appropriately).  However, VNCServerST may want to close clients for
+// a number of reasons, and from a variety of entry points.  The simplest is
+// when processSocketEvent() is called for a client, and the remote end has
+// closed its socket.  A more complex reason is when processSocketEvent() is
+// called for a client which has just sent a ClientInit with the shared flag
+// set to false - in this case we want to close all other clients.  Yet another
+// reason for disconnecting clients is when the desktop size has changed as a
+// result of a call to setPixelBuffer().
+//
+// Because we don't want to mess up any data structures which the calling code
+// may maintain regarding sockets with data to process, we can't just delete a
+// socket when we decide to close the connection to a client.  Instead, we only
+// go as far as calling shutdown() on the socket.  This should ensure that
+// eventually the calling code will get round to calling processSocketEvent()
+// for that socket.  Then we can delete the VNCSConnectionST object and its
+// associated network::Socket object, and return false from that call to let
+// the calling code know that the socket has been deleted.  This is the only
+// way that these objects get deleted.
+//
+// It is possible that there are platforms where calling shutdown() cannot
+// guarantee that processSocketEvent() will be called - if so then it may be
+// necessary to introduce some kind of "socket closure callback", but we'll
+// only do that if it proves absolutely necessary.
+//
+// One minor complication is that we don't allocate a VNCSConnectionST object
+// for a blacklisted host (since we want to minimise the resources used for
+// dealing with such a connection).  So we maintain a separate list of
+// closingSockets for this purpose.
+
+
+#include <rfb/ServerCore.h>
+#include <rfb/VNCServerST.h>
+#include <rfb/VNCSConnectionST.h>
+#include <rfb/ComparingUpdateTracker.h>
+#include <rfb/SSecurityFactoryStandard.h>
+#include <rfb/util.h>
+
+#include <rdr/types.h>
+
+using namespace rfb;
+
+static LogWriter slog("VNCServerST");
+LogWriter VNCServerST::connectionsLog("Connections");
+static SSecurityFactoryStandard defaultSecurityFactory;
+
+//
+// -=- VNCServerST Implementation
+//
+
+// -=- Constructors/Destructor
+
+VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_,
+                         SSecurityFactory* sf)
+  : blHosts(&blacklist), desktop(desktop_), desktopStarted(false), pb(0),
+    name(strDup(name_)), pointerClient(0), comparer(0),
+    renderedCursorInvalid(false),
+    securityFactory(sf ? sf : &defaultSecurityFactory),
+    queryConnectionHandler(0), useEconomicTranslate(false)
+{
+  slog.debug("creating single-threaded server %s", name.buf);
+}
+
+VNCServerST::~VNCServerST()
+{
+  slog.debug("shutting down server %s", name.buf);
+
+  // Close any active clients, with appropriate logging & cleanup
+  closeClients("Server shutdown");
+
+  // Delete all the clients, and their sockets, and any closing sockets
+  //   NB: Deleting a client implicitly removes it from the clients list
+  while (!clients.empty()) {
+    delete clients.front()->getSock();
+    delete clients.front();
+  }
+  while (!closingSockets.empty()) {
+    delete closingSockets.front();
+    closingSockets.pop_front();
+  }
+
+  // Stop the desktop object if active, *only* after deleting all clients!
+  if (desktopStarted) {
+    desktopStarted = false;
+    desktop->stop();
+  }
+
+  delete comparer;
+}
+
+
+// SocketServer methods
+
+void VNCServerST::addClient(network::Socket* sock)
+{
+  addClient(sock, false);
+}
+
+void VNCServerST::addClient(network::Socket* sock, bool reverse)
+{
+  // - Check the connection isn't black-marked
+  // *** do this in getSecurity instead?
+  CharArray address(sock->getPeerAddress());
+  if ((rfb::Server::blacklistLevel == 2) &&
+      blHosts->isBlackmarked(address.buf)) {
+    connectionsLog.error("blacklisted: %s", address.buf);
+    try {
+      SConnection::writeConnFailedFromScratch("Too many security failures",
+                                              &sock->outStream());
+    } catch (rdr::Exception&) {
+    }
+    sock->shutdown();
+    closingSockets.push_back(sock);
+    return;
+  }
+
+  VNCSConnectionST* client = new VNCSConnectionST(this, sock, reverse);
+  client->init();
+}
+
+bool VNCServerST::processSocketEvent(network::Socket* sock)
+{
+  // - Find the appropriate VNCSConnectionST and process the event
+  std::list<VNCSConnectionST*>::iterator ci;
+  for (ci = clients.begin(); ci != clients.end(); ci++) {
+    if ((*ci)->getSock() == sock) {
+      if ((*ci)->processMessages())
+        return true;
+      // processMessages failed, so delete the client
+      delete *ci;
+      break;
+    }
+  }
+
+  // - If no client is using the Socket then delete it
+  closingSockets.remove(sock);
+  delete sock;
+
+  // - Check that the desktop object is still required
+  if (authClientCount() == 0 && desktopStarted) {
+    slog.debug("no authenticated clients - stopping desktop");
+    desktopStarted = false;
+    desktop->stop();
+  }
+  
+  // - Inform the caller not to continue handling the Socket
+  return false;
+}
+
+int VNCServerST::checkTimeouts()
+{
+  int timeout = 0;
+  std::list<VNCSConnectionST*>::iterator ci, ci_next;
+  for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
+    ci_next = ci; ci_next++;
+    soonestTimeout(&timeout, (*ci)->checkIdleTimeout());
+  }
+  return timeout;
+}
+
+
+// VNCServer methods
+
+void VNCServerST::setPixelBuffer(PixelBuffer* pb_)
+{
+  pb = pb_;
+  delete comparer;
+  comparer = 0;
+
+  if (pb) {
+    comparer = new ComparingUpdateTracker(pb);
+    cursor.setPF(pb->getPF());
+    renderedCursor.setPF(pb->getPF());
+
+    std::list<VNCSConnectionST*>::iterator ci, ci_next;
+    for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
+      ci_next = ci; ci_next++;
+      (*ci)->pixelBufferChange();
+    }
+  } else {
+    if (desktopStarted)
+      throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?");
+  }
+}
+
+void VNCServerST::setColourMapEntries(int firstColour, int nColours)
+{
+  std::list<VNCSConnectionST*>::iterator ci, ci_next;
+  for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+    ci_next = ci; ci_next++;
+    (*ci)->setColourMapEntriesOrClose(firstColour, nColours);
+  }
+}
+
+void VNCServerST::bell()
+{
+  std::list<VNCSConnectionST*>::iterator ci, ci_next;
+  for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+    ci_next = ci; ci_next++;
+    (*ci)->bell();
+  }
+}
+
+void VNCServerST::serverCutText(const char* str, int len)
+{
+  std::list<VNCSConnectionST*>::iterator ci, ci_next;
+  for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+    ci_next = ci; ci_next++;
+    (*ci)->serverCutText(str, len);
+  }
+}
+
+void VNCServerST::add_changed(const Region& region)
+{
+  comparer->add_changed(region);
+}
+
+void VNCServerST::add_copied(const Region& dest, const Point& delta)
+{
+  comparer->add_copied(dest, delta);
+}
+
+bool VNCServerST::clientsReadyForUpdate()
+{
+  std::list<VNCSConnectionST*>::iterator ci;
+  for (ci = clients.begin(); ci != clients.end(); ci++) {
+    if ((*ci)->readyForUpdate())
+      return true;
+  }
+  return false;
+}
+
+void VNCServerST::tryUpdate()
+{
+  std::list<VNCSConnectionST*>::iterator ci, ci_next;
+  for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+    ci_next = ci; ci_next++;
+    (*ci)->writeFramebufferUpdateOrClose();
+  }
+}
+
+void VNCServerST::setCursor(int width, int height, int newHotspotX,
+                            int newHotspotY, void* data, void* mask)
+{
+  cursor.hotspot.x = newHotspotX;
+  cursor.hotspot.y = newHotspotY;
+  cursor.setSize(width, height);
+  memcpy(cursor.data, data, cursor.dataLen());
+  memcpy(cursor.mask.buf, mask, cursor.maskLen());
+
+  cursor.crop();
+
+  renderedCursorInvalid = true;
+
+  std::list<VNCSConnectionST*>::iterator ci, ci_next;
+  for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+    ci_next = ci; ci_next++;
+    (*ci)->renderedCursorChange();
+    (*ci)->setCursorOrClose();
+  }
+}
+
+void VNCServerST::setCursorPos(int x, int y)
+{
+  if (cursorPos.x != x || cursorPos.y != y) {
+    cursorPos.x = x;
+    cursorPos.y = y;
+    renderedCursorInvalid = true;
+    std::list<VNCSConnectionST*>::iterator ci;
+    for (ci = clients.begin(); ci != clients.end(); ci++)
+      (*ci)->renderedCursorChange();
+  }
+}
+
+// Other public methods
+
+void VNCServerST::approveConnection(network::Socket* sock, bool accept,
+                                    const char* reason)
+{
+  std::list<VNCSConnectionST*>::iterator ci;
+  for (ci = clients.begin(); ci != clients.end(); ci++) {
+    if ((*ci)->getSock() == sock) {
+      (*ci)->approveConnectionOrClose(accept, reason);
+      return;
+    }
+  }
+}
+
+void VNCServerST::closeClients(const char* reason, network::Socket* except)
+{
+  std::list<VNCSConnectionST*>::iterator i, next_i;
+  for (i=clients.begin(); i!=clients.end(); i=next_i) {
+    next_i = i; next_i++;
+    if ((*i)->getSock() != except)
+      (*i)->close(reason);
+  }
+}
+
+void VNCServerST::getSockets(std::list<network::Socket*>* sockets)
+{
+  sockets->clear();
+  std::list<VNCSConnectionST*>::iterator ci;
+  for (ci = clients.begin(); ci != clients.end(); ci++) {
+    sockets->push_back((*ci)->getSock());
+  }
+  std::list<network::Socket*>::iterator si;
+  for (si = closingSockets.begin(); si != closingSockets.end(); si++) {
+    sockets->push_back(*si);
+  }
+}
+
+SConnection* VNCServerST::getSConnection(network::Socket* sock) {
+  std::list<VNCSConnectionST*>::iterator ci;
+  for (ci = clients.begin(); ci != clients.end(); ci++) {
+    if ((*ci)->getSock() == sock)
+      return *ci;
+  }
+  return 0;
+}
+
+
+// -=- Internal methods
+
+void VNCServerST::startDesktop()
+{
+  if (!desktopStarted) {
+    slog.debug("starting desktop");
+    desktop->start(this);
+    desktopStarted = true;
+    if (!pb)
+      throw Exception("SDesktop::start() did not set a valid PixelBuffer");
+  }
+}
+
+int VNCServerST::authClientCount() {
+  int count = 0;
+  std::list<VNCSConnectionST*>::iterator ci;
+  for (ci = clients.begin(); ci != clients.end(); ci++) {
+    if ((*ci)->authenticated())
+      count++;
+  }
+  return count;
+}
+
+inline bool VNCServerST::needRenderedCursor()
+{
+  std::list<VNCSConnectionST*>::iterator ci;
+  for (ci = clients.begin(); ci != clients.end(); ci++)
+    if ((*ci)->needRenderedCursor()) return true;
+  return false;
+}
+
+// checkUpdate() is called just before sending an update.  It checks to see
+// what updates are pending and propagates them to the update tracker for each
+// client.  It uses the ComparingUpdateTracker's compare() method to filter out
+// areas of the screen which haven't actually changed.  It also checks the
+// state of the (server-side) rendered cursor, if necessary rendering it again
+// with the correct background.
+
+void VNCServerST::checkUpdate()
+{
+  bool renderCursor = needRenderedCursor();
+
+  if (comparer->is_empty() && !(renderCursor && renderedCursorInvalid))
+    return;
+
+  Region toCheck = comparer->get_changed().union_(comparer->get_copied());
+
+  if (renderCursor) {
+    Rect clippedCursorRect
+      = cursor.getRect(cursorTL()).intersect(pb->getRect());
+
+    if (!renderedCursorInvalid && (toCheck.intersect(clippedCursorRect)
+                                   .is_empty())) {
+      renderCursor = false;
+    } else {
+      renderedCursorTL = clippedCursorRect.tl;
+      renderedCursor.setSize(clippedCursorRect.width(),
+                             clippedCursorRect.height());
+      toCheck.assign_union(clippedCursorRect);
+    }
+  }
+
+  pb->grabRegion(toCheck);
+
+  if (rfb::Server::compareFB)
+    comparer->compare();
+
+  if (renderCursor) {
+    pb->getImage(renderedCursor.data,
+                 renderedCursor.getRect(renderedCursorTL));
+    renderedCursor.maskRect(cursor.getRect(cursorTL()
+                                           .subtract(renderedCursorTL)),
+                            cursor.data, cursor.mask.buf);
+    renderedCursorInvalid = false;
+  }
+
+  std::list<VNCSConnectionST*>::iterator ci, ci_next;
+  for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
+    ci_next = ci; ci_next++;
+    (*ci)->add_copied(comparer->get_copied(), comparer->get_delta());
+    (*ci)->add_changed(comparer->get_changed());
+  }
+
+  comparer->clear();
+}
diff --git a/rfb/VNCServerST.h b/rfb/VNCServerST.h
new file mode 100644
index 0000000..6655a0d
--- /dev/null
+++ b/rfb/VNCServerST.h
@@ -0,0 +1,233 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- VNCServerST.h
+
+// Single-threaded VNCServer implementation
+
+#ifndef __RFB_VNCSERVERST_H__
+#define __RFB_VNCSERVERST_H__
+
+#include <list>
+
+#include <rfb/SDesktop.h>
+#include <rfb/VNCServer.h>
+#include <rfb/Configuration.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Blacklist.h>
+#include <rfb/Cursor.h>
+#include <network/Socket.h>
+
+namespace rfb {
+
+  class VNCSConnectionST;
+  class ComparingUpdateTracker;
+  class PixelBuffer;
+
+  class VNCServerST : public VNCServer, public network::SocketServer {
+  public:
+    // -=- Constructors
+
+    //   Create a server exporting the supplied desktop.
+    VNCServerST(const char* name_, SDesktop* desktop_,
+                SSecurityFactory* securityFactory_=0);
+    virtual ~VNCServerST();
+
+
+    // Methods overridden from SocketServer
+
+    // - Run a client connection on the supplied socket
+    //   This causes the server to allocate the required structures
+    //   to handle a client connection, and to initialise the RFB
+    //   protocol.
+    //   NB:  The server assumes ownership of the Socket object.
+
+    virtual void addClient(network::Socket* sock);
+
+    // - Process an input event on a particular Socket
+    //   The platform-specific side of the server implementation calls
+    //   this method whenever data arrives on one of the active
+    //   network sockets.
+    //   The method returns true if the Socket is still in use by the
+    //   server, or false if it is no longer required and has been
+    //   deleted.
+    //   NB:  If false is returned then the Socket is deleted and must
+    //   not be accessed again!
+
+    virtual bool processSocketEvent(network::Socket* sock);
+
+    // - checkTimeouts() returns the number of milliseconds left until the next
+    //   idle timeout expires.  If any have already expired, the corresponding
+    //   connections are closed.  Zero is returned if there is no idle timeout.
+
+    virtual int checkTimeouts();
+
+
+    // Methods overridden from VNCServer
+
+    virtual void setPixelBuffer(PixelBuffer* pb);
+    virtual void setColourMapEntries(int firstColour=0, int nColours=0);
+    virtual void serverCutText(const char* str, int len);
+    virtual void add_changed(const Region &region);
+    virtual void add_copied(const Region &dest, const Point &delta);
+    virtual bool clientsReadyForUpdate();
+    virtual void tryUpdate();
+    virtual void setCursor(int width, int height, int hotspotX, int hotspotY,
+                           void* cursorData, void* mask);
+    virtual void setCursorPos(int x, int y);
+    virtual void setSSecurityFactory(SSecurityFactory* f) {securityFactory=f;}
+
+    virtual void bell();
+
+    // - Close all currently-connected clients, by calling
+    //   their close() method with the supplied reason.
+    virtual void closeClients(const char* reason) {closeClients(reason, 0);}
+
+    // VNCServerST-only methods
+
+    //   If a particular VNCSConnectionST* is specified then
+    //   that connection will NOT be closed.
+    void closeClients(const char* reason, network::Socket* sock);
+
+    // addClient() with an extra flag to say if this is a reverse connection to
+    // a listening client.  Reverse connections are not authenticated and are
+    // always shared (unless the NeverShared parameter is set).
+
+    void addClient(network::Socket* sock, bool reverse);
+
+
+    // getSockets() gets a list of sockets.  This can be used to generate an
+    // fd_set for calling select().
+
+    void getSockets(std::list<network::Socket*>* sockets);
+
+    // getSConnection() gets the SConnection for a particular Socket.  If
+    // the Socket is not recognised then null is returned.
+
+    SConnection* getSConnection(network::Socket* sock);
+
+    // getDesktopSize() returns the size of the SDesktop exported by this
+    // server.
+    Point getDesktopSize() const {return desktop->getFbSize();}
+
+    // getName() returns the name of this VNC Server.  NB: The value returned
+    // is the server's internal buffer which may change after any other methods
+    // are called - take a copy if necessary.
+    const char* getName() const {return name.buf;}
+
+    // setName() specifies the desktop name that the server should provide to
+    // clients
+    void setName(const char* name_) {name.replaceBuf(strDup(name_));}
+
+    // A QueryConnectionHandler, if supplied, is passed details of incoming
+    // connections to approve, reject, or query the user about.
+    //
+    // queryConnection() is called when a connection has been
+    // successfully authenticated.  The sock and userName arguments identify
+    // the socket and the name of the authenticated user, if any.  It should
+    // return ACCEPT if the connection should be accepted, REJECT if it should
+    // be rejected, or PENDING if a decision cannot yet be reached.  If REJECT
+    // is returned, *reason can be set to a string describing the reason - this
+    // will be delete[]ed when it is finished with.  If PENDING is returned,
+    // approveConnection() must be called some time later to accept or reject
+    // the connection.
+    enum queryResult { ACCEPT, REJECT, PENDING };
+    struct QueryConnectionHandler {
+      virtual ~QueryConnectionHandler() {}
+      virtual queryResult queryConnection(network::Socket* sock,
+                                          const char* userName,
+                                          char** reason) = 0;
+    };
+    void setQueryConnectionHandler(QueryConnectionHandler* qch) {
+      queryConnectionHandler = qch;
+    }
+
+    // queryConnection is called as described above, and either passes the
+    // request on to the registered handler, or accepts the connection if
+    // no handler has been specified.
+    virtual queryResult queryConnection(network::Socket* sock,
+                                        const char* userName,
+                                        char** reason) {
+      return queryConnectionHandler
+        ? queryConnectionHandler->queryConnection(sock, userName, reason)
+        : ACCEPT;
+    }
+
+    // approveConnection() is called by the active QueryConnectionHandler,
+    // some time after queryConnection() has returned with PENDING, to accept
+    // or reject the connection.  The accept argument should be true for
+    // acceptance, or false for rejection, in which case a string reason may
+    // also be given.
+    void approveConnection(network::Socket* sock, bool accept,
+                           const char* reason);
+
+    // setBlacklist() is called to replace the VNCServerST's internal
+    // Blacklist instance with another instance.  This allows a single
+    // Blacklist to be shared by multiple VNCServerST instances.
+    void setBlacklist(Blacklist* bl) {blHosts = bl ? bl : &blacklist;}
+
+    // setEconomicTranslate() determines (for new connections) whether pixels
+    // should be translated for <=16bpp clients using a large lookup table (fast)
+    // or separate, smaller R, G and B tables (slower).  If set to true, small tables
+    // are used, to save memory.
+    void setEconomicTranslate(bool et) { useEconomicTranslate = et; }
+
+  protected:
+
+    friend class VNCSConnectionST;
+
+    void startDesktop();
+
+    static LogWriter connectionsLog;
+    Blacklist blacklist;
+    Blacklist* blHosts;
+
+    SDesktop* desktop;
+    bool desktopStarted;
+    PixelBuffer* pb;
+
+    CharArray name;
+
+    std::list<VNCSConnectionST*> clients;
+    VNCSConnectionST* pointerClient;
+    std::list<network::Socket*> closingSockets;
+
+    ComparingUpdateTracker* comparer;
+
+    Point cursorPos;
+    Cursor cursor;
+    Point cursorTL() { return cursorPos.subtract(cursor.hotspot); }
+    Point renderedCursorTL;
+    ManagedPixelBuffer renderedCursor;
+    bool renderedCursorInvalid;
+
+    // - Check how many of the clients are authenticated.
+    int authClientCount();
+
+    bool needRenderedCursor();
+    void checkUpdate();
+
+    SSecurityFactory* securityFactory;
+    QueryConnectionHandler* queryConnectionHandler;
+    bool useEconomicTranslate;
+  };
+
+};
+
+#endif
+
diff --git a/rfb/ZRLEDecoder.cxx b/rfb/ZRLEDecoder.cxx
new file mode 100644
index 0000000..2dd4a12
--- /dev/null
+++ b/rfb/ZRLEDecoder.cxx
@@ -0,0 +1,91 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <rfb/CMsgReader.h>
+#include <rfb/CMsgHandler.h>
+#include <rfb/ZRLEDecoder.h>
+
+using namespace rfb;
+
+#define EXTRA_ARGS CMsgHandler* handler
+#define FILL_RECT(r, p) handler->fillRect(r, p)
+#define IMAGE_RECT(r, p) handler->imageRect(r, p)
+#define BPP 8
+#include <rfb/zrleDecode.h>
+#undef BPP
+#define BPP 16
+#include <rfb/zrleDecode.h>
+#undef BPP
+#define BPP 32
+#include <rfb/zrleDecode.h>
+#define CPIXEL 24A
+#include <rfb/zrleDecode.h>
+#undef CPIXEL
+#define CPIXEL 24B
+#include <rfb/zrleDecode.h>
+#undef CPIXEL
+#undef BPP
+
+Decoder* ZRLEDecoder::create(CMsgReader* reader)
+{
+  return new ZRLEDecoder(reader);
+}
+
+ZRLEDecoder::ZRLEDecoder(CMsgReader* reader_) : reader(reader_)
+{
+}
+
+ZRLEDecoder::~ZRLEDecoder()
+{
+}
+
+void ZRLEDecoder::readRect(const Rect& r, CMsgHandler* handler)
+{
+  rdr::InStream* is = reader->getInStream();
+  rdr::U8* buf = reader->getImageBuf(64 * 64 * 4);
+  switch (reader->bpp()) {
+  case 8:  zrleDecode8 (r, is, &zis, (rdr::U8*) buf, handler); break;
+  case 16: zrleDecode16(r, is, &zis, (rdr::U16*)buf, handler); break;
+  case 32:
+    {
+      const rfb::PixelFormat& pf = handler->cp.pf();
+      bool fitsInLS3Bytes = ((pf.redMax   << pf.redShift)   < (1<<24) &&
+                             (pf.greenMax << pf.greenShift) < (1<<24) &&
+                             (pf.blueMax  << pf.blueShift)  < (1<<24));
+
+      bool fitsInMS3Bytes = (pf.redShift   > 7  &&
+                             pf.greenShift > 7  &&
+                             pf.blueShift  > 7);
+
+      if ((fitsInLS3Bytes && !pf.bigEndian) ||
+          (fitsInMS3Bytes && pf.bigEndian))
+      {
+        zrleDecode24A(r, is, &zis, (rdr::U32*)buf, handler);
+      }
+      else if ((fitsInLS3Bytes && pf.bigEndian) ||
+               (fitsInMS3Bytes && !pf.bigEndian))
+      {
+        zrleDecode24B(r, is, &zis, (rdr::U32*)buf, handler);
+      }
+      else
+      {
+        zrleDecode32(r, is, &zis, (rdr::U32*)buf, handler);
+      }
+      break;
+    }
+  }
+}
diff --git a/rfb/ZRLEDecoder.h b/rfb/ZRLEDecoder.h
new file mode 100644
index 0000000..c8b1feb
--- /dev/null
+++ b/rfb/ZRLEDecoder.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_ZRLEDECODER_H__
+#define __RFB_ZRLEDECODER_H__
+
+#include <rdr/ZlibInStream.h>
+#include <rfb/Decoder.h>
+
+namespace rfb {
+
+  class ZRLEDecoder : public Decoder {
+  public:
+    static Decoder* create(CMsgReader* reader);
+    virtual void readRect(const Rect& r, CMsgHandler* handler);
+    virtual ~ZRLEDecoder();
+  private:
+    ZRLEDecoder(CMsgReader* reader);
+    CMsgReader* reader;
+    rdr::ZlibInStream zis;
+  };
+}
+#endif
diff --git a/rfb/ZRLEEncoder.cxx b/rfb/ZRLEEncoder.cxx
new file mode 100644
index 0000000..1f28869
--- /dev/null
+++ b/rfb/ZRLEEncoder.cxx
@@ -0,0 +1,122 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <rdr/OutStream.h>
+#include <rfb/Exception.h>
+#include <rfb/ImageGetter.h>
+#include <rfb/encodings.h>
+#include <rfb/ConnParams.h>
+#include <rfb/SMsgWriter.h>
+#include <rfb/ZRLEEncoder.h>
+#include <rfb/Configuration.h>
+
+using namespace rfb;
+
+rdr::MemOutStream* ZRLEEncoder::sharedMos = 0;
+int ZRLEEncoder::maxLen = 513 * 1024; // enough for width 2048 32-bit pixels
+
+IntParameter zlibLevel("ZlibLevel","Zlib compression level",-1);
+
+#define EXTRA_ARGS ImageGetter* ig
+#define GET_IMAGE_INTO_BUF(r,buf) ig->getImage(buf, r);
+#define BPP 8
+#include <rfb/zrleEncode.h>
+#undef BPP
+#define BPP 16
+#include <rfb/zrleEncode.h>
+#undef BPP
+#define BPP 32
+#include <rfb/zrleEncode.h>
+#define CPIXEL 24A
+#include <rfb/zrleEncode.h>
+#undef CPIXEL
+#define CPIXEL 24B
+#include <rfb/zrleEncode.h>
+#undef CPIXEL
+#undef BPP
+
+Encoder* ZRLEEncoder::create(SMsgWriter* writer)
+{
+  return new ZRLEEncoder(writer);
+}
+
+ZRLEEncoder::ZRLEEncoder(SMsgWriter* writer_)
+  : writer(writer_), zos(0,0,zlibLevel)
+{
+  if (sharedMos)
+    mos = sharedMos;
+  else
+    mos = new rdr::MemOutStream(129*1024);
+}
+
+ZRLEEncoder::~ZRLEEncoder()
+{
+  if (!sharedMos)
+    delete mos;
+}
+
+bool ZRLEEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
+{
+  rdr::U8* imageBuf = writer->getImageBuf(64 * 64 * 4 + 4);
+  mos->clear();
+  bool wroteAll = true;
+  *actual = r;
+
+  switch (writer->bpp()) {
+  case 8:
+    wroteAll = zrleEncode8(r, mos, &zos, imageBuf, maxLen, actual, ig);
+    break;
+  case 16:
+    wroteAll = zrleEncode16(r, mos, &zos, imageBuf, maxLen, actual, ig);
+    break;
+  case 32:
+    {
+      const PixelFormat& pf = writer->getConnParams()->pf();
+
+      bool fitsInLS3Bytes = ((pf.redMax   << pf.redShift)   < (1<<24) &&
+                             (pf.greenMax << pf.greenShift) < (1<<24) &&
+                             (pf.blueMax  << pf.blueShift)  < (1<<24));
+
+      bool fitsInMS3Bytes = (pf.redShift   > 7  &&
+                             pf.greenShift > 7  &&
+                             pf.blueShift  > 7);
+
+      if ((fitsInLS3Bytes && !pf.bigEndian) ||
+          (fitsInMS3Bytes && pf.bigEndian))
+      {
+        wroteAll = zrleEncode24A(r, mos, &zos, imageBuf, maxLen, actual, ig);
+      }
+      else if ((fitsInLS3Bytes && pf.bigEndian) ||
+               (fitsInMS3Bytes && !pf.bigEndian))
+      {
+        wroteAll = zrleEncode24B(r, mos, &zos, imageBuf, maxLen, actual, ig);
+      }
+      else
+      {
+        wroteAll = zrleEncode32(r, mos, &zos, imageBuf, maxLen, actual, ig);
+      }
+      break;
+    }
+  }
+
+  writer->startRect(*actual, encodingZRLE);
+  rdr::OutStream* os = writer->getOutStream();
+  os->writeU32(mos->length());
+  os->writeBytes(mos->data(), mos->length());
+  writer->endRect();
+  return wroteAll;
+}
diff --git a/rfb/ZRLEEncoder.h b/rfb/ZRLEEncoder.h
new file mode 100644
index 0000000..17222a3
--- /dev/null
+++ b/rfb/ZRLEEncoder.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_ZRLEENCODER_H__
+#define __RFB_ZRLEENCODER_H__
+
+#include <rdr/MemOutStream.h>
+#include <rdr/ZlibOutStream.h>
+#include <rfb/Encoder.h>
+
+namespace rfb {
+
+  class ZRLEEncoder : public Encoder {
+  public:
+    static Encoder* create(SMsgWriter* writer);
+    virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual);
+    virtual ~ZRLEEncoder();
+
+    // setMaxLen() sets the maximum size in bytes of any ZRLE rectangle.  This
+    // can be used to stop the MemOutStream from growing too large.  The value
+    // must be large enough to allow for at least one row of ZRLE tiles.  So
+    // for example for a screen width of 2048 32-bit pixels this is 2K*4*64 =
+    // 512Kbytes plus a bit of overhead.
+    static void setMaxLen(int m) { maxLen = m; }
+
+    // setSharedMos() sets a MemOutStream to be shared amongst all
+    // ZRLEEncoders.  Should be called before any ZRLEEncoders are created.
+    static void setSharedMos(rdr::MemOutStream* mos_) { sharedMos = mos_; }
+
+  private:
+    ZRLEEncoder(SMsgWriter* writer);
+    SMsgWriter* writer;
+    rdr::ZlibOutStream zos;
+    rdr::MemOutStream* mos;
+    static rdr::MemOutStream* sharedMos;
+    static int maxLen;
+  };
+}
+#endif
diff --git a/rfb/d3des.c b/rfb/d3des.c
new file mode 100644
index 0000000..9227ddd
--- /dev/null
+++ b/rfb/d3des.c
@@ -0,0 +1,434 @@
+/*
+ * This is D3DES (V5.09) by Richard Outerbridge with the double and
+ * triple-length support removed for use in VNC.  Also the bytebit[] array
+ * has been reversed so that the most significant bit in each byte of the
+ * key is ignored, not the least significant.
+ *
+ * These changes are:
+ *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* D3DES (V5.09) -
+ *
+ * A portable, public domain, version of the Data Encryption Standard.
+ *
+ * Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge.
+ * Thanks to: Dan Hoey for his excellent Initial and Inverse permutation
+ * code;  Jim Gillogly & Phil Karn for the DES key schedule code; Dennis
+ * Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau,
+ * for humouring me on.
+ *
+ * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
+ * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
+ */
+
+#include "d3des.h"
+
+static void scrunch(unsigned char *, unsigned long *);
+static void unscrun(unsigned long *, unsigned char *);
+static void desfunc(unsigned long *, unsigned long *);
+static void cookey(unsigned long *);
+
+static unsigned long KnL[32] = { 0L };
+
+static unsigned short bytebit[8]	= {
+	01, 02, 04, 010, 020, 040, 0100, 0200 };
+
+static unsigned long bigbyte[24] = {
+	0x800000L,	0x400000L,	0x200000L,	0x100000L,
+	0x80000L,	0x40000L,	0x20000L,	0x10000L,
+	0x8000L,	0x4000L,	0x2000L,	0x1000L,
+	0x800L, 	0x400L, 	0x200L, 	0x100L,
+	0x80L,		0x40L,		0x20L,		0x10L,
+	0x8L,		0x4L,		0x2L,		0x1L	};
+
+/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */
+
+static unsigned char pc1[56] = {
+	56, 48, 40, 32, 24, 16,  8,	 0, 57, 49, 41, 33, 25, 17,
+	 9,  1, 58, 50, 42, 34, 26,	18, 10,  2, 59, 51, 43, 35,
+	62, 54, 46, 38, 30, 22, 14,	 6, 61, 53, 45, 37, 29, 21,
+	13,  5, 60, 52, 44, 36, 28,	20, 12,  4, 27, 19, 11,  3 };
+
+static unsigned char totrot[16] = {
+	1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 };
+
+static unsigned char pc2[48] = {
+	13, 16, 10, 23,  0,  4,  2, 27, 14,  5, 20,  9,
+	22, 18, 11,  3, 25,  7, 15,  6, 26, 19, 12,  1,
+	40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
+	43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 };
+
+void deskey(key, edf)	/* Thanks to James Gillogly & Phil Karn! */
+unsigned char *key;
+int edf;
+{
+	register int i, j, l, m, n;
+	unsigned char pc1m[56], pcr[56];
+	unsigned long kn[32];
+
+	for ( j = 0; j < 56; j++ ) {
+		l = pc1[j];
+		m = l & 07;
+		pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0;
+		}
+	for( i = 0; i < 16; i++ ) {
+		if( edf == DE1 ) m = (15 - i) << 1;
+		else m = i << 1;
+		n = m + 1;
+		kn[m] = kn[n] = 0L;
+		for( j = 0; j < 28; j++ ) {
+			l = j + totrot[i];
+			if( l < 28 ) pcr[j] = pc1m[l];
+			else pcr[j] = pc1m[l - 28];
+			}
+		for( j = 28; j < 56; j++ ) {
+		    l = j + totrot[i];
+		    if( l < 56 ) pcr[j] = pc1m[l];
+		    else pcr[j] = pc1m[l - 28];
+		    }
+		for( j = 0; j < 24; j++ ) {
+			if( pcr[pc2[j]] ) kn[m] |= bigbyte[j];
+			if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j];
+			}
+		}
+	cookey(kn);
+	return;
+	}
+
+static void cookey(raw1)
+register unsigned long *raw1;
+{
+	register unsigned long *cook, *raw0;
+	unsigned long dough[32];
+	register int i;
+
+	cook = dough;
+	for( i = 0; i < 16; i++, raw1++ ) {
+		raw0 = raw1++;
+		*cook	 = (*raw0 & 0x00fc0000L) << 6;
+		*cook	|= (*raw0 & 0x00000fc0L) << 10;
+		*cook	|= (*raw1 & 0x00fc0000L) >> 10;
+		*cook++ |= (*raw1 & 0x00000fc0L) >> 6;
+		*cook	 = (*raw0 & 0x0003f000L) << 12;
+		*cook	|= (*raw0 & 0x0000003fL) << 16;
+		*cook	|= (*raw1 & 0x0003f000L) >> 4;
+		*cook++ |= (*raw1 & 0x0000003fL);
+		}
+	usekey(dough);
+	return;
+	}
+
+void cpkey(into)
+register unsigned long *into;
+{
+	register unsigned long *from, *endp;
+
+	from = KnL, endp = &KnL[32];
+	while( from < endp ) *into++ = *from++;
+	return;
+	}
+
+void usekey(from)
+register unsigned long *from;
+{
+	register unsigned long *to, *endp;
+
+	to = KnL, endp = &KnL[32];
+	while( to < endp ) *to++ = *from++;
+	return;
+	}
+
+void des(inblock, outblock)
+unsigned char *inblock, *outblock;
+{
+	unsigned long work[2];
+
+	scrunch(inblock, work);
+	desfunc(work, KnL);
+	unscrun(work, outblock);
+	return;
+	}
+
+static void scrunch(outof, into)
+register unsigned char *outof;
+register unsigned long *into;
+{
+	*into	 = (*outof++ & 0xffL) << 24;
+	*into	|= (*outof++ & 0xffL) << 16;
+	*into	|= (*outof++ & 0xffL) << 8;
+	*into++ |= (*outof++ & 0xffL);
+	*into	 = (*outof++ & 0xffL) << 24;
+	*into	|= (*outof++ & 0xffL) << 16;
+	*into	|= (*outof++ & 0xffL) << 8;
+	*into	|= (*outof   & 0xffL);
+	return;
+	}
+
+static void unscrun(outof, into)
+register unsigned long *outof;
+register unsigned char *into;
+{
+	*into++ = (*outof >> 24) & 0xffL;
+	*into++ = (*outof >> 16) & 0xffL;
+	*into++ = (*outof >>  8) & 0xffL;
+	*into++ =  *outof++	 & 0xffL;
+	*into++ = (*outof >> 24) & 0xffL;
+	*into++ = (*outof >> 16) & 0xffL;
+	*into++ = (*outof >>  8) & 0xffL;
+	*into	=  *outof	 & 0xffL;
+	return;
+	}
+
+static unsigned long SP1[64] = {
+	0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L,
+	0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L,
+	0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L,
+	0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L,
+	0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L,
+	0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L,
+	0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L,
+	0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L,
+	0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L,
+	0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L,
+	0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L,
+	0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L,
+	0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L,
+	0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L,
+	0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L,
+	0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L };
+
+static unsigned long SP2[64] = {
+	0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L,
+	0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L,
+	0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L,
+	0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L,
+	0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L,
+	0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L,
+	0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L,
+	0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L,
+	0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L,
+	0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L,
+	0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L,
+	0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L,
+	0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L,
+	0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L,
+	0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L,
+	0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L };
+
+static unsigned long SP3[64] = {
+	0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L,
+	0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L,
+	0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L,
+	0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L,
+	0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L,
+	0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L,
+	0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L,
+	0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L,
+	0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L,
+	0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L,
+	0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L,
+	0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L,
+	0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L,
+	0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L,
+	0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L,
+	0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L };
+
+static unsigned long SP4[64] = {
+	0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+	0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L,
+	0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L,
+	0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L,
+	0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L,
+	0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L,
+	0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L,
+	0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L,
+	0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L,
+	0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L,
+	0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L,
+	0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+	0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L,
+	0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L,
+	0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L,
+	0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L };
+
+static unsigned long SP5[64] = {
+	0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L,
+	0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L,
+	0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L,
+	0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L,
+	0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L,
+	0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L,
+	0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L,
+	0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L,
+	0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L,
+	0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L,
+	0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L,
+	0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L,
+	0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L,
+	0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L,
+	0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L,
+	0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L };
+
+static unsigned long SP6[64] = {
+	0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L,
+	0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L,
+	0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L,
+	0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L,
+	0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L,
+	0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L,
+	0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L,
+	0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L,
+	0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L,
+	0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L,
+	0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L,
+	0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L,
+	0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L,
+	0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L,
+	0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L,
+	0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L };
+
+static unsigned long SP7[64] = {
+	0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L,
+	0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L,
+	0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L,
+	0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L,
+	0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L,
+	0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L,
+	0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L,
+	0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L,
+	0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L,
+	0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L,
+	0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L,
+	0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L,
+	0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L,
+	0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L,
+	0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L,
+	0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L };
+
+static unsigned long SP8[64] = {
+	0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L,
+	0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L,
+	0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L,
+	0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L,
+	0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L,
+	0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L,
+	0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L,
+	0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L,
+	0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L,
+	0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L,
+	0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L,
+	0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L,
+	0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L,
+	0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L,
+	0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L,
+	0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L };
+
+static void desfunc(block, keys)
+register unsigned long *block, *keys;
+{
+	register unsigned long fval, work, right, leftt;
+	register int round;
+
+	leftt = block[0];
+	right = block[1];
+	work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL;
+	right ^= work;
+	leftt ^= (work << 4);
+	work = ((leftt >> 16) ^ right) & 0x0000ffffL;
+	right ^= work;
+	leftt ^= (work << 16);
+	work = ((right >> 2) ^ leftt) & 0x33333333L;
+	leftt ^= work;
+	right ^= (work << 2);
+	work = ((right >> 8) ^ leftt) & 0x00ff00ffL;
+	leftt ^= work;
+	right ^= (work << 8);
+	right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL;
+	work = (leftt ^ right) & 0xaaaaaaaaL;
+	leftt ^= work;
+	right ^= work;
+	leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL;
+
+	for( round = 0; round < 8; round++ ) {
+		work  = (right << 28) | (right >> 4);
+		work ^= *keys++;
+		fval  = SP7[ work		 & 0x3fL];
+		fval |= SP5[(work >>  8) & 0x3fL];
+		fval |= SP3[(work >> 16) & 0x3fL];
+		fval |= SP1[(work >> 24) & 0x3fL];
+		work  = right ^ *keys++;
+		fval |= SP8[ work		 & 0x3fL];
+		fval |= SP6[(work >>  8) & 0x3fL];
+		fval |= SP4[(work >> 16) & 0x3fL];
+		fval |= SP2[(work >> 24) & 0x3fL];
+		leftt ^= fval;
+		work  = (leftt << 28) | (leftt >> 4);
+		work ^= *keys++;
+		fval  = SP7[ work		 & 0x3fL];
+		fval |= SP5[(work >>  8) & 0x3fL];
+		fval |= SP3[(work >> 16) & 0x3fL];
+		fval |= SP1[(work >> 24) & 0x3fL];
+		work  = leftt ^ *keys++;
+		fval |= SP8[ work		 & 0x3fL];
+		fval |= SP6[(work >>  8) & 0x3fL];
+		fval |= SP4[(work >> 16) & 0x3fL];
+		fval |= SP2[(work >> 24) & 0x3fL];
+		right ^= fval;
+		}
+
+	right = (right << 31) | (right >> 1);
+	work = (leftt ^ right) & 0xaaaaaaaaL;
+	leftt ^= work;
+	right ^= work;
+	leftt = (leftt << 31) | (leftt >> 1);
+	work = ((leftt >> 8) ^ right) & 0x00ff00ffL;
+	right ^= work;
+	leftt ^= (work << 8);
+	work = ((leftt >> 2) ^ right) & 0x33333333L;
+	right ^= work;
+	leftt ^= (work << 2);
+	work = ((right >> 16) ^ leftt) & 0x0000ffffL;
+	leftt ^= work;
+	right ^= (work << 16);
+	work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL;
+	leftt ^= work;
+	right ^= (work << 4);
+	*block++ = right;
+	*block = leftt;
+	return;
+	}
+
+/* Validation sets:
+ *
+ * Single-length key, single-length plaintext -
+ * Key	  : 0123 4567 89ab cdef
+ * Plain  : 0123 4567 89ab cde7
+ * Cipher : c957 4425 6a5e d31d
+ *
+ * Double-length key, single-length plaintext -
+ * Key	  : 0123 4567 89ab cdef fedc ba98 7654 3210
+ * Plain  : 0123 4567 89ab cde7
+ * Cipher : 7f1d 0a77 826b 8aff
+ *
+ * Double-length key, double-length plaintext -
+ * Key	  : 0123 4567 89ab cdef fedc ba98 7654 3210
+ * Plain  : 0123 4567 89ab cdef 0123 4567 89ab cdff
+ * Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7
+ *
+ * Triple-length key, single-length plaintext -
+ * Key	  : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
+ * Plain  : 0123 4567 89ab cde7
+ * Cipher : de0b 7c06 ae5e 0ed5
+ *
+ * Triple-length key, double-length plaintext -
+ * Key	  : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
+ * Plain  : 0123 4567 89ab cdef 0123 4567 89ab cdff
+ * Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5
+ *
+ * d3des V5.0a rwo 9208.07 18:44 Graven Imagery
+ **********************************************************************/
diff --git a/rfb/d3des.h b/rfb/d3des.h
new file mode 100644
index 0000000..ea3da44
--- /dev/null
+++ b/rfb/d3des.h
@@ -0,0 +1,51 @@
+/*
+ * This is D3DES (V5.09) by Richard Outerbridge with the double and
+ * triple-length support removed for use in VNC.
+ *
+ * These changes are:
+ *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* d3des.h -
+ *
+ *	Headers and defines for d3des.c
+ *	Graven Imagery, 1992.
+ *
+ * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge
+ *	(GEnie : OUTER; CIS : [71755,204])
+ */
+
+#define EN0	0	/* MODE == encrypt */
+#define DE1	1	/* MODE == decrypt */
+
+extern void deskey(unsigned char *, int);
+/*		      hexkey[8]     MODE
+ * Sets the internal key register according to the hexadecimal
+ * key contained in the 8 bytes of hexkey, according to the DES,
+ * for encryption or decryption according to MODE.
+ */
+
+extern void usekey(unsigned long *);
+/*		    cookedkey[32]
+ * Loads the internal key register with the data in cookedkey.
+ */
+
+extern void cpkey(unsigned long *);
+/*		   cookedkey[32]
+ * Copies the contents of the internal key register into the storage
+ * located at &cookedkey[0].
+ */
+
+extern void des(unsigned char *, unsigned char *);
+/*		    from[8]	      to[8]
+ * Encrypts/Decrypts (according to the key currently loaded in the
+ * internal key register) one block of eight bytes at address 'from'
+ * into the block at address 'to'.  They can be the same.
+ */
+
+/* d3des.h V5.09 rwo 9208.04 15:06 Graven Imagery
+ ********************************************************************/
diff --git a/rfb/encodings.cxx b/rfb/encodings.cxx
new file mode 100644
index 0000000..56a64ee
--- /dev/null
+++ b/rfb/encodings.cxx
@@ -0,0 +1,46 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <string.h>
+#ifdef _WIN32
+#define strcasecmp _stricmp
+#endif
+#include <rfb/encodings.h>
+
+int rfb::encodingNum(const char* name)
+{
+  if (strcasecmp(name, "raw") == 0)      return encodingRaw;
+  if (strcasecmp(name, "copyRect") == 0) return encodingCopyRect;
+  if (strcasecmp(name, "RRE") == 0)      return encodingRRE;
+  if (strcasecmp(name, "CoRRE") == 0)    return encodingCoRRE;
+  if (strcasecmp(name, "hextile") == 0)  return encodingHextile;
+  if (strcasecmp(name, "ZRLE") == 0)     return encodingZRLE;
+  return -1;
+}
+
+const char* rfb::encodingName(unsigned int num)
+{
+  switch (num) {
+  case encodingRaw:      return "raw";
+  case encodingCopyRect: return "copyRect";
+  case encodingRRE:      return "RRE";
+  case encodingCoRRE:    return "CoRRE";
+  case encodingHextile:  return "hextile";
+  case encodingZRLE:     return "ZRLE";
+  default:               return "[unknown encoding]";
+  }
+}
diff --git a/rfb/encodings.h b/rfb/encodings.h
new file mode 100644
index 0000000..ae104b4
--- /dev/null
+++ b/rfb/encodings.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_ENCODINGS_H__
+#define __RFB_ENCODINGS_H__
+
+namespace rfb {
+
+  const unsigned int encodingRaw = 0;
+  const unsigned int encodingCopyRect = 1;
+  const unsigned int encodingRRE = 2;
+  const unsigned int encodingCoRRE = 4;
+  const unsigned int encodingHextile = 5;
+  const unsigned int encodingZRLE = 16;
+
+  const unsigned int encodingMax = 255;
+
+  const unsigned int pseudoEncodingCursor = 0xffffff11;
+  const unsigned int pseudoEncodingDesktopSize = 0xffffff21;
+
+  int encodingNum(const char* name);
+  const char* encodingName(unsigned int num);
+}
+#endif
diff --git a/rfb/hextileConstants.h b/rfb/hextileConstants.h
new file mode 100644
index 0000000..272afbb
--- /dev/null
+++ b/rfb/hextileConstants.h
@@ -0,0 +1,27 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_HEXTILECONSTANTS_H__
+#define __RFB_HEXTILECONSTANTS_H__
+namespace rfb {
+  const int hextileRaw = (1 << 0);
+  const int hextileBgSpecified = (1 << 1);
+  const int hextileFgSpecified = (1 << 2);
+  const int hextileAnySubrects = (1 << 3);
+  const int hextileSubrectsColoured = (1 << 4);
+}
+#endif
diff --git a/rfb/hextileDecode.h b/rfb/hextileDecode.h
new file mode 100644
index 0000000..dc685e3
--- /dev/null
+++ b/rfb/hextileDecode.h
@@ -0,0 +1,126 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// Hextile decoding function.
+//
+// This file is #included after having set the following macros:
+// BPP                - 8, 16 or 32
+// EXTRA_ARGS         - optional extra arguments
+// FILL_RECT          - fill a rectangle with a single colour
+// IMAGE_RECT         - draw a rectangle of pixel data from a buffer
+
+#include <rdr/InStream.h>
+#include <rfb/hextileConstants.h>
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define READ_PIXEL CONCAT2E(readOpaque,BPP)
+#define HEXTILE_DECODE CONCAT2E(hextileDecode,BPP)
+
+void HEXTILE_DECODE (const Rect& r, rdr::InStream* is, PIXEL_T* buf
+#ifdef EXTRA_ARGS
+                     , EXTRA_ARGS
+#endif
+                     )
+{
+  Rect t;
+  PIXEL_T bg = 0;
+  PIXEL_T fg = 0;
+
+  for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) {
+
+    t.br.y = min(r.br.y, t.tl.y + 16);
+
+    for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 16) {
+
+      t.br.x = min(r.br.x, t.tl.x + 16);
+
+      int tileType = is->readU8();
+
+      if (tileType & hextileRaw) {
+	is->readBytes(buf, t.area() * (BPP/8));
+	IMAGE_RECT(t, buf);
+	continue;
+      }
+
+      if (tileType & hextileBgSpecified)
+	bg = is->READ_PIXEL();
+
+#ifdef FAVOUR_FILL_RECT
+      FILL_RECT(t, bg);
+#else
+      int len = t.area();
+      PIXEL_T* ptr = (PIXEL_T*)buf;
+      while (len-- > 0) *ptr++ = bg;
+#endif
+
+      if (tileType & hextileFgSpecified)
+	fg = is->READ_PIXEL();
+
+      if (tileType & hextileAnySubrects) {
+        int nSubrects = is->readU8();
+
+        for (int i = 0; i < nSubrects; i++) {
+
+          if (tileType & hextileSubrectsColoured)
+            fg = is->READ_PIXEL();
+
+          int xy = is->readU8();
+          int wh = is->readU8();
+
+#ifdef FAVOUR_FILL_RECT
+          Rect s;
+          s.tl.x = t.tl.x + ((xy >> 4) & 15);
+          s.tl.y = t.tl.y + (xy & 15);
+          s.br.x = s.tl.x + ((wh >> 4) & 15) + 1;
+          s.br.y = s.tl.y + (wh & 15) + 1;
+          FILL_RECT(s, fg);
+#else
+          int x = ((xy >> 4) & 15);
+          int y = (xy & 15);
+          int w = ((wh >> 4) & 15) + 1;
+          int h = (wh & 15) + 1;
+          PIXEL_T* ptr = (PIXEL_T*)buf + y * t.width() + x;
+          int rowAdd = t.width() - w;
+          while (h-- > 0) {
+            int len = w;
+            while (len-- > 0) *ptr++ = fg;
+            ptr += rowAdd;
+          }
+#endif
+        }
+      }
+#ifndef FAVOUR_FILL_RECT
+      IMAGE_RECT(t, buf);
+#endif
+    }
+  }
+}
+
+#undef PIXEL_T
+#undef READ_PIXEL
+#undef HEXTILE_DECODE
+}
diff --git a/rfb/hextileEncode.h b/rfb/hextileEncode.h
new file mode 100644
index 0000000..a55842a
--- /dev/null
+++ b/rfb/hextileEncode.h
@@ -0,0 +1,247 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// Hextile encoding function.
+//
+// This file is #included after having set the following macros:
+// BPP                - 8, 16 or 32
+// EXTRA_ARGS         - optional extra arguments
+// GET_IMAGE_INTO_BUF - gets a rectangle of pixel data into a buffer
+
+#include <rdr/OutStream.h>
+#include <rfb/hextileConstants.h>
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define WRITE_PIXEL CONCAT2E(writeOpaque,BPP)
+#define HEXTILE_ENCODE CONCAT2E(hextileEncode,BPP)
+#define HEXTILE_ENCODE_TILE CONCAT2E(hextileEncodeTile,BPP)
+#define TEST_TILE_TYPE CONCAT2E(hextileTestTileType,BPP)
+
+int TEST_TILE_TYPE (PIXEL_T* data, int w, int h, PIXEL_T* bg, PIXEL_T* fg);
+int HEXTILE_ENCODE_TILE (PIXEL_T* data, int w, int h, int tileType,
+                         rdr::U8* encoded, PIXEL_T bg);
+
+void HEXTILE_ENCODE(const Rect& r, rdr::OutStream* os
+#ifdef EXTRA_ARGS
+                    , EXTRA_ARGS
+#endif
+                    )
+{
+  Rect t;
+  PIXEL_T buf[256];
+  PIXEL_T oldBg = 0, oldFg = 0;
+  bool oldBgValid = false;
+  bool oldFgValid = false;
+  rdr::U8 encoded[256*(BPP/8)];
+
+  for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) {
+
+    t.br.y = min(r.br.y, t.tl.y + 16);
+
+    for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 16) {
+
+      t.br.x = min(r.br.x, t.tl.x + 16);
+
+      GET_IMAGE_INTO_BUF(t,buf);
+
+      PIXEL_T bg, fg;
+      int tileType = TEST_TILE_TYPE(buf, t.width(), t.height(), &bg, &fg);
+
+      if (!oldBgValid || oldBg != bg) {
+        tileType |= hextileBgSpecified;
+        oldBg = bg;
+        oldBgValid = true;
+      }
+
+      int encodedLen = 0;
+
+      if (tileType & hextileAnySubrects) {
+
+        if (tileType & hextileSubrectsColoured) {
+          oldFgValid = false;
+        } else {
+          if (!oldFgValid || oldFg != fg) {
+            tileType |= hextileFgSpecified;
+            oldFg = fg;
+            oldFgValid = true;
+          }
+        }
+
+        encodedLen = HEXTILE_ENCODE_TILE(buf, t.width(), t.height(), tileType,
+                                         encoded, bg);
+
+        if (encodedLen < 0) {
+          GET_IMAGE_INTO_BUF(t,buf);
+          os->writeU8(hextileRaw);
+          os->writeBytes(buf, t.width() * t.height() * (BPP/8));
+          oldBgValid = oldFgValid = false;
+          continue;
+        }
+      }
+
+      os->writeU8(tileType);
+      if (tileType & hextileBgSpecified) os->WRITE_PIXEL(bg);
+      if (tileType & hextileFgSpecified) os->WRITE_PIXEL(fg);
+      if (tileType & hextileAnySubrects) os->writeBytes(encoded, encodedLen);
+    }
+  }
+}
+
+
+int HEXTILE_ENCODE_TILE (PIXEL_T* data, int w, int h, int tileType,
+                         rdr::U8* encoded, PIXEL_T bg)
+{
+  rdr::U8* nSubrectsPtr = encoded;
+  *nSubrectsPtr = 0;
+  encoded++;
+
+  for (int y = 0; y < h; y++)
+  {
+    int x = 0;
+    while (x < w) {
+      if (*data == bg) {
+        x++;
+        data++;
+        continue;
+      }
+
+      // Find horizontal subrect first
+      PIXEL_T* ptr = data+1;
+      PIXEL_T* eol = data+w-x;
+      while (ptr < eol && *ptr == *data) ptr++;
+      int sw = ptr - data;
+
+      ptr = data + w;
+      int sh = 1;
+      while (sh < h-y) {
+        eol = ptr + sw;
+        while (ptr < eol)
+          if (*ptr++ != *data) goto endOfHorizSubrect;
+        ptr += w - sw;
+        sh++;
+      }
+    endOfHorizSubrect:
+
+      // Find vertical subrect
+      int vh;
+      for (vh = sh; vh < h-y; vh++)
+        if (data[vh*w] != *data) break;
+
+      if (vh != sh) {
+        ptr = data+1;
+        int vw;
+        for (vw = 1; vw < sw; vw++) {
+          for (int i = 0; i < vh; i++)
+            if (ptr[i*w] != *data) goto endOfVertSubrect;
+          ptr++;
+        }
+      endOfVertSubrect:
+
+        // If vertical subrect bigger than horizontal then use that.
+        if (sw*sh < vw*vh) {
+          sw = vw;
+          sh = vh;
+        }
+      }
+
+      (*nSubrectsPtr)++;
+
+      if (tileType & hextileSubrectsColoured) {
+        if (encoded - nSubrectsPtr + (BPP/8) > w*h*(BPP/8)) return -1;
+#if (BPP == 8)
+        *encoded++ = *data;
+#elif (BPP == 16)
+        *encoded++ = ((rdr::U8*)data)[0];
+        *encoded++ = ((rdr::U8*)data)[1];
+#elif (BPP == 32)
+        *encoded++ = ((rdr::U8*)data)[0];
+        *encoded++ = ((rdr::U8*)data)[1];
+        *encoded++ = ((rdr::U8*)data)[2];
+        *encoded++ = ((rdr::U8*)data)[3];
+#endif
+      }
+
+      if (encoded - nSubrectsPtr + 2 > w*h*(BPP/8)) return -1;
+      *encoded++ = (x << 4) | y;
+      *encoded++ = ((sw-1) << 4) | (sh-1);
+
+      ptr = data+w;
+      PIXEL_T* eor = data+w*sh;
+      while (ptr < eor) {
+        eol = ptr + sw;
+        while (ptr < eol) *ptr++ = bg;
+        ptr += w - sw;
+      }
+      x += sw;
+      data += sw;
+    }
+  }
+  return encoded - nSubrectsPtr;
+}
+
+
+int TEST_TILE_TYPE (PIXEL_T* data, int w, int h, PIXEL_T* bg, PIXEL_T* fg)
+{
+  int tileType = 0;
+  PIXEL_T pix1 = *data, pix2 = 0;
+  int count1 = 0, count2 = 0;
+  PIXEL_T* end = data + w*h;
+
+  for (PIXEL_T* ptr = data; ptr < end; ptr++) {
+    if (*ptr == pix1) {
+      count1++;
+      continue;
+    }
+
+    if (count2 == 0) {
+      tileType |= hextileAnySubrects;
+      pix2 = *ptr;
+    }
+
+    if (*data == pix2) {
+      count2++;
+      continue;
+    }
+
+    tileType |= hextileSubrectsColoured;
+    break;
+  }
+
+  if (count1 >= count2) {
+    *bg = pix1; *fg = pix2;
+  } else {
+    *bg = pix2; *fg = pix1;
+  }
+  return tileType;
+}
+
+#undef PIXEL_T
+#undef WRITE_PIXEL
+#undef HEXTILE_ENCODE
+#undef HEXTILE_ENCODE_TILE
+#undef TEST_TILE_TYPE
+}
diff --git a/rfb/keysymdef.h b/rfb/keysymdef.h
new file mode 100644
index 0000000..979ebdd
--- /dev/null
+++ b/rfb/keysymdef.h
@@ -0,0 +1,1595 @@
+/* $TOG: keysymdef.h /main/28 1998/05/22 16:18:01 kaleb $ */
+
+/***********************************************************
+Copyright 1987, 1994, 1998  The Open Group
+
+All Rights Reserved.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from The Open Group.
+
+
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts
+
+                        All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+#define XK_VoidSymbol		0xFFFFFF	/* void symbol */
+
+#ifdef XK_MISCELLANY
+/*
+ * TTY Functions, cleverly chosen to map to ascii, for convenience of
+ * programming, but could have been arbitrary (at the cost of lookup
+ * tables in client code.
+ */
+
+#define XK_BackSpace		0xFF08	/* back space, back char */
+#define XK_Tab			0xFF09
+#define XK_Linefeed		0xFF0A	/* Linefeed, LF */
+#define XK_Clear		0xFF0B
+#define XK_Return		0xFF0D	/* Return, enter */
+#define XK_Pause		0xFF13	/* Pause, hold */
+#define XK_Scroll_Lock		0xFF14
+#define XK_Sys_Req		0xFF15
+#define XK_Escape		0xFF1B
+#define XK_Delete		0xFFFF	/* Delete, rubout */
+
+
+
+/* International & multi-key character composition */
+
+#define XK_Multi_key		0xFF20  /* Multi-key character compose */
+#define XK_Codeinput		0xFF37
+#define XK_SingleCandidate	0xFF3C
+#define XK_MultipleCandidate	0xFF3D
+#define XK_PreviousCandidate	0xFF3E
+
+/* Japanese keyboard support */
+
+#define XK_Kanji		0xFF21	/* Kanji, Kanji convert */
+#define XK_Muhenkan		0xFF22  /* Cancel Conversion */
+#define XK_Henkan_Mode		0xFF23  /* Start/Stop Conversion */
+#define XK_Henkan		0xFF23  /* Alias for Henkan_Mode */
+#define XK_Romaji		0xFF24  /* to Romaji */
+#define XK_Hiragana		0xFF25  /* to Hiragana */
+#define XK_Katakana		0xFF26  /* to Katakana */
+#define XK_Hiragana_Katakana	0xFF27  /* Hiragana/Katakana toggle */
+#define XK_Zenkaku		0xFF28  /* to Zenkaku */
+#define XK_Hankaku		0xFF29  /* to Hankaku */
+#define XK_Zenkaku_Hankaku	0xFF2A  /* Zenkaku/Hankaku toggle */
+#define XK_Touroku		0xFF2B  /* Add to Dictionary */
+#define XK_Massyo		0xFF2C  /* Delete from Dictionary */
+#define XK_Kana_Lock		0xFF2D  /* Kana Lock */
+#define XK_Kana_Shift		0xFF2E  /* Kana Shift */
+#define XK_Eisu_Shift		0xFF2F  /* Alphanumeric Shift */
+#define XK_Eisu_toggle		0xFF30  /* Alphanumeric toggle */
+#define XK_Kanji_Bangou		0xFF37  /* Codeinput */
+#define XK_Zen_Koho		0xFF3D	/* Multiple/All Candidate(s) */
+#define XK_Mae_Koho		0xFF3E	/* Previous Candidate */
+
+/* 0xFF31 thru 0xFF3F are under XK_KOREAN */
+
+/* Cursor control & motion */
+
+#define XK_Home			0xFF50
+#define XK_Left			0xFF51	/* Move left, left arrow */
+#define XK_Up			0xFF52	/* Move up, up arrow */
+#define XK_Right		0xFF53	/* Move right, right arrow */
+#define XK_Down			0xFF54	/* Move down, down arrow */
+#define XK_Prior		0xFF55	/* Prior, previous */
+#define XK_Page_Up		0xFF55
+#define XK_Next			0xFF56	/* Next */
+#define XK_Page_Down		0xFF56
+#define XK_End			0xFF57	/* EOL */
+#define XK_Begin		0xFF58	/* BOL */
+
+
+/* Misc Functions */
+
+#define XK_Select		0xFF60	/* Select, mark */
+#define XK_Print		0xFF61
+#define XK_Execute		0xFF62	/* Execute, run, do */
+#define XK_Insert		0xFF63	/* Insert, insert here */
+#define XK_Undo			0xFF65	/* Undo, oops */
+#define XK_Redo			0xFF66	/* redo, again */
+#define XK_Menu			0xFF67
+#define XK_Find			0xFF68	/* Find, search */
+#define XK_Cancel		0xFF69	/* Cancel, stop, abort, exit */
+#define XK_Help			0xFF6A	/* Help */
+#define XK_Break		0xFF6B
+#define XK_Mode_switch		0xFF7E	/* Character set switch */
+#define XK_script_switch        0xFF7E  /* Alias for mode_switch */
+#define XK_Num_Lock		0xFF7F
+
+/* Keypad Functions, keypad numbers cleverly chosen to map to ascii */
+
+#define XK_KP_Space		0xFF80	/* space */
+#define XK_KP_Tab		0xFF89
+#define XK_KP_Enter		0xFF8D	/* enter */
+#define XK_KP_F1		0xFF91	/* PF1, KP_A, ... */
+#define XK_KP_F2		0xFF92
+#define XK_KP_F3		0xFF93
+#define XK_KP_F4		0xFF94
+#define XK_KP_Home		0xFF95
+#define XK_KP_Left		0xFF96
+#define XK_KP_Up		0xFF97
+#define XK_KP_Right		0xFF98
+#define XK_KP_Down		0xFF99
+#define XK_KP_Prior		0xFF9A
+#define XK_KP_Page_Up		0xFF9A
+#define XK_KP_Next		0xFF9B
+#define XK_KP_Page_Down		0xFF9B
+#define XK_KP_End		0xFF9C
+#define XK_KP_Begin		0xFF9D
+#define XK_KP_Insert		0xFF9E
+#define XK_KP_Delete		0xFF9F
+#define XK_KP_Equal		0xFFBD	/* equals */
+#define XK_KP_Multiply		0xFFAA
+#define XK_KP_Add		0xFFAB
+#define XK_KP_Separator		0xFFAC	/* separator, often comma */
+#define XK_KP_Subtract		0xFFAD
+#define XK_KP_Decimal		0xFFAE
+#define XK_KP_Divide		0xFFAF
+
+#define XK_KP_0			0xFFB0
+#define XK_KP_1			0xFFB1
+#define XK_KP_2			0xFFB2
+#define XK_KP_3			0xFFB3
+#define XK_KP_4			0xFFB4
+#define XK_KP_5			0xFFB5
+#define XK_KP_6			0xFFB6
+#define XK_KP_7			0xFFB7
+#define XK_KP_8			0xFFB8
+#define XK_KP_9			0xFFB9
+
+
+
+/*
+ * Auxilliary Functions; note the duplicate definitions for left and right
+ * function keys;  Sun keyboards and a few other manufactures have such
+ * function key groups on the left and/or right sides of the keyboard.
+ * We've not found a keyboard with more than 35 function keys total.
+ */
+
+#define XK_F1			0xFFBE
+#define XK_F2			0xFFBF
+#define XK_F3			0xFFC0
+#define XK_F4			0xFFC1
+#define XK_F5			0xFFC2
+#define XK_F6			0xFFC3
+#define XK_F7			0xFFC4
+#define XK_F8			0xFFC5
+#define XK_F9			0xFFC6
+#define XK_F10			0xFFC7
+#define XK_F11			0xFFC8
+#define XK_L1			0xFFC8
+#define XK_F12			0xFFC9
+#define XK_L2			0xFFC9
+#define XK_F13			0xFFCA
+#define XK_L3			0xFFCA
+#define XK_F14			0xFFCB
+#define XK_L4			0xFFCB
+#define XK_F15			0xFFCC
+#define XK_L5			0xFFCC
+#define XK_F16			0xFFCD
+#define XK_L6			0xFFCD
+#define XK_F17			0xFFCE
+#define XK_L7			0xFFCE
+#define XK_F18			0xFFCF
+#define XK_L8			0xFFCF
+#define XK_F19			0xFFD0
+#define XK_L9			0xFFD0
+#define XK_F20			0xFFD1
+#define XK_L10			0xFFD1
+#define XK_F21			0xFFD2
+#define XK_R1			0xFFD2
+#define XK_F22			0xFFD3
+#define XK_R2			0xFFD3
+#define XK_F23			0xFFD4
+#define XK_R3			0xFFD4
+#define XK_F24			0xFFD5
+#define XK_R4			0xFFD5
+#define XK_F25			0xFFD6
+#define XK_R5			0xFFD6
+#define XK_F26			0xFFD7
+#define XK_R6			0xFFD7
+#define XK_F27			0xFFD8
+#define XK_R7			0xFFD8
+#define XK_F28			0xFFD9
+#define XK_R8			0xFFD9
+#define XK_F29			0xFFDA
+#define XK_R9			0xFFDA
+#define XK_F30			0xFFDB
+#define XK_R10			0xFFDB
+#define XK_F31			0xFFDC
+#define XK_R11			0xFFDC
+#define XK_F32			0xFFDD
+#define XK_R12			0xFFDD
+#define XK_F33			0xFFDE
+#define XK_R13			0xFFDE
+#define XK_F34			0xFFDF
+#define XK_R14			0xFFDF
+#define XK_F35			0xFFE0
+#define XK_R15			0xFFE0
+
+/* Modifiers */
+
+#define XK_Shift_L		0xFFE1	/* Left shift */
+#define XK_Shift_R		0xFFE2	/* Right shift */
+#define XK_Control_L		0xFFE3	/* Left control */
+#define XK_Control_R		0xFFE4	/* Right control */
+#define XK_Caps_Lock		0xFFE5	/* Caps lock */
+#define XK_Shift_Lock		0xFFE6	/* Shift lock */
+
+#define XK_Meta_L		0xFFE7	/* Left meta */
+#define XK_Meta_R		0xFFE8	/* Right meta */
+#define XK_Alt_L		0xFFE9	/* Left alt */
+#define XK_Alt_R		0xFFEA	/* Right alt */
+#define XK_Super_L		0xFFEB	/* Left super */
+#define XK_Super_R		0xFFEC	/* Right super */
+#define XK_Hyper_L		0xFFED	/* Left hyper */
+#define XK_Hyper_R		0xFFEE	/* Right hyper */
+#endif /* XK_MISCELLANY */
+
+/*
+ * ISO 9995 Function and Modifier Keys
+ * Byte 3 = 0xFE
+ */
+
+#ifdef XK_XKB_KEYS
+#define	XK_ISO_Lock					0xFE01
+#define	XK_ISO_Level2_Latch				0xFE02
+#define	XK_ISO_Level3_Shift				0xFE03
+#define	XK_ISO_Level3_Latch				0xFE04
+#define	XK_ISO_Level3_Lock				0xFE05
+#define	XK_ISO_Group_Shift		0xFF7E	/* Alias for mode_switch */
+#define	XK_ISO_Group_Latch				0xFE06
+#define	XK_ISO_Group_Lock				0xFE07
+#define	XK_ISO_Next_Group				0xFE08
+#define	XK_ISO_Next_Group_Lock				0xFE09
+#define	XK_ISO_Prev_Group				0xFE0A
+#define	XK_ISO_Prev_Group_Lock				0xFE0B
+#define	XK_ISO_First_Group				0xFE0C
+#define	XK_ISO_First_Group_Lock				0xFE0D
+#define	XK_ISO_Last_Group				0xFE0E
+#define	XK_ISO_Last_Group_Lock				0xFE0F
+
+#define	XK_ISO_Left_Tab					0xFE20
+#define	XK_ISO_Move_Line_Up				0xFE21
+#define	XK_ISO_Move_Line_Down				0xFE22
+#define	XK_ISO_Partial_Line_Up				0xFE23
+#define	XK_ISO_Partial_Line_Down			0xFE24
+#define	XK_ISO_Partial_Space_Left			0xFE25
+#define	XK_ISO_Partial_Space_Right			0xFE26
+#define	XK_ISO_Set_Margin_Left				0xFE27
+#define	XK_ISO_Set_Margin_Right				0xFE28
+#define	XK_ISO_Release_Margin_Left			0xFE29
+#define	XK_ISO_Release_Margin_Right			0xFE2A
+#define	XK_ISO_Release_Both_Margins			0xFE2B
+#define	XK_ISO_Fast_Cursor_Left				0xFE2C
+#define	XK_ISO_Fast_Cursor_Right			0xFE2D
+#define	XK_ISO_Fast_Cursor_Up				0xFE2E
+#define	XK_ISO_Fast_Cursor_Down				0xFE2F
+#define	XK_ISO_Continuous_Underline			0xFE30
+#define	XK_ISO_Discontinuous_Underline			0xFE31
+#define	XK_ISO_Emphasize				0xFE32
+#define	XK_ISO_Center_Object				0xFE33
+#define	XK_ISO_Enter					0xFE34
+
+#define	XK_dead_grave					0xFE50
+#define	XK_dead_acute					0xFE51
+#define	XK_dead_circumflex				0xFE52
+#define	XK_dead_tilde					0xFE53
+#define	XK_dead_macron					0xFE54
+#define	XK_dead_breve					0xFE55
+#define	XK_dead_abovedot				0xFE56
+#define	XK_dead_diaeresis				0xFE57
+#define	XK_dead_abovering				0xFE58
+#define	XK_dead_doubleacute				0xFE59
+#define	XK_dead_caron					0xFE5A
+#define	XK_dead_cedilla					0xFE5B
+#define	XK_dead_ogonek					0xFE5C
+#define	XK_dead_iota					0xFE5D
+#define	XK_dead_voiced_sound				0xFE5E
+#define	XK_dead_semivoiced_sound			0xFE5F
+#define	XK_dead_belowdot				0xFE60
+
+#define	XK_First_Virtual_Screen				0xFED0
+#define	XK_Prev_Virtual_Screen				0xFED1
+#define	XK_Next_Virtual_Screen				0xFED2
+#define	XK_Last_Virtual_Screen				0xFED4
+#define	XK_Terminate_Server				0xFED5
+
+#define	XK_AccessX_Enable				0xFE70
+#define	XK_AccessX_Feedback_Enable			0xFE71
+#define	XK_RepeatKeys_Enable				0xFE72
+#define	XK_SlowKeys_Enable				0xFE73
+#define	XK_BounceKeys_Enable				0xFE74
+#define	XK_StickyKeys_Enable				0xFE75
+#define	XK_MouseKeys_Enable				0xFE76
+#define	XK_MouseKeys_Accel_Enable			0xFE77
+#define	XK_Overlay1_Enable				0xFE78
+#define	XK_Overlay2_Enable				0xFE79
+#define	XK_AudibleBell_Enable				0xFE7A
+
+#define	XK_Pointer_Left					0xFEE0
+#define	XK_Pointer_Right				0xFEE1
+#define	XK_Pointer_Up					0xFEE2
+#define	XK_Pointer_Down					0xFEE3
+#define	XK_Pointer_UpLeft				0xFEE4
+#define	XK_Pointer_UpRight				0xFEE5
+#define	XK_Pointer_DownLeft				0xFEE6
+#define	XK_Pointer_DownRight				0xFEE7
+#define	XK_Pointer_Button_Dflt				0xFEE8
+#define	XK_Pointer_Button1				0xFEE9
+#define	XK_Pointer_Button2				0xFEEA
+#define	XK_Pointer_Button3				0xFEEB
+#define	XK_Pointer_Button4				0xFEEC
+#define	XK_Pointer_Button5				0xFEED
+#define	XK_Pointer_DblClick_Dflt			0xFEEE
+#define	XK_Pointer_DblClick1				0xFEEF
+#define	XK_Pointer_DblClick2				0xFEF0
+#define	XK_Pointer_DblClick3				0xFEF1
+#define	XK_Pointer_DblClick4				0xFEF2
+#define	XK_Pointer_DblClick5				0xFEF3
+#define	XK_Pointer_Drag_Dflt				0xFEF4
+#define	XK_Pointer_Drag1				0xFEF5
+#define	XK_Pointer_Drag2				0xFEF6
+#define	XK_Pointer_Drag3				0xFEF7
+#define	XK_Pointer_Drag4				0xFEF8
+#define	XK_Pointer_Drag5				0xFEFD
+
+#define	XK_Pointer_EnableKeys				0xFEF9
+#define	XK_Pointer_Accelerate				0xFEFA
+#define	XK_Pointer_DfltBtnNext				0xFEFB
+#define	XK_Pointer_DfltBtnPrev				0xFEFC
+
+#endif
+
+/*
+ * 3270 Terminal Keys
+ * Byte 3 = 0xFD
+ */
+
+#ifdef XK_3270
+#define XK_3270_Duplicate      0xFD01
+#define XK_3270_FieldMark      0xFD02
+#define XK_3270_Right2         0xFD03
+#define XK_3270_Left2          0xFD04
+#define XK_3270_BackTab        0xFD05
+#define XK_3270_EraseEOF       0xFD06
+#define XK_3270_EraseInput     0xFD07
+#define XK_3270_Reset          0xFD08
+#define XK_3270_Quit           0xFD09
+#define XK_3270_PA1            0xFD0A
+#define XK_3270_PA2            0xFD0B
+#define XK_3270_PA3            0xFD0C
+#define XK_3270_Test           0xFD0D
+#define XK_3270_Attn           0xFD0E
+#define XK_3270_CursorBlink    0xFD0F
+#define XK_3270_AltCursor      0xFD10
+#define XK_3270_KeyClick       0xFD11
+#define XK_3270_Jump           0xFD12
+#define XK_3270_Ident          0xFD13
+#define XK_3270_Rule           0xFD14
+#define XK_3270_Copy           0xFD15
+#define XK_3270_Play           0xFD16
+#define XK_3270_Setup          0xFD17
+#define XK_3270_Record         0xFD18
+#define XK_3270_ChangeScreen   0xFD19
+#define XK_3270_DeleteWord     0xFD1A
+#define XK_3270_ExSelect       0xFD1B
+#define XK_3270_CursorSelect   0xFD1C
+#define XK_3270_PrintScreen    0xFD1D
+#define XK_3270_Enter          0xFD1E
+#endif
+
+/*
+ *  Latin 1
+ *  Byte 3 = 0
+ */
+#ifdef XK_LATIN1
+#define XK_space               0x020
+#define XK_exclam              0x021
+#define XK_quotedbl            0x022
+#define XK_numbersign          0x023
+#define XK_dollar              0x024
+#define XK_percent             0x025
+#define XK_ampersand           0x026
+#define XK_apostrophe          0x027
+#define XK_quoteright          0x027	/* deprecated */
+#define XK_parenleft           0x028
+#define XK_parenright          0x029
+#define XK_asterisk            0x02a
+#define XK_plus                0x02b
+#define XK_comma               0x02c
+#define XK_minus               0x02d
+#define XK_period              0x02e
+#define XK_slash               0x02f
+#define XK_0                   0x030
+#define XK_1                   0x031
+#define XK_2                   0x032
+#define XK_3                   0x033
+#define XK_4                   0x034
+#define XK_5                   0x035
+#define XK_6                   0x036
+#define XK_7                   0x037
+#define XK_8                   0x038
+#define XK_9                   0x039
+#define XK_colon               0x03a
+#define XK_semicolon           0x03b
+#define XK_less                0x03c
+#define XK_equal               0x03d
+#define XK_greater             0x03e
+#define XK_question            0x03f
+#define XK_at                  0x040
+#define XK_A                   0x041
+#define XK_B                   0x042
+#define XK_C                   0x043
+#define XK_D                   0x044
+#define XK_E                   0x045
+#define XK_F                   0x046
+#define XK_G                   0x047
+#define XK_H                   0x048
+#define XK_I                   0x049
+#define XK_J                   0x04a
+#define XK_K                   0x04b
+#define XK_L                   0x04c
+#define XK_M                   0x04d
+#define XK_N                   0x04e
+#define XK_O                   0x04f
+#define XK_P                   0x050
+#define XK_Q                   0x051
+#define XK_R                   0x052
+#define XK_S                   0x053
+#define XK_T                   0x054
+#define XK_U                   0x055
+#define XK_V                   0x056
+#define XK_W                   0x057
+#define XK_X                   0x058
+#define XK_Y                   0x059
+#define XK_Z                   0x05a
+#define XK_bracketleft         0x05b
+#define XK_backslash           0x05c
+#define XK_bracketright        0x05d
+#define XK_asciicircum         0x05e
+#define XK_underscore          0x05f
+#define XK_grave               0x060
+#define XK_quoteleft           0x060	/* deprecated */
+#define XK_a                   0x061
+#define XK_b                   0x062
+#define XK_c                   0x063
+#define XK_d                   0x064
+#define XK_e                   0x065
+#define XK_f                   0x066
+#define XK_g                   0x067
+#define XK_h                   0x068
+#define XK_i                   0x069
+#define XK_j                   0x06a
+#define XK_k                   0x06b
+#define XK_l                   0x06c
+#define XK_m                   0x06d
+#define XK_n                   0x06e
+#define XK_o                   0x06f
+#define XK_p                   0x070
+#define XK_q                   0x071
+#define XK_r                   0x072
+#define XK_s                   0x073
+#define XK_t                   0x074
+#define XK_u                   0x075
+#define XK_v                   0x076
+#define XK_w                   0x077
+#define XK_x                   0x078
+#define XK_y                   0x079
+#define XK_z                   0x07a
+#define XK_braceleft           0x07b
+#define XK_bar                 0x07c
+#define XK_braceright          0x07d
+#define XK_asciitilde          0x07e
+
+#define XK_nobreakspace        0x0a0
+#define XK_exclamdown          0x0a1
+#define XK_cent        	       0x0a2
+#define XK_sterling            0x0a3
+#define XK_currency            0x0a4
+#define XK_yen                 0x0a5
+#define XK_brokenbar           0x0a6
+#define XK_section             0x0a7
+#define XK_diaeresis           0x0a8
+#define XK_copyright           0x0a9
+#define XK_ordfeminine         0x0aa
+#define XK_guillemotleft       0x0ab	/* left angle quotation mark */
+#define XK_notsign             0x0ac
+#define XK_hyphen              0x0ad
+#define XK_registered          0x0ae
+#define XK_macron              0x0af
+#define XK_degree              0x0b0
+#define XK_plusminus           0x0b1
+#define XK_twosuperior         0x0b2
+#define XK_threesuperior       0x0b3
+#define XK_acute               0x0b4
+#define XK_mu                  0x0b5
+#define XK_paragraph           0x0b6
+#define XK_periodcentered      0x0b7
+#define XK_cedilla             0x0b8
+#define XK_onesuperior         0x0b9
+#define XK_masculine           0x0ba
+#define XK_guillemotright      0x0bb	/* right angle quotation mark */
+#define XK_onequarter          0x0bc
+#define XK_onehalf             0x0bd
+#define XK_threequarters       0x0be
+#define XK_questiondown        0x0bf
+#define XK_Agrave              0x0c0
+#define XK_Aacute              0x0c1
+#define XK_Acircumflex         0x0c2
+#define XK_Atilde              0x0c3
+#define XK_Adiaeresis          0x0c4
+#define XK_Aring               0x0c5
+#define XK_AE                  0x0c6
+#define XK_Ccedilla            0x0c7
+#define XK_Egrave              0x0c8
+#define XK_Eacute              0x0c9
+#define XK_Ecircumflex         0x0ca
+#define XK_Ediaeresis          0x0cb
+#define XK_Igrave              0x0cc
+#define XK_Iacute              0x0cd
+#define XK_Icircumflex         0x0ce
+#define XK_Idiaeresis          0x0cf
+#define XK_ETH                 0x0d0
+#define XK_Eth                 0x0d0	/* deprecated */
+#define XK_Ntilde              0x0d1
+#define XK_Ograve              0x0d2
+#define XK_Oacute              0x0d3
+#define XK_Ocircumflex         0x0d4
+#define XK_Otilde              0x0d5
+#define XK_Odiaeresis          0x0d6
+#define XK_multiply            0x0d7
+#define XK_Ooblique            0x0d8
+#define XK_Ugrave              0x0d9
+#define XK_Uacute              0x0da
+#define XK_Ucircumflex         0x0db
+#define XK_Udiaeresis          0x0dc
+#define XK_Yacute              0x0dd
+#define XK_THORN               0x0de
+#define XK_Thorn               0x0de	/* deprecated */
+#define XK_ssharp              0x0df
+#define XK_agrave              0x0e0
+#define XK_aacute              0x0e1
+#define XK_acircumflex         0x0e2
+#define XK_atilde              0x0e3
+#define XK_adiaeresis          0x0e4
+#define XK_aring               0x0e5
+#define XK_ae                  0x0e6
+#define XK_ccedilla            0x0e7
+#define XK_egrave              0x0e8
+#define XK_eacute              0x0e9
+#define XK_ecircumflex         0x0ea
+#define XK_ediaeresis          0x0eb
+#define XK_igrave              0x0ec
+#define XK_iacute              0x0ed
+#define XK_icircumflex         0x0ee
+#define XK_idiaeresis          0x0ef
+#define XK_eth                 0x0f0
+#define XK_ntilde              0x0f1
+#define XK_ograve              0x0f2
+#define XK_oacute              0x0f3
+#define XK_ocircumflex         0x0f4
+#define XK_otilde              0x0f5
+#define XK_odiaeresis          0x0f6
+#define XK_division            0x0f7
+#define XK_oslash              0x0f8
+#define XK_ugrave              0x0f9
+#define XK_uacute              0x0fa
+#define XK_ucircumflex         0x0fb
+#define XK_udiaeresis          0x0fc
+#define XK_yacute              0x0fd
+#define XK_thorn               0x0fe
+#define XK_ydiaeresis          0x0ff
+#endif /* XK_LATIN1 */
+
+/*
+ *   Latin 2
+ *   Byte 3 = 1
+ */
+
+#ifdef XK_LATIN2
+#define XK_Aogonek             0x1a1
+#define XK_breve               0x1a2
+#define XK_Lstroke             0x1a3
+#define XK_Lcaron              0x1a5
+#define XK_Sacute              0x1a6
+#define XK_Scaron              0x1a9
+#define XK_Scedilla            0x1aa
+#define XK_Tcaron              0x1ab
+#define XK_Zacute              0x1ac
+#define XK_Zcaron              0x1ae
+#define XK_Zabovedot           0x1af
+#define XK_aogonek             0x1b1
+#define XK_ogonek              0x1b2
+#define XK_lstroke             0x1b3
+#define XK_lcaron              0x1b5
+#define XK_sacute              0x1b6
+#define XK_caron               0x1b7
+#define XK_scaron              0x1b9
+#define XK_scedilla            0x1ba
+#define XK_tcaron              0x1bb
+#define XK_zacute              0x1bc
+#define XK_doubleacute         0x1bd
+#define XK_zcaron              0x1be
+#define XK_zabovedot           0x1bf
+#define XK_Racute              0x1c0
+#define XK_Abreve              0x1c3
+#define XK_Lacute              0x1c5
+#define XK_Cacute              0x1c6
+#define XK_Ccaron              0x1c8
+#define XK_Eogonek             0x1ca
+#define XK_Ecaron              0x1cc
+#define XK_Dcaron              0x1cf
+#define XK_Dstroke             0x1d0
+#define XK_Nacute              0x1d1
+#define XK_Ncaron              0x1d2
+#define XK_Odoubleacute        0x1d5
+#define XK_Rcaron              0x1d8
+#define XK_Uring               0x1d9
+#define XK_Udoubleacute        0x1db
+#define XK_Tcedilla            0x1de
+#define XK_racute              0x1e0
+#define XK_abreve              0x1e3
+#define XK_lacute              0x1e5
+#define XK_cacute              0x1e6
+#define XK_ccaron              0x1e8
+#define XK_eogonek             0x1ea
+#define XK_ecaron              0x1ec
+#define XK_dcaron              0x1ef
+#define XK_dstroke             0x1f0
+#define XK_nacute              0x1f1
+#define XK_ncaron              0x1f2
+#define XK_odoubleacute        0x1f5
+#define XK_udoubleacute        0x1fb
+#define XK_rcaron              0x1f8
+#define XK_uring               0x1f9
+#define XK_tcedilla            0x1fe
+#define XK_abovedot            0x1ff
+#endif /* XK_LATIN2 */
+
+/*
+ *   Latin 3
+ *   Byte 3 = 2
+ */
+
+#ifdef XK_LATIN3
+#define XK_Hstroke             0x2a1
+#define XK_Hcircumflex         0x2a6
+#define XK_Iabovedot           0x2a9
+#define XK_Gbreve              0x2ab
+#define XK_Jcircumflex         0x2ac
+#define XK_hstroke             0x2b1
+#define XK_hcircumflex         0x2b6
+#define XK_idotless            0x2b9
+#define XK_gbreve              0x2bb
+#define XK_jcircumflex         0x2bc
+#define XK_Cabovedot           0x2c5
+#define XK_Ccircumflex         0x2c6
+#define XK_Gabovedot           0x2d5
+#define XK_Gcircumflex         0x2d8
+#define XK_Ubreve              0x2dd
+#define XK_Scircumflex         0x2de
+#define XK_cabovedot           0x2e5
+#define XK_ccircumflex         0x2e6
+#define XK_gabovedot           0x2f5
+#define XK_gcircumflex         0x2f8
+#define XK_ubreve              0x2fd
+#define XK_scircumflex         0x2fe
+#endif /* XK_LATIN3 */
+
+
+/*
+ *   Latin 4
+ *   Byte 3 = 3
+ */
+
+#ifdef XK_LATIN4
+#define XK_kra                 0x3a2
+#define XK_kappa               0x3a2	/* deprecated */
+#define XK_Rcedilla            0x3a3
+#define XK_Itilde              0x3a5
+#define XK_Lcedilla            0x3a6
+#define XK_Emacron             0x3aa
+#define XK_Gcedilla            0x3ab
+#define XK_Tslash              0x3ac
+#define XK_rcedilla            0x3b3
+#define XK_itilde              0x3b5
+#define XK_lcedilla            0x3b6
+#define XK_emacron             0x3ba
+#define XK_gcedilla            0x3bb
+#define XK_tslash              0x3bc
+#define XK_ENG                 0x3bd
+#define XK_eng                 0x3bf
+#define XK_Amacron             0x3c0
+#define XK_Iogonek             0x3c7
+#define XK_Eabovedot           0x3cc
+#define XK_Imacron             0x3cf
+#define XK_Ncedilla            0x3d1
+#define XK_Omacron             0x3d2
+#define XK_Kcedilla            0x3d3
+#define XK_Uogonek             0x3d9
+#define XK_Utilde              0x3dd
+#define XK_Umacron             0x3de
+#define XK_amacron             0x3e0
+#define XK_iogonek             0x3e7
+#define XK_eabovedot           0x3ec
+#define XK_imacron             0x3ef
+#define XK_ncedilla            0x3f1
+#define XK_omacron             0x3f2
+#define XK_kcedilla            0x3f3
+#define XK_uogonek             0x3f9
+#define XK_utilde              0x3fd
+#define XK_umacron             0x3fe
+#endif /* XK_LATIN4 */
+
+/*
+ * Latin-9 (a.k.a. Latin-0)
+ * Byte 3 = 19
+ */
+
+#ifdef XK_LATIN9
+#define XK_OE                  0x13bc
+#define XK_oe                  0x13bd
+#define XK_Ydiaeresis          0x13be
+#endif /* XK_LATIN9 */
+
+/*
+ * Katakana
+ * Byte 3 = 4
+ */
+
+#ifdef XK_KATAKANA
+#define XK_overline				       0x47e
+#define XK_kana_fullstop                               0x4a1
+#define XK_kana_openingbracket                         0x4a2
+#define XK_kana_closingbracket                         0x4a3
+#define XK_kana_comma                                  0x4a4
+#define XK_kana_conjunctive                            0x4a5
+#define XK_kana_middledot                              0x4a5  /* deprecated */
+#define XK_kana_WO                                     0x4a6
+#define XK_kana_a                                      0x4a7
+#define XK_kana_i                                      0x4a8
+#define XK_kana_u                                      0x4a9
+#define XK_kana_e                                      0x4aa
+#define XK_kana_o                                      0x4ab
+#define XK_kana_ya                                     0x4ac
+#define XK_kana_yu                                     0x4ad
+#define XK_kana_yo                                     0x4ae
+#define XK_kana_tsu                                    0x4af
+#define XK_kana_tu                                     0x4af  /* deprecated */
+#define XK_prolongedsound                              0x4b0
+#define XK_kana_A                                      0x4b1
+#define XK_kana_I                                      0x4b2
+#define XK_kana_U                                      0x4b3
+#define XK_kana_E                                      0x4b4
+#define XK_kana_O                                      0x4b5
+#define XK_kana_KA                                     0x4b6
+#define XK_kana_KI                                     0x4b7
+#define XK_kana_KU                                     0x4b8
+#define XK_kana_KE                                     0x4b9
+#define XK_kana_KO                                     0x4ba
+#define XK_kana_SA                                     0x4bb
+#define XK_kana_SHI                                    0x4bc
+#define XK_kana_SU                                     0x4bd
+#define XK_kana_SE                                     0x4be
+#define XK_kana_SO                                     0x4bf
+#define XK_kana_TA                                     0x4c0
+#define XK_kana_CHI                                    0x4c1
+#define XK_kana_TI                                     0x4c1  /* deprecated */
+#define XK_kana_TSU                                    0x4c2
+#define XK_kana_TU                                     0x4c2  /* deprecated */
+#define XK_kana_TE                                     0x4c3
+#define XK_kana_TO                                     0x4c4
+#define XK_kana_NA                                     0x4c5
+#define XK_kana_NI                                     0x4c6
+#define XK_kana_NU                                     0x4c7
+#define XK_kana_NE                                     0x4c8
+#define XK_kana_NO                                     0x4c9
+#define XK_kana_HA                                     0x4ca
+#define XK_kana_HI                                     0x4cb
+#define XK_kana_FU                                     0x4cc
+#define XK_kana_HU                                     0x4cc  /* deprecated */
+#define XK_kana_HE                                     0x4cd
+#define XK_kana_HO                                     0x4ce
+#define XK_kana_MA                                     0x4cf
+#define XK_kana_MI                                     0x4d0
+#define XK_kana_MU                                     0x4d1
+#define XK_kana_ME                                     0x4d2
+#define XK_kana_MO                                     0x4d3
+#define XK_kana_YA                                     0x4d4
+#define XK_kana_YU                                     0x4d5
+#define XK_kana_YO                                     0x4d6
+#define XK_kana_RA                                     0x4d7
+#define XK_kana_RI                                     0x4d8
+#define XK_kana_RU                                     0x4d9
+#define XK_kana_RE                                     0x4da
+#define XK_kana_RO                                     0x4db
+#define XK_kana_WA                                     0x4dc
+#define XK_kana_N                                      0x4dd
+#define XK_voicedsound                                 0x4de
+#define XK_semivoicedsound                             0x4df
+#define XK_kana_switch          0xFF7E  /* Alias for mode_switch */
+#endif /* XK_KATAKANA */
+
+/*
+ *  Arabic
+ *  Byte 3 = 5
+ */
+
+#ifdef XK_ARABIC
+#define XK_Arabic_comma                                0x5ac
+#define XK_Arabic_semicolon                            0x5bb
+#define XK_Arabic_question_mark                        0x5bf
+#define XK_Arabic_hamza                                0x5c1
+#define XK_Arabic_maddaonalef                          0x5c2
+#define XK_Arabic_hamzaonalef                          0x5c3
+#define XK_Arabic_hamzaonwaw                           0x5c4
+#define XK_Arabic_hamzaunderalef                       0x5c5
+#define XK_Arabic_hamzaonyeh                           0x5c6
+#define XK_Arabic_alef                                 0x5c7
+#define XK_Arabic_beh                                  0x5c8
+#define XK_Arabic_tehmarbuta                           0x5c9
+#define XK_Arabic_teh                                  0x5ca
+#define XK_Arabic_theh                                 0x5cb
+#define XK_Arabic_jeem                                 0x5cc
+#define XK_Arabic_hah                                  0x5cd
+#define XK_Arabic_khah                                 0x5ce
+#define XK_Arabic_dal                                  0x5cf
+#define XK_Arabic_thal                                 0x5d0
+#define XK_Arabic_ra                                   0x5d1
+#define XK_Arabic_zain                                 0x5d2
+#define XK_Arabic_seen                                 0x5d3
+#define XK_Arabic_sheen                                0x5d4
+#define XK_Arabic_sad                                  0x5d5
+#define XK_Arabic_dad                                  0x5d6
+#define XK_Arabic_tah                                  0x5d7
+#define XK_Arabic_zah                                  0x5d8
+#define XK_Arabic_ain                                  0x5d9
+#define XK_Arabic_ghain                                0x5da
+#define XK_Arabic_tatweel                              0x5e0
+#define XK_Arabic_feh                                  0x5e1
+#define XK_Arabic_qaf                                  0x5e2
+#define XK_Arabic_kaf                                  0x5e3
+#define XK_Arabic_lam                                  0x5e4
+#define XK_Arabic_meem                                 0x5e5
+#define XK_Arabic_noon                                 0x5e6
+#define XK_Arabic_ha                                   0x5e7
+#define XK_Arabic_heh                                  0x5e7  /* deprecated */
+#define XK_Arabic_waw                                  0x5e8
+#define XK_Arabic_alefmaksura                          0x5e9
+#define XK_Arabic_yeh                                  0x5ea
+#define XK_Arabic_fathatan                             0x5eb
+#define XK_Arabic_dammatan                             0x5ec
+#define XK_Arabic_kasratan                             0x5ed
+#define XK_Arabic_fatha                                0x5ee
+#define XK_Arabic_damma                                0x5ef
+#define XK_Arabic_kasra                                0x5f0
+#define XK_Arabic_shadda                               0x5f1
+#define XK_Arabic_sukun                                0x5f2
+#define XK_Arabic_switch        0xFF7E  /* Alias for mode_switch */
+#endif /* XK_ARABIC */
+
+/*
+ * Cyrillic
+ * Byte 3 = 6
+ */
+#ifdef XK_CYRILLIC
+#define XK_Serbian_dje                                 0x6a1
+#define XK_Macedonia_gje                               0x6a2
+#define XK_Cyrillic_io                                 0x6a3
+#define XK_Ukrainian_ie                                0x6a4
+#define XK_Ukranian_je                                 0x6a4  /* deprecated */
+#define XK_Macedonia_dse                               0x6a5
+#define XK_Ukrainian_i                                 0x6a6
+#define XK_Ukranian_i                                  0x6a6  /* deprecated */
+#define XK_Ukrainian_yi                                0x6a7
+#define XK_Ukranian_yi                                 0x6a7  /* deprecated */
+#define XK_Cyrillic_je                                 0x6a8
+#define XK_Serbian_je                                  0x6a8  /* deprecated */
+#define XK_Cyrillic_lje                                0x6a9
+#define XK_Serbian_lje                                 0x6a9  /* deprecated */
+#define XK_Cyrillic_nje                                0x6aa
+#define XK_Serbian_nje                                 0x6aa  /* deprecated */
+#define XK_Serbian_tshe                                0x6ab
+#define XK_Macedonia_kje                               0x6ac
+#define XK_Byelorussian_shortu                         0x6ae
+#define XK_Cyrillic_dzhe                               0x6af
+#define XK_Serbian_dze                                 0x6af  /* deprecated */
+#define XK_numerosign                                  0x6b0
+#define XK_Serbian_DJE                                 0x6b1
+#define XK_Macedonia_GJE                               0x6b2
+#define XK_Cyrillic_IO                                 0x6b3
+#define XK_Ukrainian_IE                                0x6b4
+#define XK_Ukranian_JE                                 0x6b4  /* deprecated */
+#define XK_Macedonia_DSE                               0x6b5
+#define XK_Ukrainian_I                                 0x6b6
+#define XK_Ukranian_I                                  0x6b6  /* deprecated */
+#define XK_Ukrainian_YI                                0x6b7
+#define XK_Ukranian_YI                                 0x6b7  /* deprecated */
+#define XK_Cyrillic_JE                                 0x6b8
+#define XK_Serbian_JE                                  0x6b8  /* deprecated */
+#define XK_Cyrillic_LJE                                0x6b9
+#define XK_Serbian_LJE                                 0x6b9  /* deprecated */
+#define XK_Cyrillic_NJE                                0x6ba
+#define XK_Serbian_NJE                                 0x6ba  /* deprecated */
+#define XK_Serbian_TSHE                                0x6bb
+#define XK_Macedonia_KJE                               0x6bc
+#define XK_Byelorussian_SHORTU                         0x6be
+#define XK_Cyrillic_DZHE                               0x6bf
+#define XK_Serbian_DZE                                 0x6bf  /* deprecated */
+#define XK_Cyrillic_yu                                 0x6c0
+#define XK_Cyrillic_a                                  0x6c1
+#define XK_Cyrillic_be                                 0x6c2
+#define XK_Cyrillic_tse                                0x6c3
+#define XK_Cyrillic_de                                 0x6c4
+#define XK_Cyrillic_ie                                 0x6c5
+#define XK_Cyrillic_ef                                 0x6c6
+#define XK_Cyrillic_ghe                                0x6c7
+#define XK_Cyrillic_ha                                 0x6c8
+#define XK_Cyrillic_i                                  0x6c9
+#define XK_Cyrillic_shorti                             0x6ca
+#define XK_Cyrillic_ka                                 0x6cb
+#define XK_Cyrillic_el                                 0x6cc
+#define XK_Cyrillic_em                                 0x6cd
+#define XK_Cyrillic_en                                 0x6ce
+#define XK_Cyrillic_o                                  0x6cf
+#define XK_Cyrillic_pe                                 0x6d0
+#define XK_Cyrillic_ya                                 0x6d1
+#define XK_Cyrillic_er                                 0x6d2
+#define XK_Cyrillic_es                                 0x6d3
+#define XK_Cyrillic_te                                 0x6d4
+#define XK_Cyrillic_u                                  0x6d5
+#define XK_Cyrillic_zhe                                0x6d6
+#define XK_Cyrillic_ve                                 0x6d7
+#define XK_Cyrillic_softsign                           0x6d8
+#define XK_Cyrillic_yeru                               0x6d9
+#define XK_Cyrillic_ze                                 0x6da
+#define XK_Cyrillic_sha                                0x6db
+#define XK_Cyrillic_e                                  0x6dc
+#define XK_Cyrillic_shcha                              0x6dd
+#define XK_Cyrillic_che                                0x6de
+#define XK_Cyrillic_hardsign                           0x6df
+#define XK_Cyrillic_YU                                 0x6e0
+#define XK_Cyrillic_A                                  0x6e1
+#define XK_Cyrillic_BE                                 0x6e2
+#define XK_Cyrillic_TSE                                0x6e3
+#define XK_Cyrillic_DE                                 0x6e4
+#define XK_Cyrillic_IE                                 0x6e5
+#define XK_Cyrillic_EF                                 0x6e6
+#define XK_Cyrillic_GHE                                0x6e7
+#define XK_Cyrillic_HA                                 0x6e8
+#define XK_Cyrillic_I                                  0x6e9
+#define XK_Cyrillic_SHORTI                             0x6ea
+#define XK_Cyrillic_KA                                 0x6eb
+#define XK_Cyrillic_EL                                 0x6ec
+#define XK_Cyrillic_EM                                 0x6ed
+#define XK_Cyrillic_EN                                 0x6ee
+#define XK_Cyrillic_O                                  0x6ef
+#define XK_Cyrillic_PE                                 0x6f0
+#define XK_Cyrillic_YA                                 0x6f1
+#define XK_Cyrillic_ER                                 0x6f2
+#define XK_Cyrillic_ES                                 0x6f3
+#define XK_Cyrillic_TE                                 0x6f4
+#define XK_Cyrillic_U                                  0x6f5
+#define XK_Cyrillic_ZHE                                0x6f6
+#define XK_Cyrillic_VE                                 0x6f7
+#define XK_Cyrillic_SOFTSIGN                           0x6f8
+#define XK_Cyrillic_YERU                               0x6f9
+#define XK_Cyrillic_ZE                                 0x6fa
+#define XK_Cyrillic_SHA                                0x6fb
+#define XK_Cyrillic_E                                  0x6fc
+#define XK_Cyrillic_SHCHA                              0x6fd
+#define XK_Cyrillic_CHE                                0x6fe
+#define XK_Cyrillic_HARDSIGN                           0x6ff
+#endif /* XK_CYRILLIC */
+
+/*
+ * Greek
+ * Byte 3 = 7
+ */
+
+#ifdef XK_GREEK
+#define XK_Greek_ALPHAaccent                           0x7a1
+#define XK_Greek_EPSILONaccent                         0x7a2
+#define XK_Greek_ETAaccent                             0x7a3
+#define XK_Greek_IOTAaccent                            0x7a4
+#define XK_Greek_IOTAdiaeresis                         0x7a5
+#define XK_Greek_OMICRONaccent                         0x7a7
+#define XK_Greek_UPSILONaccent                         0x7a8
+#define XK_Greek_UPSILONdieresis                       0x7a9
+#define XK_Greek_OMEGAaccent                           0x7ab
+#define XK_Greek_accentdieresis                        0x7ae
+#define XK_Greek_horizbar                              0x7af
+#define XK_Greek_alphaaccent                           0x7b1
+#define XK_Greek_epsilonaccent                         0x7b2
+#define XK_Greek_etaaccent                             0x7b3
+#define XK_Greek_iotaaccent                            0x7b4
+#define XK_Greek_iotadieresis                          0x7b5
+#define XK_Greek_iotaaccentdieresis                    0x7b6
+#define XK_Greek_omicronaccent                         0x7b7
+#define XK_Greek_upsilonaccent                         0x7b8
+#define XK_Greek_upsilondieresis                       0x7b9
+#define XK_Greek_upsilonaccentdieresis                 0x7ba
+#define XK_Greek_omegaaccent                           0x7bb
+#define XK_Greek_ALPHA                                 0x7c1
+#define XK_Greek_BETA                                  0x7c2
+#define XK_Greek_GAMMA                                 0x7c3
+#define XK_Greek_DELTA                                 0x7c4
+#define XK_Greek_EPSILON                               0x7c5
+#define XK_Greek_ZETA                                  0x7c6
+#define XK_Greek_ETA                                   0x7c7
+#define XK_Greek_THETA                                 0x7c8
+#define XK_Greek_IOTA                                  0x7c9
+#define XK_Greek_KAPPA                                 0x7ca
+#define XK_Greek_LAMDA                                 0x7cb
+#define XK_Greek_LAMBDA                                0x7cb
+#define XK_Greek_MU                                    0x7cc
+#define XK_Greek_NU                                    0x7cd
+#define XK_Greek_XI                                    0x7ce
+#define XK_Greek_OMICRON                               0x7cf
+#define XK_Greek_PI                                    0x7d0
+#define XK_Greek_RHO                                   0x7d1
+#define XK_Greek_SIGMA                                 0x7d2
+#define XK_Greek_TAU                                   0x7d4
+#define XK_Greek_UPSILON                               0x7d5
+#define XK_Greek_PHI                                   0x7d6
+#define XK_Greek_CHI                                   0x7d7
+#define XK_Greek_PSI                                   0x7d8
+#define XK_Greek_OMEGA                                 0x7d9
+#define XK_Greek_alpha                                 0x7e1
+#define XK_Greek_beta                                  0x7e2
+#define XK_Greek_gamma                                 0x7e3
+#define XK_Greek_delta                                 0x7e4
+#define XK_Greek_epsilon                               0x7e5
+#define XK_Greek_zeta                                  0x7e6
+#define XK_Greek_eta                                   0x7e7
+#define XK_Greek_theta                                 0x7e8
+#define XK_Greek_iota                                  0x7e9
+#define XK_Greek_kappa                                 0x7ea
+#define XK_Greek_lamda                                 0x7eb
+#define XK_Greek_lambda                                0x7eb
+#define XK_Greek_mu                                    0x7ec
+#define XK_Greek_nu                                    0x7ed
+#define XK_Greek_xi                                    0x7ee
+#define XK_Greek_omicron                               0x7ef
+#define XK_Greek_pi                                    0x7f0
+#define XK_Greek_rho                                   0x7f1
+#define XK_Greek_sigma                                 0x7f2
+#define XK_Greek_finalsmallsigma                       0x7f3
+#define XK_Greek_tau                                   0x7f4
+#define XK_Greek_upsilon                               0x7f5
+#define XK_Greek_phi                                   0x7f6
+#define XK_Greek_chi                                   0x7f7
+#define XK_Greek_psi                                   0x7f8
+#define XK_Greek_omega                                 0x7f9
+#define XK_Greek_switch         0xFF7E  /* Alias for mode_switch */
+#endif /* XK_GREEK */
+
+/*
+ * Technical
+ * Byte 3 = 8
+ */
+
+#ifdef XK_TECHNICAL
+#define XK_leftradical                                 0x8a1
+#define XK_topleftradical                              0x8a2
+#define XK_horizconnector                              0x8a3
+#define XK_topintegral                                 0x8a4
+#define XK_botintegral                                 0x8a5
+#define XK_vertconnector                               0x8a6
+#define XK_topleftsqbracket                            0x8a7
+#define XK_botleftsqbracket                            0x8a8
+#define XK_toprightsqbracket                           0x8a9
+#define XK_botrightsqbracket                           0x8aa
+#define XK_topleftparens                               0x8ab
+#define XK_botleftparens                               0x8ac
+#define XK_toprightparens                              0x8ad
+#define XK_botrightparens                              0x8ae
+#define XK_leftmiddlecurlybrace                        0x8af
+#define XK_rightmiddlecurlybrace                       0x8b0
+#define XK_topleftsummation                            0x8b1
+#define XK_botleftsummation                            0x8b2
+#define XK_topvertsummationconnector                   0x8b3
+#define XK_botvertsummationconnector                   0x8b4
+#define XK_toprightsummation                           0x8b5
+#define XK_botrightsummation                           0x8b6
+#define XK_rightmiddlesummation                        0x8b7
+#define XK_lessthanequal                               0x8bc
+#define XK_notequal                                    0x8bd
+#define XK_greaterthanequal                            0x8be
+#define XK_integral                                    0x8bf
+#define XK_therefore                                   0x8c0
+#define XK_variation                                   0x8c1
+#define XK_infinity                                    0x8c2
+#define XK_nabla                                       0x8c5
+#define XK_approximate                                 0x8c8
+#define XK_similarequal                                0x8c9
+#define XK_ifonlyif                                    0x8cd
+#define XK_implies                                     0x8ce
+#define XK_identical                                   0x8cf
+#define XK_radical                                     0x8d6
+#define XK_includedin                                  0x8da
+#define XK_includes                                    0x8db
+#define XK_intersection                                0x8dc
+#define XK_union                                       0x8dd
+#define XK_logicaland                                  0x8de
+#define XK_logicalor                                   0x8df
+#define XK_partialderivative                           0x8ef
+#define XK_function                                    0x8f6
+#define XK_leftarrow                                   0x8fb
+#define XK_uparrow                                     0x8fc
+#define XK_rightarrow                                  0x8fd
+#define XK_downarrow                                   0x8fe
+#endif /* XK_TECHNICAL */
+
+/*
+ *  Special
+ *  Byte 3 = 9
+ */
+
+#ifdef XK_SPECIAL
+#define XK_blank                                       0x9df
+#define XK_soliddiamond                                0x9e0
+#define XK_checkerboard                                0x9e1
+#define XK_ht                                          0x9e2
+#define XK_ff                                          0x9e3
+#define XK_cr                                          0x9e4
+#define XK_lf                                          0x9e5
+#define XK_nl                                          0x9e8
+#define XK_vt                                          0x9e9
+#define XK_lowrightcorner                              0x9ea
+#define XK_uprightcorner                               0x9eb
+#define XK_upleftcorner                                0x9ec
+#define XK_lowleftcorner                               0x9ed
+#define XK_crossinglines                               0x9ee
+#define XK_horizlinescan1                              0x9ef
+#define XK_horizlinescan3                              0x9f0
+#define XK_horizlinescan5                              0x9f1
+#define XK_horizlinescan7                              0x9f2
+#define XK_horizlinescan9                              0x9f3
+#define XK_leftt                                       0x9f4
+#define XK_rightt                                      0x9f5
+#define XK_bott                                        0x9f6
+#define XK_topt                                        0x9f7
+#define XK_vertbar                                     0x9f8
+#endif /* XK_SPECIAL */
+
+/*
+ *  Publishing
+ *  Byte 3 = a
+ */
+
+#ifdef XK_PUBLISHING
+#define XK_emspace                                     0xaa1
+#define XK_enspace                                     0xaa2
+#define XK_em3space                                    0xaa3
+#define XK_em4space                                    0xaa4
+#define XK_digitspace                                  0xaa5
+#define XK_punctspace                                  0xaa6
+#define XK_thinspace                                   0xaa7
+#define XK_hairspace                                   0xaa8
+#define XK_emdash                                      0xaa9
+#define XK_endash                                      0xaaa
+#define XK_signifblank                                 0xaac
+#define XK_ellipsis                                    0xaae
+#define XK_doubbaselinedot                             0xaaf
+#define XK_onethird                                    0xab0
+#define XK_twothirds                                   0xab1
+#define XK_onefifth                                    0xab2
+#define XK_twofifths                                   0xab3
+#define XK_threefifths                                 0xab4
+#define XK_fourfifths                                  0xab5
+#define XK_onesixth                                    0xab6
+#define XK_fivesixths                                  0xab7
+#define XK_careof                                      0xab8
+#define XK_figdash                                     0xabb
+#define XK_leftanglebracket                            0xabc
+#define XK_decimalpoint                                0xabd
+#define XK_rightanglebracket                           0xabe
+#define XK_marker                                      0xabf
+#define XK_oneeighth                                   0xac3
+#define XK_threeeighths                                0xac4
+#define XK_fiveeighths                                 0xac5
+#define XK_seveneighths                                0xac6
+#define XK_trademark                                   0xac9
+#define XK_signaturemark                               0xaca
+#define XK_trademarkincircle                           0xacb
+#define XK_leftopentriangle                            0xacc
+#define XK_rightopentriangle                           0xacd
+#define XK_emopencircle                                0xace
+#define XK_emopenrectangle                             0xacf
+#define XK_leftsinglequotemark                         0xad0
+#define XK_rightsinglequotemark                        0xad1
+#define XK_leftdoublequotemark                         0xad2
+#define XK_rightdoublequotemark                        0xad3
+#define XK_prescription                                0xad4
+#define XK_minutes                                     0xad6
+#define XK_seconds                                     0xad7
+#define XK_latincross                                  0xad9
+#define XK_hexagram                                    0xada
+#define XK_filledrectbullet                            0xadb
+#define XK_filledlefttribullet                         0xadc
+#define XK_filledrighttribullet                        0xadd
+#define XK_emfilledcircle                              0xade
+#define XK_emfilledrect                                0xadf
+#define XK_enopencircbullet                            0xae0
+#define XK_enopensquarebullet                          0xae1
+#define XK_openrectbullet                              0xae2
+#define XK_opentribulletup                             0xae3
+#define XK_opentribulletdown                           0xae4
+#define XK_openstar                                    0xae5
+#define XK_enfilledcircbullet                          0xae6
+#define XK_enfilledsqbullet                            0xae7
+#define XK_filledtribulletup                           0xae8
+#define XK_filledtribulletdown                         0xae9
+#define XK_leftpointer                                 0xaea
+#define XK_rightpointer                                0xaeb
+#define XK_club                                        0xaec
+#define XK_diamond                                     0xaed
+#define XK_heart                                       0xaee
+#define XK_maltesecross                                0xaf0
+#define XK_dagger                                      0xaf1
+#define XK_doubledagger                                0xaf2
+#define XK_checkmark                                   0xaf3
+#define XK_ballotcross                                 0xaf4
+#define XK_musicalsharp                                0xaf5
+#define XK_musicalflat                                 0xaf6
+#define XK_malesymbol                                  0xaf7
+#define XK_femalesymbol                                0xaf8
+#define XK_telephone                                   0xaf9
+#define XK_telephonerecorder                           0xafa
+#define XK_phonographcopyright                         0xafb
+#define XK_caret                                       0xafc
+#define XK_singlelowquotemark                          0xafd
+#define XK_doublelowquotemark                          0xafe
+#define XK_cursor                                      0xaff
+#endif /* XK_PUBLISHING */
+
+/*
+ *  APL
+ *  Byte 3 = b
+ */
+
+#ifdef XK_APL
+#define XK_leftcaret                                   0xba3
+#define XK_rightcaret                                  0xba6
+#define XK_downcaret                                   0xba8
+#define XK_upcaret                                     0xba9
+#define XK_overbar                                     0xbc0
+#define XK_downtack                                    0xbc2
+#define XK_upshoe                                      0xbc3
+#define XK_downstile                                   0xbc4
+#define XK_underbar                                    0xbc6
+#define XK_jot                                         0xbca
+#define XK_quad                                        0xbcc
+#define XK_uptack                                      0xbce
+#define XK_circle                                      0xbcf
+#define XK_upstile                                     0xbd3
+#define XK_downshoe                                    0xbd6
+#define XK_rightshoe                                   0xbd8
+#define XK_leftshoe                                    0xbda
+#define XK_lefttack                                    0xbdc
+#define XK_righttack                                   0xbfc
+#endif /* XK_APL */
+
+/*
+ * Hebrew
+ * Byte 3 = c
+ */
+
+#ifdef XK_HEBREW
+#define XK_hebrew_doublelowline                        0xcdf
+#define XK_hebrew_aleph                                0xce0
+#define XK_hebrew_bet                                  0xce1
+#define XK_hebrew_beth                                 0xce1  /* deprecated */
+#define XK_hebrew_gimel                                0xce2
+#define XK_hebrew_gimmel                               0xce2  /* deprecated */
+#define XK_hebrew_dalet                                0xce3
+#define XK_hebrew_daleth                               0xce3  /* deprecated */
+#define XK_hebrew_he                                   0xce4
+#define XK_hebrew_waw                                  0xce5
+#define XK_hebrew_zain                                 0xce6
+#define XK_hebrew_zayin                                0xce6  /* deprecated */
+#define XK_hebrew_chet                                 0xce7
+#define XK_hebrew_het                                  0xce7  /* deprecated */
+#define XK_hebrew_tet                                  0xce8
+#define XK_hebrew_teth                                 0xce8  /* deprecated */
+#define XK_hebrew_yod                                  0xce9
+#define XK_hebrew_finalkaph                            0xcea
+#define XK_hebrew_kaph                                 0xceb
+#define XK_hebrew_lamed                                0xcec
+#define XK_hebrew_finalmem                             0xced
+#define XK_hebrew_mem                                  0xcee
+#define XK_hebrew_finalnun                             0xcef
+#define XK_hebrew_nun                                  0xcf0
+#define XK_hebrew_samech                               0xcf1
+#define XK_hebrew_samekh                               0xcf1  /* deprecated */
+#define XK_hebrew_ayin                                 0xcf2
+#define XK_hebrew_finalpe                              0xcf3
+#define XK_hebrew_pe                                   0xcf4
+#define XK_hebrew_finalzade                            0xcf5
+#define XK_hebrew_finalzadi                            0xcf5  /* deprecated */
+#define XK_hebrew_zade                                 0xcf6
+#define XK_hebrew_zadi                                 0xcf6  /* deprecated */
+#define XK_hebrew_qoph                                 0xcf7
+#define XK_hebrew_kuf                                  0xcf7  /* deprecated */
+#define XK_hebrew_resh                                 0xcf8
+#define XK_hebrew_shin                                 0xcf9
+#define XK_hebrew_taw                                  0xcfa
+#define XK_hebrew_taf                                  0xcfa  /* deprecated */
+#define XK_Hebrew_switch        0xFF7E  /* Alias for mode_switch */
+#endif /* XK_HEBREW */
+
+/*
+ * Thai
+ * Byte 3 = d
+ */
+
+#ifdef XK_THAI
+#define XK_Thai_kokai					0xda1
+#define XK_Thai_khokhai					0xda2
+#define XK_Thai_khokhuat				0xda3
+#define XK_Thai_khokhwai				0xda4
+#define XK_Thai_khokhon					0xda5
+#define XK_Thai_khorakhang			        0xda6  
+#define XK_Thai_ngongu					0xda7  
+#define XK_Thai_chochan					0xda8  
+#define XK_Thai_choching				0xda9   
+#define XK_Thai_chochang				0xdaa  
+#define XK_Thai_soso					0xdab
+#define XK_Thai_chochoe					0xdac
+#define XK_Thai_yoying					0xdad
+#define XK_Thai_dochada					0xdae
+#define XK_Thai_topatak					0xdaf
+#define XK_Thai_thothan					0xdb0
+#define XK_Thai_thonangmontho			        0xdb1
+#define XK_Thai_thophuthao			        0xdb2
+#define XK_Thai_nonen					0xdb3
+#define XK_Thai_dodek					0xdb4
+#define XK_Thai_totao					0xdb5
+#define XK_Thai_thothung				0xdb6
+#define XK_Thai_thothahan				0xdb7
+#define XK_Thai_thothong	 			0xdb8
+#define XK_Thai_nonu					0xdb9
+#define XK_Thai_bobaimai				0xdba
+#define XK_Thai_popla					0xdbb
+#define XK_Thai_phophung				0xdbc
+#define XK_Thai_fofa					0xdbd
+#define XK_Thai_phophan					0xdbe
+#define XK_Thai_fofan					0xdbf
+#define XK_Thai_phosamphao			        0xdc0
+#define XK_Thai_moma					0xdc1
+#define XK_Thai_yoyak					0xdc2
+#define XK_Thai_rorua					0xdc3
+#define XK_Thai_ru					0xdc4
+#define XK_Thai_loling					0xdc5
+#define XK_Thai_lu					0xdc6
+#define XK_Thai_wowaen					0xdc7
+#define XK_Thai_sosala					0xdc8
+#define XK_Thai_sorusi					0xdc9
+#define XK_Thai_sosua					0xdca
+#define XK_Thai_hohip					0xdcb
+#define XK_Thai_lochula					0xdcc
+#define XK_Thai_oang					0xdcd
+#define XK_Thai_honokhuk				0xdce
+#define XK_Thai_paiyannoi				0xdcf
+#define XK_Thai_saraa					0xdd0
+#define XK_Thai_maihanakat				0xdd1
+#define XK_Thai_saraaa					0xdd2
+#define XK_Thai_saraam					0xdd3
+#define XK_Thai_sarai					0xdd4   
+#define XK_Thai_saraii					0xdd5   
+#define XK_Thai_saraue					0xdd6    
+#define XK_Thai_sarauee					0xdd7    
+#define XK_Thai_sarau					0xdd8    
+#define XK_Thai_sarauu					0xdd9   
+#define XK_Thai_phinthu					0xdda
+#define XK_Thai_maihanakat_maitho   			0xdde
+#define XK_Thai_baht					0xddf
+#define XK_Thai_sarae					0xde0    
+#define XK_Thai_saraae					0xde1
+#define XK_Thai_sarao					0xde2
+#define XK_Thai_saraaimaimuan				0xde3   
+#define XK_Thai_saraaimaimalai				0xde4  
+#define XK_Thai_lakkhangyao				0xde5
+#define XK_Thai_maiyamok				0xde6
+#define XK_Thai_maitaikhu				0xde7
+#define XK_Thai_maiek					0xde8   
+#define XK_Thai_maitho					0xde9
+#define XK_Thai_maitri					0xdea
+#define XK_Thai_maichattawa				0xdeb
+#define XK_Thai_thanthakhat				0xdec
+#define XK_Thai_nikhahit				0xded
+#define XK_Thai_leksun					0xdf0 
+#define XK_Thai_leknung					0xdf1  
+#define XK_Thai_leksong					0xdf2 
+#define XK_Thai_leksam					0xdf3
+#define XK_Thai_leksi					0xdf4  
+#define XK_Thai_lekha					0xdf5  
+#define XK_Thai_lekhok					0xdf6  
+#define XK_Thai_lekchet					0xdf7  
+#define XK_Thai_lekpaet					0xdf8  
+#define XK_Thai_lekkao					0xdf9 
+#endif /* XK_THAI */
+
+/*
+ *   Korean
+ *   Byte 3 = e
+ */
+
+#ifdef XK_KOREAN
+
+#define XK_Hangul		0xff31    /* Hangul start/stop(toggle) */
+#define XK_Hangul_Start		0xff32    /* Hangul start */
+#define XK_Hangul_End		0xff33    /* Hangul end, English start */
+#define XK_Hangul_Hanja		0xff34    /* Start Hangul->Hanja Conversion */
+#define XK_Hangul_Jamo		0xff35    /* Hangul Jamo mode */
+#define XK_Hangul_Romaja	0xff36    /* Hangul Romaja mode */
+#define XK_Hangul_Codeinput	0xff37    /* Hangul code input mode */
+#define XK_Hangul_Jeonja	0xff38    /* Jeonja mode */
+#define XK_Hangul_Banja		0xff39    /* Banja mode */
+#define XK_Hangul_PreHanja	0xff3a    /* Pre Hanja conversion */
+#define XK_Hangul_PostHanja	0xff3b    /* Post Hanja conversion */
+#define XK_Hangul_SingleCandidate	0xff3c    /* Single candidate */
+#define XK_Hangul_MultipleCandidate	0xff3d    /* Multiple candidate */
+#define XK_Hangul_PreviousCandidate	0xff3e    /* Previous candidate */
+#define XK_Hangul_Special	0xff3f    /* Special symbols */
+#define XK_Hangul_switch	0xFF7E    /* Alias for mode_switch */
+
+/* Hangul Consonant Characters */
+#define XK_Hangul_Kiyeog				0xea1
+#define XK_Hangul_SsangKiyeog				0xea2
+#define XK_Hangul_KiyeogSios				0xea3
+#define XK_Hangul_Nieun					0xea4
+#define XK_Hangul_NieunJieuj				0xea5
+#define XK_Hangul_NieunHieuh				0xea6
+#define XK_Hangul_Dikeud				0xea7
+#define XK_Hangul_SsangDikeud				0xea8
+#define XK_Hangul_Rieul					0xea9
+#define XK_Hangul_RieulKiyeog				0xeaa
+#define XK_Hangul_RieulMieum				0xeab
+#define XK_Hangul_RieulPieub				0xeac
+#define XK_Hangul_RieulSios				0xead
+#define XK_Hangul_RieulTieut				0xeae
+#define XK_Hangul_RieulPhieuf				0xeaf
+#define XK_Hangul_RieulHieuh				0xeb0
+#define XK_Hangul_Mieum					0xeb1
+#define XK_Hangul_Pieub					0xeb2
+#define XK_Hangul_SsangPieub				0xeb3
+#define XK_Hangul_PieubSios				0xeb4
+#define XK_Hangul_Sios					0xeb5
+#define XK_Hangul_SsangSios				0xeb6
+#define XK_Hangul_Ieung					0xeb7
+#define XK_Hangul_Jieuj					0xeb8
+#define XK_Hangul_SsangJieuj				0xeb9
+#define XK_Hangul_Cieuc					0xeba
+#define XK_Hangul_Khieuq				0xebb
+#define XK_Hangul_Tieut					0xebc
+#define XK_Hangul_Phieuf				0xebd
+#define XK_Hangul_Hieuh					0xebe
+
+/* Hangul Vowel Characters */
+#define XK_Hangul_A					0xebf
+#define XK_Hangul_AE					0xec0
+#define XK_Hangul_YA					0xec1
+#define XK_Hangul_YAE					0xec2
+#define XK_Hangul_EO					0xec3
+#define XK_Hangul_E					0xec4
+#define XK_Hangul_YEO					0xec5
+#define XK_Hangul_YE					0xec6
+#define XK_Hangul_O					0xec7
+#define XK_Hangul_WA					0xec8
+#define XK_Hangul_WAE					0xec9
+#define XK_Hangul_OE					0xeca
+#define XK_Hangul_YO					0xecb
+#define XK_Hangul_U					0xecc
+#define XK_Hangul_WEO					0xecd
+#define XK_Hangul_WE					0xece
+#define XK_Hangul_WI					0xecf
+#define XK_Hangul_YU					0xed0
+#define XK_Hangul_EU					0xed1
+#define XK_Hangul_YI					0xed2
+#define XK_Hangul_I					0xed3
+
+/* Hangul syllable-final (JongSeong) Characters */
+#define XK_Hangul_J_Kiyeog				0xed4
+#define XK_Hangul_J_SsangKiyeog				0xed5
+#define XK_Hangul_J_KiyeogSios				0xed6
+#define XK_Hangul_J_Nieun				0xed7
+#define XK_Hangul_J_NieunJieuj				0xed8
+#define XK_Hangul_J_NieunHieuh				0xed9
+#define XK_Hangul_J_Dikeud				0xeda
+#define XK_Hangul_J_Rieul				0xedb
+#define XK_Hangul_J_RieulKiyeog				0xedc
+#define XK_Hangul_J_RieulMieum				0xedd
+#define XK_Hangul_J_RieulPieub				0xede
+#define XK_Hangul_J_RieulSios				0xedf
+#define XK_Hangul_J_RieulTieut				0xee0
+#define XK_Hangul_J_RieulPhieuf				0xee1
+#define XK_Hangul_J_RieulHieuh				0xee2
+#define XK_Hangul_J_Mieum				0xee3
+#define XK_Hangul_J_Pieub				0xee4
+#define XK_Hangul_J_PieubSios				0xee5
+#define XK_Hangul_J_Sios				0xee6
+#define XK_Hangul_J_SsangSios				0xee7
+#define XK_Hangul_J_Ieung				0xee8
+#define XK_Hangul_J_Jieuj				0xee9
+#define XK_Hangul_J_Cieuc				0xeea
+#define XK_Hangul_J_Khieuq				0xeeb
+#define XK_Hangul_J_Tieut				0xeec
+#define XK_Hangul_J_Phieuf				0xeed
+#define XK_Hangul_J_Hieuh				0xeee
+
+/* Ancient Hangul Consonant Characters */
+#define XK_Hangul_RieulYeorinHieuh			0xeef
+#define XK_Hangul_SunkyeongeumMieum			0xef0
+#define XK_Hangul_SunkyeongeumPieub			0xef1
+#define XK_Hangul_PanSios				0xef2
+#define XK_Hangul_KkogjiDalrinIeung			0xef3
+#define XK_Hangul_SunkyeongeumPhieuf			0xef4
+#define XK_Hangul_YeorinHieuh				0xef5
+
+/* Ancient Hangul Vowel Characters */
+#define XK_Hangul_AraeA					0xef6
+#define XK_Hangul_AraeAE				0xef7
+
+/* Ancient Hangul syllable-final (JongSeong) Characters */
+#define XK_Hangul_J_PanSios				0xef8
+#define XK_Hangul_J_KkogjiDalrinIeung			0xef9
+#define XK_Hangul_J_YeorinHieuh				0xefa
+
+/* Korean currency symbol */
+#define XK_Korean_Won					0xeff
+
+#endif /* XK_KOREAN */
+
+#ifdef XK_CURRENCY
+#define XK_EcuSign					0x20a0
+#define XK_ColonSign					0x20a1
+#define XK_CruzeiroSign					0x20a2
+#define XK_FFrancSign					0x20a3
+#define XK_LiraSign					0x20a4
+#define XK_MillSign					0x20a5
+#define XK_NairaSign					0x20a6
+#define XK_PesetaSign					0x20a7
+#define XK_RupeeSign					0x20a8
+#define XK_WonSign					0x20a9
+#define XK_NewSheqelSign				0x20aa
+#define XK_DongSign					0x20ab
+#define XK_EuroSign					0x20ac
+#endif
diff --git a/rfb/msgTypes.h b/rfb/msgTypes.h
new file mode 100644
index 0000000..9bcf4d5
--- /dev/null
+++ b/rfb/msgTypes.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_MSGTYPES_H__
+#define __RFB_MSGTYPES_H__
+
+namespace rfb {
+  // server to client
+
+  const int msgTypeFramebufferUpdate = 0;
+  const int msgTypeSetColourMapEntries = 1;
+  const int msgTypeBell = 2;
+  const int msgTypeServerCutText = 3;
+
+  // client to server
+
+  const int msgTypeSetPixelFormat = 0;
+  const int msgTypeFixColourMapEntries = 1;
+  const int msgTypeSetEncodings = 2;
+  const int msgTypeFramebufferUpdateRequest = 3;
+  const int msgTypeKeyEvent = 4;
+  const int msgTypePointerEvent = 5;
+  const int msgTypeClientCutText = 6;
+}
+#endif
diff --git a/rfb/msvcwarning.h b/rfb/msvcwarning.h
new file mode 100644
index 0000000..4127ce9
--- /dev/null
+++ b/rfb/msvcwarning.h
@@ -0,0 +1,20 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#pragma warning( disable : 4244 ) // loss of data e.g. int to char
+#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false'
+#pragma warning( disable : 4786 ) // debug identifier truncated
diff --git a/rfb/rfb.dsp b/rfb/rfb.dsp
new file mode 100644
index 0000000..2521bfd
--- /dev/null
+++ b/rfb/rfb.dsp
@@ -0,0 +1,625 @@
+# Microsoft Developer Studio Project File - Name="rfb" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=rfb - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "rfb.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "rfb.mak" CFG="rfb - Win32 Debug Unicode"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "rfb - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "rfb - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE "rfb - Win32 Debug Unicode" (based on "Win32 (x86) Static Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "rfb - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O1 /I ".." /FI"msvcwarning.h" /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF  "$(CFG)" == "rfb - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF  "$(CFG)" == "rfb - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "rfb___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "rfb___Win32_Debug_Unicode"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug_Unicode"
+# PROP Intermediate_Dir "Debug_Unicode"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_LIB" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF 
+
+# Begin Target
+
+# Name "rfb - Win32 Release"
+# Name "rfb - Win32 Debug"
+# Name "rfb - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\Blacklist.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CConnection.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgHandler.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgReader.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgReaderV3.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgWriter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgWriterV3.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ComparingUpdateTracker.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Configuration.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ConnParams.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\CSecurityVncAuth.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Cursor.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\d3des.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\Decoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Encoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\encodings.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\HextileDecoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\HextileEncoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\HTTPServer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger_file.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger_stdio.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\LogWriter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\PixelBuffer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\PixelFormat.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\RawDecoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\RawEncoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\Region.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\RREDecoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\RREEncoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SConnection.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\secTypes.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ServerCore.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgHandler.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgReader.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgReaderV3.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgWriter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgWriterV3.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurityFactoryStandard.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurityVncAuth.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\win32\Threading_win32.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\TransImageGetter.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\UpdateTracker.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\util.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncAuth.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCSConnectionST.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCServerST.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZRLEDecoder.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZRLEEncoder.cxx
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\Blacklist.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CConnection.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgHandler.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgReader.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgReaderV3.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgWriter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CMsgWriterV3.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ColourCube.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ColourMap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ComparingUpdateTracker.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Configuration.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ConnParams.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CSecurity.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CSecurityNone.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CSecurityVncAuth.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Cursor.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\d3des.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Decoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Encoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\encodings.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Exception.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\hextileConstants.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\hextileDecode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\HextileDecoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\hextileEncode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\HextileEncoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Hostname.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\HTTPServer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ImageGetter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\keysymdef.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger_file.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Logger_stdio.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\LogWriter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\msgTypes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\msvcwarning.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Pixel.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\PixelBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\PixelFormat.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RawDecoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RawEncoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Rect.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Region.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\rreDecode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RREDecoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\rreEncode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RREEncoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SConnection.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SDesktop.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\secTypes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ServerCore.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgHandler.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgReader.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgReaderV3.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgWriter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SMsgWriterV3.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurity.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurityFactoryStandard.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurityNone.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SSecurityVncAuth.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Threading.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\win32\Threading_win32.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TransImageGetter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\transInitTempl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\transTempl.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TrueColourMap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\UpdateTracker.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\UserPasswdGetter.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\util.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\win32\util_win32.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncAuth.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCSConnectionST.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCServer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VNCServerST.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\zrleDecode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZRLEDecoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\zrleEncode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ZRLEEncoder.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/rfb/rreDecode.h b/rfb/rreDecode.h
new file mode 100644
index 0000000..9f69cee
--- /dev/null
+++ b/rfb/rreDecode.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// RRE decoding function.
+//
+// This file is #included after having set the following macros:
+// BPP                - 8, 16 or 32
+// EXTRA_ARGS         - optional extra arguments
+// FILL_RECT          - fill a rectangle with a single colour
+
+#include <rdr/InStream.h>
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define READ_PIXEL CONCAT2E(readOpaque,BPP)
+#define RRE_DECODE CONCAT2E(rreDecode,BPP)
+
+void RRE_DECODE (const Rect& r, rdr::InStream* is
+#ifdef EXTRA_ARGS
+                 , EXTRA_ARGS
+#endif
+                 )
+{
+  int nSubrects = is->readU32();
+  PIXEL_T bg = is->READ_PIXEL();
+  FILL_RECT(r, bg);
+
+  for (int i = 0; i < nSubrects; i++) {
+    PIXEL_T pix = is->READ_PIXEL();
+    int x = is->readU16();
+    int y = is->readU16();
+    int w = is->readU16();
+    int h = is->readU16();
+    FILL_RECT(Rect(r.tl.x+x, r.tl.y+y, r.tl.x+x+w, r.tl.y+y+h), pix);
+  }
+}
+
+#undef PIXEL_T
+#undef READ_PIXEL
+#undef RRE_DECODE
+}
diff --git a/rfb/rreEncode.h b/rfb/rreEncode.h
new file mode 100644
index 0000000..4877a12
--- /dev/null
+++ b/rfb/rreEncode.h
@@ -0,0 +1,164 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// RRE encoding function.
+//
+// This file is #included after having set the following macros:
+// BPP                - 8, 16 or 32
+//
+// The data argument to RRE_ENCODE contains the pixel data, and it writes the
+// encoded version to the given OutStream.  If the encoded version exceeds w*h
+// it aborts and returns -1, otherwise it returns the number of subrectangles.
+//
+
+#include <rdr/OutStream.h>
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define WRITE_PIXEL CONCAT2E(writeOpaque,BPP)
+#define RRE_ENCODE CONCAT2E(rreEncode,BPP)
+
+int RRE_ENCODE (PIXEL_T* data, int w, int h, rdr::OutStream* os, PIXEL_T bg);
+
+int RRE_ENCODE (void* data, int w, int h, rdr::OutStream* os)
+{
+  // Find the background colour - count occurrences of up to 4 different pixel
+  // values, and choose the one which occurs most often.
+
+  const int nCols = 4;
+  PIXEL_T pix[nCols];
+  int count[nCols] = { 0, };
+  PIXEL_T* ptr = (PIXEL_T*)data;
+  PIXEL_T* end = ptr + w*h;
+
+  while (ptr < end) {
+    int i;
+    for (i = 0; i < nCols; i++) {
+      if (count[i] == 0)
+        pix[i] = *ptr;
+
+      if (pix[i] == *ptr) {
+        count[i]++;
+        break;
+      }
+    }
+
+    if (i == nCols) break;
+    ptr++;
+  }
+  
+  int bg = 0;
+  for (int i = 1; i < nCols; i++)
+    if (count[i] > count[bg]) bg = i;
+
+  // Now call the function to do the encoding.
+
+  return RRE_ENCODE ((PIXEL_T*)data, w, h, os, pix[bg]);
+}
+
+int RRE_ENCODE (PIXEL_T* data, int w, int h, rdr::OutStream* os, PIXEL_T bg)
+{
+  int oldLen = os->length();
+  os->WRITE_PIXEL(bg);
+
+  int nSubrects = 0;
+
+  for (int y = 0; y < h; y++)
+  {
+    int x = 0;
+    while (x < w) {
+      if (*data == bg) {
+        x++;
+        data++;
+        continue;
+      }
+
+      // Find horizontal subrect first
+      PIXEL_T* ptr = data+1;
+      PIXEL_T* eol = data+w-x;
+      while (ptr < eol && *ptr == *data) ptr++;
+      int sw = ptr - data;
+
+      ptr = data + w;
+      int sh = 1;
+      while (sh < h-y) {
+        eol = ptr + sw;
+        while (ptr < eol)
+          if (*ptr++ != *data) goto endOfHorizSubrect;
+        ptr += w - sw;
+        sh++;
+      }
+    endOfHorizSubrect:
+
+      // Find vertical subrect
+      int vh;
+      for (vh = sh; vh < h-y; vh++)
+        if (data[vh*w] != *data) break;
+
+      if (vh != sh) {
+        ptr = data+1;
+        int vw;
+        for (vw = 1; vw < sw; vw++) {
+          for (int i = 0; i < vh; i++)
+            if (ptr[i*w] != *data) goto endOfVertSubrect;
+          ptr++;
+        }
+      endOfVertSubrect:
+
+        // If vertical subrect bigger than horizontal then use that.
+        if (sw*sh < vw*vh) {
+          sw = vw;
+          sh = vh;
+        }
+      }
+
+      nSubrects++;
+      os->WRITE_PIXEL(*data);
+      os->writeU16(x);
+      os->writeU16(y);
+      os->writeU16(sw);
+      os->writeU16(sh);
+      if (os->length() > oldLen + w*h) return -1;
+
+      ptr = data+w;
+      PIXEL_T* eor = data+w*sh;
+      while (ptr < eor) {
+        eol = ptr + sw;
+        while (ptr < eol) *ptr++ = bg;
+        ptr += w - sw;
+      }
+      x += sw;
+      data += sw;
+    }
+  }
+
+  return nSubrects;
+}
+
+#undef PIXEL_T
+#undef WRITE_PIXEL
+#undef RRE_ENCODE
+}
diff --git a/rfb/secTypes.cxx b/rfb/secTypes.cxx
new file mode 100644
index 0000000..7c6c25c
--- /dev/null
+++ b/rfb/secTypes.cxx
@@ -0,0 +1,64 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <string.h>
+#ifdef _WIN32
+#define strcasecmp _stricmp
+#endif
+#include <rfb/secTypes.h>
+#include <rfb/util.h>
+
+int rfb::secTypeNum(const char* name)
+{
+  if (strcasecmp(name, "None") == 0)       return secTypeNone;
+  if (strcasecmp(name, "VncAuth") == 0)    return secTypeVncAuth;
+  if (strcasecmp(name, "RA2") == 0)        return secTypeRA2;
+  if (strcasecmp(name, "RA2ne") == 0)      return secTypeRA2ne;
+  return secTypeInvalid;
+}
+
+const char* rfb::secTypeName(int num)
+{
+  switch (num) {
+  case secTypeNone:       return "None";
+  case secTypeVncAuth:    return "VncAuth";
+  case secTypeRA2:        return "RA2";
+  case secTypeRA2ne:      return "RA2ne";
+  default:                return "[unknown secType]";
+  }
+}
+
+bool rfb::secTypeEncrypts(int num)
+{
+  switch (num) {
+  case secTypeRA2:        return true;
+  default:                return false;
+  }
+}
+
+std::list<int> rfb::parseSecTypes(const char* types_)
+{
+  std::list<int> result;
+  CharArray types(strDup(types_)), type;
+  while (types.buf) {
+    strSplit(types.buf, ',', &type.buf, &types.buf);
+    int typeNum = secTypeNum(type.buf);
+    if (typeNum != secTypeInvalid)
+      result.push_back(typeNum);
+  }
+  return result;
+}
diff --git a/rfb/secTypes.h b/rfb/secTypes.h
new file mode 100644
index 0000000..f0b326e
--- /dev/null
+++ b/rfb/secTypes.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// secTypes.h - constants for the various security types.
+//
+
+#ifndef __RFB_SECTYPES_H__
+#define __RFB_SECTYPES_H__
+
+#include <list>
+
+namespace rfb {
+  const int secTypeInvalid = 0;
+  const int secTypeNone    = 1;
+  const int secTypeVncAuth = 2;
+
+  const int secTypeRA2     = 5;
+  const int secTypeRA2ne   = 6;
+
+  const int secTypeTight   = 16;
+  const int secTypeUltra   = 17;
+  const int secTypeTLS     = 18;
+
+  // result types
+
+  const int secResultOK = 0;
+  const int secResultFailed = 1;
+  const int secResultTooMany = 2; // deprecated
+
+  const char* secTypeName(int num);
+  int secTypeNum(const char* name);
+  bool secTypeEncrypts(int num);
+  std::list<int> parseSecTypes(const char* types);
+}
+
+#endif
diff --git a/rfb/transInitTempl.h b/rfb/transInitTempl.h
new file mode 100644
index 0000000..2658f9f
--- /dev/null
+++ b/rfb/transInitTempl.h
@@ -0,0 +1,254 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// transInitTempl.h - templates for functions to initialise lookup tables for
+// the translation functions.
+//
+// This file is #included after having set the following macros:
+// BPPOUT - 8, 16 or 32
+
+#if !defined(BPPOUT)
+#error "transInitTempl.h: BPPOUT not defined"
+#endif
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#ifndef SWAP16
+#define SWAP16(n) ((((n) & 0xff) << 8) | (((n) >> 8) & 0xff))
+#endif
+
+#ifndef SWAP32
+#define SWAP32(n) (((n) >> 24) | (((n) & 0x00ff0000) >> 8) | \
+                   (((n) & 0x0000ff00) << 8) | ((n) << 24))
+#endif
+
+#define OUTPIXEL rdr::CONCAT2E(U,BPPOUT)
+#define SWAPOUT CONCAT2E(SWAP,BPPOUT)
+#define initSimpleCMtoTCOUT    CONCAT2E(initSimpleCMtoTC,BPPOUT)
+#define initSimpleTCtoTCOUT    CONCAT2E(initSimpleTCtoTC,BPPOUT)
+#define initSimpleCMtoCubeOUT  CONCAT2E(initSimpleCMtoCube,BPPOUT)
+#define initSimpleTCtoCubeOUT  CONCAT2E(initSimpleTCtoCube,BPPOUT)
+#define initRGBTCtoTCOUT       CONCAT2E(initRGBTCtoTC,BPPOUT)
+#define initRGBTCtoCubeOUT     CONCAT2E(initRGBTCtoCube,BPPOUT)
+#define initOneRGBTableOUT     CONCAT2E(initOneRGBTable,BPPOUT)
+#define initOneRGBCubeTableOUT CONCAT2E(initOneRGBCubeTable,BPPOUT)
+
+#ifndef TRANS_INIT_TEMPL_ENDIAN_TEST
+#define TRANS_INIT_TEMPL_ENDIAN_TEST
+  static rdr::U32 endianTest = 1;
+  static bool nativeBigEndian = *(rdr::U8*)(&endianTest) != 1;
+#endif
+
+void initSimpleCMtoTCOUT (rdr::U8** tablep, const PixelFormat& inPF,
+                          ColourMap* cm, const PixelFormat& outPF)
+{
+  if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian)
+    throw Exception("Internal error: inPF is not native endian");
+
+  int size = 1 << inPF.bpp;
+
+  delete [] *tablep;
+  *tablep = new rdr::U8[size * sizeof(OUTPIXEL)];
+  OUTPIXEL* table = (OUTPIXEL*)*tablep;
+
+  for (int i = 0; i < size; i++) {
+    int r,g,b;
+    cm->lookup(i,&r,&g,&b);
+
+    table[i] = ((((r * outPF.redMax   + 32767) / 65535) << outPF.redShift) |
+                (((g * outPF.greenMax + 32767) / 65535) << outPF.greenShift) |
+                (((b * outPF.blueMax  + 32767) / 65535) << outPF.blueShift));
+#if (BPPOUT != 8)
+    if (outPF.bigEndian != nativeBigEndian)
+      table[i] = SWAPOUT (table[i]);
+#endif
+  }
+}
+
+void initSimpleTCtoTCOUT (rdr::U8** tablep, const PixelFormat& inPF,
+                          const PixelFormat& outPF)
+{
+  if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian)
+    throw Exception("Internal error: inPF is not native endian");
+
+  int size = 1 << inPF.bpp;
+
+  delete [] *tablep;
+  *tablep = new rdr::U8[size * sizeof(OUTPIXEL)];
+  OUTPIXEL* table = (OUTPIXEL*)*tablep;
+
+  for (int i = 0; i < size; i++) {
+    int r = (i >> inPF.redShift)   & inPF.redMax;
+    int g = (i >> inPF.greenShift) & inPF.greenMax;
+    int b = (i >> inPF.blueShift)  & inPF.blueMax;
+      
+    r = (r * outPF.redMax   + inPF.redMax/2)   / inPF.redMax;
+    g = (g * outPF.greenMax + inPF.greenMax/2) / inPF.greenMax;
+    b = (b * outPF.blueMax  + inPF.blueMax/2)  / inPF.blueMax;
+      
+    table[i] = ((r << outPF.redShift)   |
+                (g << outPF.greenShift) |
+                (b << outPF.blueShift));
+#if (BPPOUT != 8)
+    if (outPF.bigEndian != nativeBigEndian)
+      table[i] = SWAPOUT (table[i]);
+#endif
+  }
+}
+
+void initSimpleCMtoCubeOUT (rdr::U8** tablep, const PixelFormat& inPF,
+                            ColourMap* cm, ColourCube* cube)
+{
+  if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian)
+    throw Exception("Internal error: inPF is not native endian");
+
+  int size = 1 << inPF.bpp;
+
+  delete [] *tablep;
+  *tablep = new rdr::U8[size * sizeof(OUTPIXEL)];
+  OUTPIXEL* table = (OUTPIXEL*)*tablep;
+
+  for (int i = 0; i < size; i++) {
+    int r,g,b;
+    cm->lookup(i,&r,&g,&b);
+    r = (r * (cube->nRed-1)   + 32767) / 65535;
+    g = (g * (cube->nGreen-1) + 32767) / 65535;
+    b = (b * (cube->nBlue-1)  + 32767) / 65535;
+    table[i] = cube->lookup(r, g, b);
+  }
+}
+
+void initSimpleTCtoCubeOUT (rdr::U8** tablep, const PixelFormat& inPF,
+                            ColourCube* cube)
+{
+  if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian)
+    throw Exception("Internal error: inPF is not native endian");
+
+  int size = 1 << inPF.bpp;
+
+  delete [] *tablep;
+  *tablep = new rdr::U8[size * sizeof(OUTPIXEL)];
+  OUTPIXEL* table = (OUTPIXEL*)*tablep;
+
+  for (int i = 0; i < size; i++) {
+    int r = (i >> inPF.redShift)   & inPF.redMax;
+    int g = (i >> inPF.greenShift) & inPF.greenMax;
+    int b = (i >> inPF.blueShift)  & inPF.blueMax;
+
+    r = (r * (cube->nRed-1)   + inPF.redMax/2)   / inPF.redMax;
+    g = (g * (cube->nGreen-1) + inPF.greenMax/2) / inPF.greenMax;
+    b = (b * (cube->nBlue-1)  + inPF.blueMax/2)  / inPF.blueMax;
+
+    table[i] = cube->lookup(r, g, b);
+  }
+}
+
+void initOneRGBTableOUT (OUTPIXEL* table, int inMax, int outMax,
+                         int outShift, bool swap)
+{
+  int size = inMax + 1;
+
+  for (int i = 0; i < size; i++) {
+    table[i] = ((i * outMax + inMax / 2) / inMax) << outShift;
+#if (BPPOUT != 8)
+    if (swap)
+      table[i] = SWAPOUT (table[i]);
+#endif
+  }
+}
+
+void initRGBTCtoTCOUT (rdr::U8** tablep, const PixelFormat& inPF,
+                       const PixelFormat& outPF)
+{
+  if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian)
+    throw Exception("Internal error: inPF is not native endian");
+
+  int size = inPF.redMax + inPF.greenMax + inPF.blueMax + 3;
+
+  delete [] *tablep;
+  *tablep = new rdr::U8[size * sizeof(OUTPIXEL)];
+
+  OUTPIXEL* redTable = (OUTPIXEL*)*tablep;
+  OUTPIXEL* greenTable = redTable + inPF.redMax + 1;
+  OUTPIXEL* blueTable = greenTable + inPF.greenMax + 1;
+
+  bool swap = (outPF.bigEndian != nativeBigEndian);
+
+  initOneRGBTableOUT (redTable, inPF.redMax, outPF.redMax, 
+                           outPF.redShift, swap);
+  initOneRGBTableOUT (greenTable, inPF.greenMax, outPF.greenMax,
+                           outPF.greenShift, swap);
+  initOneRGBTableOUT (blueTable, inPF.blueMax, outPF.blueMax,
+                           outPF.blueShift, swap);
+}
+
+
+void initOneRGBCubeTableOUT (OUTPIXEL* table, int inMax, int outMax,
+                             int outMult)
+{
+  int size = inMax + 1;
+
+  for (int i = 0; i < size; i++) {
+    table[i] = ((i * outMax + inMax / 2) / inMax) * outMult;
+  }
+}
+
+void initRGBTCtoCubeOUT (rdr::U8** tablep, const PixelFormat& inPF,
+                         ColourCube* cube)
+{
+  if (inPF.bpp != 8 && inPF.bigEndian != nativeBigEndian)
+    throw Exception("Internal error: inPF is not native endian");
+
+  int size = inPF.redMax + inPF.greenMax + inPF.blueMax + 3 + cube->size();
+
+  delete [] *tablep;
+  *tablep = new rdr::U8[size * sizeof(OUTPIXEL)];
+
+  OUTPIXEL* redTable = (OUTPIXEL*)*tablep;
+  OUTPIXEL* greenTable = redTable + inPF.redMax + 1;
+  OUTPIXEL* blueTable = greenTable + inPF.greenMax + 1;
+  OUTPIXEL* cubeTable = blueTable + inPF.blueMax + 1;
+
+  initOneRGBCubeTableOUT (redTable,   inPF.redMax,   cube->nRed-1,
+                               cube->redMult());
+  initOneRGBCubeTableOUT (greenTable, inPF.greenMax, cube->nGreen-1,
+                               cube->greenMult());
+  initOneRGBCubeTableOUT (blueTable,  inPF.blueMax,  cube->nBlue-1,
+                               cube->blueMult());
+  for (int i = 0; i < cube->size(); i++) {
+    cubeTable[i] = cube->table[i];
+  }
+}
+
+#undef OUTPIXEL
+#undef initSimpleCMtoTCOUT
+#undef initSimpleTCtoTCOUT
+#undef initSimpleCMtoCubeOUT
+#undef initSimpleTCtoCubeOUT
+#undef initRGBTCtoTCOUT
+#undef initRGBTCtoCubeOUT
+#undef initOneRGBTableOUT
+#undef initOneRGBCubeTableOUT
+}
diff --git a/rfb/transTempl.h b/rfb/transTempl.h
new file mode 100644
index 0000000..907b839
--- /dev/null
+++ b/rfb/transTempl.h
@@ -0,0 +1,151 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// transTempl.h - templates for translation functions.
+//
+// This file is #included after having set the following macros:
+// BPPIN  - 8, 16 or 32
+// BPPOUT - 8, 16 or 32
+
+#if !defined(BPPIN) || !defined(BPPOUT)
+#error "transTempl.h: BPPIN or BPPOUT not defined"
+#endif
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#ifndef CONCAT4E
+#define CONCAT4(a,b,c,d) a##b##c##d
+#define CONCAT4E(a,b,c,d) CONCAT4(a,b,c,d)
+#endif
+
+#define INPIXEL rdr::CONCAT2E(U,BPPIN)
+#define OUTPIXEL rdr::CONCAT2E(U,BPPOUT)
+#define transSimpleINtoOUT CONCAT4E(transSimple,BPPIN,to,BPPOUT)
+#define transRGBINtoOUT CONCAT4E(transRGB,BPPIN,to,BPPOUT)
+#define transRGBCubeINtoOUT CONCAT4E(transRGBCube,BPPIN,to,BPPOUT)
+
+#if (BPPIN <= 16)
+
+// transSimpleINtoOUT uses a single table.  This can be used for any incoming
+// and outgoing pixel formats, as long as the incoming pixel format is not too
+// large (for 16bpp, the table needs 64K entries).
+
+void transSimpleINtoOUT (void* table_,
+                         const PixelFormat& inPF, void* inPtr, int inStride,
+                         const PixelFormat& outPF, void* outPtr, int outStride,
+                         int width, int height)
+{
+  OUTPIXEL* table = (OUTPIXEL*)table_;
+  INPIXEL* ip = (INPIXEL*)inPtr;
+  OUTPIXEL* op = (OUTPIXEL*)outPtr;
+  int inExtra = inStride - width;
+  int outExtra = outStride - width;
+
+  while (height > 0) {
+    OUTPIXEL* opEndOfRow = op + width;
+    while (op < opEndOfRow)
+      *op++ = table[*ip++];
+    ip += inExtra;
+    op += outExtra;
+    height--;
+  }
+}
+
+#endif
+
+#if (BPPIN >= 16)
+
+// transRGBINtoOUT uses three tables, one each for red, green and blue
+// components and adds the values to produce the result.  This can be used
+// where a single table would be too large (e.g. 32bpp).  It only works for a
+// trueColour incoming pixel format.  Usually the outgoing pixel format is
+// trueColour, but we add rather than ORing the three values so that it is also
+// possible to generate an index into a colour cube.  I believe that in most
+// cases adding is just as fast as ORing - if not then we should split this
+// into two different functions for efficiency.
+
+void transRGBINtoOUT (void* table,
+                      const PixelFormat& inPF, void* inPtr, int inStride,
+                      const PixelFormat& outPF, void* outPtr, int outStride,
+                      int width, int height)
+{
+  OUTPIXEL* redTable = (OUTPIXEL*)table;
+  OUTPIXEL* greenTable = redTable + inPF.redMax + 1;
+  OUTPIXEL* blueTable = greenTable + inPF.greenMax + 1;
+  INPIXEL* ip = (INPIXEL*)inPtr;
+  OUTPIXEL* op = (OUTPIXEL*)outPtr;
+  int inExtra = inStride - width;
+  int outExtra = outStride - width;
+
+  while (height > 0) {
+    OUTPIXEL* opEndOfRow = op + width;
+    while (op < opEndOfRow) {
+      *op++ = (redTable  [(*ip >> inPF.redShift)   & inPF.redMax] +
+               greenTable[(*ip >> inPF.greenShift) & inPF.greenMax] +
+               blueTable [(*ip >> inPF.blueShift)  & inPF.blueMax]);
+      ip++;
+    }
+    ip += inExtra;
+    op += outExtra;
+    height--;
+  }
+}
+
+// transRGBCubeINtoOUT is similar to transRGBINtoOUT but also looks up the
+// colour cube index in a fourth table to yield a pixel value.
+
+void transRGBCubeINtoOUT (void* table,
+                          const PixelFormat& inPF, void* inPtr, int inStride,
+                          const PixelFormat& outPF, void* outPtr,
+                          int outStride, int width, int height)
+{
+  OUTPIXEL* redTable = (OUTPIXEL*)table;
+  OUTPIXEL* greenTable = redTable + inPF.redMax + 1;
+  OUTPIXEL* blueTable = greenTable + inPF.greenMax + 1;
+  OUTPIXEL* cubeTable = blueTable + inPF.blueMax + 1;
+  INPIXEL* ip = (INPIXEL*)inPtr;
+  OUTPIXEL* op = (OUTPIXEL*)outPtr;
+  int inExtra = inStride - width;
+  int outExtra = outStride - width;
+
+  while (height > 0) {
+    OUTPIXEL* opEndOfRow = op + width;
+    while (op < opEndOfRow) {
+      *op++ = cubeTable[(redTable  [(*ip >> inPF.redShift)   & inPF.redMax] +
+                         greenTable[(*ip >> inPF.greenShift) & inPF.greenMax] +
+                         blueTable [(*ip >> inPF.blueShift)  & inPF.blueMax])];
+      ip++;
+    }
+    ip += inExtra;
+    op += outExtra;
+    height--;
+  }
+}
+
+#endif
+
+#undef INPIXEL
+#undef OUTPIXEL
+#undef transSimpleINtoOUT
+#undef transRGBINtoOUT
+#undef transRGBCubeINtoOUT
diff --git a/rfb/util.cxx b/rfb/util.cxx
new file mode 100644
index 0000000..2dbc2df
--- /dev/null
+++ b/rfb/util.cxx
@@ -0,0 +1,78 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <rfb/util.h>
+
+namespace rfb {
+
+  char* strDup(const char* s) {
+    if (!s) return 0;
+    int l = strlen(s);
+    char* r = new char[l+1];
+    memcpy(r, s, l+1);
+    return r;
+  };
+
+  void strFree(char* s) {
+    delete [] s;
+  }
+
+
+  bool strSplit(const char* src, const char limiter, char** out1, char** out2, bool fromEnd) {
+    CharArray out1old, out2old;
+    if (out1) out1old.buf = *out1;
+    if (out2) out2old.buf = *out2;
+    int len = strlen(src);
+    int i=0, increment=1, limit=len;
+    if (fromEnd) {
+      i=len-1; increment = -1; limit = -1;
+    }
+    while (i!=limit) {
+      if (src[i] == limiter) {
+        if (out1) {
+          *out1 = new char[i+1];
+          if (i) memcpy(*out1, src, i);
+          (*out1)[i] = 0;
+        }
+        if (out2) {
+          *out2 = new char[len-i];
+          if (len-i-1) memcpy(*out2, &src[i+1], len-i-1);
+          (*out2)[len-i-1] = 0;
+        }
+        return true;
+      }
+      i+=increment;
+    }
+    if (out1) *out1 = strDup(src);
+    if (out2) *out2 = 0;
+    return false;
+  }
+
+  bool strContains(const char* src, char c) {
+    int l=strlen(src);
+    for (int i=0; i<l; i++)
+      if (src[i] == c) return true;
+    return false;
+  }
+
+  void strCopy(char* dest, const char* src, int destlen) {
+    if (src)
+      strncpy(dest, src, destlen-1);
+    dest[src ? destlen-1 : 0] = 0;
+  }
+
+};
diff --git a/rfb/util.h b/rfb/util.h
new file mode 100644
index 0000000..d792c8d
--- /dev/null
+++ b/rfb/util.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+//
+// util.h - miscellaneous useful bits
+//
+
+#ifndef __RFB_UTIL_H__
+#define __RFB_UTIL_H__
+
+#include <string.h>
+
+namespace rfb {
+
+  // -=- Class to handle cleanup of arrays of characters
+  class CharArray {
+  public:
+    CharArray() : buf(0) {}
+    CharArray(char* str) : buf(str) {} // note: assumes ownership
+    CharArray(int len) {
+      buf = new char[len];
+    }
+    ~CharArray() {
+      delete [] buf;
+    }
+    // Get the buffer pointer & clear it (i.e. caller takes ownership)
+    char* takeBuf() {char* tmp = buf; buf = 0; return tmp;}
+    void replaceBuf(char* b) {delete [] buf; buf = b;}
+    char* buf;
+  private:
+    CharArray(const CharArray&);
+    CharArray& operator=(const CharArray&);
+  };
+
+  char* strDup(const char* s);
+  void strFree(char* s);
+
+  // Returns true if split successful.  Returns false otherwise.
+  // ALWAYS *copies* first part of string to out1 buffer.
+  // If limiter not found, leaves out2 alone (null) and just copies to out1.
+  // If out1 or out2 non-zero, calls strFree and zeroes them.
+  // If fromEnd is true, splits at end of string rather than beginning.
+  // Either out1 or out2 may be null, in which case the split will not return
+  // that part of the string.  Obviously, setting both to 0 is not useful...
+  bool strSplit(const char* src, const char limiter, char** out1, char** out2, bool fromEnd=false);
+
+  // Returns true if src contains c
+  bool strContains(const char* src, char c);
+
+  // Copies src to dest, up to specified length-1, and guarantees termination
+  void strCopy(char* dest, const char* src, int destlen);
+}
+#endif
+
+// Some platforms (e.g. Windows) include max() and min() macros in their
+// standard headers, so we define them only when not already defined.  Note
+// also that max() & min() are standard C++ template functions, so some C++
+// headers will undefine them.  We place our definitions outside the #ifndef
+// __RFB_UTIL_H__, so that you can always guarantee they will be defined if
+// this file is the last #include before you use them.
+
+#ifndef max
+#define max(a,b)            (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef min
+#define min(a,b)            (((a) < (b)) ? (a) : (b))
+#endif
+
+
+// -=- PLATFORM SPECIFIC UTILITY FUNCTIONS/IMPLEMENTATIONS
+#ifdef WIN32
+#include "win32/util_win32.h"
+#endif
+
diff --git a/rfb/vncAuth.cxx b/rfb/vncAuth.cxx
new file mode 100644
index 0000000..6bd6a62
--- /dev/null
+++ b/rfb/vncAuth.cxx
@@ -0,0 +1,61 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+//
+// vncAuth
+//
+// XXX not thread-safe, because d3des isn't - do we need to worry about this?
+//
+
+#include <string.h>
+extern "C" {
+#include <rfb/d3des.h>
+}
+#include <rfb/vncAuth.h>
+
+using namespace rfb;
+
+void rfb::vncAuthEncryptChallenge(rdr::U8* challenge, const char* passwd)
+{
+  unsigned char key[8] = { 0, };
+  int len = strlen(passwd);
+  if (len > 8) len = 8;
+  for (int i = 0; i < len; i++)
+    key[i] = passwd[i];
+
+  deskey(key, EN0);
+
+  for (int j = 0; j < vncAuthChallengeSize; j += 8)
+    des(challenge+j, challenge+j);
+}
+
+static unsigned char obfuscationKey[] = {23,82,107,6,35,78,88,7};
+
+void rfb::vncAuthObfuscatePasswd(char* passwd)
+{
+  for (int i = strlen(passwd); i < 8; i++)
+    passwd[i] = 0;
+  deskey(obfuscationKey, EN0);
+  des((unsigned char*)passwd, (unsigned char*)passwd);
+}
+
+void rfb::vncAuthUnobfuscatePasswd(char* passwd)
+{
+  deskey(obfuscationKey, DE1);
+  des((unsigned char*)passwd, (unsigned char*)passwd);
+  passwd[8] = 0;
+}
diff --git a/rfb/vncAuth.h b/rfb/vncAuth.h
new file mode 100644
index 0000000..18d87ad
--- /dev/null
+++ b/rfb/vncAuth.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#ifndef __RFB_VNCAUTH_H__
+#define __RFB_VNCAUTH_H__
+
+#include <rdr/types.h>
+
+namespace rfb {
+
+  const int vncAuthChallengeSize = 16;
+
+  void vncAuthEncryptChallenge(rdr::U8* challenge, const char* passwd);
+  void vncAuthObfuscatePasswd(char* passwd);
+  void vncAuthUnobfuscatePasswd(char* passwd);
+}
+#endif
diff --git a/rfb/win32/Threading_win32.cxx b/rfb/win32/Threading_win32.cxx
new file mode 100644
index 0000000..28cfdb7
--- /dev/null
+++ b/rfb/win32/Threading_win32.cxx
@@ -0,0 +1,155 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- Threading_win32.cxx
+// Win32 Threading interface implementation
+
+#include <malloc.h>
+
+#include <rdr/Exception.h>
+#include <rfb/LogWriter.h>
+#include <rfb/win32/Threading_win32.h>
+#include <rfb/util.h>
+
+using namespace rfb;
+
+static LogWriter vlog("Threading");
+
+static DWORD threadStorage = TlsAlloc();
+
+
+inline logAction(Thread* t, const char* action) {
+  vlog.debug("%-16.16s %s(%lx)", action, t->getName(), t);
+}
+
+inline logError(Thread* t, const char* err) {
+  vlog.error("%-16.16s %s(%lx):%s", "failed", t->getName(), t, err);
+}
+
+
+DWORD WINAPI
+Thread::threadProc(LPVOID lpParameter) {
+  Thread* thread = (Thread*) lpParameter;
+  TlsSetValue(threadStorage, thread);
+  logAction(thread, "started");
+  try {
+    thread->run();
+    logAction(thread, "stopped");
+  } catch (rdr::Exception& e) {
+    logError(thread, e.str());
+  }
+  bool deleteThread = false;
+  {
+    Lock l(thread->mutex);
+    thread->state = ThreadStopped;
+    thread->sig->signal();
+    deleteThread = thread->deleteAfterRun;
+  }
+  if (deleteThread)
+    delete thread;
+  return 0;
+}
+
+Thread::Thread(const char* name_) : sig(0), deleteAfterRun(false) {
+  sig = new Condition(mutex);
+  cond_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+  if (!name_)
+    name_ = "Unnamed";
+  name = strDup(name_);
+  thread = CreateThread(NULL, 0, threadProc, this, CREATE_SUSPENDED, &thread_id);
+  state = ThreadCreated;
+  logAction(this, "created");
+}
+
+Thread::Thread(HANDLE thread_, DWORD thread_id_) : sig(0), deleteAfterRun(false), thread(thread_), thread_id(thread_id_) {
+  sig = new Condition(mutex);
+  cond_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+  name = strDup("Native");
+  state = ThreadNative;
+  logAction(this, "created");
+}
+
+Thread::~Thread() {
+  logAction(this, "destroying");
+  if (!deleteAfterRun) this->join();
+  if (sig)
+    delete sig;
+  if (cond_event)
+    CloseHandle(cond_event);
+  logAction(this, "destroyed");
+  strFree(name);
+}
+
+void
+Thread::run() {
+}
+
+void
+Thread::start() {
+  Lock l(mutex);
+  if (state == ThreadCreated) {
+    state = ThreadStarted;
+    sig->signal();
+    ResumeThread(thread);
+  }
+}
+
+Thread*
+Thread::join() {
+  if (deleteAfterRun)
+    throw rdr::Exception("attempt to join() with deleteAfterRun thread");
+  Lock l(mutex);
+  if (!thread) {
+    logAction(this, "already joined");
+  } else {
+    logAction(this, "joining");
+    while (state == ThreadStarted) {
+      sig->wait();
+      logAction(this, "checking");
+    }
+    CloseHandle(thread);
+    thread = 0;
+    logAction(this, "joined");
+  }
+  return this;
+}
+
+const char*
+Thread::getName() const {
+  return name;
+}
+
+ThreadState
+Thread::getState() const {
+  return state;
+}
+
+unsigned long
+Thread::getThreadId() const {
+  return thread_id;
+}
+
+Thread*
+Thread::self() {
+  Thread* thread = (Thread*) TlsGetValue(threadStorage);
+  if (!thread) {
+    thread = new Thread(GetCurrentThread(), GetCurrentThreadId());
+    TlsSetValue(threadStorage, thread);
+  }
+  return thread;
+}
\ No newline at end of file
diff --git a/rfb/win32/Threading_win32.h b/rfb/win32/Threading_win32.h
new file mode 100644
index 0000000..e95e0f7
--- /dev/null
+++ b/rfb/win32/Threading_win32.h
@@ -0,0 +1,149 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- Threading_win32.h
+// Win32 Threading interface implementation
+
+#ifndef __RFB_THREADING_IMPL_WIN32
+#define __RFB_THREADING_IMPL_WIN32
+
+#define __RFB_THREADING_IMPL WIN32
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <stdio.h>
+
+namespace rfb {
+
+  class Mutex {
+  public:
+    Mutex() {
+      InitializeCriticalSection(&crit);
+    }
+    ~Mutex() {
+      DeleteCriticalSection(&crit);
+    }
+    friend class Lock;
+    friend class Condition;
+  protected:
+    void enter() {EnterCriticalSection(&crit);}
+    void exit() {LeaveCriticalSection(&crit);}
+    CRITICAL_SECTION crit;
+  };
+
+  class Lock {
+  public:
+    Lock(Mutex& m) : mutex(m) {m.enter();}
+    ~Lock() {mutex.exit();}
+  protected:
+    Mutex& mutex;
+  };
+
+  enum ThreadState {ThreadCreated, ThreadStarted, ThreadStopped, ThreadNative};
+
+  class Thread {
+  public:
+    Thread(const char* name_=0);
+    virtual ~Thread();
+
+    virtual void run();
+
+    virtual void start();
+    virtual Thread* join();
+
+    const char* getName() const;
+    ThreadState getState() const;
+
+    // Determines whether the thread should delete itself when run() returns
+    // If you set this, you must NEVER call join()!
+    void setDeleteAfterRun() {deleteAfterRun = true;};
+
+    unsigned long getThreadId() const;
+
+    static Thread* self();
+
+    friend class Condition;
+
+  protected:
+    Thread(HANDLE thread_, DWORD thread_id_);
+    static DWORD WINAPI threadProc(LPVOID lpParameter);
+
+    HANDLE thread;
+    DWORD thread_id;
+    char* name;
+    ThreadState state;
+    Condition* sig;
+    Mutex mutex;
+
+    HANDLE cond_event;
+	  Thread* cond_next;
+
+    bool deleteAfterRun;
+  };
+
+  class Condition {
+  public:
+    Condition(Mutex& m) : mutex(m), waiting(0) {
+    }
+    ~Condition() {
+    }
+    void signal() {
+      Lock l(cond_lock);
+      if (waiting) {
+        SetEvent(waiting->cond_event);
+        waiting = waiting->cond_next;
+      }
+    }
+    // - MUST hold "mutex" to call wait()
+    // WIN32: if processMsg is true then wait will continue
+    // to process messages in the thread's queue.
+    // Avoid using this unless you have to!
+    void wait(bool processMsgs=false) {
+      Thread* self = Thread::self();
+      ResetEvent(self->cond_event);
+      { Lock l(cond_lock);
+        self->cond_next = waiting;
+        waiting = self;
+      }
+      mutex.exit();
+      if (processMsgs) {
+        while (1) {
+          DWORD result = MsgWaitForMultipleObjects(1, &self->cond_event, FALSE, INFINITE, QS_ALLINPUT);
+          if (result == WAIT_OBJECT_0)
+            break;
+          MSG msg;
+          while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
+            DispatchMessage(&msg);
+          }
+        }
+      } else {
+        WaitForSingleObject(self->cond_event, INFINITE);
+      }
+      mutex.enter();
+    }
+    
+  protected:
+    Mutex& mutex;
+    Mutex cond_lock;
+	  Thread* waiting;
+  };
+
+};
+
+#endif // __RFB_THREADING_IMPL
diff --git a/rfb/win32/msvcwarning.h b/rfb/win32/msvcwarning.h
new file mode 100644
index 0000000..d0c98c3
--- /dev/null
+++ b/rfb/win32/msvcwarning.h
@@ -0,0 +1,19 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#pragma warning( disable : 4244 ) // loss of data e.g. int to char
+#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false'
diff --git a/rfb/win32/util_win32.h b/rfb/win32/util_win32.h
new file mode 100644
index 0000000..4bde5ec
--- /dev/null
+++ b/rfb/win32/util_win32.h
@@ -0,0 +1,111 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+//
+// util_win32.h - miscellaneous useful bits for Win32 only
+//
+
+#ifndef __RFB_UTIL_WIN32_H__
+#define __RFB_UTIL_WIN32_H__
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+// *** #include <iostream.h>
+
+#include <rfb/LogWriter.h>
+#include <rfb/Exception.h>
+
+namespace rfb {
+
+  // WIN32-ONLY PROFILING CODE
+  //
+  // CpuTime and CpuTimer provide a simple way to profile particular
+  // sections of code
+  //
+  // Use one CpuTime object per task to be profiled.  CpuTime instances
+  // maintain a cumulative total of time spent in user and kernel space
+  // by threads.
+  // When a CpuTime object is created, a label must be specified to
+  // identify the task being profiled.
+  // When the object is destroyed, it will print debugging information
+  // containing the user and kernel times accumulated.
+  //
+  // Place a CpuTimer object in each section of code which is to be
+  // profiled.  When the object is created, it snapshots the current
+  // kernel and user times and stores them.  These are used when the
+  // object is destroyed to establish how much time has elapsed in the
+  // intervening period.  The accumulated time is then added to the
+  // associated CpuTime object.
+  //
+  // This code works only on platforms providing __int64
+
+	class CpuTime {
+	public:
+		CpuTime(const char *name)
+			: timer_name(strDup(name)),
+			  kernel_time(0), user_time(0), max_user_time(0), iterations(0) {}
+		~CpuTime() {
+      g_log_writer.info("timer %s : %I64ums (krnl), %I64ums (user), %I64uus (user-max) (%I64u its)\n",
+				timer_name, kernel_time/10000, user_time/10000, max_user_time/10,
+				iterations);
+			delete [] timer_name;
+		}
+    static LogWriter g_log_writer;
+		char* timer_name;
+		__int64 kernel_time;
+		__int64 user_time;
+		__int64 iterations;
+		__int64 max_user_time;
+	};
+
+	class CpuTimer {
+	public:
+		inline CpuTimer(CpuTime &ct) : cputime(ct) {
+			FILETIME create_time, end_time;
+			if (!GetThreadTimes(GetCurrentThread(),
+				&create_time, &end_time,
+				(LPFILETIME)&start_kernel_time,
+				(LPFILETIME)&start_user_time)) {
+        throw rdr::SystemException("rfb::CpuTimer failed to initialise", GetLastError());
+			}
+		}
+		inline ~CpuTimer() {
+			FILETIME create_time, end_time;
+			__int64 end_kernel_time, end_user_time;
+			if (!GetThreadTimes(GetCurrentThread(),
+				&create_time, &end_time,
+				(LPFILETIME)&end_kernel_time,
+				(LPFILETIME)&end_user_time)) {
+        throw rdr::SystemException("rfb::CpuTimer destructor failed", GetLastError());
+			}
+			cputime.kernel_time += end_kernel_time - start_kernel_time;
+			cputime.user_time += end_user_time - start_user_time;
+			if (end_user_time - start_user_time > cputime.max_user_time) {
+				cputime.max_user_time = end_user_time - start_user_time;
+			}
+			cputime.iterations++;
+		}
+	private:
+		CpuTime& cputime;
+		__int64 start_kernel_time;
+		__int64 start_user_time;
+	};
+
+};
+
+#endif // __RFB_UTIL_WIN32_H__
diff --git a/rfb/zrleDecode.h b/rfb/zrleDecode.h
new file mode 100644
index 0000000..b5391b1
--- /dev/null
+++ b/rfb/zrleDecode.h
@@ -0,0 +1,251 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+//
+// ZRLE decoding function.
+//
+// This file is #included after having set the following macros:
+// BPP                - 8, 16 or 32
+// EXTRA_ARGS         - optional extra arguments
+// FILL_RECT          - fill a rectangle with a single colour
+// IMAGE_RECT         - draw a rectangle of pixel data from a buffer
+
+#include <rdr/InStream.h>
+#include <rdr/ZlibInStream.h>
+#include <assert.h>
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#ifdef CPIXEL
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define READ_PIXEL CONCAT2E(readOpaque,CPIXEL)
+#define ZRLE_DECODE CONCAT2E(zrleDecode,CPIXEL)
+#else
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define READ_PIXEL CONCAT2E(readOpaque,BPP)
+#define ZRLE_DECODE CONCAT2E(zrleDecode,BPP)
+#endif
+
+void ZRLE_DECODE (const Rect& r, rdr::InStream* is,
+                      rdr::ZlibInStream* zis, PIXEL_T* buf
+#ifdef EXTRA_ARGS
+                      , EXTRA_ARGS
+#endif
+                      )
+{
+  int length = is->readU32();
+  zis->setUnderlying(is, length);
+  Rect t;
+
+  for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 64) {
+
+    t.br.y = min(r.br.y, t.tl.y + 64);
+
+    for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 64) {
+
+      t.br.x = min(r.br.x, t.tl.x + 64);
+
+      int mode = zis->readU8();
+      bool rle = mode & 128;
+      int palSize = mode & 127;
+      PIXEL_T palette[128];
+
+      for (int i = 0; i < palSize; i++) {
+        palette[i] = zis->READ_PIXEL();
+      }
+
+      if (palSize == 1) {
+        PIXEL_T pix = palette[0];
+        FILL_RECT(t,pix);
+        continue;
+      }
+
+      if (!rle) {
+        if (palSize == 0) {
+
+          // raw
+
+#ifdef CPIXEL
+          for (PIXEL_T* ptr = buf; ptr < buf+t.area(); ptr++) {
+            *ptr = zis->READ_PIXEL();
+          }
+#else
+          zis->readBytes(buf, t.area() * (BPP / 8));
+#endif
+
+        } else {
+
+          // packed pixels
+          int bppp = ((palSize > 16) ? 8 :
+                      ((palSize > 4) ? 4 : ((palSize > 2) ? 2 : 1)));
+
+          PIXEL_T* ptr = buf;
+
+          for (int i = 0; i < t.height(); i++) {
+            PIXEL_T* eol = ptr + t.width();
+            rdr::U8 byte = 0;
+            rdr::U8 nbits = 0;
+
+            while (ptr < eol) {
+              if (nbits == 0) {
+                byte = zis->readU8();
+                nbits = 8;
+              }
+              nbits -= bppp;
+              rdr::U8 index = (byte >> nbits) & ((1 << bppp) - 1) & 127;
+              *ptr++ = palette[index];
+            }
+          }
+        }
+
+#ifdef FAVOUR_FILL_RECT
+       //fprintf(stderr,"copying data to screen %dx%d at %d,%d\n",
+        //t.width(),t.height(),t.tl.x,t.tl.y);
+        IMAGE_RECT(t,buf);
+#endif
+
+      } else {
+
+        if (palSize == 0) {
+
+          // plain RLE
+
+          PIXEL_T* ptr = buf;
+          PIXEL_T* end = ptr + t.area();
+          while (ptr < end) {
+            PIXEL_T pix = zis->READ_PIXEL();
+            int len = 1;
+            int b;
+            do {
+              b = zis->readU8();
+              len += b;
+            } while (b == 255);
+
+            assert(len <= end - ptr);
+
+#ifdef FAVOUR_FILL_RECT
+            int i = ptr - buf;
+            ptr += len;
+
+            int runX = i % t.width();
+            int runY = i / t.width();
+
+            if (runX + len > t.width()) {
+              if (runX != 0) {
+                FILL_RECT(Rect(t.tl.x+runX, t.tl.y+runY, t.width()-runX, 1),
+                          pix);
+                len -= t.width()-runX;
+                runX = 0;
+                runY++;
+              }
+
+              if (len > t.width()) {
+                FILL_RECT(Rect(t.tl.x, t.tl.y+runY, t.width(), len/t.width()),
+                          pix);
+                runY += len / t.width();
+                len = len % t.width();
+              }
+            }
+
+            if (len != 0) {
+              FILL_RECT(Rect(t.tl.x+runX, t.tl.y+runY, len, 1), pix);
+            }
+#else
+            while (len-- > 0) *ptr++ = pix;
+#endif
+
+          }
+        } else {
+
+          // palette RLE
+
+          PIXEL_T* ptr = buf;
+          PIXEL_T* end = ptr + t.area();
+          while (ptr < end) {
+            int index = zis->readU8();
+            int len = 1;
+            if (index & 128) {
+              int b;
+              do {
+                b = zis->readU8();
+                len += b;
+              } while (b == 255);
+
+              assert(len <= end - ptr);
+            }
+
+            index &= 127;
+
+            PIXEL_T pix = palette[index];
+
+#ifdef FAVOUR_FILL_RECT
+            int i = ptr - buf;
+            ptr += len;
+
+            int runX = i % t.width();
+            int runY = i / t.width();
+
+            if (runX + len > t.width()) {
+              if (runX != 0) {
+                FILL_RECT(Rect(t.tl.x+runX, t.tl.y+runY, t.width()-runX, 1),
+                          pix);
+                len -= t.width()-runX;
+                runX = 0;
+                runY++;
+              }
+
+              if (len > t.width()) {
+                FILL_RECT(Rect(t.tl.x, t.tl.y+runY, t.width(), len/t.width()),
+                          pix);
+                runY += len / t.width();
+                len = len % t.width();
+              }
+            }
+
+            if (len != 0) {
+              FILL_RECT(Rect(t.tl.x+runX, t.tl.y+runY, len, 1), pix);
+            }
+#else
+            while (len-- > 0) *ptr++ = pix;
+#endif
+          }
+        }
+      }
+
+#ifndef FAVOUR_FILL_RECT
+      //fprintf(stderr,"copying data to screen %dx%d at %d,%d\n",
+      //t.width(),t.height(),t.tl.x,t.tl.y);
+      IMAGE_RECT(t,buf);
+#endif
+    }
+  }
+
+  zis->reset();
+}
+
+#undef ZRLE_DECODE
+#undef READ_PIXEL
+#undef PIXEL_T
+}
diff --git a/rfb/zrleEncode.h b/rfb/zrleEncode.h
new file mode 100644
index 0000000..a1582f2
--- /dev/null
+++ b/rfb/zrleEncode.h
@@ -0,0 +1,328 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+//
+// zrleEncode.h - zrle encoding function.
+//
+// This file is #included after having set the following macros:
+// BPP                - 8, 16 or 32
+// EXTRA_ARGS         - optional extra arguments
+// GET_IMAGE_INTO_BUF - gets a rectangle of pixel data into a buffer
+//
+// Note that the buf argument to ZRLE_ENCODE needs to be at least one pixel
+// bigger than the largest tile of pixel data, since the ZRLE encoding
+// algorithm writes to the position one past the end of the pixel data.
+//
+
+#include <rdr/OutStream.h>
+#include <rdr/ZlibOutStream.h>
+#include <assert.h>
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#ifdef CPIXEL
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define WRITE_PIXEL CONCAT2E(writeOpaque,CPIXEL)
+#define ZRLE_ENCODE CONCAT2E(zrleEncode,CPIXEL)
+#define ZRLE_ENCODE_TILE CONCAT2E(zrleEncodeTile,CPIXEL)
+#define BPPOUT 24
+#else
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define WRITE_PIXEL CONCAT2E(writeOpaque,BPP)
+#define ZRLE_ENCODE CONCAT2E(zrleEncode,BPP)
+#define ZRLE_ENCODE_TILE CONCAT2E(zrleEncodeTile,BPP)
+#define BPPOUT BPP
+#endif
+
+#ifndef ZRLE_ONCE
+#define ZRLE_ONCE
+static const int bitsPerPackedPixel[] = {
+  0, 1, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4
+};
+
+// The PaletteHelper class helps us build up the palette from pixel data by
+// storing a reverse index using a simple hash-table
+
+class PaletteHelper {
+public:
+  enum { MAX_SIZE = 127 };
+
+  PaletteHelper()
+  {
+    memset(index, 255, sizeof(index));
+    size = 0;
+  }
+
+  inline int hash(rdr::U32 pix)
+  {
+    return (pix ^ (pix >> 17)) & 4095;
+  }
+
+  inline void insert(rdr::U32 pix)
+  {
+    if (size < MAX_SIZE) {
+      int i = hash(pix);
+      while (index[i] != 255 && key[i] != pix)
+        i++;
+      if (index[i] != 255) return;
+
+      index[i] = size;
+      key[i] = pix;
+      palette[size] = pix;
+    }
+    size++;
+  }
+
+  inline int lookup(rdr::U32 pix)
+  {
+    assert(size <= MAX_SIZE);
+    int i = hash(pix);
+    while (index[i] != 255 && key[i] != pix)
+      i++;
+    if (index[i] != 255) return index[i];
+    return -1;
+  }
+
+  rdr::U32 palette[MAX_SIZE];
+  rdr::U8 index[4096+MAX_SIZE];
+  rdr::U32 key[4096+MAX_SIZE];
+  int size;
+};
+#endif
+
+void ZRLE_ENCODE_TILE (PIXEL_T* data, int w, int h, rdr::OutStream* os);
+
+bool ZRLE_ENCODE (const Rect& r, rdr::OutStream* os,
+                  rdr::ZlibOutStream* zos, void* buf, int maxLen, Rect* actual
+#ifdef EXTRA_ARGS
+                  , EXTRA_ARGS
+#endif
+                  )
+{
+  zos->setUnderlying(os);
+  // RLE overhead is at worst 1 byte per 64x64 (4Kpixel) block
+  int worstCaseLine = r.width() * 64 * (BPPOUT/8) + 1 + r.width() / 64;
+  // Zlib overhead is at worst 6 bytes plus 5 bytes per 32Kbyte block.
+  worstCaseLine += 11 + 5 * (worstCaseLine >> 15);
+  Rect t;
+
+  for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 64) {
+
+    t.br.y = min(r.br.y, t.tl.y + 64);
+
+    if (os->length() + worstCaseLine > maxLen) {
+      if (t.tl.y == r.tl.y)
+        throw Exception("ZRLE: not enough space for first line?");
+      actual->tl = r.tl;
+      actual->br.x = r.br.x;
+      actual->br.y = t.tl.y;
+      return false;
+    }
+
+    for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 64) {
+
+      t.br.x = min(r.br.x, t.tl.x + 64);
+
+      GET_IMAGE_INTO_BUF(t,buf);
+
+      ZRLE_ENCODE_TILE((PIXEL_T*)buf, t.width(), t.height(), zos);
+    }
+
+    zos->flush();
+  }
+  return true;
+}
+
+
+void ZRLE_ENCODE_TILE (PIXEL_T* data, int w, int h, rdr::OutStream* os)
+{
+  // First find the palette and the number of runs
+
+  PaletteHelper ph;
+
+  int runs = 0;
+  int singlePixels = 0;
+
+  PIXEL_T* ptr = data;
+  PIXEL_T* end = ptr + h * w;
+  *end = ~*(end-1); // one past the end is different so the while loop ends
+
+  while (ptr < end) {
+    PIXEL_T pix = *ptr;
+    if (*++ptr != pix) {
+      singlePixels++;
+    } else {
+      while (*++ptr == pix) ;
+      runs++;
+    }
+    ph.insert(pix);
+  }
+
+  //fprintf(stderr,"runs %d, single pixels %d, paletteSize %d\n",
+  //        runs, singlePixels, ph.size);
+
+  // Solid tile is a special case
+
+  if (ph.size == 1) {
+    os->writeU8(1);
+    os->WRITE_PIXEL(ph.palette[0]);
+    return;
+  }
+
+  // Try to work out whether to use RLE and/or a palette.  We do this by
+  // estimating the number of bytes which will be generated and picking the
+  // method which results in the fewest bytes.  Of course this may not result
+  // in the fewest bytes after compression...
+
+  bool useRle = false;
+  bool usePalette = false;
+
+  int estimatedBytes = w * h * (BPPOUT/8); // start assuming raw
+
+  int plainRleBytes = ((BPPOUT/8)+1) * (runs + singlePixels);
+
+  if (plainRleBytes < estimatedBytes) {
+    useRle = true;
+    estimatedBytes = plainRleBytes;
+  }
+
+  if (ph.size < 128) {
+    int paletteRleBytes = (BPPOUT/8) * ph.size + 2 * runs + singlePixels;
+
+    if (paletteRleBytes < estimatedBytes) {
+      useRle = true;
+      usePalette = true;
+      estimatedBytes = paletteRleBytes;
+    }
+
+    if (ph.size < 17) {
+      int packedBytes = ((BPPOUT/8) * ph.size +
+                         w * h * bitsPerPackedPixel[ph.size-1] / 8);
+
+      if (packedBytes < estimatedBytes) {
+        useRle = false;
+        usePalette = true;
+        estimatedBytes = packedBytes;
+      }
+    }
+  }
+
+  if (!usePalette) ph.size = 0;
+
+  os->writeU8((useRle ? 128 : 0) | ph.size);
+
+  for (int i = 0; i < ph.size; i++) {
+    os->WRITE_PIXEL(ph.palette[i]);
+  }
+
+  if (useRle) {
+
+    PIXEL_T* ptr = data;
+    PIXEL_T* end = ptr + w * h;
+    PIXEL_T* runStart;
+    PIXEL_T pix;
+    while (ptr < end) {
+      runStart = ptr;
+      pix = *ptr++;
+      while (*ptr == pix && ptr < end)
+        ptr++;
+      int len = ptr - runStart;
+      if (len <= 2 && usePalette) {
+        int index = ph.lookup(pix);
+        if (len == 2)
+          os->writeU8(index);
+        os->writeU8(index);
+        continue;
+      }
+      if (usePalette) {
+        int index = ph.lookup(pix);
+        os->writeU8(index | 128);
+      } else {
+        os->WRITE_PIXEL(pix);
+      }
+      len -= 1;
+      while (len >= 255) {
+        os->writeU8(255);
+        len -= 255;
+      }
+      os->writeU8(len);
+    }
+
+  } else {
+
+    // no RLE
+
+    if (usePalette) {
+
+      // packed pixels
+
+      assert (ph.size < 17);
+
+      int bppp = bitsPerPackedPixel[ph.size-1];
+
+      PIXEL_T* ptr = data;
+
+      for (int i = 0; i < h; i++) {
+        rdr::U8 nbits = 0;
+        rdr::U8 byte = 0;
+
+        PIXEL_T* eol = ptr + w;
+
+        while (ptr < eol) {
+          PIXEL_T pix = *ptr++;
+          rdr::U8 index = ph.lookup(pix);
+          byte = (byte << bppp) | index;
+          nbits += bppp;
+          if (nbits >= 8) {
+            os->writeU8(byte);
+            nbits = 0;
+          }
+        }
+        if (nbits > 0) {
+          byte <<= 8 - nbits;
+          os->writeU8(byte);
+        }
+      }
+    } else {
+
+      // raw
+
+#ifdef CPIXEL
+      for (PIXEL_T* ptr = data; ptr < data+w*h; ptr++) {
+        os->WRITE_PIXEL(*ptr);
+      }
+#else
+      os->writeBytes(data, w*h*(BPP/8));
+#endif
+    }
+  }
+}
+
+#undef PIXEL_T
+#undef WRITE_PIXEL
+#undef ZRLE_ENCODE
+#undef ZRLE_ENCODE_TILE
+#undef BPPOUT
+}