Merged the changes from branch/merge-with-vnc-4.1.1, revisions 520:558.

git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@559 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/README b/README
index 73efd2b..7787172 100644
--- a/README
+++ b/README
@@ -2,7 +2,7 @@
 TightVNC Source Distribution for Unix platforms
 ===============================================
 
-Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
 Copyright (C) 2000-2004 Constantin Kaplinsky.
 Copyright (C) 2004-2005 Peter Astrand, Cendio AB
 
diff --git a/boilerplate.mk b/boilerplate.mk
index 82ac68b..38f376e 100644
--- a/boilerplate.mk
+++ b/boilerplate.mk
@@ -30,7 +30,7 @@
 .SUFFIXES: .cxx .c .o
 
 .c.o:
-	$(CC) $(ALL_CPPFLAGS) $(CFLAGS) -c $<
+	$(CC) $(ALL_CPPFLAGS) $(CFLAGS) -o $@ -c $<
 
 .cxx.o:
-	$(CXX) $(ALL_CPPFLAGS) $(CXXFLAGS) -c $<
+	$(CXX) $(ALL_CPPFLAGS) $(CXXFLAGS) -o $@ -c $<
diff --git a/configure b/configure
index 981ab2a..564099a 100755
--- a/configure
+++ b/configure
@@ -271,7 +271,7 @@
 PACKAGE_STRING=
 PACKAGE_BUGREPORT=
 
-ac_unique_file="rdr/InStream.h"
+ac_unique_file="vncviewer_unix/vncviewer.cxx"
 # Factoring default headers for most tests.
 ac_includes_default="\
 #include <stdio.h>
diff --git a/configure.in b/configure.in
index 73e0223..9b58f27 100644
--- a/configure.in
+++ b/configure.in
@@ -1,5 +1,5 @@
 dnl Process this file with autoconf to produce a configure script.
-AC_INIT(rdr/InStream.h)
+AC_INIT(vncviewer_unix/vncviewer.cxx)
 
 PACKAGE=tightvnc
 VERSION=1.5.0
diff --git a/network/Socket.h b/network/Socket.h
index 09d88be..b93da2e 100644
--- a/network/Socket.h
+++ b/network/Socket.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -21,6 +21,7 @@
 #ifndef __NETWORK_SOCKET_H__
 #define __NETWORK_SOCKET_H__
 
+#include <limits.h>
 #include <rdr/FdInStream.h>
 #include <rdr/FdOutStream.h>
 #include <rdr/Exception.h>
@@ -32,9 +33,10 @@
     Socket(int fd)
       : instream(new rdr::FdInStream(fd)),
       outstream(new rdr::FdOutStream(fd)),
-      own_streams(true) {}
+      ownStreams(true), isShutdown_(false),
+      queryConnection(false) {}
     virtual ~Socket() {
-      if (own_streams) {
+      if (ownStreams) {
         delete instream;
         delete outstream;
       }
@@ -42,7 +44,10 @@
     rdr::FdInStream &inStream() {return *instream;}
     rdr::FdOutStream &outStream() {return *outstream;}
     int getFd() {return outstream->getFd();}
-    virtual void shutdown() = 0;
+
+    // if shutdown() is overridden then the override MUST call on to here
+    virtual void shutdown() {isShutdown_ = true;}
+    bool isShutdown() const {return isShutdown_;}
 
     // information about this end of the socket
     virtual char* getMyAddress() = 0; // a string e.g. "192.168.0.1"
@@ -57,19 +62,26 @@
     // Is the remote end on the same machine?
     virtual bool sameMachine() = 0;
 
+    // Was there a "?" in the ConnectionFilter used to accept this Socket?
+    void setRequiresQuery() {queryConnection = true;}
+    bool requiresQuery() const {return queryConnection;}
+
   protected:
-    Socket() : instream(0), outstream(0), own_streams(false) {}
+    Socket() : instream(0), outstream(0), ownStreams(false),
+      isShutdown_(false), queryConnection(false) {}
     Socket(rdr::FdInStream* i, rdr::FdOutStream* o, bool own)
-      : instream(i), outstream(o), own_streams(own) {}
+      : instream(i), outstream(o), ownStreams(own),
+      isShutdown_(false), queryConnection(false) {}
     rdr::FdInStream* instream;
     rdr::FdOutStream* outstream;
-    bool own_streams;
+    bool ownStreams;
+    bool isShutdown_;
+    bool queryConnection;
   };
 
   class ConnectionFilter {
   public:
     virtual bool verifyConnection(Socket* s) = 0;
-    virtual bool queryUserAcceptConnection(Socket*) {return false;}
   };
 
   class SocketListener {
@@ -85,6 +97,7 @@
     // if one is installed.  Otherwise, returns 0.
     virtual Socket* accept() = 0;
 
+    // setFilter() applies the specified filter to all new connections
     void setFilter(ConnectionFilter* f) {filter = f;}
     int getFd() {return fd;}
   protected:
@@ -100,28 +113,34 @@
   public:
     virtual ~SocketServer() {}
 
-    // addClient() tells the server to manage the socket.
-    //   If the server can't manage the socket, it must shutdown() it.
-    virtual void addClient(network::Socket* sock) = 0;
+    // addSocket() tells the server to serve the Socket.  The caller
+    //   retains ownership of the Socket - the only way for the server
+    //   to discard a Socket is by calling shutdown() on it.
+    //   outgoing is set to true if the socket was created by connecting out
+    //   to another host, or false if the socket was created by accept()ing
+    //   an incoming connection.
+    virtual void addSocket(network::Socket* sock, bool outgoing=false) = 0;
 
-    // processSocketEvent() tells the server there is a socket read event.
-    //   If there is an error, or the socket has been closed/shutdown then
-    //   the server MUST delete the socket AND return false.
-    virtual bool processSocketEvent(network::Socket* sock) = 0;
+    // removeSocket() tells the server to stop serving the Socket.  The
+    //   caller retains ownership of the Socket - the server must NOT
+    //   delete the Socket!  This call is used mainly to cause per-Socket
+    //   resources to be freed.
+    virtual void removeSocket(network::Socket* sock) = 0;
+
+    // processSocketEvent() tells the server there is a Socket read event.
+    //   The implementation can indicate that the Socket is no longer active
+    //   by calling shutdown() on it.  The caller will then call removeSocket()
+    //   soon after processSocketEvent returns, to allow any pre-Socket
+    //   resources to be tidied up.
+    virtual void processSocketEvent(network::Socket* sock) = 0;
 
     // checkTimeouts() allows the server to check socket timeouts, etc.  The
-    // return value is the number of milliseconds to wait before
-    // checkTimeouts() should be called again.  If this number is zero then
-    // there is no timeout and checkTimeouts() should be called the next time
-    // an event occurs.
+    //   return value is the number of milliseconds to wait before
+    //   checkTimeouts() should be called again.  If this number is zero then
+    //   there is no timeout and checkTimeouts() should be called the next time
+    //   an event occurs.
     virtual int checkTimeouts() = 0;
 
-    // soonestTimeout() is a function to help work out the soonest of several
-    // timeouts.
-    static void soonestTimeout(int* timeout, int newTimeout) {
-      if (newTimeout && (!*timeout || newTimeout < *timeout))
-        *timeout = newTimeout;
-    }
     virtual bool getDisable() {return false;};
   };
 
diff --git a/network/TcpSocket.cxx b/network/TcpSocket.cxx
index 035c7f2..3212ace 100644
--- a/network/TcpSocket.cxx
+++ b/network/TcpSocket.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -81,8 +81,11 @@
 }
 
 
-void
-TcpSocket::initTcpSockets() {
+// -=- Socket initialisation
+static bool socketsInitialised = false;
+static void initSockets() {
+  if (socketsInitialised)
+    return;
 #ifdef WIN32
   WORD requiredVersion = MAKEWORD(2,0);
   WSADATA initResult;
@@ -92,8 +95,10 @@
 #else
   signal(SIGPIPE, SIG_IGN);
 #endif
+  socketsInitialised = true;
 }
 
+
 // -=- TcpSocket
 
 TcpSocket::TcpSocket(int sock, bool close)
@@ -107,6 +112,7 @@
   int sock;
 
   // - Create a socket
+  initSockets();
   if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
     throw SocketException("unable to create socket", errorNumber);
 
@@ -149,18 +155,13 @@
     } else break;
   }
 
-  int one = 1;
-  if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
-		 (char *)&one, sizeof(one)) < 0) {
-    int e = errorNumber;
-    closesocket(sock);
-    throw SocketException("unable to setsockopt TCP_NODELAY", e);
-  }
+  // Disable Nagle's algorithm, to reduce latency
+  enableNagles(sock, false);
 
   // Create the input and output streams
   instream = new FdInStream(sock);
   outstream = new FdOutStream(sock);
-  own_streams = true;
+  ownStreams = true;
 }
 
 TcpSocket::~TcpSocket() {
@@ -244,9 +245,21 @@
 
 void TcpSocket::shutdown()
 {
+  Socket::shutdown();
   ::shutdown(getFd(), 2);
 }
 
+bool TcpSocket::enableNagles(int sock, bool enable) {
+  int one = enable ? 0 : 1;
+  if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+		 (char *)&one, sizeof(one)) < 0) {
+    int e = errorNumber;
+    vlog.error("unable to setsockopt TCP_NODELAY: %d", e);
+    return false;
+  }
+  return true;
+}
+
 bool TcpSocket::isSocket(int sock)
 {
   struct sockaddr_in info;
@@ -279,12 +292,13 @@
     return;
   }
 
+  initSockets();
   if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
     throw SocketException("unable to create listening socket", errorNumber);
 
 #ifndef WIN32
   // - By default, close the socket on exec()
-  fcntl(sock, F_SETFD, FD_CLOEXEC);
+  fcntl(fd, F_SETFD, FD_CLOEXEC);
 
   int one = 1;
   if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
@@ -345,14 +359,8 @@
   fcntl(new_sock, F_SETFD, FD_CLOEXEC);
 #endif
 
-  // Disable Nagle's algorithm
-  int one = 1;
-  if (setsockopt(new_sock, IPPROTO_TCP, TCP_NODELAY,
-   (char *)&one, sizeof(one)) < 0) {
-    int e = errorNumber;
-    closesocket(new_sock);
-    throw SocketException("unable to setsockopt TCP_NODELAY", e);
-  }
+  // Disable Nagle's algorithm, to reduce latency
+  TcpSocket::enableNagles(new_sock, false);
 
   // Create the socket object & check connection is allowed
   TcpSocket* s = new TcpSocket(new_sock);
@@ -418,7 +426,8 @@
         return true;
       case Query:
         vlog.debug("QUERY %s", name.buf);
-        return queryUserAcceptConnection(s);
+        s->setRequiresQuery();
+        return true;
       case Reject:
         vlog.debug("REJECT %s", name.buf);
         return false;
diff --git a/network/TcpSocket.h b/network/TcpSocket.h
index 7469782..774c6e1 100644
--- a/network/TcpSocket.h
+++ b/network/TcpSocket.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -57,8 +57,7 @@
 
     virtual void shutdown();
 
-    static void initTcpSockets();
-
+    static bool enableNagles(int sock, bool enable);
     static bool isSocket(int sock);
     static bool isConnected(int sock);
     static int getSockPort(int sock);
diff --git a/network/msvcwarning.h b/network/msvcwarning.h
deleted file mode 100644
index e93f2bb..0000000
--- a/network/msvcwarning.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/* 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 : 4800 ) // forcing bool 'true' or 'false'
diff --git a/network/network.dsp b/network/network.dsp
index c48f5df..16dd94b 100644
--- a/network/network.dsp
+++ b/network/network.dsp
@@ -42,7 +42,7 @@
 # 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 CPP /nologo /MT /W3 /GX /O1 /I ".." /FI"rdr/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

@@ -65,7 +65,7 @@
 # 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" /FR /YX /FD /GZ /c

+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c

 # ADD BASE RSC /l 0x809 /d "_DEBUG"

 # ADD RSC /l 0x809 /d "_DEBUG"

 BSC32=bscmake.exe

@@ -88,7 +88,7 @@
 # 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 CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"rdr/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

diff --git a/rdr/msvcwarning.h b/rdr/msvcwarning.h
index e93f2bb..bea8d3f 100644
--- a/rdr/msvcwarning.h
+++ b/rdr/msvcwarning.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -15,4 +15,12 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
  * USA.
  */
+
+// Trim out extraneous cruft from windows.h includes
+#define WIN32_LEAN_AND_MEAN
+
+// Force all Windows NT-specific APIs to be visible
+#define _WIN32_WINNT 0xffff
+
 #pragma warning( disable : 4800 ) // forcing bool 'true' or 'false'
+#pragma warning( disable : 4786 ) // truncating debug information to 255 chars
\ No newline at end of file
diff --git a/rdr/rdr.dsp b/rdr/rdr.dsp
index d312863..f1633eb 100644
--- a/rdr/rdr.dsp
+++ b/rdr/rdr.dsp
@@ -42,7 +42,7 @@
 # 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 CPP /nologo /MT /W3 /GX /O1 /I ".." /FI"rdr/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

@@ -65,7 +65,7 @@
 # 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" /FR /YX /FD /GZ /c

+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c

 # ADD BASE RSC /l 0x809 /d "_DEBUG"

 # ADD RSC /l 0x809 /d "_DEBUG"

 BSC32=bscmake.exe

@@ -88,7 +88,7 @@
 # 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 CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"rdr/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

diff --git a/rfb/Blacklist.cxx b/rfb/Blacklist.cxx
index 4c4f95b..4590bef 100644
--- a/rfb/Blacklist.cxx
+++ b/rfb/Blacklist.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/Blacklist.h b/rfb/Blacklist.h
index 4df7ec8..0eb3846 100644
--- a/rfb/Blacklist.h
+++ b/rfb/Blacklist.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/CConnection.cxx b/rfb/CConnection.cxx
index c6a3eed..36778f0 100644
--- a/rfb/CConnection.cxx
+++ b/rfb/CConnection.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -44,10 +44,6 @@
   deleteReaderAndWriter();
 }
 
-void CConnection::setServerName(const char* serverName_) {
-  serverName.buf = strDup(serverName_);
-}
-
 void CConnection::deleteReaderAndWriter()
 {
   delete reader_;
@@ -207,10 +203,7 @@
 void CConnection::processSecurityMsg()
 {
   vlog.debug("processing security message");
-  bool done;
-  if (!security->processMsg(this, &done))
-    throwAuthFailureException();
-  if (done) {
+  if (security->processMsg(this)) {
     state_ = RFBSTATE_SECURITY_RESULT;
     processSecurityResultMsg();
   }
@@ -229,17 +222,23 @@
   switch (result) {
   case secResultOK:
     securityCompleted();
-    break;
+    return;
   case secResultFailed:
     vlog.debug("auth failed");
-    throwAuthFailureException();
+    break;
   case secResultTooMany:
     vlog.debug("auth failed - too many tries");
-    throwAuthFailureException();
+    break;
   default:
-    vlog.error("unknown security result");
-    throwAuthFailureException();
-  };
+    throw Exception("Unknown security result from server");
+  }
+  CharArray reason;
+  if (cp.beforeVersion(3,8))
+    reason.buf = strDup("Authentication failure");
+  else
+    reason.buf = is->readString();
+  state_ = RFBSTATE_INVALID;
+  throw AuthFailureException(reason.buf);
 }
 
 void CConnection::processInitMsg()
@@ -248,20 +247,6 @@
   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;
diff --git a/rfb/CConnection.h b/rfb/CConnection.h
index 480fca3..79110eb 100644
--- a/rfb/CConnection.h
+++ b/rfb/CConnection.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -41,11 +41,14 @@
     CConnection();
     virtual ~CConnection();
 
-    // ***
-    void setServerName(const char* serverName_);
-
     // Methods to initialise the connection
 
+    // setServerName() is used to provide a unique(ish) name for the server to
+    // which we are connected.  This might be the result of getPeerEndpoint on
+    // a TcpSocket, for example, or a host specified by DNS name & port.
+    // The serverName is used when verifying the Identity of a host (see RA2).
+    void setServerName(const char* name_) { serverName.replaceBuf(strDup(name_)); }
+
     // 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
@@ -129,7 +132,9 @@
     rdr::InStream* getInStream() { return is; }
     rdr::OutStream* getOutStream() { return os; }
 
-    char* getServerName() {return strDup(serverName.buf);}
+    // Access method used by SSecurity implementations that can verify servers'
+    // Identities, to determine the unique(ish) name of the server.
+    const char* getServerName() const { return serverName.buf; }
 
     enum stateEnum {
       RFBSTATE_UNINITIALISED,
diff --git a/rfb/CMsgHandler.cxx b/rfb/CMsgHandler.cxx
index cc7c11f..bbc1176 100644
--- a/rfb/CMsgHandler.cxx
+++ b/rfb/CMsgHandler.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -34,7 +34,7 @@
   cp.height = height;
 }
 
-void CMsgHandler::setCursor(const Point& hotspot, const Point& size, void* data, void* mask)
+void CMsgHandler::setCursor(int w, int h, const Point& hotspot, void* data, void* mask)
 {
 }
 
diff --git a/rfb/CMsgHandler.h b/rfb/CMsgHandler.h
index 29218c8..6c86df0 100644
--- a/rfb/CMsgHandler.h
+++ b/rfb/CMsgHandler.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -36,8 +36,15 @@
     CMsgHandler();
     virtual ~CMsgHandler();
 
+    // The following methods are called as corresponding messages are read.  A
+    // derived class should override these methods as desired.  Note that for
+    // the setDesktopSize(), setPixelFormat() and setName() methods, a derived
+    // class should call on to CMsgHandler's methods to set the members of cp
+    // appropriately.
+
     virtual void setDesktopSize(int w, int h);
-    virtual void setCursor(const Point& hotspot, const Point& size, void* data, void* mask);
+    virtual void setCursor(int width, int height, const Point& hotspot,
+                           void* data, void* mask);
     virtual void setPixelFormat(const PixelFormat& pf);
     virtual void setName(const char* name);
     virtual void serverInit();
diff --git a/rfb/CMsgReader.cxx b/rfb/CMsgReader.cxx
index 46973eb..38547c0 100644
--- a/rfb/CMsgReader.cxx
+++ b/rfb/CMsgReader.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -41,10 +41,6 @@
   delete [] imageBuf;
 }
 
-void CMsgReader::endMsg()
-{
-}
-
 void CMsgReader::readSetColourMapEntries()
 {
   is->skip(1);
@@ -53,13 +49,11 @@
   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();
 }
 
@@ -75,19 +69,16 @@
   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();
 }
 
@@ -128,17 +119,17 @@
   handler->copyRect(r, srcX, srcY);
 }
 
-void CMsgReader::readSetCursor(const Point& hotspot, const Point& size)
+void CMsgReader::readSetCursor(int width, int height, const Point& hotspot)
 {
-  int data_len = size.x * size.y * (handler->cp.pf().bpp/8);
-  int mask_len = ((size.x+7)/8) * size.y;
+  int data_len = width * height * (handler->cp.pf().bpp/8);
+  int mask_len = ((width+7)/8) * height;
   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);
+  handler->setCursor(width, height, hotspot, data.buf, mask.buf);
 }
 
 rdr::U8* CMsgReader::getImageBuf(int required, int requested, int* nPixels)
diff --git a/rfb/CMsgReader.h b/rfb/CMsgReader.h
index 8b4638c..7a611fc 100644
--- a/rfb/CMsgReader.h
+++ b/rfb/CMsgReader.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -53,15 +53,13 @@
     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);
+    virtual void readSetCursor(int width, int height, const Point& hotspot);
 
     CMsgReader(CMsgHandler* handler, rdr::InStream* is);
 
diff --git a/rfb/CMsgReaderV3.cxx b/rfb/CMsgReaderV3.cxx
index 9370804..d0cfc89 100644
--- a/rfb/CMsgReaderV3.cxx
+++ b/rfb/CMsgReaderV3.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -21,6 +21,7 @@
 #include <rdr/InStream.h>
 #include <rfb/CMsgReaderV3.h>
 #include <rfb/CMsgHandler.h>
+#include <rfb/util.h>
 
 using namespace rfb;
 
@@ -41,10 +42,8 @@
   PixelFormat pf;
   pf.read(is);
   handler->setPixelFormat(pf);
-  char* name = is->readString();
-  handler->setName(name);
-  delete [] name;
-  endMsg();
+  CharArray name(is->readString());
+  handler->setName(name.buf);
   handler->serverInit();
 }
 
@@ -85,7 +84,7 @@
       handler->setDesktopSize(w, h);
       break;
     case pseudoEncodingCursor:
-      readSetCursor(Point(x, y), Point(w, h));
+      readSetCursor(w, h, Point(x,y));
       break;
     case pseudoEncodingLastRect:
       nUpdateRectsLeft = 1;     // this rectangle is the last one
@@ -104,6 +103,5 @@
 {
   is->skip(1);
   nUpdateRectsLeft = is->readU16();
-  endMsg();
   handler->framebufferUpdateStart();
 }
diff --git a/rfb/CMsgReaderV3.h b/rfb/CMsgReaderV3.h
index 93c8c6a..689bb65 100644
--- a/rfb/CMsgReaderV3.h
+++ b/rfb/CMsgReaderV3.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/CMsgWriter.cxx b/rfb/CMsgWriter.cxx
index fb272c4..cdfb4e5 100644
--- a/rfb/CMsgWriter.cxx
+++ b/rfb/CMsgWriter.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -96,7 +96,7 @@
 }
 
 
-void CMsgWriter::writeKeyEvent(rdr::U32 key, bool down)
+void CMsgWriter::keyEvent(rdr::U32 key, bool down)
 {
   startMsg(msgTypeKeyEvent);
   os->writeU8(down);
@@ -106,22 +106,23 @@
 }
 
 
-void CMsgWriter::writePointerEvent(int x, int y, int buttonMask)
+void CMsgWriter::pointerEvent(const Point& pos, 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;
+  Point p(pos);
+  if (p.x < 0) p.x = 0;
+  if (p.y < 0) p.y = 0;
+  if (p.x >= cp->width) p.x = cp->width - 1;
+  if (p.y >= cp->height) p.y = cp->height - 1;
 
   startMsg(msgTypePointerEvent);
   os->writeU8(buttonMask);
-  os->writeU16(x);
-  os->writeU16(y);
+  os->writeU16(p.x);
+  os->writeU16(p.y);
   endMsg();
 }
 
 
-void CMsgWriter::writeClientCutText(const char* str, int len)
+void CMsgWriter::clientCutText(const char* str, int len)
 {
   startMsg(msgTypeClientCutText);
   os->pad(3);
@@ -129,8 +130,3 @@
   os->writeBytes(str, len);
   endMsg();
 }
-
-void CMsgWriter::setOutStream(rdr::OutStream* os_)
-{
-  os = os_;
-}
diff --git a/rfb/CMsgWriter.h b/rfb/CMsgWriter.h
index 8d6e373..19be8df 100644
--- a/rfb/CMsgWriter.h
+++ b/rfb/CMsgWriter.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -22,7 +22,7 @@
 #ifndef __RFB_CMSGWRITER_H__
 #define __RFB_CMSGWRITER_H__
 
-#include <rdr/types.h>
+#include <rfb/InputHandler.h>
 
 namespace rdr { class OutStream; }
 
@@ -32,24 +32,25 @@
   class ConnParams;
   struct Rect;
 
-  class CMsgWriter {
+  class CMsgWriter : public InputHandler {
   public:
     virtual ~CMsgWriter();
 
+    // CMsgWriter abstract interface methods
     virtual void writeClientInit(bool shared)=0;
+    virtual void startMsg(int type)=0;
+    virtual void endMsg()=0;
 
+    // CMsgWriter implemented methods
     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);
+    // InputHandler implementation
+    virtual void keyEvent(rdr::U32 key, bool down);
+    virtual void pointerEvent(const Point& pos, int buttonMask);
+    virtual void clientCutText(const char* str, int len);
 
     ConnParams* getConnParams() { return cp; }
     rdr::OutStream* getOutStream() { return os; }
diff --git a/rfb/CMsgWriterV3.cxx b/rfb/CMsgWriterV3.cxx
index a6ad237..a980795 100644
--- a/rfb/CMsgWriterV3.cxx
+++ b/rfb/CMsgWriterV3.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/CMsgWriterV3.h b/rfb/CMsgWriterV3.h
index 0cf6157..0b2f9af 100644
--- a/rfb/CMsgWriterV3.h
+++ b/rfb/CMsgWriterV3.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/CSecurity.h b/rfb/CSecurity.h
index 639d550..90a01d7 100644
--- a/rfb/CSecurity.h
+++ b/rfb/CSecurity.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -18,10 +18,14 @@
 //
 // 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.
+// 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.  It
+// should return false when it needs more data, or true when the security
+// handshaking is over and we are now waiting for the SecurityResult message
+// from the server.  In the event of failure a suitable exception should be
+// thrown.
 //
 // 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
@@ -39,7 +43,7 @@
   class CSecurity {
   public:
     virtual ~CSecurity() {}
-    virtual bool processMsg(CConnection* cc, bool* done)=0;
+    virtual bool processMsg(CConnection* cc)=0;
     virtual void destroy() { delete this; }
     virtual int getType() const = 0;
     virtual const char* description() const = 0;
diff --git a/rfb/CSecurityNone.h b/rfb/CSecurityNone.h
index 23b36ce..54c1016 100644
--- a/rfb/CSecurityNone.h
+++ b/rfb/CSecurityNone.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -28,9 +28,7 @@
 
   class CSecurityNone : public CSecurity {
   public:
-    virtual bool processMsg(CConnection* cc, bool* done) {
-      *done = true; return true;
-    }
+    virtual bool processMsg(CConnection* cc) { return true; }
     virtual int getType() const {return secTypeNone;}
     virtual const char* description() const {return "No Encryption";}
   };
diff --git a/rfb/CSecurityVncAuth.cxx b/rfb/CSecurityVncAuth.cxx
index 3d6c87c..ba5a30b 100644
--- a/rfb/CSecurityVncAuth.cxx
+++ b/rfb/CSecurityVncAuth.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -18,19 +18,25 @@
 //
 // CSecurityVncAuth
 //
+// XXX not thread-safe, because d3des isn't - do we need to worry about this?
+//
 
 #include <string.h>
 #include <stdio.h>
 #include <rfb/CConnection.h>
 #include <rfb/UserPasswdGetter.h>
-#include <rfb/vncAuth.h>
+#include <rfb/Password.h>
 #include <rfb/CSecurityVncAuth.h>
-#include <rfb/LogWriter.h>
 #include <rfb/util.h>
+extern "C" {
+#include <rfb/d3des.h>
+}
+
 
 using namespace rfb;
 
-static LogWriter vlog("VncAuth");
+static const int vncAuthChallengeSize = 16;
+
 
 CSecurityVncAuth::CSecurityVncAuth(UserPasswdGetter* upg_)
   : upg(upg_)
@@ -41,23 +47,28 @@
 {
 }
 
-bool CSecurityVncAuth::processMsg(CConnection* cc, bool* done)
+bool CSecurityVncAuth::processMsg(CConnection* cc)
 {
-  *done = false;
   rdr::InStream* is = cc->getInStream();
   rdr::OutStream* os = cc->getOutStream();
 
+  // Read the challenge & obtain the user's password
   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));
+  PlainPasswd passwd;
+  upg->getUserPasswd(0, &passwd.buf);
+
+  // Calculate the correct response
+  rdr::U8 key[8];
+  int pwdLen = strlen(passwd.buf);
+  for (int i=0; i<8; i++)
+    key[i] = i<pwdLen ? passwd.buf[i] : 0;
+  deskey(key, EN0);
+  for (int j = 0; j < vncAuthChallengeSize; j += 8)
+    des(challenge+j, challenge+j);
+
+  // Return the response to the server
   os->writeBytes(challenge, vncAuthChallengeSize);
   os->flush();
-  *done = true;
   return true;
 }
diff --git a/rfb/CSecurityVncAuth.h b/rfb/CSecurityVncAuth.h
index bfa40b3..8d38d87 100644
--- a/rfb/CSecurityVncAuth.h
+++ b/rfb/CSecurityVncAuth.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -20,7 +20,6 @@
 
 #include <rfb/CSecurity.h>
 #include <rfb/secTypes.h>
-#include <rfb/vncAuth.h>
 
 namespace rfb {
 
@@ -30,7 +29,7 @@
   public:
     CSecurityVncAuth(UserPasswdGetter* pg);
     virtual ~CSecurityVncAuth();
-    virtual bool processMsg(CConnection* cc, bool* done);
+    virtual bool processMsg(CConnection* cc);
     virtual int getType() const {return secTypeVncAuth;};
     virtual const char* description() const {return "No Encryption";}
   private:
diff --git a/rfb/ColourCube.h b/rfb/ColourCube.h
index 904256c..b83cbba 100644
--- a/rfb/ColourCube.h
+++ b/rfb/ColourCube.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/ColourMap.h b/rfb/ColourMap.h
index ee7783d..da6cb12 100644
--- a/rfb/ColourMap.h
+++ b/rfb/ColourMap.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/ComparingUpdateTracker.cxx b/rfb/ComparingUpdateTracker.cxx
index 0b548a6..ce3d68a 100644
--- a/rfb/ComparingUpdateTracker.cxx
+++ b/rfb/ComparingUpdateTracker.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -24,8 +24,7 @@
 using namespace rfb;
 
 ComparingUpdateTracker::ComparingUpdateTracker(PixelBuffer* buffer)
-  : SimpleUpdateTracker(true), fb(buffer),
-    oldFb(fb->getPF(), 0, 0), firstCompare(true)
+  : fb(buffer), oldFb(fb->getPF(), 0, 0), firstCompare(true)
 {
     changed.assign_union(fb->getRect());
 }
@@ -35,19 +34,6 @@
 }
 
 
-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()
@@ -60,7 +46,7 @@
     // 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(), vncmin(fb->height(), y+BLOCK_SIZE));
+      Rect pos(0, y, fb->width(), __rfbmin(fb->height(), y+BLOCK_SIZE));
       int srcStride;
       const rdr::U8* srcData = fb->getPixelsR(pos, &srcStride);
       oldFb.imageRect(pos, srcData, srcStride);
@@ -100,20 +86,20 @@
   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, vncmin(r.br.y, blockTop+BLOCK_SIZE));
+    Rect pos(r.tl.x, blockTop, r.br.x, __rfbmin(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 = vncmin(blockTop+BLOCK_SIZE, r.br.y);
+    int blockBottom = __rfbmin(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 = vncmin(blockLeft+BLOCK_SIZE, r.br.x);
+      int blockRight = __rfbmin(blockLeft+BLOCK_SIZE, r.br.x);
       int blockWidthInBytes = (blockRight-blockLeft) * bytesPerPixel;
 
       for (int y = blockTop; y < blockBottom; y++)
diff --git a/rfb/ComparingUpdateTracker.h b/rfb/ComparingUpdateTracker.h
index bec93d1..5d2e5ed 100644
--- a/rfb/ComparingUpdateTracker.h
+++ b/rfb/ComparingUpdateTracker.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -32,10 +32,6 @@
     // 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;
diff --git a/rfb/Configuration.cxx b/rfb/Configuration.cxx
index a08260d..9ebc20a 100644
--- a/rfb/Configuration.cxx
+++ b/rfb/Configuration.cxx
@@ -1,6 +1,6 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
  * Copyright (C) 2004-2005 Cendio AB. 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
@@ -32,15 +32,19 @@
 #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;
-
+#ifdef __RFB_THREADING_IMPL
+// On platforms that support Threading, we use Locks to make getData safe
+#define LOCK_CONFIG Lock l(*configLock())
+rfb::Mutex* configLock_ = 0;
+static rfb::Mutex* configLock() {
+  if (!configLock_)
+    configLock_ = new rfb::Mutex;
+  return configLock_;
+}
+#else
+#define LOCK_CONFIG
 #endif
 
 #include <rdr/HexOutStream.h>
@@ -51,15 +55,47 @@
 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);
+// -=- The Global Configuration object
+Configuration* Configuration::global_ = 0;
+Configuration* Configuration::global() {
+  if (!global_)
+    global_ = new Configuration("Global");
+  return global_;
 }
 
-bool Configuration::setParam(const char* name, int len,
+
+// -=- Configuration implementation
+
+Configuration::Configuration(const char* name_, Configuration* attachToGroup) 
+: name(strDup(name_)), head(0), _next(0) {
+  if (attachToGroup) {
+    _next = attachToGroup->_next;
+    attachToGroup->_next = this;
+  }
+}
+
+Configuration& Configuration::operator=(const Configuration& src) {
+  VoidParameter* current = head;
+  while (current) {
+    VoidParameter* srcParam = ((Configuration&)src).get(current->getName());
+    if (srcParam) {
+      current->immutable = false;
+      CharArray value(srcParam->getValueStr());
+      vlog.debug("operator=(%s, %s)", current->getName(), value.buf);
+      current->setParam(value.buf);
+    }
+    current = current->_next;
+  }
+  if (_next)
+    *_next=src;
+  return *this;
+}
+
+bool Configuration::set(const char* n, const char* v, bool immutable) {
+  return set(n, strlen(n), v, immutable);
+}
+
+bool Configuration::set(const char* name, int len,
                              const char* val, bool immutable)
 {
   VoidParameter* current = head;
@@ -75,10 +111,10 @@
     }
     current = current->_next;
   }
-  return false;
+  return _next ? _next->set(name, len, val, immutable) : false;
 }
 
-bool Configuration::setParam(const char* config, bool immutable) {
+bool Configuration::set(const char* config, bool immutable) {
   bool hyphen = false;
   if (config[0] == '-') {
     hyphen = true;
@@ -87,7 +123,7 @@
   }
   const char* equal = strchr(config, '=');
   if (equal) {
-    return setParam(config, equal-config, equal+1, immutable);
+    return set(config, equal-config, equal+1, immutable);
   } else if (hyphen) {
     VoidParameter* current = head;
     while (current) {
@@ -101,10 +137,10 @@
       current = current->_next;
     }
   }    
-  return false;
+  return _next ? _next->set(config, immutable) : false;
 }
 
-VoidParameter* Configuration::getParam(const char* param)
+VoidParameter* Configuration::get(const char* param)
 {
   VoidParameter* current = head;
   while (current) {
@@ -112,11 +148,13 @@
       return current;
     current = current->_next;
   }
-  return 0;
+  return _next ? _next->get(param) : 0;
 }
 
-void Configuration::listParams(int width, int nameWidth) {
+void Configuration::list(int width, int nameWidth) {
   VoidParameter* current = head;
+
+  fprintf(stderr, "%s Parameters:\n", name.buf);
   while (current) {
     char* def_str = current->getDefaultStr();
     const char* desc = current->getDescription();
@@ -150,14 +188,20 @@
     }
     current = current->_next;
   }
+
+  if (_next)
+    _next->list(width, nameWidth);
 }
 
+
 // -=- VoidParameter
 
-VoidParameter::VoidParameter(const char* name_, const char* desc_)
+VoidParameter::VoidParameter(const char* name_, const char* desc_, Configuration* conf)
   : immutable(false), _hasBeenSet(false), name(name_), description(desc_) {
-  _next = Configuration::head;
-  Configuration::head = this;
+  if (!conf)
+    conf = Configuration::global();
+  _next = conf->head;
+  conf->head = this;
 }
 
 VoidParameter::~VoidParameter() {
@@ -200,8 +244,8 @@
 // -=- AliasParameter
 
 AliasParameter::AliasParameter(const char* name_, const char* desc_,
-                               VoidParameter* param_)
-  : VoidParameter(name_, desc_), param(param_) {
+                               VoidParameter* param_, Configuration* conf)
+  : VoidParameter(name_, desc_, conf), param(param_) {
 }
 
 bool
@@ -235,8 +279,8 @@
 
 // -=- BoolParameter
 
-BoolParameter::BoolParameter(const char* name_, const char* desc_, bool v)
-: VoidParameter(name_, desc_), value(v), def_value(v) {
+BoolParameter::BoolParameter(const char* name_, const char* desc_, bool v, Configuration* conf)
+: VoidParameter(name_, desc_, conf), value(v), def_value(v) {
 }
 
 bool
@@ -271,15 +315,11 @@
 
 char*
 BoolParameter::getDefaultStr() const {
-  char* result = new char[8];
-  sprintf(result, "%d", (int)def_value);
-  return result;
+  return strDup(def_value ? "1" : "0");
 }
 
 char* BoolParameter::getValueStr() const {
-  char* result = new char[8];
-  sprintf(result, "%d", (int)value);
-  return result;
+  return strDup(value ? "1" : "0");
 }
 
 bool BoolParameter::isBool() const {
@@ -292,15 +332,21 @@
 
 // -=- IntParameter
 
-IntParameter::IntParameter(const char* name_, const char* desc_, int v)
-: VoidParameter(name_, desc_), value(v), def_value(v) {
+IntParameter::IntParameter(const char* name_, const char* desc_, int v,
+                           int minValue_, int maxValue_, Configuration* conf)
+  : VoidParameter(name_, desc_, conf), value(v), def_value(v),
+    minValue(minValue_), maxValue(maxValue_)
+{
 }
 
 bool
 IntParameter::setParam(const char* v) {
   if (immutable) return true;
   vlog.debug("set %s(Int) to %s", getName(), v);
-  value = atoi(v);
+  int i = atoi(v);
+  if (i < minValue || i > maxValue)
+    return false;
+  value = i;
   return true;
 }
 
@@ -308,6 +354,8 @@
 IntParameter::setParam(int v) {
   if (immutable) return true;
   vlog.debug("set %s(Int) to %d", getName(), v);
+  if (v < minValue || v > maxValue)
+    return false;
   value = v;
   return true;
 }
@@ -332,8 +380,8 @@
 // -=- StringParameter
 
 StringParameter::StringParameter(const char* name_, const char* desc_,
-                                 const char* v)
-  : VoidParameter(name_, desc_), value(strDup(v)), def_value(v)
+                                 const char* v, Configuration* conf)
+  : VoidParameter(name_, desc_, conf), value(strDup(v)), def_value(v)
 {
   if (!v) {
     fprintf(stderr,"Default value <null> for %s not allowed\n",name_);
@@ -346,14 +394,12 @@
 }
 
 bool StringParameter::setParam(const char* v) {
-#ifdef WIN32
-  Lock l(configLock);
-#endif
+  LOCK_CONFIG;
   if (immutable) return true;
   if (!v)
     throw rfb::Exception("setParam(<null>) not allowed");
   vlog.debug("set %s(String) to %s", getName(), v);
-  strFree(value);
+  CharArray oldValue(value);
   value = strDup(v);
   return value != 0;
 }
@@ -363,16 +409,14 @@
 }
 
 char* StringParameter::getValueStr() const {
-#ifdef WIN32
-  Lock l(configLock);
-#endif
+  LOCK_CONFIG;
   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) {
+BinaryParameter::BinaryParameter(const char* name_, const char* desc_, const void* v, int l, Configuration* conf)
+: VoidParameter(name_, desc_, conf), value(0), length(0), def_value((char*)v), def_length(l) {
   if (l) {
     value = new char[l];
     length = l;
@@ -385,18 +429,14 @@
 }
 
 bool BinaryParameter::setParam(const char* v) {
-#ifdef WIN32
-  Lock l(configLock);
-#endif
+  LOCK_CONFIG;
   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
+  LOCK_CONFIG;
   if (immutable) return; 
   vlog.debug("set %s(Binary)", getName());
   delete [] value; value = 0;
@@ -412,16 +452,12 @@
 }
 
 char* BinaryParameter::getValueStr() const {
-#ifdef WIN32
-  Lock l(configLock);
-#endif
+  LOCK_CONFIG;
   return rdr::HexOutStream::binToHexStr(value, length);
 }
 
 void BinaryParameter::getData(void** data_, int* length_) const {
-#ifdef WIN32
-  Lock l(configLock);
-#endif
+  LOCK_CONFIG;
   if (length_) *length_ = length;
   if (data_) {
     *data_ = new char[length];
diff --git a/rfb/Configuration.h b/rfb/Configuration.h
index d50c1cb..e3b85b8 100644
--- a/rfb/Configuration.h
+++ b/rfb/Configuration.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -27,38 +27,106 @@
 // 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.
+// If no Configuration is specified when creating a Parameter, then the
+// global Configuration will be assumed.
+//
+// Configurations can be "chained" into groups.  Each group has a root
+// Configuration, a pointer to which should be passed to the constructors
+// of the other group members.  set() and get() operations called on the
+// root will iterate through all of the group's members.
+//
+// NB: On platforms that support Threading, locking is performed to protect
+//     complex parameter types from concurrent access (e.g. strings).
+// NB: NO LOCKING is performed when linking Configurations to groups
+//     or when adding Parameters to Configurations.
 
 #ifndef __RFB_CONFIGURATION_H__
 #define __RFB_CONFIGURATION_H__
 
+#include <rfb/util.h>
+
 namespace rfb {
   class VoidParameter;
+  struct ParameterIterator;
 
   // -=- Configuration
   //     Class used to access parameters.
 
   class Configuration {
   public:
+    // - Create a new Configuration object
+    Configuration(const char* name, Configuration* attachToGroup=0);
+
+    // - Return the buffer containing the Configuration's name
+    const char* getName() const { return name.buf; }
+
+    // - Assignment operator.  For every Parameter in this Configuration's
+    //   group, get()s the corresponding source parameter and copies its
+    //   content.
+    Configuration& operator=(const Configuration& src);
+
     // - Set named parameter to value
-    static bool setParam(const char* param, const char* value, bool immutable=false);
+    bool set(const char* param, const char* value, bool immutable=false);
 
     // - Set parameter to value (separated by "=")
-    static bool setParam(const char* config, bool immutable=false);
+    bool set(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);
+    bool set(const char* name, int len,
+                  const char* val, bool immutable);
 
     // - Get named parameter
-    static VoidParameter* getParam(const char* param);
+    VoidParameter* get(const char* param);
 
-    static void listParams(int width=79, int nameWidth=10);
+    // - List the parameters of this Configuration group
+    void list(int width=79, int nameWidth=10);
 
-    static VoidParameter* head;
+    // - readFromFile
+    //   Read configuration parameters from the specified file.
+    void readFromFile(const char* filename);
+
+    // - writeConfigToFile
+    //   Write a new configuration parameters file, then mv it
+    //   over the old file.
+    void writeToFile(const char* filename);
+
+
+    // - Get the Global Configuration object
+    //   NB: This call does NOT lock the Configuration system.
+    //       ALWAYS ensure that if you have ANY global Parameters,
+    //       then they are defined as global objects, to ensure that
+    //       global() is called when only the main thread is running.
+    static Configuration* global();
+
+    // - Container for process-wide Global parameters
+    static bool setParam(const char* param, const char* value, bool immutable=false) {
+      return global()->set(param, value, immutable);
+    }
+    static bool setParam(const char* config, bool immutable=false) { 
+      return global()->set(config, immutable);
+    }
+    static bool setParam(const char* name, int len,
+      const char* val, bool immutable) {
+      return global()->set(name, len, val, immutable);
+    }
+    static VoidParameter* getParam(const char* param) { return global()->get(param); }
+    static void listParams(int width=79, int nameWidth=10) { global()->list(width, nameWidth); }
+
+  protected:
+    friend class VoidParameter;
+    friend struct ParameterIterator;
+
+    // Name for this Configuration
+    CharArray name;
+
+    // - Pointer to first Parameter in this group
+    VoidParameter* head;
+
+    // Pointer to next Configuration in this group
+    Configuration* _next;
+
+    // The process-wide, Global Configuration object
+    static Configuration* global_;
   };
 
   // -=- VoidParameter
@@ -66,7 +134,7 @@
 
   class VoidParameter {
   public:
-    VoidParameter(const char* name_, const char* desc_);
+    VoidParameter(const char* name_, const char* desc_, Configuration* conf=0);
     virtual  ~VoidParameter();
     const char* getName() const;
     const char* getDescription() const;
@@ -81,8 +149,11 @@
     virtual void setHasBeenSet();
     bool hasBeenSet();
 
-    VoidParameter* _next;
   protected:
+    friend class Configuration;
+    friend struct ParameterIterator;
+
+    VoidParameter* _next;
     bool immutable;
     bool _hasBeenSet;
     const char* name;
@@ -91,7 +162,7 @@
 
   class AliasParameter : public VoidParameter {
   public:
-    AliasParameter(const char* name_, const char* desc_,VoidParameter* param_);
+    AliasParameter(const char* name_, const char* desc_,VoidParameter* param_, Configuration* conf=0);
     virtual bool setParam(const char* value);
     virtual bool setParam();
     virtual char* getDefaultStr() const;
@@ -104,7 +175,7 @@
 
   class BoolParameter : public VoidParameter {
   public:
-    BoolParameter(const char* name_, const char* desc_, bool v);
+    BoolParameter(const char* name_, const char* desc_, bool v, Configuration* conf=0);
     virtual bool setParam(const char* value);
     virtual bool setParam();
     virtual void setParam(bool b);
@@ -119,7 +190,8 @@
 
   class IntParameter : public VoidParameter {
   public:
-    IntParameter(const char* name_, const char* desc_, int v);
+    IntParameter(const char* name_, const char* desc_, int v,
+                 int minValue=INT_MIN, int maxValue=INT_MAX, Configuration* conf=0);
     virtual bool setParam(const char* value);
     virtual bool setParam(int v);
     virtual char* getDefaultStr() const;
@@ -128,13 +200,14 @@
   protected:
     int value;
     int def_value;
+    int minValue, maxValue;
   };
 
   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);
+    StringParameter(const char* name_, const char* desc_, const char* v, Configuration* conf=0);
     virtual ~StringParameter();
     virtual bool setParam(const char* value);
     virtual char* getDefaultStr() const;
@@ -150,13 +223,15 @@
 
   class BinaryParameter : public VoidParameter {
   public:
-    BinaryParameter(const char* name_, const char* desc_, const void* v, int l);
+    BinaryParameter(const char* name_, const char* desc_, const void* v, int l, Configuration* conf=0);
     virtual ~BinaryParameter();
     virtual bool setParam(const char* value);
     virtual void setParam(const void* v, int l);
     virtual char* getDefaultStr() const;
     virtual char* getValueStr() const;
 
+    // getData() will return length zero if there is no data
+    // NB: data may be set to zero, OR set to a zero-length buffer
     void getData(void** data, int* length) const;
 
   protected:
@@ -166,6 +241,25 @@
     int def_length;
   };
 
+  // -=- ParameterIterator
+  //     Iterates over all the Parameters in a Configuration group.  The
+  //     current Parameter is accessed via param, the current Configuration
+  //     via config.  The next() method moves on to the next Parameter.
+
+  struct ParameterIterator {
+    ParameterIterator(Configuration* c) : config(c), param(c ? c->head : 0) {}
+    void next() {
+      param = param->_next;
+      while (!param) {
+        config = config->_next;
+        if (!config) break;
+        param = config->head;
+      }
+    }
+    Configuration* config;
+    VoidParameter* param;
+  };
+
 };
 
 #endif // __RFB_CONFIGURATION_H__
diff --git a/rfb/ConnParams.cxx b/rfb/ConnParams.cxx
index 4a8e534..d4ae589 100644
--- a/rfb/ConnParams.cxx
+++ b/rfb/ConnParams.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -27,9 +27,10 @@
 
 ConnParams::ConnParams()
   : majorVersion(0), minorVersion(0), width(0), height(0), useCopyRect(false),
-    supportsLocalCursor(false), supportsLocalXCursor(false), supportsDesktopResize(false), supportsLastRect(false), 
-    customCompressLevel(false), compressLevel(6), noJpeg(false), qualityLevel(-1), 
-    name_(0), nEncodings_(0), encodings_(0), 
+    supportsLocalCursor(false), supportsLocalXCursor(false), supportsDesktopResize(true),
+    supportsLastRect(false), customCompressLevel(false), compressLevel(6),
+    noJpeg(false), qualityLevel(-1), 
+    name_(0), nEncodings_(0), encodings_(0),
     currentEncoding_(encodingRaw), verStrPos(0)
 {
   setName("");
@@ -88,6 +89,7 @@
   nEncodings_ = nEncodings;
   useCopyRect = false;
   supportsLocalCursor = false;
+  supportsDesktopResize = false;
   supportsLocalXCursor = false;
   supportsLastRect = false;
   customCompressLevel = false;
diff --git a/rfb/ConnParams.h b/rfb/ConnParams.h
index 09e79c2..47e6a5f 100644
--- a/rfb/ConnParams.h
+++ b/rfb/ConnParams.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/Cursor.cxx b/rfb/Cursor.cxx
index b50d925..c8dc341 100644
--- a/rfb/Cursor.cxx
+++ b/rfb/Cursor.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -83,6 +83,7 @@
 {
   bool gotPix0 = false;
   bool gotPix1 = false;
+  *pix0 = *pix1 = 0;
   rdr::U8Array source(maskLen());
   memset(source.buf, 0, maskLen());
 
diff --git a/rfb/Cursor.h b/rfb/Cursor.h
index 0f18775..7d94d70 100644
--- a/rfb/Cursor.h
+++ b/rfb/Cursor.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/Decoder.cxx b/rfb/Decoder.cxx
index e04cbf9..b6e4fd5 100644
--- a/rfb/Decoder.cxx
+++ b/rfb/Decoder.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/Decoder.h b/rfb/Decoder.h
index 914b26a..3fdba53 100644
--- a/rfb/Decoder.h
+++ b/rfb/Decoder.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/Encoder.cxx b/rfb/Encoder.cxx
index aba38b3..53cb170 100644
--- a/rfb/Encoder.cxx
+++ b/rfb/Encoder.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/Encoder.h b/rfb/Encoder.h
index 71739ba..df50dd6 100644
--- a/rfb/Encoder.h
+++ b/rfb/Encoder.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/HTTPServer.cxx b/rfb/HTTPServer.cxx
index 3bac4f8..e40d480 100644
--- a/rfb/HTTPServer.cxx
+++ b/rfb/HTTPServer.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -20,11 +20,6 @@
 #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
@@ -36,6 +31,9 @@
 
 static LogWriter vlog("HTTPServer");
 
+const int clientWaitTimeMillis = 20000;
+const int idleTimeoutSecs = 5 * 60;
+
 
 //
 // -=- LineReader
@@ -94,7 +92,8 @@
 class rfb::HTTPServer::Session {
 public:
   Session(network::Socket& s, rfb::HTTPServer& srv)
-    : contentType(0), line(s.inStream(), 256), sock(s),
+    : contentType(0), contentLength(-1), lastModified(-1),
+      line(s.inStream(), 256), sock(s),
       server(srv), state(ReadRequestLine), lastActive(time(0)) {
   }
   ~Session() {
@@ -111,6 +110,8 @@
 protected:
   CharArray uri;
   const char* contentType;
+  int contentLength;
+  time_t lastModified;
   LineReader line;
   network::Socket& sock;
   rfb::HTTPServer& server;
@@ -140,6 +141,7 @@
 
 // - Write an HTTP-compliant response to the client
 
+
 void
 HTTPServer::Session::writeResponse(int result, const char* text) {
   char buffer[1024];
@@ -149,6 +151,19 @@
   OutStream& os=sock.outStream();
   writeLine(os, buffer);
   writeLine(os, "Server: TightVNC/4.0");
+  time_t now = time(0);
+  struct tm* tm = gmtime(&now);
+  strftime(buffer, 1024, "Date: %a, %d %b %Y %H:%M:%S GMT", tm);
+  writeLine(os, buffer);
+  if (lastModified == (time_t)-1 || lastModified == 0)
+    lastModified = now;
+  tm = gmtime(&lastModified);
+  strftime(buffer, 1024, "Last-Modified: %a, %d %b %Y %H:%M:%S GMT", tm);
+  writeLine(os, buffer);
+  if (contentLength != -1) {
+    sprintf(buffer,"Content-Length: %d",contentLength);
+    writeLine(os, buffer);
+  }
   writeLine(os, "Connection: close");
   os.writeBytes("Content-Type: ", 14);
   if (result == 200) {
@@ -247,7 +262,10 @@
       {
         CharArray address(sock.getPeerAddress());
         vlog.info("getting %s for %s", uri.buf, address.buf);
-        InStream* data = server.getFile(uri.buf, &contentType);
+        contentLength = -1;
+        lastModified = -1;
+        InStream* data = server.getFile(uri.buf, &contentType, &contentLength,
+                                        &lastModified);
         if (!data)
           return writeResponse(404);
 
@@ -277,9 +295,9 @@
 
 int HTTPServer::Session::checkIdleTimeout() {
   time_t now = time(0);
-  int timeout = (lastActive + rfb::Server::idleTimeout) - now;
+  int timeout = (lastActive + idleTimeoutSecs) - now;
   if (timeout > 0)
-    return timeout * 1000;
+    return secsToMillis(timeout);
   sock.shutdown();
   return 0;
 }
@@ -291,28 +309,38 @@
 
 HTTPServer::~HTTPServer() {
   std::list<Session*>::iterator i;
-  for (i=sessions.begin(); i!=sessions.end(); i++) {
-    delete (*i)->getSock();
+  for (i=sessions.begin(); i!=sessions.end(); i++)
     delete *i;
-  }
 }
 
 
 // -=- SocketServer interface implementation
 
 void
-HTTPServer::addClient(network::Socket* sock) {
+HTTPServer::addSocket(network::Socket* sock, bool) {
   Session* s = new Session(*sock, *this);
   if (!s) {
     sock->shutdown();
   } else {
-    sock->inStream().setTimeout(rfb::Server::clientWaitTimeMillis);
-    sock->outStream().setTimeout(rfb::Server::clientWaitTimeMillis);
+    sock->inStream().setTimeout(clientWaitTimeMillis);
+    sock->outStream().setTimeout(clientWaitTimeMillis);
     sessions.push_front(s);
   }
 }
 
-bool
+void
+HTTPServer::removeSocket(network::Socket* sock) {
+  std::list<Session*>::iterator i;
+  for (i=sessions.begin(); i!=sessions.end(); i++) {
+    if ((*i)->getSock() == sock) {
+      delete *i;
+      sessions.erase(i);
+      return;
+    }
+  }
+}
+
+void
 HTTPServer::processSocketEvent(network::Socket* sock) {
   std::list<Session*>::iterator i;
   for (i=sessions.begin(); i!=sessions.end(); i++) {
@@ -320,21 +348,16 @@
       try {
         if ((*i)->processHTTP()) {
           vlog.info("completed HTTP request");
-          delete *i;
-          sessions.erase(i);
-          break;
+          sock->shutdown();
         }
-        return true;
       } catch (rdr::Exception& e) {
         vlog.error("untrapped: %s", e.str());
-        delete *i;
-        sessions.erase(i);
-        break;
+        sock->shutdown();
       }
+      return;
     }
   }
-  delete sock;
-  return false;
+  throw rdr::Exception("invalid Socket in HTTPServer");
 }
 
 void HTTPServer::getSockets(std::list<network::Socket*>* sockets)
@@ -359,7 +382,9 @@
 // -=- Default getFile implementation
 
 InStream*
-HTTPServer::getFile(const char* name, const char** contentType) {
+HTTPServer::getFile(const char* name, const char** contentType,
+                    int* contentLength, time_t* lastModified)
+{
   return 0;
 }
 
diff --git a/rfb/HTTPServer.h b/rfb/HTTPServer.h
index 9431195..6412946 100644
--- a/rfb/HTTPServer.h
+++ b/rfb/HTTPServer.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -32,6 +32,7 @@
 #include <rfb/UpdateTracker.h>
 #include <rfb/Configuration.h>
 #include <network/Socket.h>
+#include <time.h>
 
 namespace rfb {
 
@@ -46,32 +47,27 @@
 
     virtual ~HTTPServer();
 
-    // -=- Client management
+    // SocketServer interface
 
-    // - Run a client connection on the supplied socket
+    // addSocket()
     //   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);
+    virtual void addSocket(network::Socket* sock, bool outgoing=false);
 
-    // -=- Event processing methods
+    // removeSocket()
+    //   Could clean up socket-specific resources here.
+    virtual void removeSocket(network::Socket* sock);
 
-    // - Process an input event on a particular Socket
+    // processSocketEvent()
     //   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 void processSocketEvent(network::Socket* sock);
 
-    virtual bool processSocketEvent(network::Socket* sock);
-
-    // - Check for socket timeouts
+    // 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().
 
@@ -92,13 +88,15 @@
     //   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);
+    virtual rdr::InStream* getFile(const char* name, const char** contentType,
+                                   int* contentLength, time_t* lastModified);
 
     // - 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.
+    //   or by passing zero in to determine whether a type is recognised or
+    //   not.
 
     static const char* guessContentType(const char* name, const char* defType);
 
diff --git a/rfb/HextileDecoder.cxx b/rfb/HextileDecoder.cxx
index 97c7ca7..e817c73 100644
--- a/rfb/HextileDecoder.cxx
+++ b/rfb/HextileDecoder.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/HextileDecoder.h b/rfb/HextileDecoder.h
index 718bd38..e7dd3d5 100644
--- a/rfb/HextileDecoder.h
+++ b/rfb/HextileDecoder.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/HextileEncoder.cxx b/rfb/HextileEncoder.cxx
index a96cf79..ba71d56 100644
--- a/rfb/HextileEncoder.cxx
+++ b/rfb/HextileEncoder.cxx
@@ -1,6 +1,6 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
  * Copyright (C) 2005 Constantin Kaplinsky.  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
diff --git a/rfb/HextileEncoder.h b/rfb/HextileEncoder.h
index f09ead8..c78107a 100644
--- a/rfb/HextileEncoder.h
+++ b/rfb/HextileEncoder.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/Hostname.h b/rfb/Hostname.h
index bdff474..ebdf816 100644
--- a/rfb/Hostname.h
+++ b/rfb/Hostname.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -19,11 +19,13 @@
 #ifndef __RFB_HOSTNAME_H__
 #define __RFB_HOSTNAME_H__
 
+#include <stdlib.h>
+#include <rdr/Exception.h>
 #include <rfb/util.h>
 
 namespace rfb {
 
-  void getHostAndPort(const char* hi, char** host, int* port, int basePort=5900) {
+  static void getHostAndPort(const char* hi, char** host, int* port, int basePort=5900) {
     CharArray portBuf;
     CharArray hostBuf;
     if (hi[0] == '[') {
diff --git a/rfb/ImageGetter.h b/rfb/ImageGetter.h
index b550a12..290249f 100644
--- a/rfb/ImageGetter.h
+++ b/rfb/ImageGetter.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/vncAuth.h b/rfb/InputHandler.h
similarity index 60%
copy from rfb/vncAuth.h
copy to rfb/InputHandler.h
index 18d87ad..b5e5e87 100644
--- a/rfb/vncAuth.h
+++ b/rfb/InputHandler.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -15,17 +15,26 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
  * USA.
  */
-#ifndef __RFB_VNCAUTH_H__
-#define __RFB_VNCAUTH_H__
+//
+// InputHandler - abstract interface for accepting keyboard &
+// pointer input and clipboard data.
+//
+
+#ifndef __RFB_INPUTHANDLER_H__
+#define __RFB_INPUTHANDLER_H__
 
 #include <rdr/types.h>
+#include <rfb/Rect.h>
 
 namespace rfb {
 
-  const int vncAuthChallengeSize = 16;
+  class InputHandler {
+  public:
+    virtual ~InputHandler() {}
+    virtual void keyEvent(rdr::U32 key, bool down) {}
+    virtual void pointerEvent(const Point& pos, int buttonMask) {}
+    virtual void clientCutText(const char* str, int len) {}
+  };
 
-  void vncAuthEncryptChallenge(rdr::U8* challenge, const char* passwd);
-  void vncAuthObfuscatePasswd(char* passwd);
-  void vncAuthUnobfuscatePasswd(char* passwd);
 }
 #endif
diff --git a/rfb/KeyRemapper.cxx b/rfb/KeyRemapper.cxx
new file mode 100644
index 0000000..05f0763
--- /dev/null
+++ b/rfb/KeyRemapper.cxx
@@ -0,0 +1,84 @@
+/* Copyright (C) 2002-2005 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/KeyRemapper.h>
+#include <rfb/Configuration.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+
+static LogWriter vlog("KeyRemapper");
+
+KeyRemapper KeyRemapper::defInstance;
+
+#ifdef __RFB_THREADING_IMPL
+static Mutex mappingLock;
+#endif
+
+void KeyRemapper::setMapping(const char* m) {
+#ifdef __RFB_THREADING_IMPL
+  Lock l(mappingLock);
+#endif
+  mapping.clear();
+  while (m[0]) {
+    int from, to;
+    char bidi;
+    const char* nextComma = strchr(m, ',');
+    if (!nextComma)
+      nextComma = m + strlen(m);
+    if (sscanf(m, "0x%x%c>0x%x", &from,
+               &bidi, &to) == 3) {
+      if (bidi != '-' && bidi != '<')
+        vlog.error("warning: unknown operation %c>, assuming ->", bidi);
+      mapping[from] = to;
+      if (bidi == '<')
+        mapping[to] = from;
+    } else {
+      vlog.error("warning: bad mapping %.*s", nextComma-m, m);
+    }
+    m = nextComma;
+    if (nextComma[0])
+      m++;
+  }
+}
+
+rdr::U32 KeyRemapper::remapKey(rdr::U32 key) const {
+#ifdef __RFB_THREADING_IMPL
+  Lock l(mappingLock);
+#endif
+  std::map<rdr::U32,rdr::U32>::const_iterator i = mapping.find(key);
+  if (i != mapping.end())
+    return i->second;
+  return key;
+}
+
+
+class KeyMapParameter : public StringParameter {
+public:
+  KeyMapParameter()
+    : StringParameter("RemapKeys", "Comma-separated list of incoming keysyms to remap.  Mappings are expressed as two hex values, prefixed by 0x, and separated by ->", "") {
+    setParam(value);
+  }
+  bool setParam(const char* v) {
+    KeyRemapper::defInstance.setMapping(v);
+    return StringParameter::setParam(v);
+  }
+} defaultParam;
+
+
diff --git a/rfb/vncAuth.h b/rfb/KeyRemapper.h
similarity index 63%
rename from rfb/vncAuth.h
rename to rfb/KeyRemapper.h
index 18d87ad..a4b7aa0 100644
--- a/rfb/vncAuth.h
+++ b/rfb/KeyRemapper.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -15,17 +15,25 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
  * USA.
  */
-#ifndef __RFB_VNCAUTH_H__
-#define __RFB_VNCAUTH_H__
 
+#ifndef __RFB_KEYREMAPPER_H__
+#define __RFB_KEYREMAPPER_H__
+
+#include <map>
 #include <rdr/types.h>
 
 namespace rfb {
 
-  const int vncAuthChallengeSize = 16;
+  class KeyRemapper {
+  public:
+    KeyRemapper(const char* m="") { setMapping(m); }
+    void setMapping(const char* m);
+    rdr::U32 remapKey(rdr::U32 key) const;
+    static KeyRemapper defInstance;
+  private:
+    std::map<rdr::U32,rdr::U32> mapping;
+  };
 
-  void vncAuthEncryptChallenge(rdr::U8* challenge, const char* passwd);
-  void vncAuthObfuscatePasswd(char* passwd);
-  void vncAuthUnobfuscatePasswd(char* passwd);
-}
-#endif
+};
+
+#endif // __RFB_KEYREMAPPER_H__
diff --git a/rfb/LogWriter.cxx b/rfb/LogWriter.cxx
index 6f267f1..c6461d1 100644
--- a/rfb/LogWriter.cxx
+++ b/rfb/LogWriter.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -53,13 +53,13 @@
 LogWriter::listLogWriters(int width) {
   // *** make this respect width...
   LogWriter* current = log_writers;
-  printf("  ");
+  fprintf(stderr, "  ");
   while (current) {
-    printf("%s", current->m_name);
+    fprintf(stderr, "%s", current->m_name);
     current = current->m_next;
-    if (current) printf(", ");
+    if (current) fprintf(stderr, ", ");
   }
-  printf("\n");
+  fprintf(stderr, "\n");
 }
 
 LogWriter* LogWriter::log_writers;
diff --git a/rfb/LogWriter.h b/rfb/LogWriter.h
index 58e81f2..124c58e 100644
--- a/rfb/LogWriter.h
+++ b/rfb/LogWriter.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/Logger.cxx b/rfb/Logger.cxx
index 84b8f55..8b3c6ee 100644
--- a/rfb/Logger.cxx
+++ b/rfb/Logger.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/Logger.h b/rfb/Logger.h
index 4233964..e53764b 100644
--- a/rfb/Logger.h
+++ b/rfb/Logger.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/Logger_file.cxx b/rfb/Logger_file.cxx
index ac249b3..8a109e4 100644
--- a/rfb/Logger_file.cxx
+++ b/rfb/Logger_file.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -53,6 +53,10 @@
 #endif
   if (!m_file) {
     if (!m_filename) return;
+    CharArray bakFilename(strlen(m_filename) + 1 + 4);
+    sprintf(bakFilename.buf, "%s.bak", m_filename);
+    remove(bakFilename.buf);
+    rename(m_filename, bakFilename.buf);
     m_file = fopen(m_filename, "w+");
     if (!m_file) return;
   }
diff --git a/rfb/Logger_file.h b/rfb/Logger_file.h
index 30c3f40..5e0c917 100644
--- a/rfb/Logger_file.h
+++ b/rfb/Logger_file.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/Logger_stdio.cxx b/rfb/Logger_stdio.cxx
index ac9556e..581dcd5 100644
--- a/rfb/Logger_stdio.cxx
+++ b/rfb/Logger_stdio.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/Logger_stdio.h b/rfb/Logger_stdio.h
index 68f795f..a1d17a0 100644
--- a/rfb/Logger_stdio.h
+++ b/rfb/Logger_stdio.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/Makefile.in b/rfb/Makefile.in
index f2976c0..3f501a9 100644
--- a/rfb/Makefile.in
+++ b/rfb/Makefile.in
@@ -21,10 +21,12 @@
   HTTPServer.cxx \
   HextileDecoder.cxx \
   HextileEncoder.cxx \
+  KeyRemapper.cxx \
   LogWriter.cxx \
   Logger.cxx \
   Logger_file.cxx \
   Logger_stdio.cxx \
+  Password.cxx \
   PixelBuffer.cxx \
   PixelFormat.cxx \
   RREEncoder.cxx \
@@ -41,6 +43,7 @@
   ServerCore.cxx \
   SSecurityFactoryStandard.cxx \
   SSecurityVncAuth.cxx \
+  Timer.cxx \
   TightDecoder.cxx \
   TightEncoder.cxx \
   TightPalette.cxx \
@@ -53,8 +56,7 @@
   ZRLEDecoder.cxx \
   encodings.cxx \
   secTypes.cxx \
-  util.cxx \
-  vncAuth.cxx
+  util.cxx
 
 SRCS = d3des.c $(CXXSRCS)
 
diff --git a/rfb/Password.cxx b/rfb/Password.cxx
new file mode 100644
index 0000000..9127862
--- /dev/null
+++ b/rfb/Password.cxx
@@ -0,0 +1,77 @@
+/* Copyright (C) 2002-2005 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.
+ */
+
+//
+// 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 <rdr/types.h>
+#include <rdr/Exception.h>
+#include <rfb/Password.h>
+
+using namespace rfb;
+
+static unsigned char d3desObfuscationKey[] = {23,82,107,6,35,78,88,7};
+
+
+PlainPasswd::PlainPasswd() {}
+
+PlainPasswd::PlainPasswd(char* pwd) : CharArray(pwd) {
+}
+
+PlainPasswd::PlainPasswd(const ObfuscatedPasswd& obfPwd) : CharArray(9) {
+  if (obfPwd.length < 8)
+    throw rdr::Exception("bad obfuscated password length");
+  deskey(d3desObfuscationKey, DE1);
+  des((rdr::U8*)obfPwd.buf, (rdr::U8*)buf);
+  buf[8] = 0;
+}
+
+PlainPasswd::~PlainPasswd() {
+  replaceBuf(0);
+}
+
+void PlainPasswd::replaceBuf(char* b) {
+  if (buf)
+    memset(buf, 0, strlen(buf));
+  CharArray::replaceBuf(b);
+}
+
+
+ObfuscatedPasswd::ObfuscatedPasswd() : length(0) {
+}
+
+ObfuscatedPasswd::ObfuscatedPasswd(int len) : CharArray(len), length(len) {
+}
+
+ObfuscatedPasswd::ObfuscatedPasswd(const PlainPasswd& plainPwd) : CharArray(8), length(8) {
+  int l = strlen(plainPwd.buf), i;
+  for (i=0; i<8; i++)
+    buf[i] = i<l ? plainPwd.buf[i] : 0;
+  deskey(d3desObfuscationKey, EN0);
+  des((rdr::U8*)buf, (rdr::U8*)buf);
+}
+
+ObfuscatedPasswd::~ObfuscatedPasswd() {
+  if (buf)
+    memset(buf, 0, length);
+}
diff --git a/rfb/Password.h b/rfb/Password.h
new file mode 100644
index 0000000..ab26903
--- /dev/null
+++ b/rfb/Password.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 2002-2005 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_PASSWORD_H__
+#define __RFB_PASSWORD_H__
+
+#include <rfb/util.h>
+
+namespace rfb {
+
+  class ObfuscatedPasswd;
+
+  class PlainPasswd : public CharArray {
+  public:
+    PlainPasswd();
+    PlainPasswd(char* pwd);
+    PlainPasswd(const ObfuscatedPasswd& obfPwd);
+    ~PlainPasswd();
+    void replaceBuf(char* b);
+  };
+
+  class ObfuscatedPasswd : public CharArray {
+  public:
+    ObfuscatedPasswd();
+    ObfuscatedPasswd(int l);
+    ObfuscatedPasswd(const PlainPasswd& plainPwd);
+    ~ObfuscatedPasswd();
+    int length;
+  };
+
+}
+#endif
diff --git a/rfb/Pixel.h b/rfb/Pixel.h
index 2b1aaf0..4e9d164 100644
--- a/rfb/Pixel.h
+++ b/rfb/Pixel.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/PixelBuffer.cxx b/rfb/PixelBuffer.cxx
index fcad227..d093426 100644
--- a/rfb/PixelBuffer.cxx
+++ b/rfb/PixelBuffer.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/PixelBuffer.h b/rfb/PixelBuffer.h
index 2ba105a..4a13923 100644
--- a/rfb/PixelBuffer.h
+++ b/rfb/PixelBuffer.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/PixelFormat.cxx b/rfb/PixelFormat.cxx
index 8809fb5..74b6837 100644
--- a/rfb/PixelFormat.cxx
+++ b/rfb/PixelFormat.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/PixelFormat.h b/rfb/PixelFormat.h
index 0f2edd2..111c38c 100644
--- a/rfb/PixelFormat.h
+++ b/rfb/PixelFormat.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/RREDecoder.cxx b/rfb/RREDecoder.cxx
index c613dbb..da56ee7 100644
--- a/rfb/RREDecoder.cxx
+++ b/rfb/RREDecoder.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/RREDecoder.h b/rfb/RREDecoder.h
index 75a5e85..2309f75 100644
--- a/rfb/RREDecoder.h
+++ b/rfb/RREDecoder.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/RREEncoder.cxx b/rfb/RREEncoder.cxx
index 612a869..b000e9d 100644
--- a/rfb/RREEncoder.cxx
+++ b/rfb/RREEncoder.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/RREEncoder.h b/rfb/RREEncoder.h
index 40b203e..1281410 100644
--- a/rfb/RREEncoder.h
+++ b/rfb/RREEncoder.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/RawDecoder.cxx b/rfb/RawDecoder.cxx
index 5a5d62b..57cb37b 100644
--- a/rfb/RawDecoder.cxx
+++ b/rfb/RawDecoder.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/RawDecoder.h b/rfb/RawDecoder.h
index b3dd9b7..9fdbb22 100644
--- a/rfb/RawDecoder.h
+++ b/rfb/RawDecoder.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/RawEncoder.cxx b/rfb/RawEncoder.cxx
index d758ec6..a2545b6 100644
--- a/rfb/RawEncoder.cxx
+++ b/rfb/RawEncoder.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/RawEncoder.h b/rfb/RawEncoder.h
index c8b6a62..1b9ad92 100644
--- a/rfb/RawEncoder.h
+++ b/rfb/RawEncoder.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/Rect.h b/rfb/Rect.h
index 1f88daf..52e92b5 100644
--- a/rfb/Rect.h
+++ b/rfb/Rect.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -21,11 +21,16 @@
 #ifndef __RFB_RECT_INCLUDED__
 #define __RFB_RECT_INCLUDED__
 
-#ifndef vncmin
-#define vncmin(a,b)            (((a) < (b)) ? (a) : (b))
+// Some platforms (e.g. Windows) include max() and min() macros in their
+// standard headers, but they are also standard C++ template functions, so some
+// C++ headers will undefine them.  So we steer clear of the names min and max
+// and define __rfbmin and __rfbmax instead.
+
+#ifndef __rfbmax
+#define __rfbmax(a,b) (((a) > (b)) ? (a) : (b))
 #endif
-#ifndef vncmax
-#define vncmax(a,b)            (((a) > (b)) ? (a) : (b))
+#ifndef __rfbmin
+#define __rfbmin(a,b) (((a) < (b)) ? (a) : (b))
 #endif
 
 namespace rfb {
@@ -69,20 +74,20 @@
     }
     inline Rect intersect(const Rect &r) const {
       Rect result;
-      result.tl.x = vncmax(tl.x, r.tl.x);
-      result.tl.y = vncmax(tl.y, r.tl.y);
-      result.br.x = vncmax(vncmin(br.x, r.br.x), result.tl.x);
-      result.br.y = vncmax(vncmin(br.y, r.br.y), result.tl.y);
+      result.tl.x = __rfbmax(tl.x, r.tl.x);
+      result.tl.y = __rfbmax(tl.y, r.tl.y);
+      result.br.x = __rfbmax(__rfbmin(br.x, r.br.x), result.tl.x);
+      result.br.y = __rfbmax(__rfbmin(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 = vncmin(tl.x, r.tl.x);
-      result.tl.y = vncmin(tl.y, r.tl.y);
-      result.br.x = vncmax(br.x, r.br.x);
-      result.br.y = vncmax(br.y, r.br.y);
+      result.tl.x = __rfbmin(tl.x, r.tl.x);
+      result.tl.y = __rfbmin(tl.y, r.tl.y);
+      result.br.x = __rfbmax(br.x, r.br.x);
+      result.br.y = __rfbmax(br.y, r.br.y);
       return result;
     }
     inline Rect translate(const Point &p) const {
diff --git a/rfb/Region.cxx b/rfb/Region.cxx
index bbcc892..7965a6c 100644
--- a/rfb/Region.cxx
+++ b/rfb/Region.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/Region.h b/rfb/Region.h
index 8fb9889..9337556 100644
--- a/rfb/Region.h
+++ b/rfb/Region.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/SConnection.cxx b/rfb/SConnection.cxx
index e969ed8..f8a3f36 100644
--- a/rfb/SConnection.cxx
+++ b/rfb/SConnection.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -21,7 +21,6 @@
 #include <rfb/secTypes.h>
 #include <rfb/SMsgReaderV3.h>
 #include <rfb/SMsgWriterV3.h>
-#include <rfb/SSecurity.h>
 #include <rfb/SConnection.h>
 #include <rfb/ServerCore.h>
 
@@ -41,10 +40,11 @@
 const SConnection::AccessRights SConnection::AccessFull       = 0xffff;
 
 
-SConnection::SConnection()
+SConnection::SConnection(SSecurityFactory* secFact, bool reverseConnection_)
   : readyForSetColourMapEntries(false),
     is(0), os(0), reader_(0), writer_(0),
-    nSecTypes(0), security(0), state_(RFBSTATE_UNINITIALISED)
+    security(0), securityFactory(secFact), state_(RFBSTATE_UNINITIALISED),
+    reverseConnection(reverseConnection_)
 {
   defaultMajorVersion = 3;
   defaultMinorVersion = 8;
@@ -74,15 +74,6 @@
   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);
@@ -144,37 +135,40 @@
 
   versionReceived();
 
+  std::list<rdr::U8> secTypes;
+  std::list<rdr::U8>::iterator i;
+  securityFactory->getSecTypes(&secTypes, reverseConnection);
+
   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;
+    for (i=secTypes.begin(); i!=secTypes.end(); i++) {
+      if (*i == secTypeNone || *i == secTypeVncAuth) break;
     }
-    if (i == nSecTypes) {
+    if (i == secTypes.end()) {
       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();
+    os->writeU32(*i);
+    if (*i == secTypeNone) os->flush();
     state_ = RFBSTATE_SECURITY;
-    security = getSSecurity(secTypes[i]);
+    security = securityFactory->getSSecurity(*i, reverseConnection);
     processSecurityMsg();
     return;
   }
 
   // list supported security types for >=3.7 clients
 
-  if (nSecTypes == 0)
+  if (secTypes.empty())
     throwConnFailedException("No supported security types");
 
-  os->writeU8(nSecTypes);
-  os->writeBytes(secTypes, nSecTypes);
+  os->writeU8(secTypes.size());
+  for (i=secTypes.begin(); i!=secTypes.end(); i++)
+    os->writeU8(*i);
   os->flush();
   state_ = RFBSTATE_SECURITY_TYPE;
 }
@@ -186,40 +180,33 @@
   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;
+
+  try {
+    state_ = RFBSTATE_SECURITY;
+    security = securityFactory->getSSecurity(secType, reverseConnection);
+  } catch (rdr::Exception& e) {
+    throwConnFailedException(e.str());
   }
-  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) {
+  try {
+    bool done = security->processMsg(this);
+    if (done) {
+      state_ = RFBSTATE_QUERYING;
       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();
+  } catch (AuthFailureException& e) {
+    vlog.error("AuthFailureException: %s", e.str());
+    os->writeU32(secResultFailed);
+    if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
+      os->writeString(e.str());
+    os->flush();
+    throw;
   }
 }
 
@@ -264,10 +251,6 @@
 {
 }
 
-void SConnection::authFailure()
-{
-}
-
 void SConnection::queryConnection(const char* userName)
 {
   approveConnection(true);
@@ -298,7 +281,6 @@
     authSuccess();
   } else {
     state_ = RFBSTATE_INVALID;
-    authFailure();
     throw AuthFailureException(reason);
   }
 }
diff --git a/rfb/SConnection.h b/rfb/SConnection.h
index 19453c6..6b943f5 100644
--- a/rfb/SConnection.h
+++ b/rfb/SConnection.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -26,6 +26,7 @@
 #include <rdr/InStream.h>
 #include <rdr/OutStream.h>
 #include <rfb/SMsgHandler.h>
+#include <rfb/SSecurity.h>
 
 namespace rfb {
 
@@ -36,7 +37,7 @@
   class SConnection : public SMsgHandler {
   public:
 
-    SConnection();
+    SConnection(SSecurityFactory* sf, bool reverseConnection_);
     virtual ~SConnection();
 
     // Methods to initialise the connection
@@ -49,10 +50,6 @@
     // (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.
@@ -78,19 +75,9 @@
     // 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
@@ -178,7 +165,8 @@
 
     stateEnum state() { return state_; }
 
-    // ssecurity() returns a pointer to this connection's SSecurity object, if any
+    // ssecurity() returns a pointer to this connection's SSecurity object, if
+    // any
     const SSecurity* ssecurity() const { return security; }
 
   protected:
@@ -186,7 +174,6 @@
 
     bool readyForSetColourMapEntries;
 
-  private:
     void processVersionMsg();
     void processSecurityTypeMsg();
     void processSecurityMsg();
@@ -197,11 +184,10 @@
     rdr::OutStream* os;
     SMsgReader* reader_;
     SMsgWriter* writer_;
-    enum { maxSecTypes = 8 };
-    int nSecTypes;
-    rdr::U8 secTypes[maxSecTypes];
     SSecurity* security;
+    SSecurityFactory* securityFactory;
     stateEnum state_;
+    bool reverseConnection;
   };
 }
 #endif
diff --git a/rfb/SDesktop.h b/rfb/SDesktop.h
index eb17a52..7b054e3 100644
--- a/rfb/SDesktop.h
+++ b/rfb/SDesktop.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -39,13 +39,14 @@
 
 #include <rfb/PixelBuffer.h>
 #include <rfb/VNCServer.h>
+#include <rfb/InputHandler.h>
 #include <rfb/Exception.h>
 
 namespace rfb {
 
   class VNCServer;
 
-  class SDesktop {
+  class SDesktop : public InputHandler {
   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
@@ -62,13 +63,6 @@
 
     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
@@ -81,6 +75,10 @@
 
     virtual Point getFbSize() = 0;
 
+    // InputHandler interface
+    // pointerEvent(), keyEvent() and clientCutText() are called in response to
+    // the relevant RFB protocol messages from clients.
+    // See InputHandler for method signatures.
   protected:
     virtual ~SDesktop() {}
   };
diff --git a/rfb/SMsgHandler.cxx b/rfb/SMsgHandler.cxx
index d6a139c..ccc97ad 100644
--- a/rfb/SMsgHandler.cxx
+++ b/rfb/SMsgHandler.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -47,18 +47,6 @@
 {
 }
 
-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
index 148403f..cf3377d 100644
--- a/rfb/SMsgHandler.h
+++ b/rfb/SMsgHandler.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -25,13 +25,13 @@
 #include <rdr/types.h>
 #include <rfb/PixelFormat.h>
 #include <rfb/ConnParams.h>
-#include <rfb/Rect.h>
+#include <rfb/InputHandler.h>
 
 namespace rdr { class InStream; }
 
 namespace rfb {
 
-  class SMsgHandler {
+  class SMsgHandler : public InputHandler {
   public:
     SMsgHandler();
     virtual ~SMsgHandler();
@@ -46,9 +46,9 @@
     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);
+
+    // InputHandler interface
+    // The InputHandler methods will be called for the corresponding messages.
 
     // supportsLocalCursor() is called whenever the status of
     // cp.supportsLocalCursor has changed.  At the moment this happens on a
diff --git a/rfb/SMsgReader.cxx b/rfb/SMsgReader.cxx
index 2939aa1..f89e0f4 100644
--- a/rfb/SMsgReader.cxx
+++ b/rfb/SMsgReader.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -21,9 +21,12 @@
 #include <rfb/util.h>
 #include <rfb/SMsgHandler.h>
 #include <rfb/SMsgReader.h>
+#include <rfb/Configuration.h>
 
 using namespace rfb;
 
+static IntParameter maxCutText("MaxCutText", "Maximum permitted length of an incoming clipboard update", 256*1024);
+
 SMsgReader::SMsgReader(SMsgHandler* handler_, rdr::InStream* is_)
   : handler(handler_), is(is_)
 {
@@ -33,16 +36,11 @@
 {
 }
 
-void SMsgReader::endMsg()
-{
-}
-
 void SMsgReader::readSetPixelFormat()
 {
   is->skip(3);
   PixelFormat pf;
   pf.read(is);
-  endMsg();
   handler->setPixelFormat(pf);
 }
 
@@ -50,12 +48,10 @@
 {
   is->skip(1);
   int nEncodings = is->readU16();
-  rdr::U32* encodings = new rdr::U32[nEncodings];
+  rdr::U32Array encodings(nEncodings);
   for (int i = 0; i < nEncodings; i++)
-    encodings[i] = is->readU32();
-  endMsg();
-  handler->setEncodings(nEncodings, encodings);
-  delete [] encodings;
+    encodings.buf[i] = is->readU32();
+  handler->setEncodings(nEncodings, encodings.buf);
 }
 
 void SMsgReader::readFramebufferUpdateRequest()
@@ -65,7 +61,6 @@
   int y = is->readU16();
   int w = is->readU16();
   int h = is->readU16();
-  endMsg();
   handler->framebufferUpdateRequest(Rect(x, y, x+w, y+h), inc);
 }
 
@@ -74,7 +69,6 @@
   bool down = is->readU8();
   is->skip(2);
   rdr::U32 key = is->readU32();
-  endMsg();
   handler->keyEvent(key, down);
 }
 
@@ -83,8 +77,7 @@
   int mask = is->readU8();
   int x = is->readU16();
   int y = is->readU16();
-  endMsg();
-  handler->pointerEvent(x, y, mask);
+  handler->pointerEvent(Point(x, y), mask);
 }
 
 
@@ -92,7 +85,7 @@
 {
   is->skip(3);
   int len = is->readU32();
-  if (len > 256*1024) {
+  if (len > maxCutText) {
     is->skip(len);
     fprintf(stderr,"cut text too long (%d bytes) - ignoring\n",len);
     return;
@@ -100,6 +93,5 @@
   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
index 4d26938..e6e4044 100644
--- a/rfb/SMsgReader.h
+++ b/rfb/SMsgReader.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -46,7 +46,6 @@
     virtual void readKeyEvent();
     virtual void readPointerEvent();
     virtual void readClientCutText();
-    virtual void endMsg();
 
     SMsgReader(SMsgHandler* handler, rdr::InStream* is);
 
diff --git a/rfb/SMsgReaderV3.cxx b/rfb/SMsgReaderV3.cxx
index 0ce1bca..be01b5d 100644
--- a/rfb/SMsgReaderV3.cxx
+++ b/rfb/SMsgReaderV3.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -36,7 +36,6 @@
 void SMsgReaderV3::readClientInit()
 {
   bool shared = is->readU8();
-  endMsg();
   handler->clientInit(shared);
 }
 
diff --git a/rfb/SMsgReaderV3.h b/rfb/SMsgReaderV3.h
index 28cc7a6..c6b7bf4 100644
--- a/rfb/SMsgReaderV3.h
+++ b/rfb/SMsgReaderV3.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/SMsgWriter.cxx b/rfb/SMsgWriter.cxx
index 067fb11..085dfc1 100644
--- a/rfb/SMsgWriter.cxx
+++ b/rfb/SMsgWriter.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -176,11 +176,6 @@
   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);
diff --git a/rfb/SMsgWriter.h b/rfb/SMsgWriter.h
index 6dc272c..ed8ad0e 100644
--- a/rfb/SMsgWriter.h
+++ b/rfb/SMsgWriter.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -80,8 +80,8 @@
     // 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;
+    virtual void writeSetCursor(int width, int height, const Point& hotspot,
+                                void* data, void* mask)=0;
     virtual void writeSetXCursor(int width, int height, int hotspotX,
                                 int hotspotY, void* data, void* mask)=0;
 
@@ -127,9 +127,6 @@
     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);
diff --git a/rfb/SMsgWriterV3.cxx b/rfb/SMsgWriterV3.cxx
index e91e514..a85f85e 100644
--- a/rfb/SMsgWriterV3.cxx
+++ b/rfb/SMsgWriterV3.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -69,14 +69,14 @@
   wsccb = cb;
 }
 
-void SMsgWriterV3::writeSetCursor(int width, int height, int hotspotX,
-                                  int hotspotY, void* data, void* mask)
+void SMsgWriterV3::writeSetCursor(int width, int height, const Point& hotspot,
+                                  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->writeS16(hotspot.x);
+  os->writeS16(hotspot.y);
   os->writeU16(width);
   os->writeU16(height);
   os->writeU32(pseudoEncodingCursor);
@@ -138,6 +138,8 @@
 void SMsgWriterV3::writeFramebufferUpdateEnd()
 {
   if (needSetDesktopSize) {
+    if (!cp->supportsDesktopResize)
+      throw Exception("Client does not support desktop resize");
     if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
       throw Exception("SMsgWriterV3 setDesktopSize: nRects out of sync");
     os->writeS16(0);
@@ -193,9 +195,3 @@
     rectsSent[currentEncoding]++;
   }
 }
-
-void SMsgWriterV3::setOutStream(rdr::OutStream* os_)
-{
-  SMsgWriter::setOutStream(os_);
-  realOS = os;
-}
diff --git a/rfb/SMsgWriterV3.h b/rfb/SMsgWriterV3.h
index fbd07d6..501fa48 100644
--- a/rfb/SMsgWriterV3.h
+++ b/rfb/SMsgWriterV3.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -33,8 +33,8 @@
     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 writeSetCursor(int width, int height, const Point& hotspot,
+                                void* data, void* mask);
     virtual void writeSetXCursor(int width, int height, int hotspotX,
 				 int hotspotY, void* data, void* mask);
     virtual void writeFramebufferUpdateStart(int nRects);
@@ -44,8 +44,6 @@
     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;
diff --git a/rfb/SSecurity.h b/rfb/SSecurity.h
index 2ca5344..108985b 100644
--- a/rfb/SSecurity.h
+++ b/rfb/SSecurity.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -18,10 +18,16 @@
 //
 // 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.
+// 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.  It
+// should return false when it needs more data, or true when the connection has
+// been successfully authenticated.  In the event of authentication failure an
+// AuthFailureException should be thrown - this will result in a "failed"
+// security result being sent to the client with the str() from the exception
+// being sent as the reason.  Any other type of failure should be indicated by
+// some other kind of exception which will cause the connection to be aborted.
 //
 // processMsg() must never block (or at least must never block until the client
 // has been authenticated) - this is to prevent denial of service attacks.
@@ -33,13 +39,13 @@
 // 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 <rdr/types.h>
 #include <rfb/util.h>
+#include <list>
 
 namespace rfb {
 
@@ -48,7 +54,7 @@
   class SSecurity {
   public:
     virtual ~SSecurity() {}
-    virtual bool processMsg(SConnection* sc, bool* done)=0;
+    virtual bool processMsg(SConnection* sc)=0;
     virtual void destroy() { delete this; }
     virtual int getType() const = 0;
 
@@ -57,20 +63,21 @@
     // 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.
+  // getSecTypes returns a list of the security types that are both configured
+  // and actually supported.  Which configuration is considered depends on the
+  // reverseConnection parameter.
   class SSecurityFactory {
   public:
     virtual ~SSecurityFactory() {}
-    virtual SSecurity* getSSecurity(int secType, bool noAuth=false)=0;
+    virtual SSecurity* getSSecurity(rdr::U8 secType, bool noAuth=false)=0;
+    virtual void getSecTypes(std::list<rdr::U8>* secTypes,
+                             bool reverseConnection) = 0;
   };
 
 }
diff --git a/rfb/SSecurityFactoryStandard.cxx b/rfb/SSecurityFactoryStandard.cxx
index e3a40aa..a072698 100644
--- a/rfb/SSecurityFactoryStandard.cxx
+++ b/rfb/SSecurityFactoryStandard.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -25,77 +25,104 @@
 #include <rfb/LogWriter.h>
 #include <rfb/Exception.h>
 #include <rfb/SSecurityFactoryStandard.h>
+#include <rfb/Password.h>
 
 using namespace rfb;
 
 static LogWriter vlog("SSecurityFactoryStandard");
 
-VncAuthPasswdParameter* SSecurityFactoryStandard::vncAuthPasswd = 0;
+StringParameter SSecurityFactoryStandard::sec_types
+("SecurityTypes",
+ "Specify which security scheme to use for incoming connections (None, VncAuth)",
+ "VncAuth");
+
+StringParameter SSecurityFactoryStandard::rev_sec_types
+("ReverseSecurityTypes",
+ "Specify encryption scheme to use for reverse connections (None)",
+ "None");
 
 
-SSecurity* SSecurityFactoryStandard::getSSecurity(int secType, bool noAuth) {
+StringParameter SSecurityFactoryStandard::vncAuthPasswdFile
+("PasswordFile", "Password file for VNC authentication", "");
+VncAuthPasswdParameter SSecurityFactoryStandard::vncAuthPasswd
+("Password", "Obfuscated binary encoding of the password which clients must supply to "
+ "access the server", &SSecurityFactoryStandard::vncAuthPasswdFile);
+
+
+SSecurity* SSecurityFactoryStandard::getSSecurity(rdr::U8 secType, bool reverseConnection) {
   switch (secType) {
-  case secTypeNone:    return new SSecurityNone();
+  case secTypeNone: return new SSecurityNone();
   case secTypeVncAuth:
-    if (!vncAuthPasswd)
-      throw rdr::Exception("No VncAuthPasswdParameter defined!");
-    return new SSecurityVncAuth(vncAuthPasswd);
+    return new SSecurityVncAuth(&vncAuthPasswd);
   default:
-    throw Exception("Unsupported secType?");
+    throw Exception("Security type not supported");
   }
 }
 
-VncAuthPasswdParameter::VncAuthPasswdParameter() {
-  if (SSecurityFactoryStandard::vncAuthPasswd)
-    throw rdr::Exception("duplicate VncAuthPasswdParameter!");
-  SSecurityFactoryStandard::vncAuthPasswd = this;
+void SSecurityFactoryStandard::getSecTypes(std::list<rdr::U8>* secTypes, bool reverseConnection) {
+  CharArray secTypesStr;
+  if (reverseConnection)
+    secTypesStr.buf = rev_sec_types.getData();
+  else
+    secTypesStr.buf = sec_types.getData();
+  std::list<int> configured = parseSecTypes(secTypesStr.buf);
+  std::list<int>::iterator i;
+  for (i=configured.begin(); i!=configured.end(); i++) {
+    if (isSecTypeSupported(*i))
+      secTypes->push_back(*i);
+  }
+}
+
+bool SSecurityFactoryStandard::isSecTypeSupported(rdr::U8 secType) {
+  switch (secType) {
+  case secTypeNone:
+  case secTypeVncAuth:
+    return true;
+  default:
+    return false;
+  }
 }
 
 
-VncAuthPasswdConfigParameter::VncAuthPasswdConfigParameter()
-: passwdParam("Password",
-   "Obfuscated binary encoding of the password which clients must supply to "
-   "access the server", 0, 0) {
+VncAuthPasswdParameter::VncAuthPasswdParameter(const char* name,
+                                               const char* desc,
+                                               StringParameter* passwdFile_)
+: BinaryParameter(name, desc, 0, 0), passwdFile(passwdFile_) {
 }
 
-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);
+char* VncAuthPasswdParameter::getVncAuthPasswd() {
+  ObfuscatedPasswd obfuscated;
+  getData((void**)&obfuscated.buf, &obfuscated.length);
+
+  if (obfuscated.length == 0) {
+    if (passwdFile) {
+      CharArray fname(passwdFile->getData());
+      if (!fname.buf[0]) {
+        vlog.info("neither %s nor %s params set", getName(), passwdFile->getName());
+        return 0;
+      }
+
+      FILE* fp = fopen(fname.buf, "r");
+      if (!fp) {
+        vlog.error("opening password file '%s' failed",fname.buf);
+        return 0;
+      }
+
+      vlog.debug("reading password file");
+      obfuscated.buf = new char[128];
+      obfuscated.length = fread(obfuscated.buf, 1, 128, fp);
+      fclose(fp);
+    } else {
+      vlog.info("%s parameter not set", getName());
+    }
+  }
+
+  try {
+    PlainPasswd password(obfuscated);
     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");
+  } catch (...) {
     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
index 5fced04..165881e 100644
--- a/rfb/SSecurityFactoryStandard.h
+++ b/rfb/SSecurityFactoryStandard.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -44,31 +44,24 @@
 
 namespace rfb {
 
-  class VncAuthPasswdParameter : public VncAuthPasswdGetter {
+  class VncAuthPasswdParameter : public VncAuthPasswdGetter, BinaryParameter {
   public:
-    VncAuthPasswdParameter();
-    virtual ~VncAuthPasswdParameter() {}
+    VncAuthPasswdParameter(const char* name, const char* desc, StringParameter* passwdFile_);
+    virtual char* getVncAuthPasswd();
+  protected:
+    StringParameter* passwdFile;
   };
 
   class SSecurityFactoryStandard : public SSecurityFactory {
   public:
-    virtual SSecurity* getSSecurity(int secType, bool noAuth);
-    static VncAuthPasswdParameter* vncAuthPasswd;
-  };
-
-  class VncAuthPasswdConfigParameter : public VncAuthPasswdParameter {
-  public:
-    VncAuthPasswdConfigParameter();
-    virtual char* getVncAuthPasswd();
+    virtual SSecurity* getSSecurity(rdr::U8 secType, bool reverse);
+    virtual void getSecTypes(std::list<rdr::U8>* secTypes, bool reverse);
+    static StringParameter sec_types;
+    static StringParameter rev_sec_types;
+    static StringParameter vncAuthPasswdFile;
+    static VncAuthPasswdParameter vncAuthPasswd;
   protected:
-    BinaryParameter passwdParam;
-  };
-
-  class VncAuthPasswdFileParameter : public VncAuthPasswdParameter {
-  public:
-    VncAuthPasswdFileParameter();
-    virtual char* getVncAuthPasswd();
-    StringParameter param;
+    virtual bool isSecTypeSupported(rdr::U8 secType);
   };
 
 }
diff --git a/rfb/SSecurityNone.h b/rfb/SSecurityNone.h
index 09b2db4..5c19f29 100644
--- a/rfb/SSecurityNone.h
+++ b/rfb/SSecurityNone.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -28,9 +28,7 @@
 
   class SSecurityNone : public SSecurity {
   public:
-    virtual bool processMsg(SConnection* sc, bool* done) {
-      *done = true; return true;
-    }
+    virtual bool processMsg(SConnection* sc) { return true; }
     virtual int getType() const {return secTypeNone;}
     virtual const char* getUserName() const {return 0;}
   };
diff --git a/rfb/SSecurityVncAuth.cxx b/rfb/SSecurityVncAuth.cxx
index 532d1a6..29a3b96 100644
--- a/rfb/SSecurityVncAuth.cxx
+++ b/rfb/SSecurityVncAuth.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -18,20 +18,27 @@
 //
 // SSecurityVncAuth
 //
+// XXX not thread-safe, because d3des isn't - do we need to worry about this?
+//
 
 #include <rfb/SSecurityVncAuth.h>
 #include <rdr/RandomStream.h>
 #include <rfb/SConnection.h>
-#include <rfb/vncAuth.h>
+#include <rfb/Password.h>
 #include <rfb/Configuration.h>
 #include <rfb/LogWriter.h>
 #include <rfb/util.h>
+#include <rfb/Exception.h>
 #include <string.h>
 #include <stdio.h>
+extern "C" {
+#include <rfb/d3des.h>
+}
+
 
 using namespace rfb;
 
-static LogWriter vlog("VncAuth");
+static LogWriter vlog("SVncAuth");
 
 
 SSecurityVncAuth::SSecurityVncAuth(VncAuthPasswdGetter* pg_)
@@ -39,9 +46,8 @@
 {
 }
 
-bool SSecurityVncAuth::processMsg(SConnection* sc, bool* done)
+bool SSecurityVncAuth::processMsg(SConnection* sc)
 {
-  *done = false;
   rdr::InStream* is = sc->getInStream();
   rdr::OutStream* os = sc->getOutStream();
 
@@ -51,33 +57,31 @@
     os->writeBytes(challenge, vncAuthChallengeSize);
     os->flush();
     sentChallenge = true;
-    return true;
+    return false;
   }
 
-  if (responsePos >= vncAuthChallengeSize) return false;
-  while (is->checkNoWait(1) && responsePos < vncAuthChallengeSize) {
+  while (responsePos < vncAuthChallengeSize && is->checkNoWait(1))
     response[responsePos++] = is->readU8();
-  }
 
-  if (responsePos < vncAuthChallengeSize) return true;
+  if (responsePos < vncAuthChallengeSize) return false;
 
-  CharArray passwd(pg->getVncAuthPasswd());
+  PlainPasswd passwd(pg->getVncAuthPasswd());
 
-  // Beyond this point, there is no more VNCAuth protocol to perform.
-  *done = true;
+  if (!passwd.buf)
+    throw AuthFailureException("No password configured for VNC Auth");
 
-  if (!passwd.buf) {
-    failureMessage_.buf = strDup("No password configured for VNC Auth");
-    vlog.error(failureMessage_.buf);
-    return false;
-  }
+  // Calculate the expected response
+  rdr::U8 key[8];
+  int pwdLen = strlen(passwd.buf);
+  for (int i=0; i<8; i++)
+    key[i] = i<pwdLen ? passwd.buf[i] : 0;
+  deskey(key, EN0);
+  for (int j = 0; j < vncAuthChallengeSize; j += 8)
+    des(challenge+j, challenge+j);
 
-  vncAuthEncryptChallenge(challenge, passwd.buf);
-  memset(passwd.buf, 0, strlen(passwd.buf));
-
-  if (memcmp(challenge, response, vncAuthChallengeSize) != 0) {
-    return false;
-  }
+  // Check the actual response
+  if (memcmp(challenge, response, vncAuthChallengeSize) != 0)
+    throw AuthFailureException();
 
   return true;
 }
diff --git a/rfb/SSecurityVncAuth.h b/rfb/SSecurityVncAuth.h
index edbd720..1d0a82d 100644
--- a/rfb/SSecurityVncAuth.h
+++ b/rfb/SSecurityVncAuth.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -26,7 +26,7 @@
 
 #include <rfb/SSecurity.h>
 #include <rfb/secTypes.h>
-#include <rfb/vncAuth.h>
+#include <rdr/types.h>
 
 namespace rfb {
 
@@ -40,10 +40,11 @@
   class SSecurityVncAuth : public SSecurity {
   public:
     SSecurityVncAuth(VncAuthPasswdGetter* pg);
-    virtual bool processMsg(SConnection* sc, bool* done);
+    virtual bool processMsg(SConnection* sc);
     virtual int getType() const {return secTypeVncAuth;}
     virtual const char* getUserName() const {return 0;}
   private:
+    enum {vncAuthChallengeSize = 16};
     rdr::U8 challenge[vncAuthChallengeSize];
     rdr::U8 response[vncAuthChallengeSize];
     bool sentChallenge;
diff --git a/rfb/ServerCore.cxx b/rfb/ServerCore.cxx
index fe61ecb..750daae 100644
--- a/rfb/ServerCore.cxx
+++ b/rfb/ServerCore.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -24,38 +24,29 @@
 #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",
- 0);
-
+ "The number of seconds after which an idle VNC connection will be dropped "
+ "(zero means no timeout)",
+ 0, 0);
 rfb::IntParameter rfb::Server::maxDisconnectionTime
 ("MaxDisconnectionTime",
  "Terminate when no client has been connected for s seconds", 
- 0);
+ 0, 0);
 rfb::IntParameter rfb::Server::maxConnectionTime
 ("MaxConnectionTime",
  "Terminate when a client has been connected for s seconds", 
- 0);
+ 0, 0);
 rfb::IntParameter rfb::Server::maxIdleTime
 ("MaxIdleTime",
  "Terminate after s seconds of user inactivity", 
- 0);
+ 0, 0);
 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");
+ 20000, 0);
 rfb::BoolParameter rfb::Server::compareFB
 ("CompareFB",
  "Perform pixel comparison on framebuffer to reduce unnecessary updates",
@@ -101,8 +92,3 @@
 ("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
index b01fcfd..68d7b74 100644
--- a/rfb/ServerCore.h
+++ b/rfb/ServerCore.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -37,8 +37,6 @@
     static IntParameter maxConnectionTime;
     static IntParameter maxIdleTime;
     static IntParameter clientWaitTimeMillis;
-    static StringParameter sec_types;
-    static StringParameter rev_sec_types;
     static BoolParameter compareFB;
     static BoolParameter protocol3_3;
     static BoolParameter alwaysShared;
@@ -49,7 +47,6 @@
     static BoolParameter acceptCutText;
     static BoolParameter sendCutText;
     static BoolParameter queryConnect;
-    static IntParameter blacklistLevel;
 
   };
 
diff --git a/rfb/Threading.h b/rfb/Threading.h
index effc436..66b3aa0 100644
--- a/rfb/Threading.h
+++ b/rfb/Threading.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -25,7 +25,7 @@
 #define __RFB_THREADING_H__
 
 #ifdef WIN32
-#include <rfb/win32/Threading_win32.h>
+#include <rfb_win32/Threading.h>
 #endif
 
 #endif // __RFB_THREADING_H__
diff --git a/rfb/Timer.cxx b/rfb/Timer.cxx
new file mode 100644
index 0000000..59d250e
--- /dev/null
+++ b/rfb/Timer.cxx
@@ -0,0 +1,174 @@
+/* Copyright (C) 2002-2005 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.
+ */
+
+// -=- Timer.cxx
+
+#include <stdio.h>
+#ifdef WIN32
+#include <windows.h>
+#ifndef _WIN32_WCE
+#include <sys/timeb.h>
+#endif
+#endif
+#include <rfb/Timer.h>
+#include <rfb/util.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+
+#ifndef __NO_DEFINE_VLOG__
+static LogWriter vlog("Timer");
+#endif
+
+
+// Win32 does not provide gettimeofday, so we emulate it to simplify the
+// Timer code.
+
+#ifdef _WIN32
+static void gettimeofday(struct timeval* tv, void*)
+{
+  LARGE_INTEGER counts, countsPerSec;
+  static double usecPerCount = 0.0;
+
+  if (QueryPerformanceCounter(&counts)) {
+    if (usecPerCount == 0.0) {
+      QueryPerformanceFrequency(&countsPerSec);
+      usecPerCount = 1000000.0 / countsPerSec.QuadPart;
+    }
+
+    LONGLONG usecs = (LONGLONG)(counts.QuadPart * usecPerCount);
+    tv->tv_usec = (long)(usecs % 1000000);
+    tv->tv_sec = (long)(usecs / 1000000);
+
+  } else {
+#ifndef _WIN32_WCE
+    struct timeb tb;
+    ftime(&tb);
+    tv->tv_sec = tb.time;
+    tv->tv_usec = tb.millitm * 1000;
+#else
+    throw SystemException("QueryPerformanceCounter", GetLastError());
+#endif
+  }
+}
+#endif
+
+
+// Millisecond timeout processing helper functions
+
+inline static timeval addMillis(timeval inTime, int millis) {
+  int secs = millis / 1000;
+  millis = millis % 1000;
+  inTime.tv_sec += secs;
+  inTime.tv_usec += millis * 1000;
+  if (inTime.tv_usec >= 1000000) {
+    inTime.tv_sec++;
+    inTime.tv_usec -= 1000000;
+  }
+  return inTime;
+}
+
+inline static int diffTimeMillis(timeval later, timeval earlier) {
+  return ((later.tv_sec - earlier.tv_sec) * 1000) + ((later.tv_usec - earlier.tv_usec) / 1000);
+}
+
+std::list<Timer*> Timer::pending;
+
+int Timer::checkTimeouts() {
+  if (pending.empty())
+    return 0;
+  timeval now;
+  gettimeofday(&now, 0);
+  while (pending.front()->isBefore(now)) {
+    Timer* timer = pending.front();
+    pending.pop_front();
+    vlog.debug("handleTimeout(%p)", timer);
+    if (timer->cb->handleTimeout(timer)) {
+      timer->dueTime = addMillis(timer->dueTime, timer->timeoutMs);
+      if (timer->isBefore(now)) {
+        // Time has jumped forwards!
+	      vlog.info("time has moved forwards!");
+        timer->dueTime = addMillis(now, timer->timeoutMs);
+      }
+      insertTimer(timer);
+    } else if (pending.empty()) {
+      return 0;
+    }
+  }
+  return getNextTimeout();
+}
+
+int Timer::getNextTimeout() {
+  timeval now;
+  gettimeofday(&now, 0);
+  int toWait = __rfbmax(1, diffTimeMillis(pending.front()->dueTime, now));
+  if (toWait > pending.front()->timeoutMs) {
+    if (toWait - pending.front()->timeoutMs < 1000) {
+      vlog.info("gettimeofday is broken...");
+      return toWait;
+    }
+    // Time has jumped backwards!
+    vlog.info("time has moved backwards!");
+    pending.front()->dueTime = now;
+    toWait = 1;
+  }
+  return toWait;
+}
+
+void Timer::insertTimer(Timer* t) {
+  std::list<Timer*>::iterator i;
+  for (i=pending.begin(); i!=pending.end(); i++) {
+    if (t->isBefore((*i)->dueTime)) {
+      pending.insert(i, t);
+      return;
+    }
+  }
+  pending.push_back(t);
+}
+
+void Timer::start(int timeoutMs_) {
+  timeval now;
+  gettimeofday(&now, 0);
+  stop();
+  timeoutMs = timeoutMs_;
+  dueTime = addMillis(now, timeoutMs);
+  insertTimer(this);
+}
+
+void Timer::stop() {
+  pending.remove(this);
+}
+
+bool Timer::isStarted() {
+  std::list<Timer*>::iterator i;
+  for (i=pending.begin(); i!=pending.end(); i++) {
+    if (*i == this)
+      return true;
+  }
+  return false;
+}
+
+int Timer::getTimeoutMs() {
+  return timeoutMs;
+}
+
+bool Timer::isBefore(timeval other) {
+  return (dueTime.tv_sec < other.tv_sec) ||
+    ((dueTime.tv_sec == other.tv_sec) &&
+     (dueTime.tv_usec < other.tv_usec));
+}
diff --git a/rfb/Timer.h b/rfb/Timer.h
new file mode 100644
index 0000000..e295b82
--- /dev/null
+++ b/rfb/Timer.h
@@ -0,0 +1,102 @@
+/* Copyright (C) 2002-2005 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_TIMER_H__
+#define __RFB_TIMER_H__
+
+#include <list>
+#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <sys/time.h>
+#endif
+
+namespace rfb {
+
+  /* Timer
+
+     Cross-platform timeout handling.  The caller creates instances of Timer and passes a
+     Callback implementation to each.  The Callback will then be called with a pointer to
+     the Timer instance that timed-out when the timeout occurs.
+
+     The static methods of Timer are used by the main loop of the application both to
+     dispatch elapsed Timer callbacks and to determine how long to wait in select() for
+     the next timeout to occur.
+
+  */
+
+  struct Timer {
+
+    struct Callback {
+      // handleTimeout
+      //   Passed a pointer to the Timer that has timed out.  If the handler returns true
+      //   then the Timer is reset and left running, causing another timeout after the
+      //   appropriate interval.
+      //   If the handler returns false then the Timer is cancelled.
+      virtual bool handleTimeout(Timer* t) = 0;
+    };
+
+    // checkTimeouts()
+    //   Dispatches any elapsed Timers, and returns the number of milliseconds until the
+    //   next Timer will timeout.
+    static int checkTimeouts();
+
+    // getNextTimeout()
+    //   Returns the number of milliseconds until the next timeout, without dispatching
+    //   any elapsed Timers.
+    static int getNextTimeout();
+
+    // Create a Timer with the specified callback handler
+    Timer(Callback* cb_) {cb = cb_;}
+    ~Timer() {stop();}
+
+    // startTimer
+    //   Starts the timer, causing a timeout after the specified number of milliseconds.
+    //   If the timer is already active then it will be implicitly cancelled and re-started.
+    void start(int timeoutMs_);
+
+    // stopTimer
+    //   Cancels the timer.
+    void stop();
+
+    // isStarted
+    //   Determines whether the timer is started.
+    bool isStarted();
+
+    // getTimeoutMs
+    //   Determines the previously used timeout value, if any.
+    //   Usually used with isStarted() to get the _current_ timeout.
+    int getTimeoutMs();
+
+    // isBefore
+    //   Determine whether the Timer will timeout before the specified time.
+    bool isBefore(timeval other);
+
+  protected:
+    timeval dueTime;
+    int timeoutMs;
+    Callback* cb;
+
+    static void insertTimer(Timer* t);
+    // The list of currently active Timers, ordered by time left until timeout.
+    static std::list<Timer*> pending;
+  };
+
+};
+
+#endif
diff --git a/rfb/TransImageGetter.cxx b/rfb/TransImageGetter.cxx
index 0b494a9..82c291b 100644
--- a/rfb/TransImageGetter.cxx
+++ b/rfb/TransImageGetter.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/TransImageGetter.h b/rfb/TransImageGetter.h
index 60ab069..5328e6d 100644
--- a/rfb/TransImageGetter.h
+++ b/rfb/TransImageGetter.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/TrueColourMap.h b/rfb/TrueColourMap.h
index c0d4907..1e87fa4 100644
--- a/rfb/TrueColourMap.h
+++ b/rfb/TrueColourMap.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/UpdateTracker.cxx b/rfb/UpdateTracker.cxx
index cc0fb10..14ac49d 100644
--- a/rfb/UpdateTracker.cxx
+++ b/rfb/UpdateTracker.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -30,34 +30,34 @@
 
 static LogWriter vlog("UpdateTracker");
 
-// -=- ClippedUpdateTracker
 
-void ClippedUpdateTracker::add_changed(const Region &region) {
-  child.add_changed(region.intersect(cliprgn));
+// -=- ClippingUpdateTracker
+
+void ClippingUpdateTracker::add_changed(const Region &region) {
+  ut->add_changed(region.intersect(clipRect));
 }
 
-void ClippedUpdateTracker::add_copied(const Region &dest, const Point &delta) {
+void ClippingUpdateTracker::add_copied(const Region &dest, const Point &delta) {
   // Clip the destination to the display area
-  Region clipdest = dest.intersect(cliprgn);
+  Region clipdest = dest.intersect(clipRect);
   if (clipdest.is_empty())  return;
 
   // Clip the source to the screen
   Region tmp = clipdest;
   tmp.translate(delta.negate());
-  tmp.assign_intersect(cliprgn);
+  tmp.assign_intersect(clipRect);
   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);
+    ut->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);
-  }
+  if (!tmp.is_empty())
+    ut->add_changed(tmp);
 }
 
 // SimpleUpdateTracker
@@ -140,7 +140,7 @@
   changed.assign_subtract(region);
 }
 
-void SimpleUpdateTracker::get_update(UpdateInfo* info, const Region& clip)
+void SimpleUpdateTracker::getUpdateInfo(UpdateInfo* info, const Region& clip)
 {
   copied.assign_subtract(changed);
   info->changed = changed.intersect(clip);
@@ -148,25 +148,9 @@
   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);
-  }
+void SimpleUpdateTracker::copyTo(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
index 5015a25..5b51317 100644
--- a/rfb/UpdateTracker.h
+++ b/rfb/UpdateTracker.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -47,25 +47,24 @@
     virtual void add_copied(const Region &dest, const Point &delta) = 0;
   };
 
-  class ClippedUpdateTracker : public UpdateTracker {
+  class ClippingUpdateTracker : 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_;};
+    ClippingUpdateTracker() : ut(0) {}
+    ClippingUpdateTracker(UpdateTracker* ut_, const Rect& r=Rect()) : ut(ut_), clipRect(r) {}
+    
+    void setUpdateTracker(UpdateTracker* ut_) {ut = ut_;}
+    void setClipRect(const Rect& cr) {clipRect = cr;}
 
     virtual void add_changed(const Region &region);
     virtual void add_copied(const Region &dest, const Point &delta);
   protected:
-    UpdateTracker &child;
-    Region cliprgn;
+    UpdateTracker* ut;
+    Region clipRect;
   };
 
   class SimpleUpdateTracker : public UpdateTracker {
   public:
-    SimpleUpdateTracker(bool use_copyrect=false);
+    SimpleUpdateTracker(bool use_copyrect=true);
     virtual ~SimpleUpdateTracker();
 
     virtual void enable_copyrect(bool enable);
@@ -75,13 +74,10 @@
     virtual void subtract(const Region& region);
 
     // Fill the supplied UpdateInfo structure with update information
-    virtual void get_update(UpdateInfo* info, const Region& cliprgn);
+    virtual void getUpdateInfo(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);
+    // Copy the contained updates to another tracker
+    virtual void copyTo(UpdateTracker* to) const;
 
 
     // Get the changed/copied regions
diff --git a/rfb/UserPasswdGetter.h b/rfb/UserPasswdGetter.h
index c242ed0..18b0bae 100644
--- a/rfb/UserPasswdGetter.h
+++ b/rfb/UserPasswdGetter.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -20,12 +20,11 @@
 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;
+    // 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).
+    virtual void getUserPasswd(char** user, char** password)=0;
   };
 }
 #endif
diff --git a/rfb/VNCSConnectionST.cxx b/rfb/VNCSConnectionST.cxx
index e7bc09f..fe60e43 100644
--- a/rfb/VNCSConnectionST.cxx
+++ b/rfb/VNCSConnectionST.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -21,6 +21,7 @@
 #include <rfb/secTypes.h>
 #include <rfb/ServerCore.h>
 #include <rfb/ComparingUpdateTracker.h>
+#include <rfb/KeyRemapper.h>
 #define XK_MISCELLANY
 #define XK_XKB_KEYS
 #include <rfb/keysymdef.h>
@@ -31,8 +32,8 @@
 
 VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
                                    bool reverse)
-  : sock(s), reverseConnection(reverse), server(server_),
-    image_getter(server->useEconomicTranslate),
+  : SConnection(server_->securityFactory, reverse), sock(s), server(server_),
+    updates(false), image_getter(server->useEconomicTranslate),
     drawRenderedCursor(false), removeRenderedCursor(false),
     pointerEventTime(0), accessRights(AccessDefault),
     startTime(time(0)), m_pFileTransfer(0)
@@ -41,21 +42,11 @@
   peerEndpoint.buf = sock->getPeerEndpoint();
   VNCServerST::connectionsLog.write(1,"accepted: %s", peerEndpoint.buf);
 
+  // Configure the socket
   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);
-  }
-
+  // Add this client to the VNCServerST
   if (server->m_pFTManager != NULL) {
     SFileTransfer *pFT = server->m_pFTManager->createObject(sock);
     if (pFT != NULL) {
@@ -71,7 +62,8 @@
 {
   // If we reach here then VNCServerST is deleting us!
   VNCServerST::connectionsLog.write(1,"closed: %s (%s)",
-                                    peerEndpoint.buf, closeReason.buf);
+                                    peerEndpoint.buf,
+                                    (closeReason.buf) ? closeReason.buf : "");
 
   // Release any keys the client still had pressed
   std::set<rdr::U32>::iterator i;
@@ -114,16 +106,17 @@
       server->lastDisconnectTime = time(0);
   }
 
-  // Just shutdown the socket.  This will cause processMessages to
-  // eventually fail, causing us and our socket to be deleted.
+  // Just shutdown the socket and mark our state as closing.  Eventually the
+  // calling code will call VNCServerST's removeSocket() method causing us to
+  // be deleted.
   sock->shutdown();
   setState(RFBSTATE_CLOSING);
 }
 
 
-bool VNCSConnectionST::processMessages()
+void VNCSConnectionST::processMessages()
 {
-  if (state() == RFBSTATE_CLOSING) return false;
+  if (state() == RFBSTATE_CLOSING) return;
   try {
     // - Now set appropriate socket timeouts and process data
     setSocketTimeouts();
@@ -135,15 +128,11 @@
 
     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()
@@ -181,9 +170,11 @@
 
       cp.width = server->pb->width();
       cp.height = server->pb->height();
-      if (!writer()->writeSetDesktopSize()) {
-        close("Client does not support desktop resize");
-        return;
+      if (state() == RFBSTATE_NORMAL) {
+        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
@@ -259,13 +250,13 @@
     // now.
     vlog.info("Time has gone forwards - resetting idle timeout");
     lastEventTime = now;
-    return idleTimeout;
+    return secsToMillis(idleTimeout);
   }
   if (timeLeft <= 0) {
     close("Idle timeout");
     return 0;
   }
-  return timeLeft * 1000;
+  return secsToMillis(timeLeft);
 }
 
 // renderedCursorChange() is called whenever the server-side rendered cursor
@@ -313,29 +304,10 @@
 
 // -=- 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
@@ -357,18 +329,36 @@
 
 void VNCSConnectionST::queryConnection(const char* userName)
 {
+  // - Authentication succeeded - clear from blacklist
+  CharArray name; name.buf = sock->getPeerAddress();
+  server->blHosts->clearBlackmark(name.buf);
+
+  // - Special case to provide a more useful error message
+  if (rfb::Server::neverShared && !rfb::Server::disconnectClients &&
+    server->authClientCount() > 0) {
+    approveConnection(false, "The server is already in use");
+    return;
+  }
+
   // - Does the client have the right to bypass the query?
-  if (reverseConnection || !rfb::Server::queryConnect ||
+  if (reverseConnection ||
+      !(rfb::Server::queryConnect || sock->requiresQuery()) ||
       (accessRights & AccessNoQuery))
   {
     approveConnection(true);
     return;
   }
 
+  // - Get the server to display an Accept/Reject dialog, if required
+  //   If a dialog is displayed, the result will be PENDING, and the
+  //   server will call approveConnection at a later time
   CharArray reason;
   VNCServerST::queryResult qr = server->queryConnection(sock, userName,
                                                         &reason.buf);
-  if (qr == VNCServerST::PENDING) return;
+  if (qr == VNCServerST::PENDING)
+    return;
+
+  // - If server returns ACCEPT/REJECT then pass result to SConnection
   approveConnection(qr == VNCServerST::ACCEPT, reason.buf);
 }
 
@@ -383,7 +373,8 @@
       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
+      // - Refuse this connection if there are existing clients, in addition to
+      // this one
       if (server->authClientCount() > 1) {
         close("Server is already in use");
         return;
@@ -403,14 +394,14 @@
   setCursor();
 }
 
-void VNCSConnectionST::pointerEvent(int x, int y, int buttonMask)
+void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask)
 {
   pointerEventTime = lastEventTime = time(0);
   server->lastUserInputTime = lastEventTime;
   if (!(accessRights & AccessPtrEvents)) return;
   if (!rfb::Server::acceptPointerEvents) return;
   if (!server->pointerClient || server->pointerClient == this) {
-    pointerEventPos = Point(x, y);
+    pointerEventPos = pos;
     if (buttonMask)
       server->pointerClient = this;
     else
@@ -443,6 +434,10 @@
   if (!(accessRights & AccessKeyEvents)) return;
   if (!rfb::Server::acceptKeyEvents) return;
 
+  // Remap the key if required
+  if (server->keyRemapper)
+    key = server->keyRemapper->remapKey(key);
+
   // Turn ISO_Left_Tab into shifted Tab.
   VNCSConnectionSTShiftPresser shiftPresser(server->desktop);
   if (key == XK_ISO_Left_Tab) {
@@ -533,10 +528,9 @@
   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);
+                           server->cursor.height(),
+                           server->cursor.hotspot,
+                           transData, server->cursor.mask.buf);
 }
 
 
@@ -585,7 +579,7 @@
     if (renderedCursorRect.is_empty()) {
       drawRenderedCursor = false;
     } else if (!updates.get_changed().union_(updates.get_copied())
-        .intersect(renderedCursorRect).is_empty()) {
+               .intersect(renderedCursorRect).is_empty()) {
       drawRenderedCursor = true;
     }
 
@@ -600,7 +594,7 @@
 
   UpdateInfo update;
   updates.enable_copyrect(cp.useCopyRect);
-  updates.get_update(&update, requested);
+  updates.getUpdateInfo(&update, requested);
   if (!update.is_empty() || writer()->needFakeUpdate() || drawRenderedCursor) {
     // Compute the number of rectangles. Tight encoder makes the things more
     // complicated as compared to the original RealVNC.
@@ -671,11 +665,9 @@
 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;
-  }
+  soonestTimeout(&timeoutms, secsToMillis(rfb::Server::idleTimeout));
+  if (timeoutms == 0)
+    timeoutms = -1;
   sock->inStream().setTimeout(timeoutms);
   sock->outStream().setTimeout(timeoutms);
 }
diff --git a/rfb/VNCSConnectionST.h b/rfb/VNCSConnectionST.h
index 99aaf3b..a04296d 100644
--- a/rfb/VNCSConnectionST.h
+++ b/rfb/VNCSConnectionST.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -57,9 +57,9 @@
 
     // 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();
+    // reading might block.  shutdown() will be called on the connection's
+    // Socket if an error occurs, via the close() call.
+    void processMessages();
 
     void writeFramebufferUpdateOrClose();
     void pixelBufferChange();
@@ -118,16 +118,14 @@
     // 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 pointerEvent(const Point& pos, 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 framebufferUpdateRequest(const Rect& r, bool incremental);
     virtual void setInitialColourMap();
     virtual void supportsLocalCursor();
 
@@ -154,7 +152,6 @@
 
     network::Socket* sock;
     CharArray peerEndpoint;
-    bool reverseConnection;
     VNCServerST* server;
     SimpleUpdateTracker updates;
     TransImageGetter image_getter;
diff --git a/rfb/VNCServer.h b/rfb/VNCServer.h
index e80044c..df0fb0e 100644
--- a/rfb/VNCServer.h
+++ b/rfb/VNCServer.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -69,11 +69,11 @@
     // 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,
+    virtual void setCursor(int width, int height, const Point& hotspot,
                            void* cursorData, void* mask) = 0;
 
     // setCursorPos() tells the server the current position of the cursor.
-    virtual void setCursorPos(int x, int y) = 0;
+    virtual void setCursorPos(const Point& p) = 0;
 
     // setSSecurityFactory() tells the server which factory to use when
     // attempting to authenticate connections.
diff --git a/rfb/VNCServerST.cxx b/rfb/VNCServerST.cxx
index 0721a3c..7e3e0c6 100644
--- a/rfb/VNCServerST.cxx
+++ b/rfb/VNCServerST.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -32,25 +32,19 @@
 // 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.
+// The responsibility for creating and deleting sockets is entirely with the
+// calling code.  When VNCServerST wants to close a connection to a client it
+// calls the VNCSConnectionST's close() method which calls shutdown() on the
+// socket.  Eventually the calling code will notice that the socket has been
+// shut down and call removeSocket() so that we can delete the
+// VNCSConnectionST.  Note that the socket must not be deleted by the calling
+// code until after removeSocket() has been called.
 //
 // 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.
+// dealing with such a connection).  In order to properly implement the
+// getSockets function, we must maintain a separate closingSockets list,
+// otherwise blacklisted connections might be "forgotten".
 
 
 #include <rfb/ServerCore.h>
@@ -58,6 +52,7 @@
 #include <rfb/VNCSConnectionST.h>
 #include <rfb/ComparingUpdateTracker.h>
 #include <rfb/SSecurityFactoryStandard.h>
+#include <rfb/KeyRemapper.h>
 #include <rfb/util.h>
 
 #include <rdr/types.h>
@@ -80,7 +75,8 @@
     name(strDup(name_)), pointerClient(0), comparer(0),
     renderedCursorInvalid(false),
     securityFactory(sf ? sf : &defaultSecurityFactory),
-    queryConnectionHandler(0), useEconomicTranslate(false),
+    queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance),
+    useEconomicTranslate(false),
     lastConnectionTime(0), disableclients(false)
 {
   lastUserInputTime = lastDisconnectTime = time(0);
@@ -97,13 +93,8 @@
   // 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) {
@@ -117,18 +108,12 @@
 
 // SocketServer methods
 
-void VNCServerST::addClient(network::Socket* sock)
-{
-  addClient(sock, false);
-}
-
-void VNCServerST::addClient(network::Socket* sock, bool reverse)
+void VNCServerST::addSocket(network::Socket* sock, bool outgoing)
 {
   // - 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)) {
+  if (blHosts->isBlackmarked(address.buf)) {
     connectionsLog.error("blacklisted: %s", address.buf);
     try {
       SConnection::writeConnFailedFromScratch("Too many security failures",
@@ -144,37 +129,43 @@
     lastConnectionTime = time(0);
   }
 
-  VNCSConnectionST* client = new VNCSConnectionST(this, sock, reverse);
+  VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing);
   client->init();
 }
 
-bool VNCServerST::processSocketEvent(network::Socket* sock)
+void VNCServerST::removeSocket(network::Socket* sock) {
+  // - If the socket has resources allocated to it, delete them
+  std::list<VNCSConnectionST*>::iterator ci;
+  for (ci = clients.begin(); ci != clients.end(); ci++) {
+    if ((*ci)->getSock() == sock) {
+      // - Delete the per-Socket resources
+      delete *ci;
+
+      // - Check that the desktop object is still required
+      if (authClientCount() == 0 && desktopStarted) {
+        slog.debug("no authenticated clients - stopping desktop");
+        desktopStarted = false;
+        desktop->stop();
+      }
+      return;
+    }
+  }
+
+  // - If the Socket has no resources, it may have been a closingSocket
+  closingSockets.remove(sock);
+}
+
+void 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;
+      (*ci)->processMessages();
+      return;
     }
   }
-
-  // - 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;
+  throw rdr::Exception("invalid Socket in VNCServerST");
 }
 
 int VNCServerST::checkTimeouts()
@@ -342,11 +333,10 @@
   }
 }
 
-void VNCServerST::setCursor(int width, int height, int newHotspotX,
-                            int newHotspotY, void* data, void* mask)
+void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
+                            void* data, void* mask)
 {
-  cursor.hotspot.x = newHotspotX;
-  cursor.hotspot.y = newHotspotY;
+  cursor.hotspot = newHotspot;
   cursor.setSize(width, height);
   memcpy(cursor.data, data, cursor.dataLen());
   memcpy(cursor.mask.buf, mask, cursor.maskLen());
@@ -363,11 +353,10 @@
   }
 }
 
-void VNCServerST::setCursorPos(int x, int y)
+void VNCServerST::setCursorPos(const Point& pos)
 {
-  if (cursorPos.x != x || cursorPos.y != y) {
-    cursorPos.x = x;
-    cursorPos.y = y;
+  if (!cursorPos.equals(pos)) {
+    cursorPos = pos;
     renderedCursorInvalid = true;
     std::list<VNCSConnectionST*>::iterator ci;
     for (ci = clients.begin(); ci != clients.end(); ci++)
diff --git a/rfb/VNCServerST.h b/rfb/VNCServerST.h
index 9d5fe95..bc15b7f 100644
--- a/rfb/VNCServerST.h
+++ b/rfb/VNCServerST.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -40,6 +40,7 @@
   class VNCSConnectionST;
   class ComparingUpdateTracker;
   class PixelBuffer;
+  class KeyRemapper;
 
   class VNCServerST : public VNCServer, public network::SocketServer {
   public:
@@ -53,30 +54,25 @@
 
     // 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.
+    // addSocket
+    //   Causes the server to allocate an RFB-protocol management
+    //   structure for the socket & initialise it.
+    virtual void addSocket(network::Socket* sock, bool outgoing=false);
 
-    virtual void addClient(network::Socket* sock);
+    // removeSocket
+    //   Clean up any resources associated with the Socket
+    virtual void removeSocket(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!
+    // processSocketEvent
+    //   Read more RFB data from the Socket.  If an error occurs during
+    //   processing then shutdown() is called on the Socket, causing
+    //   removeSocket() to be called by the caller at a later time.
+    virtual void processSocketEvent(network::Socket* sock);
 
-    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.
-
+    // 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();
 
 
@@ -89,9 +85,9 @@
     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,
+    virtual void setCursor(int width, int height, const Point& hotspot,
                            void* cursorData, void* mask);
-    virtual void setCursorPos(int x, int y);
+    virtual void setCursorPos(const Point& p);
     virtual void setSSecurityFactory(SSecurityFactory* f) {securityFactory=f;}
 
     virtual void bell();
@@ -102,16 +98,10 @@
 
     // VNCServerST-only methods
 
-    //   If a particular VNCSConnectionST* is specified then
-    //   that connection will NOT be closed.
+    // closeClients() closes all RFB sessions, except the specified one (if
+    // any), and logs the specified reason for closure.
     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().
 
@@ -183,11 +173,15 @@
     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.
+    // 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; }
 
+    // setKeyRemapper() replaces the VNCServerST's default key remapper.
+    // NB: A null pointer is valid here.
+    void setKeyRemapper(KeyRemapper* kr) { keyRemapper = kr; }
+
     void getConnInfo(ListConnInfo * listConn);
     void setConnStatus(ListConnInfo* listConn);
 
@@ -235,6 +229,7 @@
 
     SSecurityFactory* securityFactory;
     QueryConnectionHandler* queryConnectionHandler;
+    KeyRemapper* keyRemapper;
     bool useEconomicTranslate;
     
     time_t lastUserInputTime;
diff --git a/rfb/ZRLEDecoder.cxx b/rfb/ZRLEDecoder.cxx
index 2dd4a12..b7c6912 100644
--- a/rfb/ZRLEDecoder.cxx
+++ b/rfb/ZRLEDecoder.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/ZRLEDecoder.h b/rfb/ZRLEDecoder.h
index c8b1feb..fe96c73 100644
--- a/rfb/ZRLEDecoder.h
+++ b/rfb/ZRLEDecoder.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/ZRLEEncoder.h b/rfb/ZRLEEncoder.h
index 17222a3..7768917 100644
--- a/rfb/ZRLEEncoder.h
+++ b/rfb/ZRLEEncoder.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -34,7 +34,8 @@
     // 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.
+    // 512Kbytes plus a bit of overhead (the overhead is about 1/16 of the
+    // width, in this example about 128 bytes).
     static void setMaxLen(int m) { maxLen = m; }
 
     // setSharedMos() sets a MemOutStream to be shared amongst all
diff --git a/rfb/d3des.c b/rfb/d3des.c
index 9227ddd..eaca581 100644
--- a/rfb/d3des.c
+++ b/rfb/d3des.c
@@ -173,14 +173,14 @@
 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;
+	*into++ = (unsigned char)((*outof >> 24) & 0xffL);
+	*into++ = (unsigned char)((*outof >> 16) & 0xffL);
+	*into++ = (unsigned char)((*outof >>  8) & 0xffL);
+	*into++ = (unsigned char)(*outof++	 & 0xffL);
+	*into++ = (unsigned char)((*outof >> 24) & 0xffL);
+	*into++ = (unsigned char)((*outof >> 16) & 0xffL);
+	*into++ = (unsigned char)((*outof >>  8) & 0xffL);
+	*into	=  (unsigned char)(*outof	 & 0xffL);
 	return;
 	}
 
diff --git a/rfb/encodings.cxx b/rfb/encodings.cxx
index d3b0ccb..6aa81c4 100644
--- a/rfb/encodings.cxx
+++ b/rfb/encodings.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/encodings.h b/rfb/encodings.h
index 20be0ae..51f6f1e 100644
--- a/rfb/encodings.h
+++ b/rfb/encodings.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/hextileConstants.h b/rfb/hextileConstants.h
index 272afbb..b8713cb 100644
--- a/rfb/hextileConstants.h
+++ b/rfb/hextileConstants.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/hextileDecode.h b/rfb/hextileDecode.h
index 0c5559a..77befc7 100644
--- a/rfb/hextileDecode.h
+++ b/rfb/hextileDecode.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -52,11 +52,11 @@
 
   for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) {
 
-    t.br.y = vncmin(r.br.y, t.tl.y + 16);
+    t.br.y = __rfbmin(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 = vncmin(r.br.x, t.tl.x + 16);
+      t.br.x = __rfbmin(r.br.x, t.tl.x + 16);
 
       int tileType = is->readU8();
 
diff --git a/rfb/hextileEncode.h b/rfb/hextileEncode.h
index 27c7e01..9f8bd55 100644
--- a/rfb/hextileEncode.h
+++ b/rfb/hextileEncode.h
@@ -1,6 +1,6 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
  * Copyright (C) 2005 Constantin Kaplinsky.  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
@@ -61,11 +61,11 @@
 
   for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) {
 
-    t.br.y = vncmin(r.br.y, t.tl.y + 16);
+    t.br.y = __rfbmin(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 = vncmin(r.br.x, t.tl.x + 16);
+      t.br.x = __rfbmin(r.br.x, t.tl.x + 16);
 
       GET_IMAGE_INTO_BUF(t,buf);
 
diff --git a/rfb/hextileEncodeBetter.h b/rfb/hextileEncodeBetter.h
index 59df102..2b6b160 100644
--- a/rfb/hextileEncodeBetter.h
+++ b/rfb/hextileEncodeBetter.h
@@ -1,6 +1,6 @@
 /* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
  * Copyright (C) 2005 Constantin Kaplinsky.  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
@@ -294,11 +294,11 @@
 
   for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) {
 
-    t.br.y = vncmin(r.br.y, t.tl.y + 16);
+    t.br.y = __rfbmin(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 = vncmin(r.br.x, t.tl.x + 16);
+      t.br.x = __rfbmin(r.br.x, t.tl.x + 16);
 
       GET_IMAGE_INTO_BUF(t,buf);
 
diff --git a/rfb/msgTypes.h b/rfb/msgTypes.h
index 2b05c75..f6f8d5c 100644
--- a/rfb/msgTypes.h
+++ b/rfb/msgTypes.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/msvcwarning.h b/rfb/msvcwarning.h
deleted file mode 100644
index 4127ce9..0000000
--- a/rfb/msvcwarning.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/* 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
index b38f266..561703f 100644
--- a/rfb/rfb.dsp
+++ b/rfb/rfb.dsp
@@ -42,7 +42,7 @@
 # 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 CPP /nologo /MT /W3 /GX /O1 /I ".." /FI"rdr/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

@@ -65,7 +65,7 @@
 # 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" /FR /YX /FD /GZ /c

+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c

 # ADD BASE RSC /l 0x809 /d "_DEBUG"

 # ADD RSC /l 0x809 /d "_DEBUG"

 BSC32=bscmake.exe

@@ -88,7 +88,7 @@
 # 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 CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"rdr/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

@@ -210,6 +210,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\KeyRemapper.cxx

+# End Source File

+# Begin Source File

+

 SOURCE=.\Logger.cxx

 # End Source File

 # Begin Source File

@@ -226,6 +230,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\Password.cxx

+# End Source File

+# Begin Source File

+

 SOURCE=.\PixelBuffer.cxx

 # End Source File

 # Begin Source File

@@ -314,10 +322,6 @@
 # End Source File

 # Begin Source File

 

-SOURCE=.\win32\Threading_win32.cxx

-# End Source File

-# Begin Source File

-

 SOURCE=.\TightDecoder.cxx

 # ADD CPP /I "../jpeg"

 # End Source File

@@ -348,10 +352,6 @@
 # End Source File

 # Begin Source File

 

-SOURCE=.\vncAuth.cxx

-# End Source File

-# Begin Source File

-

 SOURCE=.\VNCSConnectionST.cxx

 # End Source File

 # Begin Source File

@@ -520,6 +520,14 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\InputHandler.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\KeyRemapper.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\keysymdef.h

 # End Source File

 # Begin Source File

@@ -552,6 +560,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\Password.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\Pixel.h

 # End Source File

 # Begin Source File

@@ -672,10 +684,6 @@
 # End Source File

 # Begin Source File

 

-SOURCE=.\win32\Threading_win32.h

-# End Source File

-# Begin Source File

-

 SOURCE=.\tightDecode.h

 # End Source File

 # Begin Source File

@@ -728,14 +736,6 @@
 # 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

diff --git a/rfb/rreDecode.h b/rfb/rreDecode.h
index 9f69cee..1f5bdf8 100644
--- a/rfb/rreDecode.h
+++ b/rfb/rreDecode.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/rreEncode.h b/rfb/rreEncode.h
index 4877a12..3f83487 100644
--- a/rfb/rreEncode.h
+++ b/rfb/rreEncode.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/secTypes.cxx b/rfb/secTypes.cxx
index c850684..830d844 100644
--- a/rfb/secTypes.cxx
+++ b/rfb/secTypes.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -29,6 +29,8 @@
   if (strcasecmp(name, "Tight") == 0)      return secTypeTight;
   if (strcasecmp(name, "RA2") == 0)        return secTypeRA2;
   if (strcasecmp(name, "RA2ne") == 0)      return secTypeRA2ne;
+  if (strcasecmp(name, "SSPI") == 0)       return secTypeSSPI;
+  if (strcasecmp(name, "SSPIne") == 0)       return secTypeSSPIne;
   return secTypeInvalid;
 }
 
@@ -40,6 +42,8 @@
   case secTypeTight:      return "Tight";
   case secTypeRA2:        return "RA2";
   case secTypeRA2ne:      return "RA2ne";
+  case secTypeSSPI:       return "SSPI";
+  case secTypeSSPIne:     return "SSPIne";
   default:                return "[unknown secType]";
   }
 }
@@ -47,8 +51,11 @@
 bool rfb::secTypeEncrypts(int num)
 {
   switch (num) {
-  case secTypeRA2:        return true;
-  default:                return false;
+  case secTypeRA2:
+  case secTypeSSPI:
+    return true;
+  default:
+    return false;
   }
 }
 
diff --git a/rfb/secTypes.h b/rfb/secTypes.h
index f0b326e..3cf783b 100644
--- a/rfb/secTypes.h
+++ b/rfb/secTypes.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -32,6 +32,9 @@
   const int secTypeRA2     = 5;
   const int secTypeRA2ne   = 6;
 
+  const int secTypeSSPI    = 7;
+  const int secTypeSSPIne    = 8;
+
   const int secTypeTight   = 16;
   const int secTypeUltra   = 17;
   const int secTypeTLS     = 18;
diff --git a/rfb/transInitTempl.h b/rfb/transInitTempl.h
index 2658f9f..464cfdf 100644
--- a/rfb/transInitTempl.h
+++ b/rfb/transInitTempl.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/transTempl.h b/rfb/transTempl.h
index 907b839..09dc7f9 100644
--- a/rfb/transTempl.h
+++ b/rfb/transTempl.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/util.cxx b/rfb/util.cxx
index 94aa632..5745432 100644
--- a/rfb/util.cxx
+++ b/rfb/util.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb/util.h b/rfb/util.h
index 02183d7..fa205f0 100644
--- a/rfb/util.h
+++ b/rfb/util.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -23,13 +23,7 @@
 #ifndef __RFB_UTIL_H__
 #define __RFB_UTIL_H__
 
-#ifndef vncmin
-#define vncmin(a,b)            (((a) < (b)) ? (a) : (b))
-#endif
-#ifndef vncmax
-#define vncmax(a,b)            (((a) > (b)) ? (a) : (b))
-#endif
-
+#include <limits.h>
 #include <string.h>
 
 namespace rfb {
@@ -71,8 +65,36 @@
 
   // Copies src to dest, up to specified length-1, and guarantees termination
   void strCopy(char* dest, const char* src, int destlen);
+
+
+  // HELPER functions for timeout handling
+
+  // soonestTimeout() is a function to help work out the soonest of several
+  //   timeouts.
+  inline void soonestTimeout(int* timeout, int newTimeout) {
+    if (newTimeout && (!*timeout || newTimeout < *timeout))
+      *timeout = newTimeout;
+  }
+
+  // secsToMillis() turns seconds into milliseconds, capping the value so it
+  //   can't wrap round and become -ve
+  inline int secsToMillis(int secs) {
+    return (secs < 0 || secs > (INT_MAX/1000) ? INT_MAX : secs * 1000);
+  }
 }
 
+// Some platforms (e.g. Windows) include max() and min() macros in their
+// standard headers, but they are also standard C++ template functions, so some
+// C++ headers will undefine them.  So we steer clear of the names min and max
+// and define __rfbmin and __rfbmax instead.
+
+#ifndef __rfbmax
+#define __rfbmax(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+#ifndef __rfbmin
+#define __rfbmin(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
 // Declare strcasecmp() and/or strncasecmp() if absent on this system.
 
 #if !defined(WIN32) && !defined(HAVE_STRCASECMP)
@@ -86,10 +108,4 @@
 }
 #endif
 
-#endif // __RFB_UTIL_H__
-
-// -=- PLATFORM SPECIFIC UTILITY FUNCTIONS/IMPLEMENTATIONS
-#ifdef WIN32
-#include "win32/util_win32.h"
 #endif
-
diff --git a/rfb/vncAuth.cxx b/rfb/vncAuth.cxx
deleted file mode 100644
index 6bd6a62..0000000
--- a/rfb/vncAuth.cxx
+++ /dev/null
@@ -1,61 +0,0 @@
-/* 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/win32/msvcwarning.h b/rfb/win32/msvcwarning.h
deleted file mode 100644
index d0c98c3..0000000
--- a/rfb/win32/msvcwarning.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/* 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
deleted file mode 100644
index 4bde5ec..0000000
--- a/rfb/win32/util_win32.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/* 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
index e1f85f7..15d2790 100644
--- a/rfb/zrleDecode.h
+++ b/rfb/zrleDecode.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -61,11 +61,11 @@
 
   for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 64) {
 
-    t.br.y = vncmin(r.br.y, t.tl.y + 64);
+    t.br.y = __rfbmin(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 = vncmin(r.br.x, t.tl.x + 64);
+      t.br.x = __rfbmin(r.br.x, t.tl.x + 64);
 
       int mode = zis->readU8();
       bool rle = mode & 128;
diff --git a/rfb/zrleEncode.h b/rfb/zrleEncode.h
index 42505a3..9b7263b 100644
--- a/rfb/zrleEncode.h
+++ b/rfb/zrleEncode.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -130,7 +130,7 @@
 
   for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 64) {
 
-    t.br.y = vncmin(r.br.y, t.tl.y + 64);
+    t.br.y = __rfbmin(r.br.y, t.tl.y + 64);
 
     if (os->length() + worstCaseLine > maxLen) {
       if (t.tl.y == r.tl.y)
@@ -143,7 +143,7 @@
 
     for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 64) {
 
-      t.br.x = vncmin(r.br.x, t.tl.x + 64);
+      t.br.x = __rfbmin(r.br.x, t.tl.x + 64);
 
       GET_IMAGE_INTO_BUF(t,buf);
 
diff --git a/rfb_win32/AboutDialog.cxx b/rfb_win32/AboutDialog.cxx
index efb15c0..030be1b 100644
--- a/rfb_win32/AboutDialog.cxx
+++ b/rfb_win32/AboutDialog.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -43,7 +43,7 @@
   // Get our executable's version info
   FileVersionInfo verInfo;
 
-  SetWindowText(GetDlgItem(handle, Version), verInfo.getVerString(_T("FileVersion")));
+  SetWindowText(GetDlgItem(handle, Version), verInfo.getVerString(_T("ProductVersion")));
   SetWindowText(GetDlgItem(handle, Copyright), verInfo.getVerString(_T("LegalCopyright")));
-  SetWindowText(GetDlgItem(handle, Description), verInfo.getVerString(_T("FileDescription")));
+  SetWindowText(GetDlgItem(handle, Description), verInfo.getVerString(_T("ProductName")));
 }
diff --git a/rfb_win32/AboutDialog.h b/rfb_win32/AboutDialog.h
index cb1713d..0dd9d49 100644
--- a/rfb_win32/AboutDialog.h
+++ b/rfb_win32/AboutDialog.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb_win32/BitmapInfo.h b/rfb_win32/BitmapInfo.h
new file mode 100644
index 0000000..6a6f0d2
--- /dev/null
+++ b/rfb_win32/BitmapInfo.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2002-2005 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_WIN32_BITMAP_INFO_H__
+#define __RFB_WIN32_BITMAP_INFO_H__
+
+#include <windows.h>
+#include <rdr/types.h>
+
+namespace rfb {
+  namespace win32 {
+
+    struct BitmapInfo {
+      BITMAPINFOHEADER bmiHeader;
+      union {
+        struct {
+          DWORD red;
+          DWORD green;
+          DWORD blue;
+        } mask;
+        RGBQUAD color[256];
+      };
+    };
+
+    inline void initMaxAndShift(DWORD mask, int* max, int* shift) {
+      for ((*shift) = 0; (mask & 1) == 0; (*shift)++) mask >>= 1;
+        (*max) = (rdr::U16)mask;
+    }
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/CKeyboard.cxx b/rfb_win32/CKeyboard.cxx
index ad852a0..28aceab 100644
--- a/rfb_win32/CKeyboard.cxx
+++ b/rfb_win32/CKeyboard.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -24,7 +24,6 @@
 #include <rfb/keysymdef.h>
 
 #include <rfb_win32/CKeyboard.h>
-#include <rfb/CMsgWriter.h>
 #include <rfb/LogWriter.h>
 #include <rfb_win32/OSVersion.h>
 #include "keymap.h"
@@ -67,7 +66,7 @@
 
 class ModifierKeyReleaser {
 public:
-  ModifierKeyReleaser(CMsgWriter* writer_, int vkCode, bool extended)
+  ModifierKeyReleaser(InputHandler* writer_, int vkCode, bool extended)
     : writer(writer_), extendedVkey(vkCode + (extended ? 256 : 0)),
       keysym(0)
   {}
@@ -76,17 +75,17 @@
       keysym = (*downKeysym)[extendedVkey];
       vlog.debug("fake release extendedVkey 0x%x, keysym 0x%x",
                  extendedVkey, keysym);
-      writer->writeKeyEvent(keysym, false);
+      writer->keyEvent(keysym, false);
     }
   }
   ~ModifierKeyReleaser() {
     if (keysym) {
       vlog.debug("fake press extendedVkey 0x%x, keysym 0x%x",
                  extendedVkey, keysym);
-      writer->writeKeyEvent(keysym, true);
+      writer->keyEvent(keysym, true);
     }
   }
-  CMsgWriter* writer;
+  InputHandler* writer;
   int extendedVkey;
   rdr::U32 keysym;
 };
@@ -96,7 +95,7 @@
 #define IS_PRINTABLE_LATIN1(c) (((c) >= 32 && (c) <= 126) || (c) == 128 || \
                                 ((c) >= 160 && (c) <= 255))
 
-void win32::CKeyboard::keyEvent(CMsgWriter* writer, rdr::U8 vkey,
+void win32::CKeyboard::keyEvent(InputHandler* writer, rdr::U8 vkey,
                                 rdr::U32 flags, bool down)
 {
   bool extended = (flags & 0x1000000);
@@ -212,11 +211,11 @@
 
 // releaseAllKeys() - write key release events to the server for all keys
 // that are currently regarded as being down.
-void win32::CKeyboard::releaseAllKeys(CMsgWriter* writer) {
+void win32::CKeyboard::releaseAllKeys(InputHandler* writer) {
   std::map<int,rdr::U32>::iterator i, next_i;
   for (i=downKeysym.begin(); i!=downKeysym.end(); i=next_i) {
     next_i = i; next_i++;
-    writer->writeKeyEvent((*i).second, false);
+    writer->keyEvent((*i).second, false);
     downKeysym.erase(i);
   }
 }
@@ -225,12 +224,12 @@
 // actually sent a key down event for the given key.  The key up event always
 // contains the same keysym we used in the key down event, regardless of what
 // it would look up as using the current keyboard state.
-void win32::CKeyboard::releaseKey(CMsgWriter* writer, int extendedVkey)
+void win32::CKeyboard::releaseKey(InputHandler* writer, int extendedVkey)
 {
   if (downKeysym.find(extendedVkey) != downKeysym.end()) {
     vlog.debug("release extendedVkey 0x%x, keysym 0x%x",
                extendedVkey, downKeysym[extendedVkey]);
-    writer->writeKeyEvent(downKeysym[extendedVkey], false);
+    writer->keyEvent(downKeysym[extendedVkey], false);
     downKeysym.erase(extendedVkey);
   }
 }
@@ -242,18 +241,18 @@
 // first.  This can happen in two cases: (a) when a single key press results in
 // more than one character, and (b) when shift is released while another key is
 // autorepeating.
-void win32::CKeyboard::pressKey(CMsgWriter* writer, int extendedVkey,
+void win32::CKeyboard::pressKey(InputHandler* writer, int extendedVkey,
                                 rdr::U32 keysym)
 {
   if (downKeysym.find(extendedVkey) != downKeysym.end()) {
     if (downKeysym[extendedVkey] != keysym) {
       vlog.debug("release extendedVkey 0x%x, keysym 0x%x",
                  extendedVkey, downKeysym[extendedVkey]);
-      writer->writeKeyEvent(downKeysym[extendedVkey], false);
+      writer->keyEvent(downKeysym[extendedVkey], false);
     }
   }
   vlog.debug("press   extendedVkey 0x%x, keysym 0x%x",
              extendedVkey, keysym);
-  writer->writeKeyEvent(keysym, true);
+  writer->keyEvent(keysym, true);
   downKeysym[extendedVkey] = keysym;
 }
diff --git a/rfb_win32/CKeyboard.h b/rfb_win32/CKeyboard.h
index 10346ff..666ebce 100644
--- a/rfb_win32/CKeyboard.h
+++ b/rfb_win32/CKeyboard.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -23,25 +23,23 @@
 #ifndef __RFB_WIN32_CKEYBOARD_H__
 #define __RFB_WIN32_CKEYBOARD_H__
 
-#include <rdr/types.h>
+#include <rfb/InputHandler.h>
 #include <map>
 
 namespace rfb {
 
-  class CMsgWriter;
-
   namespace win32 {
 
     class CKeyboard {
     public:
-      void keyEvent(CMsgWriter* writer, rdr::U8 vkey, rdr::U32 flags,
+      void keyEvent(InputHandler* writer, rdr::U8 vkey, rdr::U32 flags,
                     bool down);
-      void releaseAllKeys(CMsgWriter* writer);
+      void releaseAllKeys(InputHandler* writer);
       const std::map<int,rdr::U32>& pressedKeys() const {return downKeysym;};
       bool keyPressed(int k) const {return downKeysym.find(k)!=downKeysym.end();}
     private:
-      void win32::CKeyboard::releaseKey(CMsgWriter* writer, int extendedVkey);
-      void win32::CKeyboard::pressKey(CMsgWriter* writer, int extendedVkey,
+      void win32::CKeyboard::releaseKey(InputHandler* writer, int extendedVkey);
+      void win32::CKeyboard::pressKey(InputHandler* writer, int extendedVkey,
                                       rdr::U32 keysym);
       std::map<int,rdr::U32> downKeysym;
     };
diff --git a/rfb_win32/CPointer.cxx b/rfb_win32/CPointer.cxx
index 1cab662..3d0d934 100644
--- a/rfb_win32/CPointer.cxx
+++ b/rfb_win32/CPointer.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -16,9 +16,7 @@
  * USA.
  */
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
-
 #include <rfb/LogWriter.h>
 #include <rfb_win32/CPointer.h>
 
@@ -37,21 +35,21 @@
 }
 
 
-void CPointer::pointerEvent(CMsgWriter* writer, int x, int y, int buttonMask) {
+void CPointer::pointerEvent(InputHandler* writer, const Point& pos, int buttonMask) {
   //
   // - Duplicate Event Filtering
   //
 
   bool maskChanged = buttonMask != currButtonMask;
-  bool posChanged = !Point(x, y).equals(currPos);
+  bool posChanged = !pos.equals(currPos);
   if (!(posChanged || maskChanged))
     return;
 
   // Pass on the event to the event-interval handler
-  threePointerEvent(writer, x, y, buttonMask);
+  threePointerEvent(writer, pos, buttonMask);
 
   // Save the position and mask
-  currPos = Point(x, y);
+  currPos = pos;
   currButtonMask = buttonMask;
 }
 
@@ -66,7 +64,7 @@
   return buttonMask;
 }
 
-void CPointer::threePointerEvent(CMsgWriter* writer, int x, int y, int buttonMask) {
+void CPointer::threePointerEvent(InputHandler* writer, const Point& pos, int buttonMask) {
   //
   // - 3-Button Mouse Emulation
   //
@@ -84,7 +82,7 @@
         //   expires then we know we should actually send this event
         vlog.debug("emulate3: start timer");
         threeTimer.start(100);
-        threePos = Point(x, y);
+        threePos = pos;
         threeMask = buttonMask;
         return;
 
@@ -94,7 +92,7 @@
         vlog.debug("emulate3: stop timer (state)");
         threeTimer.stop();
         if (threeEmulating == ((buttonMask & 5) == 5))
-          intervalPointerEvent(writer, threePos.x, threePos.y, threeMask);
+          intervalPointerEvent(writer, threePos, threeMask);
         else
           threeEmulating = ((buttonMask & 5) == 5);
       }
@@ -104,11 +102,11 @@
       if (threeTimer.isActive()) {
         // - We are timing for an emulation event
 
-        if (abs(threePos.x - x) <= 4 || abs(threePos.y - y) <= 4) {
+        if (abs(threePos.x - pos.x) <= 4 || abs(threePos.y - pos.y) <= 4) {
           //   If the mouse has moved too far since the button-change event then flush
           vlog.debug("emulate3: stop timer (moved)");
           threeTimer.stop();
-          intervalPointerEvent(writer, threePos.x, threePos.y, threeMask);
+          intervalPointerEvent(writer, threePos, threeMask);
 
         } else {
           //   Otherwise, we ignore the new event
@@ -129,14 +127,14 @@
   }
 
   // - Let the event pass through to the next stage of processing
-  intervalPointerEvent(writer, x, y, buttonMask);
+  intervalPointerEvent(writer, pos, buttonMask);
 }
 
-void CPointer::intervalPointerEvent(CMsgWriter* writer, int x, int y, int buttonMask) {
+void CPointer::intervalPointerEvent(InputHandler* writer, const Point& pos, int buttonMask) {
   //
   // - Pointer Event Interval
   //
-  vlog.write(101, "ptrEvent: %d,%d (%lx)", x, y, buttonMask);
+  vlog.write(101, "ptrEvent: %d,%d (%lx)", pos.x, pos.y, buttonMask);
 
   // Send the event immediately if we haven't sent one for a while
   bool sendNow = !intervalTimer.isActive();
@@ -145,14 +143,14 @@
     // If the buttons have changed then flush queued events and send now
     sendNow = true;
     if (intervalQueued)
-      writer->writePointerEvent(intervalPos.x, intervalPos.y, intervalMask);
+      writer->pointerEvent(intervalPos, intervalMask);
     intervalQueued = false;
   }
 
   if (!sendNow) {
     // If we're not sending now then just queue the event
     intervalQueued = true;
-    intervalPos = Point(x, y);
+    intervalPos = pos;
     intervalMask = buttonMask;
   } else {
     // Start the interval timer if required, and send the event
@@ -160,15 +158,15 @@
     intervalMask = buttonMask;
     if (pointerEventInterval)
       intervalTimer.start(pointerEventInterval);
-    writer->writePointerEvent(x, y, buttonMask);
+    writer->pointerEvent(pos, buttonMask);
   }
 }
 
-void CPointer::handleTimer(CMsgWriter* writer, int timerId) {
+void CPointer::handleTimer(InputHandler* writer, int timerId) {
   if (timerId == intervalTimer.getId()) {
     // Pointer interval has expired - send any queued events
     if (intervalQueued) {
-      writer->writePointerEvent(intervalPos.x, intervalPos.y, intervalMask);
+      writer->pointerEvent(intervalPos, intervalMask);
       intervalQueued = false;
     } else {
       intervalTimer.stop();
@@ -183,6 +181,6 @@
     if (threeEmulating)
       threeMask = emulate3Mask(threeMask);
 
-    intervalPointerEvent(writer, threePos.x, threePos.y, threeMask);
+    intervalPointerEvent(writer, threePos, threeMask);
   }
 }
diff --git a/rfb_win32/CPointer.h b/rfb_win32/CPointer.h
index f111de7..b591601 100644
--- a/rfb_win32/CPointer.h
+++ b/rfb_win32/CPointer.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -25,14 +25,12 @@
 
 #include <rdr/Exception.h>
 #include <rfb/Configuration.h>
-#include <rfb/CMsgWriter.h>
+#include <rfb/InputHandler.h>
 #include <rfb/Rect.h>
 #include <rfb_win32/IntervalTimer.h>
 
 namespace rfb {
 
-  class CMsgWriter;
-
   namespace win32 {
 
     class CPointer {
@@ -40,8 +38,8 @@
       CPointer();
       ~CPointer();
 
-      void pointerEvent(CMsgWriter* writer, int x, int y, int buttonMask);
-      void handleTimer(CMsgWriter* writer, int timerId);
+      void pointerEvent(InputHandler* writer, const Point& pos, int buttonMask);
+      void handleTimer(InputHandler* writer, int timerId);
 
       void setHWND(HWND w) {intervalTimer.setHWND(w); threeTimer.setHWND(w);}
       void setIntervalTimerId(int id) {intervalTimer.setId(id);}
@@ -56,13 +54,13 @@
       bool emulate3;
       int pointerEventInterval;
 
-      void intervalPointerEvent(CMsgWriter* writer, int x, int y, int buttonMask);
+      void intervalPointerEvent(InputHandler* writer, const Point& pos, int buttonMask);
       IntervalTimer intervalTimer;
       bool intervalQueued;
       Point intervalPos;
       int intervalMask;
 
-      void threePointerEvent(CMsgWriter* writer, int x, int y, int buttonMask);
+      void threePointerEvent(InputHandler* writer, const Point& pos, int buttonMask);
       IntervalTimer threeTimer;
       Point threePos;
       int threeMask;
diff --git a/rfb_win32/CleanDesktop.cxx b/rfb_win32/CleanDesktop.cxx
index 9fb8347..39cca11 100644
--- a/rfb_win32/CleanDesktop.cxx
+++ b/rfb_win32/CleanDesktop.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -18,16 +18,22 @@
 
 // -=- CleanDesktop.cxx
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <wininet.h>
 #include <shlobj.h>
-
 #include <rfb_win32/CleanDesktop.h>
 #include <rfb_win32/CurrentUser.h>
 #include <rfb_win32/Registry.h>
+#include <rfb_win32/OSVersion.h>
 #include <rfb/LogWriter.h>
 #include <rdr/Exception.h>
+#include <set>
+
+#ifdef SPI_GETUIEFFECTS
+#define RFB_HAVE_SPI_UIEFFECTS
+#else
+#pragma message("  NOTE: Not building Get/Set UI Effects support.")
+#endif
 
 using namespace rfb;
 using namespace rfb::win32;
@@ -47,37 +53,93 @@
     if (handle)
       handle->Release();
   }
+
+  // enableItem
+  //   enables or disables the Nth Active Desktop item
+  bool enableItem(int i, bool enable_) {
+    COMPONENT item;
+    memset(&item, 0, sizeof(item));
+    item.dwSize = sizeof(item);
+
+    HRESULT hr = handle->GetDesktopItem(i, &item, 0);
+    if (hr != S_OK) {
+      vlog.error("unable to GetDesktopItem %d: %ld", i, hr);
+      return false;
+    }
+    item.fChecked = enable_;
+    vlog.debug("%sbling %d: \"%s\"", enable_ ? "ena" : "disa", i, (const char*)CStr(item.wszFriendlyName));
+
+    hr = handle->ModifyDesktopItem(&item, COMP_ELEM_CHECKED);
+    return hr == S_OK;
+  }
+  
+  // enable
+  //   Attempts to enable/disable Active Desktop, returns true if the setting changed,
+  //   false otherwise.
+  //   If Active Desktop *can* be enabled/disabled then that is done.
+  //   If Active Desktop is always on (XP/2K3) then instead the individual items are
+  //   disabled, and true is returned to indicate that they need to be restored later.
   bool enable(bool enable_) {
-    // - Get the current Active Desktop options
+    bool modifyComponents = false;
+
+    vlog.debug("ActiveDesktop::enable");
+
+    // - Firstly, try to disable Active Desktop entirely
+    HRESULT hr;
     COMPONENTSOPT adOptions;
     memset(&adOptions, 0, sizeof(adOptions));
     adOptions.dwSize = sizeof(adOptions);
 
-    HRESULT result = handle->GetDesktopItemOptions(&adOptions, 0);
-    if (result != S_OK) {
-      vlog.error("failed to get Active Desktop options: %d", result);
+    // Attempt to actually disable/enable AD
+    hr = handle->GetDesktopItemOptions(&adOptions, 0);
+    if (hr == S_OK) {
+      // If Active Desktop is already in the desired state then return false (no change)
+      // NB: If AD is enabled AND restoreItems is set then we regard it as disabled...
+      if (((adOptions.fActiveDesktop==0) && restoreItems.empty()) == (enable_==false))
+        return false;
+      adOptions.fActiveDesktop = enable_;
+      hr = handle->SetDesktopItemOptions(&adOptions, 0);
+    }
+    // Apply the change, then test whether it actually took effect
+    if (hr == S_OK)
+      hr = handle->ApplyChanges(AD_APPLY_REFRESH);
+    if (hr == S_OK)
+      hr = handle->GetDesktopItemOptions(&adOptions, 0);
+    if (hr == S_OK)
+      modifyComponents = (adOptions.fActiveDesktop==0) != (enable_==false);
+    if (hr != S_OK) {
+      vlog.error("failed to get/set Active Desktop options: %ld", hr);
       return false;
     }
 
-    // - If Active Desktop is active, disable it
-    if ((adOptions.fActiveDesktop==0) != (enable_==0)) {
-      if (enable_)
-        vlog.debug("enabling Active Desktop");
-      else
-        vlog.debug("disabling Active Desktop");
-
-      adOptions.fActiveDesktop = enable_;
-      result = handle->SetDesktopItemOptions(&adOptions, 0);
-      if (result != S_OK) {
-        vlog.error("failed to disable ActiveDesktop: %d", result);
+    if (enable_) {
+      // - We are re-enabling Active Desktop.  If there are components in restoreItems
+      //   then restore them!
+      std::set<int>::const_iterator i;
+      for (i=restoreItems.begin(); i!=restoreItems.end(); i++) {
+        enableItem(*i, true);
+      }
+      restoreItems.clear();
+    } else if (modifyComponents) {
+      // - Disable all currently enabled items, and add the disabled ones to restoreItems
+      int itemCount = 0;
+      hr = handle->GetDesktopItemCount(&itemCount, 0);
+      if (hr != S_OK) {
+        vlog.error("failed to get desktop item count: %ld", hr);
         return false;
       }
-      handle->ApplyChanges(AD_APPLY_REFRESH);
-      return true;
+      for (unsigned int i=0; i<itemCount; i++) {
+        if (enableItem(i, false))
+          restoreItems.insert(i);
+      }
     }
-    return false;
+
+    // - Apply whatever changes we have made, but DON'T save them!
+    hr = handle->ApplyChanges(AD_APPLY_REFRESH);
+    return hr == S_OK;
   }
   IActiveDesktop* handle;
+  std::set<int> restoreItems;
 };
 
 
@@ -91,7 +153,8 @@
 }
 
 
-CleanDesktop::CleanDesktop() : restoreActiveDesktop(false), restoreWallpaper(false), restoreEffects(false) {
+CleanDesktop::CleanDesktop() : restoreActiveDesktop(false), restoreWallpaper(false),
+                               restorePattern(false), restoreEffects(false) {
   CoInitialize(0);
 }
 
@@ -194,13 +257,13 @@
 
 
 void CleanDesktop::disableEffects() {
-#if (WINVER >= 0x500)
   try {
     ImpersonateCurrentUser icu;
 
     vlog.debug("disable desktop effects");
 
     SysParamsInfo(SPI_SETFONTSMOOTHING, FALSE, 0, SPIF_SENDCHANGE);
+#ifdef RFB_HAVE_SPI_UIEFFECTS
     if (SysParamsInfo(SPI_GETUIEFFECTS, 0, &uiEffects, 0) == ERROR_CALL_NOT_IMPLEMENTED) {
       SysParamsInfo(SPI_GETCOMBOBOXANIMATION, 0, &comboBoxAnim, 0);
       SysParamsInfo(SPI_GETGRADIENTCAPTIONS, 0, &gradientCaptions, 0);
@@ -214,21 +277,23 @@
       SysParamsInfo(SPI_SETMENUANIMATION, 0, FALSE, SPIF_SENDCHANGE);
     } else {
       SysParamsInfo(SPI_SETUIEFFECTS, 0, FALSE, SPIF_SENDCHANGE);
+
+      // We *always* restore UI effects overall, since there is no Windows GUI to do it
+      uiEffects = TRUE;
     }
+#else
+    vlog.debug("  not supported");
+#endif
     restoreEffects = true;
 
   } catch (rdr::Exception& e) {
     vlog.info(e.str());
   }
-#else
-      vlog.info("disableffects not implemented");
-#endif
 }
 
 void CleanDesktop::enableEffects() {
   try {
     if (restoreEffects) {
-#if (WINVER >= 0x500)
       ImpersonateCurrentUser icu;
 
       vlog.debug("restore desktop effects");
@@ -236,6 +301,7 @@
       RegKey desktopCfg;
       desktopCfg.openKey(HKEY_CURRENT_USER, _T("Control Panel\\Desktop"));
       SysParamsInfo(SPI_SETFONTSMOOTHING, desktopCfg.getInt(_T("FontSmoothing"), 0) != 0, 0, SPIF_SENDCHANGE);
+#ifdef RFB_HAVE_SPI_UIEFFECTS
       if (SysParamsInfo(SPI_SETUIEFFECTS, 0, (void*)uiEffects, SPIF_SENDCHANGE) == ERROR_CALL_NOT_IMPLEMENTED) {
         SysParamsInfo(SPI_SETCOMBOBOXANIMATION, 0, (void*)comboBoxAnim, SPIF_SENDCHANGE);
         SysParamsInfo(SPI_SETGRADIENTCAPTIONS, 0, (void*)gradientCaptions, SPIF_SENDCHANGE);
@@ -245,7 +311,7 @@
       }
       restoreEffects = false;
 #else
-      vlog.info("enableEffects not implemented");
+      vlog.info("  not supported");
 #endif
     }
 
diff --git a/rfb_win32/CleanDesktop.h b/rfb_win32/CleanDesktop.h
index 73f4153..22e246f 100644
--- a/rfb_win32/CleanDesktop.h
+++ b/rfb_win32/CleanDesktop.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb_win32/Clipboard.cxx b/rfb_win32/Clipboard.cxx
index 867f885..a4c43f0 100644
--- a/rfb_win32/Clipboard.cxx
+++ b/rfb_win32/Clipboard.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -61,15 +61,16 @@
 
 
 //
-// -=- ASCII filter (in-place)
+// -=- ISO-8859-1 (Latin 1) filter (in-place)
 //
 
 void
-removeNonAsciiChars(char* text) {
+removeNonISOLatin1Chars(char* text) {
   int len = strlen(text);
   int i=0, j=0;
   for (; i<len; i++) {
-    if ((text[i] >= 1) && (text[i] <= 127))
+    if (((text[i] >= 1) && (text[i] <= 127)) ||
+        ((text[i] >= 160) && (text[i] <= 255)))
       text[j++] = text[i];
   }
   text[j] = 0;
@@ -126,7 +127,7 @@
               } else {
                 CharArray unix_text;
                 unix_text.buf = dos2unix(clipdata);
-                // removeNonAsciiChars(unix_text.buf);
+                removeNonISOLatin1Chars(unix_text.buf);
                 notifier->notifyClipboardChanged(unix_text.buf, strlen(unix_text.buf));
               }
             } else {
@@ -162,7 +163,7 @@
     // - Pre-process the supplied clipboard text into DOS format
     CharArray dos_text;
     dos_text.buf = unix2dos(text);
-    // removeNonAsciiChars(dos_text.buf); 
+    removeNonISOLatin1Chars(dos_text.buf);
     int dos_text_len = strlen(dos_text.buf);
 
     // - Allocate global memory for the data
diff --git a/rfb_win32/Clipboard.h b/rfb_win32/Clipboard.h
index 57297e1..b79768f 100644
--- a/rfb_win32/Clipboard.h
+++ b/rfb_win32/Clipboard.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb_win32/CompatibleBitmap.h b/rfb_win32/CompatibleBitmap.h
new file mode 100644
index 0000000..4beed8d
--- /dev/null
+++ b/rfb_win32/CompatibleBitmap.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 2002-2005 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_WIN32_COMPAT_BITMAP_H__
+#define __RFB_WIN32_COMPAT_BITMAP_H__
+
+#include <windows.h>
+#include <rdr/Exception.h>
+
+namespace rfb {
+  namespace win32 {
+
+    class CompatibleBitmap {
+    public:
+      CompatibleBitmap(HDC hdc, int width, int height) {
+        hbmp = CreateCompatibleBitmap(hdc, width, height);
+        if (!hbmp)
+          throw rdr::SystemException("CreateCompatibleBitmap() failed", GetLastError());
+      }
+      virtual ~CompatibleBitmap() {
+        if (hbmp) DeleteObject(hbmp);
+      }
+      operator HBITMAP() const {return hbmp;}
+    protected:
+      HBITMAP hbmp;
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/ComputerName.h b/rfb_win32/ComputerName.h
new file mode 100644
index 0000000..110caa5
--- /dev/null
+++ b/rfb_win32/ComputerName.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2002-2005 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_WIN32_COMPUTERNAME_H__
+#define __RFB_WIN32_COMPUTERNAME_H__
+
+#include <windows.h>
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+  namespace win32 {
+
+    // Get the computer name
+    struct ComputerName : TCharArray {
+      ComputerName() : TCharArray(MAX_COMPUTERNAME_LENGTH+1) {
+        ULONG namelength = MAX_COMPUTERNAME_LENGTH+1;
+        if (!GetComputerName(buf, &namelength))
+          _tcscpy(buf, _T(""));
+      }
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/CurrentUser.cxx b/rfb_win32/CurrentUser.cxx
index 1a24485..7562d29 100644
--- a/rfb_win32/CurrentUser.cxx
+++ b/rfb_win32/CurrentUser.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -18,48 +18,98 @@
 
 // -=- Currentuser.cxx
 
-#include <windows.h>
-#include <lmcons.h>
+#include <stdlib.h>
+#include <rfb/LogWriter.h>
 #include <rfb_win32/CurrentUser.h>
+#include <rfb_win32/DynamicFn.h>
 #include <rfb_win32/Service.h>
 #include <rfb_win32/OSVersion.h>
+#include <lmcons.h>
 
 using namespace rfb;
 using namespace win32;
 
+static LogWriter vlog("CurrentUser");
 
-CurrentUserToken::CurrentUserToken() : isValid_(false) {
-  // - If the OS doesn't support security, ignore it
-  if (!isServiceProcess()) {
-    // - Running in User-Mode - just get our current token
-    if (!OpenProcessToken(GetCurrentProcess(), GENERIC_ALL, &h)) {
-      DWORD err = GetLastError();
-      if (err != ERROR_CALL_NOT_IMPLEMENTED)
-       throw rdr::SystemException("OpenProcessToken failed", GetLastError());
+
+const TCHAR* shellIconClass = _T("Shell_TrayWnd");
+
+BOOL CALLBACK enumWindows(HWND hwnd, LPARAM lParam) {
+  TCHAR className[16];
+  if (GetClassName(hwnd, className, sizeof(className)) &&
+      (_tcscmp(className, shellIconClass) == 0)) {
+    vlog.debug("located tray icon window (%s)", (const char*)CStr(className));
+    DWORD processId = 0;
+    GetWindowThreadProcessId(hwnd, &processId);
+    if (!processId)
+      return TRUE;
+    Handle process = OpenProcess(MAXIMUM_ALLOWED, FALSE, processId);
+    if (!process.h)
+      return TRUE;
+    if (!OpenProcessToken(process, MAXIMUM_ALLOWED, (HANDLE*)lParam))
+      return TRUE;
+    vlog.debug("obtained user token");
+    return FALSE;
+  }
+  return TRUE;
+}
+
+BOOL CALLBACK enumDesktops(LPTSTR lpszDesktop, LPARAM lParam) {
+  HDESK desktop = OpenDesktop(lpszDesktop, 0, FALSE, DESKTOP_ENUMERATE);
+  vlog.debug("opening \"%s\"", lpszDesktop);
+  if (!desktop) {
+    vlog.info("desktop \"%s\" inaccessible", (const char*)CStr(lpszDesktop));
+    return TRUE;
+  }
+  BOOL result = EnumDesktopWindows(desktop, enumWindows, lParam);
+  if (!CloseDesktop(desktop))
+    vlog.info("unable to close desktop: %ld", GetLastError());
+  return result;
+}
+
+
+CurrentUserToken::CurrentUserToken() : isSafe_(false) {
+  if (isServiceProcess()) {
+    // If the platform is Windows 95/98/Me then we must fake the token's presence
+    if (osVersion.isPlatformWindows) {
+      try {
+        UserName un;
+        h = INVALID_HANDLE_VALUE;
+      } catch (rdr::SystemException& e) {
+        if (e.err != ERROR_NOT_LOGGED_ON)
+          throw;
+        if (FindWindow(shellIconClass, 0))
+          h = INVALID_HANDLE_VALUE;
+      }
+      isSafe_ = (h != 0);
+      return;
     }
-    isValid_ = true;
-  } else {
-    // - Under XP/2003 and above, we can just ask the operating system
+
+    // Try to get the user token using the Terminal Services APIs
+    //   NB: This will only work under XP/2003 and later
     typedef BOOL (WINAPI *WTSQueryUserToken_proto)(ULONG, PHANDLE);
     DynamicFn<WTSQueryUserToken_proto> _WTSQueryUserToken(_T("wtsapi32.dll"), "WTSQueryUserToken");
     if (_WTSQueryUserToken.isValid()) {
       (*_WTSQueryUserToken)(-1, &h);
-      isValid_ = true;
-    } else {
-      // - Under NT/2K we have to resort to a nasty hack...
-      HWND tray = FindWindow(_T("Shell_TrayWnd"), 0);
-      if (!tray)
-        return;
-      DWORD processId = 0;
-      GetWindowThreadProcessId(tray, &processId);
-      if (!processId)
-        return;
-      Handle process = OpenProcess(MAXIMUM_ALLOWED, FALSE, processId);
-      if (!process.h)
-        return;
-      OpenProcessToken(process, MAXIMUM_ALLOWED, &h);
-      isValid_ = true;
+      isSafe_ = true;
+      return;
     }
+
+    // Try to find the Shell Tray Icon window and take its token
+    //   NB: This will only work under NT/2K (and later, but they're dealt with above)
+    //   NB: If the shell is not running then this will return an Unsafe Null token.
+    EnumDesktops(GetProcessWindowStation(), enumDesktops, (LONG)&h);
+    isSafe_ = (h != 0);
+  } else {
+    // Try to open the security token for the User-Mode process
+    if (!OpenProcessToken(GetCurrentProcess(), GENERIC_ALL, &h)) {
+      DWORD err = GetLastError();
+      if (err != ERROR_CALL_NOT_IMPLEMENTED)
+        throw rdr::SystemException("OpenProcessToken failed", err);
+      // Under Windows 95/98/Me, we fake the handle value...
+      h = INVALID_HANDLE_VALUE;
+    }
+    isSafe_ = true;
   }
 }
 
@@ -68,8 +118,8 @@
   RegCloseKey(HKEY_CURRENT_USER);
   if (!isServiceProcess())
     return;
-  if (!token.isValid())
-    throw rdr::Exception("CurrentUserToken is not valid");
+  if (!token.canImpersonate())
+    throw rdr::Exception("Cannot impersonate unsafe or null token");
   if (!ImpersonateLoggedOnUser(token)) {
     DWORD err = GetLastError();
     if (err != ERROR_CALL_NOT_IMPLEMENTED)
@@ -83,6 +133,7 @@
     if (err != ERROR_CALL_NOT_IMPLEMENTED)
       exit(err);
   }
+  RegCloseKey(HKEY_CURRENT_USER);
 }
 
 
@@ -91,3 +142,11 @@
   if (!GetUserName(buf, &len))
     throw rdr::SystemException("GetUserName failed", GetLastError());
 }
+
+
+UserSID::UserSID() {
+  CurrentUserToken token;
+  if (!token.canImpersonate())
+    return;
+  setSID(Sid::FromToken(token.h));
+}
diff --git a/rfb_win32/CurrentUser.h b/rfb_win32/CurrentUser.h
index 469946b..794f27c 100644
--- a/rfb_win32/CurrentUser.h
+++ b/rfb_win32/CurrentUser.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -26,29 +26,41 @@
 #ifndef __RFB_WIN32_CURRENT_USER_H__
 #define __RFB_WIN32_CURRENT_USER_H__
 
-#include <rfb_win32/Service.h>
-#include <rfb_win32/OSVersion.h>
-#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/Handle.h>
+#include <rfb_win32/Security.h>
 
 namespace rfb {
 
   namespace win32 {
 
     // CurrentUserToken
-    //   h == 0
-    //     if platform is not NT *or* unable to get token
-    //     *or* if process is hosting service.
-    //   h != 0
-    //     if process is hosting service *and*
-    //     if platform is NT *and* able to get token.
-    //   isValid() == true
-    //     if platform is not NT *or* token is valid.
+    //   CurrentUserToken is a Handle containing the security token
+    //   for the currently logged-on user, or null if no user is
+    //   logged on.
+    //
+    //   Under Windows 95/98/Me, which don't support security tokens,
+    //   the token will be INVALID_HANDLE_VALUE if a user is logged on.
+    //
+    //   Under Windows NT/2K, it may be the case that the token is
+    //   null even when a user *is* logged on, because we use some hacks
+    //   to detect the user's token and sometimes they fail.  On these
+    //   platforms, isSafe() will return False if the token is null.
+    //
+    //   Under Windows XP, etc, isSafe() will always be True, and the token
+    //   will always be set to the currently logged on user's token.
+    //
+    //   canImpersonate() tests whether there is a user token that is safe
+    //   to impersonate.
+    //
+    //   noUserLoggedOn() tests whether there is *definitely* no user logged on.
 
     struct CurrentUserToken : public Handle {
       CurrentUserToken();
-      bool isValid() const {return isValid_;};
+      bool isSafe() const { return isSafe_; };
+      bool canImpersonate() const { return h && isSafe(); }
+      bool noUserLoggedOn() const { return !h && isSafe(); }
     private:
-      bool isValid_;
+      bool isSafe_;
     };
 
     // ImpersonateCurrentUser
@@ -66,11 +78,21 @@
 
     // UserName
     //   Returns the name of the user the thread is currently running as.
+    //   Raises a SystemException in case of error.
+    //   NB: Raises a SystemException with err == ERROR_NOT_LOGGED_ON if
+    //       running under Windows 9x/95/Me and no user is logged on.
 
     struct UserName : public TCharArray {
       UserName();
     };
 
+    // UserSID
+    //   Returns the SID of the currently logged-on user (i.e. the session user)
+
+    struct UserSID : public Sid {
+      UserSID();
+    };
+
   }
 
 }
diff --git a/rfb_win32/DIBSectionBuffer.cxx b/rfb_win32/DIBSectionBuffer.cxx
index 026e5ed..18ce3ea 100644
--- a/rfb_win32/DIBSectionBuffer.cxx
+++ b/rfb_win32/DIBSectionBuffer.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -15,16 +15,16 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
  * USA.
  */
-#include <rfb/LogWriter.h>
 
 #include <rfb_win32/DIBSectionBuffer.h>
-#include <rfb_win32/Win32Util.h>
-
+#include <rfb_win32/DeviceContext.h>
+#include <rfb_win32/BitmapInfo.h>
+#include <rfb/LogWriter.h>
 
 using namespace rfb;
 using namespace win32;
 
-static LogWriter vlog("DIBSection");
+static LogWriter vlog("DIBSectionBuffer");
 
 
 DIBSectionBuffer::DIBSectionBuffer(HWND window_)
@@ -53,7 +53,7 @@
   format = pf;
   recreateBuffer();
   if ((pf.bpp <= 8) && pf.trueColour) {
-    vlog.debug("creating %d-bit TrueColor palette", pf.depth);
+    vlog.info("creating %d-bit TrueColour palette", pf.depth);
     for (int i=0; i < (1<<(pf.depth)); i++) {
       palette[i].b = ((((i >> pf.blueShift) & pf.blueMax) * 65535) + pf.blueMax/2) / pf.blueMax;
       palette[i].g = ((((i >> pf.greenShift) & pf.greenMax) * 65535) + pf.greenMax/2) / pf.greenMax;
@@ -114,8 +114,6 @@
     bi.mask.green = format.greenMax << format.greenShift;
     bi.mask.blue = format.blueMax << format.blueShift;
 
-    vlog.debug("area=%d, bpp=%d", width_ * height_, format.bpp);
-
     // Create a DIBSection to draw into
     if (device)
       new_bitmap = ::CreateDIBSection(device, (BITMAPINFO*)&bi.bmiHeader, iUsage,
@@ -199,10 +197,13 @@
         format.depth++;
         bits = bits >> 1;
       }
+      if (format.depth > format.bpp)
+        throw Exception("Bad DIBSection format (depth exceeds bpp)");
     } else {
       // Set the DIBSection's palette
       refreshPalette();
     }
+
   }
 }
 
diff --git a/rfb_win32/DIBSectionBuffer.h b/rfb_win32/DIBSectionBuffer.h
index 51e2da3..ad1a310 100644
--- a/rfb_win32/DIBSectionBuffer.h
+++ b/rfb_win32/DIBSectionBuffer.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -25,9 +25,7 @@
 #ifndef __RFB_WIN32_DIB_SECTION_BUFFER_H__
 #define __RFB_WIN32_DIB_SECTION_BUFFER_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
-
 #include <rfb/PixelBuffer.h>
 #include <rfb/Region.h>
 #include <rfb/ColourMap.h>
diff --git a/rfb_win32/DeviceContext.cxx b/rfb_win32/DeviceContext.cxx
new file mode 100644
index 0000000..4f70a1b
--- /dev/null
+++ b/rfb_win32/DeviceContext.cxx
@@ -0,0 +1,188 @@
+/* Copyright (C) 2002-2005 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_win32/DeviceContext.h>
+#include <rfb_win32/CompatibleBitmap.h>
+#include <rfb_win32/BitmapInfo.h>
+#include <rdr/Exception.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace win32;
+
+
+static LogWriter vlog("DeviceContext");
+
+PixelFormat DeviceContext::getPF() const {
+  return getPF(dc);
+}
+
+PixelFormat DeviceContext::getPF(HDC dc) {
+  PixelFormat format;
+  CompatibleBitmap bitmap(dc, 1, 1);
+
+  // -=- Get the bitmap format information
+  BitmapInfo bi;
+  memset(&bi, 0, sizeof(bi));
+  bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+  bi.bmiHeader.biBitCount = 0;
+  if (!::GetDIBits(dc, bitmap, 0, 1, NULL, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) {
+    throw rdr::SystemException("unable to determine device pixel format", GetLastError());
+  }
+  if (!::GetDIBits(dc, bitmap, 0, 1, NULL, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) {
+    throw rdr::SystemException("unable to determine pixel shifts/palette", GetLastError());
+  }
+
+  // Set the initial format information
+  format.trueColour = bi.bmiHeader.biBitCount > 8;
+  format.bigEndian = 0;
+  format.bpp = bi.bmiHeader.biBitCount;
+
+  if (format.trueColour) {
+    DWORD rMask=0, gMask=0, bMask=0;
+
+    // Which true colour format is the DIB section using?
+    switch (bi.bmiHeader.biCompression) {
+    case BI_RGB:
+      // Default RGB layout
+      switch (bi.bmiHeader.biBitCount) {
+      case 16:
+        // RGB 555 - High Colour
+        vlog.info("16-bit High Colour");
+        rMask = 0x7c00;
+        bMask = 0x001f;
+        gMask = 0x03e0;
+        break;
+      case 24:
+      case 32:
+        // RGB 888 - True Colour
+        vlog.info("24/32-bit High Colour");
+        rMask = 0xff0000;
+        gMask = 0x00ff00;
+        bMask = 0x0000ff;
+        break;
+      default:
+        vlog.error("bits per pixel %u not supported", bi.bmiHeader.biBitCount);
+        throw rdr::Exception("unknown bits per pixel specified");
+      };
+      break;
+    case BI_BITFIELDS:
+      // Custom RGB layout
+      rMask = bi.mask.red;
+      gMask = bi.mask.green;
+      bMask = bi.mask.blue;
+      vlog.info("%lu-bit BitFields: (%lx, %lx, %lx)",
+                 bi.bmiHeader.biBitCount, rMask, gMask, bMask);
+      break;
+    };
+
+    // Convert the data we just retrieved
+    initMaxAndShift(rMask, &format.redMax, &format.redShift);
+    initMaxAndShift(gMask, &format.greenMax, &format.greenShift);
+    initMaxAndShift(bMask, &format.blueMax, &format.blueShift);
+
+    // Calculate the depth from the colour shifts
+    format.depth = 0;
+    Pixel bits = rMask | gMask | bMask;
+    while (bits) {
+      format.depth++;
+      bits = bits >> 1;
+    }
+
+    // Check that the depth & bpp are valid
+    if (format.depth > format.bpp) {
+      vlog.error("depth exceeds bits per pixel!");
+      format.bpp = format.depth;
+    }
+
+    // Correct the bits-per-pixel to something we're happy with
+    if (format.bpp <= 16)
+      format.bpp = 16;
+    else if (format.bpp <= 32)
+      format.bpp = 32;
+  } else {
+    // Palettised format - depth reflects number of colours,
+    // but bits-per-pixel is ALWAYS 8
+    format.depth = format.bpp;
+    if (format.bpp < 8)
+      format.bpp = 8;
+    vlog.info("%d-colour palettised", 1<<format.depth);
+  }
+
+  return format;
+}
+
+Rect DeviceContext::getClipBox() const {
+  return getClipBox(dc);
+}
+
+Rect DeviceContext::getClipBox(HDC dc) {
+  // Get the display dimensions
+  RECT cr;
+  if (!GetClipBox(dc, &cr))
+    throw rdr::SystemException("GetClipBox", GetLastError());
+  return Rect(cr.left, cr.top, cr.right, cr.bottom);
+}
+
+
+DeviceDC::DeviceDC(const TCHAR* deviceName) {
+  dc = ::CreateDC(_T("DISPLAY"), deviceName, NULL, NULL);
+  if (!dc)
+    throw rdr::SystemException("failed to create DeviceDC", GetLastError());
+}
+
+DeviceDC::~DeviceDC() {
+  if (dc)
+    DeleteDC(dc);
+}
+
+
+WindowDC::WindowDC(HWND wnd) : hwnd(wnd) {
+  dc = GetDC(wnd);
+  if (!dc)
+    throw rdr::SystemException("GetDC failed", GetLastError());
+}
+
+WindowDC::~WindowDC() {
+  if (dc)
+    ReleaseDC(hwnd, dc);
+}
+
+
+CompatibleDC::CompatibleDC(HDC existing) {
+  dc = CreateCompatibleDC(existing);
+  if (!dc)
+    throw rdr::SystemException("CreateCompatibleDC failed", GetLastError());
+}
+
+CompatibleDC::~CompatibleDC() {
+  if (dc)
+    DeleteDC(dc);
+}
+
+
+BitmapDC::BitmapDC(HDC hdc, HBITMAP hbitmap) : CompatibleDC(hdc){
+  oldBitmap = (HBITMAP)SelectObject(dc, hbitmap);
+  if (!oldBitmap)
+    throw rdr::SystemException("SelectObject to CompatibleDC failed",
+    GetLastError());
+}
+
+BitmapDC::~BitmapDC() {
+  SelectObject(dc, oldBitmap);
+}
diff --git a/rfb_win32/DeviceContext.h b/rfb_win32/DeviceContext.h
new file mode 100644
index 0000000..9d91cec
--- /dev/null
+++ b/rfb_win32/DeviceContext.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 2002-2005 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.
+ */
+
+// DeviceContext base class, wrapping Windows HDC, plus some
+// helper classes tailored to particular types of DC, such as
+// window and device DCs.
+
+#ifndef __RFB_WIN32_DEVICECONTEXT_H__
+#define __RFB_WIN32_DEVICECONTEXT_H__
+
+#include <windows.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/Rect.h>
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    // Base class, providing methods to get the bounding (clip) box,
+    // and the pixel format, and access to the HDC itself.
+    class DeviceContext {
+    public:
+      DeviceContext() : dc(0) {}
+      virtual ~DeviceContext() {}
+      operator HDC() const {return dc;}
+      PixelFormat getPF() const;
+      static PixelFormat getPF(HDC dc);
+      Rect getClipBox() const;
+      static Rect getClipBox(HDC dc);
+    protected:
+      HDC dc;
+    };
+
+    // -=- DeviceContext that opens a specific display device
+    class DeviceDC : public DeviceContext {
+    public:
+      DeviceDC(const TCHAR* deviceName);
+      ~DeviceDC();
+    };
+
+    // Get a DC for a particular window's client area.
+    class WindowDC : public DeviceContext {
+    public:
+      WindowDC(HWND wnd);
+      virtual ~WindowDC();
+    protected:
+      HWND hwnd;
+    };
+
+    // Create a new DC, compatible with an existing one.
+    class CompatibleDC : public DeviceContext {
+    public:
+      CompatibleDC(HDC existing);
+      virtual ~CompatibleDC();
+    };
+
+    // Create a new DC, compatible with an existing one, and
+    // select the specified bitmap into it.
+    class BitmapDC : public CompatibleDC {
+    public:
+      BitmapDC(HDC hdc, HBITMAP hbitmap);
+      ~BitmapDC();
+    protected:
+      HBITMAP oldBitmap;
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/DeviceFrameBuffer.cxx b/rfb_win32/DeviceFrameBuffer.cxx
index 70975c3..8da894e 100644
--- a/rfb_win32/DeviceFrameBuffer.cxx
+++ b/rfb_win32/DeviceFrameBuffer.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -21,25 +21,22 @@
 // The DeviceFrameBuffer class encapsulates the pixel data of the system
 // display.
 
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <assert.h>
-
 #include <vector>
-
-#include <rfb/LogWriter.h>
-#include <rfb/Exception.h>
-#include <rdr/types.h>
-#include <rfb/VNCServer.h>
 #include <rfb_win32/DeviceFrameBuffer.h>
-#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/DeviceContext.h>
 #include <rfb_win32/OSVersion.h>
+#include <rfb_win32/IconInfo.h>
+#include <rfb/VNCServer.h>
+#include <rfb/LogWriter.h>
 
-namespace rfb {
+using namespace rfb;
+using namespace win32;
 
-namespace win32 {
+static LogWriter vlog("DeviceFrameBuffer");
 
-static LogWriter vlog("FrameBuffer");
+BoolParameter DeviceFrameBuffer::useCaptureBlt("UseCaptureBlt",
+  "Use a slower capture method that ensures that alpha blended windows appear correctly",
+  true);
 
 
 // -=- DeviceFrameBuffer class
@@ -67,10 +64,7 @@
   // -=- Get the display dimensions and pixel format
 
   // Get the display dimensions
-  RECT cr;
-  if (!GetClipBox(device, &cr))
-    throw rdr::SystemException("GetClipBox", GetLastError());
-  deviceCoords = Rect(cr.left, cr.top, cr.right, cr.bottom);
+  deviceCoords = DeviceContext::getClipBox(device);
   if (!wRect.is_empty())
     deviceCoords = wRect.translate(deviceCoords.tl);
   int w = deviceCoords.width();
@@ -120,7 +114,7 @@
   // Note: Microsoft's documentation lies directly about CAPTUREBLT and claims it works on 98/ME
   //       If you try CAPTUREBLT on 98 then you get blank output...
   if (!::BitBlt(tmpDC, rect.tl.x, rect.tl.y, rect.width(), rect.height(), device, src.x, src.y,
-    osVersion.isPlatformNT ? CAPTUREBLT | SRCCOPY : SRCCOPY)) {
+    (osVersion.isPlatformNT && useCaptureBlt) ? (CAPTUREBLT | SRCCOPY) : SRCCOPY)) {
     if (ignoreGrabErrors)
       vlog.error("BitBlt failed:%ld", GetLastError());
     else
@@ -176,7 +170,7 @@
   // - If hCursor is null then there is no cursor - clear the old one
 
   if (hCursor == 0) {
-    server->setCursor(0, 0, 0, 0, 0, 0);
+    server->setCursor(0, 0, Point(), 0, 0);
     return;
   }
 
@@ -189,8 +183,10 @@
     BITMAP maskInfo;
     if (!GetObject(iconInfo.hbmMask, sizeof(BITMAP), &maskInfo))
       throw rdr::SystemException("GetObject() failed", GetLastError());
-
-    assert(maskInfo.bmPlanes == 1 && maskInfo.bmBitsPixel == 1);
+    if (maskInfo.bmPlanes != 1)
+      throw rdr::Exception("unsupported multi-plane cursor");
+    if (maskInfo.bmBitsPixel != 1)
+      throw rdr::Exception("unsupported cursor mask format");
 
     // - Create the cursor pixel buffer and mask storage
     //   NB: The cursor pixel buffer is NOT used here.  Instead, we
@@ -278,8 +274,7 @@
       memcpy(cursorBm.data, cursor.data, cursor.dataLen());
     }
 
-    server->setCursor(cursor.width(), cursor.height(),
-                      cursor.hotspot.x, cursor.hotspot.y,
+    server->setCursor(cursor.width(), cursor.height(), cursor.hotspot,
                       cursorBm.data, cursor.mask.buf);
   } catch (rdr::Exception& e) {
     vlog.error(e.str());
@@ -292,7 +287,3 @@
   if (!format.trueColour)
     copyDevicePaletteToDIB(device, this);
 }
-
-}; // namespace win32
-
-}; // namespace rfb
diff --git a/rfb_win32/DeviceFrameBuffer.h b/rfb_win32/DeviceFrameBuffer.h
index 5e97b22..7718c33 100644
--- a/rfb_win32/DeviceFrameBuffer.h
+++ b/rfb_win32/DeviceFrameBuffer.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -26,13 +26,12 @@
 #ifndef __RFB_WIN32_DEVICE_FRAME_BUFFER_H__
 #define __RFB_WIN32_DEVICE_FRAME_BUFFER_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
-
 #include <rfb_win32/DIBSectionBuffer.h>
 #include <rfb/Cursor.h>
 #include <rfb/Region.h>
 #include <rfb/Exception.h>
+#include <rfb/Configuration.h>
 
 namespace rfb {
 
@@ -85,6 +84,8 @@
       // Set whether grabRect should ignore errors or throw exceptions
       // Only set this if you are sure you'll capture the errors some other way!
       void setIgnoreGrabErrors(bool ie) {ignoreGrabErrors=ie;}
+      
+      static BoolParameter useCaptureBlt;
 
     protected:
       // Translate supplied Desktop coordinates into Device-relative coordinates
@@ -98,22 +99,6 @@
       bool ignoreGrabErrors;
     };
 
-    // -=- createDisplayDeviceFrameBuffer
-    // createDisplayDeviceFrameBuffer must be passed the name of a display device,
-    // and will return a new FrameBuffer object attached to that display
-    // device.
-    // If the device name is not specified then the default display is
-    // returned.
-
-    DeviceFrameBuffer *createDisplayDeviceFrameBuffer(const char *device=0);
-
-    // -=- createDisplayFrameBuffers
-    // Creates a set of framebuffers, one for each available display
-    // device.
-
-    typedef std::vector<DeviceFrameBuffer *> DeviceFrameBuffers;
-    void createDisplayDeviceFrameBuffers(DeviceFrameBuffers *fbs);
-
   };
 
 };
diff --git a/rfb_win32/Dialog.cxx b/rfb_win32/Dialog.cxx
index 157cf5f..398334f 100644
--- a/rfb_win32/Dialog.cxx
+++ b/rfb_win32/Dialog.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -25,9 +25,15 @@
 #include <rfb/LogWriter.h>
 #include <rdr/Exception.h>
 #include <rfb_win32/Win32Util.h>
+
 #ifdef _DIALOG_CAPTURE
+#ifdef PropSheet_IndexToId
 #include <rfb_win32/DeviceFrameBuffer.h>
 #include <extra/LoadBMP.cxx>
+#else
+#undef _DIALOG_CAPTURE
+#pragma message("  NOTE: Not building Dialog Capture support.")
+#endif
 #endif
 
 using namespace rfb;
@@ -208,6 +214,33 @@
 }
 
 
+// For some reason, DLGTEMPLATEEX isn't defined in the Windows headers - go figure...
+struct DLGTEMPLATEEX {
+  WORD dlgVer;
+  WORD signature;
+  DWORD helpID;
+  DWORD exStyle;
+  DWORD style;
+  WORD cDlgItems;
+  short x;
+  short y;
+  short cx;
+  short cy;
+};
+
+static int CALLBACK removeCtxtHelp(HWND hwnd, UINT message, LPARAM lParam) {
+  if (message == PSCB_PRECREATE) {
+    // Remove the context-help style, to remove the titlebar ? button
+    // *** Nasty hack to cope with new & old dialog template formats...
+    if (((DLGTEMPLATEEX*)lParam)->signature == 0xffff)
+      ((DLGTEMPLATEEX*)lParam)->style &= ~DS_CONTEXTHELP;
+    else
+      ((LPDLGTEMPLATE)lParam)->style &= ~DS_CONTEXTHELP;
+  }
+  return TRUE;
+}
+
+
 bool PropSheet::showPropSheet(HWND owner, bool showApply, bool showCtxtHelp, bool capture) {
   if (alreadyShowing) return false;
   alreadyShowing = true;
@@ -227,7 +260,8 @@
     // Initialise and create the PropertySheet itself
     PROPSHEETHEADER header;
     header.dwSize = PROPSHEETHEADER_V1_SIZE;
-    header.dwFlags = PSH_MODELESS | (showApply ? 0 : PSH_NOAPPLYNOW) /*| (showCtxtHelp ? 0 : PSH_NOCONTEXTHELP)*/;
+    header.dwFlags = PSH_MODELESS | (showApply ? 0 : PSH_NOAPPLYNOW) | (showCtxtHelp ? 0 : PSH_USECALLBACK);
+    header.pfnCallback = removeCtxtHelp;
     header.hwndParent = owner;
     header.hInstance = inst;
     header.pszCaption = title.buf;
@@ -245,9 +279,7 @@
     centerWindow(handle, owner);
     plog.info("created %lx", handle);
 
-#if (WINVER >= 0x0500)
 #ifdef _DIALOG_CAPTURE
-    // *** NOT TESTED
     if (capture) {
       plog.info("capturing \"%s\"", (const char*)CStr(title.buf));
       char* tmpdir = getenv("TEMP");
@@ -264,15 +296,23 @@
             DispatchMessage(&msg);
         }
         fb.grabRect(fb.getRect());
+        TCHAR title[128];
+        if (!GetWindowText(PropSheet_GetCurrentPageHwnd(handle), title, sizeof(title)))
+          _stprintf(title, _T("capture%d"), i);
+        CharArray pageTitle(strDup(title));
+        for (int j=0; j<strlen(pageTitle.buf); j++) {
+          if (pageTitle.buf[j] == '/' || pageTitle.buf[j] == '\\' || pageTitle.buf[j] == ':')
+            pageTitle.buf[j] = '-';
+        }
         char filename[256];
-        sprintf(filename, "%s\\capture%d.bmp", tmpdir, i);
+        sprintf(filename, "%s\\%s.bmp", tmpdir, pageTitle.buf);
+        vlog.debug("writing to %s", filename);
         saveBMP(filename, &fb);
         i++;
       }
       ReleaseDC(handle, dc);
     } else {
 #endif
-#endif
       try {
         if (owner)
           EnableWindow(owner, FALSE);
@@ -291,11 +331,9 @@
           EnableWindow(owner, TRUE);
         throw;
       }
-#if (WINVER >= 0x0500)
 #ifdef _DIALOG_CAPTURE
     }
 #endif
-#endif
 
     plog.info("finished %lx", handle);
 
diff --git a/rfb_win32/Dialog.h b/rfb_win32/Dialog.h
index d46133a..9784ba4 100644
--- a/rfb_win32/Dialog.h
+++ b/rfb_win32/Dialog.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -24,7 +24,6 @@
 #ifndef __RFB_WIN32_DIALOG_H__
 #define __RFB_WIN32_DIALOG_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <prsht.h>
 #include <list>
diff --git a/rfb_win32/DynamicFn.cxx b/rfb_win32/DynamicFn.cxx
new file mode 100644
index 0000000..e933f24
--- /dev/null
+++ b/rfb_win32/DynamicFn.cxx
@@ -0,0 +1,45 @@
+/* Copyright (C) 2002-2005 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_win32/DynamicFn.h>
+#include <rfb_win32/TCharArray.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("DynamicFn");
+
+
+DynamicFnBase::DynamicFnBase(const TCHAR* dllName, const char* fnName) : dllHandle(0), fnPtr(0) {
+  dllHandle = LoadLibrary(dllName);
+  if (!dllHandle) {
+    vlog.info("DLL %s not found (%d)", (const char*)CStr(dllName), GetLastError());
+    return;
+  }
+  fnPtr = GetProcAddress(dllHandle, fnName);
+  if (!fnPtr)
+    vlog.info("proc %s not found in %s (%d)", fnName, (const char*)CStr(dllName), GetLastError());
+}
+
+DynamicFnBase::~DynamicFnBase() {
+  if (dllHandle)
+    FreeLibrary(dllHandle);
+}
+
+
diff --git a/rfb_win32/DynamicFn.h b/rfb_win32/DynamicFn.h
new file mode 100644
index 0000000..57fdbec
--- /dev/null
+++ b/rfb_win32/DynamicFn.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2002-2005 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.
+ */
+
+// Helper class managing dynamic linkage to DLL functions.
+
+#ifndef __RFB_WIN32_DYNAMICFN_H__
+#define __RFB_WIN32_DYNAMICFN_H__
+
+#include <windows.h>
+
+namespace rfb {
+  namespace win32 {
+
+    class DynamicFnBase {
+    public:
+      DynamicFnBase(const TCHAR* dllName, const char* fnName);
+      ~DynamicFnBase();
+      bool isValid() const {return fnPtr != 0;}
+    protected:
+      void* fnPtr;
+      HMODULE dllHandle;
+    private:
+      DynamicFnBase(const DynamicFnBase&);
+      DynamicFnBase operator=(const DynamicFnBase&);
+    };
+
+    template<class T> class DynamicFn : public DynamicFnBase {
+    public:
+      DynamicFn(const TCHAR* dllName, const char* fnName) : DynamicFnBase(dllName, fnName) {}
+      T operator *() const {return (T)fnPtr;};
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/EventManager.cxx b/rfb_win32/EventManager.cxx
new file mode 100644
index 0000000..0f9993b
--- /dev/null
+++ b/rfb_win32/EventManager.cxx
@@ -0,0 +1,103 @@
+/* Copyright (C) 2002-2005 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_win32/EventManager.h>
+#include <rdr/Exception.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("EventManager");
+
+
+EventManager::EventManager() : eventCount(0) {
+}
+
+EventManager::~EventManager() {
+}
+
+
+bool EventManager::addEvent(HANDLE event, EventHandler* ecb) {
+  if (eventCount >= MAXIMUM_WAIT_OBJECTS-1)
+    return false;
+  events[eventCount] = event;
+  handlers[eventCount] = ecb;
+  eventCount++;
+  return true;
+}
+
+void EventManager::removeEvent(HANDLE event) {
+  for (int i=0; i<eventCount; i++) {
+    if (events[i] == event) {
+      for (int j=i; j<eventCount-1; j++) {
+        events[j] = events[j+1];
+        handlers[j] = handlers[j+1];
+      }
+      eventCount--;
+      return;
+    }
+  }
+  throw rdr::Exception("Event not registered");
+}
+
+
+int EventManager::checkTimeouts() {
+  return 0;
+}
+
+BOOL EventManager::getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg) {
+  while (true) {
+    // - Process any pending timeouts
+    DWORD timeout = checkTimeouts();
+    if (timeout == 0)
+      timeout = INFINITE;
+
+    // - Events take precedence over messages
+    DWORD result;
+    if (eventCount) {
+      // - Check whether any events are set
+      result = WaitForMultipleObjects(eventCount, events, FALSE, 0);
+      if (result == WAIT_TIMEOUT) {
+        // - No events are set, so check for messages
+        if (PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE)) 
+          return msg->message != WM_QUIT;
+
+        // - Block waiting for an event to be set, or a message
+        result = MsgWaitForMultipleObjects(eventCount, events, FALSE, timeout,
+                                           QS_ALLINPUT);
+        if (result == WAIT_OBJECT_0 + eventCount) {
+          // - Return the message, if any
+          if (PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE)) 
+            return msg->message != WM_QUIT;
+          continue;
+        }
+      }
+    } else
+      return GetMessage(msg, hwnd, minMsg, maxMsg);
+
+    if ((result >= WAIT_OBJECT_0) && (result < (WAIT_OBJECT_0 + eventCount))) {
+      // - An event was set - call the handler
+      int index = result - WAIT_OBJECT_0;
+      handlers[index]->processEvent(events[index]);
+    } else if (result == WAIT_FAILED) {
+      // - An error has occurred, so return the error status code
+      return -1;
+    }
+  }
+}
diff --git a/rfb_win32/EventManager.h b/rfb_win32/EventManager.h
new file mode 100644
index 0000000..bb66e34
--- /dev/null
+++ b/rfb_win32/EventManager.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2002-2005 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.
+ */
+
+// -=- EventManager.h
+
+// Win32 event manager.  Caller supplies event & handler pairs and
+// then uses getMessage() in place of ::GetMessage() in the main
+// loop.  EventManager calls the event handler whenever the event
+// is set.
+// Ownership of events remains with the caller.
+// It is the responsibility of handlers to reset events.
+
+#ifndef __RFB_WIN32_EVENT_MGR_H__
+#define __RFB_WIN32_EVENT_MGR_H__
+
+#include <rfb_win32/Win32Util.h>
+
+namespace rfb {
+  namespace win32 {
+
+    class EventHandler {
+    public:
+      virtual ~EventHandler() {}
+      virtual void processEvent(HANDLE event) = 0;
+    };
+
+    class EventManager {
+    public:
+      EventManager();
+      virtual ~EventManager();
+
+      // Add a Win32 event & handler for it
+      //   NB: The handler must call ResetEvent on the event.
+      //   NB: The caller retains ownership of the event.
+      virtual bool addEvent(HANDLE event, EventHandler* ecb);
+
+      // Remove a Win32 event
+      virtual void removeEvent(HANDLE event);
+
+      // getMessage
+      //   Waits for a message to become available on the thread's message queue,
+      //   and returns it.  If any registered events become set while waiting then
+      //   their handlers are called before returning.
+      //   Returns zero if the message is WM_QUIT, -1 in case of error, >0 otherwise.
+      virtual BOOL getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg);
+
+    protected:
+      // checkTimeouts
+      //   Derived classes should override this to perform any extra processing,
+      //   returning the maximum number of milliseconds after which the callback
+      //   should be called again.
+      virtual int checkTimeouts();
+
+      HANDLE events[MAXIMUM_WAIT_OBJECTS];
+      EventHandler* handlers[MAXIMUM_WAIT_OBJECTS-1];
+      int eventCount;
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb/vncAuth.h b/rfb_win32/Handle.h
similarity index 63%
copy from rfb/vncAuth.h
copy to rfb_win32/Handle.h
index 18d87ad..d3baa58 100644
--- a/rfb/vncAuth.h
+++ b/rfb_win32/Handle.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -15,17 +15,29 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
  * USA.
  */
-#ifndef __RFB_VNCAUTH_H__
-#define __RFB_VNCAUTH_H__
 
-#include <rdr/types.h>
+// Wrapper for Win32 HANDLEs that can/must be CloseHandle()d.
+
+#ifndef __RFB_WIN32_HANDLE_H__
+#define __RFB_WIN32_HANDLE_H__
+
+#include <windows.h>
 
 namespace rfb {
+  namespace win32 {
 
-  const int vncAuthChallengeSize = 16;
 
-  void vncAuthEncryptChallenge(rdr::U8* challenge, const char* passwd);
-  void vncAuthObfuscatePasswd(char* passwd);
-  void vncAuthUnobfuscatePasswd(char* passwd);
-}
+    class Handle {
+    public:
+      Handle(HANDLE h_=0) : h(h_) {}
+      ~Handle() {
+        if (h) CloseHandle(h);
+      }
+      operator HANDLE() {return h;}
+      HANDLE h;
+    };
+
+  };
+};
+
 #endif
diff --git a/rfb_win32/IconInfo.h b/rfb_win32/IconInfo.h
new file mode 100644
index 0000000..cb33a42
--- /dev/null
+++ b/rfb_win32/IconInfo.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2002-2005 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_WIN32_ICONINFO_H__
+#define __RFB_WIN32_ICONINFO_H__
+
+#include <windows.h>
+#include <rdr/Exception.h>
+
+namespace rfb {
+  namespace win32 {
+
+    struct IconInfo : public ICONINFO {
+      IconInfo(HICON icon) {
+        if (!GetIconInfo(icon, this))
+          throw rdr::SystemException("GetIconInfo() failed", GetLastError());
+      }
+      ~IconInfo() {
+        if (hbmColor)
+          DeleteObject(hbmColor);
+        if (hbmMask)
+          DeleteObject(hbmMask);
+      }
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/IntervalTimer.h b/rfb_win32/IntervalTimer.h
index 645d0ee..ddfae49 100644
--- a/rfb_win32/IntervalTimer.h
+++ b/rfb_win32/IntervalTimer.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb_win32/LaunchProcess.cxx b/rfb_win32/LaunchProcess.cxx
index 0a48d60..56a712e 100644
--- a/rfb_win32/LaunchProcess.cxx
+++ b/rfb_win32/LaunchProcess.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -19,9 +19,10 @@
 // -=- LaunchProcess.cxx
 
 #include <rfb_win32/LaunchProcess.h>
+#include <rfb_win32/ModuleFileName.h>
 #include <rfb_win32/Win32Util.h>
-
-#include <rfb/util.h>
+#include <rdr/Exception.h>
+#include <stdio.h>
 
 using namespace rfb;
 using namespace win32;
@@ -37,10 +38,11 @@
 }
 
 
-void LaunchProcess::start(HANDLE userToken) {
+void LaunchProcess::start(HANDLE userToken, bool createConsole) {
   if (procInfo.hProcess && (WaitForSingleObject(procInfo.hProcess, 0) != WAIT_OBJECT_0))
     return;
   await();
+  returnCode = STILL_ACTIVE;
 
   // - Create storage for the process startup information
   STARTUPINFO sinfo;
@@ -58,19 +60,15 @@
     exePath.buf = tstrDup(exeName.buf);
   }
 
-  // - Start the VNC server
+  // - Start the process
   // Note: We specify the exe's precise path in the ApplicationName parameter,
   //       AND include the name as the first part of the CommandLine parameter,
   //       because CreateProcess doesn't make ApplicationName argv[0] in C programs.
   TCharArray cmdLine(_tcslen(exeName.buf) + 3 + _tcslen(params.buf) + 1);
   _stprintf(cmdLine.buf, _T("\"%s\" %s"), exeName.buf, params.buf);
-#ifdef _DEBUG
-  DWORD flags = CREATE_NEW_CONSOLE;
-#else
-  DWORD flags = CREATE_NO_WINDOW;
-#endif
+  DWORD flags = createConsole ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW;
   BOOL success;
-  if (userToken)
+  if (userToken != INVALID_HANDLE_VALUE)
     success = CreateProcessAsUser(userToken, exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo);
   else
     success = CreateProcess(exePath.buf, cmdLine.buf, 0, 0, FALSE, flags, 0, 0, &sinfo, &procInfo);
@@ -81,12 +79,25 @@
   WaitForInputIdle(procInfo.hProcess, 15000);
 }
 
-void LaunchProcess::await() {
+void LaunchProcess::detach()
+{
   if (!procInfo.hProcess)
     return;
-  WaitForSingleObject(procInfo.hProcess, INFINITE);
   CloseHandle(procInfo.hProcess);
   CloseHandle(procInfo.hThread);
   memset(&procInfo, 0, sizeof(procInfo));
 }
 
+bool LaunchProcess::await(DWORD timeoutMs) {
+  if (!procInfo.hProcess)
+    return true;
+  DWORD result = WaitForSingleObject(procInfo.hProcess, timeoutMs);
+  if (result == WAIT_OBJECT_0) {
+    GetExitCodeProcess(procInfo.hProcess, &returnCode);
+    detach();
+    return true;
+  } else if (result == WAIT_FAILED) {
+    throw rdr::SystemException("await() failed", GetLastError());
+  }
+  return false;
+}
diff --git a/rfb_win32/LaunchProcess.h b/rfb_win32/LaunchProcess.h
index 6fd34e9..38521dc 100644
--- a/rfb_win32/LaunchProcess.h
+++ b/rfb_win32/LaunchProcess.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -24,9 +24,7 @@
 #ifndef __RFB_WIN32_LAUNCHPROCESS_H__
 #define __RFB_WIN32_LAUNCHPROCESS_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
-
 #include <rfb_win32/TCharArray.h>
 
 namespace rfb {
@@ -38,14 +36,28 @@
       LaunchProcess(const TCHAR* exeName_, const TCHAR* params);
       ~LaunchProcess();
 
-      // If userToken is 0 then starts as current user, otherwise
-      // starts as the specified user.  userToken must be a primary token.
-      void start(HANDLE userToken);
+      // start() starts the specified process with the supplied
+      //   command-line.
+      //   If userToken is INVALID_HANDLE_VALUE then starts the process
+      //   as the current user, otherwise as the specified user.
+      //   If createConsole is true then CREATE_CONSOLE_WINDOW is passed
+      //   as an extra flag to the process creation call.
+      void start(HANDLE userToken, bool createConsole=false);
 
-      // Wait for the process to quit, and close the handles to it.
-      void await();
+      // Detatch from the child process. After detatching from a child
+      //   process, no other methods should be called on the object
+      //   that started it
+      void detach();
+
+      // Wait for the process to quit, up to the specified timeout, and
+      //   close the handles to it once it has quit.
+      //   If the process quits within the timeout then true is returned
+      //   and returnCode is set. If it has not quit then false is returned.
+      //   If an error occurs then an exception will be thrown.
+      bool await(DWORD timeoutMs=INFINITE);
 
       PROCESS_INFORMATION procInfo;
+      DWORD returnCode;
     protected:
       TCharArray exeName;
       TCharArray params;
diff --git a/rfb_win32/LocalMem.h b/rfb_win32/LocalMem.h
new file mode 100644
index 0000000..a99d324
--- /dev/null
+++ b/rfb_win32/LocalMem.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 2002-2005 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_WIN32_LOCALMEM_H__
+#define __RFB_WIN32_LOCALMEM_H__
+
+#include <windows.h>
+#include <rdr/Exception.h>
+
+namespace rfb {
+  namespace win32 {
+
+    // Allocate and/or manage LocalAlloc memory.
+    struct LocalMem {
+      LocalMem(int size) : ptr(LocalAlloc(LMEM_FIXED, size)) {
+        if (!ptr) throw rdr::SystemException("LocalAlloc", GetLastError());
+      }
+      LocalMem(void* p) : ptr(p) {}
+      ~LocalMem() {LocalFree(ptr);}
+      operator void*() {return ptr;}
+      void* takePtr() {
+        void* t = ptr; ptr = 0; return t;
+      }
+      void* ptr;
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/LogicalPalette.h b/rfb_win32/LogicalPalette.h
new file mode 100644
index 0000000..204f108
--- /dev/null
+++ b/rfb_win32/LogicalPalette.h
@@ -0,0 +1,90 @@
+/* Copyright (C) 2002-2005 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_WIN32_LOGPALETTE_H__
+#define __RFB_WIN32_LOGPALETTE_H__
+
+#include <windows.h>
+#include <rdr/Exception.h>
+
+namespace rfb {
+  namespace win32 {
+
+    class LogicalPalette {
+    public:
+      LogicalPalette() {
+        BYTE buf[sizeof(LOGPALETTE)+256*sizeof(PALETTEENTRY)];
+        LOGPALETTE* logpal = (LOGPALETTE*)buf;
+        logpal->palVersion = 0x300;
+        logpal->palNumEntries = 256;
+        for (int i=0; i<256;i++) {
+          logpal->palPalEntry[i].peRed = 0;
+          logpal->palPalEntry[i].peGreen = 0;
+          logpal->palPalEntry[i].peBlue = 0;
+          logpal->palPalEntry[i].peFlags = 0;
+        }
+        palette = CreatePalette(logpal);
+        if (!palette)
+          throw rdr::SystemException("failed to CreatePalette", GetLastError());
+      }
+      ~LogicalPalette() {
+        if (palette && !DeleteObject(palette))
+          throw rdr::SystemException("del palette failed", GetLastError());
+      }
+      void setEntries(int start, int count, const Colour* cols) {
+        if (numEntries < count) {
+          ResizePalette(palette, start+count);
+          numEntries = start+count;
+        }
+        PALETTEENTRY* logpal = new PALETTEENTRY[count];
+        for (int i=0; i<count; i++) {
+          logpal[i].peRed = cols[i].r >> 8;
+          logpal[i].peGreen = cols[i].g >> 8;
+          logpal[i].peBlue = cols[i].b >> 8;
+          logpal[i].peFlags = 0;
+        }
+        UnrealizeObject(palette);
+        SetPaletteEntries(palette, start, count, logpal);
+        delete [] logpal;
+      }
+      HPALETTE getHandle() {return palette;}
+    protected:
+      HPALETTE palette;
+      int numEntries;
+    };
+
+    class PaletteSelector {
+    public:
+      PaletteSelector(HDC dc, HPALETTE pal) : device(dc), redrawRequired(false) {
+        oldPal = SelectPalette(dc, pal, FALSE);
+        redrawRequired = RealizePalette(dc) > 0;
+      }
+      ~PaletteSelector() {
+        if (oldPal) SelectPalette(device, oldPal, TRUE);
+      }
+      bool isRedrawRequired() {return redrawRequired;}
+    protected:
+      HPALETTE oldPal;
+      HDC device;
+      bool redrawRequired;
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/LowLevelKeyEvents.cxx b/rfb_win32/LowLevelKeyEvents.cxx
new file mode 100644
index 0000000..322d1f4
--- /dev/null
+++ b/rfb_win32/LowLevelKeyEvents.cxx
@@ -0,0 +1,96 @@
+/* Copyright (C) 2002-2005 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 <windows.h>
+#include <rfb_win32/LowLevelKeyEvents.h>
+#include <rfb/Threading.h>
+#include <rfb/LogWriter.h>
+#include <list>
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("LowLevelKeyEvents");
+
+
+HHOOK hook = 0;
+std::list<HWND> windows;
+Mutex windowLock;
+
+
+static bool filterKeyEvent(int vkCode) {
+  switch (vkCode) {
+  case VK_LWIN:
+  case VK_RWIN:
+  case VK_SNAPSHOT:
+    return true;
+  case VK_TAB:
+    if (GetAsyncKeyState(VK_MENU) & 0x8000)
+      return true;
+  case VK_ESCAPE:
+    if (GetAsyncKeyState(VK_MENU) & 0x8000)
+      return true;
+    if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
+      return true;
+  }
+  return false;
+}
+
+LRESULT CALLBACK LowLevelKeyEventProc(int nCode,
+                                      WPARAM wParam,
+                                      LPARAM lParam) {
+  if (nCode >= 0) {
+    Lock l(windowLock);
+    HWND foreground = GetForegroundWindow();
+    std::list<HWND>::iterator i;
+    for (i=windows.begin(); i!=windows.end(); i++) {
+      if (*i == foreground) {
+        UINT msgType = wParam;
+        KBDLLHOOKSTRUCT* msgInfo = (KBDLLHOOKSTRUCT*)lParam;
+        if (filterKeyEvent(msgInfo->vkCode)) {
+          vlog.debug("filtered event %lx(%lu) %lu", msgInfo->vkCode, msgInfo->vkCode, wParam);
+          PostMessage(*i, wParam, msgInfo->vkCode, (msgInfo->scanCode & 0xff) << 16);
+          return 1;
+        }
+      }
+    }
+  }
+  return CallNextHookEx(hook, nCode, wParam, lParam);
+}
+
+
+bool rfb::win32::enableLowLevelKeyEvents(HWND hwnd) {
+// ***  return false; // *** THIS CODE IS EXPERIMENTAL, SO DISABLED BY DEFAULT!
+  Lock l(windowLock);
+  if (windows.empty() && !hook)
+    hook = SetWindowsHookEx(WH_KEYBOARD_LL, &LowLevelKeyEventProc, GetModuleHandle(0), 0);
+  if (hook)
+    windows.push_back(hwnd);
+  vlog.debug("enable %p -> %s", hwnd, hook ? "success" : "failure");
+  return hook != 0;
+}
+
+void rfb::win32::disableLowLevelKeyEvents(HWND hwnd) {
+  vlog.debug("disable %p", hwnd);
+  Lock l(windowLock);
+  windows.remove(hwnd);
+  if (windows.empty() && hook) {
+    UnhookWindowsHookEx(hook);
+    hook = 0;
+  }
+}
diff --git a/rfb_win32/LowLevelKeyEvents.h b/rfb_win32/LowLevelKeyEvents.h
new file mode 100644
index 0000000..40d2ecf
--- /dev/null
+++ b/rfb_win32/LowLevelKeyEvents.h
@@ -0,0 +1,49 @@
+/* Copyright (C) 2002-2005 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.
+ */
+
+// -=- LowLevelKeyEvents.h
+//
+// This interface allows keyboard events destined for a particular window
+// to be intercepted early in the keyboard message queue and posted directly
+// to the window.  This is used to avoid having the operating system process
+// keys such as VK_LWIN, VK_RWIN, etc.
+//
+
+#ifndef __RFB_WIN32_LOW_LEVEL_KEY_EVENTS_H__
+#define __RFB_WIN32_LOW_LEVEL_KEY_EVENTS_H__
+
+namespace rfb {
+
+  namespace win32 {
+
+    // enableLowLevelKeyEvents
+    //   Specifies that keyboard events destined for the specified window should
+    //   be posted directly to the window, rather than being passed via the normal
+    //   Windows keyboard message queue.
+    bool enableLowLevelKeyEvents(HWND hwnd);
+
+    // disableLowLevelKeyEvents
+    //   Causes the specified window to revert to the normal Windows keyboard
+    //   event processing mechanism.
+    void disableLowLevelKeyEvents(HWND hwnd);
+
+  };
+
+};
+
+#endif // __RFB_WIN32_LOW_LEVEL_KEY_EVENTS_H__
diff --git a/rfb_win32/ModuleFileName.h b/rfb_win32/ModuleFileName.h
new file mode 100644
index 0000000..2264e89
--- /dev/null
+++ b/rfb_win32/ModuleFileName.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2002-2005 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_WIN32_MODULE_FILENAME_H__
+#define __RFB_WIN32_MODULE_FILENAME_H__
+
+#include <windows.h>
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+  namespace win32 {
+
+    struct ModuleFileName : public TCharArray {
+      ModuleFileName(HMODULE module=0) : TCharArray(MAX_PATH) {
+        if (!module)
+          module = GetModuleHandle(0);
+        if (!GetModuleFileName(module, buf, MAX_PATH))
+          buf[0] = 0;
+      }
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/MonitorInfo.cxx b/rfb_win32/MonitorInfo.cxx
new file mode 100644
index 0000000..03772e9
--- /dev/null
+++ b/rfb_win32/MonitorInfo.cxx
@@ -0,0 +1,205 @@
+/* Copyright (C) 2002-2005 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_win32/DynamicFn.h>
+#include <rfb_win32/MonitorInfo.h>
+#include <rfb_win32/Win32Util.h>
+#include <rdr/Exception.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("MonitorInfo");
+
+
+// If we are building in multi-monitor support (i.e. the headers support it)
+//   then do dynamic imports of the required system calls, and provide any
+//   other code that wouldn't otherwise compile.
+#ifdef RFB_HAVE_MONITORINFO
+#include <tchar.h>
+typedef HMONITOR (WINAPI *_MonitorFromWindow_proto)(HWND,DWORD);
+static rfb::win32::DynamicFn<_MonitorFromWindow_proto> _MonitorFromWindow(_T("user32.dll"), "MonitorFromWindow");
+typedef HMONITOR (WINAPI *_MonitorFromRect_proto)(LPCRECT,DWORD);
+static rfb::win32::DynamicFn<_MonitorFromRect_proto> _MonitorFromRect(_T("user32.dll"), "MonitorFromRect");
+typedef BOOL (WINAPI *_GetMonitorInfo_proto)(HMONITOR,LPMONITORINFO);
+static rfb::win32::DynamicFn<_GetMonitorInfo_proto> _GetMonitorInfo(_T("user32.dll"), "GetMonitorInfoA");
+typedef BOOL (WINAPI *_EnumDisplayMonitors_proto)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
+static rfb::win32::DynamicFn<_EnumDisplayMonitors_proto> _EnumDisplayMonitors(_T("user32.dll"), "EnumDisplayMonitors");
+static void fillMonitorInfo(HMONITOR monitor, MonitorInfo* mi) {
+  vlog.debug("monitor=%lx", monitor);
+  if (!_GetMonitorInfo.isValid())
+    throw rdr::Exception("no GetMonitorInfo");
+  memset(mi, 0, sizeof(MONITORINFOEXA));
+  mi->cbSize = sizeof(MONITORINFOEXA);
+  if (!(*_GetMonitorInfo)(monitor, mi))
+    throw rdr::SystemException("failed to GetMonitorInfo", GetLastError());
+  vlog.debug("monitor is %d,%d-%d,%d", mi->rcMonitor.left, mi->rcMonitor.top, mi->rcMonitor.right, mi->rcMonitor.bottom);
+  vlog.debug("work area is %d,%d-%d,%d", mi->rcWork.left, mi->rcWork.top, mi->rcWork.right, mi->rcWork.bottom);
+  vlog.debug("device is \"%s\"", mi->szDevice);
+}
+#else
+#pragma message("  NOTE: Not building Multi-Monitor support.")
+#endif
+
+
+MonitorInfo::MonitorInfo(HWND window) {
+  cbSize = sizeof(MonitorInfo);
+  szDevice[0] = 0;
+
+#ifdef RFB_HAVE_MONITORINFO
+  try {
+    if (_MonitorFromWindow.isValid()) {
+      HMONITOR monitor = (*_MonitorFromWindow)(window, MONITOR_DEFAULTTONEAREST);
+      if (!monitor)
+        throw rdr::SystemException("failed to get monitor", GetLastError());
+      fillMonitorInfo(monitor, this);
+      return;
+    }
+  } catch (rdr::Exception& e) {
+    vlog.error(e.str());
+  }
+#endif
+
+  // Legacy fallbacks - just return the desktop settings
+  vlog.debug("using legacy fall-backs");
+  HWND desktop = GetDesktopWindow();
+  GetWindowRect(desktop, &rcMonitor);
+  SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
+  dwFlags = 0;
+}
+
+MonitorInfo::MonitorInfo(const RECT& r) {
+  cbSize = sizeof(MonitorInfo);
+  szDevice[0] = 0;
+
+#ifdef RFB_HAVE_MONITORINFO
+  try {
+    if (_MonitorFromRect.isValid()) {
+      HMONITOR monitor = (*_MonitorFromRect)(&r, MONITOR_DEFAULTTONEAREST);
+      if (!monitor)
+        throw rdr::SystemException("failed to get monitor", GetLastError());
+      fillMonitorInfo(monitor, this);
+      return;
+    }
+  } catch (rdr::Exception& e) {
+    vlog.error(e.str());
+  }
+#endif
+
+  // Legacy fallbacks - just return the desktop settings
+  vlog.debug("using legacy fall-backs");
+  HWND desktop = GetDesktopWindow();
+  GetWindowRect(desktop, &rcMonitor);
+  SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
+  dwFlags = 0;
+}
+
+
+#ifdef RFB_HAVE_MONITORINFO
+
+struct monitorByNameData {
+  MonitorInfo* info;
+  const char* monitorName;
+};
+
+static BOOL CALLBACK monitorByNameEnumProc(HMONITOR monitor,
+                                    HDC dc,
+                                    LPRECT pos,
+                                    LPARAM d) {
+  monitorByNameData* data = (monitorByNameData*)d;
+  memset(data->info, 0, sizeof(MONITORINFOEXA));
+  data->info->cbSize = sizeof(MONITORINFOEXA);
+  if ((*_GetMonitorInfo)(monitor, data->info)) {
+    if (stricmp(data->monitorName, data->info->szDevice) == 0)
+      return FALSE;
+  }
+
+  return TRUE;
+}
+
+#endif
+
+MonitorInfo::MonitorInfo(const char* devName) {
+#ifdef RFB_HAVE_MONITORINFO
+  if (!_EnumDisplayMonitors.isValid()) {
+    vlog.debug("EnumDisplayMonitors not found");
+  } else {
+    monitorByNameData data;
+    data.info = this;
+    data.monitorName = devName;
+
+    (*_EnumDisplayMonitors)(0, 0, &monitorByNameEnumProc, (LPARAM)&data);
+    if (stricmp(data.monitorName, szDevice) == 0)
+      return;
+  }
+#endif
+  // If multi-monitor is not built, or not supported by the OS,
+  //   or if the named monitor is not found, revert to the primary monitor.
+  vlog.debug("reverting to primary monitor");
+  cbSize = sizeof(MonitorInfo);
+  szDevice[0] = 0;
+
+  HWND desktop = GetDesktopWindow();
+  GetWindowRect(desktop, &rcMonitor);
+  SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
+  dwFlags = 0;
+}
+
+void MonitorInfo::moveTo(HWND handle) {
+  vlog.debug("moveTo monitor=%s", szDevice);
+
+#ifdef RFB_HAVE_MONITORINFO
+  MonitorInfo mi(handle);
+  if (strcmp(szDevice, mi.szDevice) != 0) {
+    centerWindow(handle, rcWork);
+    clipTo(handle);
+  }
+#endif
+}
+
+void MonitorInfo::clipTo(RECT* r) {
+  vlog.debug("clipTo monitor=%s", szDevice);
+
+  if (r->top < rcWork.top) {
+    r->bottom += rcWork.top - r->top; r->top = rcWork.top;
+  }
+  if (r->left < rcWork.left) {
+    r->right += rcWork.left - r->left; r->left = rcWork.left;
+  }
+  if (r->bottom > rcWork.bottom) {
+    r->top += rcWork.bottom - r->bottom; r->bottom = rcWork.bottom;
+  }
+  if (r->right > rcWork.right) {
+    r->left += rcWork.right - r->right; r->right = rcWork.right;
+  }
+  r->left = max(r->left, rcWork.left);
+  r->right = min(r->right, rcWork.right);
+  r->top = max(r->top, rcWork.top);
+  r->bottom = min(r->bottom, rcWork.bottom);
+}
+
+void MonitorInfo::clipTo(HWND handle) {
+  RECT r;
+  GetWindowRect(handle, &r);
+  clipTo(&r);
+  SetWindowPos(handle, 0, r.left, r.top, r.right-r.left, r.bottom-r.top,
+               SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
+}
+
+
diff --git a/rfb_win32/MonitorInfo.h b/rfb_win32/MonitorInfo.h
new file mode 100644
index 0000000..acf2775
--- /dev/null
+++ b/rfb_win32/MonitorInfo.h
@@ -0,0 +1,72 @@
+/* Copyright (C) 2002-2005 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.
+ */
+
+// Helper class used to obtain information about a particular monitor.
+// This class wraps the Windows MONITORINFOEX ASCII structure, providing
+// methods that can safely be called on both multi-monitor aware systems
+// and older "legacy" systems.
+
+
+#ifndef __RFB_WIN32_MONITORINFO_H__
+#define __RFB_WIN32_MONITORINFO_H__
+
+#include <windows.h>
+#ifdef MONITOR_DEFAULTTONULL
+#define RFB_HAVE_MONITORINFO
+#endif
+
+namespace rfb {
+  namespace win32 {
+
+    // Structure containing info on the monitor nearest the window.
+    // Copes with multi-monitor OSes and older ones.
+#ifdef RFB_HAVE_MONITORINFO
+    struct MonitorInfo : MONITORINFOEXA {
+#else
+    struct MonitorInfo {
+      DWORD cbSize;
+      RECT rcMonitor;
+      RECT rcWork;
+      DWORD dwFlags;
+      char szDevice[1]; // Always null...
+#endif
+
+      // Constructor: Obtains monitor info for the monitor that has the
+      //   greatest overlap with the supplied window or rectangle.
+      MonitorInfo(HWND hwnd);
+      MonitorInfo(const RECT& r);
+
+      // Constructor: Obtains monitor info for the name monitor.  Monitor
+      //   names should be those obtained from the MonitorInfo
+      //   szDevice field, and usually look like "\\.\DISPLAY<n>"
+      MonitorInfo(const char* devName);
+
+      // Move the specified window to reside on the monitor.
+      void moveTo(HWND handle);
+
+      // Clip the specified rectangle or window to the monitor's working area.
+      //   The rectangle/window is moved so that as much as possible resides
+      //   on the working area of the monitor, and is then intersected with it.
+      void clipTo(HWND handle);
+      void clipTo(RECT* r);
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/MsgBox.h b/rfb_win32/MsgBox.h
new file mode 100644
index 0000000..5957139
--- /dev/null
+++ b/rfb_win32/MsgBox.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 2002-2005 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_WIN32_MSGBOX_H__
+#define __RFB_WIN32_MSGBOX_H__
+
+#include <windows.h>
+#include <rfb_win32/TCharArray.h>
+
+namespace rfb {
+  namespace win32 {
+
+    // Define rfb::win32::AppName somewhere in the application.
+    // The MsgBox function will use the specified application name
+    // as the prefix for the message box title.
+    // Message box titles are based on the (standard Win32) flags
+    // passed to the MsgBox helper function.
+
+    extern TStr AppName;
+
+    // Wrapper around Win32 MessageBox()
+    static int MsgBox(HWND parent, const TCHAR* msg, UINT flags) {
+      const TCHAR* msgType = 0;
+      UINT tflags = flags & 0x70;
+      if (tflags == MB_ICONHAND)
+        msgType = _T("Error");
+      else if (tflags == MB_ICONQUESTION)
+        msgType = _T("Question");
+      else if (tflags == MB_ICONEXCLAMATION)
+        msgType = _T("Warning");
+      else if (tflags == MB_ICONASTERISK)
+        msgType = _T("Information");
+      flags |= MB_TOPMOST | MB_SETFOREGROUND;
+      int len = _tcslen(AppName.buf) + 1;
+      if (msgType) len += _tcslen(msgType) + 3;
+      TCharArray title = new TCHAR[len];
+      _tcscpy(title.buf, AppName.buf);
+      if (msgType) {
+        _tcscat(title.buf, _T(" : "));
+        _tcscat(title.buf, msgType);
+      }
+      return MessageBox(parent, msg, title.buf, flags);
+    }
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/MsgWindow.cxx b/rfb_win32/MsgWindow.cxx
index 519d6ab..95bd523 100644
--- a/rfb_win32/MsgWindow.cxx
+++ b/rfb_win32/MsgWindow.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -113,4 +113,4 @@
 LRESULT
 MsgWindow::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
   return SafeDefWindowProc(getHandle(), msg, wParam, lParam);
-}
\ No newline at end of file
+}
diff --git a/rfb_win32/MsgWindow.h b/rfb_win32/MsgWindow.h
index 94baca3..92b6cf2 100644
--- a/rfb_win32/MsgWindow.h
+++ b/rfb_win32/MsgWindow.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -24,9 +24,7 @@
 #ifndef __RFB_WIN32_MSG_WINDOW_H__
 #define __RFB_WIN32_MSG_WINDOW_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
-
 #include <rfb_win32/TCharArray.h>
 
 namespace rfb {
diff --git a/rfb_win32/OSVersion.cxx b/rfb_win32/OSVersion.cxx
index 1976098..3d74c95 100644
--- a/rfb_win32/OSVersion.cxx
+++ b/rfb_win32/OSVersion.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb_win32/OSVersion.h b/rfb_win32/OSVersion.h
index 1d52943..18ec003 100644
--- a/rfb_win32/OSVersion.h
+++ b/rfb_win32/OSVersion.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -27,7 +27,6 @@
 #ifndef __RFB_WIN32_OS_VERSION_H__
 #define __RFB_WIN32_OS_VERSION_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 
 namespace rfb {
diff --git a/rfb_win32/RegConfig.cxx b/rfb_win32/RegConfig.cxx
index fcb309b..dd1c3b0 100644
--- a/rfb_win32/RegConfig.cxx
+++ b/rfb_win32/RegConfig.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -23,7 +23,7 @@
 #include <rfb_win32/RegConfig.h>
 #include <rfb/LogWriter.h>
 #include <rfb/util.h>
-#include <rdr/HexOutStream.h>
+//#include <rdr/HexOutStream.h>
 
 using namespace rfb;
 using namespace rfb::win32;
@@ -32,90 +32,28 @@
 static LogWriter vlog("RegConfig");
 
 
-class rfb::win32::RegReaderThread : public Thread {
-public:
-  RegReaderThread(RegistryReader& reader, const HKEY key);
-  ~RegReaderThread();
-  virtual void run();
-  virtual Thread* join();
-protected:
-  RegistryReader& reader;
-  RegKey key;
-  HANDLE event;
-};
-
-RegReaderThread::RegReaderThread(RegistryReader& reader_, const HKEY key_) : Thread("RegConfig"), reader(reader_), key(key_) {
+RegConfig::RegConfig(EventManager* em) : eventMgr(em), event(CreateEvent(0, TRUE, FALSE, 0)), callback(0) {
+  if (em->addEvent(event, this))
+    eventMgr = em;
 }
 
-RegReaderThread::~RegReaderThread() {
+RegConfig::~RegConfig() {
+  if (eventMgr)
+    eventMgr->removeEvent(event);
 }
 
-void
-RegReaderThread::run() {
-  vlog.debug("RegReaderThread started");
-  while (key) {
-    // - Wait for changes
-    vlog.debug("waiting for changes");
-    key.awaitChange(true, REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET);
-
-    // - Load settings
-    RegistryReader::loadRegistryConfig(key);
-
-    // - Notify specified thread of changes
-    if (reader.notifyThread)
-      PostThreadMessage(reader.notifyThread->getThreadId(),
-        reader.notifyMsg.message, 
-        reader.notifyMsg.wParam, 
-        reader.notifyMsg.lParam);
-    else if (reader.notifyWindow)
-      PostMessage(reader.notifyWindow,
-        reader.notifyMsg.message, 
-        reader.notifyMsg.wParam, 
-        reader.notifyMsg.lParam);
-  }
-}
-
-Thread*
-RegReaderThread::join() {
-  RegKey old_key = key;
-  key.close();
-  if ((HKEY)old_key) {
-    // *** Closing the key doesn't always seem to work
-    //     Writing to it always will, instead...
-    vlog.debug("closing key");
-    old_key.setString(_T("dummy"), _T(""));
-  }
-  return Thread::join();
-}
-
-
-RegistryReader::RegistryReader() : thread(0), notifyThread(0) {
-  memset(&notifyMsg, 0, sizeof(notifyMsg));
-}
-
-RegistryReader::~RegistryReader() {
-  if (thread) delete thread->join();
-}
-
-bool RegistryReader::setKey(const HKEY rootkey, const TCHAR* keyname) {
-  if (thread) delete thread->join();
-  thread = 0;
-  
-  RegKey key;
+bool RegConfig::setKey(const HKEY rootkey, const TCHAR* keyname) {
   try {
     key.createKey(rootkey, keyname);
-    loadRegistryConfig(key);
+    processEvent(event);
+    return true;
   } catch (rdr::Exception& e) {
     vlog.debug(e.str());
     return false;
   }
-  thread = new RegReaderThread(*this, key);
-  if (thread) thread->start();
-  return true;
 }
 
-void
-RegistryReader::loadRegistryConfig(RegKey& key) {
+void RegConfig::loadRegistryConfig(RegKey& key) {
   DWORD i = 0;
   try {
     while (1) {
@@ -131,21 +69,46 @@
   }
 }
 
-bool RegistryReader::setNotifyThread(Thread* thread, UINT winMsg, WPARAM wParam, LPARAM lParam) {
-  notifyMsg.message = winMsg;
-  notifyMsg.wParam = wParam;
-  notifyMsg.lParam = lParam;
-  notifyThread = thread;
-  notifyWindow = 0;
-  return true;
+void RegConfig::processEvent(HANDLE event_) {
+  vlog.info("registry changed");
+
+  // Reinstate the registry change notifications
+  ResetEvent(event);
+  key.awaitChange(true, REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET, event);
+
+  // Load settings
+  loadRegistryConfig(key);
+
+  // Notify the callback, if supplied
+  if (callback)
+    callback->regConfigChanged();
 }
 
-bool RegistryReader::setNotifyWindow(HWND window, UINT winMsg, WPARAM wParam, LPARAM lParam) {
-  notifyMsg.message = winMsg;
-  notifyMsg.wParam = wParam;
-  notifyMsg.lParam = lParam;
-  notifyWindow = window;
-  notifyThread = 0;
-  return true;
+
+RegConfigThread::RegConfigThread() : Thread("RegConfigThread"), config(&eventMgr) {
 }
 
+RegConfigThread::~RegConfigThread() {
+  join();
+}
+
+bool RegConfigThread::start(const HKEY rootKey, const TCHAR* keyname) {
+  if (config.setKey(rootKey, keyname)) {
+    Thread::start();
+    return true;
+  }
+  return false;
+}
+
+void RegConfigThread::run() {
+  DWORD result = 0;
+  MSG msg;
+  while ((result = eventMgr.getMessage(&msg, 0, 0, 0)) > 0) {}
+  if (result < 0)
+    throw rdr::SystemException("RegConfigThread failed", GetLastError());
+}
+
+Thread* RegConfigThread::join() {
+  PostThreadMessage(getThreadId(), WM_QUIT, 0, 0);
+  return Thread::join();
+}
diff --git a/rfb_win32/RegConfig.h b/rfb_win32/RegConfig.h
index 3fced85..e9c01b1 100644
--- a/rfb_win32/RegConfig.h
+++ b/rfb_win32/RegConfig.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -26,27 +26,55 @@
 
 #include <rfb/Threading.h>
 #include <rfb/Configuration.h>
-
 #include <rfb_win32/Registry.h>
+#include <rfb_win32/EventManager.h>
+#include <rfb_win32/Handle.h>
 
 namespace rfb {
 
   namespace win32 {
 
-    class RegistryReader {
+    class RegConfig : EventHandler {
     public:
-      RegistryReader();
-      ~RegistryReader();
+      RegConfig(EventManager* em);
+      ~RegConfig();
+
+      // Specify the registry key to read Configuration items from
       bool setKey(const HKEY rootkey, const TCHAR* keyname);
-      bool setNotifyThread(Thread* thread, UINT winMsg, WPARAM wParam=0, LPARAM lParam=0);
-      bool setNotifyWindow(HWND window, UINT winMsg, WPARAM wParam=0, LPARAM lParam=0);
+
+      // Support for a callback, run in the RegConfig host thread whenever
+      // the registry configuration changes
+      class Callback {
+      public:
+        virtual ~Callback() {}
+        virtual void regConfigChanged() = 0;
+      };
+      void setCallback(Callback* cb) { callback = cb; }
+
+      // Read entries from the specified key into the Configuration
       static void loadRegistryConfig(RegKey& key);
     protected:
-      friend class RegReaderThread;
-      Thread* thread;
-      Thread* notifyThread;
-      HWND notifyWindow;
-      MSG notifyMsg;
+      // EventHandler interface and trigger event
+      virtual void processEvent(HANDLE event);
+
+      EventManager* eventMgr;
+      Handle event;
+      Callback* callback;
+      RegKey key;
+    };
+
+    class RegConfigThread : Thread {
+    public:
+      RegConfigThread();
+      ~RegConfigThread();
+
+      // Start the thread, reading from the specified key
+      bool start(const HKEY rootkey, const TCHAR* keyname);
+    protected:
+      void run();
+      Thread* join();
+      EventManager eventMgr;
+      RegConfig config;
     };
 
   };
diff --git a/rfb_win32/Registry.cxx b/rfb_win32/Registry.cxx
index de9238f..4ece4ba 100644
--- a/rfb_win32/Registry.cxx
+++ b/rfb_win32/Registry.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -18,14 +18,14 @@
 
 // -=- Registry.cxx
 
-#include <rfb/LogWriter.h>
 #include <rfb_win32/Registry.h>
+#include <rfb_win32/Security.h>
+#include <rfb_win32/DynamicFn.h>
 #include <rdr/MemOutStream.h>
 #include <rdr/HexOutstream.h>
 #include <rdr/HexInStream.h>
-#include <rfb_win32/Security.h>
-
 #include <stdlib.h>
+#include <rfb/LogWriter.h>
 
 // These flags are required to control access control inheritance,
 // but are not defined by VC6's headers.  These definitions comes
@@ -69,6 +69,7 @@
 
 
 void RegKey::setHKEY(HKEY k, bool fK) {
+  vlog.debug("setHKEY(%x,%d)", k, (int)fK);
   close();
   freeKey = fK;
   key = k;
@@ -82,6 +83,7 @@
     vlog.error("RegCreateKey(%x, %s): %x", root.key, name, result);
     throw rdr::SystemException("RegCreateKeyEx", result);
   }
+  vlog.debug("createKey(%x,%s) = %x", root.key, (const char*)CStr(name), key);
   freeKey = true;
   return true;
 }
@@ -91,6 +93,8 @@
   LONG result = RegOpenKeyEx(root.key, name, 0, readOnly ? KEY_READ : KEY_ALL_ACCESS, &key);
   if (result != ERROR_SUCCESS)
     throw rdr::SystemException("RegOpenKeyEx (open)", result);
+  vlog.debug("openKey(%x,%s,%s) = %x", root.key, (const char*)CStr(name),
+	         readOnly ? "ro" : "rw", key);
   freeKey = true;
 }
 
@@ -109,6 +113,7 @@
 
 void RegKey::close() {
   if (freeKey) {
+    vlog.debug("RegCloseKey(%x)", key);
     RegCloseKey(key);
     key = 0;
   }
@@ -126,8 +131,8 @@
     throw rdr::SystemException("RegDeleteValue", result);
 }
 
-void RegKey::awaitChange(bool watchSubTree, DWORD filter) const {
-  LONG result = RegNotifyChangeKeyValue(key, watchSubTree, filter, 0, FALSE);
+void RegKey::awaitChange(bool watchSubTree, DWORD filter, HANDLE event) const {
+  LONG result = RegNotifyChangeKeyValue(key, watchSubTree, filter, event, event != 0);
   if (result != ERROR_SUCCESS)
     throw rdr::SystemException("RegNotifyChangeKeyValue", result);
 }
@@ -206,6 +211,16 @@
   return getInt(valname, def ? 1 : 0) > 0;
 }
 
+static inline TCHAR* terminateData(char* data, int length)
+{
+  // We must terminate the string, just to be sure.  Stupid Win32...
+  int len = length/sizeof(TCHAR);
+  TCharArray str(len+1);
+  memcpy(str.buf, data, length);
+  str.buf[len] = 0;
+  return str.takeBuf();
+}
+
 TCHAR* RegKey::getRepresentation(const TCHAR* valname) const {
   DWORD type, length;
   LONG result = RegQueryValueEx(key, valname, 0, &type, 0, &length);
@@ -224,12 +239,7 @@
     }
   case REG_SZ:
     if (length) {
-      // We must terminate the string, just to be sure.  Stupid Win32...
-      int len = length/sizeof(TCHAR);
-      TCharArray str(len+1);
-      memcpy(str.buf, data.buf, length);
-      str.buf[len] = 0;
-      return str.takeBuf();
+      return terminateData(data.buf, length);
     } else {
       return tstrDup(_T(""));
     }
@@ -239,6 +249,22 @@
       _stprintf(tmp.buf, _T("%u"), *((DWORD*)data.buf));
       return tmp.takeBuf();
     }
+  case REG_EXPAND_SZ:
+    {
+    if (length) {
+      TCharArray str(terminateData(data.buf, length));
+      DWORD required = ExpandEnvironmentStrings(str.buf, 0, 0);
+      if (required==0)
+        throw rdr::SystemException("ExpandEnvironmentStrings", GetLastError());
+      TCharArray result(required);
+      length = ExpandEnvironmentStrings(str.buf, result.buf, required);
+      if (required<length)
+        rdr::Exception("unable to expand environment strings");
+      return result.takeBuf();
+    } else {
+      return tstrDup(_T(""));
+    }
+    }
   default:
     throw rdr::Exception("unsupported registry type");
   }
@@ -270,3 +296,21 @@
     throw rdr::SystemException("RegEnumValue", result);
   return valueName.buf;
 }
+
+const TCHAR* RegKey::getKeyName(int i) {
+  DWORD maxValueNameLen;
+  LONG result = RegQueryInfoKey(key, 0, 0, 0, 0, &maxValueNameLen, 0, 0, 0, 0, 0, 0);
+  if (result != ERROR_SUCCESS)
+    throw rdr::SystemException("RegQueryInfoKey", result);
+  if (valueNameBufLen < maxValueNameLen + 1) {
+    valueNameBufLen = maxValueNameLen + 1;
+    delete [] valueName.buf;
+    valueName.buf = new TCHAR[valueNameBufLen];
+  }
+  DWORD length = valueNameBufLen;
+  result = RegEnumKeyEx(key, i, valueName.buf, &length, NULL, 0, 0, 0);
+  if (result == ERROR_NO_MORE_ITEMS) return 0;
+  if (result != ERROR_SUCCESS)
+    throw rdr::SystemException("RegEnumKey", result);
+  return valueName.buf;
+}
diff --git a/rfb_win32/Registry.h b/rfb_win32/Registry.h
index 1998c49..68d535c 100644
--- a/rfb_win32/Registry.h
+++ b/rfb_win32/Registry.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -22,9 +22,7 @@
 #ifndef __RFB_WIN32_REGISTRY_H__
 #define __RFB_WIN32_REGISTRY_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
-
 #include <rfb_win32/Security.h>
 #include <rfb/util.h>
 
@@ -45,9 +43,9 @@
       ~RegKey();
 
       void setHKEY(HKEY key, bool freeKey);
-    protected:
-      HKEY operator=(const RegKey& k);
-      HKEY operator=(HKEY k);
+    private:
+      RegKey& operator=(const RegKey& k);
+      HKEY& operator=(const HKEY& k);
     public:
 
       // Returns true if key was created, false if already existed
@@ -67,7 +65,9 @@
       void deleteValue(const TCHAR* name) const;
 
 
-      void awaitChange(bool watchSubTree, DWORD filter) const;
+      // Block waiting for a registry change, OR return immediately and notify the
+      // event when there is a change, if specified
+      void awaitChange(bool watchSubTree, DWORD filter, HANDLE event=0) const;
 
       void setExpandString(const TCHAR* valname, const TCHAR* s) const;
       void setString(const TCHAR* valname, const TCHAR* s) const;
@@ -91,10 +91,11 @@
 
       bool isValue(const TCHAR* valname) const;
 
-      // Get the name of value number "i"
+      // Get the name of value/key number "i"
       // If there are fewer than "i" values then return 0
       // NAME IS OWNED BY RegKey OBJECT!
       const TCHAR* getValueName(int i);
+      const TCHAR* getKeyName(int i);
 
       operator HKEY() const;
     protected:
diff --git a/rfb_win32/SDisplay.cxx b/rfb_win32/SDisplay.cxx
index 4916c48..0af5064 100644
--- a/rfb_win32/SDisplay.cxx
+++ b/rfb_win32/SDisplay.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -20,21 +20,19 @@
 //
 // The SDisplay class encapsulates a particular system display.
 
-#include <assert.h>
-
 #include <rfb_win32/SDisplay.h>
 #include <rfb_win32/Service.h>
-#include <rfb_win32/WMShatter.h>
-#include <rfb_win32/osVersion.h>
-#include <rfb_win32/Win32Util.h>
-#include <rfb_win32/IntervalTimer.h>
+#include <rfb_win32/TsSessions.h>
 #include <rfb_win32/CleanDesktop.h>
-
-#include <rfb/util.h>
-#include <rfb/LogWriter.h>
+#include <rfb_win32/CurrentUser.h>
+#include <rfb_win32/DynamicFn.h>
+#include <rfb_win32/MonitorInfo.h>
+#include <rfb_win32/SDisplayCorePolling.h>
+#include <rfb_win32/SDisplayCoreWMHooks.h>
+#include <rfb_win32/SDisplayCoreDriver.h>
 #include <rfb/Exception.h>
+#include <rfb/LogWriter.h>
 
-#include <rfb/Configuration.h>
 
 using namespace rdr;
 using namespace rfb;
@@ -44,209 +42,37 @@
 
 // - SDisplay-specific configuration options
 
-BoolParameter rfb::win32::SDisplay::use_hooks("UseHooks",
-  "Set hooks in the operating system to capture display updates more efficiently", true);
+IntParameter rfb::win32::SDisplay::updateMethod("UpdateMethod",
+  "How to discover desktop updates; 0 - Polling, 1 - Application hooking, 2 - Driver hooking.", 1);
 BoolParameter rfb::win32::SDisplay::disableLocalInputs("DisableLocalInputs",
   "Disable local keyboard and pointer input while the server is in use", false);
 StringParameter rfb::win32::SDisplay::disconnectAction("DisconnectAction",
   "Action to perform when all clients have disconnected.  (None, Lock, Logoff)", "None");
-
+StringParameter displayDevice("DisplayDevice",
+  "Display device name of the monitor to be remoted, or empty to export the whole desktop.", "");
 BoolParameter rfb::win32::SDisplay::removeWallpaper("RemoveWallpaper",
-  "Remove the desktop wallpaper when the server in in use.", false);
+  "Remove the desktop wallpaper when the server is in use.", false);
 BoolParameter rfb::win32::SDisplay::removePattern("RemovePattern",
-  "Remove the desktop background pattern when the server in in use.", false);
+  "Remove the desktop background pattern when the server is in use.", false);
 BoolParameter rfb::win32::SDisplay::disableEffects("DisableEffects",
   "Disable desktop user interface effects when the server is in use.", false);
 
 
-// - WM_TIMER ID values
-
-#define TIMER_CURSOR 1
-#define TIMER_UPDATE 2
-#define TIMER_UPDATE_AND_POLL 3
-
-
-// -=- Polling settings
-
-const int POLLING_SEGMENTS = 16;
-
-const int FG_POLLING_FPS = 20;
-const int FG_POLLING_FS_INTERVAL = 1000 / FG_POLLING_FPS;
-const int FG_POLLING_INTERVAL = FG_POLLING_FS_INTERVAL / POLLING_SEGMENTS;
-
-const int BG_POLLING_FS_INTERVAL = 5000;
-const int BG_POLLING_INTERVAL = BG_POLLING_FS_INTERVAL / POLLING_SEGMENTS;
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// SDisplayCore
-//
-
-// The SDisplay Core object is created by SDisplay's start() method
-// and deleted by its stop() method.
-// The Core must be created in the current input desktop in order
-// to operate - SDisplay is responsible for ensuring that.
-// The structures contained in the Core are manipulated directly
-// by the SDisplay, which is also responsible for detecting when
-// a desktop-switch is required.
-
-class rfb::win32::SDisplayCore : public MsgWindow {
-public:
-  SDisplayCore(SDisplay* display);
-  ~SDisplayCore();
-
-  void setPixelBuffer(DeviceFrameBuffer* pb_);
-
-  bool isRestartRequired();
-
-  virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
-
-  // -=- Timers
-  IntervalTimer pollTimer;
-  IntervalTimer cursorTimer;
-
-  // -=- Input handling
-  rfb::win32::SPointer ptr;
-  rfb::win32::SKeyboard kbd;
-  rfb::win32::Clipboard clipboard;
-
-  // -=- Hook handling objects used outside thread run() method
-  WMCopyRect wm_copyrect;
-  WMPoller wm_poller;
-  WMCursor cursor;
-  WMMonitor wm_monitor;
-  WMHooks wm_hooks;
-  WMBlockInput wm_input;
-
-  // -=- Tidying the desktop
-  CleanDesktop cleanDesktop;
-  bool isWallpaperRemoved;
-  bool isPatternRemoved;
-  bool areEffectsDisabled;
-
-  // -=- Full screen polling
-  int poll_next_y;
-  int poll_y_increment;
-
-  // Are we using hooks?
-  bool use_hooks;
-  bool using_hooks;
-
-  // State of the display object
-  SDisplay* display;
-};
-
-SDisplayCore::SDisplayCore(SDisplay* display_)
-: MsgWindow(_T("SDisplayCore")), display(display_),
-  using_hooks(0), use_hooks(rfb::win32::SDisplay::use_hooks),
-  isWallpaperRemoved(rfb::win32::SDisplay::removeWallpaper),
-  isPatternRemoved(rfb::win32::SDisplay::removePattern),
-  areEffectsDisabled(rfb::win32::SDisplay::disableEffects),
-  pollTimer(getHandle(), TIMER_UPDATE_AND_POLL),
-  cursorTimer(getHandle(), TIMER_CURSOR) {
-  setPixelBuffer(display->pb);
-}
-
-SDisplayCore::~SDisplayCore() {
-}
-
-void SDisplayCore::setPixelBuffer(DeviceFrameBuffer* pb) {
-  poll_y_increment = (display->pb->height()+POLLING_SEGMENTS-1)/POLLING_SEGMENTS;
-  poll_next_y = display->screenRect.tl.y;
-  wm_hooks.setClipRect(display->screenRect);
-  wm_copyrect.setClipRect(display->screenRect);
-  wm_poller.setClipRect(display->screenRect);
-}
-
-
-bool SDisplayCore::isRestartRequired() {
-  // - We must restart the SDesktop if:
-  // 1. We are no longer in the input desktop.
-  // 2. The use_hooks setting has changed.
-
-  // - Check that we are in the input desktop
-  if (rfb::win32::desktopChangeRequired())
-    return true;
-
-  // - Check that the hooks setting hasn't changed
-  // NB: We can't just check using_hooks because that can be false
-  // because they failed, even though use_hooks is true!
-  if (use_hooks != rfb::win32::SDisplay::use_hooks)
-    return true;
-
-  // - Check that the desktop optimisation settings haven't changed
-  //   This isn't very efficient, but it shouldn't change very often!
-  if ((isWallpaperRemoved != rfb::win32::SDisplay::removeWallpaper) ||
-      (isPatternRemoved != rfb::win32::SDisplay::removePattern) ||
-      (areEffectsDisabled != rfb::win32::SDisplay::disableEffects))
-    return true;
-
-  return false;
-}
-
-LRESULT SDisplayCore::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
-  switch (msg) {
-
-  case WM_TIMER:
-
-    if (display->server && display->server->clientsReadyForUpdate()) {
-
-      // - Check that the SDesktop doesn't need restarting
-      if (isRestartRequired()) {
-        display->restart();
-        return 0;
-      }
-    
-      // - Action depends on the timer message type
-      switch (wParam) {
-
-        // POLL THE SCREEN
-      case TIMER_UPDATE_AND_POLL:
-        // Handle window dragging, polling of consoles, etc.
-        while (wm_poller.processEvent()) {}
-
-        // Poll the next strip of the screen (in Screen coordinates)
-        {
-          Rect pollrect = display->screenRect;
-          if (poll_next_y >= pollrect.br.y) {
-            // Yes.  Reset the counter and return
-            poll_next_y = pollrect.tl.y;
-          } else {
-            // No.  Poll the next section
-            pollrect.tl.y = poll_next_y;
-            poll_next_y += poll_y_increment;
-            pollrect.br.y = min(poll_next_y, pollrect.br.y);
-            display->add_changed(pollrect);
-          }
-        }
-        break;
-
-      case TIMER_CURSOR:
-        display->triggerUpdate();
-        break;
-
-      };
-
-    }
-    return 0;
-
-  };
-
-  return MsgWindow::processMessage(msg, wParam, lParam);
-}
-
 //////////////////////////////////////////////////////////////////////////////
 //
 // SDisplay
 //
 
+typedef BOOL (WINAPI *_LockWorkStation_proto)();
+DynamicFn<_LockWorkStation_proto> _LockWorkStation(_T("user32.dll"), "LockWorkStation");
+
 // -=- Constructor/Destructor
 
-SDisplay::SDisplay(const TCHAR* devName)
-  : server(0), change_tracker(true), pb(0),
-    deviceName(tstrDup(devName)), device(0), releaseDevice(false),
-    core(0), statusLocation(0)
+SDisplay::SDisplay()
+  : server(0), pb(0), device(0),
+    core(0), ptr(0), kbd(0), clipboard(0),
+    inputs(0), monitor(0), cleanDesktop(0), cursor(0),
+    statusLocation(0)
 {
   updateEvent.h = CreateEvent(0, TRUE, FALSE, 0);
 }
@@ -271,57 +97,14 @@
 void SDisplay::start(VNCServer* vs)
 {
   vlog.debug("starting");
+
+  // Try to make session zero the console session
+  if (!inConsoleSession())
+    setConsoleSession();
+
+  // Start the SDisplay core
   server = vs;
-
-  // Switch to the current input desktop
-  // ***
-  if (rfb::win32::desktopChangeRequired()) {
-    if (!rfb::win32::changeDesktop())
-      throw rdr::Exception("unable to switch into input desktop");
-  }
-
-  // Clear the change tracker
-  change_tracker.clear();
-
-  // Create the framebuffer object
-  recreatePixelBuffer();
-
-  // Create the SDisplayCore
-  core = new SDisplayCore(this);
-  assert(core);
-
-  // Start display monitor and clipboard handler
-  core->wm_monitor.setNotifier(this);
-  core->clipboard.setNotifier(this);
-
-  // Apply desktop optimisations
-  if (removePattern)
-    core->cleanDesktop.disablePattern();
-  if (removeWallpaper)
-    core->cleanDesktop.disableWallpaper();
-  if (disableEffects)
-    core->cleanDesktop.disableEffects();
-
-  // Start hooks
-  core->wm_hooks.setClipRect(screenRect);
-  if (core->use_hooks) {
-    // core->wm_hooks.setDiagnosticRange(0, 0x400-1);
-    core->using_hooks = core->wm_hooks.setUpdateTracker(this);
-    if (!core->using_hooks)
-      vlog.debug("hook subsystem failed to initialise");
-  }
-
-  // Set up timers
-  core->pollTimer.start(core->using_hooks ? BG_POLLING_INTERVAL : FG_POLLING_INTERVAL);
-  core->cursorTimer.start(10);
-
-  // Register an interest in faked copyrect events
-  core->wm_copyrect.setUpdateTracker(&change_tracker);
-  core->wm_copyrect.setClipRect(screenRect);
-
-  // Polling of particular windows on the desktop
-  core->wm_poller.setUpdateTracker(&change_tracker);
-  core->wm_poller.setClipRect(screenRect);
+  startCore();
 
   vlog.debug("started");
 
@@ -331,108 +114,229 @@
 void SDisplay::stop()
 {
   vlog.debug("stopping");
+
+  // If we successfully start()ed then perform the DisconnectAction
   if (core) {
-    // If SDisplay was actually active then perform the disconnect action
+    CurrentUserToken cut;
     CharArray action = disconnectAction.getData();
     if (stricmp(action.buf, "Logoff") == 0) {
-      ExitWindowsEx(EWX_LOGOFF, 0);
-    } else if (stricmp(action.buf, "Lock") == 0) {
-      typedef BOOL (WINAPI *_LockWorkStation_proto)();
-      DynamicFn<_LockWorkStation_proto> _LockWorkStation(_T("user32.dll"), "LockWorkStation");
-      if (_LockWorkStation.isValid())
-        (*_LockWorkStation)();
+      if (!cut.h)
+        vlog.info("ignoring DisconnectAction=Logoff - no current user");
       else
         ExitWindowsEx(EWX_LOGOFF, 0);
+    } else if (stricmp(action.buf, "Lock") == 0) {
+      if (!cut.h) {
+        vlog.info("ignoring DisconnectAction=Lock - no current user");
+      } else {
+        if (_LockWorkStation.isValid())
+          (*_LockWorkStation)();
+        else
+          ExitWindowsEx(EWX_LOGOFF, 0);
+      }
     }
   }
-  delete core;
-  core = 0;
-  delete pb;
-  pb = 0;
-  if (device) {
-    if (releaseDevice)
-      ReleaseDC(0, device);
-    else
-      DeleteDC(device);
-  }
-  device = 0;
+
+  // Stop the SDisplayCore
   if (server)
     server->setPixelBuffer(0);
-
+  stopCore();
   server = 0;
+
   vlog.debug("stopped");
 
   if (statusLocation) *statusLocation = false;
 }
 
-void SDisplay::restart() {
-  vlog.debug("restarting");
-  // Close down the hooks
-  delete core;
-  core = 0;
+
+void SDisplay::startCore() {
+
+  // Currently, we just check whether we're in the console session, and
+  //   fail if not
+  if (!inConsoleSession())
+    throw rdr::Exception("Console is not session zero - oreconnect to restore Console sessin");
+  
+  // Switch to the current input desktop
+  if (rfb::win32::desktopChangeRequired()) {
+    if (!rfb::win32::changeDesktop())
+      throw rdr::Exception("unable to switch into input desktop");
+  }
+
+  // Initialise the change tracker and clipper
+  updates.clear();
+  clipper.setUpdateTracker(server);
+
+  // Create the framebuffer object
+  recreatePixelBuffer(true);
+
+  // Create the SDisplayCore
+  updateMethod_ = updateMethod;
+  int tryMethod = updateMethod_;
+  while (!core) {
+    try {
+      if (tryMethod == 2)
+        core = new SDisplayCoreDriver(this, &updates);
+      else if (tryMethod == 1)
+        core = new SDisplayCoreWMHooks(this, &updates);
+      else
+        core = new SDisplayCorePolling(this, &updates);
+      core->setScreenRect(screenRect);
+    } catch (rdr::Exception& e) {
+      delete core; core = 0;
+      if (tryMethod == 0)
+        throw rdr::Exception("unable to access desktop");
+      tryMethod--;
+      vlog.error(e.str());
+    }
+  }
+  vlog.info("Started %s", core->methodName());
+
+  // Start display monitor, clipboard handler and input handlers
+  monitor = new WMMonitor;
+  monitor->setNotifier(this);
+  clipboard = new Clipboard;
+  clipboard->setNotifier(this);
+  ptr = new SPointer;
+  kbd = new SKeyboard;
+  inputs = new WMBlockInput;
+  cursor = new WMCursor;
+
+  // Apply desktop optimisations
+  cleanDesktop = new CleanDesktop;
+  if (removePattern)
+    cleanDesktop->disablePattern();
+  if (removeWallpaper)
+    cleanDesktop->disableWallpaper();
+  if (disableEffects)
+    cleanDesktop->disableEffects();
+  isWallpaperRemoved = removeWallpaper;
+  isPatternRemoved = removePattern;
+  areEffectsDisabled = disableEffects;
+}
+
+void SDisplay::stopCore() {
+  if (core)
+    vlog.info("Stopping %s", core->methodName());
+  delete core; core = 0;
+  delete pb; pb = 0;
+  delete device; device = 0;
+  delete monitor; monitor = 0;
+  delete clipboard; clipboard = 0;
+  delete inputs; inputs = 0;
+  delete ptr; ptr = 0;
+  delete kbd; kbd = 0;
+  delete cleanDesktop; cleanDesktop = 0;
+  delete cursor; cursor = 0;
+  ResetEvent(updateEvent);
+}
+
+
+bool SDisplay::areHooksAvailable() {
+  return WMHooks::areAvailable();
+}
+
+bool SDisplay::isDriverAvailable() {
+  return SDisplayCoreDriver::isAvailable();
+}
+
+
+bool SDisplay::isRestartRequired() {
+  // - We must restart the SDesktop if:
+  // 1. We are no longer in the input desktop.
+  // 2. The any setting has changed.
+
+  // - Check that our session is the Console
+  if (!inConsoleSession())
+    return true;
+
+  // - Check that we are in the input desktop
+  if (rfb::win32::desktopChangeRequired())
+    return true;
+
+  // - Check that the update method setting hasn't changed
+  //   NB: updateMethod reflects the *selected* update method, not
+  //   necessarily the one in use, since we fall back to simpler
+  //   methods if more advanced ones fail!
+  if (updateMethod_ != updateMethod)
+    return true;
+
+  // - Check that the desktop optimisation settings haven't changed
+  //   This isn't very efficient, but it shouldn't change very often!
+  if ((isWallpaperRemoved != removeWallpaper) ||
+      (isPatternRemoved != removePattern) ||
+      (areEffectsDisabled != disableEffects))
+    return true;
+
+  return false;
+}
+
+
+void SDisplay::restartCore() {
+  vlog.info("restarting");
+
+  // Stop the existing Core  related resources
+  stopCore();
   try {
-    // Re-start the hooks if possible
-    start(server);
-    vlog.debug("restarted");
+    // Start a new Core if possible
+    startCore();
+    vlog.info("restarted");
   } catch (rdr::Exception& e) {
-    // If start() fails then we MUST disconnect all clients,
-    // to cause the server to stop using the desktop.
+    // If startCore() fails then we MUST disconnect all clients,
+    // to cause the server to stop() the desktop.
     // Otherwise, the SDesktop is in an inconsistent state
-    // and the server will crash
+    // and the server will crash.
     server->closeClients(e.str());
   }
 }
 
 
-void SDisplay::pointerEvent(const Point& pos, rdr::U8 buttonmask) {
+void SDisplay::pointerEvent(const Point& pos, int buttonmask) {
   if (pb->getRect().contains(pos)) {
     Point screenPos = pos.translate(screenRect.tl);
-    core->ptr.pointerEvent(screenPos, buttonmask);
+    // - Check that the SDesktop doesn't need restarting
+    if (isRestartRequired())
+      restartCore();
+    if (ptr)
+      ptr->pointerEvent(screenPos, buttonmask);
   }
 }
 
 void SDisplay::keyEvent(rdr::U32 key, bool down) {
-  core->kbd.keyEvent(key, down);
+  // - Check that the SDesktop doesn't need restarting
+  if (isRestartRequired())
+    restartCore();
+  if (kbd)
+    kbd->keyEvent(key, down);
 }
 
 void SDisplay::clientCutText(const char* text, int len) {
   CharArray clip_sz(len+1);
   memcpy(clip_sz.buf, text, len);
   clip_sz.buf[len] = 0;
-  core->clipboard.setClipText(clip_sz.buf);
+  clipboard->setClipText(clip_sz.buf);
 }
 
 
 void SDisplay::framebufferUpdateRequest()
 {
-  triggerUpdate();
+  SetEvent(updateEvent);
 }
 
 Point SDisplay::getFbSize() {
   bool startAndStop = !core;
+
   // If not started, do minimal initialisation to get desktop size.
-  if (startAndStop) recreatePixelBuffer();
+  if (startAndStop)
+    recreatePixelBuffer();
   Point result = Point(pb->width(), pb->height());
+
   // Destroy the initialised structures.
-  if (startAndStop) stop();
+  if (startAndStop)
+    stopCore();
   return result;
 }
 
 
 void
-SDisplay::add_changed(const Region& rgn) {
-  change_tracker.add_changed(rgn);
-  triggerUpdate();
-}
-
-void
-SDisplay::add_copied(const Region& dest, const Point& delta) {
-  change_tracker.add_copied(dest, delta);
-  triggerUpdate();
-}
-
-
-void
 SDisplay::notifyClipboardChanged(const char* text, int len) {
   vlog.debug("clipboard text changed");
   if (server)
@@ -462,36 +366,42 @@
   }
 }
 
-bool
+void
 SDisplay::processEvent(HANDLE event) {
   if (event == updateEvent) {
-    vlog.info("processEvent");
+    vlog.write(120, "processEvent");
     ResetEvent(updateEvent);
 
     // - If the SDisplay isn't even started then quit now
     if (!core) {
       vlog.error("not start()ed");
-      return true;
+      return;
     }
 
     // - Ensure that the disableLocalInputs flag is respected
-    core->wm_input.blockInputs(SDisplay::disableLocalInputs);
+    inputs->blockInputs(disableLocalInputs);
 
     // - Only process updates if the server is ready
     if (server && server->clientsReadyForUpdate()) {
       bool try_update = false;
 
       // - Check that the SDesktop doesn't need restarting
-      if (core->isRestartRequired()) {
-        restart();
-        return true;
+      if (isRestartRequired()) {
+        restartCore();
+        return;
       }
-    
-      // *** window dragging can be improved - more frequent, more cunning about updates
-      while (core->wm_copyrect.processEvent()) {}
-        
+
+      // - Flush any updates from the core
+      try {
+        core->flushUpdates();
+      } catch (rdr::Exception& e) {
+        vlog.error(e.str());
+        restartCore();
+        return;
+      }
+
       // Ensure the cursor is up to date
-      WMCursor::Info info = core->cursor.getCursorInfo();
+      WMCursor::Info info = cursor->getCursorInfo();
       if (old_cursor != info) {
         // Update the cursor shape if the visibility has changed
         bool set_cursor = info.visible != old_cursor.visible;
@@ -505,7 +415,7 @@
         // Update the cursor position
         // NB: First translate from Screen coordinates to Desktop
         Point desktopPos = info.position.translate(screenRect.tl.negate());
-        server->setCursorPos(desktopPos.x, desktopPos.y);
+        server->setCursorPos(desktopPos);
         try_update = true;
 
         old_cursor = info;
@@ -513,100 +423,102 @@
 
       // Flush any changes to the server
       try_update = flushChangeTracker() || try_update;
-      if (try_update)
+      if (try_update) {
         server->tryUpdate();
+      }
     }
-  } else {
-    CloseHandle(event);
-    return false;
+    return;
   }
-  return true;
+  throw rdr::Exception("No such event");
 }
 
 
 // -=- Protected methods
 
 void
-SDisplay::recreatePixelBuffer() {
-  vlog.debug("attaching to device %s", deviceName);
-
+SDisplay::recreatePixelBuffer(bool force) {
   // Open the specified display device
-  HDC new_device;
-  if (deviceName.buf) {
-    new_device = ::CreateDC(_T("DISPLAY"), deviceName.buf, NULL, NULL);
-    releaseDevice = false;
-  } else {
-    // If no device is specified, open entire screen.
-    // Doing this with CreateDC creates problems on multi-monitor systems.
-    new_device = ::GetDC(0);
-    releaseDevice = true;
+  //   If no device is specified, open entire screen using GetDC().
+  //   Opening the whole display with CreateDC doesn't work on multi-monitor
+  //   systems for some reason.
+  DeviceContext* new_device = 0;
+  TCharArray deviceName(displayDevice.getData());
+  if (deviceName.buf[0]) {
+    vlog.info("Attaching to device %s", (const char*)CStr(deviceName.buf));
+    new_device = new DeviceDC(deviceName.buf);
   }
-  if (!new_device)
-    throw SystemException("cannot open the display", GetLastError());
+  if (!new_device) {
+    vlog.info("Attaching to virtual desktop");
+    new_device = new WindowDC(0);
+  }
 
-  // Get the coordinates of the entire virtual display
+  // Get the coordinates of the specified dispay device
   Rect newScreenRect;
-  {
-    WindowDC rootDC(0);
-    RECT r;
-    if (!GetClipBox(rootDC, &r))
-      throw rdr::SystemException("GetClipBox", GetLastError());
-    newScreenRect = Rect(r.left, r.top, r.right, r.bottom);
-  }
-
-  // Create a DeviceFrameBuffer attached to it
-  DeviceFrameBuffer* new_buffer = new DeviceFrameBuffer(new_device);
-
-  // Has anything actually changed about the screen or the buffer?
-  if (!pb ||
-      (!newScreenRect.equals(screenRect)) ||
-      (!new_buffer->getPF().equal(pb->getPF())))
-  {
-    // Yes.  Update the buffer state.
-    screenRect = newScreenRect;
-    vlog.debug("creating pixel buffer for device");
-
-    // Flush any existing changes to the server
-    flushChangeTracker();
-
-    // Replace the old PixelBuffer
-    if (pb) delete pb;
-    if (device) DeleteDC(device);
-    pb = new_buffer;
-    device = new_device;
-
-    // Initialise the pixels
-    pb->grabRegion(pb->getRect());
-
-    // Prevent future grabRect operations from throwing exceptions
-    pb->setIgnoreGrabErrors(true);
-
-    // Update the SDisplayCore if required
-    if (core)
-      core->setPixelBuffer(pb);
-
-    // Inform the server of the changes
-    if (server)
-      server->setPixelBuffer(pb);
-
+  if (deviceName.buf[0]) {
+    MonitorInfo info(CStr(deviceName.buf));
+    newScreenRect = Rect(info.rcMonitor.left, info.rcMonitor.top,
+                         info.rcMonitor.right, info.rcMonitor.bottom);
   } else {
-    delete new_buffer;
-    DeleteDC(new_device);
+    newScreenRect = new_device->getClipBox();
   }
+
+  // If nothing has changed & a recreate has not been forced, delete
+  // the new device context and return
+  if (pb && !force &&
+    newScreenRect.equals(screenRect) &&
+    new_device->getPF().equal(pb->getPF())) {
+    delete new_device;
+    return;
+  }
+
+  // Flush any existing changes to the server
+  flushChangeTracker();
+
+  // Delete the old pixelbuffer and device context
+  vlog.debug("deleting old pixel buffer & device");
+  if (pb)
+    delete pb;
+  if (device)
+    delete device;
+
+  // Create a DeviceFrameBuffer attached to the new device
+  vlog.debug("creating pixel buffer");
+  DeviceFrameBuffer* new_buffer = new DeviceFrameBuffer(*new_device);
+
+  // Replace the old PixelBuffer
+  screenRect = newScreenRect;
+  pb = new_buffer;
+  device = new_device;
+
+  // Initialise the pixels
+  pb->grabRegion(pb->getRect());
+
+  // Prevent future grabRect operations from throwing exceptions
+  pb->setIgnoreGrabErrors(true);
+
+  // Update the clipping update tracker
+  clipper.setClipRect(pb->getRect());
+
+  // Inform the core of the changes
+  if (core)
+    core->setScreenRect(screenRect);
+
+  // Inform the server of the changes
+  if (server)
+    server->setPixelBuffer(pb);
 }
 
 bool SDisplay::flushChangeTracker() {
-  if (change_tracker.is_empty())
+  if (updates.is_empty())
     return false;
-  // Translate the update coordinates from Screen coords to Desktop
-  change_tracker.translate(screenRect.tl.negate());
-  // Flush the updates through
-  change_tracker.get_update(*server);
-  change_tracker.clear();
-  return true;
-}
 
-void SDisplay::triggerUpdate() {
-  if (core)
-    SetEvent(updateEvent);
+  vlog.write(120, "flushChangeTracker");
+
+  // Translate the update coordinates from Screen coords to Desktop
+  updates.translate(screenRect.tl.negate());
+
+  // Clip the updates & flush them to the server
+  updates.copyTo(&clipper);
+  updates.clear();
+  return true;
 }
diff --git a/rfb_win32/SDisplay.h b/rfb_win32/SDisplay.h
index c4c08bf..6dbb50a 100644
--- a/rfb_win32/SDisplay.h
+++ b/rfb_win32/SDisplay.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -20,31 +20,21 @@
 //
 // The SDisplay class encapsulates a system display.
 
-// *** THIS INTERFACE NEEDS TIDYING TO SEPARATE COORDINATE SYSTEMS BETTER ***
-
 #ifndef __RFB_SDISPLAY_H__
 #define __RFB_SDISPLAY_H__
 
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
 #include <rfb/SDesktop.h>
 #include <rfb/UpdateTracker.h>
 #include <rfb/Configuration.h>
-#include <rfb/util.h>
-
-#include <winsock2.h>
-#include <rfb_win32/Win32Util.h>
-#include <rfb_win32/SocketManager.h>
-#include <rfb_win32/DeviceFrameBuffer.h>
+#include <rfb_win32/Handle.h>
+#include <rfb_win32/EventManager.h>
 #include <rfb_win32/SInput.h>
 #include <rfb_win32/Clipboard.h>
-#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/CleanDesktop.h>
 #include <rfb_win32/WMCursor.h>
-#include <rfb_win32/WMHooks.h>
 #include <rfb_win32/WMNotifier.h>
-#include <rfb_win32/WMWindowCopyRect.h>
-#include <rfb_win32/WMPoller.h>
+#include <rfb_win32/DeviceFrameBuffer.h>
+#include <rfb_win32/DeviceContext.h>
 
 namespace rfb {
 
@@ -54,33 +44,33 @@
     // -=- SDisplay
     //
 
-    class SDisplayCore;
+    class SDisplayCore {
+    public:
+      virtual ~SDisplayCore() {};
+      virtual void setScreenRect(const Rect& screenRect_) = 0;
+      virtual void flushUpdates() = 0;
+      virtual const char* methodName() const = 0;
+    };
 
     class SDisplay : public SDesktop,
       WMMonitor::Notifier,
       Clipboard::Notifier,
-      UpdateTracker,
-      public SocketManager::EventHandler
+      public EventHandler
     {
     public:
-      SDisplay(const TCHAR* device=0);
+      SDisplay();
       virtual ~SDisplay();
 
       // -=- SDesktop interface
 
       virtual void start(VNCServer* vs);
       virtual void stop();
-      virtual void pointerEvent(const Point& pos, rdr::U8 buttonmask);
+      virtual void pointerEvent(const Point& pos, int buttonmask);
       virtual void keyEvent(rdr::U32 key, bool down);
       virtual void clientCutText(const char* str, int len);
       virtual void framebufferUpdateRequest();
       virtual Point getFbSize();
 
-      // -=- UpdateTracker
-
-      virtual void add_changed(const Region& rgn);
-      virtual void add_copied(const Region& dest, const Point& delta);
-
       // -=- Clipboard
       
       virtual void notifyClipboardChanged(const char* text, int len);
@@ -92,7 +82,7 @@
       // -=- EventHandler interface
 
       HANDLE getUpdateEvent() {return updateEvent;}
-      virtual bool processEvent(HANDLE event);
+      virtual void processEvent(HANDLE event);
 
       // -=- Notification of whether or not SDisplay is started
 
@@ -100,38 +90,62 @@
 
       friend class SDisplayCore;
 
-      static BoolParameter use_hooks;
+      static IntParameter updateMethod;
       static BoolParameter disableLocalInputs;
       static StringParameter disconnectAction;
       static BoolParameter removeWallpaper;
       static BoolParameter removePattern;
       static BoolParameter disableEffects;
 
-    protected:
-      void restart();
-      void recreatePixelBuffer();
-      bool flushChangeTracker();  // true if flushed, false if empty
+      // -=- Use by VNC Config to determine whether hooks, driver, etc are available
+      static bool areHooksAvailable();
+      static bool isDriverAvailable();
 
-      void triggerUpdate();
+
+    protected:
+      bool isRestartRequired();
+      void startCore();
+      void stopCore();
+      void restartCore();
+      void recreatePixelBuffer(bool force=false);
+      bool flushChangeTracker();  // true if flushed, false if empty
 
       VNCServer* server;
 
       // -=- Display pixel buffer
       DeviceFrameBuffer* pb;
-      TCharArray deviceName;
-      HDC device;
-      bool releaseDevice;
+      DeviceContext* device;
 
       // -=- The coordinates of Window's entire virtual Screen
       Rect screenRect;
 
-      // -=- All changes are collected in Display coords and merged
-      SimpleUpdateTracker change_tracker;
+      // -=- All changes are collected in UN-CLIPPED Display coords and merged
+      //     When they are to be flushed to the VNCServer, they are changed
+      //     to server coords and clipped appropriately.
+      SimpleUpdateTracker updates;
+      ClippingUpdateTracker clipper;
 
       // -=- Internal SDisplay implementation
       SDisplayCore* core;
+      int updateMethod_;
 
-      // -=- Cursor
+      // Inputs
+      SPointer* ptr;
+      SKeyboard* kbd;
+      Clipboard* clipboard;
+      WMBlockInput* inputs;
+
+      // Desktop state
+      WMMonitor* monitor;
+
+      // Desktop optimisation
+      CleanDesktop* cleanDesktop;
+      bool isWallpaperRemoved;
+      bool isPatternRemoved;
+      bool areEffectsDisabled;
+
+      // Cursor
+      WMCursor* cursor;
       WMCursor::Info old_cursor;
       Region old_cursor_region;
       Point cursor_renderpos;
diff --git a/rfb_win32/SDisplayCoreDriver.h b/rfb_win32/SDisplayCoreDriver.h
new file mode 100644
index 0000000..5fea75c
--- /dev/null
+++ b/rfb_win32/SDisplayCoreDriver.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2002-2005 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.
+ */
+
+// -=- SDisplayCoreDriver.h
+//
+// Placeholder for SDisplayCore mirror-driver implementation.
+
+#ifndef __RFB_SDISPLAY_CORE_DRIVER_H__
+#define __RFB_SDISPLAY_CORE_DRIVER_H__
+
+#include <rfb_win32/SDisplay.h>
+
+namespace rfb {
+  namespace win32 {
+
+    class SDisplayCoreDriver: public SDisplayCore {
+    public:
+      SDisplayCoreDriver(SDisplay* display, UpdateTracker* ut) {
+        throw rdr::Exception("Not supported");
+      }
+
+      // - Called by SDisplay to inform Core of the screen size
+      virtual void setScreenRect(const Rect& screenRect_) {}
+
+      // - Called by SDisplay to flush updates to the specified tracker
+      virtual void flushUpdates() {}
+
+      virtual const char* methodName() const { return "VNC Mirror Driver"; }
+
+      // - Determine whether the display driver is installed & usable
+      static bool isAvailable() { return false; }
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/SDisplayCorePolling.cxx b/rfb_win32/SDisplayCorePolling.cxx
new file mode 100644
index 0000000..fc57ecd
--- /dev/null
+++ b/rfb_win32/SDisplayCorePolling.cxx
@@ -0,0 +1,81 @@
+/* Copyright (C) 2002-2005 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.
+ */
+
+// -=- SDisplayCorePolling.cxx
+
+#include <rfb_win32/SDisplayCorePolling.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("SDisplayCorePolling");
+
+const int POLLING_SEGMENTS = 16;
+
+const int SDisplayCorePolling::pollTimerId = 1;
+
+SDisplayCorePolling::SDisplayCorePolling(SDisplay* d, UpdateTracker* ut, int pollInterval_)
+  : MsgWindow(_T("rfb::win32::SDisplayCorePolling")), updateTracker(ut),
+  pollTimer(getHandle(), pollTimerId), pollNextStrip(false), display(d) {
+  pollInterval = max(10, (pollInterval_ / POLLING_SEGMENTS));
+  copyrect.setUpdateTracker(ut);
+}
+
+SDisplayCorePolling::~SDisplayCorePolling() {
+}
+
+LRESULT SDisplayCorePolling::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+  if (msg == WM_TIMER && wParam == pollTimerId) {
+    pollNextStrip = true;
+    SetEvent(display->getUpdateEvent());
+    return 0;
+  }
+  return MsgWindow::processMessage(msg, wParam, lParam);
+}
+
+void SDisplayCorePolling::setScreenRect(const Rect& screenRect_) {
+  vlog.info("setScreenRect");
+  screenRect = screenRect_;
+  pollIncrementY = (screenRect.height()+POLLING_SEGMENTS-1)/POLLING_SEGMENTS;
+  pollNextY = screenRect.tl.y;
+  pollTimer.start(pollInterval);
+}
+
+void SDisplayCorePolling::flushUpdates() {
+  vlog.write(120, "flushUpdates");
+
+  // Check for window movement
+  while (copyrect.processEvent()) {}
+
+  if (pollNextStrip) {
+    // Poll the next strip of the screen (in Screen coordinates)
+    pollNextStrip = false;
+    Rect pollrect = screenRect;
+    if (pollNextY >= pollrect.br.y) {
+      // Yes.  Reset the counter and return
+      pollNextY = pollrect.tl.y;
+    } else {
+      // No.  Poll the next section
+      pollrect.tl.y = pollNextY;
+      pollNextY += pollIncrementY;
+      pollrect.br.y = min(pollNextY, pollrect.br.y);
+      updateTracker->add_changed(pollrect);
+    }
+  }
+}
diff --git a/rfb_win32/SDisplayCorePolling.h b/rfb_win32/SDisplayCorePolling.h
new file mode 100644
index 0000000..9e1b5ad
--- /dev/null
+++ b/rfb_win32/SDisplayCorePolling.h
@@ -0,0 +1,75 @@
+/* Copyright (C) 2002-2005 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.
+ */
+
+// -=- SDisplayCorePolling.h
+//
+// SDisplayCore implementation that simply polls the screen, in sections,
+// in order to detect changes.  This Core will signal the SDisplay's
+// updateEvent regularly, causing it to call the Core back to propagate
+// changes to the VNC Server.
+
+
+#ifndef __RFB_SDISPLAY_CORE_POLLING_H__
+#define __RFB_SDISPLAY_CORE_POLLING_H__
+
+#include <rfb_win32/SDisplay.h>
+#include <rfb_win32/IntervalTimer.h>
+#include <rfb_win32/WMWindowCopyRect.h>
+
+namespace rfb {
+  namespace win32 {
+
+    class SDisplayCorePolling : public SDisplayCore, protected MsgWindow {
+    public:
+      SDisplayCorePolling(SDisplay* display, UpdateTracker* ut, int pollIntervalMs=50);
+      ~SDisplayCorePolling();
+
+      // - Called by SDisplay to inform Core of the screen size
+      virtual void setScreenRect(const Rect& screenRect_);
+
+      // - Called by SDisplay to flush updates to the specified tracker
+      virtual void flushUpdates();
+
+      virtual const char* methodName() const { return "Polling"; }
+
+    protected:
+      // - MsgWindow overrides
+      //   processMessage is used to service the cursor & polling timers
+      virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+      // - Hooking subcomponents used to track the desktop state
+      WMCopyRect copyrect;
+
+      // - Background full screen polling fields
+      IntervalTimer pollTimer;
+      static const int pollTimerId;
+      Rect screenRect;
+      int pollInterval;
+      int pollNextY;
+      int pollIncrementY;
+      bool pollNextStrip;
+
+      // - Handle back to the owning SDisplay, and to the UpdateTracker to flush to
+      SDisplay* display;
+      UpdateTracker* updateTracker;
+    };
+
+  };
+};
+
+#endif
\ No newline at end of file
diff --git a/rfb_win32/SDisplayCoreWMHooks.cxx b/rfb_win32/SDisplayCoreWMHooks.cxx
new file mode 100644
index 0000000..10b88e0
--- /dev/null
+++ b/rfb_win32/SDisplayCoreWMHooks.cxx
@@ -0,0 +1,74 @@
+/* Copyright (C) 2002-2005 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.
+ */
+
+// -=- SDisplayCoreWMHooks.cxx
+
+#include <rfb_win32/SDisplayCoreWMHooks.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("SDisplayCoreWMHooks");
+
+const int SDisplayCoreWMHooks::cursorTimerId = 2;
+const int SDisplayCoreWMHooks::consolePollTimerId = 3;
+
+
+SDisplayCoreWMHooks::SDisplayCoreWMHooks(SDisplay* d, UpdateTracker* ut)
+  : SDisplayCorePolling(d, ut, 5000),
+  cursorTimer(getHandle(), cursorTimerId),
+  consolePollTimer(getHandle(), consolePollTimerId),
+  pollConsoles(false) {
+  if (!hooks.setEvent(display->getUpdateEvent()))
+    throw rdr::Exception("hook subsystem failed to initialise");
+  poller.setUpdateTracker(updateTracker);
+  cursorTimer.start(20);
+  consolePollTimer.start(200);
+}
+
+SDisplayCoreWMHooks::~SDisplayCoreWMHooks() {
+}
+
+LRESULT SDisplayCoreWMHooks::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+  if (msg == WM_TIMER) {
+    if (wParam == cursorTimerId) {
+      SetEvent(display->getUpdateEvent());
+      return 0;
+    } else if (wParam == consolePollTimerId) {
+      pollConsoles = true;
+      SetEvent(display->getUpdateEvent());
+      return 0;
+    }
+  }
+  return SDisplayCorePolling::processMessage(msg, wParam, lParam);
+}
+
+void SDisplayCoreWMHooks::flushUpdates() {
+  // Poll any visible console windows
+  if (pollConsoles) {
+    pollConsoles = false;
+    poller.processEvent();
+  }
+
+  // Check for updates from the hooks
+  hooks.getUpdates(updateTracker);
+
+  // Check for updates from the polling Core
+  SDisplayCorePolling::flushUpdates();
+}
diff --git a/rfb_win32/SDisplayCoreWMHooks.h b/rfb_win32/SDisplayCoreWMHooks.h
new file mode 100644
index 0000000..24fa5cd
--- /dev/null
+++ b/rfb_win32/SDisplayCoreWMHooks.h
@@ -0,0 +1,68 @@
+/* Copyright (C) 2002-2005 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.
+ */
+
+// -=- SDisplayCoreWMHooks.h
+//
+// SDisplayCore implementation that uses WMHooks to capture changes to
+// the display.
+// Whenever changes are detected, the SDisplay's updateEvent is signalled,
+// so that it can perform housekeeping tasks (like ensuring the currently
+// active desktop is the correct one), before flushing changes from the
+// Core to the VNC Server.  The SDisplay will clip the changes before they
+// reach the VNC Server.
+
+
+#ifndef __RFB_SDISPLAY_CORE_WMHOOKS_H__
+#define __RFB_SDISPLAY_CORE_WMHOOKS_H__
+
+#include <rfb_win32/SDisplayCorePolling.h>
+#include <rfb_win32/WMHooks.h>
+#include <rfb_win32/WMPoller.h>
+
+namespace rfb {
+  namespace win32 {
+
+    class SDisplayCoreWMHooks : public SDisplayCorePolling {
+    public:
+      SDisplayCoreWMHooks(SDisplay* display, UpdateTracker* ut);
+      ~SDisplayCoreWMHooks();
+
+      // - Called by SDisplay to flush updates to the specified tracker
+      virtual void flushUpdates();
+
+      virtual const char* methodName() const { return "VNC Hooks"; }
+
+    protected:
+      // - MsgWindow overrides
+      //   processMessage is used to service the cursor & polling timers
+      virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+      // - Hooking subcomponents used to track the desktop state
+      WMHooks hooks;
+      WMPoller poller;
+      IntervalTimer cursorTimer;
+      IntervalTimer consolePollTimer;
+      bool pollConsoles;
+      static const int consolePollTimerId;
+      static const int cursorTimerId;
+    };
+
+  };
+};
+
+#endif
diff --git a/rfb_win32/SInput.cxx b/rfb_win32/SInput.cxx
index 457a861..db59287 100644
--- a/rfb_win32/SInput.cxx
+++ b/rfb_win32/SInput.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -26,23 +26,30 @@
 #define XK_CURRENCY
 #include <rfb/keysymdef.h>
 
-// * Force the windows headers to include all the SendInput stuff
-#define _WIN32_WINNT 0x401
-
+#include <tchar.h>
 #include <rfb_win32/SInput.h>
+#include <rfb_win32/MonitorInfo.h>
 #include <rfb_win32/Service.h>
-#include <rfb/LogWriter.h>
 #include <rfb_win32/OSVersion.h>
-#include <rfb_win32/Win32Util.h>
-#include "keymap.h"
+#include <rfb_win32/DynamicFn.h>
+#include <rfb_win32/keymap.h>
+#include <rdr/Exception.h>
+#include <rfb/LogWriter.h>
+
+#if(defined(INPUT_MOUSE) && defined(RFB_HAVE_MONITORINFO))
+#define RFB_HAVE_SENDINPUT
+#else
+#pragma message("  NOTE: Not building SendInput support.")
+#endif
 
 using namespace rfb;
 
 static LogWriter vlog("SInput");
 
-
+#ifdef RFB_HAVE_SENDINPUT
 typedef UINT (WINAPI *_SendInput_proto)(UINT, LPINPUT, int);
 static win32::DynamicFn<_SendInput_proto> _SendInput(_T("user32.dll"), "SendInput");
+#endif
 
 //
 // -=- Pointer implementation for Win32
@@ -68,7 +75,7 @@
 }
 
 void
-win32::SPointer::pointerEvent(const Point& pos, rdr::U8 buttonmask)
+win32::SPointer::pointerEvent(const Point& pos, int buttonmask)
 {
   // - We are specifying absolute coordinates
   DWORD flags = MOUSEEVENTF_ABSOLUTE;
@@ -118,7 +125,7 @@
     // The event lies outside the primary monitor.  Under Win2K, we can just use
     // SendInput, which allows us to provide coordinates scaled to the virtual desktop.
     // SendInput is available on all multi-monitor-aware platforms.
-#ifdef SM_CXVIRTUALSCREEN
+#ifdef RFB_HAVE_SENDINPUT
     if (osVersion.isPlatformNT) {
       if (!_SendInput.isValid())
         throw rdr::Exception("SendInput not available");
diff --git a/rfb_win32/SInput.h b/rfb_win32/SInput.h
index dcd779e..2a0b3e6 100644
--- a/rfb_win32/SInput.h
+++ b/rfb_win32/SInput.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -32,8 +32,6 @@
 
 namespace rfb {
 
-  class CMsgWriter;
-
   namespace win32 {
 
     // -=- Pointer event handling
@@ -44,7 +42,7 @@
       // - Create a pointer event at a the given coordinates, with the
       //   specified button state.  The event must be specified using
       //   Screen coordinates.
-      void pointerEvent(const Point& pos, rdr::U8 buttonmask);
+      void pointerEvent(const Point& pos, int buttonmask);
     protected:
       Point last_position;
       rdr::U8 last_buttonmask;
diff --git a/rfb_win32/Security.cxx b/rfb_win32/Security.cxx
new file mode 100644
index 0000000..985f00c
--- /dev/null
+++ b/rfb_win32/Security.cxx
@@ -0,0 +1,192 @@
+/* Copyright (C) 2002-2005 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.
+ */
+
+// -=- Security.cxx
+
+#include <rfb_win32/Security.h>
+#include <rfb_win32/DynamicFn.h>
+#include <rfb/LogWriter.h>
+
+#include <lmcons.h>
+#include <Accctrl.h>
+#include <list>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("SecurityWin32");
+
+
+Trustee::Trustee(const TCHAR* name,
+                 TRUSTEE_FORM form,
+                 TRUSTEE_TYPE type) {
+  pMultipleTrustee = 0;
+  MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
+  TrusteeForm = form;
+  TrusteeType = type;
+  ptstrName = (TCHAR*)name;
+}
+
+
+ExplicitAccess::ExplicitAccess(const TCHAR* name,
+                               TRUSTEE_FORM type,
+                               DWORD perms,
+                               ACCESS_MODE mode,
+                               DWORD inherit) {
+  Trustee = rfb::win32::Trustee(name, type);
+  grfAccessPermissions = perms;
+  grfAccessMode = mode;
+  grfInheritance = inherit;
+}
+
+
+AccessEntries::AccessEntries() : entries(0), entry_count(0) {}
+
+AccessEntries::~AccessEntries() {
+  delete [] entries;
+}
+
+void AccessEntries::allocMinEntries(int count) {
+  if (count > entry_count) {
+    EXPLICIT_ACCESS* new_entries = new EXPLICIT_ACCESS[entry_count+1];
+    if (entries) {
+      memcpy(new_entries, entries, sizeof(EXPLICIT_ACCESS) * entry_count);
+      delete entries;
+    }
+    entries = new_entries;
+  }
+}
+
+void AccessEntries::addEntry(const TCHAR* trusteeName,
+                             DWORD permissions,
+                             ACCESS_MODE mode) {
+  allocMinEntries(entry_count+1);
+  ZeroMemory(&entries[entry_count], sizeof(EXPLICIT_ACCESS));
+  entries[entry_count] = ExplicitAccess(trusteeName, TRUSTEE_IS_NAME, permissions, mode);
+  entry_count++;
+}
+
+void AccessEntries::addEntry(const PSID sid,
+                             DWORD permissions,
+                             ACCESS_MODE mode) {
+  allocMinEntries(entry_count+1);
+  ZeroMemory(&entries[entry_count], sizeof(EXPLICIT_ACCESS));
+  entries[entry_count] = ExplicitAccess((TCHAR*)sid, TRUSTEE_IS_SID, permissions, mode);
+  entry_count++;
+}
+
+
+PSID Sid::copySID(const PSID sid) {
+  if (!IsValidSid(sid))
+    throw rdr::Exception("invalid SID in copyPSID");
+  PSID buf = (PSID)new rdr::U8[GetLengthSid(sid)];
+  if (!CopySid(GetLengthSid(sid), buf, sid))
+    throw rdr::SystemException("CopySid failed", GetLastError());
+  return buf;
+}
+
+void Sid::setSID(const PSID sid) {
+  delete [] buf;
+  buf = (rdr::U8*)copySID(sid);
+}
+
+void Sid::getUserNameAndDomain(TCHAR** name, TCHAR** domain) {
+  DWORD nameLen = 0;
+  DWORD domainLen = 0;
+  SID_NAME_USE use;
+  LookupAccountSid(0, (PSID)buf, 0, &nameLen, 0, &domainLen, &use);
+  if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+    throw rdr::SystemException("Unable to determine SID name lengths", GetLastError());
+  vlog.info("nameLen=%d, domainLen=%d, use=%d", nameLen, domainLen, use);
+  *name = new TCHAR[nameLen];
+  *domain = new TCHAR[domainLen];
+  if (!LookupAccountSid(0, (PSID)buf, *name, &nameLen, *domain, &domainLen, &use))
+    throw rdr::SystemException("Unable to lookup account SID", GetLastError());
+}
+
+
+Sid::Administrators::Administrators() {
+  PSID sid = 0;
+  SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY;
+  if (!AllocateAndInitializeSid(&ntAuth, 2,
+                                SECURITY_BUILTIN_DOMAIN_RID,
+                                DOMAIN_ALIAS_RID_ADMINS,
+                                0, 0, 0, 0, 0, 0, &sid)) 
+    throw rdr::SystemException("Sid::Administrators", GetLastError());
+  setSID(sid);
+  FreeSid(sid);
+}
+
+Sid::SYSTEM::SYSTEM() {
+  PSID sid = 0;
+  SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY;
+  if (!AllocateAndInitializeSid(&ntAuth, 1,
+                                SECURITY_LOCAL_SYSTEM_RID,
+                                0, 0, 0, 0, 0, 0, 0, &sid))
+          throw rdr::SystemException("Sid::SYSTEM", GetLastError());
+  setSID(sid);
+  FreeSid(sid);
+}
+
+Sid::FromToken::FromToken(HANDLE h) {
+  DWORD required = 0;
+  GetTokenInformation(h, TokenUser, 0, 0, &required);
+  rdr::U8Array tmp(required);
+  if (!GetTokenInformation(h, TokenUser, tmp.buf, required, &required))
+    throw rdr::SystemException("GetTokenInformation", GetLastError());
+  TOKEN_USER* tokenUser = (TOKEN_USER*)tmp.buf;
+  setSID(tokenUser->User.Sid);
+}
+
+
+PACL rfb::win32::CreateACL(const AccessEntries& ae, PACL existing_acl) {
+  typedef DWORD (WINAPI *_SetEntriesInAcl_proto) (ULONG, PEXPLICIT_ACCESS, PACL, PACL*);
+#ifdef UNICODE
+  const char* fnName = "SetEntriesInAclW";
+#else
+  const char* fnName = "SetEntriesInAclA";
+#endif
+  DynamicFn<_SetEntriesInAcl_proto> _SetEntriesInAcl(_T("advapi32.dll"), fnName);
+  if (!_SetEntriesInAcl.isValid())
+    throw rdr::SystemException("CreateACL failed; no SetEntriesInAcl", ERROR_CALL_NOT_IMPLEMENTED);
+  PACL new_dacl;
+  DWORD result;
+  if ((result = (*_SetEntriesInAcl)(ae.entry_count, ae.entries, existing_acl, &new_dacl)) != ERROR_SUCCESS)
+    throw rdr::SystemException("SetEntriesInAcl", result);
+  return new_dacl;
+}
+
+
+PSECURITY_DESCRIPTOR rfb::win32::CreateSdWithDacl(const PACL dacl) {
+  SECURITY_DESCRIPTOR absSD;
+  if (!InitializeSecurityDescriptor(&absSD, SECURITY_DESCRIPTOR_REVISION))
+    throw rdr::SystemException("InitializeSecurityDescriptor", GetLastError());
+  Sid::SYSTEM owner;
+  if (!SetSecurityDescriptorOwner(&absSD, owner, FALSE))
+    throw rdr::SystemException("SetSecurityDescriptorOwner", GetLastError());
+  Sid::Administrators group;
+  if (!SetSecurityDescriptorGroup(&absSD, group, FALSE))
+    throw rdr::SystemException("SetSecurityDescriptorGroupp", GetLastError());
+  if (!SetSecurityDescriptorDacl(&absSD, TRUE, dacl, FALSE))
+    throw rdr::SystemException("SetSecurityDescriptorDacl", GetLastError());
+  DWORD sdSize = GetSecurityDescriptorLength(&absSD);
+  SecurityDescriptorPtr sd(sdSize);
+  if (!MakeSelfRelativeSD(&absSD, sd, &sdSize))
+    throw rdr::SystemException("MakeSelfRelativeSD", GetLastError());
+  return sd.takeSD();
+}
diff --git a/rfb_win32/Security.h b/rfb_win32/Security.h
index d92e314..1e2e906 100644
--- a/rfb_win32/Security.h
+++ b/rfb_win32/Security.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -25,16 +25,10 @@
 #define __RFB_WIN32_SECURITY_H__
 
 #include <rdr/types.h>
-#include <rdr/Exception.h>
-#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/LocalMem.h>
 #include <rfb_win32/TCharArray.h>
-
-#include <lmcons.h>
-#include <Accctrl.h>
 #include <aclapi.h>
 
-#include <list>
-
 namespace rfb {
 
   namespace win32 {
@@ -42,14 +36,7 @@
     struct Trustee : public TRUSTEE {
       Trustee(const TCHAR* name,
               TRUSTEE_FORM form=TRUSTEE_IS_NAME,
-              TRUSTEE_TYPE type=TRUSTEE_IS_UNKNOWN)
-      {
-        pMultipleTrustee = 0;
-        MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
-        TrusteeForm = form;
-        TrusteeType = type;
-        ptstrName = (TCHAR*)name;
-      }
+              TRUSTEE_TYPE type=TRUSTEE_IS_UNKNOWN);
     };
 
     struct ExplicitAccess : public EXPLICIT_ACCESS {
@@ -57,86 +44,56 @@
                      TRUSTEE_FORM type,
                      DWORD perms,
                      ACCESS_MODE mode,
-                     DWORD inherit=0)
-      {
-        Trustee = rfb::win32::Trustee(name, type);
-        grfAccessPermissions = perms;
-        grfAccessMode = mode;
-        grfInheritance = inherit;
-      }
+                     DWORD inherit=0);
     };
 
     // Helper class for building access control lists
     struct AccessEntries {
-      AccessEntries() : entries(0), entry_count(0) {}
-      ~AccessEntries() {delete [] entries;}
-      void allocMinEntries(int count) {
-        if (count > entry_count) {
-          EXPLICIT_ACCESS* new_entries = new EXPLICIT_ACCESS[entry_count+1];
-          if (entries) {
-            memcpy(new_entries, entries, sizeof(EXPLICIT_ACCESS) * entry_count);
-            delete entries;
-          }
-          entries = new_entries;
-        }
-      }
+      AccessEntries();
+      ~AccessEntries();
+      void allocMinEntries(int count);
       void addEntry(const TCHAR* trusteeName,
                     DWORD permissions,
-                    ACCESS_MODE mode)
-      {
-        allocMinEntries(entry_count+1);
-        ZeroMemory(&entries[entry_count], sizeof(EXPLICIT_ACCESS));
-        entries[entry_count] = ExplicitAccess(trusteeName, TRUSTEE_IS_NAME, permissions, mode);
-        entry_count++;
-      }
-      void addEntry(const PSID sid, DWORD permissions, ACCESS_MODE mode) {
-        allocMinEntries(entry_count+1);
-        ZeroMemory(&entries[entry_count], sizeof(EXPLICIT_ACCESS));
-        entries[entry_count] = ExplicitAccess((TCHAR*)sid, TRUSTEE_IS_SID, permissions, mode);
-        entry_count++;
-      }
+                    ACCESS_MODE mode);
+      void addEntry(const PSID sid,
+                    DWORD permissions,
+                    ACCESS_MODE mode);
 
       EXPLICIT_ACCESS* entries;
       int entry_count;
     };
 
     // Helper class for handling SIDs
-    struct Sid {
-      Sid() : sid(0) {}
-      Sid(PSID sid_) : sid(sid_) {}
-      ~Sid() {
-        if (sid) FreeSid(sid);
-      }
-      operator PSID() const {return sid;}
-      PSID operator=(const PSID sid_) {
-        if (sid) FreeSid(sid);
-        sid = sid_;
-      }
+    struct Sid : rdr::U8Array {
+      Sid() {}
+      operator PSID() const {return (PSID)buf;}
+      PSID takePSID() {PSID r = (PSID)buf; buf = 0; return r;}
 
-      static PSID Administrators() {
-        PSID sid = 0;
-        SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY;
-        if (!AllocateAndInitializeSid(&ntAuth, 2,
-          SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
-          0, 0, 0, 0, 0, 0,
-          &sid)) 
-          throw rdr::SystemException("Sid::Administrators", GetLastError());
-        return sid;
-      }
-      static PSID SYSTEM() {
-        PSID sid = 0;
-        SID_IDENTIFIER_AUTHORITY ntAuth = SECURITY_NT_AUTHORITY;
-        if (!AllocateAndInitializeSid(&ntAuth, 1,
-          SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0,
-          &sid))
-          throw rdr::SystemException("Sid::SYSTEM", GetLastError());
-        return sid;
-      }
+      static PSID copySID(const PSID sid);
 
-    protected:
-      PSID sid;
+      void setSID(const PSID sid);
+
+      void getUserNameAndDomain(TCHAR** name, TCHAR** domain);
+
+      struct Administrators;
+      struct SYSTEM;
+      struct FromToken;
+
+    private:
+      Sid(const Sid&);
+      Sid& operator=(const Sid&);
     };
       
+    struct Sid::Administrators : public Sid {
+      Administrators();
+    };
+    struct Sid::SYSTEM : public Sid {
+      SYSTEM();
+    };
+    struct Sid::FromToken : public Sid {
+      FromToken(HANDLE h);
+    };
+
     // Helper class for handling & freeing ACLs
     struct AccessControlList : public LocalMem {
       AccessControlList(int size) : LocalMem(size) {}
@@ -145,22 +102,7 @@
     };
 
     // Create a new ACL based on supplied entries and, if supplied, existing ACL 
-    static PACL CreateACL(const AccessEntries& ae, PACL existing_acl=0) {
-      typedef DWORD (WINAPI *_SetEntriesInAcl_proto) (ULONG, PEXPLICIT_ACCESS, PACL, PACL*);
-#ifdef UNICODE
-      const char* fnName = "SetEntriesInAclW";
-#else
-      const char* fnName = "SetEntriesInAclA";
-#endif
-      DynamicFn<_SetEntriesInAcl_proto> _SetEntriesInAcl(_T("advapi32.dll"), fnName);
-      if (!_SetEntriesInAcl.isValid())
-        throw rdr::SystemException("CreateACL failed; no SetEntriesInAcl", ERROR_CALL_NOT_IMPLEMENTED);
-      PACL new_dacl;
-      DWORD result;
-      if ((result = (*_SetEntriesInAcl)(ae.entry_count, ae.entries, existing_acl, &new_dacl)) != ERROR_SUCCESS)
-        throw rdr::SystemException("SetEntriesInAcl", result);
-      return new_dacl;
-    }
+    PACL CreateACL(const AccessEntries& ae, PACL existing_acl=0);
 
     // Helper class for memory-management of self-relative SecurityDescriptors
     struct SecurityDescriptorPtr : LocalMem {
@@ -172,24 +114,7 @@
     // Create a new self-relative Security Descriptor, owned by SYSTEM/Administrators,
     //   with the supplied DACL and no SACL.  The returned value can be assigned
     //   to a SecurityDescriptorPtr to be managed.
-    static PSECURITY_DESCRIPTOR CreateSdWithDacl(const PACL dacl) {
-      SECURITY_DESCRIPTOR absSD;
-      if (!InitializeSecurityDescriptor(&absSD, SECURITY_DESCRIPTOR_REVISION))
-        throw rdr::SystemException("InitializeSecurityDescriptor", GetLastError());
-      Sid owner(Sid::SYSTEM());
-      if (!SetSecurityDescriptorOwner(&absSD, owner, FALSE))
-        throw rdr::SystemException("SetSecurityDescriptorOwner", GetLastError());
-      Sid group(Sid::Administrators());
-      if (!SetSecurityDescriptorGroup(&absSD, group, FALSE))
-        throw rdr::SystemException("SetSecurityDescriptorGroupp", GetLastError());
-      if (!SetSecurityDescriptorDacl(&absSD, TRUE, dacl, FALSE))
-        throw rdr::SystemException("SetSecurityDescriptorDacl", GetLastError());
-      DWORD sdSize = GetSecurityDescriptorLength(&absSD);
-      SecurityDescriptorPtr sd(sdSize);
-      if (!MakeSelfRelativeSD(&absSD, sd, &sdSize))
-        throw rdr::SystemException("MakeSelfRelativeSD", GetLastError());
-      return sd.takeSD();
-    }
+    PSECURITY_DESCRIPTOR CreateSdWithDacl(const PACL dacl);
 
   }
 
diff --git a/rfb_win32/Service.cxx b/rfb_win32/Service.cxx
index b00c290..2b11a22 100644
--- a/rfb_win32/Service.cxx
+++ b/rfb_win32/Service.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -20,15 +20,15 @@
 
 #include <rfb_win32/Service.h>
 #include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/DynamicFn.h>
+#include <rfb_win32/ModuleFileName.h>
 #include <rfb_win32/Registry.h>
-#include <rfb_win32/Win32Util.h>
 #include <rfb_win32/OSVersion.h>
 #include <rfb/Threading.h>
-#include <rfb/LogWriter.h>
-#include <rfb/util.h>
-#include <rdr/Exception.h>
-
 #include <logmessages/messages.h>
+#include <rdr/Exception.h>
+#include <rfb/LogWriter.h>
+
 
 using namespace rdr;
 using namespace rfb;
@@ -42,22 +42,26 @@
 Service* service = 0;
 
 VOID WINAPI serviceHandler(DWORD control) {
-  vlog.debug("service control %u", control);
   switch (control) {
   case SERVICE_CONTROL_INTERROGATE:
+    vlog.info("cmd: report status");
     service->setStatus();
-    break;
+    return;
   case SERVICE_CONTROL_PARAMCHANGE:
+    vlog.info("cmd: param change");
     service->readParams();
-    break;
+    return;
   case SERVICE_CONTROL_SHUTDOWN:
+    vlog.info("cmd: OS shutdown");
     service->osShuttingDown();
-    break;
+    return;
   case SERVICE_CONTROL_STOP:
+    vlog.info("cmd: stop");
     service->setStatus(SERVICE_STOP_PENDING);
     service->stop();
-    break;
-  }
+    return;
+  };
+  vlog.debug("cmd: unknown %lu", control);
 }
 
 
@@ -87,11 +91,14 @@
 
 VOID WINAPI serviceProc(DWORD dwArgc, LPTSTR* lpszArgv) {
   vlog.debug("entering %s serviceProc", service->getName());
+  vlog.info("registering handler...");
   service->status_handle = RegisterServiceCtrlHandler(service->getName(), serviceHandler);
   if (!service->status_handle) {
-    vlog.error("unable to register service control handler");
-    return;
+    DWORD err = GetLastError();
+    vlog.error("failed to register handler: %lu", err);
+    ExitProcess(err);
   }
+  vlog.debug("registered handler (%lx)", service->status_handle);
   service->setStatus(SERVICE_START_PENDING);
   vlog.debug("entering %s serviceMain", service->getName());
   service->status.dwWin32ExitCode = service->serviceMain(dwArgc, lpszArgv);
@@ -166,9 +173,9 @@
   status.dwCurrentState = state;
   status.dwCheckPoint++;
   if (!SetServiceStatus(status_handle, &status)) {
+    status.dwCurrentState = SERVICE_STOPPED;
     status.dwWin32ExitCode = GetLastError();
     vlog.error("unable to set service status:%u", status.dwWin32ExitCode);
-    stop();
   }
   vlog.debug("set status to %u(%u)", state, status.dwCheckPoint);
 }
@@ -597,7 +604,7 @@
   return true;
 }
 
-void rfb::win32::printServiceStatus(const TCHAR* name) {
+DWORD rfb::win32::getServiceState(const TCHAR* name) {
   if (osVersion.isPlatformNT) {
     // - Open the SCM
     ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
@@ -614,25 +621,25 @@
     if (!ControlService(service, SERVICE_CONTROL_INTERROGATE, (SERVICE_STATUS*)&status))
       throw rdr::SystemException("unable to query the service", GetLastError());
 
-    printf("Service is in the ");
-    switch (status.dwCurrentState) {
-    case SERVICE_RUNNING: printf("running"); break;
-    case SERVICE_STOPPED: printf("stopped"); break;
-    case SERVICE_STOP_PENDING: printf("stop pending"); break;
-    default: printf("unknown (%lu)", status.dwCurrentState); break;
-    };
-    printf(" state.\n");
-
+    return status.dwCurrentState;
   } else {
     HWND service_window = findServiceWindow(name);
-    printf("Service is in the ");
-    if (!service_window) printf("stopped");
-    else printf("running");
-    printf(" state.\n");
+    return service_window ? SERVICE_RUNNING : SERVICE_STOPPED;
   }
 }
 
+char* rfb::win32::serviceStateName(DWORD state) {
+  switch (state) {
+  case SERVICE_RUNNING: return strDup("Running");
+  case SERVICE_STOPPED: return strDup("Stopped");
+  case SERVICE_STOP_PENDING: return strDup("Stopping");
+  };
+  CharArray tmp(32);
+  sprintf(tmp.buf, "Unknown (%lu)", state);
+  return tmp.takeBuf();
+}
+
 
 bool rfb::win32::isServiceProcess() {
   return service != 0;
-}
\ No newline at end of file
+}
diff --git a/rfb_win32/Service.h b/rfb_win32/Service.h
index 164381a..00abe10 100644
--- a/rfb_win32/Service.h
+++ b/rfb_win32/Service.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -27,7 +27,6 @@
 #ifndef __RFB_WIN32_SERVICE_H__
 #define __RFB_WIN32_SERVICE_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 
 namespace rfb {
@@ -61,13 +60,13 @@
       // - Service control notifications
 
       // To get notified when the OS is shutting down
-      virtual void osShuttingDown() = 0;
+      virtual void osShuttingDown() {};
 
       // To get notified when the service parameters change
-      virtual void readParams() = 0;
+      virtual void readParams() {};
 
       // To cause the serviceMain() routine to return
-      virtual void stop() = 0;
+      virtual void stop() {};
 
     public:
       SERVICE_STATUS_HANDLE status_handle;
@@ -111,7 +110,13 @@
 
     bool startService(const TCHAR* name);
     bool stopService(const TCHAR* name);
-    void printServiceStatus(const TCHAR* name);
+
+    // -=- Get the state of the named service (one of the NT service state values)
+    DWORD getServiceState(const TCHAR* name);
+
+    // -=- Convert a supplied service state value to a printable string e.g. Running, Stopped...
+    //     The caller must delete the returned string buffer
+    char* serviceStateName(DWORD state);
 
     // -=- Routine to determine whether the host process is running a service
     bool isServiceProcess();
diff --git a/rfb_win32/SocketManager.cxx b/rfb_win32/SocketManager.cxx
index 6d1980c..1d52bc8 100644
--- a/rfb_win32/SocketManager.cxx
+++ b/rfb_win32/SocketManager.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -18,10 +18,8 @@
 
 // -=- SocketManager.cxx
 
-#define WIN32_LEAN_AND_MEAN
 #include <winsock2.h>
-#include <assert.h>
-
+#include <list>
 #include <rfb/LogWriter.h>
 #include <rfb_win32/SocketManager.h>
 
@@ -33,218 +31,183 @@
 
 // -=- SocketManager
 
-SocketManager::SocketManager() : sockets(0), events(0), nSockets(0), nAvail(0) {
+SocketManager::SocketManager() {
 }
 
 SocketManager::~SocketManager() {
-  for (int i=0; i<nSockets; i++) {
-    if (!sockets[i].is_event)
-      WSACloseEvent(events[i]);
-  }
-  delete [] events;
-  delete [] sockets;
 }
 
 
-void SocketManager::addListener(network::SocketListener* sock_, network::SocketServer* srvr) {
+static requestAddressChangeEvents(network::SocketListener* sock_) {
+  DWORD dummy = 0;
+  if (WSAIoctl(sock_->getFd(), SIO_ADDRESS_LIST_CHANGE, 0, 0, 0, 0, &dummy, 0, 0) == SOCKET_ERROR) {
+    DWORD err = WSAGetLastError();
+    if (err != WSAEWOULDBLOCK)
+      vlog.error("Unable to track address changes", err);
+  }
+}
+
+
+void SocketManager::addListener(network::SocketListener* sock_,
+                                network::SocketServer* srvr,
+                                AddressChangeNotifier* acn) {
   WSAEVENT event = WSACreateEvent();
-  assert(event != WSA_INVALID_EVENT);
-  addListener(sock_, event, srvr);
-}
+  long flags = FD_ACCEPT | FD_CLOSE;
+  if (acn)
+    flags |= FD_ADDRESS_LIST_CHANGE;
+  try {
+    if (event && (WSAEventSelect(sock_->getFd(), event, flags) == SOCKET_ERROR))
+      throw rdr::SystemException("Unable to select on listener", WSAGetLastError());
 
-void SocketManager::addSocket(network::Socket* sock_, network::SocketServer* srvr) {
-  WSAEVENT event = WSACreateEvent();
-  assert(event != WSA_INVALID_EVENT);
-  addSocket(sock_, event, srvr);
-}
+    // requestAddressChangeEvents MUST happen after WSAEventSelect, so that the socket is non-blocking
+    if (acn)
+      requestAddressChangeEvents(sock_);
 
-
-BOOL SocketManager::getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg) {
-  while (true) {
-    // First check for idle timeout
-
-    network::SocketServer* server = 0;
-    int timeout = 0;
-    for (int i=0; i<nSockets; i++) {
-      if (!sockets[i].is_event &&
-          sockets[i].server != server) {
-        server = sockets[i].server;
-        int t = server->checkTimeouts();
-        if (t > 0 && (timeout == 0 || t < timeout))
-          timeout = t;
-      }
-    }
-    if (timeout == 0)
-      timeout = INFINITE;
-
-    // - Network IO is less common than messages - process it first
-    DWORD result;
-    if (nSockets) {
-      result = WaitForMultipleObjects(nSockets, events, FALSE, 0);
-      if (result == WAIT_TIMEOUT) {
-        if (PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE)) 
-          return msg->message != WM_QUIT;
-
-        result = MsgWaitForMultipleObjects(nSockets, events, FALSE, timeout,
-                                           QS_ALLINPUT);
-        if (result == WAIT_OBJECT_0 + nSockets) {
-          if (PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE)) 
-            return msg->message != WM_QUIT;
-          continue;
-        }
-      }
-    } else
-      return GetMessage(msg, hwnd, minMsg, maxMsg);
-
-    if ((result >= WAIT_OBJECT_0) && (result < (WAIT_OBJECT_0 + nSockets))) {
-      int index = result - WAIT_OBJECT_0;
-
-      // - Process a socket event
-
-      if (sockets[index].is_event) {
-        // Process a general Win32 event
-        // NB: The handler must reset the event!
-
-        if (!sockets[index].handler->processEvent(events[index])) {
-          removeSocket(index);
-          continue;
-        }
-      } else if (sockets[index].is_conn) {
-        // Process data from an active connection
-
-        // Cancel event notification for this socket
-        if (WSAEventSelect(sockets[index].fd, events[index], 0) == SOCKET_ERROR)
-          vlog.info("unable to disable WSAEventSelect:%u", WSAGetLastError());
-
-        // Reset the event object
-        WSAResetEvent(events[index]);
-
-        // Call the socket server to process the event
-        if (!sockets[index].server->processSocketEvent(sockets[index].sock.conn)) {
-          removeSocket(index);
-          continue;
-        }
-
-        // Re-instate the required socket event
-        // If the read event is still valid, the event object gets set here
-        if (WSAEventSelect(sockets[index].fd, events[index], FD_READ | FD_CLOSE) == SOCKET_ERROR)
-          throw rdr::SystemException("unable to re-enable WSAEventSelect:%u", WSAGetLastError());
-
-      } else {
-        // Accept an incoming connection
-        vlog.debug("accepting incoming connection");
-
-        // What kind of event is this?
-        WSANETWORKEVENTS network_events;
-        WSAEnumNetworkEvents(sockets[index].fd, events[index], &network_events);
-        if (network_events.lNetworkEvents & FD_ACCEPT) {
-          network::Socket* new_sock = sockets[index].sock.listener->accept();
-          if ((sockets[index].server)->getDisable()) {
-            delete new_sock;
-            new_sock = 0;
-          }
-          if (new_sock) {
-            sockets[index].server->addClient(new_sock);
-            addSocket(new_sock, sockets[index].server);
-          }
-        } else if (network_events.lNetworkEvents & FD_CLOSE) {
-          vlog.info("deleting listening socket");
-          network::SocketListener* s = sockets[index].sock.listener;
-          removeSocket(index);
-          delete s;
-        } else {
-          vlog.error("unknown network event for listener");
-        }
-
-      }
-    } else if (result == WAIT_FAILED) {
-      throw rdr::SystemException("unable to wait for events", GetLastError());
-    }
+    // addEvent is the last thing we do, so that the event is NOT registered if previous steps fail
+    if (!event || !addEvent(event, this))
+      throw rdr::Exception("Unable to add listener");
+  } catch (rdr::Exception& e) {
+    if (event)
+      WSACloseEvent(event);
+    delete sock_;
+    vlog.error(e.str());
+    throw;
   }
-}
 
-
-void SocketManager::resizeArrays(int numSockets) {
-  if (nAvail >= numSockets) return;
-  while (nAvail < numSockets)
-    nAvail = max(16, nAvail*2);
-
-  SocketInfo* newinfo = new SocketInfo[nAvail];
-  HANDLE* newevents = new HANDLE[nAvail];
-  for (int i=0; i<nSockets; i++) {
-    newinfo[i] = sockets[i];
-    newevents[i] = events[i];
-  }
-  delete [] sockets;
-  delete [] events;
-  sockets = newinfo;
-  events = newevents;
-}
-
-void SocketManager::addSocket(network::Socket* sock, HANDLE event, network::SocketServer* server) {
-  resizeArrays(nSockets+1);
-
-  sockets[nSockets].sock.conn = sock;
-  sockets[nSockets].fd = sock->getFd();
-  sockets[nSockets].server = server;
-  events[nSockets] = event;
-  sockets[nSockets].is_conn = true;
-  sockets[nSockets].is_event = false;
-
-  if (WSAEventSelect(sock->getFd(), event, FD_READ | FD_CLOSE) == SOCKET_ERROR)
-    throw rdr::SystemException("unable to select on socket", WSAGetLastError());
-  nSockets++;
-}
-
-void SocketManager::addListener(network::SocketListener* sock, HANDLE event, network::SocketServer* server) {
-  resizeArrays(nSockets+1);
-
-  sockets[nSockets].sock.listener = sock;
-  sockets[nSockets].fd = sock->getFd();
-  sockets[nSockets].server = server;
-  events[nSockets] = event;
-  sockets[nSockets].is_conn = false;
-  sockets[nSockets].is_event = false;
-
-  if (WSAEventSelect(sock->getFd(), event, FD_ACCEPT | FD_CLOSE) == SOCKET_ERROR)
-    throw rdr::SystemException("unable to select on listener", WSAGetLastError());
-  nSockets++;
+  ListenInfo li;
+  li.sock = sock_;
+  li.server = srvr;
+  li.notifier = acn;
+  listeners[event] = li;
 }
 
 void SocketManager::remListener(network::SocketListener* sock) {
-  for (int index=0; index<nSockets; index++) {
-    if (!sockets[index].is_conn &&
-        !sockets[index].is_event) {
-      vlog.debug("removing listening socket");
-      removeSocket(index);
+  std::map<HANDLE,ListenInfo>::iterator i;
+  for (i=listeners.begin(); i!=listeners.end(); i++) {
+    if (i->second.sock == sock) {
+      removeEvent(i->first);
+      WSACloseEvent(i->first);
       delete sock;
+      listeners.erase(i);
+      return;
+    }
+  }
+  throw rdr::Exception("Listener not registered");
+}
+
+
+void SocketManager::addSocket(network::Socket* sock_, network::SocketServer* srvr, bool outgoing) {
+  WSAEVENT event = WSACreateEvent();
+  if (!event || !addEvent(event, this) ||
+      (WSAEventSelect(sock_->getFd(), event, FD_READ | FD_CLOSE) == SOCKET_ERROR)) {
+    if (event)
+      WSACloseEvent(event);
+    delete sock_;
+    vlog.error("Unable to add connection");
+    return;
+  }
+  ConnInfo ci;
+  ci.sock = sock_;
+  ci.server = srvr;
+  connections[event] = ci;
+  srvr->addSocket(sock_, outgoing);
+}
+
+void SocketManager::remSocket(network::Socket* sock_) {
+  std::map<HANDLE,ConnInfo>::iterator i;
+  for (i=connections.begin(); i!=connections.end(); i++) {
+    if (i->second.sock == sock_) {
+      i->second.server->removeSocket(sock_);
+      removeEvent(i->first);
+      WSACloseEvent(i->first);
+      delete sock_;
+      connections.erase(i);
+      return;
+    }
+  }
+  throw rdr::Exception("Socket not registered");
+}
+
+
+int SocketManager::checkTimeouts() {
+  network::SocketServer* server = 0;
+  int timeout = EventManager::checkTimeouts();
+
+  std::map<HANDLE,ListenInfo>::iterator i;
+  for (i=listeners.begin(); i!=listeners.end(); i++)
+    soonestTimeout(&timeout, i->second.server->checkTimeouts());
+
+  std::list<network::Socket*> shutdownSocks;
+  std::map<HANDLE,ConnInfo>::iterator j, j_next;
+  for (j=connections.begin(); j!=connections.end(); j=j_next) {
+    j_next = j; j_next++;
+    if (j->second.sock->isShutdown())
+      shutdownSocks.push_back(j->second.sock);
+  }
+
+  std::list<network::Socket*>::iterator k;
+  for (k=shutdownSocks.begin(); k!=shutdownSocks.end(); k++)
+    remSocket(*k);
+
+  return timeout;
+}
+
+
+void SocketManager::processEvent(HANDLE event) {
+  if (listeners.count(event)) {
+    ListenInfo li = listeners[event];
+
+    // Accept an incoming connection
+    vlog.debug("accepting incoming connection");
+
+    // What kind of event is this?
+    WSANETWORKEVENTS network_events;
+    WSAEnumNetworkEvents(li.sock->getFd(), event, &network_events);
+    if (network_events.lNetworkEvents & FD_ACCEPT) {
+      network::Socket* new_sock = li.sock->accept();
+      if (new_sock && li.server->getDisable()) {
+        delete new_sock;
+        new_sock = 0;
+      }
+      if (new_sock)
+        addSocket(new_sock, li.server, false);
+    } else if (network_events.lNetworkEvents & FD_CLOSE) {
+      vlog.info("deleting listening socket");
+      remListener(li.sock);
+    } else if (network_events.lNetworkEvents & FD_ADDRESS_LIST_CHANGE) {
+      li.notifier->processAddressChange(li.sock);
+      DWORD dummy = 0;
+      requestAddressChangeEvents(li.sock);
+    } else {
+      vlog.error("unknown listener event: %lx", network_events.lNetworkEvents);
+    }
+  } else if (connections.count(event)) {
+    ConnInfo ci = connections[event];
+
+    try {
+      // Process data from an active connection
+
+      // Cancel event notification for this socket
+      if (WSAEventSelect(ci.sock->getFd(), event, 0) == SOCKET_ERROR)
+        throw rdr::SystemException("unable to disable WSAEventSelect:%u", WSAGetLastError());
+
+      // Reset the event object
+      WSAResetEvent(event);
+
+      // Call the socket server to process the event
+      ci.server->processSocketEvent(ci.sock);
+      if (ci.sock->isShutdown()) {
+        remSocket(ci.sock);
+        return;
+      }
+
+      // Re-instate the required socket event
+      // If the read event is still valid, the event object gets set here
+      if (WSAEventSelect(ci.sock->getFd(), event, FD_READ | FD_CLOSE) == SOCKET_ERROR)
+        throw rdr::SystemException("unable to re-enable WSAEventSelect:%u", WSAGetLastError());
+    } catch (rdr::Exception& e) {
+      vlog.error(e.str());
+      remSocket(ci.sock);
     }
   }
 }
-
-void SocketManager::addEvent(HANDLE event, EventHandler* ecb) {
-  resizeArrays(nSockets+1);
-
-  sockets[nSockets].handler = ecb;
-  events[nSockets] = event;
-  sockets[nSockets].is_conn = false;
-  sockets[nSockets].is_event = true;
-
-  nSockets++;
-}
-
-void SocketManager::removeSocket(int index) {
-  if (index >= nSockets)
-    throw rdr::Exception("attempting to remove unregistered socket");
-
-  if (!sockets[index].is_event)
-    WSACloseEvent(events[index]);
-
-  for (int i=index; i<nSockets-1; i++) {
-    sockets[i] = sockets[i+1];
-    events[i] = events[i+1];
-  }
-
-  nSockets--;
-}
-
diff --git a/rfb_win32/SocketManager.h b/rfb_win32/SocketManager.h
index 791370f..ef35974 100644
--- a/rfb_win32/SocketManager.h
+++ b/rfb_win32/SocketManager.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -30,23 +30,32 @@
 #ifndef __RFB_WIN32_SOCKET_MGR_H__
 #define __RFB_WIN32_SOCKET_MGR_H__
 
-#include <list>
-
+#include <map>
 #include <network/Socket.h>
-#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/EventManager.h>
 
 namespace rfb {
-
   namespace win32 {
 
-    class SocketManager {
+    class SocketManager : public EventManager, EventHandler {
     public:
       SocketManager();
       virtual ~SocketManager();
 
+      // AddressChangeNotifier callback interface
+      // If an object implementing this is passed to addListener then it will be
+      // called whenever the SocketListener's address list changes
+      class AddressChangeNotifier {
+      public:
+        virtual ~AddressChangeNotifier() {}
+        virtual void processAddressChange(network::SocketListener* sl) = 0;
+      };
+
       // Add a listening socket.  Incoming connections will be added to the supplied
       // SocketServer.
-      void addListener(network::SocketListener* sock_, network::SocketServer* srvr);
+      void addListener(network::SocketListener* sock_,
+                       network::SocketServer* srvr,
+                       AddressChangeNotifier* acn = 0);
 
       // Remove and delete a listening socket.
       void remListener(network::SocketListener* sock);
@@ -54,50 +63,24 @@
       // Add an already-connected socket.  Socket events will cause the supplied
       // SocketServer to be called.  The socket must ALREADY BE REGISTERED with
       // the SocketServer.
-      void addSocket(network::Socket* sock_, network::SocketServer* srvr);
-
-      // Add a Win32 event & handler for it to the SocketManager
-      // This event will be blocked on along with the registered Sockets, and the
-      // handler called whenever it is discovered to be set.
-      // NB: SocketManager does NOT call ResetEvent on the event!
-      // NB: If processEvent returns false then the event is no longer registered,
-      //     and the event object is assumed to have been closed by processEvent()
-      struct EventHandler {
-        virtual ~EventHandler() {}
-        virtual bool processEvent(HANDLE event) = 0;
-      };
-      void addEvent(HANDLE event, EventHandler* ecb);
-
-      // getMessage
-      //
-      // Either return a message from the thread's message queue or process a socket
-      // event.
-      // Returns whenever a message needs processing.  Returns false if message is
-      // WM_QUIT, true for all other messages.
-      BOOL getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg);
+      void addSocket(network::Socket* sock_, network::SocketServer* srvr, bool outgoing=true);
 
     protected:
-      void addListener(network::SocketListener* sock, HANDLE event, network::SocketServer* server);
-      void addSocket(network::Socket* sock, HANDLE event, network::SocketServer* server);
-      void resizeArrays(int numSockets);
-      void removeSocket(int index);
-      struct SocketInfo {
-        union {
-          network::Socket* conn;
-          network::SocketListener* listener;
-        } sock;
-        SOCKET fd;
-        bool is_conn;
-        bool is_event;
-        union {
-          network::SocketServer* server;
-          EventHandler* handler;
-        };
+      virtual int checkTimeouts();
+      virtual void processEvent(HANDLE event);
+      virtual void remSocket(network::Socket* sock);
+
+      struct ConnInfo {
+        network::Socket* sock;
+        network::SocketServer* server;
       };
-      SocketInfo* sockets;
-      HANDLE* events;
-      int nSockets;
-      int nAvail;
+      struct ListenInfo {
+        network::SocketListener* sock;
+        network::SocketServer* server;
+        AddressChangeNotifier* notifier;
+      };
+      std::map<HANDLE, ListenInfo> listeners;
+      std::map<HANDLE, ConnInfo> connections;
    };
 
   }
diff --git a/rfb_win32/TCharArray.cxx b/rfb_win32/TCharArray.cxx
index f8f03a6..fd4c078 100644
--- a/rfb_win32/TCharArray.cxx
+++ b/rfb_win32/TCharArray.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb_win32/TCharArray.h b/rfb_win32/TCharArray.h
index 399e00a..dde63b7 100644
--- a/rfb_win32/TCharArray.h
+++ b/rfb_win32/TCharArray.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -40,10 +40,10 @@
 #ifndef __RFB_WIN32_TCHARARRAY_H__
 #define __RFB_WIN32_TCHARARRAY_H__
 
-#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
 #include <tchar.h>
-
 #include <rfb/util.h>
+#include <rfb/Password.h>
 
 namespace rfb {
 
@@ -98,6 +98,20 @@
     WCHAR* buf;
   };
 
+  // -=- Wide-character-based password-buffer handler.  Zeroes the password
+  //     buffer when deleted or replaced.
+  class WPlainPasswd : public WCharArray {
+  public:
+    WPlainPasswd() {}
+    WPlainPasswd(WCHAR* str) : WCharArray(str) {}
+    ~WPlainPasswd() {replaceBuf(0);}
+    void replaceBuf(WCHAR* str) {
+      if (buf)
+        memset(buf, 0, sizeof(WCHAR)*wcslen(buf));
+      WCharArray::replaceBuf(str);
+    }
+  };
+    
 #ifdef _UNICODE
 #define tstrDup wstrDup
 #define tstrFree wstrFree
@@ -105,6 +119,7 @@
 #define tstrContains wstrContains
   typedef WCharArray TCharArray;
   typedef WStr TStr;
+  typedef WPlainPasswd TPlainPasswd;
 #else
 #define tstrDup strDup
 #define tstrFree strFree
@@ -112,8 +127,9 @@
 #define tstrContains strContains
   typedef CharArray TCharArray;
   typedef CStr TStr;
+  typedef PlainPasswd TPlainPasswd;
 #endif
 
 };
 
-#endif
\ No newline at end of file
+#endif
diff --git a/rfb/win32/Threading_win32.cxx b/rfb_win32/Threading.cxx
similarity index 74%
rename from rfb/win32/Threading_win32.cxx
rename to rfb_win32/Threading.cxx
index 28cfdb7..c41ac38 100644
--- a/rfb/win32/Threading_win32.cxx
+++ b/rfb_win32/Threading.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -16,15 +16,15 @@
  * USA.
  */
 
-// -=- Threading_win32.cxx
+// -=- Threading.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>
+#include <rfb_win32/Threading.h>
 
 using namespace rfb;
 
@@ -33,11 +33,11 @@
 static DWORD threadStorage = TlsAlloc();
 
 
-inline logAction(Thread* t, const char* action) {
+inline void logAction(Thread* t, const char* action) {
   vlog.debug("%-16.16s %s(%lx)", action, t->getName(), t);
 }
 
-inline logError(Thread* t, const char* err) {
+inline void logError(Thread* t, const char* err) {
   vlog.error("%-16.16s %s(%lx):%s", "failed", t->getName(), t, err);
 }
 
@@ -65,34 +65,28 @@
   return 0;
 }
 
-Thread::Thread(const char* name_) : sig(0), deleteAfterRun(false) {
+Thread::Thread(const char* name_) : name(strDup(name_ ? name_ : "Unnamed")), 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);
+  cond_event.h = CreateEvent(NULL, TRUE, FALSE, NULL);
+  thread.h = 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");
+Thread::Thread(HANDLE thread_, DWORD thread_id_) : name(strDup("Native")), sig(0), deleteAfterRun(false),
+  thread(thread_), thread_id(thread_id_) {
+  cond_event.h = CreateEvent(NULL, TRUE, FALSE, NULL);
   state = ThreadNative;
   logAction(this, "created");
 }
 
 Thread::~Thread() {
   logAction(this, "destroying");
-  if (!deleteAfterRun) this->join();
+  if (!deleteAfterRun && state != ThreadNative)
+    this->join();
   if (sig)
     delete sig;
-  if (cond_event)
-    CloseHandle(cond_event);
   logAction(this, "destroyed");
-  strFree(name);
 }
 
 void
@@ -114,7 +108,7 @@
   if (deleteAfterRun)
     throw rdr::Exception("attempt to join() with deleteAfterRun thread");
   Lock l(mutex);
-  if (!thread) {
+  if (state == ThreadJoined) {
     logAction(this, "already joined");
   } else {
     logAction(this, "joining");
@@ -122,8 +116,7 @@
       sig->wait();
       logAction(this, "checking");
     }
-    CloseHandle(thread);
-    thread = 0;
+    state = ThreadJoined;
     logAction(this, "joined");
   }
   return this;
@@ -131,7 +124,7 @@
 
 const char*
 Thread::getName() const {
-  return name;
+  return name.buf;
 }
 
 ThreadState
@@ -144,12 +137,15 @@
   return thread_id;
 }
 
+
 Thread*
 Thread::self() {
   Thread* thread = (Thread*) TlsGetValue(threadStorage);
   if (!thread) {
+    // *** memory leak - could use GetExitCodeThread to lazily detect when
+    //     to clean up native thread objects
     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.h
similarity index 70%
rename from rfb/win32/Threading_win32.h
rename to rfb_win32/Threading.h
index e95e0f7..850f04d 100644
--- a/rfb/win32/Threading_win32.h
+++ b/rfb_win32/Threading.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -24,10 +24,11 @@
 
 #define __RFB_THREADING_IMPL WIN32
 
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
+#include <rfb_win32/Handle.h>
+#include <rfb/util.h>
+#include <rdr/Exception.h>
+//#include <stdio.h>
 
-#include <stdio.h>
 
 namespace rfb {
 
@@ -55,7 +56,7 @@
     Mutex& mutex;
   };
 
-  enum ThreadState {ThreadCreated, ThreadStarted, ThreadStopped, ThreadNative};
+  enum ThreadState {ThreadCreated, ThreadStarted, ThreadStopped, ThreadJoined, ThreadNative};
 
   class Thread {
   public:
@@ -84,14 +85,14 @@
     Thread(HANDLE thread_, DWORD thread_id_);
     static DWORD WINAPI threadProc(LPVOID lpParameter);
 
-    HANDLE thread;
+    win32::Handle thread;
     DWORD thread_id;
-    char* name;
+    CharArray name;
     ThreadState state;
     Condition* sig;
     Mutex mutex;
 
-    HANDLE cond_event;
+    win32::Handle cond_event;
 	  Thread* cond_next;
 
     bool deleteAfterRun;
@@ -103,18 +104,22 @@
     }
     ~Condition() {
     }
-    void signal() {
+
+    // Wake up the specified number of threads that are waiting
+    // on this Condition, or all of them if -1 is specified.
+    void signal(int howMany=1) {
       Lock l(cond_lock);
-      if (waiting) {
+      while (waiting && howMany!=0) {
         SetEvent(waiting->cond_event);
         waiting = waiting->cond_next;
+        if (howMany>0) --howMany;
       }
     }
-    // - 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) {
+
+    // NB: Must hold "mutex" to call wait()
+    // Wait until either the Condition is signalled or the timeout
+    // expires.
+    void wait(DWORD timeout=INFINITE) {
       Thread* self = Thread::self();
       ResetEvent(self->cond_event);
       { Lock l(cond_lock);
@@ -122,20 +127,20 @@
         waiting = self;
       }
       mutex.exit();
-      if (processMsgs) {
-        while (1) {
-          DWORD result = MsgWaitForMultipleObjects(1, &self->cond_event, FALSE, INFINITE, QS_ALLINPUT);
-          if (result == WAIT_OBJECT_0)
+      DWORD result = WaitForSingleObject(self->cond_event, timeout);
+      mutex.enter();
+      if (result == WAIT_TIMEOUT) {
+        Lock l(cond_lock);
+        // Remove this thread from the Condition
+        for (Thread** removeFrom = &waiting; *removeFrom; removeFrom = &(*removeFrom)->cond_next) {
+          if (*removeFrom == self) {
+            *removeFrom = self->cond_next;
             break;
-          MSG msg;
-          while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
-            DispatchMessage(&msg);
           }
         }
-      } else {
-        WaitForSingleObject(self->cond_event, INFINITE);
+      } else if (result == WAIT_FAILED) {
+        throw rdr::SystemException("failed to wait on Condition", GetLastError());
       }
-      mutex.enter();
     }
     
   protected:
diff --git a/rfb_win32/TrayIcon.h b/rfb_win32/TrayIcon.h
index 85680f3..dc5102a 100644
--- a/rfb_win32/TrayIcon.h
+++ b/rfb_win32/TrayIcon.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -23,7 +23,6 @@
 #ifndef __RFB_WIN32_TRAY_ICON_H__
 #define __RFB_WIN32_TRAY_ICON_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <shellapi.h>
 #include <rfb_win32/MsgWindow.h>
diff --git a/rfb_win32/TsSessions.cxx b/rfb_win32/TsSessions.cxx
new file mode 100644
index 0000000..efe7564
--- /dev/null
+++ b/rfb_win32/TsSessions.cxx
@@ -0,0 +1,93 @@
+/* Copyright (C) 2002-2005 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_win32/TsSessions.h>
+#include <rfb_win32/DynamicFn.h>
+#include <rfb/LogWriter.h>
+#include <rdr/Exception.h>
+#include <tchar.h>
+
+#ifdef ERROR_CTX_WINSTATION_BUSY
+#define RFB_HAVE_WINSTATION_CONNECT
+#else
+#pragma message("  NOTE: Not building WinStationConnect support.")
+#endif
+
+static rfb::LogWriter vlog("TsSessions");
+
+namespace rfb {
+namespace win32 {
+
+  // Windows XP (and later) functions used to handle session Ids
+  typedef BOOLEAN (WINAPI *_WinStationConnect_proto) (HANDLE,ULONG,ULONG,PCWSTR,ULONG);
+  DynamicFn<_WinStationConnect_proto> _WinStationConnect(_T("winsta.dll"), "WinStationConnectW");
+  typedef DWORD (WINAPI *_WTSGetActiveConsoleSessionId_proto) ();
+  DynamicFn<_WTSGetActiveConsoleSessionId_proto> _WTSGetActiveConsoleSessionId(_T("kernel32.dll"), "WTSGetActiveConsoleSessionId");
+  typedef BOOL (WINAPI *_ProcessIdToSessionId_proto) (DWORD, DWORD*);
+  DynamicFn<_ProcessIdToSessionId_proto> _ProcessIdToSessionId(_T("kernel32.dll"), "ProcessIdToSessionId");
+  typedef BOOL (WINAPI *_LockWorkStation_proto)();
+  DynamicFn<_LockWorkStation_proto> _LockWorkStation(_T("user32.dll"), "LockWorkStation");
+
+
+  ProcessSessionId::ProcessSessionId(DWORD processId) {
+    id = 0;
+    if (!_ProcessIdToSessionId.isValid())
+      return;
+    if (processId == -1)
+      processId = GetCurrentProcessId();
+    if (!(*_ProcessIdToSessionId)(GetCurrentProcessId(), &id))
+      throw rdr::SystemException("ProcessIdToSessionId", GetLastError());
+  }
+
+  ProcessSessionId mySessionId;
+
+  ConsoleSessionId::ConsoleSessionId() {
+    if (_WTSGetActiveConsoleSessionId.isValid())
+      id = (*_WTSGetActiveConsoleSessionId)();
+    else
+      id = 0;
+  }
+
+  bool inConsoleSession() {
+    ConsoleSessionId console;
+    return console.id == mySessionId.id;
+  }
+
+  void setConsoleSession(DWORD sessionId) {
+#ifdef RFB_HAVE_WINSTATION_CONNECT
+    if (!_WinStationConnect.isValid())
+      throw rdr::Exception("WinSta APIs missing");
+    if (sessionId == -1)
+      sessionId = mySessionId.id;
+
+    // Try to reconnect our session to the console
+    ConsoleSessionId console;
+    vlog.info("Console session is %d", console.id);
+    if (!(*_WinStationConnect)(0, sessionId, console.id, L"", 0))
+      throw rdr::SystemException("Unable to connect session to Console", GetLastError());
+
+    // Lock the newly connected session, for security
+    if (_LockWorkStation.isValid())
+      (*_LockWorkStation)();
+#else
+    throw rdr::Exception("setConsoleSession not implemented");
+#endif
+  }
+
+};
+};
diff --git a/rfb_win32/TsSessions.h b/rfb_win32/TsSessions.h
new file mode 100644
index 0000000..b15ada7
--- /dev/null
+++ b/rfb_win32/TsSessions.h
@@ -0,0 +1,61 @@
+/* Copyright (C) 2002-2005 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.
+ */
+
+// Windows version-independent Terminal Services Session discovery
+// and manipulation API.  This code will eventually be replaced
+// by the full TS-compatibility scheme.
+
+#ifndef __RFB_WIN32_TSSESSIONS_H__
+#define __RFB_WIN32_TSSESSIONS_H__
+
+#include <windows.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    struct SessionId {
+      DWORD id;
+    };
+
+    // Session Id for a given process
+    struct ProcessSessionId : SessionId {
+      ProcessSessionId(DWORD processId = -1);
+    };
+
+    // Session Id for current process
+    extern ProcessSessionId mySessionId;
+
+    // Current console Session Id
+    struct ConsoleSessionId : SessionId {
+      ConsoleSessionId();
+    };
+
+    // Check whether the process is in the Console session at present
+    bool inConsoleSession();
+
+    // Make the specified session the Console session.
+    //   If sessionId is -1 then the process' session is
+    //   made the Console session.
+    void setConsoleSession(DWORD sessionId = -1);
+
+  };
+
+};
+
+#endif
diff --git a/rfb_win32/WMCursor.cxx b/rfb_win32/WMCursor.cxx
index 871d937..4d696cb 100644
--- a/rfb_win32/WMCursor.cxx
+++ b/rfb_win32/WMCursor.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -20,11 +20,10 @@
 
 // *** DOESN'T SEEM TO WORK WITH GetCursorInfo POS CODE BUILT-IN UNDER NT4SP6
 // *** INSTEAD, WE LOOK FOR Win2000/Win98 OR ABOVE
-#define WINVER 0x0500
 
 #include <rfb_win32/WMCursor.h>
 #include <rfb_win32/OSVersion.h>
-#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/DynamicFn.h>
 #include <rfb/Exception.h>
 #include <rfb/LogWriter.h>
 
@@ -35,12 +34,19 @@
 static LogWriter vlog("WMCursor");
 
 
+#ifdef CURSOR_SHOWING
+#define RFB_HAVE_GETCURSORINFO
+#else
+#pragma message("  NOTE: Not building GetCursorInfo support.")
+#endif
+
+#ifdef RFB_HAVE_GETCURSORINFO
 typedef BOOL (WINAPI *_GetCursorInfo_proto)(PCURSORINFO pci);
 DynamicFn<_GetCursorInfo_proto> _GetCursorInfo(_T("user32.dll"), "GetCursorInfo");
+#endif
 
-
-WMCursor::WMCursor() : hooks(0), library(0), use_getCursorInfo(false), cursor(0) {
-#if (WINVER >= 0x0500)
+WMCursor::WMCursor() : hooks(0), use_getCursorInfo(false), cursor(0) {
+#ifdef RFB_HAVE_GETCURSORINFO
   // Check the OS version
   bool is_win98 = (osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) &&
     (osVersion.dwMajorVersion > 4) || ((osVersion.dwMajorVersion == 4) && (osVersion.dwMinorVersion > 0));
@@ -48,13 +54,12 @@
 
   // Use GetCursorInfo if OS version is sufficient
   use_getCursorInfo = (is_win98 || is_win2K) && _GetCursorInfo.isValid();
-#else
-#pragma message ("not building in GetCursorInfo support")
 #endif
+  cursor = (HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
   if (!use_getCursorInfo) {
     hooks = new WMCursorHooks();
     if (hooks && hooks->start()) {
-      vlog.info("falling back to cursor hooking");
+      vlog.info("falling back to cursor hooking: %p", hooks);
     } else {
       delete hooks;
       hooks = 0;
@@ -63,18 +68,18 @@
   } else {
     vlog.info("using GetCursorInfo");
   }
-  cursor = (HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
 }
 
 WMCursor::~WMCursor() {
-  if (hooks) delete hooks;
-  if (library) FreeLibrary(library);
+  vlog.debug("deleting WMCursorHooks (%p)", hooks);
+  if (hooks)
+    delete hooks;
 }
   
 WMCursor::Info
 WMCursor::getCursorInfo() {
   Info result;
-#if (WINVER >= 0x0500)
+#ifdef RFB_HAVE_GETCURSORINFO
   if (use_getCursorInfo) {
     CURSORINFO info;
     info.cbSize = sizeof(CURSORINFO);
@@ -88,7 +93,8 @@
 #endif
   // Fall back to the old way of doing things
   POINT pos;
-  if (hooks) cursor = hooks->getCursor();
+  if (hooks)
+    cursor = hooks->getCursor();
   result.cursor = cursor;
   result.visible = cursor != 0;
   GetCursorPos(&pos);
diff --git a/rfb_win32/WMCursor.h b/rfb_win32/WMCursor.h
index a96822a..41f9ee8 100644
--- a/rfb_win32/WMCursor.h
+++ b/rfb_win32/WMCursor.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -25,12 +25,10 @@
 #ifndef __RFB_WIN32_WM_CURSOR_H__
 #define __RFB_WIN32_WM_CURSOR_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <rfb_win32/WMHooks.h>
 
 namespace rfb {
-
   namespace win32 {
 
     class WMCursor {
@@ -53,13 +51,11 @@
       Info getCursorInfo();
     protected:
       WMCursorHooks* hooks;
-      HMODULE library;
       bool use_getCursorInfo;
       HCURSOR cursor;
     };
 
   };
-
 };
 
 #endif // __RFB_WIN32_WM_CURSOR_H__
diff --git a/rfb_win32/WMHooks.cxx b/rfb_win32/WMHooks.cxx
index 26a2363..2d69053 100644
--- a/rfb_win32/WMHooks.cxx
+++ b/rfb_win32/WMHooks.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -18,10 +18,11 @@
 
 // -=- WMHooks.cxx
 
-#include <wm_hooks/wm_hooks.h>
-
 #include <rfb_win32/WMHooks.h>
+#include <rfb_win32/DynamicFn.h>
 #include <rfb_win32/Service.h>
+#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/IntervalTimer.h>
 #include <rfb/Threading.h>
 #include <rfb/LogWriter.h>
 
@@ -32,11 +33,29 @@
 
 static LogWriter vlog("WMHooks");
 
+
+typedef UINT (*WM_Hooks_WMVAL_proto)();
+typedef BOOL (*WM_Hooks_Install_proto)(DWORD owner, DWORD thread);
+typedef BOOL (*WM_Hooks_Remove_proto)(DWORD owner);
+typedef BOOL (*WM_Hooks_EnableCursorShape_proto)(BOOL enable);
+#ifdef _DEBUG
+typedef void (*WM_Hooks_SetDiagnosticRange_proto)(UINT min, UINT max);
+DynamicFn<WM_Hooks_SetDiagnosticRange_proto> WM_Hooks_SetDiagnosticRange(_T("wm_hooks.dll"), "WM_Hooks_SetDiagnosticRange");
+#endif
+
+
 class WMHooksThread : public Thread {
 public:
-  WMHooksThread() : Thread("WMHookThread"), active(true) {}
+  WMHooksThread() : Thread("WMHookThread"), active(true),
+    WM_Hooks_Install(_T("wm_hooks.dll"), "WM_Hooks_Install"),
+    WM_Hooks_Remove(_T("wm_hooks.dll"), "WM_Hooks_Remove"),
+    WM_Hooks_EnableCursorShape(_T("wm_hooks.dll"), "WM_Hooks_EnableCursorShape") {
+  }
   virtual void run();
   virtual Thread* join();
+  DynamicFn<WM_Hooks_Install_proto> WM_Hooks_Install;;
+  DynamicFn<WM_Hooks_Remove_proto> WM_Hooks_Remove;
+  DynamicFn<WM_Hooks_EnableCursorShape_proto> WM_Hooks_EnableCursorShape;
 protected:
   bool active;
 };
@@ -48,52 +67,62 @@
 HCURSOR hook_cursor = (HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
 
 
-bool
-StartHookThread() {
-  if (hook_mgr) return true;
-  vlog.debug("opening hook thread");
+static bool StartHookThread() {
+  if (hook_mgr)
+    return true;
+  vlog.debug("creating thread");
   hook_mgr = new WMHooksThread();
-  if (!WM_Hooks_Install(hook_mgr->getThreadId(), 0)) {
+  if (!hook_mgr->WM_Hooks_Install.isValid() ||
+      !hook_mgr->WM_Hooks_Remove.isValid()) {
+    vlog.debug("hooks not available");
+    return false;
+  }
+  vlog.debug("installing hooks");
+  if (!(*hook_mgr->WM_Hooks_Install)(hook_mgr->getThreadId(), 0)) {
     vlog.error("failed to initialise hooks");
     delete hook_mgr->join();
     hook_mgr = 0;
     return false;
   }
+  vlog.debug("starting thread");
   hook_mgr->start();
   return true;
 }
 
-void
-StopHookThread() {
-  if (!hook_mgr) return;
-  if (!hooks.empty() || !cursor_hooks.empty()) return;
-  vlog.debug("closing hook thread");
+static void StopHookThread() {
+  if (!hook_mgr)
+    return;
+  if (!hooks.empty() || !cursor_hooks.empty())
+    return;
+  vlog.debug("closing thread");
   delete hook_mgr->join();
   hook_mgr = 0;
 }
 
 
-bool
-AddHook(WMHooks* hook) {
+static bool AddHook(WMHooks* hook) {
   vlog.debug("adding hook");
   Lock l(hook_mgr_lock);
-  if (!StartHookThread()) return false;
+  if (!StartHookThread())
+    return false;
   hooks.push_back(hook);
   return true;
 }
 
-bool
-AddCursorHook(WMCursorHooks* hook) {
+static bool AddCursorHook(WMCursorHooks* hook) {
   vlog.debug("adding cursor hook");
   Lock l(hook_mgr_lock);
-  if (cursor_hooks.empty()) WM_Hooks_EnableCursorShape(TRUE);
-  if (!StartHookThread()) return false;
+  if (!StartHookThread())
+    return false;
+  if (!hook_mgr->WM_Hooks_EnableCursorShape.isValid())
+    return false;
+  if (cursor_hooks.empty() && !(*hook_mgr->WM_Hooks_EnableCursorShape)(TRUE))
+    return false;
   cursor_hooks.push_back(hook);
   return true;
 }
 
-bool
-RemHook(WMHooks* hook) {
+static bool RemHook(WMHooks* hook) {
   {
     vlog.debug("removing hook");
     Lock l(hook_mgr_lock);
@@ -103,76 +132,107 @@
   return true;
 }
 
-bool
-RemCursorHook(WMCursorHooks* hook) {
+static bool RemCursorHook(WMCursorHooks* hook) {
   {
     vlog.debug("removing cursor hook");
     Lock l(hook_mgr_lock);
     cursor_hooks.remove(hook);
+    if (hook_mgr->WM_Hooks_EnableCursorShape.isValid() &&
+        cursor_hooks.empty())
+      (*hook_mgr->WM_Hooks_EnableCursorShape)(FALSE);
   }
   StopHookThread();
-  if (cursor_hooks.empty()) WM_Hooks_EnableCursorShape(FALSE);
   return true;
 }
 
-void
-NotifyHooksRegion(const Region& r) {
+static void NotifyHooksRegion(const Region& r) {
   Lock l(hook_mgr_lock);
   std::list<WMHooks*>::iterator i;
-  for (i=hooks.begin(); i!=hooks.end(); i++) {
-    (*i)->new_changes.add_changed(r);
-    if (!(*i)->notified) {
-      (*i)->notified = true;
-      PostMessage((*i)->getHandle(), WM_USER, 0, 0);
-    }
-  }
+  for (i=hooks.begin(); i!=hooks.end(); i++)
+    (*i)->NotifyHooksRegion(r);
 }
 
-void
-NotifyHooksCursor(HCURSOR c) {
+static void NotifyHooksCursor(HCURSOR c) {
   Lock l(hook_mgr_lock);
   hook_cursor = c;
 }
 
+
+static UINT GetMsgVal(DynamicFn<WM_Hooks_WMVAL_proto>& fn) {
+  if (fn.isValid())
+    return (*fn)();
+  return WM_NULL;
+}
+
 void
 WMHooksThread::run() {
-  UINT windowMsg = WM_Hooks_WindowChanged();
-  UINT clientAreaMsg = WM_Hooks_WindowClientAreaChanged();
-  UINT borderMsg = WM_Hooks_WindowBorderChanged();
-  UINT rectangleMsg = WM_Hooks_RectangleChanged();
-  UINT cursorMsg = WM_Hooks_CursorChanged();
+  // Obtain message ids for all supported hook messages
+  DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_WindowChanged(_T("wm_hooks.dll"), "WM_Hooks_WindowChanged");
+  DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_WindowBorderChanged(_T("wm_hooks.dll"), "WM_Hooks_WindowBorderChanged");
+  DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_WindowClientAreaChanged(_T("wm_hooks.dll"), "WM_Hooks_WindowClientAreaChanged");
+  DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_RectangleChanged(_T("wm_hooks.dll"), "WM_Hooks_RectangleChanged");
+  DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_CursorChanged(_T("wm_hooks.dll"), "WM_Hooks_CursorChanged");
+  UINT windowMsg = GetMsgVal(WM_Hooks_WindowChanged);
+  UINT clientAreaMsg = GetMsgVal(WM_Hooks_WindowClientAreaChanged);
+  UINT borderMsg = GetMsgVal(WM_Hooks_WindowBorderChanged);
+  UINT rectangleMsg = GetMsgVal(WM_Hooks_RectangleChanged);
+  UINT cursorMsg = GetMsgVal(WM_Hooks_CursorChanged);
 #ifdef _DEBUG
-  UINT diagnosticMsg = WM_Hooks_Diagnostic();
+  DynamicFn<WM_Hooks_WMVAL_proto> WM_Hooks_Diagnostic(_T("wm_hooks.dll"), "WM_Hooks_Diagnostic");
+  UINT diagnosticMsg = GetMsgVal(WM_Hooks_Diagnostic);
 #endif
   MSG msg;
   RECT wrect;
   HWND hwnd;
   int count = 0;
 
+  // Update delay handling
+  //   We delay updates by 40-80ms, so that the triggering application has time to
+  //   actually complete them before we notify the hook callbacks & they go off
+  //   capturing screen state.
+  const int updateDelayMs = 40;
+  MsgWindow updateDelayWnd(_T("WMHooks::updateDelay"));
+  IntervalTimer updateDelayTimer(updateDelayWnd.getHandle(), 1);
+  Region updates[2];
+  int activeRgn = 0;
+
   vlog.debug("starting hook thread");
 
   while (active && GetMessage(&msg, NULL, 0, 0)) {
     count++;
-    if (msg.message == windowMsg) {
+
+    if (msg.message == WM_TIMER) {
+      // Actually notify callbacks of graphical updates
+      NotifyHooksRegion(updates[1-activeRgn]);
+      if (updates[activeRgn].is_empty())
+        updateDelayTimer.stop();
+      activeRgn = 1-activeRgn;
+      updates[activeRgn].clear();
+
+    } else if (msg.message == windowMsg) {
+      // An entire window has (potentially) changed
       hwnd = (HWND) msg.lParam;
       if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
-          GetWindowRect(hwnd, &wrect) && !IsRectEmpty(&wrect))
-      {
-        NotifyHooksRegion(Rect(wrect.left, wrect.top,
-                               wrect.right, wrect.bottom));
-
+        GetWindowRect(hwnd, &wrect) && !IsRectEmpty(&wrect)) {
+          updates[activeRgn].assign_union(Rect(wrect.left, wrect.top,
+                                               wrect.right, wrect.bottom));
+          updateDelayTimer.start(updateDelayMs);
       }
+
     } else if (msg.message == clientAreaMsg) {
+      // The client area of a window has (potentially) changed
       hwnd = (HWND) msg.lParam;
       if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
           GetClientRect(hwnd, &wrect) && !IsRectEmpty(&wrect))
       {
         POINT pt = {0,0};
         if (ClientToScreen(hwnd, &pt)) {
-          NotifyHooksRegion(Rect(wrect.left+pt.x, wrect.top+pt.y,
-                                 wrect.right+pt.x, wrect.bottom+pt.y));
+          updates[activeRgn].assign_union(Rect(wrect.left+pt.x, wrect.top+pt.y,
+                                               wrect.right+pt.x, wrect.bottom+pt.y));
+          updateDelayTimer.start(updateDelayMs);
         }
       }
+
     } else if (msg.message == borderMsg) {
       hwnd = (HWND) msg.lParam;
       if (IsWindow(hwnd) && IsWindowVisible(hwnd) && !IsIconic(hwnd) &&
@@ -187,14 +247,19 @@
           changed.assign_subtract(Rect(crect.left+pt.x, crect.top+pt.y,
                                        crect.right+pt.x, crect.bottom+pt.y));
         }
-        NotifyHooksRegion(changed);
+        if (!changed.is_empty()) {
+          updates[activeRgn].assign_union(changed);
+          updateDelayTimer.start(updateDelayMs);
+        }
       }
     } else if (msg.message == rectangleMsg) {
       Rect r = Rect(LOWORD(msg.wParam), HIWORD(msg.wParam),
                     LOWORD(msg.lParam), HIWORD(msg.lParam));
       if (!r.is_empty()) {
-        NotifyHooksRegion(r);
+        updates[activeRgn].assign_union(r);
+        updateDelayTimer.start(updateDelayMs);
       }
+
     } else if (msg.message == cursorMsg) {
       NotifyHooksCursor((HCURSOR)msg.lParam);
 #ifdef _DEBUG
@@ -205,7 +270,7 @@
   }
 
   vlog.debug("stopping hook thread - processed %d events", count);
-  WM_Hooks_Remove(getThreadId());
+  (*WM_Hooks_Remove)(getThreadId());
 }
 
 Thread*
@@ -219,65 +284,52 @@
 
 // -=- WMHooks class
 
-rfb::win32::WMHooks::WMHooks()
-  : clipper(0), new_changes(true), fg_window(0),
-  notified(false), MsgWindow(_T("WMHooks")) {
+rfb::win32::WMHooks::WMHooks() : updateEvent(0) {
 }
 
 rfb::win32::WMHooks::~WMHooks() {
   RemHook(this);
-  if (clipper) delete clipper;
 }
 
-LRESULT
-rfb::win32::WMHooks::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
-  switch (msg) {
-  case WM_USER:
-    {
-      // *** Yield, to allow the triggering update event to be processed
-      //     BEFORE we try to grab the resulting changes.
-      // *** IMPROVES THINGS NOTICABLY ON WinXP
-      Sleep(0);
-      // ***
-
-      Lock l(hook_mgr_lock);
-      notified = false;
-      new_changes.get_update(*clipper);
-      new_changes.clear();
-    }
-    break;
-  }
-  return MsgWindow::processMessage(msg, wParam, lParam);
+bool rfb::win32::WMHooks::setEvent(HANDLE ue) {
+  if (updateEvent)
+    RemHook(this);
+  updateEvent = ue;
+  return AddHook(this);
 }
 
-bool
-rfb::win32::WMHooks::setClipRect(const Rect& r) {
-  clip_region = r;
-  if (clipper) clipper->set_clip_region(clip_region);
+bool rfb::win32::WMHooks::getUpdates(UpdateTracker* ut) {
+  if (!updatesReady) return false;
+  Lock l(hook_mgr_lock);
+  updates.copyTo(ut);
+  updates.clear();
+  updatesReady = false;
   return true;
 }
 
-bool
-rfb::win32::WMHooks::setUpdateTracker(UpdateTracker* ut) {
-  if (clipper) delete clipper;
-  clipper = new ClippedUpdateTracker(*ut);
-  clipper->set_clip_region(clip_region);
-  return AddHook(this);
+bool rfb::win32::WMHooks::areAvailable() {
+  WMHooksThread wmht;
+  return wmht.WM_Hooks_Install.isValid();
 }
 
 #ifdef _DEBUG
 void
 rfb::win32::WMHooks::setDiagnosticRange(UINT min, UINT max) {
-  WM_Hooks_SetDiagnosticRange(min, max);
+  if (WM_Hooks_SetDiagnosticRange.isValid())
+    (*WM_Hooks_SetDiagnosticRange)(min, max);
 }
 #endif
 
+void rfb::win32::WMHooks::NotifyHooksRegion(const Region& r) {
+  // hook_mgr_lock is already held at this point
+  updates.add_changed(r);
+  updatesReady = true;
+  SetEvent(updateEvent);
+}
+
 
 // -=- WMBlockInput class
 
-Mutex blockMutex;
-int blockCount = 0;
-
 rfb::win32::WMBlockInput::WMBlockInput() : active(false) {
 }
 
@@ -285,22 +337,40 @@
   blockInputs(false);
 }
 
-bool rfb::win32::WMBlockInput::blockInputs(bool on) {
-  if (on == active) return true;
-  vlog.debug("blockInput changed");
-  Lock l(blockMutex);
-  int newCount = blockCount;
-  if (on)
-    newCount++;
-  else
-    newCount--;
-  if (WM_Hooks_EnableRealInputs(newCount==0, newCount==0)) {
-    vlog.debug("set blocking to %d", newCount);
-    blockCount = newCount;
-    active = on;
-    return true;
+typedef BOOL (*WM_Hooks_EnableRealInputs_proto)(BOOL pointer, BOOL keyboard);
+DynamicFn<WM_Hooks_EnableRealInputs_proto>* WM_Hooks_EnableRealInputs = 0;
+static bool blockRealInputs(bool block_) {
+  // NB: Requires blockMutex to be held!
+  if (block_) {
+    if (WM_Hooks_EnableRealInputs)
+      return true;
+    // Enable blocking
+    WM_Hooks_EnableRealInputs = new DynamicFn<WM_Hooks_EnableRealInputs_proto>(_T("wm_hooks.dll"), "WM_Hooks_EnableRealInputs");
+    if (WM_Hooks_EnableRealInputs->isValid() && (**WM_Hooks_EnableRealInputs)(false, false))
+      return true;
   }
-  return false;
+  if (WM_Hooks_EnableRealInputs) {
+    // Clean up the DynamicFn, either if init failed, or block_ is false
+    if (WM_Hooks_EnableRealInputs->isValid())
+      (**WM_Hooks_EnableRealInputs)(true, true);
+    delete WM_Hooks_EnableRealInputs;
+    WM_Hooks_EnableRealInputs = 0;
+  }
+  return block_ == (WM_Hooks_EnableRealInputs != 0);
+}
+
+Mutex blockMutex;
+int blockCount = 0;
+
+bool rfb::win32::WMBlockInput::blockInputs(bool on) {
+  if (active == on) return true;
+  Lock l(blockMutex);
+  int newCount = on ? blockCount+1 : blockCount-1;
+  if (!blockRealInputs(newCount > 0))
+    return false;
+  blockCount = newCount;
+  active = on;
+  return true;
 }
 
 
diff --git a/rfb_win32/WMHooks.h b/rfb_win32/WMHooks.h
index 791df76..4713b41 100644
--- a/rfb_win32/WMHooks.h
+++ b/rfb_win32/WMHooks.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -21,43 +21,50 @@
 #ifndef __RFB_WIN32_WM_HOOKS_H__
 #define __RFB_WIN32_WM_HOOKS_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <rfb/UpdateTracker.h>
 #include <rdr/Exception.h>
-#include <rfb_win32/MsgWindow.h>
+#include <rfb_win32/Win32Util.h>
 
 namespace rfb {
 
   namespace win32 {
 
-    class WMHooks : public MsgWindow {
+    // -=- WMHooks
+    //     Uses the wm_hooks DLL to intercept window messages, to get _hints_ as
+    //     to what may have changed on-screen.  Updates are notified via a Win32
+    //     event, and retrieved using the getUpdates method, which is thread-safe.
+    class WMHooks {
     public:
       WMHooks();
       ~WMHooks();
 
-      bool setClipRect(const Rect& cr);
-      bool setUpdateTracker(UpdateTracker* ut);
+      // Specify the event object to notify.  Starts the hook subsystem if it is
+      // not already active, and returns false if the hooks fail to start.
+      bool setEvent(HANDLE updateEvent);
 
-      virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+      // Copies any new updates to the UpdateTracker.  Returns true if new updates
+      // were added, false otherwise.
+      bool getUpdates(UpdateTracker* ut);
+
+      // Determine whether the hooks DLL is installed on the system
+      static bool areAvailable();
 
 #ifdef _DEBUG
       // Get notifications of any messages in the given range, to any hooked window
       void setDiagnosticRange(UINT min, UINT max);
 #endif
 
+      // * INTERNAL NOTIFICATION FUNCTION * 
+      void NotifyHooksRegion(const Region& r);
     protected:
-      ClippedUpdateTracker* clipper;
-      Region clip_region;
-
-      void* fg_window;
-      Rect fg_window_rect;
-
-    public:
-      SimpleUpdateTracker new_changes;
-      bool notified;
+      HANDLE updateEvent;
+      bool updatesReady;
+      SimpleUpdateTracker updates;
     };
 
+    // -=- Support for filtering out local input events while remote connections are
+    //     active.  Implemented using SetWindowsHookEx for portability.
     class WMBlockInput {
     public:
       WMBlockInput();
diff --git a/rfb_win32/WMNotifier.cxx b/rfb_win32/WMNotifier.cxx
index 9773abf..20a5445 100644
--- a/rfb_win32/WMNotifier.cxx
+++ b/rfb_win32/WMNotifier.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -30,7 +30,7 @@
 static LogWriter vlog("WMMonitor");
 
 
-WMMonitor::WMMonitor() : MsgWindow(_T("WMMonitor")) {
+WMMonitor::WMMonitor() : MsgWindow(_T("WMMonitor")), notifier(0) {
 }
 
 WMMonitor::~WMMonitor() {
diff --git a/rfb_win32/WMNotifier.h b/rfb_win32/WMNotifier.h
index 564d176..a760964 100644
--- a/rfb_win32/WMNotifier.h
+++ b/rfb_win32/WMNotifier.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb_win32/WMPoller.cxx b/rfb_win32/WMPoller.cxx
index f568b21..f850534 100644
--- a/rfb_win32/WMPoller.cxx
+++ b/rfb_win32/WMPoller.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -35,35 +35,19 @@
 
 // -=- WMPoller class
 
-rfb::win32::WMPoller::WMPoller() : clipper(0) {
-}
-
-rfb::win32::WMPoller::~WMPoller() {
-  if (clipper) delete clipper;
-}
-
 bool
 rfb::win32::WMPoller::processEvent() {
   PollInfo info;
-  if (clipper && poll_console_windows) {
+  if (poll_console_windows && ut) {
     ::EnumWindows(WMPoller::enumWindowProc, (LPARAM) &info);
-    clipper->add_changed(info.poll_include);
+    ut->add_changed(info.poll_include);
   }
   return false;
 }
 
 bool
-rfb::win32::WMPoller::setClipRect(const Rect& r) {
-  clip_region = r;
-  if (clipper) clipper->set_clip_region(clip_region);
-  return true;
-}
-
-bool
-rfb::win32::WMPoller::setUpdateTracker(UpdateTracker* ut) {
-  if (clipper) delete clipper;
-  clipper = new ClippedUpdateTracker(*ut);
-  clipper->set_clip_region(clip_region);
+rfb::win32::WMPoller::setUpdateTracker(UpdateTracker* ut_) {
+  ut = ut_;
   return true;
 }
 
diff --git a/rfb_win32/WMPoller.h b/rfb_win32/WMPoller.h
index 3f3f402..851b69f 100644
--- a/rfb_win32/WMPoller.h
+++ b/rfb_win32/WMPoller.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -28,7 +28,6 @@
 #ifndef __RFB_WIN32_WM_POLLER_H__
 #define __RFB_WIN32_WM_POLLER_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <rfb/UpdateTracker.h>
 #include <rfb/Configuration.h>
@@ -39,11 +38,9 @@
 
     class WMPoller {
     public:
-      WMPoller();
-      ~WMPoller();
+      WMPoller() : ut(0) {}
 
       bool processEvent();
-      bool setClipRect(const Rect& cr);
       bool setUpdateTracker(UpdateTracker* ut);
 
       static BoolParameter poll_console_windows;
@@ -55,9 +52,7 @@
       static bool checkPollWindow(HWND w);
       static void pollWindow(HWND w, PollInfo* info);
       static BOOL CALLBACK enumWindowProc(HWND w, LPARAM lp);
-
-      ClippedUpdateTracker* clipper;
-      Region clip_region;
+      UpdateTracker* ut;
     };
 
   };
diff --git a/rfb_win32/WMShatter.cxx b/rfb_win32/WMShatter.cxx
index f6a7484..e68abfb 100644
--- a/rfb_win32/WMShatter.cxx
+++ b/rfb_win32/WMShatter.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb_win32/WMShatter.h b/rfb_win32/WMShatter.h
index 7b81678..3ea63b1 100644
--- a/rfb_win32/WMShatter.h
+++ b/rfb_win32/WMShatter.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -33,11 +33,9 @@
 #ifndef __RFB_WIN32_SHATTER_H__
 #define __RFB_WIN32_SHATTER_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 
 namespace rfb {
-
   namespace win32 {
 
     bool IsSafeWM(HWND window, UINT msg, WPARAM wParam, LPARAM lParam);
@@ -47,7 +45,6 @@
     LRESULT SafeDispatchMessage(const MSG* msg);
 
   };
-
 };
 
 #endif // __RFB_WIN32_SHATTER_H__
diff --git a/rfb_win32/WMWindowCopyRect.cxx b/rfb_win32/WMWindowCopyRect.cxx
index 46d85ea..63c1da2 100644
--- a/rfb_win32/WMWindowCopyRect.cxx
+++ b/rfb_win32/WMWindowCopyRect.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -29,55 +29,39 @@
 
 // -=- WMHooks class
 
-rfb::win32::WMCopyRect::WMCopyRect() : clipper(0), fg_window(0) {
-}
-
-rfb::win32::WMCopyRect::~WMCopyRect() {
-  if (clipper) delete clipper;
+rfb::win32::WMCopyRect::WMCopyRect() : ut(0), fg_window(0) {
 }
 
 bool
 rfb::win32::WMCopyRect::processEvent() {
-  if (clipper) {
-    // See if the foreground window has moved
-    HWND window = GetForegroundWindow();
-    if (window) {
-      RECT wrect;
-      if (IsWindow(window) && IsWindowVisible(window) && GetWindowRect(window, &wrect)) {
-        Rect winrect(wrect.left, wrect.top, wrect.right, wrect.bottom);
-        if (fg_window == window) {
-
-          if (!fg_window_rect.tl.equals(winrect.tl)) {
-            // Window has moved - send a copyrect event to the client
-            Point delta = Point(winrect.tl.x-fg_window_rect.tl.x, winrect.tl.y-fg_window_rect.tl.y);
-            Region copy_dest = winrect;
-            clipper->add_copied(copy_dest, delta);
-            clipper->add_changed(Region(fg_window_rect).subtract(copy_dest));
-          }
+  // See if the foreground window has moved
+  HWND window = GetForegroundWindow();
+  if (window) {
+    RECT wrect;
+    if (IsWindow(window) && IsWindowVisible(window) && GetWindowRect(window, &wrect)) {
+      Rect winrect(wrect.left, wrect.top, wrect.right, wrect.bottom);
+      if (fg_window == window) {
+        if (!fg_window_rect.tl.equals(winrect.tl)  && ut) {
+          // Window has moved - send a copyrect event to the client
+          Point delta = Point(winrect.tl.x-fg_window_rect.tl.x, winrect.tl.y-fg_window_rect.tl.y);
+          Region copy_dest = winrect;
+          ut->add_copied(copy_dest, delta);
+          ut->add_changed(Region(fg_window_rect).subtract(copy_dest));
         }
-        fg_window = window;
-        fg_window_rect = winrect;
-      } else {
-        fg_window = 0;
       }
+      fg_window = window;
+      fg_window_rect = winrect;
     } else {
       fg_window = 0;
     }
+  } else {
+    fg_window = 0;
   }
   return false;
 }
 
 bool
-rfb::win32::WMCopyRect::setClipRect(const Rect& r) {
-  clip_region = r;
-  if (clipper) clipper->set_clip_region(clip_region);
-  return true;
-}
-
-bool
-rfb::win32::WMCopyRect::setUpdateTracker(UpdateTracker* ut) {
-  if (clipper) delete clipper;
-  clipper = new ClippedUpdateTracker(*ut);
-  clipper->set_clip_region(clip_region);
+rfb::win32::WMCopyRect::setUpdateTracker(UpdateTracker* ut_) {
+  ut = ut_;
   return true;
 }
diff --git a/rfb_win32/WMWindowCopyRect.h b/rfb_win32/WMWindowCopyRect.h
index 0750d86..5a0e876 100644
--- a/rfb_win32/WMWindowCopyRect.h
+++ b/rfb_win32/WMWindowCopyRect.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -36,15 +36,12 @@
     class WMCopyRect {
     public:
       WMCopyRect();
-      ~WMCopyRect();
 
       bool processEvent();
-      bool setClipRect(const Rect& cr);
       bool setUpdateTracker(UpdateTracker* ut);
 
     protected:
-      ClippedUpdateTracker* clipper;
-      Region clip_region;
+      UpdateTracker* ut;
       void* fg_window;
       Rect fg_window_rect;
     };
diff --git a/rfb_win32/Win32Util.cxx b/rfb_win32/Win32Util.cxx
index 28fae2e..ef8039a 100644
--- a/rfb_win32/Win32Util.cxx
+++ b/rfb_win32/Win32Util.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -18,221 +18,30 @@
 
 // Win32Util.cxx
 
+#include <rfb_win32/ModuleFileName.h>
 #include <rfb_win32/Win32Util.h>
-#include <rdr/Exception.h>
+#include <rfb_win32/MonitorInfo.h>
+#include <rfb_win32/Handle.h>
 #include <rdr/HexOutStream.h>
-
+#include <rdr/Exception.h>
 
 namespace rfb {
 namespace win32 {
 
-LogicalPalette::LogicalPalette() : palette(0), numEntries(0) {
-  BYTE buf[sizeof(LOGPALETTE)+256*sizeof(PALETTEENTRY)];
-  LOGPALETTE* logpal = (LOGPALETTE*)buf;
-  logpal->palVersion = 0x300;
-  logpal->palNumEntries = 256;
-  for (int i=0; i<256;i++) {
-    logpal->palPalEntry[i].peRed = 0;
-    logpal->palPalEntry[i].peGreen = 0;
-    logpal->palPalEntry[i].peBlue = 0;
-    logpal->palPalEntry[i].peFlags = 0;
-  }
-  palette = CreatePalette(logpal);
-  if (!palette)
-    throw rdr::SystemException("failed to CreatePalette", GetLastError());
-}
-
-LogicalPalette::~LogicalPalette() {
-  if (palette)
-    if (!DeleteObject(palette))
-      throw rdr::SystemException("del palette failed", GetLastError());
-}
-
-void LogicalPalette::setEntries(int start, int count, const Colour* cols) {
-  if (numEntries < count) {
-    ResizePalette(palette, start+count);
-    numEntries = start+count;
-  }
-  PALETTEENTRY* logpal = new PALETTEENTRY[count];
-  for (int i=0; i<count; i++) {
-    logpal[i].peRed = cols[i].r >> 8;
-    logpal[i].peGreen = cols[i].g >> 8;
-    logpal[i].peBlue = cols[i].b >> 8;
-    logpal[i].peFlags = 0;
-  }
-  UnrealizeObject(palette);
-  SetPaletteEntries(palette, start, count, logpal);
-  delete [] logpal;
-}
-
-
-static LogWriter dcLog("DeviceContext");
-
-PixelFormat DeviceContext::getPF() const {
-  return getPF(dc);
-}
-
-PixelFormat DeviceContext::getPF(HDC dc) {
-  PixelFormat format;
-  CompatibleBitmap bitmap(dc, 1, 1);
-
-  // -=- Get the bitmap format information
-  BitmapInfo bi;
-  memset(&bi, 0, sizeof(bi));
-  bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
-  bi.bmiHeader.biBitCount = 0;
-
-  if (!::GetDIBits(dc, bitmap, 0, 1, NULL, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) {
-    throw rdr::SystemException("unable to determine device pixel format", GetLastError());
-  }
-  if (!::GetDIBits(dc, bitmap, 0, 1, NULL, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) {
-    throw rdr::SystemException("unable to determine pixel shifts/palette", GetLastError());
-  }
-
-  // -=- Munge the bitmap info here
-  switch (bi.bmiHeader.biBitCount) {
-  case 1:
-  case 4:
-    bi.bmiHeader.biBitCount = 8;
-    break;
-  case 24:
-    bi.bmiHeader.biBitCount = 32;
-    break;
-  }
-  bi.bmiHeader.biPlanes = 1;
-
-  format.trueColour = bi.bmiHeader.biBitCount > 8;
-  format.bigEndian = 0;
-  format.bpp = format.depth = bi.bmiHeader.biBitCount;
-
-  if (format.trueColour) {
-    DWORD rMask=0, gMask=0, bMask=0;
-
-    // Which true colour format is the DIB section using?
-    switch (bi.bmiHeader.biCompression) {
-    case BI_RGB:
-      // Default RGB layout
-      switch (bi.bmiHeader.biBitCount) {
-      case 16:
-        // RGB 555 - High Colour
-        dcLog.info("16-bit High Color");
-        rMask = 0x7c00;
-        bMask = 0x001f;
-        gMask = 0x03e0;
-        format.depth = 15;
-        break;
-      case 24:
-      case 32:
-        // RGB 888 - True Colour
-        dcLog.info("24/32-bit High Color");
-        rMask = 0xff0000;
-        gMask = 0x00ff00;
-        bMask = 0x0000ff;
-        format.depth = 24;
-        break;
-      default:
-        dcLog.error("bits per pixel %u not supported", bi.bmiHeader.biBitCount);
-        throw rdr::Exception("unknown bits per pixel specified");
-      };
-      break;
-    case BI_BITFIELDS:
-      // Custom RGB layout
-      rMask = bi.mask.red;
-      gMask = bi.mask.green;
-      bMask = bi.mask.blue;
-      dcLog.info("BitFields format: %lu, (%lx, %lx, %lx)",
-        bi.bmiHeader.biBitCount, rMask, gMask, bMask);
-      if (format.bpp == 32)
-        format.depth = 24; // ...probably
-      break;
-    };
-
-    // Convert the data we just retrieved
-    initMaxAndShift(rMask, &format.redMax, &format.redShift);
-    initMaxAndShift(gMask, &format.greenMax, &format.greenShift);
-    initMaxAndShift(bMask, &format.blueMax, &format.blueShift);
-  }
-
-  return format;
-}
-
-
-WindowDC::WindowDC(HWND wnd) : hwnd(wnd) {
-  dc = GetDC(wnd);
-  if (!dc)
-    throw rdr::SystemException("GetDC failed", GetLastError());
-}
-WindowDC::~WindowDC() {
-  if (dc)
-    ReleaseDC(hwnd, dc);
-}
-
-
-CompatibleDC::CompatibleDC(HDC existing) {
-  dc = CreateCompatibleDC(existing);
-  if (!dc)
-    throw rdr::SystemException("CreateCompatibleDC failed", GetLastError());
-}
-CompatibleDC::~CompatibleDC() {
-  if (dc)
-    DeleteDC(dc);
-}
-
-
-BitmapDC::BitmapDC(HDC hdc, HBITMAP hbitmap) : CompatibleDC(hdc){
-  oldBitmap = (HBITMAP)SelectObject(dc, hbitmap);
-  if (!oldBitmap)
-    throw rdr::SystemException("SelectObject to CompatibleDC failed",
-    GetLastError());
-}
-BitmapDC::~BitmapDC() {
-  SelectObject(dc, oldBitmap);
-}
-
-
-CompatibleBitmap::CompatibleBitmap(HDC hdc, int width, int height) {
-  hbmp = CreateCompatibleBitmap(hdc, width, height);
-  if (!hbmp)
-    throw rdr::SystemException("CreateCompatibleBitmap() failed", 
-    GetLastError());
-}
-CompatibleBitmap::~CompatibleBitmap() {
-  if (hbmp) DeleteObject(hbmp);
-}
-
-
-PaletteSelector::PaletteSelector(HDC dc, HPALETTE pal) : device(dc), redrawRequired(false) {
-  oldPal = SelectPalette(dc, pal, FALSE);
-  redrawRequired = RealizePalette(dc) > 0;
-}
-PaletteSelector::~PaletteSelector() {
-  if (oldPal) SelectPalette(device, oldPal, TRUE);
-}
-
-
-IconInfo::IconInfo(HICON icon) {
-  if (!GetIconInfo(icon, this))
-    throw rdr::SystemException("GetIconInfo() failed", GetLastError());
-}
-IconInfo::~IconInfo() {
-  if (hbmColor)
-    DeleteObject(hbmColor);
-  if (hbmMask)
-    DeleteObject(hbmMask);
-}
-
-
-ModuleFileName::ModuleFileName(HMODULE module) : TCharArray(MAX_PATH) {
-  if (!module) module = GetModuleHandle(0);
-  if (!GetModuleFileName(module, buf, MAX_PATH))
-    buf[0] = 0;
-}
-
 
 FileVersionInfo::FileVersionInfo(const TCHAR* filename) {
   // Get executable name
   ModuleFileName exeName;
-  if (!filename) filename = exeName.buf;
+  if (!filename)
+    filename = exeName.buf;
+
+  // Attempt to open the file, to cause Access Denied, etc, errors
+  // to be correctly reported, since the GetFileVersionInfoXXX calls lie...
+  {
+    Handle file(CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0));
+	  if (file.h == INVALID_HANDLE_VALUE)
+      throw rdr::SystemException("Failed to open file", GetLastError());
+  }
 
   // Get version info size
   DWORD handle;
@@ -249,7 +58,7 @@
 const TCHAR* FileVersionInfo::getVerString(const TCHAR* name, DWORD langId) {
   char langIdBuf[sizeof(langId)];
   for (int i=sizeof(langIdBuf)-1; i>=0; i--) {
-    langIdBuf[i] = langId & 0xff;
+    langIdBuf[i] = (char) (langId & 0xff);
     langId = langId >> 8;
   }
 
@@ -273,173 +82,31 @@
 }
 
 
-static LogWriter dfbLog("DynamicFn");
-
-DynamicFnBase::DynamicFnBase(const TCHAR* dllName, const char* fnName) : dllHandle(0), fnPtr(0) {
-  dllHandle = LoadLibrary(dllName);
-  if (!dllHandle) {
-    dfbLog.info("DLL %s not found (%d)", (const char*)CStr(dllName), GetLastError());
-    return;
-  }
-  fnPtr = GetProcAddress(dllHandle, fnName);
-  if (!fnPtr)
-    dfbLog.info("proc %s not found in %s (%d)", fnName, (const char*)CStr(dllName), GetLastError());
-}
-
-DynamicFnBase::~DynamicFnBase() {
-  if (dllHandle)
-    FreeLibrary(dllHandle);
-}
-
-
-static LogWriter miLog("MonitorInfo");
-
-MonitorInfo::MonitorInfo(HWND window) {
-#if (WINVER >= 0x0500)
-  typedef HMONITOR (WINAPI *_MonitorFromWindow_proto)(HWND,DWORD);
-  rfb::win32::DynamicFn<_MonitorFromWindow_proto> _MonitorFromWindow(_T("user32.dll"), "MonitorFromWindow");
-  typedef BOOL (WINAPI *_GetMonitorInfo_proto)(HMONITOR,LPMONITORINFO);
-  rfb::win32::DynamicFn<_GetMonitorInfo_proto> _GetMonitorInfo(_T("user32.dll"), "GetMonitorInfoA");
-
-  // Can we dynamically link to the monitor functions?
-  if (_MonitorFromWindow.isValid()) {
-    if (_GetMonitorInfo.isValid()) {
-      HMONITOR monitor = (*_MonitorFromWindow)(window, MONITOR_DEFAULTTONEAREST);
-      miLog.debug("monitor=%lx", monitor);
-      if (monitor) {
-        memset(this, 0, sizeof(MONITORINFOEXA));
-        cbSize = sizeof(MONITORINFOEXA);
-        if ((*_GetMonitorInfo)(monitor, this)) {
-          miLog.debug("monitor is %d,%d-%d,%d", rcMonitor.left, rcMonitor.top, rcMonitor.right, rcMonitor.bottom);
-          miLog.debug("work area is %d,%d-%d,%d", rcWork.left, rcWork.top, rcWork.right, rcWork.bottom);
-          miLog.debug("device is \"%s\"", szDevice);
-          return;
-        }
-        miLog.error("failed to get monitor info: %ld", GetLastError());
-      }
-    } else {
-      miLog.debug("GetMonitorInfo not found");
-    }
-  } else {
-      miLog.debug("MonitorFromWindow not found");
-  }
-#else
-#pragma message ("not building in GetMonitorInfo")
-  cbSize = sizeof(MonitorInfo);
-  szDevice[0] = 0;
-#endif
-
-  // Legacy fallbacks - just return the desktop settings
-  miLog.debug("using legacy fall-backs");
-  HWND desktop = GetDesktopWindow();
-  GetWindowRect(desktop, &rcMonitor);
-  SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);
-  dwFlags = 0;
-}
-
-
-#if (WINVER >= 0x0500)
-
-struct moveToMonitorData {
-  HWND window;
-  const char* monitorName;
-};
-
-typedef BOOL (WINAPI *_GetMonitorInfo_proto)(HMONITOR,LPMONITORINFO);
-static rfb::win32::DynamicFn<_GetMonitorInfo_proto> _GetMonitorInfo(_T("user32.dll"), "GetMonitorInfoA");
-
-static BOOL CALLBACK moveToMonitorEnumProc(HMONITOR monitor,
-                                    HDC dc,
-                                    LPRECT pos,
-                                    LPARAM d) {
-  moveToMonitorData* data = (moveToMonitorData*)d;
-  MONITORINFOEXA info;
-  memset(&info, 0, sizeof(info));
-  info.cbSize = sizeof(info);
-
-  if ((*_GetMonitorInfo)(monitor, &info)) {
-    if (stricmp(data->monitorName, info.szDevice) == 0) {
-      SetWindowPos(data->window, 0,
-        info.rcMonitor.left, info.rcMonitor.top,
-        info.rcMonitor.right, info.rcMonitor.bottom,
-        SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
-      return FALSE;
-    }
-  }
-
-  return TRUE;
-}
-
-#endif
-
-void moveToMonitor(HWND handle, const char* device) {
-  miLog.debug("moveToMonitor %s", device);
-
-#if (WINVER >= 0x500)
-  typedef BOOL (WINAPI *_EnumDisplayMonitors_proto)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
-  rfb::win32::DynamicFn<_EnumDisplayMonitors_proto> _EnumDisplayMonitors(_T("user32.dll"), "EnumDisplayMonitors");
-  if (!_EnumDisplayMonitors.isValid()) {
-    miLog.debug("EnumDisplayMonitors not found");
-    return;
-  }
-
-  moveToMonitorData data;
-  data.window = handle;
-  data.monitorName = device;
-
-  (*_EnumDisplayMonitors)(0, 0, &moveToMonitorEnumProc, (LPARAM)&data);
-#endif
-}
-
-
-void centerWindow(HWND handle, HWND parent, bool clipToParent) {
+void centerWindow(HWND handle, HWND parent) {
   RECT r;
-  if (parent && IsWindowVisible(parent)) {
-    if (!GetWindowRect(parent, &r)) return;
-  } else {
-    MonitorInfo mi(handle);
+  MonitorInfo mi(parent ? parent : handle);
+  if (!parent || !IsWindowVisible(parent) || !GetWindowRect(parent, &r))
     r=mi.rcWork;
-  }
-  centerWindow(handle, r, clipToParent);
+  centerWindow(handle, r);
+  mi.clipTo(handle);
 }
 
-void centerWindow(HWND handle, const RECT& r, bool clipToRect) {
+void centerWindow(HWND handle, const RECT& r) {
   RECT wr;
   if (!GetWindowRect(handle, &wr)) return;
   int w = wr.right-wr.left;
   int h = wr.bottom-wr.top;
-  if (clipToRect) {
-    w = min(r.right-r.left, w);
-    h = min(r.bottom-r.top, h);
-  }
   int x = (r.left + r.right - w)/2;
   int y = (r.top + r.bottom - h)/2;
-  UINT flags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | (clipToRect ? 0 : SWP_NOSIZE);
-  SetWindowPos(handle, 0, x, y, w, h, flags);
+  UINT flags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSIZE;
+  SetWindowPos(handle, 0, x, y, 0, 0, flags);
 }
 
-
-int MsgBox(HWND parent, const TCHAR* msg, UINT flags) {
-  const TCHAR* msgType = 0;
-  UINT tflags = flags & 0x70;
-  if (tflags == MB_ICONHAND)
-    msgType = _T("Error");
-  else if (tflags == MB_ICONQUESTION)
-    msgType = _T("Question");
-  else if (tflags == MB_ICONEXCLAMATION)
-    msgType = _T("Warning");
-  else if (tflags == MB_ICONASTERISK)
-    msgType = _T("Information");
-  flags |= MB_TOPMOST | MB_SETFOREGROUND;
-  int len = _tcslen(AppName.buf) + 1;
-  if (msgType) len += _tcslen(msgType) + 3;
-  TCharArray title = new TCHAR[len];
-  _tcscpy(title.buf, AppName.buf);
-  if (msgType) {
-    _tcscat(title.buf, _T(" : "));
-    _tcscat(title.buf, msgType);
-  }
-  return MessageBox(parent, msg, title.buf, flags);
+void resizeWindow(HWND handle, int width, int height) {
+  RECT r;
+  GetWindowRect(handle, &r);
+  SetWindowPos(handle, 0, 0, 0, width, height, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE);
+  centerWindow(handle, r);
 }
 
 
diff --git a/rfb_win32/Win32Util.h b/rfb_win32/Win32Util.h
index 5f0ab5a..8cc1a2b 100644
--- a/rfb_win32/Win32Util.h
+++ b/rfb_win32/Win32Util.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -25,105 +25,12 @@
 #ifndef __RFB_WIN32_GDIUTIL_H__
 #define __RFB_WIN32_GDIUTIL_H__
 
-#include <rfb/ColourMap.h>
-#include <rfb/PixelFormat.h>
-#include <rfb/Rect.h>
 #include <rfb_win32/TCharArray.h>
 
 namespace rfb {
 
   namespace win32 {
 
-    class LogicalPalette {
-    public:
-      LogicalPalette();
-      ~LogicalPalette();
-      void setEntries(int start, int count, const Colour* cols);
-      HPALETTE getHandle() {return palette;}
-    protected:
-      HPALETTE palette;
-      int numEntries;
-    };
-
-    class DeviceContext {
-    public:
-      DeviceContext() : dc(0) {}
-      virtual ~DeviceContext() {}
-      operator HDC() const {return dc;}
-      PixelFormat getPF() const;
-      static PixelFormat getPF(HDC dc);
-    protected:
-      HDC dc;
-    };
-
-    class WindowDC : public DeviceContext {
-    public:
-      WindowDC(HWND wnd);
-      virtual ~WindowDC();
-    protected:
-      HWND hwnd;
-    };
-
-    class CompatibleDC : public DeviceContext {
-    public:
-      CompatibleDC(HDC existing);
-      virtual ~CompatibleDC();
-    };
-
-    class BitmapDC : public CompatibleDC {
-    public:
-      BitmapDC(HDC hdc, HBITMAP hbitmap);
-      ~BitmapDC();
-    protected:
-      HBITMAP oldBitmap;
-    };
-
-    class CompatibleBitmap {
-    public:
-      CompatibleBitmap(HDC hdc, int width, int height);
-      virtual ~CompatibleBitmap();
-      operator HBITMAP() const {return hbmp;}
-    protected:
-      HBITMAP hbmp;
-    };
-
-    struct BitmapInfo {
-      BITMAPINFOHEADER bmiHeader;
-      union {
-        struct {
-          DWORD red;
-          DWORD green;
-          DWORD blue;
-        } mask;
-        RGBQUAD color[256];
-      };
-    };
-
-    inline void initMaxAndShift(DWORD mask, int* max, int* shift) {
-      for ((*shift) = 0; (mask & 1) == 0; (*shift)++) mask >>= 1;
-        (*max) = (rdr::U16)mask;
-    }
-
-    class PaletteSelector {
-    public:
-      PaletteSelector(HDC dc, HPALETTE pal);
-      ~PaletteSelector();
-      bool isRedrawRequired() {return redrawRequired;}
-    protected:
-      HPALETTE oldPal;
-      HDC device;
-      bool redrawRequired;
-    };
-
-    struct IconInfo : public ICONINFO {
-      IconInfo(HICON icon);
-      ~IconInfo();
-    };
-
-    struct ModuleFileName : public TCharArray {
-      ModuleFileName(HMODULE module=0);
-    };
-
     struct FileVersionInfo : public TCharArray {
       FileVersionInfo(const TCHAR* filename=0);
       const TCHAR* getVerString(const TCHAR* name, DWORD langId = 0x080904b0);
@@ -131,84 +38,15 @@
 
     bool splitPath(const TCHAR* path, TCHAR** dir, TCHAR** file);
 
-    class DynamicFnBase {
-    public:
-      DynamicFnBase(const TCHAR* dllName, const char* fnName);
-      ~DynamicFnBase();
-      bool isValid() const {return fnPtr != 0;}
-    protected:
-      void* fnPtr;
-      HMODULE dllHandle;
-    };
-
-    template<class T> class DynamicFn : public DynamicFnBase {
-    public:
-      DynamicFn(const TCHAR* dllName, const char* fnName) : DynamicFnBase(dllName, fnName) {}
-      T operator *() const {return (T)fnPtr;};
-    };
-
-    // Structure containing info on the monitor nearest the window.
-    // Copes with multi-monitor OSes and older ones.
-#if (WINVER >= 0x0500)
-    struct MonitorInfo : MONITORINFOEXA {
-      MonitorInfo(HWND hwnd);
-    };
-#else
-    struct MonitorInfo {
-      MonitorInfo(HWND hwnd);
-      DWORD cbSize;
-      RECT rcMonitor;
-      RECT rcWork;
-      DWORD dwFlags;
-      char szDevice[1]; // Always null...
-    };
-#endif
-    void moveToMonitor(HWND handle, const char* device);
-
-    class Handle {
-    public:
-      Handle(HANDLE h_=0) : h(h_) {}
-      ~Handle() {
-        if (h) CloseHandle(h);
-      }
-      operator HANDLE() {return h;}
-      HANDLE h;
-    };
-
     // Center the window to a rectangle, or to a parent window.
     // Optionally, resize the window to lay within the rect or parent window
     // If the parent window is NULL then the working area if the window's
     // current monitor is used instead.
-    void centerWindow(HWND handle, const RECT& r, bool clipToRect=false);
-    void centerWindow(HWND handle, HWND parent, bool clipToRect=false);
+    void centerWindow(HWND handle, const RECT& r);
+    void centerWindow(HWND handle, HWND parent);
 
-    // MsgBox helper function.  Define rfb::win32::AppName somewhere in your
-    // code and MsgBox will use its value in informational messages.
-    extern TStr AppName;
-    int MsgBox(HWND parent, const TCHAR* message, UINT flags);
-
-    // Get the computer name
-    struct ComputerName : TCharArray {
-      ComputerName() : TCharArray(MAX_COMPUTERNAME_LENGTH+1) {
-        ULONG namelength = MAX_COMPUTERNAME_LENGTH+1;
-        if (!GetComputerName(buf, &namelength))
-          _tcscpy(buf, _T(""));
-      }
-    };
-
-    // Allocate and/or manage LocalAlloc memory.
-    struct LocalMem {
-      LocalMem(int size) : ptr(LocalAlloc(LMEM_FIXED, size)) {
-        if (!ptr) throw rdr::SystemException("LocalAlloc", GetLastError());
-      }
-      LocalMem(void* p) : ptr(p) {}
-      ~LocalMem() {LocalFree(ptr);}
-      operator void*() {return ptr;}
-      void* takePtr() {
-        void* t = ptr; ptr = 0; return t;
-      }
-      void* ptr;
-    };
+    // resizeWindow resizes a window about its center
+    void resizeWindow(HWND handle, int width, int height);
 
   };
 
diff --git a/rfb_win32/keymap.h b/rfb_win32/keymap.h
index 69ce66f..a340d09 100644
--- a/rfb_win32/keymap.h
+++ b/rfb_win32/keymap.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/rfb_win32/msvcwarning.h b/rfb_win32/msvcwarning.h
deleted file mode 100644
index e50d9c1..0000000
--- a/rfb_win32/msvcwarning.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/* 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 info truncated
diff --git a/rfb_win32/rfb_win32.dsp b/rfb_win32/rfb_win32.dsp
index e9b746c..664e396 100644
--- a/rfb_win32/rfb_win32.dsp
+++ b/rfb_win32/rfb_win32.dsp
@@ -42,7 +42,7 @@
 # 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 /O2 /I ".." /FI"msvcwarning.h" /D "NDEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /YX /FD /c

+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /FI"rdr/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

@@ -65,7 +65,7 @@
 # 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" /FR /YX /FD /GZ /c

+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_LIB" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c

 # ADD BASE RSC /l 0x809 /d "_DEBUG"

 # ADD RSC /l 0x809 /d "_DEBUG"

 BSC32=bscmake.exe

@@ -88,7 +88,7 @@
 # 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 CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"rdr/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

@@ -134,6 +134,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\DeviceContext.cxx

+# End Source File

+# Begin Source File

+

 SOURCE=.\DeviceFrameBuffer.cxx

 # End Source File

 # Begin Source File

@@ -146,6 +150,14 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\DynamicFn.cxx

+# End Source File

+# Begin Source File

+

+SOURCE=.\EventManager.cxx

+# End Source File

+# Begin Source File

+

 SOURCE=.\FolderManager.cxx

 # End Source File

 # Begin Source File

@@ -158,6 +170,14 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\LowLevelKeyEvents.cxx

+# End Source File

+# Begin Source File

+

+SOURCE=.\MonitorInfo.cxx

+# End Source File

+# Begin Source File

+

 SOURCE=.\MsgWindow.cxx

 # End Source File

 # Begin Source File

@@ -186,6 +206,18 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\SDisplayCorePolling.cxx

+# End Source File

+# Begin Source File

+

+SOURCE=.\SDisplayCoreWMHooks.cxx

+# End Source File

+# Begin Source File

+

+SOURCE=.\Security.cxx

+# End Source File

+# Begin Source File

+

 SOURCE=.\Service.cxx

 # End Source File

 # Begin Source File

@@ -210,10 +242,18 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\Threading.cxx

+# End Source File

+# Begin Source File

+

 SOURCE=.\ToolBar.cxx

 # End Source File

 # Begin Source File

 

+SOURCE=.\TsSessions.cxx

+# End Source File

+# Begin Source File

+

 SOURCE=.\Win32Util.cxx

 # End Source File

 # Begin Source File

@@ -250,6 +290,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\BitmapInfo.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\CKeyboard.h

 # End Source File

 # Begin Source File

@@ -262,6 +306,14 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\CompatibleBitmap.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\ComputerName.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\CPointer.h

 # End Source File

 # Begin Source File

@@ -270,6 +322,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\DeviceContext.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\DeviceFrameBuffer.h

 # End Source File

 # Begin Source File

@@ -282,10 +338,26 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\DynamicFn.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\EventManager.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\FolderManager.h

 # End Source File

 # Begin Source File

 

+SOURCE=.\Handle.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\IconInfo.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\IntervalTimer.h

 # End Source File

 # Begin Source File

@@ -302,6 +374,30 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\LocalMem.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\LogicalPalette.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\LowLevelKeyEvents.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\ModuleFileName.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\MonitorInfo.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\MsgBox.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\MsgWindow.h

 # End Source File

 # Begin Source File

@@ -330,6 +426,18 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\SDisplayCoreDriver.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\SDisplayCorePolling.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\SDisplayCoreWMHooks.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\Security.h

 # End Source File

 # Begin Source File

@@ -358,6 +466,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\Threading.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\ToolBar.h

 # End Source File

 # Begin Source File

@@ -366,6 +478,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\TsSessions.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\Win32Util.h

 # End Source File

 # Begin Source File

diff --git a/rfbplayer/rfbplayer.dsp b/rfbplayer/rfbplayer.dsp
index 15dc54c..8c919ae 100644
--- a/rfbplayer/rfbplayer.dsp
+++ b/rfbplayer/rfbplayer.dsp
@@ -43,7 +43,7 @@
 # PROP Ignore_Export_Lib 0

 # PROP Target_Dir ""

 # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c

-# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /D "NDEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /c

+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /FI"rdr/msvcwarning.h" /D "NDEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /c

 # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32

 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32

 # ADD BASE RSC /l 0x419 /d "NDEBUG"

@@ -73,7 +73,7 @@
 # PROP Ignore_Export_Lib 0

 # PROP Target_Dir ""

 # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c

-# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c

+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c

 # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32

 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32

 # ADD BASE RSC /l 0x419 /d "_DEBUG"

diff --git a/tx/Makefile.in b/tx/Makefile.in
index b953325..89c30b1 100644
--- a/tx/Makefile.in
+++ b/tx/Makefile.in
@@ -1,6 +1,5 @@
 
-SRCS = TXWindow.cxx TXScrollbar.cxx TXViewport.cxx TXImage.cxx TXMenu.cxx \
-       Timer.cxx
+SRCS = TXWindow.cxx TXScrollbar.cxx TXViewport.cxx TXImage.cxx TXMenu.cxx
 
 OBJS = $(SRCS:.cxx=.o)
 
diff --git a/tx/TXButton.h b/tx/TXButton.h
index c22dbf9..b747279 100644
--- a/tx/TXButton.h
+++ b/tx/TXButton.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -62,8 +62,8 @@
     text.buf = rfb::strDup(text_);
     int textWidth = XTextWidth(defaultFS, text.buf, strlen(text.buf));
     int textHeight = (defaultFS->ascent + defaultFS->descent);
-    int newWidth = vncmax(width(), textWidth + xPad*2 + bevel*2);
-    int newHeight = vncmax(height(), textHeight + yPad*2 + bevel*2);
+    int newWidth = __rfbmax(width(), textWidth + xPad*2 + bevel*2);
+    int newHeight = __rfbmax(height(), textHeight + yPad*2 + bevel*2);
     if (width() < newWidth || height() < newHeight) {
       resize(newWidth, newHeight);
     }
diff --git a/tx/TXCheckbox.h b/tx/TXCheckbox.h
index 41eef27..3c2a23b 100644
--- a/tx/TXCheckbox.h
+++ b/tx/TXCheckbox.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -71,8 +71,8 @@
     text = strdup(text_);
     int textWidth = XTextWidth(defaultFS, text, strlen(text));
     int textHeight = (defaultFS->ascent + defaultFS->descent);
-    int newWidth = vncmax(width(), textWidth + xPad*2 + boxPad*2 + boxSize);
-    int newHeight = vncmax(height(), textHeight + yPad*2);
+    int newWidth = __rfbmax(width(), textWidth + xPad*2 + boxPad*2 + boxSize);
+    int newHeight = __rfbmax(height(), textHeight + yPad*2);
     if (width() < newWidth || height() < newHeight) {
       resize(newWidth, newHeight);
     }
diff --git a/tx/TXDialog.h b/tx/TXDialog.h
index 01677a9..0bfd88a 100644
--- a/tx/TXDialog.h
+++ b/tx/TXDialog.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -38,9 +38,7 @@
     : TXWindow(dpy, width, height), done(false), ok(false), modal(modal_)
   {
     toplevel(name, this);
-    int dpyWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
-    int dpyHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
-    setUSPosition((dpyWidth - width - 10) / 2, (dpyHeight - height - 30) / 2);
+    resize(width, height);
   }
 
   virtual ~TXDialog() {}
@@ -75,6 +73,14 @@
   // to make sure that checkboxes have the right state, etc.
   virtual void initDialog() {}
 
+  // resize() is overidden here to re-center the dialog
+  void resize(int w, int h) {
+    TXWindow::resize(w,h);
+    int dpyWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
+    int dpyHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
+    setUSPosition((dpyWidth - width() - 10) / 2, (dpyHeight - height() - 30) / 2);
+  }    
+
 protected:
   virtual void deleteWindow(TXWindow* w) {
     ok = false;
diff --git a/tx/TXEntry.h b/tx/TXEntry.h
index 0d1f005..9002957 100644
--- a/tx/TXEntry.h
+++ b/tx/TXEntry.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -58,7 +58,7 @@
                  | ButtonPressMask);
     text[0] = 0;
     int textHeight = (defaultFS->ascent + defaultFS->descent);
-    int newHeight = vncmax(height(), textHeight + yPad*2 + bevel*2);
+    int newHeight = __rfbmax(height(), textHeight + yPad*2 + bevel*2);
     if (height() < newHeight) {
       resize(width(), newHeight);
     }
diff --git a/tx/TXImage.cxx b/tx/TXImage.cxx
index 7801578..5fa6828 100644
--- a/tx/TXImage.cxx
+++ b/tx/TXImage.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -74,8 +74,8 @@
   if (w == width() && h == height()) return;
 
   int oldStrideBytes = getStride() * (format.bpp/8);
-  int rowsToCopy = vncmin(h, height());
-  int bytesPerRow = vncmin(w, width()) * (format.bpp/8);
+  int rowsToCopy = __rfbmin(h, height());
+  int bytesPerRow = __rfbmin(w, width()) * (format.bpp/8);
   rdr::U8* oldData = 0;
   bool allocData = false;
 
diff --git a/tx/TXImage.h b/tx/TXImage.h
index 8185366..055bd22 100644
--- a/tx/TXImage.h
+++ b/tx/TXImage.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/tx/TXLabel.h b/tx/TXLabel.h
index bbd893a..3d5200d 100644
--- a/tx/TXLabel.h
+++ b/tx/TXLabel.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -63,12 +63,12 @@
     } while (text.buf[i] != 0);
     int textHeight = ((defaultFS->ascent + defaultFS->descent + lineSpacing)
                       * lines);
-
-    int newWidth = vncmax(width(), textWidth + xPad*2);
-    int newHeight = vncmax(height(), textHeight + yPad*2);
+    int newWidth = __rfbmax(width(), textWidth + xPad*2);
+    int newHeight = __rfbmax(height(), textHeight + yPad*2);
     if (width() < newWidth || height() < newHeight) {
       resize(newWidth, newHeight);
     }
+    invalidate();
   }
 
 private:
diff --git a/tx/TXMenu.cxx b/tx/TXMenu.cxx
index 4069f2d..92712f5 100644
--- a/tx/TXMenu.cxx
+++ b/tx/TXMenu.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/tx/TXMenu.h b/tx/TXMenu.h
index 82dafa5..d0245ee 100644
--- a/tx/TXMenu.h
+++ b/tx/TXMenu.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/tx/TXMsgBox.h b/tx/TXMsgBox.h
index 00c4eb5..ff84e6a 100644
--- a/tx/TXMsgBox.h
+++ b/tx/TXMsgBox.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -49,7 +49,7 @@
 class TXMsgBox : public TXDialog, public TXButtonCallback {
 public:
   TXMsgBox(Display* dpy, const char* text, unsigned int flags, const char* title=0)
-    : TXDialog(dpy, 1, 1, "Message"),
+    : TXDialog(dpy, 1, 1, "Message", true),
       textLabel(dpy, "", this),
     okButton(dpy, "OK", this, this, 60),
     cancelButton(dpy, "Cancel", this, this, 60)
@@ -99,7 +99,8 @@
   }
 
   virtual void buttonActivate(TXButton* b) {
-    ok = (b == &okButton);   
+    ok = (b == &okButton);
+    done = true; 
     unmap();
   }
 
diff --git a/tx/TXScrollbar.cxx b/tx/TXScrollbar.cxx
index 946ccff..47e124c 100644
--- a/tx/TXScrollbar.cxx
+++ b/tx/TXScrollbar.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/tx/TXScrollbar.h b/tx/TXScrollbar.h
index 4cc2afa..87fec3d 100644
--- a/tx/TXScrollbar.h
+++ b/tx/TXScrollbar.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/tx/TXViewport.cxx b/tx/TXViewport.cxx
index 67982e9..abe5173 100644
--- a/tx/TXViewport.cxx
+++ b/tx/TXViewport.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -100,21 +100,19 @@
   else if (ev->y_root == 0)     bumpScrollY = bumpScrollPixels;
 
   if (bumpScrollX || bumpScrollY) {
-    if (bumpScrollTimer.isSet()) return true;
+    if (bumpScrollTimer.isStarted()) return true;
     if (setOffset(xOff + bumpScrollX, yOff + bumpScrollY)) {
-      bumpScrollTimer.reset(25);
+      bumpScrollTimer.start(25);
       return true;
     }
   }
 
-  bumpScrollTimer.cancel();
+  bumpScrollTimer.stop();
   return false;
 }
 
-void TXViewport::timerCallback(Timer* timer)
-{
-  if (setOffset(xOff + bumpScrollX, yOff + bumpScrollY))
-    bumpScrollTimer.reset(25);
+bool TXViewport::handleTimeout(rfb::Timer* timer) {
+  return setOffset(xOff + bumpScrollX, yOff + bumpScrollY);
 }
 
 void TXViewport::resizeNotify()
diff --git a/tx/TXViewport.h b/tx/TXViewport.h
index 9dddc2f..0c9857d 100644
--- a/tx/TXViewport.h
+++ b/tx/TXViewport.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -28,12 +28,12 @@
 #ifndef __TXVIEWPORT_H__
 #define __TXVIEWPORT_H__
 
+#include <rfb/Timer.h>
 #include "TXWindow.h"
 #include "TXScrollbar.h"
-#include "Timer.h"
 
 class TXViewport : public TXWindow, public TXScrollbarCallback,
-                   public TimerCallback {
+                   public rfb::Timer::Callback {
 public:
   TXViewport(Display* dpy_, int width, int height, TXWindow* parent_=0);
   virtual ~TXViewport();
@@ -62,14 +62,14 @@
 private:
   virtual void resizeNotify();
   virtual void scrollbarPos(int x, int y, TXScrollbar* sb);
-  virtual void timerCallback(Timer* timer);
+  virtual bool handleTimeout(rfb::Timer* timer);
   TXWindow* clipper;
   TXWindow* child;
   TXScrollbar* hScrollbar;
   TXScrollbar* vScrollbar;
   const int scrollbarSize;
   int xOff, yOff;
-  Timer bumpScrollTimer;
+  rfb::Timer bumpScrollTimer;
   bool bumpScroll;
   bool needScrollbars;
   int bumpScrollX, bumpScrollY;
diff --git a/tx/TXWindow.cxx b/tx/TXWindow.cxx
index 1114685..6d211be 100644
--- a/tx/TXWindow.cxx
+++ b/tx/TXWindow.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -73,7 +73,7 @@
   white = enabledBg = cols[5].pixel;
   defaultGC = XCreateGC(dpy, DefaultRootWindow(dpy), 0, 0);
   defaultFS
-    = XLoadQueryFont(dpy, "-*-helvetica-medium-r-*-*-12-*-*-*-*-*-iso8859-*");
+    = XLoadQueryFont(dpy, "-*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*");
   if (!defaultFS) {
     defaultFS = XLoadQueryFont(dpy, "fixed");
     if (!defaultFS) {
diff --git a/tx/TXWindow.h b/tx/TXWindow.h
index 20c31c9..5ada181 100644
--- a/tx/TXWindow.h
+++ b/tx/TXWindow.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -110,6 +110,7 @@
   void resize(int w, int h);
   void raise()                 { XRaiseWindow(dpy, win()); }
   void setBorderWidth(int bw);
+  void invalidate(int x=0, int y=0, int w=0, int h=0) { XClearArea(dpy, win(), x, y, w, h, True); }
 
   // ownSelection requests that the window owns the given selection from the
   // given time (the time should be taken from an X event).
diff --git a/tx/Timer.cxx b/tx/Timer.cxx
deleted file mode 100644
index 3acb631..0000000
--- a/tx/Timer.cxx
+++ /dev/null
@@ -1,110 +0,0 @@
-/* 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.
- */
-//
-// Timer.cxx
-//
-
-// XXX Lynx/OS 2.3: get proto for gettimeofday()
-#ifdef Lynx
-#include <sys/proto.h>
-#endif
-
-#include "Timer.h"
-
-static Timer* timers;
-
-void Timer::callTimers()
-{
-  struct timeval now;
-  gettimeofday(&now, 0);
-  while (timers) {
-    Timer* timer = timers;
-    if (timer->before(now)) {
-      timers = timers->next;
-      timer->cb->timerCallback(timer);
-    } else {
-      break;
-    }
-  }
-}
-
-bool Timer::getTimeout(struct timeval* timeout)
-{
-  if (!timers) return false;
-  Timer* timer = timers;
-  struct timeval now;
-  gettimeofday(&now, 0);
-  if (timer->before(now)) {
-    timeout->tv_sec = 0;
-    timeout->tv_usec = 0;
-    return true;
-  }
-  timeout->tv_sec = timer->tv.tv_sec - now.tv_sec;
-  if (timer->tv.tv_usec < now.tv_usec) {
-    timeout->tv_usec = timer->tv.tv_usec + 1000000 - now.tv_usec;
-    timeout->tv_sec--;
-  } else {
-    timeout->tv_usec = timer->tv.tv_usec - now.tv_usec;
-  }
-  return true;
-}
-
-Timer::Timer(TimerCallback* cb_) : cb(cb_) {}
-Timer::~Timer()
-{
-  cancel();
-}
-
-void Timer::reset(int ms) {
-  cancel();
-  gettimeofday(&tv, 0);
-  tv.tv_sec += ms / 1000;
-  tv.tv_usec += (ms % 1000) * 1000;
-  if (tv.tv_usec > 1000000) {
-    tv.tv_sec += 1;
-    tv.tv_usec -= 1000000;
-  }
-
-  Timer** timerPtr;
-  for (timerPtr = &timers; *timerPtr; timerPtr = &(*timerPtr)->next) {
-    if (before((*timerPtr)->tv)) {
-      next = *timerPtr;
-      *timerPtr = this;
-      break;
-    }
-  }
-  if (!*timerPtr) {
-    next = 0;
-    *timerPtr = this;
-  }
-}
-
-void Timer::cancel() {
-  for (Timer** timerPtr = &timers; *timerPtr; timerPtr = &(*timerPtr)->next) {
-    if (*timerPtr == this) {
-      *timerPtr = (*timerPtr)->next;
-      break;
-    }
-  }
-}
-
-bool Timer::isSet() {
-  for (Timer* timer = timers; timer; timer = timer->next)
-    if (timer == this) return true;
-  return false;
-}
diff --git a/tx/Timer.h b/tx/Timer.h
deleted file mode 100644
index dabe4cd..0000000
--- a/tx/Timer.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/* 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 __TIMER_H__
-#define __TIMER_H__
-
-#include <sys/time.h>
-#include <unistd.h>
-
-class TimerCallback;
-
-class Timer {
-public:
-  static void callTimers();
-  static bool getTimeout(struct timeval* timeout);
-
-  Timer(TimerCallback* cb_);
-  ~Timer();
-  void reset(int ms);
-  void cancel();
-  bool isSet();
-  bool before(struct timeval other) {
-    return (tv.tv_sec < other.tv_sec ||
-            (tv.tv_sec == other.tv_sec && tv.tv_usec < other.tv_usec));
-  }
-private:
-  struct timeval tv;
-  TimerCallback* cb;
-  Timer* next;
-};
-
-class TimerCallback {
-public:
-  virtual void timerCallback(Timer* timer) = 0;
-};
-
-#endif
diff --git a/vncconfig/Authentication.h b/vncconfig/Authentication.h
index 5923c2c..f4b38f8 100644
--- a/vncconfig/Authentication.h
+++ b/vncconfig/Authentication.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -18,62 +18,28 @@
 #ifndef WINVNCCONF_AUTHENTICATION
 #define WINVNCCONF_AUTHENTICATION
 
+#include <vncconfig/PasswordDialog.h>
 #include <rfb_win32/Registry.h>
 #include <rfb_win32/Dialog.h>
-#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/OSVersion.h>
+#include <rfb_win32/MsgBox.h>
 #include <rfb/ServerCore.h>
 #include <rfb/secTypes.h>
-#include <rfb/vncAuth.h>
+#include <rfb/Password.h>
 
-
-extern rfb::VncAuthPasswdConfigParameter vncAuthPasswd;
+static rfb::BoolParameter queryOnlyIfLoggedOn("QueryOnlyIfLoggedOn",
+  "Only prompt for a local user to accept incoming connections if there is a user logged on", false);
 
 namespace rfb {
 
   namespace win32 {
 
-    class VncPasswdDialog : public Dialog {
-    public:
-      VncPasswdDialog(const RegKey& rk) : Dialog(GetModuleHandle(0)), regKey(rk), warnPasswdInsecure(false) {}
-      bool showDialog() {
-        return Dialog::showDialog(MAKEINTRESOURCE(IDD_AUTH_VNC_PASSWD));
-      }
-      bool onOk() {
-        TCharArray password1 = getItemString(IDC_PASSWORD1);
-        TCharArray password2 = getItemString(IDC_PASSWORD2);;
-        if (_tcscmp(password1.buf, password2.buf) != 0) {
-          MsgBox(0, _T("The supplied passwords do not match"),
-                 MB_ICONEXCLAMATION | MB_OK);
-          return false;
-        }
-        if (warnPasswdInsecure &&
-          (MsgBox(0, _T("Please note that your VNC password cannot be stored securely on this system.  ")
-                     _T("Are you sure you wish to continue?"),
-                  MB_YESNO | MB_ICONWARNING) == IDNO))
-          return false;
-        char passwd[9];
-        memset(passwd, 0, sizeof(passwd));
-        strCopy(passwd, CStr(password1.buf), sizeof(passwd));
-        vncAuthObfuscatePasswd(passwd);
-        regKey.setBinary(_T("Password"), passwd, 8);
-        return true;
-      }
-      void setWarnPasswdInsecure(bool warn) {
-        warnPasswdInsecure = warn;
-      }
-    protected:
-      const RegKey& regKey;
-      bool warnPasswdInsecure;
-    };
-
     class AuthenticationPage : public PropSheetPage {
     public:
       AuthenticationPage(const RegKey& rk)
-        : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_AUTHENTICATION)),
-        passwd(rk), regKey(rk) {}
+        : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_AUTHENTICATION)), regKey(rk) {}
       void initDialog() {
-        CharArray sec_types_str;
-        sec_types_str.buf = rfb::Server::sec_types.getData();
+        CharArray sec_types_str(SSecurityFactoryStandard::sec_types.getData());
         std::list<int> sec_types = parseSecTypes(sec_types_str.buf);
 
         useNone = useVNC = false;
@@ -83,55 +49,94 @@
           else if ((*i) == secTypeVncAuth) useVNC = true;
         }
 
+        HWND security = GetDlgItem(handle, IDC_ENCRYPTION);
+        SendMessage(security, CB_ADDSTRING, 0, (LPARAM)_T("Always Off"));
+        SendMessage(security, CB_SETCURSEL, 0, 0);
+        enableItem(IDC_AUTH_NT, false); enableItem(IDC_AUTH_NT_CONF, false);
+        enableItem(IDC_ENCRYPTION, false); enableItem(IDC_AUTH_RA2_CONF, false);
+
         setItemChecked(IDC_AUTH_NONE, useNone);
         setItemChecked(IDC_AUTH_VNC, useVNC);
         setItemChecked(IDC_QUERY_CONNECT, rfb::Server::queryConnect);
+        setItemChecked(IDC_QUERY_LOGGED_ON, queryOnlyIfLoggedOn);
+        onCommand(IDC_AUTH_NONE, 0);
       }
       bool onCommand(int id, int cmd) {
         switch (id) {
         case IDC_AUTH_VNC_PASSWD:
-          passwd.showDialog();
+          {
+            PasswordDialog passwdDlg(regKey, registryInsecure);
+            passwdDlg.showDialog(handle);
+          }
           return true;
         case IDC_AUTH_NONE:
         case IDC_AUTH_VNC:
+          enableItem(IDC_AUTH_VNC_PASSWD, isItemChecked(IDC_AUTH_VNC));
         case IDC_QUERY_CONNECT:
-          setChanged((rfb::Server::queryConnect != isItemChecked(IDC_QUERY_CONNECT)) ||
-                     (useNone != isItemChecked(IDC_AUTH_NONE)) ||
-                     (useVNC != isItemChecked(IDC_AUTH_VNC)));
+        case IDC_QUERY_LOGGED_ON:
+          setChanged((useNone != isItemChecked(IDC_AUTH_NONE)) ||
+                     (useVNC != isItemChecked(IDC_AUTH_VNC)) ||
+                     (rfb::Server::queryConnect != isItemChecked(IDC_QUERY_CONNECT)) ||
+                     (queryOnlyIfLoggedOn != isItemChecked(IDC_QUERY_LOGGED_ON)));
+          enableItem(IDC_QUERY_LOGGED_ON, enableQueryOnlyIfLoggedOn());
           return false;
         };
         return false;
       }
       bool onOk() {
+        bool useVncChanged = useVNC != isItemChecked(IDC_AUTH_VNC);
         useVNC = isItemChecked(IDC_AUTH_VNC);
         useNone = isItemChecked(IDC_AUTH_NONE);
         if (useVNC) {
-          CharArray password = vncAuthPasswd.getVncAuthPasswd();
-          if (!password.buf || strlen(password.buf) == 0) {
-            MsgBox(0, _T("The VNC authentication method is enabled, but no password is specified!  ")
-                      _T("The password dialog will now be shown."), MB_ICONEXCLAMATION | MB_OK);
-            passwd.showDialog();
-          }
+          verifyVncPassword(regKey);
           regKey.setString(_T("SecurityTypes"), _T("VncAuth"));
-        } else if (useNone) {
+        } else {
+          if (haveVncPassword() && useVncChanged &&
+              MsgBox(0, _T("The VNC authentication method is disabled, but a password is still stored for it.\n")
+                        _T("Do you want to remove the VNC authentication password from the registry?"),
+                        MB_ICONWARNING | MB_YESNO) == IDYES) {
+            regKey.setBinary(_T("Password"), 0, 0);
+          }
           regKey.setString(_T("SecurityTypes"), _T("None"));
         }
         regKey.setString(_T("ReverseSecurityTypes"), _T("None"));
         regKey.setBool(_T("QueryConnect"), isItemChecked(IDC_QUERY_CONNECT));
+        regKey.setBool(_T("QueryOnlyIfLoggedOn"), isItemChecked(IDC_QUERY_LOGGED_ON));
         return true;
       }
       void setWarnPasswdInsecure(bool warn) {
-        passwd.setWarnPasswdInsecure(warn);
+        registryInsecure = warn;
       }
+      bool enableQueryOnlyIfLoggedOn() {
+        return isItemChecked(IDC_QUERY_CONNECT) && osVersion.isPlatformNT && (osVersion.dwMajorVersion >= 5);
+      }
+
+
+      static bool haveVncPassword() {
+        PlainPasswd password(SSecurityFactoryStandard::vncAuthPasswd.getVncAuthPasswd());
+        return password.buf && strlen(password.buf) != 0;
+      }
+
+      static void verifyVncPassword(const RegKey& regKey) {
+        if (!haveVncPassword()) {
+          MsgBox(0, _T("The VNC authentication method is enabled, but no password is specified.\n")
+                    _T("The password dialog will now be shown."), MB_ICONINFORMATION | MB_OK);
+          PasswordDialog passwd(regKey, registryInsecure);
+          passwd.showDialog();
+        }
+      }
+
     protected:
       RegKey regKey;
-      VncPasswdDialog passwd;
+      static bool registryInsecure;
       bool useNone;
       bool useVNC;
     };
 
   };
 
+  bool AuthenticationPage::registryInsecure = false;
+
 };
 
 #endif
diff --git a/vncconfig/Connections.h b/vncconfig/Connections.h
index 133e81c..7512cc6 100644
--- a/vncconfig/Connections.h
+++ b/vncconfig/Connections.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -22,6 +22,7 @@
 
 #include <rfb_win32/Registry.h>
 #include <rfb_win32/Dialog.h>
+#include <rfb_win32/ModuleFileName.h>
 #include <rfb/Configuration.h>
 #include <rfb/Blacklist.h>
 #include <network/TcpSocket.h>
@@ -43,42 +44,37 @@
     public:
       ConnHostDialog() : Dialog(GetModuleHandle(0)) {}
       bool showDialog(const TCHAR* pat) {
-        delete [] pattern.buf;
-        pattern.buf = tstrDup(pat);
+        pattern.replaceBuf(tstrDup(pat));
         return Dialog::showDialog(MAKEINTRESOURCE(IDD_CONN_HOST));
       }
       void initDialog() {
-        if (_tcslen(pattern.buf) == 0) {
-          delete [] pattern.buf;
-          pattern.buf = tstrDup(_T("+"));
-        }
+        if (_tcslen(pattern.buf) == 0)
+          pattern.replaceBuf(tstrDup("+"));
 
         if (pattern.buf[0] == _T('+'))
           setItemChecked(IDC_ALLOW, true);
+        else if (pattern.buf[0] == _T('?'))
+          setItemChecked(IDC_QUERY, true);
         else
           setItemChecked(IDC_DENY, true);
 
         setItemString(IDC_HOST_PATTERN, &pattern.buf[1]);
-
-        delete [] pattern.buf;
-        pattern.buf = 0;
+        pattern.replaceBuf(0);
       }
       bool onOk() {
-        delete [] pattern.buf;
-        pattern.buf = 0;
-
         TCharArray host = getItemString(IDC_HOST_PATTERN);
-
         TCharArray newPat(_tcslen(host.buf)+2);
         if (isItemChecked(IDC_ALLOW))
           newPat.buf[0] = _T('+');
+        else if (isItemChecked(IDC_QUERY))
+          newPat.buf[0] = _T('?');
         else
           newPat.buf[0] = _T('-');
         newPat.buf[1] = 0;
         _tcscat(newPat.buf, host.buf);
 
-        network::TcpFilter::Pattern pat = network::TcpFilter::parsePattern(CStr(newPat.buf));
-        pattern.buf = TCharArray(network::TcpFilter::patternToStr(pat)).takeBuf();
+        network::TcpFilter::Pattern pat(network::TcpFilter::parsePattern(CStr(newPat.buf)));
+        pattern.replaceBuf(TCharArray(network::TcpFilter::patternToStr(pat)).takeBuf());
         return true;
       }
       const TCHAR* getPattern() {return pattern.buf;}
@@ -92,10 +88,11 @@
         : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_CONNECTIONS)), regKey(rk) {}
       void initDialog() {
         vlog.debug("set IDC_PORT %d", (int)port_number);
-        setItemInt(IDC_PORT, port_number);
+        setItemInt(IDC_PORT, port_number ? port_number : 5900);
+        setItemChecked(IDC_RFB_ENABLE, port_number != 0);
         setItemInt(IDC_IDLE_TIMEOUT, rfb::Server::idleTimeout);
         vlog.debug("set IDC_HTTP_PORT %d", (int)http_port);
-        setItemInt(IDC_HTTP_PORT, http_port);
+        setItemInt(IDC_HTTP_PORT, http_port ? http_port : 5800);
         setItemChecked(IDC_HTTP_ENABLE, http_port != 0);
         enableItem(IDC_HTTP_PORT, http_port != 0);
         setItemChecked(IDC_LOCALHOST, localHost);
@@ -113,7 +110,7 @@
             SendMessage(listBox, LB_ADDSTRING, 0, (LPARAM)(const TCHAR*)TStr(first.buf));
         }
 
-        onCommand(IDC_HOSTS, EN_CHANGE);
+        onCommand(IDC_RFB_ENABLE, EN_CHANGE);
       }
       bool onCommand(int id, int cmd) {
         switch (id) {
@@ -133,8 +130,7 @@
         case IDC_PORT:
           if (cmd == EN_CHANGE) {
             try {
-              if (getItemInt(IDC_PORT) > 100)
-                setItemInt(IDC_HTTP_PORT, getItemInt(IDC_PORT)-100);
+              setItemInt(IDC_HTTP_PORT, rfbPortToHTTP(getItemInt(IDC_PORT)));
             } catch (...) {
             }
           }
@@ -145,19 +141,33 @@
           return false;
 
         case IDC_HTTP_ENABLE:
-          enableItem(IDC_HTTP_PORT, isItemChecked(IDC_HTTP_ENABLE));
-          setChanged(isChanged());
-          return false;
-
+        case IDC_RFB_ENABLE:
         case IDC_LOCALHOST:
-          enableItem(IDC_HOSTS, !isItemChecked(IDC_LOCALHOST));
-          enableItem(IDC_HOST_REMOVE, !isItemChecked(IDC_LOCALHOST));
-          enableItem(IDC_HOST_UP, !isItemChecked(IDC_LOCALHOST));
-          enableItem(IDC_HOST_DOWN, !isItemChecked(IDC_LOCALHOST));
-          enableItem(IDC_HOST_EDIT, !isItemChecked(IDC_LOCALHOST));
-          enableItem(IDC_HOST_ADD, !isItemChecked(IDC_LOCALHOST));
-          setChanged(isChanged());
-          return false;
+          {
+            // HTTP port
+            enableItem(IDC_HTTP_PORT, isItemChecked(IDC_HTTP_ENABLE) && isItemChecked(IDC_RFB_ENABLE));
+            enableItem(IDC_HTTP_ENABLE, isItemChecked(IDC_RFB_ENABLE));
+
+            // RFB port
+            enableItem(IDC_PORT, isItemChecked(IDC_RFB_ENABLE));
+
+            // Hosts
+            enableItem(IDC_LOCALHOST, isItemChecked(IDC_RFB_ENABLE));
+
+            bool enableHosts = !isItemChecked(IDC_LOCALHOST) && isItemChecked(IDC_RFB_ENABLE);
+            enableItem(IDC_HOSTS, enableHosts);
+            enableItem(IDC_HOST_ADD, enableHosts);
+            if (!enableHosts) {
+              enableItem(IDC_HOST_REMOVE, enableHosts);
+              enableItem(IDC_HOST_UP, enableHosts);
+              enableItem(IDC_HOST_DOWN, enableHosts);
+              enableItem(IDC_HOST_EDIT, enableHosts);
+            } else {
+              onCommand(IDC_HOSTS, EN_CHANGE);
+            }
+            setChanged(isChanged());
+            return false;
+          }
 
         case IDC_HOST_ADD:
           if (hostDialog.showDialog(_T("")))
@@ -228,11 +238,11 @@
         return false;
       }
       bool onOk() {
-        regKey.setInt(_T("PortNumber"), getItemInt(IDC_PORT));
-        regKey.setInt(_T("LocalHost"), isItemChecked(IDC_LOCALHOST));
+        regKey.setInt(_T("PortNumber"), isItemChecked(IDC_RFB_ENABLE) ? getItemInt(IDC_PORT) : 0);
         regKey.setInt(_T("IdleTimeout"), getItemInt(IDC_IDLE_TIMEOUT));
-        regKey.setInt(_T("HTTPPortNumber"), isItemChecked(IDC_HTTP_ENABLE) ? getItemInt(IDC_HTTP_PORT) : 0);
-
+        regKey.setInt(_T("HTTPPortNumber"), isItemChecked(IDC_HTTP_ENABLE) && isItemChecked(IDC_RFB_ENABLE)
+                                            ? getItemInt(IDC_HTTP_PORT) : 0);
+        regKey.setInt(_T("LocalHost"), isItemChecked(IDC_LOCALHOST));
         regKey.setString(_T("Hosts"), TCharArray(getHosts()).buf);
         return true;
       }
@@ -266,6 +276,16 @@
         }
         return strDup(hosts_str.buf);
       }
+      int rfbPortToHTTP(int rfbPort) {
+        int offset = -100;
+        if (http_port)
+          offset = http_port - port_number;
+        int httpPort = rfbPort + offset;
+        if (httpPort <= 0)
+          httpPort = rfbPort;
+        return httpPort;
+      }
+
     protected:
       RegKey regKey;
       ConnHostDialog hostDialog;
@@ -275,4 +295,4 @@
 
 };
 
-#endif
\ No newline at end of file
+#endif
diff --git a/vncconfig/Desktop.h b/vncconfig/Desktop.h
index a0a325a..164269a 100644
--- a/vncconfig/Desktop.h
+++ b/vncconfig/Desktop.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -21,6 +21,7 @@
 #include <rfb_win32/Registry.h>
 #include <rfb_win32/Dialog.h>
 #include <rfb_win32/SDisplay.h>
+#include <rfb_win32/DynamicFn.h>
 
 namespace rfb {
 
@@ -90,4 +91,4 @@
 
 };
 
-#endif
\ No newline at end of file
+#endif
diff --git a/vncconfig/Hooking.h b/vncconfig/Hooking.h
index b9bec15..9be82f3 100644
--- a/vncconfig/Hooking.h
+++ b/vncconfig/Hooking.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -21,6 +21,7 @@
 #include <rfb_win32/Registry.h>
 #include <rfb_win32/Dialog.h>
 #include <rfb_win32/SDisplay.h>
+#include <rfb_win32/WMPoller.h>
 #include <rfb/ServerCore.h>
 
 namespace rfb {
@@ -32,26 +33,48 @@
       HookingPage(const RegKey& rk)
         : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_HOOKING)), regKey(rk) {}
       void initDialog() {
-        setItemChecked(IDC_USEHOOKS, rfb::win32::SDisplay::use_hooks);
+        setItemChecked(IDC_USEPOLLING, rfb::win32::SDisplay::updateMethod == 0);
+        setItemChecked(IDC_USEHOOKS, (rfb::win32::SDisplay::updateMethod == 1) &&
+                       rfb::win32::SDisplay::areHooksAvailable());
+        enableItem(IDC_USEHOOKS, rfb::win32::SDisplay::areHooksAvailable());
+        setItemChecked(IDC_USEDRIVER, (rfb::win32::SDisplay::updateMethod == 2) &&
+                       rfb::win32::SDisplay::isDriverAvailable());
+        enableItem(IDC_USEDRIVER, rfb::win32::SDisplay::isDriverAvailable());
         setItemChecked(IDC_POLLCONSOLES, rfb::win32::WMPoller::poll_console_windows);
-        setItemChecked(IDC_COMPAREFB, rfb::Server::compareFB);
+        setItemChecked(IDC_CAPTUREBLT, osVersion.isPlatformNT &&
+                       rfb::win32::DeviceFrameBuffer::useCaptureBlt);
+        enableItem(IDC_CAPTUREBLT, osVersion.isPlatformNT);
+        onCommand(IDC_USEHOOKS, 0);
       }
       bool onCommand(int id, int cmd) {
         switch (id) {
+        case IDC_USEPOLLING:
         case IDC_USEHOOKS:
+        case IDC_USEDRIVER:
         case IDC_POLLCONSOLES:
-        case IDC_COMPAREFB:
-          setChanged((rfb::win32::SDisplay::use_hooks != isItemChecked(IDC_USEHOOKS)) ||
+        case IDC_CAPTUREBLT:
+          setChanged(((rfb::win32::SDisplay::updateMethod == 0) != isItemChecked(IDC_USEPOLLING)) ||
+            ((rfb::win32::SDisplay::updateMethod == 1) != isItemChecked(IDC_USEHOOKS)) ||
+            ((rfb::win32::SDisplay::updateMethod == 2) != isItemChecked(IDC_USEDRIVER)) ||
             (rfb::win32::WMPoller::poll_console_windows != isItemChecked(IDC_POLLCONSOLES)) ||
-            (rfb::Server::compareFB != isItemChecked(IDC_COMPAREFB)));
+            (rfb::win32::DeviceFrameBuffer::useCaptureBlt != isItemChecked(IDC_CAPTUREBLT)));
+          enableItem(IDC_POLLCONSOLES, isItemChecked(IDC_USEHOOKS));
           break;
         }
         return false;
       }
       bool onOk() {
-        regKey.setBool(_T("UseHooks"), isItemChecked(IDC_USEHOOKS));
+        if (isItemChecked(IDC_USEPOLLING))
+          regKey.setInt(_T("UpdateMethod"), 0);
+        if (isItemChecked(IDC_USEHOOKS))
+          regKey.setInt(_T("UpdateMethod"), 1);
+        if (isItemChecked(IDC_USEDRIVER))
+          regKey.setInt(_T("UpdateMethod"), 2);
         regKey.setBool(_T("PollConsoleWindows"), isItemChecked(IDC_POLLCONSOLES));
-        regKey.setBool(_T("CompareFB"), isItemChecked(IDC_COMPAREFB));
+        regKey.setBool(_T("UseCaptureBlt"), isItemChecked(IDC_CAPTUREBLT));
+
+        // *** LEGACY compatibility ***
+        regKey.setBool(_T("UseHooks"), isItemChecked(IDC_USEHOOKS));
         return true;
       }
     protected:
@@ -62,4 +85,4 @@
 
 };
 
-#endif
\ No newline at end of file
+#endif
diff --git a/vncconfig/Inputs.h b/vncconfig/Inputs.h
index 2735d24..1e0b4ba 100644
--- a/vncconfig/Inputs.h
+++ b/vncconfig/Inputs.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -25,10 +25,10 @@
 
 #include <rfb_win32/Registry.h>
 #include <rfb_win32/Dialog.h>
+#include <rfb_win32/OSVersion.h>
 #include <rfb/ServerCore.h>
 
 namespace rfb {
-
   namespace win32 {
 
     class InputsPage : public PropSheetPage {
@@ -42,6 +42,7 @@
         setItemChecked(IDC_ACCEPT_CUTTEXT, rfb::Server::acceptCutText);
         setItemChecked(IDC_SEND_CUTTEXT, rfb::Server::sendCutText);
         setItemChecked(IDC_DISABLE_LOCAL_INPUTS, SDisplay::disableLocalInputs);
+        enableItem(IDC_DISABLE_LOCAL_INPUTS, !osVersion.isPlatformWindows);
         BOOL blocked = FALSE;
         if (SystemParametersInfo(SPI_GETBLOCKSENDINPUTRESETS, 0, &blocked, 0))
           setItemChecked(IDC_AFFECT_SCREENSAVER, !blocked);
@@ -78,7 +79,6 @@
     };
 
   };
-
 };
 
-#endif
\ No newline at end of file
+#endif
diff --git a/vncconfig/Legacy.cxx b/vncconfig/Legacy.cxx
index 5926651..ae5d716 100644
--- a/vncconfig/Legacy.cxx
+++ b/vncconfig/Legacy.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -19,6 +19,7 @@
 #include <vncconfig/Legacy.h>
 
 #include <rfb/LogWriter.h>
+#include <rfb/Password.h>
 #include <rfb_win32/CurrentUser.h>
 
 using namespace rfb;
@@ -216,10 +217,9 @@
         }
         regKey.setInt(_T("QueryTimeout"), key.getInt(_T("QueryTimeout"), 10));
 
-        rfb::CharArray passwd;
-        int length;
-        key.getBinary(_T("Password"), (void**)&passwd.buf, &length, 0, 0);
-        regKey.setBinary(_T("Password"), passwd.buf, length);
+        ObfuscatedPasswd passwd;
+        key.getBinary(_T("Password"), (void**)&passwd.buf, &passwd.length, 0, 0);
+        regKey.setBinary(_T("Password"), passwd.buf, passwd.length);
 
         bool enableInputs = key.getBool(_T("InputsEnabled"), true);
         regKey.setBool(_T("AcceptKeyEvents"), enableInputs);
diff --git a/vncconfig/Legacy.h b/vncconfig/Legacy.h
index e66dc56..02059a6 100644
--- a/vncconfig/Legacy.h
+++ b/vncconfig/Legacy.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -19,15 +19,14 @@
 #ifndef WINVNCCONF_LEGACY
 #define WINVNCCONF_LEGACY
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <lmcons.h>
-
 #include <vncconfig/resource.h>
 #include <rfb_win32/Registry.h>
 #include <rfb_win32/Dialog.h>
-#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/MsgBox.h>
 #include <rfb/ServerCore.h>
+#include <rfb/secTypes.h>
 
 namespace rfb {
 
@@ -83,4 +82,4 @@
 
 };
 
-#endif
\ No newline at end of file
+#endif
diff --git a/vncconfig/PasswordDialog.cxx b/vncconfig/PasswordDialog.cxx
new file mode 100644
index 0000000..d26d86f
--- /dev/null
+++ b/vncconfig/PasswordDialog.cxx
@@ -0,0 +1,52 @@
+/* Copyright (C) 2004-2005 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 <vncconfig/resource.h>
+#include <vncconfig/PasswordDialog.h>
+#include <rfb_win32/MsgBox.h>
+#include <rfb/Password.h>
+
+using namespace rfb;
+using namespace win32;
+
+PasswordDialog::PasswordDialog(const RegKey& rk, bool registryInsecure_)
+  : Dialog(GetModuleHandle(0)), regKey(rk), registryInsecure(registryInsecure_) {
+}
+
+bool PasswordDialog::showDialog(HWND owner) {
+  return Dialog::showDialog(MAKEINTRESOURCE(IDD_AUTH_VNC_PASSWD), owner);
+}
+
+bool PasswordDialog::onOk() {
+  TPlainPasswd password1(getItemString(IDC_PASSWORD1));
+  TPlainPasswd password2(getItemString(IDC_PASSWORD2));
+  if (_tcscmp(password1.buf, password2.buf) != 0) {
+    MsgBox(0, _T("The supplied passwords do not match"),
+           MB_ICONEXCLAMATION | MB_OK);
+    return false;
+  }
+  if (registryInsecure &&
+      (MsgBox(0, _T("Please note that your password cannot be stored securely on this system.  ")
+                 _T("Are you sure you wish to continue?"),
+              MB_YESNO | MB_ICONWARNING) == IDNO))
+    return false;
+  PlainPasswd password(strDup(password1.buf));
+  ObfuscatedPasswd obfPwd(password);
+  regKey.setBinary(_T("Password"), obfPwd.buf, obfPwd.length);
+  return true;
+}
diff --git a/rfb/vncAuth.h b/vncconfig/PasswordDialog.h
similarity index 60%
copy from rfb/vncAuth.h
copy to vncconfig/PasswordDialog.h
index 18d87ad..dd23f8e 100644
--- a/rfb/vncAuth.h
+++ b/vncconfig/PasswordDialog.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2004-2005 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
@@ -15,17 +15,26 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
  * USA.
  */
-#ifndef __RFB_VNCAUTH_H__
-#define __RFB_VNCAUTH_H__
+#ifndef WINVNCCONF_PASSWORD_DIALOG
+#define WINVNCCONF_PASSWORD_DIALOG
 
-#include <rdr/types.h>
+#include <rfb_win32/Registry.h>
+#include <rfb_win32/Dialog.h>
 
 namespace rfb {
+  namespace win32 {
 
-  const int vncAuthChallengeSize = 16;
+    class PasswordDialog : public Dialog {
+    public:
+      PasswordDialog(const RegKey& rk, bool registryInsecure_);
+      bool showDialog(HWND owner=0);
+      bool onOk();
+    protected:
+      const RegKey& regKey;
+      bool registryInsecure;
+    };
 
-  void vncAuthEncryptChallenge(rdr::U8* challenge, const char* passwd);
-  void vncAuthObfuscatePasswd(char* passwd);
-  void vncAuthUnobfuscatePasswd(char* passwd);
-}
-#endif
+  };
+};
+
+#endif
\ No newline at end of file
diff --git a/vncconfig/Sharing.h b/vncconfig/Sharing.h
index b63b39a..872ae13 100644
--- a/vncconfig/Sharing.h
+++ b/vncconfig/Sharing.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -56,4 +56,4 @@
 
 };
 
-#endif
\ No newline at end of file
+#endif
diff --git a/vncconfig/resource.h b/vncconfig/resource.h
index 99e2cea..ca1fbf5 100644
--- a/vncconfig/resource.h
+++ b/vncconfig/resource.h
@@ -37,6 +37,7 @@
 #define IDC_HOST_UP                     1019
 #define IDC_BUTTON4                     1020
 #define IDC_HOST_DOWN                   1020
+#define IDC_AUTH_INPUTONLY_PASSWD       1020
 #define IDC_HOST_EDIT                   1021
 #define IDC_PASSWORD2                   1022
 #define IDC_LEGACY_IMPORT               1023
@@ -57,19 +58,34 @@
 #define IDC_COPYRIGHT                   1042
 #define IDC_HTTP_ENABLE                 1043
 #define IDC_HTTP_PORT                   1044
-#define A                               1045
 #define IDC_BL_THRESHOLD                1046
 #define IDC_BL_TIMEOUT                  1047
 #define IDC_AFFECT_SCREENSAVER          1048
 #define IDC_LOCALHOST                   1049
 #define IDC_DISABLE_LOCAL_INPUTS        1050
-#define IDC_QUERY_CONNECT               1051
+#define IDC_AUTH_NT                     1051
+#define IDC_AUTH_NT_CONF                1052
+#define IDC_AUTH_RA2_CONF               1053
+#define IDC_QUERY_CONNECT               1055
 #define IDC_DISCONNECT_NONE             1056
 #define IDC_DISCONNECT_LOCK             1057
 #define IDC_DISCONNECT_LOGOFF           1058
 #define IDC_REMOVE_WALLPAPER            1059
 #define IDC_REMOVE_PATTERN              1060
 #define IDC_DISABLE_EFFECTS             1061
+#define IDC_CAPTUREBLT                  1062
+#define IDC_ENCRYPTION                  1063
+#define IDC_QUERY                       1064
+#define IDC_USEPOLLING                  1066
+#define IDC_USEDRIVER                   1068
+#define IDC_QUERY_LOGGED_ON             1069
+#define IDC_AUTH_ADMIN_PASSWD           1076
+#define IDC_AUTH_VIEWONLY_PASSWD        1077
+#define IDC_AUTH_ADMIN_ENABLE           1078
+#define IDC_AUTH_VIEWONLY_ENABLE        1079
+#define IDC_AUTH_INPUTONLY_ENABLE       1080
+#define IDC_AUTH_VNC_EXT                1081
+#define IDC_RFB_ENABLE                  1082
 #define ID_OPTIONS                      40001
 #define ID_CLOSE                        40002
 #define ID_ABOUT                        40003
@@ -80,7 +96,7 @@
 #ifndef APSTUDIO_READONLY_SYMBOLS
 #define _APS_NEXT_RESOURCE_VALUE        117
 #define _APS_NEXT_COMMAND_VALUE         40004
-#define _APS_NEXT_CONTROL_VALUE         1062
+#define _APS_NEXT_CONTROL_VALUE         1083
 #define _APS_NEXT_SYMED_VALUE           101
 #endif
 #endif
diff --git a/vncconfig/vncconfig.cxx b/vncconfig/vncconfig.cxx
index b03f47a..6c9e1c5 100644
--- a/vncconfig/vncconfig.cxx
+++ b/vncconfig/vncconfig.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -16,7 +16,6 @@
  * USA.
  */
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <commctrl.h>
 #include <string.h>
@@ -50,8 +49,6 @@
 TStr rfb::win32::AppName("VNC Config");
 
 
-VncAuthPasswdConfigParameter vncAuthPasswd;
-
 #ifdef _DEBUG
 BoolParameter captureDialogs("CaptureDialogs", "", false);
 #endif
@@ -119,14 +116,14 @@
       bool warnOnChangePassword = false;
       try {
         AccessEntries access;
-        Sid adminSID = Sid::Administrators();
-        Sid systemSID = Sid::SYSTEM();
+        Sid::Administrators adminSID;
+        Sid::SYSTEM systemSID;
         access.addEntry(adminSID, KEY_ALL_ACCESS, GRANT_ACCESS);
         access.addEntry(systemSID, KEY_ALL_ACCESS, GRANT_ACCESS);
-        UserName user;
+        UserSID userSID;
         if (configKey == HKEY_CURRENT_USER)
-          access.addEntry(user.buf, KEY_ALL_ACCESS, GRANT_ACCESS);
-        AccessControlList acl = CreateACL(access);
+          access.addEntry(userSID, KEY_ALL_ACCESS, GRANT_ACCESS);
+        AccessControlList acl(CreateACL(access));
 
         // Set the DACL, and don't allow the key to inherit its parent's DACL
         rootKey.setDACL(acl, false);
@@ -145,9 +142,9 @@
         warnOnChangePassword = true;
       }
 
-      // Start the RegConfig reader, to load in existing settings
-      RegistryReader config;
-      config.setKey(configKey, _T("Software\\TightVNC\\WinVNC4"));
+      // Start a RegConfig thread, to load in existing settings
+      RegConfigThread config;
+      config.start(configKey, _T("Software\\TightVNC\\WinVNC4"));
 
       // Build the dialog
       std::list<PropSheetPage*> pages;
@@ -175,8 +172,6 @@
 #else
       sheet.showPropSheet(0, true, false);
 #endif
-
-      return 0;
     } catch (rdr::SystemException& e) {
       switch (e.err) {
       case ERROR_ACCESS_DENIED:
@@ -186,8 +181,11 @@
       };
       throw;
     }
+
   } catch (rdr::Exception& e) {
     MsgBox(NULL, TStr(e.str()), MB_ICONEXCLAMATION | MB_OK);
     return 1;
   }
+
+  return 0;
 }
diff --git a/vncconfig/vncconfig.dsp b/vncconfig/vncconfig.dsp
index 8181fe7..a02ecf9 100644
--- a/vncconfig/vncconfig.dsp
+++ b/vncconfig/vncconfig.dsp
@@ -44,7 +44,7 @@
 # PROP Ignore_Export_Lib 0

 # PROP Target_Dir ""

 # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c

-# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /D "NDEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /c

+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /FI"rdr/msvcwarning.h" /D "NDEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /c

 # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32

 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32

 # ADD BASE RSC /l 0x809 /d "NDEBUG"

@@ -70,7 +70,7 @@
 # PROP Ignore_Export_Lib 0

 # PROP Target_Dir ""

 # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c

-# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I ".." /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c

+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I ".." /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c

 # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32

 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32

 # ADD BASE RSC /l 0x809 /d "_DEBUG"

@@ -97,7 +97,7 @@
 # PROP Ignore_Export_Lib 0

 # PROP Target_Dir ""

 # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I ".." /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c

-# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I ".." /D "_WINDOWS" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c

+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I ".." /FI"rdr/msvcwarning.h" /D "_WINDOWS" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c

 # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32

 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32

 # ADD BASE RSC /l 0x809 /d "_DEBUG"

@@ -125,6 +125,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\PasswordDialog.cxx

+# End Source File

+# Begin Source File

+

 SOURCE=.\vncconfig.cxx

 # End Source File

 # End Group

@@ -157,6 +161,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\PasswordDialog.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\resource.h

 # End Source File

 # Begin Source File

diff --git a/vncconfig/vncconfig.rc b/vncconfig/vncconfig.rc
index b401892..bf2f969 100644
--- a/vncconfig/vncconfig.rc
+++ b/vncconfig/vncconfig.rc
@@ -61,19 +61,29 @@
 // Dialog
 //
 
-IDD_AUTHENTICATION DIALOG DISCARDABLE  0, 0, 225, 86
+IDD_AUTHENTICATION DIALOG DISCARDABLE  0, 0, 193, 135
 STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
 CAPTION "Authentication"
 FONT 8, "MS Sans Serif"
 BEGIN
-    CONTROL         "No Authentication or Encryption",IDC_AUTH_NONE,"Button",
-                    BS_AUTORADIOBUTTON | WS_GROUP,7,10,143,15
-    CONTROL         "VNC 3.3 Authentication, no Encryption",IDC_AUTH_VNC,
-                    "Button",BS_AUTORADIOBUTTON,7,30,143,15
-    PUSHBUTTON      "Set Password",IDC_AUTH_VNC_PASSWD,155,30,55,15
-    CONTROL         "Prompt local user to accept incoming connections",
+    CONTROL         "No Authentication",IDC_AUTH_NONE,"Button",
+                    BS_AUTORADIOBUTTON | WS_GROUP,7,10,113,15
+    CONTROL         "VNC Password Authentication",IDC_AUTH_VNC,"Button",
+                    BS_AUTORADIOBUTTON,7,30,113,15
+    PUSHBUTTON      "Configure",IDC_AUTH_VNC_PASSWD,125,30,61,15
+    CONTROL         "NT Logon Authentication",IDC_AUTH_NT,"Button",
+                    BS_AUTORADIOBUTTON,7,50,113,15
+    PUSHBUTTON      "Configure",IDC_AUTH_NT_CONF,125,50,61,15
+    LTEXT           "Encryption:",IDC_STATIC,7,70,42,15,SS_CENTERIMAGE
+    COMBOBOX        IDC_ENCRYPTION,49,70,71,50,CBS_DROPDOWN | WS_VSCROLL | 
+                    WS_TABSTOP
+    PUSHBUTTON      "Generate Keys",IDC_AUTH_RA2_CONF,125,70,61,15
+    CONTROL         "Prompt local user to accept connections",
                     IDC_QUERY_CONNECT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
-                    7,50,211,15
+                    7,95,181,15
+    CONTROL         "Only prompt when there is a user logged on",
+                    IDC_QUERY_LOGGED_ON,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,20,110,166,15
 END
 
 IDD_CONNECTIONS DIALOG DISCARDABLE  0, 0, 218, 198
@@ -81,11 +91,11 @@
 CAPTION "Connections"
 FONT 8, "MS Sans Serif"
 BEGIN
-    LTEXT           "Accept connections on port:",IDC_STATIC,7,10,138,15,
-                    SS_CENTERIMAGE
+    CONTROL         "Accept connections on port:",IDC_RFB_ENABLE,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,7,10,138,15
+    EDITTEXT        IDC_PORT,150,10,61,15,ES_AUTOHSCROLL | ES_NUMBER
     LTEXT           "Disconnect idle clients after (seconds):",IDC_STATIC,7,
                     25,138,15,SS_CENTERIMAGE
-    EDITTEXT        IDC_PORT,150,10,61,15,ES_AUTOHSCROLL | ES_NUMBER
     EDITTEXT        IDC_IDLE_TIMEOUT,150,25,61,15,ES_AUTOHSCROLL | ES_NUMBER
     CONTROL         "Serve Java viewer via HTTP on port:",IDC_HTTP_ENABLE,
                     "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,40,138,15
@@ -103,32 +113,35 @@
     PUSHBUTTON      "&Edit",IDC_HOST_EDIT,150,170,55,15
 END
 
-IDD_HOOKING DIALOG DISCARDABLE  0, 0, 198, 95
+IDD_HOOKING DIALOG DISCARDABLE  0, 0, 197, 101
 STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
-CAPTION "Hooks"
+CAPTION "Capture Method"
 FONT 8, "MS Sans Serif"
 BEGIN
-    CONTROL         "Use VNC hooks to track graphical updates",IDC_USEHOOKS,
-                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,10,184,15
+    CONTROL         "Poll for changes to the desktop",IDC_USEPOLLING,"Button",
+                    BS_AUTORADIOBUTTON | WS_GROUP,7,10,183,15
+    CONTROL         "Use VNC hooks to track changes",IDC_USEHOOKS,"Button",
+                    BS_AUTORADIOBUTTON,7,25,183,15
     CONTROL         "Poll console windows for updates",IDC_POLLCONSOLES,
-                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,25,184,15
-    CONTROL         "Filter out updates that have no effect (recommended)",
-                    IDC_COMPAREFB,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,40,
-                    184,15
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,25,40,165,15
+    CONTROL         "Use VNC Mirror driver to track changes",IDC_USEDRIVER,
+                    "Button",BS_AUTORADIOBUTTON,7,55,183,15
+    CONTROL         "Capture alpha-blended windows",IDC_CAPTUREBLT,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,7,70,183,15
 END
 
 IDD_AUTH_VNC_PASSWD DIALOG DISCARDABLE  0, 0, 212, 70
 STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION | 
     WS_SYSMENU
-CAPTION "WinVNC Password"
+CAPTION "VNC Server Password"
 FONT 8, "MS Sans Serif"
 BEGIN
-    EDITTEXT        IDC_PASSWORD1,75,10,130,15,ES_PASSWORD | ES_AUTOHSCROLL
-    EDITTEXT        IDC_PASSWORD2,75,30,130,14,ES_PASSWORD | ES_AUTOHSCROLL
     LTEXT           "New Password:",IDC_STATIC,7,10,63,15
+    EDITTEXT        IDC_PASSWORD1,75,10,130,15,ES_PASSWORD | ES_AUTOHSCROLL
+    LTEXT           "Confirm Password:",IDC_STATIC,7,30,63,14
+    EDITTEXT        IDC_PASSWORD2,75,30,130,14,ES_PASSWORD | ES_AUTOHSCROLL
     DEFPUSHBUTTON   "OK",IDOK,100,50,50,15
     PUSHBUTTON      "Cancel",IDCANCEL,155,50,50,15
-    LTEXT           "Confirm Password:",IDC_STATIC,7,30,63,14
 END
 
 IDD_LEGACY DIALOG DISCARDABLE  0, 0, 166, 92
@@ -141,18 +154,19 @@
                     BS_AUTOCHECKBOX | WS_TABSTOP,7,35,152,15
 END
 
-IDD_CONN_HOST DIALOG DISCARDABLE  0, 0, 225, 47
+IDD_CONN_HOST DIALOG DISCARDABLE  0, 0, 225, 57
 STYLE DS_SYSMODAL | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION
 CAPTION "Specify Host IP Address Pattern"
 FONT 8, "MS Sans Serif"
 BEGIN
-    DEFPUSHBUTTON   "OK",IDOK,168,7,50,14
-    PUSHBUTTON      "Cancel",IDCANCEL,168,24,50,14
+    EDITTEXT        IDC_HOST_PATTERN,65,5,100,15,ES_AUTOHSCROLL
     CONTROL         "&Allow",IDC_ALLOW,"Button",BS_AUTORADIOBUTTON | 
-                    WS_GROUP,7,7,53,16
-    CONTROL         "&Deny",IDC_DENY,"Button",BS_AUTORADIOBUTTON,7,23,53,15
-    EDITTEXT        IDC_HOST_PATTERN,65,7,100,16,ES_AUTOHSCROLL
-    LTEXT           "e.g. 192.168.0.0/255.255.0.0",IDC_STATIC,65,25,100,15
+                    WS_GROUP,7,5,53,15
+    CONTROL         "&Deny",IDC_DENY,"Button",BS_AUTORADIOBUTTON,7,20,53,15
+    CONTROL         "Query",IDC_QUERY,"Button",BS_AUTORADIOBUTTON,7,35,53,15
+    DEFPUSHBUTTON   "OK",IDOK,115,35,50,15
+    PUSHBUTTON      "Cancel",IDCANCEL,170,35,50,15
+    LTEXT           "e.g. 192.168.0.0/255.255.0.0",IDC_STATIC,65,20,100,15
 END
 
 IDD_SHARING DIALOG DISCARDABLE  0, 0, 186, 95
@@ -177,10 +191,10 @@
 CAPTION "Inputs"
 FONT 8, "MS Sans Serif"
 BEGIN
-    CONTROL         "Accept keyboard events from clients",IDC_ACCEPT_KEYS,
-                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,25,172,15
     CONTROL         "Accept pointer events from clients",IDC_ACCEPT_PTR,
                     "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,10,172,15
+    CONTROL         "Accept keyboard events from clients",IDC_ACCEPT_KEYS,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,25,172,15
     CONTROL         "Accept clipboard updates from clients",
                     IDC_ACCEPT_CUTTEXT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
                     7,40,172,15
@@ -216,13 +230,6 @@
 CAPTION "Desktop"
 FONT 8, "MS Sans Serif"
 BEGIN
-    CONTROL         "Do nothing",IDC_DISCONNECT_NONE,"Button",
-                    BS_AUTORADIOBUTTON,15,80,155,15
-    CONTROL         "Lock workstation",IDC_DISCONNECT_LOCK,"Button",
-                    BS_AUTORADIOBUTTON,15,95,155,15
-    CONTROL         "Logoff user",IDC_DISCONNECT_LOGOFF,"Button",
-                    BS_AUTORADIOBUTTON,15,110,155,15
-    GROUPBOX        "When last client disconnects",IDC_STATIC,7,70,171,60
     GROUPBOX        "While connected",IDC_STATIC,7,5,171,60
     CONTROL         "Remove wallpaper",IDC_REMOVE_WALLPAPER,"Button",
                     BS_AUTOCHECKBOX | WS_TABSTOP,15,15,155,15
@@ -230,6 +237,13 @@
                     BS_AUTOCHECKBOX | WS_TABSTOP,15,30,155,15
     CONTROL         "Disable user interface effects",IDC_DISABLE_EFFECTS,
                     "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,46,155,14
+    GROUPBOX        "When last client disconnects",IDC_STATIC,7,70,171,60
+    CONTROL         "Do nothing",IDC_DISCONNECT_NONE,"Button",
+                    BS_AUTORADIOBUTTON,15,80,155,15
+    CONTROL         "Lock workstation",IDC_DISCONNECT_LOCK,"Button",
+                    BS_AUTORADIOBUTTON,15,95,155,15
+    CONTROL         "Logoff user",IDC_DISCONNECT_LOGOFF,"Button",
+                    BS_AUTORADIOBUTTON,15,110,155,15
 END
 
 
@@ -244,18 +258,25 @@
     IDD_AUTHENTICATION, DIALOG
     BEGIN
         LEFTMARGIN, 7
-        RIGHTMARGIN, 218
-        VERTGUIDE, 150
-        VERTGUIDE, 155
-        VERTGUIDE, 210
+        RIGHTMARGIN, 186
+        VERTGUIDE, 20
+        VERTGUIDE, 49
+        VERTGUIDE, 120
+        VERTGUIDE, 125
         TOPMARGIN, 7
-        BOTTOMMARGIN, 79
+        BOTTOMMARGIN, 128
         HORZGUIDE, 10
         HORZGUIDE, 25
         HORZGUIDE, 30
         HORZGUIDE, 45
         HORZGUIDE, 50
         HORZGUIDE, 65
+        HORZGUIDE, 70
+        HORZGUIDE, 85
+        HORZGUIDE, 90
+        HORZGUIDE, 105
+        HORZGUIDE, 110
+        HORZGUIDE, 125
     END
 
     IDD_CONNECTIONS, DIALOG
@@ -290,13 +311,16 @@
     IDD_HOOKING, DIALOG
     BEGIN
         LEFTMARGIN, 7
-        RIGHTMARGIN, 191
+        RIGHTMARGIN, 190
+        VERTGUIDE, 25
         TOPMARGIN, 7
-        BOTTOMMARGIN, 88
+        BOTTOMMARGIN, 94
         HORZGUIDE, 10
         HORZGUIDE, 25
         HORZGUIDE, 40
         HORZGUIDE, 55
+        HORZGUIDE, 70
+        HORZGUIDE, 85
     END
 
     IDD_AUTH_VNC_PASSWD, DIALOG
@@ -305,6 +329,7 @@
         RIGHTMARGIN, 205
         VERTGUIDE, 70
         VERTGUIDE, 75
+        VERTGUIDE, 90
         VERTGUIDE, 100
         VERTGUIDE, 150
         VERTGUIDE, 155
@@ -334,12 +359,16 @@
     IDD_CONN_HOST, DIALOG
     BEGIN
         LEFTMARGIN, 7
-        RIGHTMARGIN, 218
+        RIGHTMARGIN, 220
         VERTGUIDE, 60
         VERTGUIDE, 65
+        VERTGUIDE, 115
         VERTGUIDE, 165
-        TOPMARGIN, 7
-        BOTTOMMARGIN, 40
+        VERTGUIDE, 170
+        TOPMARGIN, 5
+        BOTTOMMARGIN, 50
+        HORZGUIDE, 20
+        HORZGUIDE, 35
     END
 
     IDD_SHARING, DIALOG
@@ -390,21 +419,9 @@
     IDD_DESKTOP, DIALOG
     BEGIN
         LEFTMARGIN, 7
-        RIGHTMARGIN, 178
-        VERTGUIDE, 15
-        VERTGUIDE, 170
-        TOPMARGIN, 5
-        BOTTOMMARGIN, 130
-        HORZGUIDE, 15
-        HORZGUIDE, 30
-        HORZGUIDE, 45
-        HORZGUIDE, 60
-        HORZGUIDE, 65
-        HORZGUIDE, 70
-        HORZGUIDE, 80
-        HORZGUIDE, 95
-        HORZGUIDE, 110
-        HORZGUIDE, 125
+        RIGHTMARGIN, 182
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 32
     END
 END
 #endif    // APSTUDIO_INVOKED
@@ -417,8 +434,8 @@
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 4,0,0,26
- PRODUCTVERSION 4,0,0,26
+ FILEVERSION 4,1,1,0
+ PRODUCTVERSION 4,1,1,0
  FILEFLAGSMASK 0x3fL
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -435,15 +452,15 @@
         BEGIN
             VALUE "Comments", "\0"
             VALUE "CompanyName", "Constantin Kaplinsky\0"
-            VALUE "FileDescription", "VNC Server Configuration Applet for Win32\0"
-            VALUE "FileVersion", "4.0\0"
-            VALUE "InternalName", "VNC Config 4.0\0"
-            VALUE "LegalCopyright", "Copyright (C) 1998-2004 [many holders]\0"
+            VALUE "FileDescription", "TightVNC Server Configuration Applet for Win32\0"
+            VALUE "FileVersion", "4.1.1\0"
+            VALUE "InternalName", "vncconfig\0"
+            VALUE "LegalCopyright", "Copyright (C) 1998-2005 [many holders]\0"
             VALUE "LegalTrademarks", "TightVNC\0"
             VALUE "OriginalFilename", "vncconfig.exe\0"
             VALUE "PrivateBuild", "\0"
-            VALUE "ProductName", "TightVNC Config 4.0\0"
-            VALUE "ProductVersion", "4.0\0"
+            VALUE "ProductName", "TightVNC Configurator\0"
+            VALUE "ProductVersion", "4.1.1\0"
             VALUE "SpecialBuild", "\0"
         END
     END
diff --git a/vncconfig_unix/Makefile.in b/vncconfig_unix/Makefile.in
index 58277c3..4891fcd 100644
--- a/vncconfig_unix/Makefile.in
+++ b/vncconfig_unix/Makefile.in
@@ -1,7 +1,7 @@
 
-SRCS = vncExt.c vncconfig.cxx
+SRCS = vncExt.c vncconfig.cxx QueryConnectDialog.cxx
 
-OBJS = vncExt.o vncconfig.o
+OBJS = vncExt.o vncconfig.o QueryConnectDialog.o
 
 program = vncconfig
 
diff --git a/vncconfig_unix/QueryConnectDialog.cxx b/vncconfig_unix/QueryConnectDialog.cxx
new file mode 100644
index 0000000..c154051
--- /dev/null
+++ b/vncconfig_unix/QueryConnectDialog.cxx
@@ -0,0 +1,88 @@
+/* Copyright (C) 2002-2005 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/Exception.h>
+#include "QueryConnectDialog.h"
+#include "vncExt.h"
+
+QueryConnectDialog::QueryConnectDialog(Display* dpy,
+                                       const char* address_,
+                                       const char* user_,
+                                       int timeout_,
+                                       QueryResultCallback* cb)
+  : TXDialog(dpy, 300, 100, "VNC Server : Accept Connection?"),
+    addressLbl(dpy, "Host:",this),
+    address(dpy, address_, this),
+    userLbl(dpy, "User:", this),
+    user(dpy, user_, this),
+    timeoutLbl(dpy, "Seconds until automatic reject:", this),
+    timeout(dpy, "0000000000", this),
+    accept(dpy, "Accept", this, this, 60),
+    reject(dpy, "Reject", this, this, 60),
+    callback(cb), timeUntilReject(timeout_), timer(this)
+{
+  const int pad = 4;
+  int y=pad;
+  int lblWidth = __rfbmax(addressLbl.width(), userLbl.width());
+  userLbl.move(pad+lblWidth-userLbl.width(), y);
+  user.move(pad+lblWidth, y);
+  addressLbl.move(pad+lblWidth-addressLbl.width(), y+=userLbl.height());
+  address.move(pad+lblWidth, y);
+  timeoutLbl.move(pad, y+=addressLbl.height());
+  timeout.move(pad+timeoutLbl.width(), y);
+  accept.move(pad, y+=addressLbl.height());
+  int maxWidth = __rfbmax(user.width(), address.width()+pad+lblWidth);
+  maxWidth = __rfbmax(maxWidth, accept.width()*3);
+  maxWidth = __rfbmax(maxWidth, timeoutLbl.width()+timeout.width()+pad);
+  reject.move(maxWidth-reject.width(), y);
+  resize(maxWidth + pad, y+reject.height()+pad);
+  setBorderWidth(1);
+  refreshTimeout();
+  timer.start(1000);
+}
+
+void QueryConnectDialog::deleteWindow(TXWindow*) {
+  unmap();
+  callback->queryRejected();
+}
+
+void QueryConnectDialog::buttonActivate(TXButton* b) {
+  unmap();
+  if (b == &accept)
+    callback->queryApproved();
+  else if (b == &reject)
+    callback->queryRejected();
+}
+  
+bool QueryConnectDialog::handleTimeout(rfb::Timer* t) {
+  if (timeUntilReject-- == 0) {
+    unmap();
+    callback->queryTimedOut();
+    return false;
+  } else {
+    refreshTimeout();
+    return true;
+  }
+}
+
+void QueryConnectDialog::refreshTimeout() {
+  char buf[16];
+  sprintf(buf, "%d", timeUntilReject);
+  timeout.setText(buf);
+}
diff --git a/vncconfig_unix/QueryConnectDialog.h b/vncconfig_unix/QueryConnectDialog.h
new file mode 100644
index 0000000..f685dc3
--- /dev/null
+++ b/vncconfig_unix/QueryConnectDialog.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2005 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 __QUERYCONNECTDIALOG_H__
+#define __QUERYCONNECTDIALOG_H__
+
+#include <rfb/Timer.h>
+#include "TXLabel.h"
+#include "TXButton.h"
+#include "TXDialog.h"
+
+class QueryResultCallback {
+ public:
+  virtual ~QueryResultCallback() {}
+  virtual void queryApproved() = 0;
+  virtual void queryRejected() = 0;
+  virtual void queryTimedOut() { queryRejected(); };
+};
+
+class QueryConnectDialog : public TXDialog, public TXEventHandler,
+                           public TXButtonCallback,
+                           public rfb::Timer::Callback
+{
+ public:
+  QueryConnectDialog(Display* dpy, const char* address_,
+                     const char* user_, int timeout_,
+                     QueryResultCallback* cb);
+  void handleEvent(TXWindow*, XEvent* ) { }
+  void deleteWindow(TXWindow*);
+  void buttonActivate(TXButton* b);
+  bool handleTimeout(rfb::Timer* t);
+ private:
+  void refreshTimeout();
+  TXLabel addressLbl, address, userLbl, user, timeoutLbl, timeout;
+  TXButton accept, reject;
+  QueryResultCallback* callback;
+  int timeUntilReject;
+  rfb::Timer timer;
+};
+
+#endif
diff --git a/vncconfig_unix/buildtime.c b/vncconfig_unix/buildtime.c
index a96031c..3f4c369 100644
--- a/vncconfig_unix/buildtime.c
+++ b/vncconfig_unix/buildtime.c
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/vncconfig_unix/vncExt.c b/vncconfig_unix/vncExt.c
index ac1dab3..ff5532b 100644
--- a/vncconfig_unix/vncExt.c
+++ b/vncconfig_unix/vncExt.c
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -18,6 +18,7 @@
 #include <stdio.h>
 
 #define NEED_REPLIES
+#include <X11/Xlib.h>
 #include <X11/Xlibint.h>
 #define _VNCEXT_PROTO_
 #include "vncExt.h"
@@ -26,6 +27,8 @@
                                                   xEvent* w);
 static Bool XVncExtSelectionChangeNotifyWireToEvent(Display* dpy, XEvent* e,
                                                     xEvent* w);
+static Bool XVncExtQueryConnectNotifyWireToEvent(Display* dpy, XEvent* e,
+                                                 xEvent* w);
 
 static Bool extensionInited = False;
 static XExtCodes* codes = 0;
@@ -40,6 +43,8 @@
                      XVncExtClientCutTextNotifyWireToEvent);
     XESetWireToEvent(dpy, codes->first_event + VncExtSelectionChangeNotify,
                      XVncExtSelectionChangeNotifyWireToEvent);
+    XESetWireToEvent(dpy, codes->first_event + VncExtQueryConnectNotify,
+                     XVncExtQueryConnectNotifyWireToEvent);
   }
   return codes != 0;
 }
@@ -286,6 +291,55 @@
   return rep.success;
 }
 
+Bool XVncExtGetQueryConnect(Display* dpy, char** addr, char** user,
+                            int* timeout, void** opaqueId)
+{
+  xVncExtGetQueryConnectReq* req;
+  xVncExtGetQueryConnectReply rep;
+
+  if (!checkExtension(dpy)) return False;
+
+  LockDisplay(dpy);
+  GetReq(VncExtGetQueryConnect, req);
+  req->reqType = codes->major_opcode;
+  req->vncExtReqType = X_VncExtGetQueryConnect;
+  if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
+    UnlockDisplay(dpy);
+    SyncHandle();
+    return False;
+  }
+  UnlockDisplay(dpy);
+  SyncHandle();
+
+  *addr = Xmalloc(rep.addrLen+1);
+  _XReadPad(dpy, *addr, rep.addrLen);
+  (*addr)[rep.addrLen] = 0;
+  *user = Xmalloc(rep.userLen+1);
+  _XReadPad(dpy, *user, rep.userLen);
+  (*user)[rep.userLen] = 0;
+  *timeout = rep.timeout;
+  *opaqueId = (void*)rep.opaqueId;
+  return True;
+}
+
+Bool XVncExtApproveConnect(Display* dpy, void* opaqueId, int approve)
+{
+  xVncExtApproveConnectReq* req;
+
+  if (!checkExtension(dpy)) return False;
+
+  LockDisplay(dpy);
+  GetReq(VncExtApproveConnect, req);
+  req->reqType = codes->major_opcode;
+  req->vncExtReqType = X_VncExtApproveConnect;
+  req->approve = approve;
+  req->opaqueId = (CARD32)opaqueId;
+  UnlockDisplay(dpy);
+  SyncHandle();
+  return True;
+}
+
+
 static Bool XVncExtClientCutTextNotifyWireToEvent(Display* dpy, XEvent* e,
                                                   xEvent* w)
 {
@@ -314,3 +368,17 @@
   ev->selection = wire->selection;
   return True;
 }
+
+static Bool XVncExtQueryConnectNotifyWireToEvent(Display* dpy, XEvent* e,
+                                                    xEvent* w)
+{
+  XVncExtQueryConnectEvent* ev = (XVncExtQueryConnectEvent*)e;
+  xVncExtQueryConnectNotifyEvent* wire
+    = (xVncExtQueryConnectNotifyEvent*)w;
+  ev->type = wire->type & 0x7f;
+  ev->serial = _XSetLastRequestRead(dpy,(xGenericReply*)wire);
+  ev->send_event = (wire->type & 0x80) != 0;
+  ev->display = dpy;
+  ev->window = wire->window;
+  return True;
+}
diff --git a/vncconfig_unix/vncExt.h b/vncconfig_unix/vncExt.h
index de69f4e..f1502c4 100644
--- a/vncconfig_unix/vncExt.h
+++ b/vncconfig_unix/vncExt.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -30,13 +30,17 @@
 #define X_VncExtGetClientCutText 5
 #define X_VncExtSelectInput 6
 #define X_VncExtConnect 7
+#define X_VncExtGetQueryConnect 8
+#define X_VncExtApproveConnect 9
 
 #define VncExtClientCutTextNotify 0
 #define VncExtSelectionChangeNotify 1
+#define VncExtQueryConnectNotify 2
 #define VncExtClientCutTextMask (1 << VncExtClientCutTextNotify)
 #define VncExtSelectionChangeMask (1 << VncExtSelectionChangeNotify)
+#define VncExtQueryConnectMask (1 << VncExtQueryConnectNotify)
 
-#define VncExtNumberEvents 2
+#define VncExtNumberEvents 3
 #define VncExtNumberErrors 0
 
 #ifndef _VNCEXT_SERVER_
@@ -51,6 +55,10 @@
 Bool XVncExtGetClientCutText(Display* dpy, char** str, int* len);
 Bool XVncExtSelectInput(Display* dpy, Window w, int mask);
 Bool XVncExtConnect(Display* dpy, char* hostAndPort);
+Bool XVncExtGetQueryConnect(Display* dpy, char** addr,
+                            char** user, int* timeout, void** opaqueId);
+Bool XVncExtApproveConnect(Display* dpy, void* opaqueId, int approve);
+
 
 typedef struct {
   int type;
@@ -70,6 +78,14 @@
   Atom selection;
 } XVncExtSelectionChangeEvent;
 
+typedef struct {
+  int type;
+  unsigned long serial;
+  Bool send_event;
+  Display *display;
+  Window window;
+} XVncExtQueryConnectEvent;
+
 #endif
 
 #ifdef _VNCEXT_PROTO_
@@ -243,6 +259,40 @@
 
 
 typedef struct {
+  CARD8 reqType;       /* always VncExtReqCode */
+  CARD8 vncExtReqType; /* always VncExtGetQueryConnect */
+  CARD16 length B16;
+} xVncExtGetQueryConnectReq;
+#define sz_xVncExtGetQueryConnectReq 4
+
+typedef struct {
+ BYTE type; /* X_Reply */
+ BYTE pad0;
+ CARD16 sequenceNumber B16;
+ CARD32 length B32;
+ CARD32 addrLen B32;
+ CARD32 userLen B32;
+ CARD32 timeout B32;
+ CARD32 opaqueId B32;
+ CARD32 pad4 B32;
+ CARD32 pad5 B32;
+} xVncExtGetQueryConnectReply;
+#define sz_xVncExtGetQueryConnectReply 32
+
+typedef struct {
+  CARD8 reqType;       /* always VncExtReqCode */
+  CARD8 vncExtReqType; /* always VncExtApproveConnect */
+  CARD16 length B16;
+  CARD8 approve;
+  CARD8 pad0;
+  CARD16 pad1;
+  CARD32 opaqueId B32;
+} xVncExtApproveConnectReq;
+#define sz_xVncExtApproveConnectReq 12
+
+
+
+typedef struct {
   BYTE type;    /* always eventBase + VncExtClientCutTextNotify */
   BYTE pad0;
   CARD16 sequenceNumber B16;
@@ -270,6 +320,20 @@
 } xVncExtSelectionChangeNotifyEvent;
 #define sz_xVncExtSelectionChangeNotifyEvent 32
 
+typedef struct {
+  BYTE type;    /* always eventBase + VncExtQueryConnectNotify */
+  BYTE pad0;
+  CARD16 sequenceNumber B16;
+  CARD32 window B32;
+  CARD32 pad6 B32;
+  CARD32 pad1 B32;
+  CARD32 pad2 B32;
+  CARD32 pad3 B32;
+  CARD32 pad4 B32;
+  CARD32 pad5 B32;
+} xVncExtQueryConnectNotifyEvent;
+#define sz_xVncExtQueryConnectNotifyEvent 32
+
 #endif
 
 #ifdef __cplusplus
diff --git a/vncconfig_unix/vncconfig.cxx b/vncconfig_unix/vncconfig.cxx
index e707ffb..c901d19 100644
--- a/vncconfig_unix/vncconfig.cxx
+++ b/vncconfig_unix/vncconfig.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -38,8 +38,11 @@
 #include <rfb/Configuration.h>
 #include <rfb/Logger_stdio.h>
 #include <rfb/LogWriter.h>
+#include <rfb/Timer.h>
 #include "TXWindow.h"
 #include "TXCheckbox.h"
+#include "TXLabel.h"
+#include "QueryConnectDialog.h"
 
 using namespace rfb;
 
@@ -48,6 +51,16 @@
 StringParameter displayname("display", "The X display", "");
 BoolParameter noWindow("nowin", "Don't display a window", 0);
 BoolParameter iconic("iconic", "Start with window iconified", 0);
+BoolParameter sendPrimary("SendPrimary", "Send the PRIMARY as well as the "
+                          "CLIPBOARD selection", true);
+IntParameter pollTime("poll",
+                      "How often to poll for clipboard changes in ms", 0);
+
+inline const char* selectionName(Atom sel) {
+  if (sel == xaCLIPBOARD) return "CLIPBOARD";
+  if (sel == XA_PRIMARY) return "PRIMARY";
+  return "unknown";
+}
 
 #define ACCEPT_CUT_TEXT "AcceptCutText"
 #define SEND_CUT_TEXT "SendCutText"
@@ -67,14 +80,20 @@
 
 class VncConfigWindow : public TXWindow, public TXEventHandler,
                         public TXDeleteWindowCallback,
-                        public TXCheckboxCallback {
+                        public TXCheckboxCallback,
+                        public rfb::Timer::Callback,
+                        public QueryResultCallback {
 public:
   VncConfigWindow(Display* dpy)
-    : TXWindow(dpy, 300, 100), clientCutText(0), clientCutTextLen(0),
+    : TXWindow(dpy, 300, 100), cutText(0), cutTextLen(0),
       acceptClipboard(dpy, "Accept clipboard from viewers", this, false, this),
       sendClipboard(dpy, "Send clipboard to viewers", this, false, this),
-      sendPrimary(dpy, "Send primary selection to viewers", this, false, this)
+      sendPrimaryCB(dpy, "Send primary selection to viewers", this,false,this),
+      pollTimer(this),
+      queryConnectDialog(0)
   {
+    selection[0] = selection[1] = 0;
+    selectionLen[0] = selectionLen[1] = 0;
     int y = yPad;
     acceptClipboard.move(xPad, y);
     acceptClipboard.checked(getBoolParam(dpy, ACCEPT_CUT_TEXT));
@@ -82,14 +101,22 @@
     sendClipboard.move(xPad, y);
     sendClipboard.checked(getBoolParam(dpy, SEND_CUT_TEXT));
     y += sendClipboard.height();
-    sendPrimary.move(xPad, y);
-    sendPrimary.checked(true);
-    sendPrimary.disabled(!sendClipboard.checked());
-    y += sendPrimary.height();
+    sendPrimaryCB.move(xPad, y);
+    sendPrimaryCB.checked(sendPrimary);
+    sendPrimaryCB.disabled(!sendClipboard.checked());
+    y += sendPrimaryCB.height();
     setEventHandler(this);
     toplevel("VNC config", this, 0, 0, 0, iconic);
     XVncExtSelectInput(dpy, win(),
-                       VncExtClientCutTextMask|VncExtSelectionChangeMask);
+                       VncExtClientCutTextMask|
+                       VncExtSelectionChangeMask|
+                       VncExtQueryConnectMask);
+    XConvertSelection(dpy, XA_PRIMARY, XA_STRING,
+                      XA_PRIMARY, win(), CurrentTime);
+    XConvertSelection(dpy, xaCLIPBOARD, XA_STRING,
+                      xaCLIPBOARD, win(), CurrentTime);
+    if (pollTime != 0)
+      pollTimer.start(pollTime);
   }
 
   // handleEvent(). If we get a ClientCutTextNotify event from Xvnc, set the
@@ -101,28 +128,55 @@
     if (acceptClipboard.checked()) {
       if (ev->type == vncExtEventBase + VncExtClientCutTextNotify) {
         XVncExtClientCutTextEvent* cutEv = (XVncExtClientCutTextEvent*)ev;
-        if (clientCutText)
-          XFree(clientCutText);
-        clientCutText = 0;
-        if (XVncExtGetClientCutText(dpy, &clientCutText, &clientCutTextLen)) {
-          vlog.debug("Got client cut text");
-          XStoreBytes(dpy, clientCutText, clientCutTextLen);
+        if (cutText)
+          XFree(cutText);
+        cutText = 0;
+        if (XVncExtGetClientCutText(dpy, &cutText, &cutTextLen)) {
+          vlog.debug("Got client cut text: '%.*s%s'",
+                     cutTextLen<9?cutTextLen:8, cutText,
+                     cutTextLen<9?"":"...");
+          XStoreBytes(dpy, cutText, cutTextLen);
           ownSelection(XA_PRIMARY, cutEv->time);
           ownSelection(xaCLIPBOARD, cutEv->time);
+          delete [] selection[0];
+          delete [] selection[1];
+          selection[0] = selection[1] = 0;
+          selectionLen[0] = selectionLen[1] = 0;
         }
       }
     }
     if (sendClipboard.checked()) {
       if (ev->type == vncExtEventBase + VncExtSelectionChangeNotify) {
+        vlog.debug("selection change event");
         XVncExtSelectionChangeEvent* selEv = (XVncExtSelectionChangeEvent*)ev;
         if (selEv->selection == xaCLIPBOARD ||
-            (selEv->selection == XA_PRIMARY && sendPrimary.checked())) {
+            (selEv->selection == XA_PRIMARY && sendPrimaryCB.checked())) {
           if (!selectionOwner(selEv->selection))
             XConvertSelection(dpy, selEv->selection, XA_STRING,
                               selEv->selection, win(), CurrentTime);
         }
       }
     }
+    if (ev->type == vncExtEventBase + VncExtQueryConnectNotify) {
+       vlog.debug("query connection event");
+       if (queryConnectDialog)
+         delete queryConnectDialog;
+       queryConnectDialog = 0;
+       char* qcAddress;
+       char* qcUser;
+       int qcTimeout;
+       if (XVncExtGetQueryConnect(dpy, &qcAddress, &qcUser,
+                                  &qcTimeout, &queryConnectId)) {
+         if (qcTimeout)
+           queryConnectDialog = new QueryConnectDialog(dpy, qcAddress,
+                                                       qcUser, qcTimeout,
+                                                       this);
+         if (queryConnectDialog)
+           queryConnectDialog->map();
+         XFree(qcAddress);
+         XFree(qcUser);
+       }
+    }
   }
   
 
@@ -131,11 +185,11 @@
   // into the requested property.  TXWindow will handle the rest.
   bool selectionRequest(Window requestor, Atom selection, Atom property)
   {
-    if (clientCutText)
+    if (cutText)
       XChangeProperty(dpy, requestor, property, XA_STRING, 8,
-                      PropModeReplace, (unsigned char*)clientCutText,
-                      clientCutTextLen);
-    return clientCutText;
+                      PropModeReplace, (unsigned char*)cutText,
+                      cutTextLen);
+    return cutText;
   }
 
   // selectionNotify() is called when we have requested the selection from the
@@ -147,8 +201,26 @@
       return;
 
     if (data && format == 8) {
-      vlog.debug("setting selection as server cut text");
-      XVncExtSetServerCutText(dpy, (char*)data, nitems);
+      int i = (ev->selection == XA_PRIMARY ? 0 : 1);
+      if (selectionLen[i] == nitems && memcmp(selection[i], data, nitems) == 0)
+        return;
+      delete [] selection[i];
+      selection[i] = new char[nitems];
+      memcpy(selection[i], data, nitems);
+      selectionLen[i] = nitems;
+      if (cutTextLen == nitems && memcmp(cutText, data, nitems) == 0) {
+        vlog.debug("ignoring duplicate cut text");
+        return;
+      }
+      if (cutText)
+        XFree(cutText);
+      cutText = (char*)malloc(nitems); // assuming XFree() same as free()
+      memcpy(cutText, data, nitems);
+      cutTextLen = nitems;
+      vlog.debug("sending %s selection as server cut text: '%.*s%s'",
+                 selectionName(ev->selection),cutTextLen<9?cutTextLen:8,
+                 cutText, cutTextLen<9?"":"...");
+      XVncExtSetServerCutText(dpy, cutText, cutTextLen);
     }
   }
 
@@ -165,28 +237,61 @@
     } else if (checkbox == &sendClipboard) {
       XVncExtSetParam(dpy, (sendClipboard.checked()
                             ? SEND_CUT_TEXT "=1" : SEND_CUT_TEXT "=0"));
-      sendPrimary.disabled(!sendClipboard.checked());
+      sendPrimaryCB.disabled(!sendClipboard.checked());
     }
   }
 
+  // rfb::Timer::Callback interface
+  virtual bool handleTimeout(rfb::Timer* timer) {
+    if (sendPrimaryCB.checked() && !selectionOwner(XA_PRIMARY))
+      XConvertSelection(dpy, XA_PRIMARY, XA_STRING,
+                        XA_PRIMARY, win(), CurrentTime);
+    if (!selectionOwner(xaCLIPBOARD))
+      XConvertSelection(dpy, xaCLIPBOARD, XA_STRING,
+                        xaCLIPBOARD, win(), CurrentTime);
+    return true;
+  }
+
+  // QueryResultCallback interface
+  virtual void queryApproved() {
+    XVncExtApproveConnect(dpy, queryConnectId, 1);
+  }
+  virtual void queryRejected() {
+    XVncExtApproveConnect(dpy, queryConnectId, 0);
+  }
+
 private:
-  char* clientCutText;
-  int clientCutTextLen;
-  TXCheckbox acceptClipboard, sendClipboard, sendPrimary;
+  char* cutText;
+  int cutTextLen;
+  char* selection[2];
+  int selectionLen[2];
+  TXCheckbox acceptClipboard, sendClipboard, sendPrimaryCB;
+  rfb::Timer pollTimer;
+
+  QueryConnectDialog* queryConnectDialog;
+  void* queryConnectId;
 };
 
 static void usage()
 {
-  fprintf(stderr,"usage: %s [-display <display>] [-nowin] [-iconic]\n",
+  fprintf(stderr,"usage: %s [parameters]\n",
           programName);
-  fprintf(stderr,"       %s [-display <display>] -connect <host>[:<port>]\n",
+  fprintf(stderr,"       %s [parameters] -connect <host>[:<port>]\n",
           programName);
-  fprintf(stderr,"       %s [-display <display>] -disconnect\n", programName);
-  fprintf(stderr,"       %s [-display <display>] [-set] <param>=<value> ...\n",
+  fprintf(stderr,"       %s [parameters] -disconnect\n", programName);
+  fprintf(stderr,"       %s [parameters] [-set] <Xvnc-param>=<value> ...\n",
           programName);
-  fprintf(stderr,"       %s [-display <display>] -list\n", programName);
-  fprintf(stderr,"       %s [-display <display>] -get <param>\n", programName);
-  fprintf(stderr,"       %s [-display <display>] -desc <param>\n",programName);
+  fprintf(stderr,"       %s [parameters] -list\n", programName);
+  fprintf(stderr,"       %s [parameters] -get <param>\n", programName);
+  fprintf(stderr,"       %s [parameters] -desc <param>\n",programName);
+  fprintf(stderr,"\n"
+          "Parameters can be turned on with -<param> or off with -<param>=0\n"
+          "Parameters which take a value can be specified as "
+          "-<param> <value>\n"
+          "Other valid forms are <param>=<value> -<param>=<value> "
+          "--<param>=<value>\n"
+          "Parameter names are case-insensitive.  The parameters are:\n\n");
+  Configuration::listParams(79, 14);
   exit(1);
 }
 
@@ -295,11 +400,31 @@
     if (!noWindow) w.map();
 
     while (true) {
+      struct timeval tv;
+      struct timeval* tvp = 0;
+
+      // Process any incoming X events
       TXWindow::handleXEvents(dpy);
+      
+      // Process expired timers and get the time until the next one
+      int timeoutMs = Timer::checkTimeouts();
+      if (timeoutMs) {
+        tv.tv_sec = timeoutMs / 1000;
+        tv.tv_usec = (timeoutMs % 1000) * 1000;
+        tvp = &tv;
+      }
+      
+      // If there are X requests pending then poll, don't wait!
+      if (XPending(dpy)) {
+        tv.tv_usec = tv.tv_sec = 0;
+        tvp = &tv;
+      }
+      
+      // Wait for X events, VNC traffic, or the next timer expiry
       fd_set rfds;
       FD_ZERO(&rfds);
       FD_SET(ConnectionNumber(dpy), &rfds);
-      int n = select(FD_SETSIZE, &rfds, 0, 0, 0);
+      int n = select(FD_SETSIZE, &rfds, 0, 0, tvp);
       if (n < 0) throw rdr::SystemException("select",errno);
     }
 
diff --git a/vncconfig_unix/vncconfig.man b/vncconfig_unix/vncconfig.man
index 33f7d49..e24753d 100644
--- a/vncconfig_unix/vncconfig.man
+++ b/vncconfig_unix/vncconfig.man
@@ -1,34 +1,35 @@
-.TH vncconfig 1 "30 December 2004" "TightVNC" "Virtual Network Computing"
+.TH vncconfig 1 "17 Apr 2006" "TightVNC" "Virtual Network Computing"
 .SH NAME
 vncconfig \- configure and control a VNC server
 .SH SYNOPSIS
 .B vncconfig
-[\fB\-display\fP \fIXdisplay\fP] [\fB\-nowin\fP] [\fB\-iconic\fP]
+.RI [ parameters ] 
 .br
 .B vncconfig
-[\fB\-display\fP \fIXdisplay\fP]
+.RI [ parameters ] 
 .B \-connect
 .IR host [: port ]
 .br
 .B vncconfig
-[\fB\-display\fP \fIXdisplay\fP]
+.RI [ parameters ] 
 .B \-disconnect
 .br
 .B vncconfig
-[\fB\-display\fP \fIXdisplay\fP]
-.IR param = value " ..."
+.RI [ parameters ] 
+.RB [ -set ] 
+.IR Xvnc-param = value " ..."
 .br
 .B vncconfig
-[\fB\-display\fP \fIXdisplay\fP]
+.RI [ parameters ] 
 .B \-list
 .br
 .B vncconfig
-[\fB\-display\fP \fIXdisplay\fP]
-\fB\-get\fP \fIparam\fP
+.RI [ parameters ] 
+\fB\-get\fP \fIXvnc-param\fP
 .br
 .B vncconfig
-[\fB\-display\fP \fIXdisplay\fP]
-\fB\-desc\fP \fIparam\fP
+.RI [ parameters ] 
+\fB\-desc\fP \fIXvnc-param\fP
 .SH DESCRIPTION
 .B vncconfig
 is used to configure and control a running instance of Xvnc, or any other X
@@ -57,18 +58,6 @@
 
 .SH OPTIONS
 .TP
-.B \-display \fIXdisplay\fP
-Specifies the Xvnc server to control.
-
-.TP
-.B \-nowin
-When run as a "helper" app, don't put up a window.
-
-.TP
-.B \-iconic
-When run as a "helper" app, make the window iconified at startup.
-
-.TP
 .B \-connect \fIhost\fP[:\fIport\fP]
 Tells an Xvnc server to make a "reverse" connection to a listening VNC viewer
 (normally connections are made the other way round - the viewer connects to the
@@ -82,7 +71,7 @@
 displayed anywhere.
 
 .TP
-.IR param = value
+[\fB-set\fP] \fIXvnc-param\fP=\fIvalue\fP
 Sets an Xvnc parameter to the given value.  Note that some of Xvnc's parameters
 are read only once at startup so that changing them in this way may not have
 any effect.
@@ -92,13 +81,36 @@
 Lists all the parameters supported by Xvnc.
 
 .TP
-.B \-get \fIparam\fP
+.B \-get \fIXvnc-param\fP
 Prints the current value of the given Xvnc parameter.
 
 .TP
-.B \-desc \fIparam\fP
+.B \-desc \fIXvnc-param\fP
 Prints a short description of the given Xvnc parameter.
 
+.SH PARAMETERS
+.B vncconfig
+also has parameters of its own which can be set on the command line.  These
+should not be confused with Xvnc's parameters which are manipulated with the
+\fB-set\fP, \fB-get\fP, \fB-list\fP and \fB-desc\fP options.
+
+Parameters can be turned on with -\fIparam\fP or off with -\fIparam\fP=0.
+Parameters which take a value can be specified as -\fIparam\fP \fIvalue\fP.
+Other valid forms are \fIparam\fP\fB=\fP\fIvalue\fP -\fIparam\fP=\fIvalue\fP
+--\fIparam\fP=\fIvalue\fP.  Parameter names are case-insensitive.
+
+.TP
+.B \-display \fIXdisplay\fP
+Specifies the Xvnc server to control.
+
+.TP
+.B \-nowin
+When run as a "helper" app, don't put up a window.
+
+.TP
+.B \-iconic
+When run as a "helper" app, make the window iconified at startup.
+
 .SH SEE ALSO
 .BR vncpasswd (1),
 .BR vncviewer (1),
diff --git a/vncinstall b/vncinstall
index e5ab017..ad5dd83 100755
--- a/vncinstall
+++ b/vncinstall
@@ -1,6 +1,6 @@
 #!/bin/sh
 #
-#  Copyright (C) 2002-2003 RealVNC Ltd.
+#  Copyright (C) 2002-2005 RealVNC Ltd.
 #
 #  This is free software; you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
@@ -58,7 +58,6 @@
 
 for f in xc/programs/Xserver/Xvnc vncviewer/vncviewer vncpasswd/vncpasswd \
 	 vncconfig/vncconfig vncserver x0vncserver/x0vncserver; do
-
   if [ ! -f $f ]; then
     echo "Couldn't find $f"
   else
diff --git a/vncpasswd/Makefile.in b/vncpasswd/Makefile.in
index fb625e0..927a7b9 100644
--- a/vncpasswd/Makefile.in
+++ b/vncpasswd/Makefile.in
@@ -5,7 +5,7 @@
 
 program = vncpasswd
 
-DEP_LIBS = ../rfb/librfb.a # ../network/libnetwork.a ../rdr/librdr.a
+DEP_LIBS = ../rfb/librfb.a
 
 DIR_CPPFLAGS = -I$(top_srcdir)
 
diff --git a/vncpasswd/vncpasswd.cxx b/vncpasswd/vncpasswd.cxx
index c8dd777..e889879 100644
--- a/vncpasswd/vncpasswd.cxx
+++ b/vncpasswd/vncpasswd.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -21,9 +21,12 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
-#include <rfb/vncAuth.h>
+#include <rfb/Password.h>
 #include <rfb/util.h>
 
+#include <termios.h>
+
+
 using namespace rfb;
 
 char* prog;
@@ -34,6 +37,33 @@
   exit(1);
 }
 
+
+static void enableEcho(bool enable) {
+  termios attrs;
+  tcgetattr(fileno(stdin), &attrs);
+  if (enable)
+    attrs.c_lflag |= ECHO;
+  else
+    attrs.c_lflag &= ~ECHO;
+  attrs.c_lflag |= ECHONL;
+  tcsetattr(fileno(stdin), TCSAFLUSH, &attrs);
+}
+
+static char* getpassword(const char* prompt) {
+  PlainPasswd buf(256);
+  fputs(prompt, stdout);
+  enableEcho(false);
+  char* result = fgets(buf.buf, 256, stdin);
+  enableEcho(true);
+  if (result) {
+    if (result[strlen(result)-1] == '\n')
+      result[strlen(result)-1] = 0;
+    return buf.takeBuf();
+  }
+  return 0;
+}
+
+
 int main(int argc, char** argv)
 {
   prog = argv[0];
@@ -63,13 +93,13 @@
   }
 
   while (true) {
-    char* passwd = getpass("Password: ");
-    if (!passwd) {
-      perror("getpass error");
+    PlainPasswd passwd(getpassword("Password:"));
+    if (!passwd.buf) {
+      perror("getpassword error");
       exit(1);
     }   
-    if (strlen(passwd) < 6) {
-      if (strlen(passwd) == 0) {
+    if (strlen(passwd.buf) < 6) {
+      if (strlen(passwd.buf) == 0) {
         fprintf(stderr,"Password not changed\n");
         exit(1);
       }
@@ -77,20 +107,12 @@
       continue;
     }
 
-    if (strlen(passwd) > 8)
-      passwd[8] = '\0';
-
-    CharArray passwdCopy(strDup(passwd));
-
-    passwd = getpass("Verify: ");
-    if (!passwd) {
+    PlainPasswd passwd2(getpassword("Verify:"));
+    if (!passwd2.buf) {
       perror("getpass error");
       exit(1);
     }   
-    if (strlen(passwd) > 8)
-      passwd[8] = '\0';
-
-    if (strcmp(passwdCopy.buf, passwd) != 0) {
+    if (strcmp(passwd.buf, passwd2.buf) != 0) {
       fprintf(stderr,"Passwords don't match - try again\n");
       continue;
     }
@@ -102,18 +124,15 @@
     }
     chmod(fname, S_IRUSR|S_IWUSR);
 
-    vncAuthObfuscatePasswd(passwd);
+    ObfuscatedPasswd obfuscated(passwd);
 
-    if (fwrite(passwd, 8, 1, fp) != 1) {
+    if (fwrite(obfuscated.buf, obfuscated.length, 1, fp) != 1) {
       fprintf(stderr,"Writing to %s failed\n",fname);
       exit(1);
     }
 
     fclose(fp);
 
-    for (unsigned int i = 0; i < strlen(passwd); i++)
-      passwd[i] = passwdCopy.buf[i] = 0;
-
     return 0;
   }
 }
diff --git a/vncpasswd/vncpasswd.man b/vncpasswd/vncpasswd.man
index 86ea6cb..8fd6874 100644
--- a/vncpasswd/vncpasswd.man
+++ b/vncpasswd/vncpasswd.man
@@ -1,4 +1,4 @@
-.TH vncpasswd 1 "30 December 2004" "TightVNC" "Virtual Network Computing"
+.TH vncpasswd 1 "05 May 2006" "TightVNC" "Virtual Network Computing"
 .SH NAME
 vncpasswd \- change a VNC password
 .SH SYNOPSIS
diff --git a/vncserver b/vncserver
index 19333cb..16fe54a 100755
--- a/vncserver
+++ b/vncserver
@@ -1,6 +1,6 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 #
-#  Copyright (C) 2002-2003 RealVNC Ltd.
+#  Copyright (C) 2002-2005 RealVNC Ltd.
 #  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
 #
 #  This is free software; you can redistribute it and/or modify
diff --git a/vncserver.man b/vncserver.man
index 12f0656..9eb88ec 100644
--- a/vncserver.man
+++ b/vncserver.man
@@ -1,4 +1,4 @@
-.TH vncserver 1 "18 May 2004" "TightVNC" "Virtual Network Computing"
+.TH vncserver 1 "17 Apr 2006" "TightVNC" "Virtual Network Computing"
 .SH NAME
 vncserver \- start or stop a VNC server
 .SH SYNOPSIS
diff --git a/vncviewer/CConn.cxx b/vncviewer/CConn.cxx
new file mode 100644
index 0000000..d141e50
--- /dev/null
+++ b/vncviewer/CConn.cxx
@@ -0,0 +1,710 @@
+/* Copyright (C) 2002-2005 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 <windows.h>
+#include <winsock2.h>
+#include <vncviewer/UserPasswdDialog.h>
+#include <vncviewer/CConn.h>
+#include <vncviewer/CConnThread.h>
+#include <vncviewer/resource.h>
+#include <rfb/encodings.h>
+#include <rfb/secTypes.h>
+#include <rfb/CSecurityNone.h>
+#include <rfb/CSecurityVncAuth.h>
+#include <rfb/CMsgWriter.h>
+#include <rfb/Configuration.h>
+#include <rfb/LogWriter.h>
+#include <rfb_win32/AboutDialog.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+using namespace rdr;
+
+// - Statics & consts
+
+static LogWriter vlog("CConn");
+
+
+const int IDM_FULLSCREEN = ID_FULLSCREEN;
+const int IDM_SEND_MENU_KEY = ID_SEND_MENU_KEY;
+const int IDM_SEND_CAD = ID_SEND_CAD;
+const int IDM_SEND_CTLESC = ID_SEND_CTLESC;
+const int IDM_ABOUT = ID_ABOUT;
+const int IDM_OPTIONS = ID_OPTIONS;
+const int IDM_INFO = ID_INFO;
+const int IDM_NEWCONN = ID_NEW_CONNECTION;
+const int IDM_REQUEST_REFRESH = ID_REQUEST_REFRESH;
+const int IDM_CTRL_KEY = ID_CTRL_KEY;
+const int IDM_ALT_KEY = ID_ALT_KEY;
+const int IDM_FILE_TRANSFER = ID_FILE_TRANSFER;
+const int IDM_CONN_SAVE_AS = ID_CONN_SAVE_AS;
+
+
+static IntParameter debugDelay("DebugDelay","Milliseconds to display inverted "
+                               "pixel data - a debugging feature", 0);
+
+
+//
+// -=- CConn implementation
+//
+
+RegKey            CConn::userConfigKey;
+
+
+CConn::CConn() 
+  : window(0), sock(0), sockEvent(CreateEvent(0, TRUE, FALSE, 0)), requestUpdate(false),
+    sameMachine(false), encodingChange(false), formatChange(false),
+    reverseConnection(false), lastUsedEncoding_(encodingRaw), isClosed_(false) {
+}
+
+CConn::~CConn() {
+  delete window;
+}
+
+bool CConn::initialise(network::Socket* s, bool reverse) {
+  // Set the server's name for MRU purposes
+  CharArray endpoint(s->getPeerEndpoint());
+  setServerName(endpoint.buf);
+  if (!options.host.buf)
+    options.setHost(endpoint.buf);
+
+  // Initialise the underlying CConnection
+  setStreams(&s->inStream(), &s->outStream());
+
+  // Enable processing of window messages while blocked on I/O
+  s->inStream().setBlockCallback(this);
+
+  // Initialise the viewer options
+  applyOptions(options);
+
+  // - Set which auth schemes we support, in order of preference
+  addSecType(secTypeVncAuth);
+  addSecType(secTypeNone);
+
+  // Start the RFB protocol
+  sock = s;
+  reverseConnection = reverse;
+  initialiseProtocol();
+
+  m_fileTransfer.initialize(&s->inStream(), &s->outStream());
+
+  return true;
+}
+
+
+void
+CConn::applyOptions(CConnOptions& opt) {
+  // - If any encoding-related settings have changed then we must
+  //   notify the server of the new settings
+  encodingChange |= ((options.useLocalCursor != opt.useLocalCursor) ||
+                     (options.useDesktopResize != opt.useDesktopResize) ||
+                     (options.customCompressLevel != opt.customCompressLevel) ||
+                     (options.compressLevel != opt.compressLevel) ||
+                     (options.noJpeg != opt.noJpeg) ||
+                     (options.qualityLevel != opt.qualityLevel) ||
+                     (options.preferredEncoding != opt.preferredEncoding));
+
+  // - If the preferred pixel format has changed then notify the server
+  formatChange |= (options.fullColour != opt.fullColour);
+  if (!opt.fullColour)
+    formatChange |= (options.lowColourLevel != opt.lowColourLevel);
+
+  // - Save the new set of options
+  options = opt;
+
+  // - Set optional features in ConnParams
+  cp.supportsLocalCursor = options.useLocalCursor;
+  cp.supportsDesktopResize = options.useDesktopResize;
+  cp.customCompressLevel = options.customCompressLevel;
+  cp.compressLevel = options.compressLevel;
+  cp.noJpeg = options.noJpeg;
+  cp.qualityLevel = options.qualityLevel;
+
+  // - Configure connection sharing on/off
+  setShared(options.shared);
+
+  // - Whether to use protocol 3.3 for legacy compatibility
+  setProtocol3_3(options.protocol3_3);
+
+  // - Apply settings that affect the window, if it is visible
+  if (window) {
+    window->setMonitor(options.monitor.buf);
+    window->setFullscreen(options.fullScreen);
+    window->setEmulate3(options.emulate3);
+    window->setPointerEventInterval(options.pointerEventInterval);
+    window->setMenuKey(options.menuKey);
+    window->setDisableWinKeys(options.disableWinKeys);
+    window->setShowToolbar(options.showToolbar);
+    if (!options.useLocalCursor)
+      window->setCursor(0, 0, Point(), 0, 0);
+  }
+}
+
+
+void
+CConn::displayChanged() {
+  // Display format has changed - recalculate the full-colour pixel format
+  calculateFullColourPF();
+}
+
+void
+CConn::paintCompleted() {
+  // A repaint message has just completed - request next update if necessary
+  requestNewUpdate();
+}
+
+bool
+CConn::sysCommand(WPARAM wParam, LPARAM lParam) {
+  // - If it's one of our (F8 Menu) messages
+  switch (wParam) {
+  case IDM_FULLSCREEN:
+    options.fullScreen = !window->isFullscreen();
+    window->setFullscreen(options.fullScreen);
+    return true;
+  case IDM_SHOW_TOOLBAR:
+    options.showToolbar = !window->isToolbarEnabled();
+    window->setShowToolbar(options.showToolbar);
+    return true;
+  case IDM_CTRL_KEY:
+    window->kbd.keyEvent(this, VK_CONTROL, 0, !window->kbd.keyPressed(VK_CONTROL));
+    return true;
+  case IDM_ALT_KEY:
+    window->kbd.keyEvent(this, VK_MENU, 0, !window->kbd.keyPressed(VK_MENU));
+    return true;
+  case IDM_SEND_MENU_KEY:
+    window->kbd.keyEvent(this, options.menuKey, 0, true);
+    window->kbd.keyEvent(this, options.menuKey, 0, false);
+    return true;
+  case IDM_SEND_CAD:
+    window->kbd.keyEvent(this, VK_CONTROL, 0, true);
+    window->kbd.keyEvent(this, VK_MENU, 0, true);
+    window->kbd.keyEvent(this, VK_DELETE, 0x1000000, true);
+    window->kbd.keyEvent(this, VK_DELETE, 0x1000000, false);
+    window->kbd.keyEvent(this, VK_MENU, 0, false);
+    window->kbd.keyEvent(this, VK_CONTROL, 0, false);
+    return true;
+  case IDM_SEND_CTLESC:
+    window->kbd.keyEvent(this, VK_CONTROL, 0, true);
+    window->kbd.keyEvent(this, VK_ESCAPE, 0, true);
+    window->kbd.keyEvent(this, VK_ESCAPE, 0, false);
+    window->kbd.keyEvent(this, VK_CONTROL, 0, false);
+    return true;
+  case IDM_REQUEST_REFRESH:
+    try {
+      writer()->writeFramebufferUpdateRequest(Rect(0,0,cp.width,cp.height), false);
+      requestUpdate = false;
+    } catch (rdr::Exception& e) {
+      close(e.str());
+    }
+    return true;
+  case IDM_NEWCONN:
+    {
+      Thread* newThread = new CConnThread;
+    }
+    return true;
+  case IDM_OPTIONS:
+    // Update the monitor device name in the CConnOptions instance
+    options.monitor.replaceBuf(window->getMonitor());
+    showOptionsDialog();
+    return true;
+  case IDM_INFO:
+    infoDialog.showDialog(this);
+    return true;
+  case IDM_ABOUT:
+    AboutDialog::instance.showDialog();
+    return true;
+  case IDM_FILE_TRANSFER:
+    m_fileTransfer.show(window->getHandle());
+    return true;
+  case IDM_CONN_SAVE_AS:
+    return true;
+  case ID_CLOSE:
+    // FIXME: Remove the corresponding toolbar button.
+    return true;
+  };
+  return false;
+}
+
+
+void
+CConn::closeWindow() {
+  vlog.info("window closed");
+  close();
+}
+
+
+void
+CConn::refreshMenu(bool enableSysItems) {
+  HMENU menu = GetSystemMenu(window->getHandle(), FALSE);
+
+  if (!enableSysItems) {
+    // Gray out menu items that might cause a World Of Pain
+    EnableMenuItem(menu, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
+    EnableMenuItem(menu, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
+    EnableMenuItem(menu, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED);
+    EnableMenuItem(menu, SC_MINIMIZE, MF_BYCOMMAND | MF_ENABLED);
+    EnableMenuItem(menu, SC_MAXIMIZE, MF_BYCOMMAND | MF_ENABLED);
+  }
+
+  // Update the modifier key menu items
+  UINT ctrlCheckFlags = window->kbd.keyPressed(VK_CONTROL) ? MF_CHECKED : MF_UNCHECKED;
+  UINT altCheckFlags = window->kbd.keyPressed(VK_MENU) ? MF_CHECKED : MF_UNCHECKED;
+  CheckMenuItem(menu, IDM_CTRL_KEY, MF_BYCOMMAND | ctrlCheckFlags);
+  CheckMenuItem(menu, IDM_ALT_KEY, MF_BYCOMMAND | altCheckFlags);
+
+  // Ensure that the Send <MenuKey> menu item has the correct text
+  if (options.menuKey) {
+    TCharArray menuKeyStr(options.menuKeyName());
+    TCharArray tmp(_tcslen(menuKeyStr.buf) + 6);
+    _stprintf(tmp.buf, _T("Send %s"), menuKeyStr.buf);
+    if (!ModifyMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf))
+      InsertMenu(menu, IDM_SEND_CAD, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf);
+  } else {
+    RemoveMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND);
+  }
+
+  // Set the menu fullscreen option tick
+  CheckMenuItem(menu, IDM_FULLSCREEN, (window->isFullscreen() ? MF_CHECKED : 0) | MF_BYCOMMAND);
+
+  // Set the menu toolbar option tick
+  int toolbarFlags = window->isToolbarEnabled() ? MF_CHECKED : 0;
+  CheckMenuItem(menu, IDM_SHOW_TOOLBAR, MF_BYCOMMAND | toolbarFlags);
+
+  // In the full-screen mode, "Show toolbar" should be grayed.
+  toolbarFlags = window->isFullscreen() ? MF_GRAYED : MF_ENABLED;
+  EnableMenuItem(menu, IDM_SHOW_TOOLBAR, MF_BYCOMMAND | toolbarFlags);
+}
+
+
+void
+CConn::blockCallback() {
+  // - An InStream has blocked on I/O while processing an RFB message
+  //   We re-enable socket event notifications, so we'll know when more
+  //   data is available, then we sit and dispatch window events until
+  //   the notification arrives.
+  if (!isClosed()) {
+    if (WSAEventSelect(sock->getFd(), sockEvent, FD_READ | FD_CLOSE) == SOCKET_ERROR)
+      throw rdr::SystemException("Unable to wait for sokcet data", WSAGetLastError());
+  }
+  while (true) {
+    // If we have closed then we can't block waiting for data
+    if (isClosed())
+      throw rdr::EndOfStream();
+
+    // Wait for socket data, or a message to process
+    DWORD result = MsgWaitForMultipleObjects(1, &sockEvent.h, FALSE, INFINITE, QS_ALLINPUT);
+    if (result == WAIT_OBJECT_0) {
+      // - Network event notification.  Return control to I/O routine.
+      break;
+    } else if (result == WAIT_FAILED) {
+      // - The wait operation failed - raise an exception
+      throw rdr::SystemException("blockCallback wait error", GetLastError());
+    }
+
+    // - There should be a message in the message queue
+    MSG msg;
+    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+      // IMPORTANT: We mustn't call TranslateMessage() here, because instead we
+      // call ToAscii() in CKeyboard::keyEvent().  ToAscii() stores dead key
+      // state from one call to the next, which would be messed up by calls to
+      // TranslateMessage() (actually it looks like TranslateMessage() calls
+      // ToAscii() internally).
+      DispatchMessage(&msg);
+    }
+  }
+
+  // Before we return control to the InStream, reset the network event
+  WSAEventSelect(sock->getFd(), sockEvent, 0);
+  ResetEvent(sockEvent);
+}
+
+
+void CConn::keyEvent(rdr::U32 key, bool down) {
+  if (!options.sendKeyEvents) return;
+  try {
+    writer()->keyEvent(key, down);
+  } catch (rdr::Exception& e) {
+    close(e.str());
+  }
+}
+void CConn::pointerEvent(const Point& pos, int buttonMask) {
+  if (!options.sendPtrEvents) return;
+  try {
+    writer()->pointerEvent(pos, buttonMask);
+  } catch (rdr::Exception& e) {
+    close(e.str());
+  }
+}
+void CConn::clientCutText(const char* str, int len) {
+  if (!options.clientCutText) return;
+  if (state() != RFBSTATE_NORMAL) return;
+  try {
+    writer()->clientCutText(str, len);
+  } catch (rdr::Exception& e) {
+    close(e.str());
+  }
+}
+
+
+CSecurity* CConn::getCSecurity(int secType)
+{
+  switch (secType) {
+  case secTypeNone:
+    return new CSecurityNone();
+  case secTypeVncAuth:
+    return new CSecurityVncAuth(this);
+  default:
+    throw Exception("Unsupported secType?");
+  }
+}
+
+
+void
+CConn::setColourMapEntries(int first, int count, U16* rgbs) {
+  vlog.debug("setColourMapEntries: first=%d, count=%d", first, count);
+  int i;
+  for (i=0;i<count;i++)
+    window->setColour(i+first, rgbs[i*3], rgbs[i*3+1], rgbs[i*3+2]);
+  // *** change to 0, 256?
+  window->refreshWindowPalette(first, count);
+}
+
+void
+CConn::bell() {
+  if (options.acceptBell)
+    MessageBeep(-1);
+}
+
+
+void
+CConn::setDesktopSize(int w, int h) {
+  vlog.debug("setDesktopSize %dx%d", w, h);
+
+  // Resize the window's buffer
+  if (window)
+    window->setSize(w, h);
+
+  // Tell the underlying CConnection
+  CConnection::setDesktopSize(w, h);
+}
+
+void
+CConn::setCursor(int w, int h, const Point& hotspot, void* data, void* mask) {
+  if (!options.useLocalCursor) return;
+
+  // Set the window to use the new cursor
+  window->setCursor(w, h, hotspot, data, mask);
+}
+
+
+void
+CConn::close(const char* reason) {
+  // If already closed then ignore this
+  if (isClosed())
+    return;
+
+  // Hide the window, if it exists
+  if (window)
+    ShowWindow(window->getHandle(), SW_HIDE);
+
+  // Save the reason & flag that we're closed & shutdown the socket
+  isClosed_ = true;
+  closeReason_.replaceBuf(strDup(reason));
+  sock->shutdown();
+}
+
+
+void
+CConn::showOptionsDialog() {
+  optionsDialog.showDialog(this);
+}
+
+
+void
+CConn::framebufferUpdateEnd() {
+  if (debugDelay != 0) {
+    vlog.debug("debug delay %d",(int)debugDelay);
+    UpdateWindow(window->getHandle());
+    Sleep(debugDelay);
+    std::list<rfb::Rect>::iterator i;
+    for (i = debugRects.begin(); i != debugRects.end(); i++) {
+      window->invertRect(*i);
+    }
+    debugRects.clear();
+  }
+  if (options.autoSelect)
+    autoSelectFormatAndEncoding();
+
+  // Always request the next update
+  requestUpdate = true;
+
+  // Check that at least part of the window has changed
+  if (!GetUpdateRect(window->getHandle(), 0, FALSE)) {
+    if (!(GetWindowLong(window->getHandle(), GWL_STYLE) & WS_MINIMIZE))
+      requestNewUpdate();
+  }
+
+  // Make sure the local cursor is shown
+  window->showCursor();
+}
+
+
+// Note: The method below is duplicated in vncviewer_unix/CConn.cxx!
+
+// autoSelectFormatAndEncoding() chooses the format and encoding appropriate
+// to the connection speed:
+//
+//   Above 16Mbps (timing for at least a second), switch to hextile
+//   Otherwise, switch to ZRLE
+//
+//   Above 256Kbps, use full colour mode
+//
+void
+CConn::autoSelectFormatAndEncoding() {
+  int kbitsPerSecond = sock->inStream().kbitsPerSecond();
+  unsigned int newEncoding = options.preferredEncoding;
+
+  bool newFullColour = options.fullColour;
+  unsigned int timeWaited = sock->inStream().timeWaited();
+
+  // Select best encoding
+  if (kbitsPerSecond > 16000 && timeWaited >= 10000) {
+    newEncoding = encodingHextile;
+  } else {
+    newEncoding = encodingZRLE;
+  }
+
+  if (newEncoding != options.preferredEncoding) {
+    vlog.info("Throughput %d kbit/s - changing to %s encoding",
+            kbitsPerSecond, encodingName(newEncoding));
+    options.preferredEncoding = newEncoding;
+    encodingChange = true;
+  }
+
+  if (kbitsPerSecond == 0) {
+    return;
+  }
+
+  if (cp.beforeVersion(3, 8)) {
+    // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with
+    // cursors "asynchronously". If this happens in the middle of a
+    // pixel format change, the server will encode the cursor with
+    // the old format, but the client will try to decode it
+    // according to the new format. This will lead to a
+    // crash. Therefore, we do not allow automatic format change for
+    // old servers.
+    return;
+  }
+  
+  // Select best color level
+  newFullColour = (kbitsPerSecond > 256);
+  if (newFullColour != options.fullColour) {
+    vlog.info("Throughput %d kbit/s - full color is now %s", 
+              kbitsPerSecond,
+              newFullColour ? "enabled" : "disabled");
+    options.fullColour = newFullColour;
+    formatChange = true;
+  }
+}
+
+void
+CConn::requestNewUpdate() {
+  if (!requestUpdate) return;
+
+  if (formatChange) {
+    // Select the required pixel format
+    if (options.fullColour) {
+      window->setPF(fullColourPF);
+    } else {
+      switch (options.lowColourLevel) {
+      case 0:
+        window->setPF(PixelFormat(8,3,0,1,1,1,1,2,1,0));
+        break;
+      case 1:
+        window->setPF(PixelFormat(8,6,0,1,3,3,3,4,2,0));
+        break;
+      case 2:
+        window->setPF(PixelFormat(8,8,0,0,0,0,0,0,0,0));
+        break;
+      }
+    }
+
+    // Print the current pixel format
+    char str[256];
+    window->getPF().print(str, 256);
+    vlog.info("Using pixel format %s",str);
+
+    // Save the connection pixel format and tell server to use it
+    cp.setPF(window->getPF());
+    writer()->writeSetPixelFormat(cp.pf());
+
+    // Correct the local window's palette
+    if (!window->getNativePF().trueColour)
+      window->refreshWindowPalette(0, 1 << cp.pf().depth);
+  }
+
+  if (encodingChange) {
+    vlog.info("Using %s encoding",encodingName(options.preferredEncoding));
+    writer()->writeSetEncodings(options.preferredEncoding, true);
+  }
+
+  writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height),
+                                          !formatChange);
+
+  encodingChange = formatChange = requestUpdate = false;
+}
+
+
+void
+CConn::calculateFullColourPF() {
+  // If the server is palette based then use palette locally
+  // Also, don't bother doing bgr222
+  if (!serverDefaultPF.trueColour || (serverDefaultPF.depth < 6)) {
+    fullColourPF = serverDefaultPF;
+    options.fullColour = true;
+  } else {
+    // If server is trueColour, use lowest depth PF
+    PixelFormat native = window->getNativePF();
+    if ((serverDefaultPF.bpp < native.bpp) ||
+        ((serverDefaultPF.bpp == native.bpp) &&
+        (serverDefaultPF.depth < native.depth)))
+      fullColourPF = serverDefaultPF;
+    else
+      fullColourPF = window->getNativePF();
+  }
+  formatChange = true;
+}
+
+
+void
+CConn::setName(const char* name) {
+  if (window)
+    window->setName(name);
+  CConnection::setName(name);
+}
+
+
+void CConn::serverInit() {
+  CConnection::serverInit();
+
+  // If using AutoSelect with old servers, start in FullColor
+  // mode. See comment in autoSelectFormatAndEncoding. 
+  if (cp.beforeVersion(3, 8) && options.autoSelect) {
+    options.fullColour = true;
+  }
+
+  // Show the window
+  window = new DesktopWindow(this);
+  window->setName(cp.name());
+  window->setSize(cp.width, cp.height);
+  applyOptions(options);
+
+  // Save the server's current format
+  serverDefaultPF = cp.pf();
+
+  // Calculate the full-colour format to use
+  calculateFullColourPF();
+
+  // Request the initial update
+  vlog.info("requesting initial update");
+  formatChange = encodingChange = requestUpdate = true;
+  requestNewUpdate();
+
+  // Update the window menu
+  HMENU wndmenu = GetSystemMenu(window->getHandle(), FALSE);
+  int toolbarChecked = options.showToolbar ? MF_CHECKED : 0;
+
+  AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
+  AppendMenu(wndmenu, MF_STRING, IDM_FULLSCREEN, _T("&Full screen"));
+  AppendMenu(wndmenu, MF_STRING | toolbarChecked, IDM_SHOW_TOOLBAR,
+             _T("Show tool&bar"));
+  AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
+  AppendMenu(wndmenu, MF_STRING, IDM_CTRL_KEY, _T("Ctr&l"));
+  AppendMenu(wndmenu, MF_STRING, IDM_ALT_KEY, _T("Al&t"));
+  AppendMenu(wndmenu, MF_STRING, IDM_SEND_CAD, _T("Send Ctrl-Alt-&Del"));
+  AppendMenu(wndmenu, MF_STRING, IDM_SEND_CTLESC, _T("Send Ctrl-&Esc"));
+  AppendMenu(wndmenu, MF_STRING, IDM_REQUEST_REFRESH, _T("Refres&h Screen"));
+  AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
+  AppendMenu(wndmenu, MF_STRING, IDM_NEWCONN, _T("Ne&w Connection..."));
+  AppendMenu(wndmenu, MF_STRING, IDM_OPTIONS, _T("&Options..."));
+  AppendMenu(wndmenu, MF_STRING, IDM_INFO, _T("Connection &Info..."));
+  AppendMenu(wndmenu, MF_STRING, IDM_ABOUT, _T("&About..."));
+}
+
+void
+CConn::serverCutText(const char* str, int len) {
+  if (!options.serverCutText) return;
+  window->serverCutText(str, len);
+}
+
+
+void CConn::beginRect(const Rect& r, unsigned int encoding) {
+  sock->inStream().startTiming();
+}
+
+void CConn::endRect(const Rect& r, unsigned int encoding) {
+  sock->inStream().stopTiming();
+  lastUsedEncoding_ = encoding;
+  if (debugDelay != 0) {
+    window->invertRect(r);
+    debugRects.push_back(r);
+  }
+}
+
+void CConn::fillRect(const Rect& r, Pixel pix) {
+  window->fillRect(r, pix);
+}
+void CConn::imageRect(const Rect& r, void* pixels) {
+  window->imageRect(r, pixels);
+}
+void CConn::copyRect(const Rect& r, int srcX, int srcY) {
+  window->copyRect(r, srcX, srcY);
+}
+
+void CConn::getUserPasswd(char** user, char** password) {
+/*
+  if (!user && options.passwordFile.buf[0]) {
+    FILE* fp = fopen(options.passwordFile.buf, "rb");
+    if (!fp) return false;
+    char data[256];
+    int datalen = fread(data, 1, 256, fp);
+    fclose(fp);
+    if (datalen != 8) return false;
+    vncAuthUnobfuscatePasswd(data);
+    *password = strDup(data);
+    memset(data, 0, strlen(data));
+    return true;
+  }
+*/
+  if (user && options.userName.buf)
+    *user = strDup(options.userName.buf);
+  if (password && options.password.buf)
+    *password = strDup(options.password.buf);
+  if ((user && !*user) || (password && !*password)) {
+    // Missing username or password - prompt the user
+    UserPasswdDialog userPasswdDialog;
+    userPasswdDialog.setCSecurity(getCurrentCSecurity());
+    userPasswdDialog.getUserPasswd(user, password);
+  }
+  if (user) options.setUserName(*user);
+  if (password) options.setPassword(*password);
+}
+
+bool CConn::processFTMsg(int type) {
+  return m_fileTransfer.processFTMsg(type);
+}
diff --git a/vncviewer/CConn.h b/vncviewer/CConn.h
new file mode 100644
index 0000000..29023f3
--- /dev/null
+++ b/vncviewer/CConn.h
@@ -0,0 +1,165 @@
+/* Copyright (C) 2002-2005 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.
+ */
+
+// -=- CConn.h
+
+// Windows-specific implementation of CConnection
+
+#ifndef __RFB_WIN32_CCONN_H__
+#define __RFB_WIN32_CCONN_H__
+
+#include <network/Socket.h>
+#include <rfb/CConnection.h>
+#include <rfb/Cursor.h>
+#include <rfb/UserPasswdGetter.h>
+#include <rfb_win32/Registry.h>
+#include <rfb_win32/Handle.h>
+#include <vncviewer/InfoDialog.h>
+#include <vncviewer/OptionsDialog.h>
+#include <vncviewer/CConnOptions.h>
+#include <vncviewer/DesktopWindow.h>
+#include <vncviewer/FileTransfer.h>
+#include <list>
+
+
+namespace rfb {
+
+  namespace win32 {
+
+    class CConn : public CConnection,
+                  UserPasswdGetter,
+                  DesktopWindow::Callback,
+                  rdr::FdInStreamBlockCallback
+    {
+    public:
+      CConn();
+      ~CConn();
+
+      // - Start the VNC session on the supplied socket
+      //   The socket must already be connected to a host
+      bool initialise(network::Socket* s, bool reverse=false);
+
+      // - Set/get the session options
+      void applyOptions(CConnOptions& opt);
+      const CConnOptions& getOptions() const { return options; };
+
+      // - Show the options dialog for the connection
+      void showOptionsDialog();
+
+      // - Close the socket & set the reason for closure
+      void close(const char* reason=0);
+      bool isClosed() const { return isClosed_; }
+      const char* closeReason() const { return closeReason_.buf; }
+
+      // - Last received encoding, for the Info dialog
+      int lastUsedEncoding() const { return lastUsedEncoding_; }
+
+      // - Get at the DesktopWindow, if any
+      DesktopWindow* getWindow() { return window; }
+
+      // - Get at the underlying Socket
+      network::Socket* getSocket() { return sock; }
+
+      // - Get the server's preferred format
+      const PixelFormat& getServerDefaultPF() const { return serverDefaultPF; }
+
+      // Global user-config registry key
+      static RegKey userConfigKey;
+
+      bool processFTMsg(int type);
+
+    protected:
+      // InputHandler interface (via DesktopWindow::Callback)
+      void keyEvent(rdr::U32 key, bool down);
+      void pointerEvent(const Point& pos, int buttonMask);
+      void clientCutText(const char* str, int len);
+
+      // DesktopWindow::Callback interface
+      void displayChanged();
+      void paintCompleted();
+      bool sysCommand(WPARAM wParam, LPARAM lParam);
+      void closeWindow();
+      void refreshMenu(bool enableSysCommands);
+
+      // CConnection interface
+      CSecurity* getCSecurity(int secType);
+      void setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs);
+      void bell();
+      void framebufferUpdateEnd();
+      void setDesktopSize(int w, int h);
+      void setCursor(int w, int h, const Point& hotspot, void* data, void* mask);
+      void setName(const char* name);
+      void serverInit();
+      void serverCutText(const char* str, int len);
+      void beginRect(const Rect& r, unsigned int encoding);
+      void endRect(const Rect& r, unsigned int encoding);
+      void fillRect(const Rect& r, Pixel pix);
+      void imageRect(const Rect& r, void* pixels);
+      void copyRect(const Rect& r, int srcX, int srcY);
+
+      // rdr::FdInStreamBlockCallback interface
+      void blockCallback();
+
+      // UserPasswdGetter interface
+      // (overridden to allow a pre-supplied username & password)
+      void getUserPasswd(char** user, char** password);
+
+      // CConn-specific internal interface
+      void autoSelectFormatAndEncoding();
+      void requestNewUpdate();
+      void calculateFullColourPF();
+
+      // The desktop window
+      DesktopWindow* window;
+
+      // Info and Options dialogs
+      OptionsDialog optionsDialog;
+      InfoDialog infoDialog;
+
+      // VNC Viewer options
+      CConnOptions options;
+
+      // Pixel format and encoding
+      PixelFormat serverDefaultPF;
+      PixelFormat fullColourPF;
+      bool sameMachine;
+      bool encodingChange;
+      bool formatChange;
+      int lastUsedEncoding_;
+
+      // Networking and RFB protocol
+      network::Socket* sock;
+      Handle sockEvent;
+      bool reverseConnection;
+      bool requestUpdate;
+
+      // Debugging/logging
+      std::list<Rect> debugRects;
+      CharArray closeReason_;
+      bool isClosed_;
+
+      FileTransfer m_fileTransfer;
+    };
+
+  };
+
+};
+
+#endif
+
+
diff --git a/vncviewer/CViewOptions.cxx b/vncviewer/CConnOptions.cxx
similarity index 85%
rename from vncviewer/CViewOptions.cxx
rename to vncviewer/CConnOptions.cxx
index 76a624b..4ea0ada 100644
--- a/vncviewer/CViewOptions.cxx
+++ b/vncviewer/CConnOptions.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -16,12 +16,11 @@
  * USA.
  */
 
-#include <vncviewer/CViewOptions.h>
+#include <vncviewer/CConnOptions.h>
 #include <rfb/Configuration.h>
 #include <rfb/encodings.h>
-#include <rfb/vncAuth.h>
 #include <rfb/LogWriter.h>
-#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/MsgBox.h>
 #include <rfb_win32/Registry.h>
 #include <rdr/HexInStream.h>
 #include <rdr/HexOutStream.h>
@@ -33,6 +32,9 @@
 static StringParameter passwordFile("PasswordFile",
 				    "Password file for VNC authentication", "");
 
+// - Settings stored in the registry & in .vnc files, by Save Defaults and
+//   Save Configuration respectively.
+
 static BoolParameter useLocalCursor("UseLocalCursor", "Render the mouse cursor locally", true);
 static BoolParameter useDesktopResize("UseDesktopResize", "Support dynamic desktop resizing", true);
 
@@ -66,14 +68,15 @@
                          "Send pointer (mouse) events to the server.", true);
 static BoolParameter sendKeyEvents("SendKeyEvents",
                          "Send key presses (and releases) to the server.", true);
-static BoolParameter sendSysKeys("SendSysKeys",
-                         "Send system keys (Alt combinations) to the server.", true);
 
 static BoolParameter clientCutText("ClientCutText",
                          "Send clipboard changes to the server.", true);
 static BoolParameter serverCutText("ServerCutText",
                          "Accept clipboard changes from the server.", true);
 
+static BoolParameter disableWinKeys("DisableWinKeys",
+                         "Pass special Windows keys directly to the server.", true);
+
 static BoolParameter protocol3_3("Protocol3.3",
                          "Only use protocol version 3.3", false);
 
@@ -92,6 +95,8 @@
 
 static StringParameter monitor("Monitor", "The monitor to open the VNC Viewer window on, if available.", "");
 static StringParameter menuKey("MenuKey", "The key which brings up the popup menu", "F8");
+static BoolParameter autoReconnect("AutoReconnect", "Offer to reconnect to the remote server if the connection"
+                                   "is dropped because an error occurs.", true);
 
 static BoolParameter customCompressLevel("CustomCompressLevel",
 					 "Use custom compression level. "
@@ -111,18 +116,24 @@
 				 "0 = Low, 9 = High",
 				 6);
 
-CViewOptions::CViewOptions()
+CConnOptions::CConnOptions()
 : useLocalCursor(::useLocalCursor), useDesktopResize(::useDesktopResize),
 autoSelect(::autoSelect), fullColour(::fullColour), fullScreen(::fullScreen),
-shared(::sharedConnection), sendPtrEvents(::sendPtrEvents), sendKeyEvents(::sendKeyEvents), sendSysKeys(::sendSysKeys),
+shared(::sharedConnection), sendPtrEvents(::sendPtrEvents), sendKeyEvents(::sendKeyEvents),
 preferredEncoding(encodingZRLE), clientCutText(::clientCutText), serverCutText(::serverCutText),
-protocol3_3(::protocol3_3), acceptBell(::acceptBell), showToolbar(::showToolbar), lowColourLevel(::lowColourLevel),
-pointerEventInterval(ptrEventInterval), emulate3(::emulate3), monitor(::monitor.getData()),
+disableWinKeys(::disableWinKeys), protocol3_3(::protocol3_3), acceptBell(::acceptBell),
+lowColourLevel(::lowColourLevel), pointerEventInterval(ptrEventInterval),
+emulate3(::emulate3), monitor(::monitor.getData()), showToolbar(::showToolbar),
 customCompressLevel(::customCompressLevel), compressLevel(::compressLevel), 
-noJpeg(::noJpeg), qualityLevel(::qualityLevel), passwordFile(::passwordFile.getData())
+noJpeg(::noJpeg), qualityLevel(::qualityLevel), passwordFile(::passwordFile.getData()),
+autoReconnect(::autoReconnect)
 {
-  CharArray encodingName(::preferredEncoding.getData());
-  preferredEncoding = encodingNum(encodingName.buf);
+  if (autoSelect) {
+    preferredEncoding = encodingZRLE;
+  } else {
+    CharArray encodingName(::preferredEncoding.getData());
+    preferredEncoding = encodingNum(encodingName.buf);
+  }
   setMenuKey(CharArray(::menuKey.getData()).buf);
 
   if (!::autoSelect.hasBeenSet()) {
@@ -138,7 +149,7 @@
 }
 
 
-void CViewOptions::readFromFile(const char* filename) {
+void CConnOptions::readFromFile(const char* filename) {
   FILE* f = fopen(filename, "r");
   if (!f)
     throw rdr::Exception("Failed to read configuration file");
@@ -185,15 +196,10 @@
           } else if (stricmp(name.buf, "UserName") == 0) {
             userName.replaceBuf(value.takeBuf());
           } else if (stricmp(name.buf, "Password") == 0) {
-            int len = 0;
-            CharArray obfuscated;
-            rdr::HexInStream::hexStrToBin(value.buf, &obfuscated.buf, &len);
-            if (len == 8) {
-              password.replaceBuf(new char[9]);
-              memcpy(password.buf, obfuscated.buf, 8);
-              vncAuthUnobfuscatePasswd(password.buf);
-              password.buf[8] = 0;
-            }
+            ObfuscatedPasswd obfPwd;
+            rdr::HexInStream::hexStrToBin(value.buf, (char**)&obfPwd.buf, &obfPwd.length);
+            PlainPasswd passwd(obfPwd);
+            password.replaceBuf(passwd.takeBuf());
           }
         } else if (stricmp(section.buf, "Options") == 0) {
             // V4 options
@@ -218,12 +224,12 @@
             sendPtrEvents = atoi(value.buf);
           } else if (stricmp(name.buf, "SendKeyEvents") == 0) {
             sendKeyEvents = atoi(value.buf);
-          } else if (stricmp(name.buf, "SendSysKeys") == 0) {
-            sendSysKeys = atoi(value.buf);
           } else if (stricmp(name.buf, "SendCutText") == 0) {
             clientCutText = atoi(value.buf);
           } else if (stricmp(name.buf, "AcceptCutText") == 0) {
             serverCutText = atoi(value.buf);
+          } else if (stricmp(name.buf, "DisableWinKeys") == 0) {
+            disableWinKeys = atoi(value.buf);
           } else if (stricmp(name.buf, "AcceptBell") == 0) {
             acceptBell = atoi(value.buf);
           } else if (stricmp(name.buf, "Emulate3") == 0) {
@@ -236,6 +242,9 @@
             monitor.replaceBuf(value.takeBuf());
           } else if (stricmp(name.buf, "MenuKey") == 0) {
             setMenuKey(value.buf);
+          } else if (stricmp(name.buf, "AutoReconnect") == 0) {
+            autoReconnect = atoi(value.buf);
+
           } else if (stricmp(name.buf, "CustomCompressLevel") == 0) {
 	    customCompressLevel = atoi(value.buf);
           } else if (stricmp(name.buf, "CompressLevel") == 0) {
@@ -274,6 +283,10 @@
       }
     }
 
+    // If AutoSelect is enabled then override the preferred encoding
+    if (autoSelect)
+      preferredEncoding = encodingZRLE;
+
     setConfigFileName(filename);
   } catch (rdr::Exception&) {
     if (f) fclose(f);
@@ -281,7 +294,7 @@
   }
 }
 
-void CViewOptions::writeToFile(const char* filename) {
+void CConnOptions::writeToFile(const char* filename) {
   FILE* f = fopen(filename, "w");
   if (!f)
     throw rdr::Exception("Failed to write configuration file");
@@ -298,11 +311,8 @@
       if (MsgBox(0, _T("Do you want to include the VNC Password in this configuration file?\n")
                     _T("Storing the password is more convenient but poses a security risk."),
                     MB_YESNO | MB_DEFBUTTON2 | MB_ICONWARNING) == IDYES) {
-        char obfuscated[9];
-        memset(obfuscated, 0, sizeof(obfuscated));
-        strCopy(obfuscated, password.buf, sizeof(obfuscated));
-        vncAuthObfuscatePasswd(obfuscated);
-        CharArray obfuscatedHex = rdr::HexOutStream::binToHexStr(obfuscated, 8);
+        ObfuscatedPasswd obfPwd(password);
+        CharArray obfuscatedHex = rdr::HexOutStream::binToHexStr(obfPwd.buf, obfPwd.length);
         fprintf(f, "Password=%s\n", obfuscatedHex.buf);
       }
     }
@@ -320,9 +330,9 @@
     fprintf(f, "Shared=%d\n", (int)shared);
     fprintf(f, "SendPtrEvents=%d\n", (int)sendPtrEvents);
     fprintf(f, "SendKeyEvents=%d\n", (int)sendKeyEvents);
-    fprintf(f, "SendSysKeys=%d\n", (int)sendSysKeys);
     fprintf(f, "SendCutText=%d\n", (int)clientCutText);
     fprintf(f, "AcceptCutText=%d\n", (int)serverCutText);
+    fprintf(f, "DisableWinKeys=%d\n", (int)disableWinKeys);
     fprintf(f, "AcceptBell=%d\n", (int)acceptBell);
     fprintf(f, "Emulate3=%d\n", (int)emulate3);
     fprintf(f, "ShowToolbar=%d\n", (int)showToolbar);
@@ -330,6 +340,7 @@
     if (monitor.buf)
       fprintf(f, "Monitor=%s\n", monitor.buf);
     fprintf(f, "MenuKey=%s\n", CharArray(menuKeyName()).buf);
+    fprintf(f, "AutoReconnect=%d\n", (int)autoReconnect);
     fprintf(f, "CustomCompressLevel=%d\n", customCompressLevel);
     fprintf(f, "CompressLevel=%d\n", compressLevel);
     fprintf(f, "NoJPEG=%d\n", noJpeg);
@@ -344,7 +355,7 @@
 }
 
 
-void CViewOptions::writeDefaults() {
+void CConnOptions::writeDefaults() {
   RegKey key;
   key.createKey(HKEY_CURRENT_USER, _T("Software\\TightVNC\\VNCviewer4"));
   key.setBool(_T("UseLocalCursor"), useLocalCursor);
@@ -357,9 +368,9 @@
   key.setBool(_T("Shared"), shared);
   key.setBool(_T("SendPointerEvents"), sendPtrEvents);
   key.setBool(_T("SendKeyEvents"), sendKeyEvents);
-  key.setBool(_T("SendSysKeys"), sendSysKeys);
   key.setBool(_T("ClientCutText"), clientCutText);
   key.setBool(_T("ServerCutText"), serverCutText);
+  key.setBool(_T("DisableWinKeys"), disableWinKeys);
   key.setBool(_T("Protocol3.3"), protocol3_3);
   key.setBool(_T("AcceptBell"), acceptBell);
   key.setBool(_T("ShowToolbar"), showToolbar);
@@ -368,6 +379,7 @@
   if (monitor.buf)
     key.setString(_T("Monitor"), TStr(monitor.buf));
   key.setString(_T("MenuKey"), TCharArray(menuKeyName()).buf);
+  key.setBool(_T("AutoReconnect"), autoReconnect);
   key.setInt(_T("CustomCompressLevel"), customCompressLevel);
   key.setInt(_T("CompressLevel"), compressLevel);
   key.setInt(_T("NoJPEG"), noJpeg);
@@ -375,13 +387,13 @@
 }
 
 
-void CViewOptions::setUserName(const char* user) {userName.replaceBuf(strDup(user));}
-void CViewOptions::setPassword(const char* pwd) {password.replaceBuf(strDup(pwd));}
-void CViewOptions::setConfigFileName(const char* cfn) {configFileName.replaceBuf(strDup(cfn));}
-void CViewOptions::setHost(const char* h) {host.replaceBuf(strDup(h));}
-void CViewOptions::setMonitor(const char* m) {monitor.replaceBuf(strDup(m));}
+void CConnOptions::setUserName(const char* user) {userName.replaceBuf(strDup(user));}
+void CConnOptions::setPassword(const char* pwd) {password.replaceBuf(strDup(pwd));}
+void CConnOptions::setConfigFileName(const char* cfn) {configFileName.replaceBuf(strDup(cfn));}
+void CConnOptions::setHost(const char* h) {host.replaceBuf(strDup(h));}
+void CConnOptions::setMonitor(const char* m) {monitor.replaceBuf(strDup(m));}
 
-void CViewOptions::setMenuKey(const char* keyName) {
+void CConnOptions::setMenuKey(const char* keyName) {
   if (!keyName[0]) {
     menuKey = 0;
   } else {
@@ -393,7 +405,7 @@
     }
   }
 }
-char* CViewOptions::menuKeyName() {
+char* CConnOptions::menuKeyName() {
   int fNum = (menuKey-VK_F1)+1;
   if (fNum<1 || fNum>12)
     return strDup("");
@@ -403,7 +415,7 @@
 }
 
 
-CViewOptions& CViewOptions::operator=(const CViewOptions& o) {
+CConnOptions& CConnOptions::operator=(const CConnOptions& o) {
   useLocalCursor = o.useLocalCursor;
   useDesktopResize = o.useDesktopResize;
   fullScreen = o.fullScreen;
@@ -414,9 +426,9 @@
   shared = o.shared;
   sendPtrEvents = o.sendPtrEvents;
   sendKeyEvents = o.sendKeyEvents;
-  sendSysKeys = o.sendSysKeys;
   clientCutText = o.clientCutText;
   serverCutText = o.serverCutText;
+  disableWinKeys = o.disableWinKeys;
   emulate3 = o.emulate3;
   pointerEventInterval = o.pointerEventInterval;
   protocol3_3 = o.protocol3_3;
@@ -428,6 +440,7 @@
   setHost(o.host.buf);
   setMonitor(o.monitor.buf);
   menuKey = o.menuKey;
+  autoReconnect = o.autoReconnect;
   customCompressLevel = o.customCompressLevel;
   compressLevel = o.compressLevel;
   noJpeg = o.noJpeg;
diff --git a/vncviewer/CViewOptions.h b/vncviewer/CConnOptions.h
similarity index 76%
rename from vncviewer/CViewOptions.h
rename to vncviewer/CConnOptions.h
index febd284..59fd0a3 100644
--- a/vncviewer/CViewOptions.h
+++ b/vncviewer/CConnOptions.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -16,15 +16,15 @@
  * USA.
  */
 
-// -=- CViewOptions.h
+// -=- CConnOptions.h
 
-// Definition of the CViewOptions class, responsible for storing the
-// current & requested VNCviewer options.
+// Definition of the CConnOptions class, responsible for storing the
+// current & requested VNC Viewer options.
 
-#ifndef __RFB_WIN32_CVIEW_OPTIONS_H__
-#define __RFB_WIN32_CVIEW_OPTIONS_H__
+#ifndef __RFB_WIN32_CCONN_OPTIONS_H__
+#define __RFB_WIN32_CCONN_OPTIONS_H__
 
-#include <rfb/util.h>
+#include <rfb/Password.h>
 
 namespace rfb {
 
@@ -32,19 +32,19 @@
 
     //
     // -=- Options structure.  Each viewer option has a corresponding
-    //     entry in CViewOptions.  The viewer options are set by calling
-    //     CView::applyOptions(...)
-    //     The CViewOptions structure automatically picks up the default
+    //     entry in CConnOptions.  The viewer options are set by calling
+    //     CConn::applyOptions(...)
+    //     The CConnOptions structure automatically picks up the default
     //     value of each option from the Configuration system
     //     The readFromFile and writeFromFile methods can be used to load
     //     and save VNC configuration files.  readFromFile is backwards
     //     compatible with 3.3 releases, while writeToFile is not.
 
-    class CViewOptions {
+    class CConnOptions {
     public:
-      CViewOptions();
-      CViewOptions(const CViewOptions& o) {operator=(o);}
-      CViewOptions& operator=(const CViewOptions& o);
+      CConnOptions();
+      CConnOptions(const CConnOptions& o) {operator=(o);}
+      CConnOptions& operator=(const CConnOptions& o);
       void readFromFile(const char* filename_);
       void writeToFile(const char* filename_);
       void writeDefaults();
@@ -58,17 +58,17 @@
       bool shared;
       bool sendPtrEvents;
       bool sendKeyEvents;
-      bool sendSysKeys;
       bool showToolbar;
       bool clientCutText;
       bool serverCutText;
+      bool disableWinKeys;
       bool emulate3;
       int pointerEventInterval;
       bool protocol3_3;
       bool acceptBell;
       CharArray userName;
       void setUserName(const char* user);
-      CharArray password;
+      PlainPasswd password;
       void setPassword(const char* pwd);
       CharArray configFileName;
       void setConfigFileName(const char* cfn);
@@ -79,6 +79,7 @@
       unsigned int menuKey;
       void setMenuKey(const char* keyName);
       char* menuKeyName();
+      bool autoReconnect;
 
       bool customCompressLevel;
       int compressLevel;
diff --git a/vncviewer/CConnThread.cxx b/vncviewer/CConnThread.cxx
new file mode 100644
index 0000000..cfd2695
--- /dev/null
+++ b/vncviewer/CConnThread.cxx
@@ -0,0 +1,198 @@
+/* Copyright (C) 2002-2005 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.
+ */
+
+// -=- CConnThread.cxx
+
+// A CConnThread instance is created for each new connection.
+// The CConnThread creates the corresponding CConn instance
+// and manages it.
+
+#include <stdlib.h>
+#include <rfb/LogWriter.h>
+#include <rfb/Hostname.h>
+#include <rfb_win32/MsgBox.h>
+#include <network/TcpSocket.h>
+#include <vncviewer/CConnThread.h>
+#include <vncviewer/CConn.h>
+#include <vncviewer/ConnectionDialog.h>
+#include <vncviewer/ConnectingDialog.h>
+#include <vncviewer/UserPasswdDialog.h>
+#include <set>
+
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("CConnThread");
+
+static std::set<CConnThread*> threads;
+static Mutex threadsLock;
+static Handle noMoreThreads(CreateEvent(0, TRUE, FALSE, 0));
+
+
+CConnThread::CConnThread() : Thread("CConnThread"), isConfig(false),
+                             sock(0), reverse(false) {
+  vlog.info("CConnThread (dialog)");
+  setDeleteAfterRun();
+  Lock l(threadsLock);
+  threads.insert(this);
+  start();
+}
+
+CConnThread::CConnThread(const char* hostOrConfig_, bool isConfig_)
+ : Thread("CConnThread"), hostOrConfig(strDup(hostOrConfig_)),
+   isConfig(isConfig_), sock(0), reverse(false) {
+  vlog.info("CConnThread (host/port)");
+  setDeleteAfterRun();
+  Lock l(threadsLock);
+  threads.insert(this);
+  start();
+}
+
+CConnThread::CConnThread(network::Socket* sock_, bool reverse_)
+ : Thread("CConnThread"), isConfig(false), sock(sock_), reverse(reverse_) {
+  vlog.info("CConnThread (reverse connection)");
+  setDeleteAfterRun();
+  Lock l(threadsLock);
+  threads.insert(this);
+  start();
+}
+
+CConnThread::~CConnThread() {
+  Lock l(threadsLock);
+  threads.erase(this);
+  if (threads.empty())
+    SetEvent(noMoreThreads);
+  delete sock;
+}
+
+
+void CConnThread::run() {
+  CConnOptions options;
+  bool reconnect;
+
+  do {
+    {
+      CConn conn;
+      reconnect = false;
+
+      // If there is no socket object then set the host & port info
+      if (!sock && !options.host.buf) {
+        try {
+          if (isConfig) {
+            // A configuration file name was specified - load it
+            CharArray filename = hostOrConfig.takeBuf();
+            options.readFromFile(filename.buf);
+          } else {
+            // An actual hostname (and possibly port) was specified
+            options.host.replaceBuf(hostOrConfig.takeBuf());
+          }
+
+          if (!options.host.buf) {
+            // No host was specified - prompt for one
+            ConnectionDialog connDlg(&conn);
+            if (!connDlg.showDialog())
+              return;
+            options = conn.getOptions();
+            options.setHost(CStr(connDlg.hostname.buf));
+          }
+        } catch (rdr::Exception& e) {
+          MsgBox(0, TStr(e.str()), MB_ICONERROR | MB_OK);
+          return;
+        }
+      }
+
+      // Apply the connection options to the CConn
+      conn.applyOptions(options);
+
+      if (!sock) {
+        // There is no existing connection - better make one
+        const char* hostAndPort = conn.getOptions().host.buf;
+
+        try {
+          ConnectingDialog dlg;
+          sock = dlg.connect(hostAndPort);
+
+          // If the connection was cancelled by the user, just quit
+          if (!sock)
+            return;
+        } catch(rdr::Exception& e) {
+          MsgBox(NULL, TStr(e.str()), MB_ICONERROR | MB_OK);
+          return;
+        }
+
+        // Try to add the caller to the MRU
+        MRU::addToMRU(hostAndPort);
+      }
+
+      // Run the RFB protocol over the connected socket
+      conn.initialise(sock, reverse);
+      while (!conn.isClosed()) {
+        try {
+          conn.getInStream()->check(1,1);
+          conn.processMsg();
+        } catch (rdr::EndOfStream) {
+          if (conn.state() == CConnection::RFBSTATE_NORMAL)
+            conn.close();
+          else
+            conn.close("The connection closed unexpectedly");
+        } catch (rfb::AuthCancelledException) {
+          conn.close();
+        } catch (rfb::AuthFailureException& e) {
+          // Clear the password, in case we auto-reconnect
+          options = conn.getOptions();
+          options.password.replaceBuf(0);
+          conn.applyOptions(options);
+          conn.close(e.str());
+        } catch (rdr::Exception& e) {
+          conn.close(e.str());
+        }
+      }
+
+      // If there is a cause for closing the connection logged then display it
+      if (conn.closeReason()) {
+        reconnect = !reverse && conn.getOptions().autoReconnect;
+        if (!reconnect) {
+          MsgBox(0, TStr(conn.closeReason()), MB_ICONINFORMATION | MB_OK);
+        } else {
+          options = conn.getOptions();
+          const char* format = "%s\nDo you wish to attempt to reconnect to %s?";
+          CharArray message(strlen(conn.closeReason()) + strlen(format) +
+                            strlen(conn.getOptions().host.buf));
+          sprintf(message.buf, format, conn.closeReason(), conn.getOptions().host.buf);
+          if (MsgBox(0, TStr(message.buf), MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) != IDYES)
+            reconnect = false;
+        }
+      }
+    } // Exit the CConn's scope, implicitly destroying it & making it safe to delete the TcpSocket
+
+    // Clean up the old socket, if any
+    delete sock; sock = 0;
+  } while (reconnect);
+}
+
+
+BOOL CConnThread::getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg) {
+  while (!PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE)) {
+    DWORD result = MsgWaitForMultipleObjects(1, &noMoreThreads.h, FALSE, INFINITE, QS_ALLINPUT);
+    if (result == WAIT_OBJECT_0)
+      return FALSE;
+    else if (result == WAIT_FAILED)
+      throw rdr::SystemException("CConnThread::getMessage wait failed", GetLastError());
+  }
+  return msg->message != WM_QUIT;
+}
diff --git a/vncviewer/CConnThread.h b/vncviewer/CConnThread.h
new file mode 100644
index 0000000..7a8451c
--- /dev/null
+++ b/vncviewer/CConnThread.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2005 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.
+ */
+
+// -=- CConnThread.h
+
+// CConn-managing Thread implementation.
+
+#ifndef __RFB_WIN32_CCONN_THREAD_H__
+#define __RFB_WIN32_CCONN_THREAD_H__
+
+#include <network/Socket.h>
+#include <rfb/Threading.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class CConnThread : public Thread {
+    public:
+      CConnThread();
+      CConnThread(const char* hostOrConfig, bool isConfig=false);
+      CConnThread(network::Socket* sock, bool reverse=false);
+      ~CConnThread();
+
+      void run();
+
+      // Special getMessage call that returns FALSE if message is WM_QUIT,
+      // OR if there are no more CConnThreads running.
+      static BOOL getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg);
+    protected:
+      CharArray hostOrConfig;
+      bool isConfig;
+      network::Socket* sock;
+      bool reverse;
+    };
+
+  };
+
+};
+
+#endif // __RFB_WIN32_CCONN_THREAD_H__
diff --git a/vncviewer/CViewManager.cxx b/vncviewer/CViewManager.cxx
deleted file mode 100644
index 09ed1fe..0000000
--- a/vncviewer/CViewManager.cxx
+++ /dev/null
@@ -1,252 +0,0 @@
-/* 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 <winsock2.h>
-#include <vncviewer/CViewManager.h>
-#include <vncviewer/CView.h>
-#include <vncviewer/ConnectionDialog.h>
-#include <vncviewer/ConnectingDialog.h>
-#include <rfb/Hostname.h>
-#include <rfb/util.h>
-#include <rfb/LogWriter.h>
-#include <rfb/vncAuth.h>
-#include <rdr/HexInStream.h>
-#include <network/TcpSocket.h>
-
-using namespace rfb;
-using namespace win32;
-
-static LogWriter vlog("CViewManager");
-
-
-// -=- Custom thread class used internally
-
-class CViewThread : public Thread {
-public:
-  CViewThread(network::Socket* s, CViewManager& cvm);
-  CViewThread(const char* conninfo, CViewManager& cvm, bool infoIsConfigFile);
-  virtual ~CViewThread();
-
-  virtual void run();
-protected:
-  void setSocket(network::Socket* sock);
-
-  network::Socket* sock;
-  CharArray hostname;
-  CViewManager& manager;
-
-  bool useConfigFile;
-};
-
-
-CViewThread::CViewThread(network::Socket* s, CViewManager& cvm)
-: Thread("CView"), sock(s), manager(cvm) {
-  setDeleteAfterRun();
-}
-
-CViewThread::CViewThread(const char* h, CViewManager& cvm, bool hIsConfigFile)
-: Thread("CView"), sock(0), manager(cvm), useConfigFile(hIsConfigFile) {
-  setDeleteAfterRun();
-  if (h) hostname.buf = strDup(h);
-}
-
-
-CViewThread::~CViewThread() {
-  vlog.debug("~CViewThread");
-  manager.remThread(this);
-  delete sock;
-}
-
-
-void CViewThread::run() {
-  try {
-    CView view;
-    view.setManager(&manager);
-
-    if (!sock) {
-      try {
-        // If the hostname is actually a config filename then read it
-        if (useConfigFile) {
-          CharArray filename = hostname.takeBuf();
-          CViewOptions options;
-          options.readFromFile(filename.buf);
-          if (options.host.buf)
-            hostname.buf = strDup(options.host.buf);
-          view.applyOptions(options);
-        }
-
-        // If there is no hostname then present the connection
-        // dialog
-        if (!hostname.buf) {
-          ConnectionDialog conn(&view);
-          if (!conn.showDialog())
-            return;
-          hostname.buf = strDup(conn.hostname.buf);
-
-          // *** hack - Tell the view object the hostname
-          CViewOptions opt(view.getOptions());
-          opt.setHost(hostname.buf);
-          view.applyOptions(opt);
-        }
-
-        // Parse the host name & port
-        CharArray host;
-        int port;
-        getHostAndPort(hostname.buf, &host.buf, &port);
-
-        // Attempt to connect
-        ConnectingDialog dlg;
-        // this is a nasty hack to get round a Win2K and later "feature" which
-        // puts your second window in the background unless the first window
-        // you put up actually gets some input.  Just generate a fake shift
-        // event, which seems to do the trick.
-        keybd_event(VK_SHIFT, MapVirtualKey(VK_SHIFT, 0), 0, 0);
-        keybd_event(VK_SHIFT, MapVirtualKey(VK_SHIFT, 0), KEYEVENTF_KEYUP, 0);
-        sock = new network::TcpSocket(host.buf, port);
-      } catch(rdr::Exception& e) {
-        vlog.error("unable to connect to %s (%s)", hostname, e.str());
-        MsgBox(NULL, TStr(e.str()), MB_ICONERROR | MB_OK);
-        return;
-      }
-
-      // Try to add the caller to the MRU
-      MRU::addToMRU(hostname.buf);
-    }
-
-    view.initialise(sock);
-    try {
-      view.postQuitOnDestroy(true);
-      while (true) {
-        // - processMsg is designed to be callable in response to select().
-        //   As a result, it can be called when FdInStream data is available,
-        //   BUT there may be no actual RFB data available.  This is the case
-        //   for example when reading data over an encrypted stream - an
-        //   entire block must be read from the FdInStream before any data
-        //   becomes available through the top-level encrypted stream.
-        //   Since we are using blockCallback and not doing a select() here,
-        //   we simply check() for some data on the top-level RFB stream.
-        //   This ensures that processMsg will only be called when there is
-        //   actually something to do.  In the meantime, blockCallback()
-        //   will be called, keeping the user interface responsive.
-        view.getInStream()->check(1,1);
-        view.processMsg();
-      }
-    } catch (CView::QuitMessage& e) {
-      // - Cope silently with WM_QUIT messages
-      vlog.debug("QuitMessage received (wParam=%d)", e.wParam);
-    } catch (rdr::EndOfStream& e) {
-      // - Copy silently with disconnection if in NORMAL state
-      if (view.state() == CConnection::RFBSTATE_NORMAL)
-        vlog.debug(e.str());
-      else {
-        view.postQuitOnDestroy(false);
-        throw rfb::Exception("server closed connection unexpectedly");
-      }
-    } catch (rdr::Exception&) {
-      // - We MUST do this, otherwise ~CView will cause a
-      //   PostQuitMessage and any MessageBox call will quit immediately.
-      view.postQuitOnDestroy(false);
-      throw;
-    }
-  } catch(rdr::Exception& e) {
-    // - Something went wrong - display the error
-    vlog.error("error: %s", e.str());
-    MsgBox(NULL, TStr(e.str()), MB_ICONERROR | MB_OK);
-  }
-}
-
-
-// -=- CViewManager itself
-
-CViewManager::CViewManager()
-: MsgWindow(_T("CViewManager")), threadsSig(threadsMutex),
-  mainThread(Thread::self()) {
-}
-
-CViewManager::~CViewManager() {
-  while (!socks.empty()) {
-    network::SocketListener* sock = socks.front();
-    delete sock;
-    socks.pop_front();
-  }
-  awaitEmpty();
-}
-
-
-void CViewManager::awaitEmpty() {
-  Lock l(threadsMutex);
-  while (!threads.empty()) {
-    threadsSig.wait(true);
-  }
-}
-
-
-void CViewManager::addThread(Thread* t) {
-  Lock l(threadsMutex);
-  threads.push_front(t);
-}
-
-void CViewManager::remThread(Thread* t) {
-  Lock l(threadsMutex);
-  threads.remove(t);
-  threadsSig.signal();
-
-  // If there are no listening sockets then post a quit message when the
-  // last client disconnects
-  if (socks.empty())
-    PostThreadMessage(mainThread->getThreadId(), WM_QUIT, 0, 0);
-}
-
-
-bool CViewManager::addClient(const char* hostinfo, bool isConfigFile) {
-  CViewThread* thread = new CViewThread(hostinfo, *this, isConfigFile);
-  addThread(thread);
-  thread->start();
-  return true;
-}
-
-bool CViewManager::addListener(network::SocketListener* sock) {
-  socks.push_back(sock);
-  WSAAsyncSelect(sock->getFd(), getHandle(), WM_USER, FD_ACCEPT);
-  return true;
-}
-
-bool CViewManager::addDefaultTCPListener(int port) {
-  return addListener(new network::TcpListener(port));
-}
-
-
-LRESULT CViewManager::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
-  switch (msg) {
-  case WM_USER:
-    std::list<network::SocketListener*>::iterator i;
-    for (i=socks.begin(); i!=socks.end(); i++) {
-      if (wParam == (*i)->getFd()) {
-        network::Socket* new_sock = (*i)->accept();
-        CharArray connname;
-        connname.buf = new_sock->getPeerEndpoint();
-        vlog.debug("accepted connection: %s", connname);
-        CViewThread* thread = new CViewThread(new_sock, *this);
-        addThread(thread);
-        thread->start();
-        break;
-      }
-    }
-    break;
-  }
-  return MsgWindow::processMessage(msg, wParam, lParam);
-}
diff --git a/vncviewer/CViewManager.h b/vncviewer/CViewManager.h
deleted file mode 100644
index 3d11dd9..0000000
--- a/vncviewer/CViewManager.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/* 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.
- */
-
-// -=- CViewManager.h
-
-// Creates and manages threads to run CView instances.
-
-#ifndef __RFB_WIN32_CVIEW_MANAGER_H__
-#define __RFB_WIN32_CVIEW_MANAGER_H__
-
-#include <list>
-#include <network/Socket.h>
-#include <rfb/Threading.h>
-#include <rfb_win32/MsgWindow.h>
-
-namespace rfb {
-
-  namespace win32 {
-
-    class CViewManager : public MsgWindow {
-    public:
-      CViewManager();
-      ~CViewManager();
-
-      void awaitEmpty();
-
-      void addThread(Thread* t);
-      void remThread(Thread* t);
-
-      bool addClient(const char* hostinfo, bool isConfigFile=false);
-
-      bool addListener(network::SocketListener* sock);
-      bool addDefaultTCPListener(int port);
-
-      LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
-
-    protected:
-      std::list<network::SocketListener*> socks;
-      std::list<Thread*> threads;
-      Mutex threadsMutex;
-      Condition threadsSig;
-      Thread* mainThread;
-    };
-
-  };
-
-};
-
-#endif
diff --git a/vncviewer/ConnectingDialog.cxx b/vncviewer/ConnectingDialog.cxx
new file mode 100644
index 0000000..60fcb66
--- /dev/null
+++ b/vncviewer/ConnectingDialog.cxx
@@ -0,0 +1,160 @@
+/* Copyright (C) 2002-2005 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.
+ */
+
+// -=- ConnectingDialog.cxx
+
+#include <stdlib.h>
+#include <vncviewer/ConnectingDialog.h>
+#include <vncviewer/resource.h>
+#include <network/TcpSocket.h>
+#include <rfb/Threading.h>
+#include <rfb/Hostname.h>
+#include <map>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+
+// ConnectingDialog callback
+static BOOL CALLBACK ConnectingDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
+  bool* activePtr = (bool*)GetWindowLong(hwnd, GWL_USERDATA);
+  switch (uMsg) {
+  case WM_INITDIALOG:
+    SetWindowLong(hwnd, GWL_USERDATA, lParam);
+    return TRUE;
+	case WM_COMMAND:
+	  switch (LOWORD(wParam)) {
+	  case IDCANCEL:
+      if (activePtr)
+        *activePtr = false;
+		  return TRUE;
+		}
+		break;
+	case WM_DESTROY:
+    if (activePtr)
+      *activePtr = false;
+	  return TRUE;
+	}
+	return 0;
+}
+
+
+// Global map, used by ConnectingDialog::Threads to call back to their owning
+// ConnectingDialogs, while coping with the fact that the owner may already have quit.
+static std::map<int, ConnectingDialog*> dialogs;
+static int nextDialogId = 0;
+static Mutex dialogsLock;
+
+
+// ConnectingDialog::Thread
+//   Attempts to connect to the specified host.  If the connection succeeds, the
+//   socket is saved in the owning ConnectingDialog, if still available, and the
+//   event is signalled.  If the connection fails, the Exception text is returned
+//   to the dialog.  If the dialog is already gone, the Exception/socket are discarded.
+//   NB: This thread class cleans itself up on exit - DO NOT join()!
+class ConnectingDialog::Thread : public rfb::Thread {
+public:
+  Thread(int dialogId_, const char* hostAndPort) : dialogId(dialogId_) {
+    setDeleteAfterRun();
+    getHostAndPort(hostAndPort, &host.buf, &port);
+  }
+  virtual void run() {
+    try {
+      returnSock(new network::TcpSocket(host.buf, port));
+    } catch (rdr::Exception& e) {
+      returnException(e);
+    }
+  }
+  void returnSock(network::Socket* s) {
+    Lock l(dialogsLock);
+    if (dialogs.count(dialogId)) {
+      dialogs[dialogId]->newSocket = s;
+      SetEvent(dialogs[dialogId]->readyEvent);
+    } else {
+      delete s;
+    }
+  }
+  void returnException(const rdr::Exception& e) {
+    Lock l(dialogsLock);
+    if (dialogs.count(dialogId)) {
+      dialogs[dialogId]->errMsg.replaceBuf(strDup(e.str()));
+      SetEvent(dialogs[dialogId]->readyEvent);
+    }
+  };
+  CharArray host;
+  int port;
+  int dialogId;
+};
+
+
+ConnectingDialog::ConnectingDialog() : dialog(0), readyEvent(CreateEvent(0, TRUE, FALSE, 0)),
+                                       newSocket(0), dialogId(0) {
+}
+
+network::Socket* ConnectingDialog::connect(const char* hostAndPort) {
+  Thread* connectThread = 0;
+  bool active = true;
+  errMsg.replaceBuf(0);
+  newSocket = 0;
+
+  // Get a unique dialog identifier and create the dialog window
+  {
+    Lock l(dialogsLock);
+    dialogId = ++nextDialogId;
+    dialogs[dialogId] = this;
+    dialog = CreateDialogParam(GetModuleHandle(0),
+      MAKEINTRESOURCE(IDD_CONNECTING_DLG), 0, &ConnectingDlgProc, (long)&active);
+    ShowWindow(dialog, SW_SHOW);
+    ResetEvent(readyEvent);
+  }
+
+  // Create and start the connection thread
+  try {
+    connectThread = new Thread(dialogId, hostAndPort);
+    connectThread->start();
+  } catch (rdr::Exception& e) {
+    errMsg.replaceBuf(strDup(e.str()));
+    active = false;
+  }
+
+  // Process window messages until the connection thread signals readyEvent, or the dialog is cancelled
+  while (active && (MsgWaitForMultipleObjects(1, &readyEvent.h, FALSE, INFINITE, QS_ALLINPUT) == WAIT_OBJECT_0 + 1)) {
+    MSG msg;
+    while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
+      DispatchMessage(&msg);
+  }
+
+  // Remove this dialog from the table
+  //   NB: If the dialog was cancelled then the thread is still running, and will only
+  //       discover that we're gone when it looks up our unique Id in the dialog table.
+  {
+    Lock l(dialogsLock);
+    dialogs.erase(dialogId);
+  }
+
+  // Close the dialog window
+  DestroyWindow(dialog); dialog=0;
+
+  // Throw the exception, if there was one
+  if (errMsg.buf)
+    throw rdr::Exception(errMsg.buf);
+
+  // Otherwise, return the socket
+  //   NB: The socket will be null if the dialog was cancelled
+  return newSocket;
+}
diff --git a/vncviewer/ConnectingDialog.h b/vncviewer/ConnectingDialog.h
index b146ced..c38b3a1 100644
--- a/vncviewer/ConnectingDialog.h
+++ b/vncviewer/ConnectingDialog.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -18,78 +18,48 @@
 
 // -=- ConnectingDialog.h
 
-// Dialog to indicate to the user that the viewer is attempting to make an
-// outgoing connection.
+// ConnectingDialog instances are used to display a status dialog while a
+// connection attempt is in progress.  The connection attempt is performed
+// in a background thread by the ConnectingDialog, to allow the status dialog
+// to remain interactive.  If the dialog is cancelled then it will close and
+// the connection dialog will eventually tidy itself up.
 
 #ifndef __RFB_WIN32_CONNECTING_DLG_H__
 #define __RFB_WIN32_CONNECTING_DLG_H__
 
-#include <rfb_win32/Dialog.h>
-#include <rfb/Threading.h>
-#include <vncviewer/resource.h>
+#include <windows.h>
+#include <network/Socket.h>
+#include <rfb/util.h>
+#include <rfb_win32/Handle.h>
 
 namespace rfb {
 
   namespace win32 {
 
-    BOOL CALLBACK ConnectingDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
-	    switch (uMsg) {
-	    case WM_INITDIALOG:
-		    {
-			    SetWindowLong(hwnd, GWL_USERDATA, lParam);
-			    return TRUE;
-		    }
-	    case WM_COMMAND:
-		    switch (LOWORD(wParam)) {
-		    case IDCANCEL:
-          network::Socket* sock = (network::Socket*) GetWindowLong(hwnd, GWL_USERDATA);
-          sock->shutdown();
-			    EndDialog(hwnd, FALSE);
-			    return TRUE;
-		    }
-		    break;
-	    case WM_DESTROY:
-		    EndDialog(hwnd, TRUE);
-		    return TRUE;
-	    }
-	    return 0;
-    }
-
-    // *** hacky bit - should use async connect so dialog behaves properly
-    class ConnectingDialog : public Thread {
+    class ConnectingDialog {
     public:
-      ConnectingDialog() : Thread("ConnectingDialog") {
-        dialog = 0;
-        active = true;
-        start();
-      }
-      virtual ~ConnectingDialog() {
-        // *** join() required here because otherwise ~Thread calls Thread::join()
-        join();
-      }
-      virtual void run() {
-        dialog = CreateDialogParam(GetModuleHandle(0),
-          MAKEINTRESOURCE(IDD_CONNECTING_DLG), 0, &ConnectingDlgProc, 0);
-        ShowWindow(dialog, SW_SHOW);
-        MSG msg;
-        while (active && GetMessage(&msg, dialog, 0, 0)) {
-          DispatchMessage(&msg);
-        }
-        DestroyWindow(dialog);
-      }
-      virtual Thread* join() {
-        active = false;
-        if (dialog)
-          PostMessage(dialog, WM_QUIT, 0, 0);
-        return Thread::join();
-      }
+      ConnectingDialog();
+
+      // connect
+      //   Show a Connecting dialog and attempt to connect to the specified host
+      //   in the background.
+      //   If the connection succeeds then the Socket is returned.
+      //   If an error occurs, an Exception is thrown.
+      //   If the dialog is cancelled then null is returned.
+      network::Socket* connect(const char* hostAndPort);
     protected:
       HWND dialog;
-      bool active;
+      network::Socket* newSocket;
+      CharArray errMsg;
+      Handle readyEvent;
+      int dialogId;
+
+      class Thread;
+      friend class Thread;
     };
 
   };
 
 };
 
-#endif
\ No newline at end of file
+#endif
diff --git a/vncviewer/ConnectionDialog.cxx b/vncviewer/ConnectionDialog.cxx
index c083444..e7c6b0a 100644
--- a/vncviewer/ConnectionDialog.cxx
+++ b/vncviewer/ConnectionDialog.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -15,9 +15,11 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
  * USA.
  */
+
 #include <vncviewer/ConnectionDialog.h>
-#include <vncviewer/CView.h>
+#include <vncviewer/CConn.h>
 #include <vncviewer/resource.h>
+#include <rfb_win32/AboutDialog.h>
 
 #include <tchar.h>
 
@@ -25,7 +27,7 @@
 using namespace rfb::win32;
 
 
-ConnectionDialog::ConnectionDialog(CView* view_) : Dialog(GetModuleHandle(0)), view(view_) {
+ConnectionDialog::ConnectionDialog(CConn* conn_) : Dialog(GetModuleHandle(0)), conn(conn_) {
 }
 
 
@@ -47,6 +49,13 @@
 
   // Select the first item in the list
   SendMessage(box, CB_SETCURSEL, 0, 0);
+
+  // Fill out the Security: drop-down and select the preferred option
+  HWND security = GetDlgItem(handle, IDC_SECURITY_LEVEL);
+  LRESULT n = SendMessage(security, CB_ADDSTRING, 0, (LPARAM)_T("Always Off"));
+  if (n != CB_ERR)
+    SendMessage(security, CB_SETCURSEL, n, 0);
+  enableItem(IDC_SECURITY_LEVEL, false);
 }
 
 
@@ -63,7 +72,7 @@
     AboutDialog::instance.showDialog();
     return true;
   case IDC_OPTIONS:
-    view->optionsDialog.showDialog(view);
+    conn->showOptionsDialog();
     return true;
   };
   return false;
diff --git a/vncviewer/ConnectionDialog.h b/vncviewer/ConnectionDialog.h
index 554c86f..f739280 100644
--- a/vncviewer/ConnectionDialog.h
+++ b/vncviewer/ConnectionDialog.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -31,18 +31,18 @@
 
   namespace win32 {
 
-    class CView;
+    class CConn;
 
     class ConnectionDialog : Dialog {
     public:
-      ConnectionDialog(CView* view);
+      ConnectionDialog(CConn* view);
       virtual bool showDialog();
       virtual void initDialog();
       virtual bool onOk();
       virtual bool onCommand(int id, int cmd);
       TCharArray hostname;
     protected:
-      CView* view;
+      CConn* conn;
     };
 
   };
diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx
new file mode 100644
index 0000000..eee55ff
--- /dev/null
+++ b/vncviewer/DesktopWindow.cxx
@@ -0,0 +1,1092 @@
+/* Copyright (C) 2002-2005 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 <windows.h>
+#include <commctrl.h>
+#include <rfb/Configuration.h>
+#include <rfb/LogWriter.h>
+#include <rfb_win32/WMShatter.h>
+#include <rfb_win32/LowLevelKeyEvents.h>
+#include <rfb_win32/MonitorInfo.h>
+#include <rfb_win32/DeviceContext.h>
+#include <rfb_win32/Win32Util.h>
+#include <vncviewer/DesktopWindow.h>
+#include <vncviewer/resource.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+
+// - Statics & consts
+
+static LogWriter vlog("DesktopWindow");
+
+const int TIMER_BUMPSCROLL = 1;
+const int TIMER_POINTER_INTERVAL = 2;
+const int TIMER_POINTER_3BUTTON = 3;
+
+
+//
+// -=- DesktopWindowClass
+
+//
+// Window class used as the basis for all DesktopWindow instances
+//
+
+class DesktopWindowClass {
+public:
+  DesktopWindowClass();
+  ~DesktopWindowClass();
+  ATOM classAtom;
+  HINSTANCE instance;
+};
+
+LRESULT CALLBACK DesktopWindowProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
+  LRESULT result;
+  if (msg == WM_CREATE)
+    SetWindowLong(wnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams);
+  else if (msg == WM_DESTROY)
+    SetWindowLong(wnd, GWL_USERDATA, 0);
+  DesktopWindow* _this = (DesktopWindow*) GetWindowLong(wnd, GWL_USERDATA);
+  if (!_this) {
+    vlog.info("null _this in %x, message %u", wnd, msg);
+    return rfb::win32::SafeDefWindowProc(wnd, msg, wParam, lParam);
+  }
+
+  try {
+    result = _this->processMessage(msg, wParam, lParam);
+  } catch (rdr::Exception& e) {
+    vlog.error("untrapped: %s", e.str());
+  }
+
+  return result;
+};
+
+static HCURSOR dotCursor = (HCURSOR)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDC_DOT_CURSOR), IMAGE_CURSOR, 0, 0, LR_SHARED);
+static HCURSOR arrowCursor = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED); 
+
+DesktopWindowClass::DesktopWindowClass() : classAtom(0) {
+  WNDCLASS wndClass;
+  wndClass.style = 0;
+  wndClass.lpfnWndProc = DesktopWindowProc;
+  wndClass.cbClsExtra = 0;
+  wndClass.cbWndExtra = 0;
+  wndClass.hInstance = instance = GetModuleHandle(0);
+  wndClass.hIcon = (HICON)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 0, 0, LR_SHARED);
+  if (!wndClass.hIcon)
+    printf("unable to load icon:%ld", GetLastError());
+  wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
+  wndClass.hbrBackground = NULL;
+  wndClass.lpszMenuName = 0;
+  wndClass.lpszClassName = _T("rfb::win32::DesktopWindowClass");
+  classAtom = RegisterClass(&wndClass);
+  if (!classAtom) {
+    throw rdr::SystemException("unable to register DesktopWindow window class", GetLastError());
+  }
+}
+
+DesktopWindowClass::~DesktopWindowClass() {
+  if (classAtom) {
+    UnregisterClass((const TCHAR*)classAtom, instance);
+  }
+}
+
+DesktopWindowClass baseClass;
+
+//
+// -=- FrameClass
+
+//
+// Window class used for child windows that display pixel data
+//
+
+class FrameClass {
+public:
+  FrameClass();
+  ~FrameClass();
+  ATOM classAtom;
+  HINSTANCE instance;
+};
+
+LRESULT CALLBACK FrameProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
+  LRESULT result;
+  if (msg == WM_CREATE)
+    SetWindowLong(wnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams);
+  else if (msg == WM_DESTROY)
+    SetWindowLong(wnd, GWL_USERDATA, 0);
+  DesktopWindow* _this = (DesktopWindow*) GetWindowLong(wnd, GWL_USERDATA);
+  if (!_this) {
+    vlog.info("null _this in %x, message %u", wnd, msg);
+    return rfb::win32::SafeDefWindowProc(wnd, msg, wParam, lParam);
+  }
+
+  try {
+    result = _this->processFrameMessage(msg, wParam, lParam);
+  } catch (rdr::Exception& e) {
+    vlog.error("untrapped: %s", e.str());
+  }
+
+  return result;
+}
+
+FrameClass::FrameClass() : classAtom(0) {
+  WNDCLASS wndClass;
+  wndClass.style = 0;
+  wndClass.lpfnWndProc = FrameProc;
+  wndClass.cbClsExtra = 0;
+  wndClass.cbWndExtra = 0;
+  wndClass.hInstance = instance = GetModuleHandle(0);
+  wndClass.hIcon = 0;
+  wndClass.hCursor = NULL;
+  wndClass.hbrBackground = NULL;
+  wndClass.lpszMenuName = 0;
+  wndClass.lpszClassName = _T("rfb::win32::FrameClass");
+  classAtom = RegisterClass(&wndClass);
+  if (!classAtom) {
+    throw rdr::SystemException("unable to register Frame window class", GetLastError());
+  }
+}
+
+FrameClass::~FrameClass() {
+  if (classAtom) {
+    UnregisterClass((const TCHAR*)classAtom, instance);
+  }
+}
+
+FrameClass frameClass;
+
+
+//
+// -=- DesktopWindow instance implementation
+//
+
+DesktopWindow::DesktopWindow(Callback* cb) 
+  : buffer(0),
+    showToolbar(false),
+    client_size(0, 0, 16, 16), window_size(0, 0, 32, 32),
+    cursorVisible(false), cursorAvailable(false), cursorInBuffer(false),
+    systemCursorVisible(true), trackingMouseLeave(false),
+    handle(0), frameHandle(0), has_focus(false), palette_changed(false),
+    fullscreenActive(false), fullscreenRestore(false),
+    bumpScroll(false), callback(cb) {
+
+  // Create the window
+  const char* name = "DesktopWindow";
+  handle = CreateWindow((const TCHAR*)baseClass.classAtom, TStr(name),
+    WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
+    0, 0, 10, 10, 0, 0, baseClass.instance, this);
+  if (!handle)
+    throw rdr::SystemException("unable to create WMNotifier window instance", GetLastError());
+  vlog.debug("created window \"%s\" (%x)", name, handle);
+
+  // Create the toolbar
+  tb.create(handle);
+  vlog.debug("created toolbar window \"%s\" (%x)", "ViewerToolBar", tb.getHandle());
+
+  // Create the frame window
+  frameHandle = CreateWindowEx(WS_EX_CLIENTEDGE, (const TCHAR*)frameClass.classAtom,
+    0, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT,
+    CW_USEDEFAULT, CW_USEDEFAULT, handle, 0, frameClass.instance, this);
+  if (!frameHandle) {
+    throw rdr::SystemException("unable to create rfb frame window instance", GetLastError());
+  }
+  vlog.debug("created window \"%s\" (%x)", "Frame Window", frameHandle);
+
+  // Initialise the CPointer pointer handler
+  ptr.setHWND(frameHandle);
+  ptr.setIntervalTimerId(TIMER_POINTER_INTERVAL);
+  ptr.set3ButtonTimerId(TIMER_POINTER_3BUTTON);
+
+  // Initialise the bumpscroll timer
+  bumpScrollTimer.setHWND(handle);
+  bumpScrollTimer.setId(TIMER_BUMPSCROLL);
+
+  // Hook the clipboard
+  clipboard.setNotifier(this);
+
+  // Create the backing buffer
+  buffer = new win32::DIBSectionBuffer(frameHandle);
+
+  // Show the window
+  centerWindow(handle, 0);
+  ShowWindow(handle, SW_SHOW);
+}
+
+DesktopWindow::~DesktopWindow() {
+  vlog.debug("~DesktopWindow");
+  showSystemCursor();
+  if (handle) {
+    disableLowLevelKeyEvents(handle);
+    DestroyWindow(handle);
+    handle = 0;
+  }
+  delete buffer;
+  vlog.debug("~DesktopWindow done");
+}
+
+
+void DesktopWindow::setFullscreen(bool fs) {
+  if (fs && !fullscreenActive) {
+    fullscreenActive = bumpScroll = true;
+
+    // Un-minimize the window if required
+    if (GetWindowLong(handle, GWL_STYLE) & WS_MINIMIZE)
+      ShowWindow(handle, SW_RESTORE);
+
+    // Save the current window position
+    GetWindowRect(handle, &fullscreenOldRect);
+
+    // Find the size of the display the window is on
+    MonitorInfo mi(handle);
+
+    // Hide the toolbar
+    if (tb.isVisible())
+      tb.hide();
+    SetWindowLong(frameHandle, GWL_EXSTYLE, 0);
+
+    // Set the window full-screen
+    DWORD flags = GetWindowLong(handle, GWL_STYLE);
+    fullscreenOldFlags = flags;
+    flags = flags & ~(WS_CAPTION | WS_THICKFRAME | WS_MAXIMIZE | WS_MINIMIZE);
+    vlog.debug("flags=%x", flags);
+
+    SetWindowLong(handle, GWL_STYLE, flags);
+    SetWindowPos(handle, HWND_TOP, mi.rcMonitor.left, mi.rcMonitor.top,
+      mi.rcMonitor.right-mi.rcMonitor.left,
+      mi.rcMonitor.bottom-mi.rcMonitor.top,
+      SWP_FRAMECHANGED);
+  } else if (!fs && fullscreenActive) {
+    fullscreenActive = bumpScroll = false;
+
+    // Show the toolbar
+    if (showToolbar)
+      tb.show();
+    SetWindowLong(frameHandle, GWL_EXSTYLE, WS_EX_CLIENTEDGE);
+
+    // Set the window non-fullscreen
+    SetWindowLong(handle, GWL_STYLE, fullscreenOldFlags);
+
+    // Set the window position
+    SetWindowPos(handle, HWND_NOTOPMOST,
+      fullscreenOldRect.left, fullscreenOldRect.top,
+      fullscreenOldRect.right - fullscreenOldRect.left, 
+      fullscreenOldRect.bottom - fullscreenOldRect.top,
+      SWP_FRAMECHANGED);
+  }
+
+  // Adjust the viewport offset to cope with change in size between FS
+  // and previous window state.
+  setViewportOffset(scrolloffset);
+}
+
+void DesktopWindow::setShowToolbar(bool st)
+{
+  showToolbar = st;
+
+  if (showToolbar && !tb.isVisible()) {
+    tb.show();
+  } else if (!showToolbar && tb.isVisible()) {
+    tb.hide();
+  }
+}
+
+void DesktopWindow::setDisableWinKeys(bool dwk) {
+  // Enable low-level event hooking, so we get special keys directly
+  if (dwk)
+    enableLowLevelKeyEvents(handle);
+  else
+    disableLowLevelKeyEvents(handle);
+}
+
+
+void DesktopWindow::setMonitor(const char* monitor) {
+  MonitorInfo mi(monitor);
+  mi.moveTo(handle);
+}
+
+char* DesktopWindow::getMonitor() const {
+  MonitorInfo mi(handle);
+  return strDup(mi.szDevice);
+}
+
+
+bool DesktopWindow::setViewportOffset(const Point& tl) {
+  Point np = Point(max(0, min(tl.x, buffer->width()-client_size.width())),
+    max(0, min(tl.y, buffer->height()-client_size.height())));
+  Point delta = np.translate(scrolloffset.negate());
+  if (!np.equals(scrolloffset)) {
+    scrolloffset = np;
+    ScrollWindowEx(frameHandle, -delta.x, -delta.y, 0, 0, 0, 0, SW_INVALIDATE);
+    UpdateWindow(frameHandle);
+    return true;
+  }
+  return false;
+}
+
+
+bool DesktopWindow::processBumpScroll(const Point& pos)
+{
+  if (!bumpScroll) return false;
+  int bumpScrollPixels = 20;
+  bumpScrollDelta = Point();
+
+  if (pos.x == client_size.width()-1)
+    bumpScrollDelta.x = bumpScrollPixels;
+  else if (pos.x == 0)
+    bumpScrollDelta.x = -bumpScrollPixels;
+  if (pos.y == client_size.height()-1)
+    bumpScrollDelta.y = bumpScrollPixels;
+  else if (pos.y == 0)
+    bumpScrollDelta.y = -bumpScrollPixels;
+
+  if (bumpScrollDelta.x || bumpScrollDelta.y) {
+    if (bumpScrollTimer.isActive()) return true;
+    if (setViewportOffset(scrolloffset.translate(bumpScrollDelta))) {
+      bumpScrollTimer.start(25);
+      return true;
+    }
+  }
+
+  bumpScrollTimer.stop();
+  return false;
+}
+
+
+LRESULT
+DesktopWindow::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+  switch (msg) {
+
+    // -=- Process standard window messages
+
+  case WM_NOTIFY:
+    if (wParam == ID_TOOLBAR)
+      tb.processWM_NOTIFY(wParam, lParam);
+    break;
+
+  case WM_DISPLAYCHANGE:
+    // Display format has changed - notify callback
+    callback->displayChanged();
+    break;
+
+    // -=- Window position
+
+    // Prevent the window from being resized to be too large if in normal mode.
+    // If maximized or fullscreen the allow oversized windows.
+
+  case WM_WINDOWPOSCHANGING:
+    {
+      WINDOWPOS* wpos = (WINDOWPOS*)lParam;
+      if (wpos->flags & SWP_NOSIZE)
+        break;
+
+      // Work out how big the window should ideally be
+      DWORD current_style = GetWindowLong(frameHandle, GWL_STYLE);
+      DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL);
+      DWORD style_ex = GetWindowLong(frameHandle, GWL_EXSTYLE);
+
+      RECT r;
+      SetRect(&r, 0, 0, buffer->width(), buffer->height());
+      AdjustWindowRectEx(&r, style, FALSE, style_ex);
+      Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom);
+      if (current_style & WS_VSCROLL)
+        reqd_size.br.x += GetSystemMetrics(SM_CXVSCROLL);
+      if (current_style & WS_HSCROLL)
+        reqd_size.br.y += GetSystemMetrics(SM_CXHSCROLL);
+
+      SetRect(&r, reqd_size.tl.x, reqd_size.tl.y, reqd_size.br.x, reqd_size.br.y);
+      if (tb.isVisible())
+        r.bottom += tb.getHeight();
+      AdjustWindowRect(&r, GetWindowLong(handle, GWL_STYLE), FALSE);
+      reqd_size = Rect(r.left, r.top, r.right, r.bottom);
+
+      RECT current;
+      GetWindowRect(handle, &current);
+
+      if (!(GetWindowLong(handle, GWL_STYLE) & WS_MAXIMIZE) && !fullscreenActive) {
+        // Ensure that the window isn't resized too large
+        if (wpos->cx > reqd_size.width()) {
+          wpos->cx = reqd_size.width();
+          wpos->x = current.left;
+        }
+        if (wpos->cy > reqd_size.height()) {
+          wpos->cy = reqd_size.height();
+          wpos->y = current.top;
+        }
+      }
+    }
+    break;
+
+    // Resize child windows and update window size info we have cached.
+
+  case WM_SIZE:
+    {
+      Point old_offset = desktopToClient(Point(0, 0));
+      RECT r;
+
+      // Resize child windows
+      GetClientRect(handle, &r);
+      if (tb.isVisible()) {
+        MoveWindow(frameHandle, 0, tb.getHeight(),
+                   r.right, r.bottom - tb.getHeight(), TRUE);
+      } else {
+        MoveWindow(frameHandle, 0, 0, r.right, r.bottom, TRUE);
+      }
+      tb.autoSize();
+ 
+      // Update the cached sizing information
+      GetWindowRect(frameHandle, &r);
+      window_size = Rect(r.left, r.top, r.right, r.bottom);
+      GetClientRect(frameHandle, &r);
+      client_size = Rect(r.left, r.top, r.right, r.bottom);
+
+      // Determine whether scrollbars are required
+      calculateScrollBars();
+
+      // Redraw if required
+      if ((!old_offset.equals(desktopToClient(Point(0, 0)))))
+        InvalidateRect(frameHandle, 0, TRUE);
+    }
+    break;
+
+    // -=- Bump-scrolling
+
+  case WM_TIMER:
+    switch (wParam) {
+    case TIMER_BUMPSCROLL:
+      if (!setViewportOffset(scrolloffset.translate(bumpScrollDelta)))
+        bumpScrollTimer.stop();
+      break;
+    case TIMER_POINTER_INTERVAL:
+    case TIMER_POINTER_3BUTTON:
+      ptr.handleTimer(callback, wParam);
+      break;
+    }
+    break;
+
+    // -=- Track whether or not the window has focus
+
+  case WM_SETFOCUS:
+    has_focus = true;
+    break;
+  case WM_KILLFOCUS:
+    has_focus = false;
+    cursorOutsideBuffer();
+    // Restore the keyboard to a consistent state
+    kbd.releaseAllKeys(callback);
+    break;
+
+    // -=- If the menu is about to be shown, make sure it's up to date
+
+  case WM_INITMENU:
+    callback->refreshMenu(true);
+    break;
+
+    // -=- Handle the extra window menu items
+
+    // Pass system menu messages to the callback and only attempt
+    // to process them ourselves if the callback returns false.
+  case WM_SYSCOMMAND:
+    // Call the supplied callback
+    if (callback->sysCommand(wParam, lParam))
+      break;
+
+    // - Not processed by the callback, so process it as a system message
+    switch (wParam & 0xfff0) {
+
+      // When restored, ensure that full-screen mode is re-enabled if required.
+    case SC_RESTORE:
+      {
+      if (GetWindowLong(handle, GWL_STYLE) & WS_MINIMIZE) {
+        rfb::win32::SafeDefWindowProc(handle, msg, wParam, lParam);
+        setFullscreen(fullscreenRestore);
+      }
+      else if (fullscreenActive)
+        setFullscreen(false);
+      else
+        rfb::win32::SafeDefWindowProc(handle, msg, wParam, lParam);
+
+      return 0;
+      }
+
+      // If we are maximized or minimized then that cancels full-screen mode.
+    case SC_MINIMIZE:
+    case SC_MAXIMIZE:
+      fullscreenRestore = fullscreenActive;
+      setFullscreen(false);
+      break;
+
+    }
+    break;
+
+    // Treat all menu commands as system menu commands
+  case WM_COMMAND:
+    SendMessage(handle, WM_SYSCOMMAND, wParam, lParam);
+    return 0;
+
+    // -=- Handle keyboard input
+
+  case WM_KEYUP:
+  case WM_KEYDOWN:
+    // Hook the MenuKey to pop-up the window menu
+    if (menuKey && (wParam == menuKey)) {
+
+      bool ctrlDown = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0;
+      bool altDown = (GetAsyncKeyState(VK_MENU) & 0x8000) != 0;
+      bool shiftDown = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
+      if (!(ctrlDown || altDown || shiftDown)) {
+
+        // If MenuKey is being released then pop-up the menu
+        if ((msg == WM_KEYDOWN)) {
+          // Make sure it's up to date
+          //
+          // NOTE: Here we call refreshMenu only to grey out Move and Size
+          //       menu items. Other things will be refreshed once again
+          //       while processing the WM_INITMENU message.
+          //
+          callback->refreshMenu(false);
+
+          // Show it under the pointer
+          POINT pt;
+          GetCursorPos(&pt);
+          cursorInBuffer = false;
+          TrackPopupMenu(GetSystemMenu(handle, FALSE),
+            TPM_CENTERALIGN | TPM_VCENTERALIGN, pt.x, pt.y, 0, handle, 0);
+        }
+
+        // Ignore the MenuKey keypress for both press & release events
+        return 0;
+      }
+    }
+	case WM_SYSKEYDOWN:
+	case WM_SYSKEYUP:
+    kbd.keyEvent(callback, wParam, lParam, (msg == WM_KEYDOWN) || (msg == WM_SYSKEYDOWN));
+    return 0;
+
+    // -=- Handle the window closing
+
+  case WM_CLOSE:
+    vlog.debug("WM_CLOSE %x", handle);
+    callback->closeWindow();
+    break;
+
+  }
+
+  return rfb::win32::SafeDefWindowProc(handle, msg, wParam, lParam);
+}
+
+LRESULT
+DesktopWindow::processFrameMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+  switch (msg) {
+
+    // -=- Paint the remote frame buffer
+
+  case WM_PAINT:
+    {
+      PAINTSTRUCT ps;
+      HDC paintDC = BeginPaint(frameHandle, &ps);
+      if (!paintDC)
+        throw rdr::SystemException("unable to BeginPaint", GetLastError());
+      Rect pr = Rect(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
+
+      if (!pr.is_empty()) {
+
+        // Draw using the correct palette
+        PaletteSelector pSel(paintDC, windowPalette.getHandle());
+
+        if (buffer->bitmap) {
+          // Update the bitmap's palette
+          if (palette_changed) {
+            palette_changed = false;
+            buffer->refreshPalette();
+          }
+
+          // Get device context
+          BitmapDC bitmapDC(paintDC, buffer->bitmap);
+
+          // Blit the border if required
+          Rect bufpos = desktopToClient(buffer->getRect());
+          if (!pr.enclosed_by(bufpos)) {
+            vlog.debug("draw border");
+            HBRUSH black = (HBRUSH) GetStockObject(BLACK_BRUSH);
+            RECT r;
+            SetRect(&r, 0, 0, bufpos.tl.x, client_size.height()); FillRect(paintDC, &r, black);
+            SetRect(&r, bufpos.tl.x, 0, bufpos.br.x, bufpos.tl.y); FillRect(paintDC, &r, black);
+            SetRect(&r, bufpos.br.x, 0, client_size.width(), client_size.height()); FillRect(paintDC, &r, black);
+            SetRect(&r, bufpos.tl.x, bufpos.br.y, bufpos.br.x, client_size.height()); FillRect(paintDC, &r, black);
+          }
+
+          // Do the blit
+          Point buf_pos = clientToDesktop(pr.tl);
+
+          if (!BitBlt(paintDC, pr.tl.x, pr.tl.y, pr.width(), pr.height(),
+                      bitmapDC, buf_pos.x, buf_pos.y, SRCCOPY))
+            throw rdr::SystemException("unable to BitBlt to window", GetLastError());
+        }
+      }
+
+      EndPaint(frameHandle, &ps);
+
+      // - Notify the callback that a paint message has finished processing
+      callback->paintCompleted();
+    }
+    return 0;
+
+    // -=- Palette management
+
+  case WM_PALETTECHANGED:
+    vlog.debug("WM_PALETTECHANGED");
+    if ((HWND)wParam == frameHandle) {
+      vlog.debug("ignoring");
+      break;
+    }
+  case WM_QUERYNEWPALETTE:
+    vlog.debug("re-selecting palette");
+    {
+      WindowDC wdc(frameHandle);
+      PaletteSelector pSel(wdc, windowPalette.getHandle());
+      if (pSel.isRedrawRequired()) {
+        InvalidateRect(frameHandle, 0, FALSE);
+        UpdateWindow(frameHandle);
+      }
+    }
+    return TRUE;
+
+  case WM_VSCROLL:
+  case WM_HSCROLL: 
+    {
+      Point delta;
+      int newpos = (msg == WM_VSCROLL) ? scrolloffset.y : scrolloffset.x;
+
+      switch (LOWORD(wParam)) {
+      case SB_PAGEUP: newpos -= 50; break;
+      case SB_PAGEDOWN: newpos += 50; break;
+      case SB_LINEUP: newpos -= 5; break;
+      case SB_LINEDOWN: newpos += 5; break;
+      case SB_THUMBTRACK:
+      case SB_THUMBPOSITION: newpos = HIWORD(wParam); break;
+      default: vlog.info("received unknown scroll message");
+      };
+
+      if (msg == WM_HSCROLL)
+        setViewportOffset(Point(newpos, scrolloffset.y));
+      else
+        setViewportOffset(Point(scrolloffset.x, newpos));
+  
+      SCROLLINFO si;
+      si.cbSize = sizeof(si); 
+      si.fMask  = SIF_POS; 
+      si.nPos   = newpos; 
+      SetScrollInfo(frameHandle, (msg == WM_VSCROLL) ? SB_VERT : SB_HORZ, &si, TRUE); 
+    }
+    break;
+
+    // -=- Cursor shape/visibility handling
+
+  case WM_SETCURSOR:
+    if (LOWORD(lParam) != HTCLIENT)
+      break;
+    SetCursor(cursorInBuffer ? dotCursor : arrowCursor);
+    return TRUE;
+
+  case WM_MOUSELEAVE:
+    trackingMouseLeave = false;
+    cursorOutsideBuffer();
+    return 0;
+
+    // -=- Mouse input handling
+
+  case WM_MOUSEMOVE:
+  case WM_LBUTTONUP:
+  case WM_MBUTTONUP:
+  case WM_RBUTTONUP:
+  case WM_LBUTTONDOWN:
+  case WM_MBUTTONDOWN:
+  case WM_RBUTTONDOWN:
+#ifdef WM_MOUSEWHEEL
+  case WM_MOUSEWHEEL:
+#endif
+    if (has_focus)
+    {
+      if (!trackingMouseLeave) {
+        TRACKMOUSEEVENT tme;
+        tme.cbSize = sizeof(TRACKMOUSEEVENT);
+        tme.dwFlags = TME_LEAVE;
+        tme.hwndTrack = frameHandle;
+        _TrackMouseEvent(&tme);
+        trackingMouseLeave = true;
+      }
+      int mask = 0;
+      if (LOWORD(wParam) & MK_LBUTTON) mask |= 1;
+      if (LOWORD(wParam) & MK_MBUTTON) mask |= 2;
+      if (LOWORD(wParam) & MK_RBUTTON) mask |= 4;
+
+#ifdef WM_MOUSEWHEEL
+      if (msg == WM_MOUSEWHEEL) {
+        int delta = (short)HIWORD(wParam);
+        int repeats = (abs(delta)+119) / 120;
+        int wheelMask = (delta > 0) ? 8 : 16;
+        vlog.debug("repeats %d, mask %d\n",repeats,wheelMask);
+        for (int i=0; i<repeats; i++) {
+          ptr.pointerEvent(callback, oldpos, mask | wheelMask);
+          ptr.pointerEvent(callback, oldpos, mask);
+        }
+      } else {
+#endif
+        Point clientPos = Point(LOWORD(lParam), HIWORD(lParam));
+        Point p = clientToDesktop(clientPos);
+
+        // If the mouse is not within the server buffer area, do nothing
+        cursorInBuffer = buffer->getRect().contains(p);
+        if (!cursorInBuffer) {
+          cursorOutsideBuffer();
+          break;
+        }
+
+        // If we're locally rendering the cursor then redraw it
+        if (cursorAvailable) {
+          // - Render the cursor!
+          if (!p.equals(cursorPos)) {
+            hideLocalCursor();
+            cursorPos = p;
+            showLocalCursor();
+            if (cursorVisible)
+              hideSystemCursor();
+          }
+        }
+
+        // If we are doing bump-scrolling then try that first...
+        if (processBumpScroll(clientPos))
+          break;
+
+        // Send a pointer event to the server
+        ptr.pointerEvent(callback, p, mask);
+        oldpos = p;
+#ifdef WM_MOUSEWHEEL
+      }
+#endif
+    } else {
+      cursorOutsideBuffer();
+    }
+    break;
+  }
+
+  return rfb::win32::SafeDefWindowProc(frameHandle, msg, wParam, lParam);
+}
+
+
+void
+DesktopWindow::hideLocalCursor() {
+  // - Blit the cursor backing store over the cursor
+  // *** ALWAYS call this BEFORE changing buffer PF!!!
+  if (cursorVisible) {
+    cursorVisible = false;
+    buffer->imageRect(cursorBackingRect, cursorBacking.data);
+    invalidateDesktopRect(cursorBackingRect);
+  }
+}
+
+void
+DesktopWindow::showLocalCursor() {
+  if (cursorAvailable && !cursorVisible && cursorInBuffer) {
+    if (!buffer->getPF().equal(cursor.getPF()) ||
+      cursor.getRect().is_empty()) {
+      vlog.info("attempting to render invalid local cursor");
+      cursorAvailable = false;
+      showSystemCursor();
+      return;
+    }
+    cursorVisible = true;
+    
+    cursorBackingRect = cursor.getRect().translate(cursorPos).translate(cursor.hotspot.negate());
+    cursorBackingRect = cursorBackingRect.intersect(buffer->getRect());
+    buffer->getImage(cursorBacking.data, cursorBackingRect);
+
+    renderLocalCursor();
+
+    invalidateDesktopRect(cursorBackingRect);
+  }
+}
+
+void DesktopWindow::cursorOutsideBuffer()
+{
+  cursorInBuffer = false;
+  hideLocalCursor();
+  showSystemCursor();
+}
+
+void
+DesktopWindow::renderLocalCursor()
+{
+  Rect r = cursor.getRect();
+  r = r.translate(cursorPos).translate(cursor.hotspot.negate());
+  buffer->maskRect(r, cursor.data, cursor.mask.buf);
+}
+
+void
+DesktopWindow::hideSystemCursor() {
+  if (systemCursorVisible) {
+    vlog.debug("hide system cursor");
+    systemCursorVisible = false;
+    ShowCursor(FALSE);
+  }
+}
+
+void
+DesktopWindow::showSystemCursor() {
+  if (!systemCursorVisible) {
+    vlog.debug("show system cursor");
+    systemCursorVisible = true;
+    ShowCursor(TRUE);
+  }
+}
+
+
+bool
+DesktopWindow::invalidateDesktopRect(const Rect& crect) {
+  Rect rect = desktopToClient(crect);
+  if (rect.intersect(client_size).is_empty()) return false;
+  RECT invalid = {rect.tl.x, rect.tl.y, rect.br.x, rect.br.y};
+  InvalidateRect(frameHandle, &invalid, FALSE);
+  return true;
+}
+
+
+void
+DesktopWindow::notifyClipboardChanged(const char* text, int len) {
+  callback->clientCutText(text, len);
+}
+
+
+void
+DesktopWindow::setPF(const PixelFormat& pf) {
+  // If the cursor is the wrong format then clear it
+  if (!pf.equal(buffer->getPF()))
+    setCursor(0, 0, Point(), 0, 0);
+
+  // Update the desktop buffer
+  buffer->setPF(pf);
+  
+  // Redraw the window
+  InvalidateRect(frameHandle, 0, FALSE);
+}
+
+void
+DesktopWindow::setSize(int w, int h) {
+  vlog.debug("setSize %dx%d", w, h);
+
+  // If the locally-rendered cursor is visible then remove it
+  hideLocalCursor();
+
+  // Resize the backing buffer
+  buffer->setSize(w, h);
+
+  // If the window is not maximised or full-screen then resize it
+  if (!(GetWindowLong(handle, GWL_STYLE) & WS_MAXIMIZE) && !fullscreenActive) {
+    // Resize the window to the required size
+    RECT r = {0, 0, w, h};
+    AdjustWindowRectEx(&r, GetWindowLong(frameHandle, GWL_STYLE), FALSE,
+                       GetWindowLong(frameHandle, GWL_EXSTYLE));
+    if (tb.isVisible())
+      r.bottom += tb.getHeight();
+    AdjustWindowRect(&r, GetWindowLong(handle, GWL_STYLE), FALSE);
+
+    // Resize about the center of the window, and clip to current monitor
+    MonitorInfo mi(handle);
+    resizeWindow(handle, r.right-r.left, r.bottom-r.top);
+    mi.clipTo(handle);
+  } else {
+    // Ensure the screen contents are consistent
+    InvalidateRect(frameHandle, 0, FALSE);
+  }
+
+  // Enable/disable scrollbars as appropriate
+  calculateScrollBars();
+}
+
+void
+DesktopWindow::setCursor(int w, int h, const Point& hotspot, void* data, void* mask) {
+  hideLocalCursor();
+
+  cursor.hotspot = hotspot;
+
+  cursor.setSize(w, h);
+  cursor.setPF(buffer->getPF());
+  cursor.imageRect(cursor.getRect(), data);
+  memcpy(cursor.mask.buf, mask, cursor.maskLen());
+  cursor.crop();
+
+  cursorBacking.setSize(w, h);
+  cursorBacking.setPF(buffer->getPF());
+
+  cursorAvailable = true;
+
+  showLocalCursor();
+}
+
+PixelFormat
+DesktopWindow::getNativePF() const {
+  vlog.debug("getNativePF()");
+  return WindowDC(handle).getPF();
+}
+
+
+void
+DesktopWindow::refreshWindowPalette(int start, int count) {
+  vlog.debug("refreshWindowPalette(%d, %d)", start, count);
+
+  Colour colours[256];
+  if (count > 256) {
+    vlog.debug("%d palette entries", count);
+    throw rdr::Exception("too many palette entries");
+  }
+
+  // Copy the palette from the DIBSectionBuffer
+  ColourMap* cm = buffer->getColourMap();
+  if (!cm) return;
+  for (int i=0; i<count; i++) {
+    int r, g, b;
+    cm->lookup(i, &r, &g, &b);
+    colours[i].r = r;
+    colours[i].g = g;
+    colours[i].b = b;
+  }
+
+  // Set the window palette
+  windowPalette.setEntries(start, count, colours);
+
+  // Cause the window to be redrawn
+  palette_changed = true;
+  InvalidateRect(handle, 0, FALSE);
+}
+
+
+void DesktopWindow::calculateScrollBars() {
+  // Calculate the required size of window
+  DWORD current_style = GetWindowLong(frameHandle, GWL_STYLE);
+  DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL);
+  DWORD style_ex = GetWindowLong(frameHandle, GWL_EXSTYLE);
+  DWORD old_style;
+  RECT r;
+  SetRect(&r, 0, 0, buffer->width(), buffer->height());
+  AdjustWindowRectEx(&r, style, FALSE, style_ex);
+  Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom);
+
+  if (!bumpScroll) {
+    // We only enable scrollbars if bump-scrolling is not active.
+    // Effectively, this means if full-screen is not active,
+    // but I think it's better to make these things explicit.
+    
+    // Work out whether scroll bars are required
+    do {
+      old_style = style;
+
+      if (!(style & WS_HSCROLL) && (reqd_size.width() > window_size.width())) {
+        style |= WS_HSCROLL;
+        reqd_size.br.y += GetSystemMetrics(SM_CXHSCROLL);
+      }
+      if (!(style & WS_VSCROLL) && (reqd_size.height() > window_size.height())) {
+        style |= WS_VSCROLL;
+        reqd_size.br.x += GetSystemMetrics(SM_CXVSCROLL);
+      }
+    } while (style != old_style);
+  }
+
+  // Tell Windows to update the window style & cached settings
+  if (style != current_style) {
+    SetWindowLong(frameHandle, GWL_STYLE, style);
+    SetWindowPos(frameHandle, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+  }
+
+  // Update the scroll settings
+  SCROLLINFO si;
+  if (style & WS_VSCROLL) {
+    si.cbSize = sizeof(si); 
+    si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS; 
+    si.nMin   = 0; 
+    si.nMax   = buffer->height(); 
+    si.nPage  = buffer->height() - (reqd_size.height() - window_size.height()); 
+    maxscrolloffset.y = max(0, si.nMax-si.nPage);
+    scrolloffset.y = min(maxscrolloffset.y, scrolloffset.y);
+    si.nPos   = scrolloffset.y;
+    SetScrollInfo(frameHandle, SB_VERT, &si, TRUE);
+  }
+  if (style & WS_HSCROLL) {
+    si.cbSize = sizeof(si); 
+    si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS; 
+    si.nMin   = 0;
+    si.nMax   = buffer->width(); 
+    si.nPage  = buffer->width() - (reqd_size.width() - window_size.width()); 
+    maxscrolloffset.x = max(0, si.nMax-si.nPage);
+    scrolloffset.x = min(maxscrolloffset.x, scrolloffset.x);
+    si.nPos   = scrolloffset.x;
+    SetScrollInfo(frameHandle, SB_HORZ, &si, TRUE);
+  }
+
+  // Update the cached client size
+  GetClientRect(frameHandle, &r);
+  client_size = Rect(r.left, r.top, r.right, r.bottom);
+}
+
+
+void
+DesktopWindow::setName(const char* name) {
+  SetWindowText(handle, TStr(name));
+}
+
+
+void
+DesktopWindow::serverCutText(const char* str, int len) {
+  CharArray t(len+1);
+  memcpy(t.buf, str, len);
+  t.buf[len] = 0;
+  clipboard.setClipText(t.buf);
+}
+
+
+void DesktopWindow::fillRect(const Rect& r, Pixel pix) {
+  if (cursorBackingRect.overlaps(r)) hideLocalCursor();
+  buffer->fillRect(r, pix);
+  invalidateDesktopRect(r);
+}
+void DesktopWindow::imageRect(const Rect& r, void* pixels) {
+  if (cursorBackingRect.overlaps(r)) hideLocalCursor();
+  buffer->imageRect(r, pixels);
+  invalidateDesktopRect(r);
+}
+void DesktopWindow::copyRect(const Rect& r, int srcX, int srcY) {
+  if (cursorBackingRect.overlaps(r) ||
+      cursorBackingRect.overlaps(Rect(srcX, srcY, srcX+r.width(), srcY+r.height())))
+    hideLocalCursor();
+  buffer->copyRect(r, Point(r.tl.x-srcX, r.tl.y-srcY));
+  invalidateDesktopRect(r);
+}
+
+void DesktopWindow::invertRect(const Rect& r) {
+  int stride;
+  rdr::U8* p = buffer->getPixelsRW(r, &stride);
+  for (int y = 0; y < r.height(); y++) {
+    for (int x = 0; x < r.width(); x++) {
+      switch (buffer->getPF().bpp) {
+      case 8:  ((rdr::U8* )p)[x+y*stride] ^= 0xff;       break;
+      case 16: ((rdr::U16*)p)[x+y*stride] ^= 0xffff;     break;
+      case 32: ((rdr::U32*)p)[x+y*stride] ^= 0xffffffff; break;
+      }
+    }
+  }
+  invalidateDesktopRect(r);
+}
diff --git a/vncviewer/DesktopWindow.h b/vncviewer/DesktopWindow.h
new file mode 100644
index 0000000..d54cd5f
--- /dev/null
+++ b/vncviewer/DesktopWindow.h
@@ -0,0 +1,254 @@
+/* Copyright (C) 2002-2005 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.
+ */
+
+// -=- DesktopWindow.h
+
+// Each VNC connection instance (CConn) creates a DesktopWindow the
+// server initialisation message has been received.  The CConn is
+// responsible for all RFB-specific and network issues.  The
+// DesktopWindow is responsible for all GUI management issues.
+//
+// DesktopWindow provides a FullFramePixelBuffer interface for the
+// CConn to render updates into.  It also requires a callback object
+// to be supplied, which will be notified when various events occur.
+
+#ifndef __RFB_WIN32_DESKTOP_WINDOW_H__
+#define __RFB_WIN32_DESKTOP_WINDOW_H__
+
+#include <rfb/Cursor.h>
+#include <rfb_win32/CKeyboard.h>
+#include <rfb_win32/CPointer.h>
+#include <rfb_win32/Clipboard.h>
+#include <rfb_win32/DIBSectionBuffer.h>
+#include <rfb_win32/LogicalPalette.h>
+#include <vncviewer/ViewerToolBar.h>
+
+
+namespace rfb {
+
+  namespace win32 {
+
+    class DesktopWindow : rfb::win32::Clipboard::Notifier {
+    public:
+      class Callback;
+
+      DesktopWindow(Callback* cb_);
+      ~DesktopWindow();
+
+      // - Window message handling procedure
+      LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+      // - Window message handling procedure for the framebuffer window
+      LRESULT processFrameMessage(UINT msg, WPARAM wParam, LPARAM lParam);
+
+      // - Determine the native pixel format of the window
+      //   This can (and often will) differ from the PixelBuffer format
+      PixelFormat getNativePF() const;
+
+      // - Get the underlying window handle
+      //   This is used by F8Menu to modify the window's menu
+      HWND getHandle() const {return handle;}
+
+      // - Get the framebuffer window handle
+      HWND getFrameHandle() const {return frameHandle;}
+
+      // - Set the window title
+      void setName(const char* name);
+
+      // - Set the key that causes the system/F8 menu to be displayed
+      void setMenuKey(rdr::U8 key) { menuKey = key; }
+
+      // - Pointer event handling
+      void setEmulate3(bool em3) { ptr.enableEmulate3(em3); }
+      void setPointerEventInterval(int interval) { ptr.enableInterval(interval); }
+
+      // - Set the pixel format, size etc of the underlying PixelBuffer
+      void setPF(const PixelFormat& pf);
+      PixelFormat getPF() const { return buffer->getPF(); }
+      void setSize(int w, int h);
+      void setColour(int i, int r, int g, int b) {buffer->setColour(i, r, g, b);}
+
+      // - Set the cursor to render when the pointer is within the desktop buffer
+      void setCursor(int w, int h, const Point& hotspot, void* data, void* mask);
+      void showCursor() { showLocalCursor(); }
+
+      // - Set the window fullscreen / determine whether it is fullscreen
+      void setFullscreen(bool fs);
+      bool isFullscreen() { return fullscreenActive; }
+
+      // - Set/get the toolbar's state
+      void setShowToolbar(bool st);
+      bool isToolbarEnabled() { return showToolbar; }
+
+      // - Set whether to disable special Windows keys & pass them straight to server
+      void setDisableWinKeys(bool dwk);
+
+      // - Set/get which monitor the window should be displayed on
+      void setMonitor(const char* monitor);
+      char* getMonitor() const;
+
+      // - Set the local clipboard
+      void serverCutText(const char* str, int len);
+
+      // - Draw into the desktop buffer & update the window
+      void fillRect(const Rect& r, Pixel pix);
+      void imageRect(const Rect& r, void* pixels);
+      void copyRect(const Rect& r, int srcX, int srcY);
+
+      void invertRect(const Rect& r);
+
+      // - Update the window palette if the display is palette-based.
+      //   Colours are pulled from the desktop buffer's ColourMap.
+      //   Only the specified range of indexes is dealt with.
+      //   After the update, the entire window is redrawn.
+      void refreshWindowPalette(int start, int count);
+
+      // Clipboard::Notifier interface
+      void notifyClipboardChanged(const char* text, int len);
+
+      // DesktopWindow Callback interface
+      class Callback : public InputHandler {
+      public:
+        virtual ~Callback() {}
+        virtual void displayChanged() = 0;
+        virtual void paintCompleted() = 0;
+        virtual bool sysCommand(WPARAM wParam, LPARAM lParam) = 0;
+        virtual void closeWindow() = 0;
+        virtual void refreshMenu(bool enableSysItems) = 0;
+      };
+
+      // Currently accessible so that the CConn can releaseAllKeys & check
+      // whether Ctrl and Alt are down...
+      rfb::win32::CKeyboard kbd;
+
+    protected:
+      // Routines to convert between Desktop and client (window) coordinates
+      Point desktopToClient(const Point& p) {
+        Point pos = p;
+        if (client_size.width() > buffer->width())
+          pos.x += (client_size.width() - buffer->width()) / 2;
+        else if (client_size.width() < buffer->width())
+          pos.x -= scrolloffset.x;
+        if (client_size.height() > buffer->height())
+          pos.y += (client_size.height() - buffer->height()) / 2;
+        else if (client_size.height() < buffer->height())
+          pos.y -= scrolloffset.y;
+        return pos;
+      }
+      Rect desktopToClient(const Rect& r) {
+        return Rect(desktopToClient(r.tl), desktopToClient(r.br));
+      }
+      Point clientToDesktop(const Point& p) {
+        Point pos = p;
+        if (client_size.width() > buffer->width())
+          pos.x -= (client_size.width() - buffer->width()) / 2;
+        else if (client_size.width() < buffer->width())
+          pos.x += scrolloffset.x;
+        if (client_size.height() > buffer->height())
+          pos.y -= (client_size.height() - buffer->height()) / 2;
+        else if (client_size.height() < buffer->height())
+          pos.y += scrolloffset.y;
+        return pos;
+      }
+      Rect clientToDesktop(const Rect& r) {
+        return Rect(clientToDesktop(r.tl), clientToDesktop(r.br));
+      }
+
+      // Internal routine used by the scrollbars & bump scroller to select
+      // the portion of the Desktop to display
+      bool setViewportOffset(const Point& tl);
+    
+      // Bump scroll handling.  Bump scrolling is used if the window is
+      // in fullscreen mode and the Desktop is larger than the window
+      bool processBumpScroll(const Point& cursorPos);
+      void setBumpScroll(bool on);
+      bool bumpScroll;
+      Point bumpScrollDelta;
+      IntervalTimer bumpScrollTimer;
+
+      // Locally-rendered VNC cursor
+      void hideLocalCursor();
+      void showLocalCursor();
+      void renderLocalCursor();
+
+      // The system-rendered cursor
+      void hideSystemCursor();
+      void showSystemCursor();
+
+      // cursorOutsideBuffer() is called whenever we detect that the mouse has
+      // moved outside the desktop.  It restores the system arrow cursor.
+      void cursorOutsideBuffer();
+
+      // Returns true if part of the supplied rect is visible, false otherwise
+      bool invalidateDesktopRect(const Rect& crect);
+
+      // Determine whether or not we need to enable/disable scrollbars and set the
+      // window style accordingly
+      void calculateScrollBars();
+
+      // Win32-specific input handling
+      rfb::win32::CPointer ptr;
+      Point oldpos;
+      rfb::win32::Clipboard clipboard;
+
+      // Palette handling
+      LogicalPalette windowPalette;
+      bool palette_changed;
+
+      // - Full-screen mode
+      RECT fullscreenOldRect;
+      DWORD fullscreenOldFlags;
+      bool fullscreenActive;
+      bool fullscreenRestore;
+
+      // Cursor handling
+      Cursor cursor;
+      bool systemCursorVisible;  // Should system-cursor be drawn?
+      bool trackingMouseLeave;
+      bool cursorInBuffer;    // Is cursor position within server buffer? (ONLY for LocalCursor)
+      bool cursorVisible;     // Is cursor currently rendered?
+      bool cursorAvailable;   // Is cursor available for rendering?
+      Point cursorPos;
+      ManagedPixelBuffer cursorBacking;
+      Rect cursorBackingRect;
+
+      // ToolBar handling
+      ViewerToolBar tb;
+      bool showToolbar;
+
+      // Local window state
+      win32::DIBSectionBuffer* buffer;
+      bool has_focus;
+      Rect window_size;
+      Rect client_size;
+      Point scrolloffset;
+      Point maxscrolloffset;
+      HWND handle;
+      HWND frameHandle;
+      rdr::U8 menuKey;
+
+      Callback* callback;
+    };
+
+  };
+
+};
+
+#endif // __RFB_WIN32_DESKTOP_WINDOW_H__
+
+
diff --git a/vncviewer/InfoDialog.cxx b/vncviewer/InfoDialog.cxx
index 0d2313a..e74896d 100644
--- a/vncviewer/InfoDialog.cxx
+++ b/vncviewer/InfoDialog.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -17,7 +17,7 @@
  */
 #include <vncviewer/InfoDialog.h>
 #include <vncviewer/resource.h>
-#include <vncviewer/CView.h>
+#include <vncviewer/CConn.h>
 #include <rfb/secTypes.h>
 #include <rfb/encodings.h>
 #include <rfb/CSecurity.h>
@@ -29,37 +29,37 @@
 static LogWriter vlog("Info");
 
 
-bool InfoDialog::showDialog(CView* vw) {
-  view = vw;
+bool InfoDialog::showDialog(CConn* cc) {
+  conn = cc;
   return Dialog::showDialog(MAKEINTRESOURCE(IDD_CONNECTION_INFO));
 }
 
 void InfoDialog::initDialog() {
   char buf[256];
 
-  setItemString(IDC_INFO_NAME, TStr(view->cp.name()));
+  setItemString(IDC_INFO_NAME, TStr(conn->cp.name()));
 
-  setItemString(IDC_INFO_HOST, TCharArray(view->sock->getPeerAddress()).buf);
+  setItemString(IDC_INFO_HOST, TCharArray(conn->getSocket()->getPeerAddress()).buf);
 
-  Rect bufRect = view->buffer->getRect();
-  sprintf(buf, "%dx%d", bufRect.width(), bufRect.height());
+  sprintf(buf, "%dx%d", conn->cp.width, conn->cp.height);
   setItemString(IDC_INFO_SIZE, TStr(buf));
 
-  view->cp.pf().print(buf, 256);
+  conn->cp.pf().print(buf, 256);
   setItemString(IDC_INFO_PF, TStr(buf));
 
-  view->serverDefaultPF.print(buf, 256);
+  conn->getServerDefaultPF().print(buf, 256);
   setItemString(IDC_INFO_DEF_PF, TStr(buf));
 
-  setItemString(IDC_REQUESTED_ENCODING, TStr(encodingName(view->getOptions().preferredEncoding)));
-  setItemString(IDC_LAST_ENCODING, TStr(encodingName(view->lastUsedEncoding())));
+  setItemString(IDC_REQUESTED_ENCODING, TStr(encodingName(conn->getOptions().preferredEncoding)));
+  setItemString(IDC_LAST_ENCODING, TStr(encodingName(conn->lastUsedEncoding())));
 
-  sprintf(buf, "%d kbits/s", view->sock->inStream().kbitsPerSecond());
+  sprintf(buf, "%d kbits/s", conn->getSocket()->inStream().kbitsPerSecond());
   setItemString(IDC_INFO_LINESPEED, TStr(buf));
 
-  sprintf(buf, "%d.%d", view->cp.majorVersion, view->cp.minorVersion);
+  sprintf(buf, "%d.%d", conn->cp.majorVersion, conn->cp.minorVersion);
   setItemString(IDC_INFO_VERSION, TStr(buf));
 
-  int secType = view->getCurrentCSecurity()->getType();
-  setItemString(IDC_INFO_SECURITY, TStr(secTypeName(secType)));
+  const CSecurity* cSec = conn->getCurrentCSecurity();
+  setItemString(IDC_INFO_SECURITY, TStr(secTypeName(cSec->getType())));
+  setItemString(IDC_INFO_ENCRYPTION, TStr(cSec->description()));
 }
diff --git a/vncviewer/InfoDialog.h b/vncviewer/InfoDialog.h
index 7a64d38..752d53c 100644
--- a/vncviewer/InfoDialog.h
+++ b/vncviewer/InfoDialog.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -30,15 +30,15 @@
 
   namespace win32 {
 
-    class CView;
+    class CConn;
 
     class InfoDialog : Dialog {
     public:
-      InfoDialog() : Dialog(GetModuleHandle(0)), view(0) {}
-      virtual bool showDialog(CView* vw);
+      InfoDialog() : Dialog(GetModuleHandle(0)), conn(0) {}
+      virtual bool showDialog(CConn* vw);
       virtual void initDialog();
     protected:
-      CView* view;
+      CConn* conn;
     };
 
   };
diff --git a/vncviewer/ListenServer.h b/vncviewer/ListenServer.h
new file mode 100644
index 0000000..4d1590c
--- /dev/null
+++ b/vncviewer/ListenServer.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2002-2005 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.
+ */
+
+// -=- ListenServer.h
+
+#ifndef __RFB_WIN32_LISTEN_SERVER_H__
+#define __RFB_WIN32_LISTEN_SERVER_H__
+
+#include <windows.h>
+#include <winsock2.h>
+#include <network/Socket.h>
+#include <rfb_win32/MsgWindow.h>
+#include <vncviewer/CConnThread.h>
+
+
+namespace rfb {
+  namespace win32 {
+
+    class ListenServer : MsgWindow {
+    public:
+      ListenServer(network::SocketListener* l) : MsgWindow(_T("rfb::win32::ListenServer")), sock(l) {
+        if (WSAAsyncSelect(l->getFd(), getHandle(), WM_USER, FD_ACCEPT) == SOCKET_ERROR)
+          throw rdr::SystemException("unable to monitor listen socket", WSAGetLastError());
+      }
+
+      LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+        if (msg == WM_USER) {
+          network::Socket* newConn = sock->accept();
+          Thread* newThread = new CConnThread(newConn, true);
+          return 0;
+        }
+        return MsgWindow::processMessage(msg, wParam, lParam);
+      }
+    protected:
+      network::SocketListener* sock;
+    };
+
+  };
+};
+
+#endif
\ No newline at end of file
diff --git a/vncviewer/ListenTrayIcon.h b/vncviewer/ListenTrayIcon.h
new file mode 100644
index 0000000..7e334d9
--- /dev/null
+++ b/vncviewer/ListenTrayIcon.h
@@ -0,0 +1,95 @@
+/* Copyright (C) 2002-2005 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.
+ */
+
+// -=- ListenTrayIcon.h
+
+#ifndef __RFB_WIN32_LISTEN_TRAY_ICON_H__
+#define __RFB_WIN32_LISTEN_TRAY_ICON_H__
+
+#include <rfb_win32/TrayIcon.h>
+#include <rfb_win32/AboutDialog.h>
+
+namespace rfb {
+  namespace win32 {
+
+    class ListenTrayIcon : public TrayIcon {
+    public:
+      ListenTrayIcon() {
+        setIcon(IDI_ICON);
+        setToolTip(_T("VNC Viewer"));
+      }
+      virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
+        switch(msg) {
+
+        case WM_USER:
+          switch (lParam) {
+          case WM_LBUTTONDBLCLK:
+            SendMessage(getHandle(), WM_COMMAND, ID_NEW_CONNECTION, 0);
+            break;
+          case WM_RBUTTONUP:
+            HMENU menu = LoadMenu(GetModuleHandle(0), MAKEINTRESOURCE(IDR_TRAY));
+            HMENU trayMenu = GetSubMenu(menu, 0);
+
+            // First item is New Connection, the default
+            SetMenuDefaultItem(trayMenu, ID_NEW_CONNECTION, FALSE);
+
+            // SetForegroundWindow is required, otherwise Windows ignores the
+            // TrackPopupMenu because the window isn't the foreground one, on
+            // some older Windows versions...
+            SetForegroundWindow(getHandle());
+
+            // Display the menu
+            POINT pos;
+            GetCursorPos(&pos);
+            TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, getHandle(), 0);
+            break;
+			    } 
+			    return 0;
+
+        case WM_COMMAND:
+          switch (LOWORD(wParam)) {
+          case ID_NEW_CONNECTION:
+            {
+              Thread* connThread = new CConnThread();
+              break;
+            }
+          case ID_OPTIONS:
+            OptionsDialog::global.showDialog(0);
+            break;
+          case ID_ABOUT:
+            AboutDialog::instance.showDialog();
+            break;
+          case ID_CLOSE:
+            SendMessage(getHandle(), WM_CLOSE, 0, 0);
+            break;
+          }
+          return 0;
+
+        case WM_CLOSE:
+          PostQuitMessage(0);
+          return 0;
+        }
+
+        return TrayIcon::processMessage(msg, wParam, lParam);
+      }
+    };
+
+  };
+};
+
+#endif // __RFB_WIN32_LISTEN_TRAY_ICON_H__
\ No newline at end of file
diff --git a/vncviewer/MRU.h b/vncviewer/MRU.h
index f065a06..ae703b3 100644
--- a/vncviewer/MRU.h
+++ b/vncviewer/MRU.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -18,13 +18,10 @@
 #ifndef __VIEWER_MRU_H__
 #define __VIEWER_MRU_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <list>
 #include <set>
-
 #include <rfb_win32/Registry.h>
-
 #include <rfb/util.h>
 #include <rdr/HexOutStream.h>
 
@@ -85,7 +82,7 @@
             TCharArray keyname = rdr::HexOutStream::binToHexStr(&order[i], 1);
             try {
               TCharArray hostname = key.getString(keyname.buf);
-              if (strcmp(name, CStr(hostname.buf)) == 0) {
+              if (stricmp(name, CStr(hostname.buf)) == 0) {
                 keycode = order[i];
                 found = true;
                 break;
@@ -109,8 +106,6 @@
           orderlen = 0;
         }
 
-        printf("keycode=%d\n", (int)keycode);
-
         orderlen++;
         int i, j=orderlen-1;
         for (i=0; i<orderlen-1; i++) {
@@ -124,8 +119,6 @@
           order[i] = order[i-1];
         order[0] = keycode;
 
-        printf("selected %d\n", (int)keycode);
-
         TCharArray keyname = rdr::HexOutStream::binToHexStr((char*)&keycode, 1);
         key.setString(keyname.buf, TStr(name));
         key.setBinary(_T("Order"), order, orderlen);
diff --git a/vncviewer/OptionsDialog.cxx b/vncviewer/OptionsDialog.cxx
index 0985f5b..2e43b38 100644
--- a/vncviewer/OptionsDialog.cxx
+++ b/vncviewer/OptionsDialog.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -16,20 +16,16 @@
  * USA.
  */
 
-#define WIN32_LEAN_AND_MEAN
-#if (_WIN32_WINNT < 0x0400)
-#define _WIN32_WINNT 0x0400
-#endif
-#include <windows.h>
-#include <commdlg.h>
-
 #include <vncviewer/OptionsDialog.h>
-#include <vncviewer/CView.h>
+#include <vncviewer/CConn.h>
 #include <vncviewer/resource.h>
 #include <rfb_win32/Registry.h>
-#include <rfb/LogWriter.h>
+#include <rfb_win32/MsgBox.h>
+#include <rfb_win32/OSVersion.h>
 #include <rfb/encodings.h>
 #include <rfb/CConnection.h>
+#include <commdlg.h>
+#include <rfb/LogWriter.h>
 
 using namespace rfb;
 using namespace rfb::win32;
@@ -38,22 +34,22 @@
 
 
 struct OptionsInfo {
-  CView* view;
-  CViewOptions options;
+  CConn* view;
+  CConnOptions options;
 };
 
 
 OptionsDialog rfb::win32::OptionsDialog::global;
 
 
-class VNCviewerOptions : public PropSheet {
+class ViewerOptions : public PropSheet {
 public:
-  VNCviewerOptions(OptionsInfo& info_, std::list<PropSheetPage*> pages)
+  ViewerOptions(OptionsInfo& info_, std::list<PropSheetPage*> pages)
     : PropSheet(GetModuleHandle(0), 
     info_.view ? _T("VNC Viewer Options") : _T("VNC Viewer Defaults"), pages),
     info(info_), changed(false) {
   }
-  ~VNCviewerOptions() {
+  ~ViewerOptions() {
     if (changed) {
       if (info.view)
         // Apply the settings to the supplied session object
@@ -121,7 +117,7 @@
       dlg->options.preferredEncoding = encodingHextile;
     if (isItemChecked(IDC_ENCODING_RAW))
       dlg->options.preferredEncoding = encodingRaw;
-    ((VNCviewerOptions*)propSheet)->setChanged();
+    ((ViewerOptions*)propSheet)->setChanged();
     return true;
   }
   virtual bool onCommand(int id, int cmd) {
@@ -165,6 +161,7 @@
     enableItem(IDC_PROTOCOL_3_3, (!dlg->view) || (dlg->view->state() != CConnection::RFBSTATE_NORMAL));
     setItemChecked(IDC_PROTOCOL_3_3, dlg->options.protocol3_3);
     setItemChecked(IDC_ACCEPT_BELL, dlg->options.acceptBell);
+    setItemChecked(IDC_AUTO_RECONNECT, dlg->options.autoReconnect);
     setItemChecked(IDC_SHOW_TOOLBAR, dlg->options.showToolbar);
   }
   virtual bool onOk() {
@@ -174,8 +171,9 @@
     dlg->options.useDesktopResize = isItemChecked(IDC_DESKTOP_RESIZE);
     dlg->options.protocol3_3 = isItemChecked(IDC_PROTOCOL_3_3);
     dlg->options.acceptBell = isItemChecked(IDC_ACCEPT_BELL);
+    dlg->options.autoReconnect = isItemChecked(IDC_AUTO_RECONNECT);
     dlg->options.showToolbar = isItemChecked(IDC_SHOW_TOOLBAR);
-    ((VNCviewerOptions*)propSheet)->setChanged();
+    ((ViewerOptions*)propSheet)->setChanged();
     return true;
   }
 protected:
@@ -190,9 +188,10 @@
   virtual void initDialog() {
     setItemChecked(IDC_SEND_POINTER, dlg->options.sendPtrEvents);
     setItemChecked(IDC_SEND_KEYS, dlg->options.sendKeyEvents);
-    setItemChecked(IDC_SEND_SYSKEYS, dlg->options.sendSysKeys);
     setItemChecked(IDC_CLIENT_CUTTEXT, dlg->options.clientCutText);
     setItemChecked(IDC_SERVER_CUTTEXT, dlg->options.serverCutText);
+    setItemChecked(IDC_DISABLE_WINKEYS, dlg->options.disableWinKeys && !osVersion.isPlatformWindows);
+    enableItem(IDC_DISABLE_WINKEYS, !osVersion.isPlatformWindows);
     setItemChecked(IDC_EMULATE3, dlg->options.emulate3);
     setItemChecked(IDC_POINTER_INTERVAL, dlg->options.pointerEventInterval != 0);
 
@@ -213,9 +212,9 @@
   virtual bool onOk() {
     dlg->options.sendPtrEvents = isItemChecked(IDC_SEND_POINTER);
     dlg->options.sendKeyEvents = isItemChecked(IDC_SEND_KEYS);
-    dlg->options.sendSysKeys = isItemChecked(IDC_SEND_SYSKEYS);
     dlg->options.clientCutText = isItemChecked(IDC_CLIENT_CUTTEXT);
     dlg->options.serverCutText = isItemChecked(IDC_SERVER_CUTTEXT);
+    dlg->options.disableWinKeys = isItemChecked(IDC_DISABLE_WINKEYS);
     dlg->options.emulate3 = isItemChecked(IDC_EMULATE3);
     dlg->options.pointerEventInterval = 
       isItemChecked(IDC_POINTER_INTERVAL) ? 200 : 0;
@@ -229,14 +228,13 @@
     else
       dlg->options.setMenuKey(CStr(keyName.buf));
 
-    ((VNCviewerOptions*)propSheet)->setChanged();
+    ((ViewerOptions*)propSheet)->setChanged();
     return true;
   }
 protected:
   OptionsInfo* dlg;
 };
 
-
 class DefaultsPage : public PropSheetPage {
 public:
   DefaultsPage(OptionsInfo* dlg_)
@@ -247,10 +245,9 @@
     enableItem(IDC_SAVE_CONFIG, dlg->options.configFileName.buf);
   }
   virtual bool onCommand(int id, int cmd) {
-    HWND hwnd = dlg->view ? dlg->view->getHandle() : 0;
     switch (id) {
     case IDC_LOAD_DEFAULTS:
-      dlg->options = CViewOptions();
+      dlg->options = CConnOptions();
       break;
     case IDC_SAVE_DEFAULTS:
       propSheet->commitPages();
@@ -262,7 +259,7 @@
     case IDC_SAVE_CONFIG:
       propSheet->commitPages();
       dlg->options.writeToFile(dlg->options.configFileName.buf);
-      MsgBox(hwnd, _T("Options saved successfully"),
+      MsgBox(handle, _T("Options saved successfully"),
              MB_OK | MB_ICONINFORMATION);
       return 0;
     case IDC_SAVE_CONFIG_AS:
@@ -281,7 +278,7 @@
 #else
       ofn.lStructSize = sizeof(ofn);
 #endif
-      ofn.hwndOwner = hwnd;
+      ofn.hwndOwner = handle;
       ofn.lpstrFilter = _T("VNC Connection Options\000*.vnc\000");
       ofn.lpstrFile = newFilename;
       currentDir[0] = 0;
@@ -298,7 +295,7 @@
 
       // Save the Options
       dlg->options.writeToFile(CStr(newFilename));
-      MsgBox(hwnd, _T("Options saved successfully"),
+      MsgBox(handle, _T("Options saved successfully"),
              MB_OK | MB_ICONINFORMATION);
       return 0;
     };
@@ -313,7 +310,7 @@
 OptionsDialog::OptionsDialog() : visible(false) {
 }
 
-bool OptionsDialog::showDialog(CView* view, bool capture) {
+bool OptionsDialog::showDialog(CConn* view, bool capture) {
   if (visible) return false;
   visible = true;
 
@@ -331,8 +328,9 @@
   DefaultsPage defPage(&info); if (view) pages.push_back(&defPage);
 
   // Show the property sheet
-  VNCviewerOptions dialog(info, pages);
-  dialog.showPropSheet(view ? view->getHandle() : 0, false, false, capture);
+  ViewerOptions dialog(info, pages);
+  dialog.showPropSheet(view && view->getWindow() ? view->getWindow()->getHandle() : 0,
+                       false, false, capture);
 
   visible = false;
   return dialog.changed;
diff --git a/vncviewer/OptionsDialog.h b/vncviewer/OptionsDialog.h
index eec9b96..fcddc71 100644
--- a/vncviewer/OptionsDialog.h
+++ b/vncviewer/OptionsDialog.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -23,19 +23,18 @@
 #ifndef __RFB_WIN32_OPTIONS_DIALOG_H__
 #define __RFB_WIN32_OPTIONS_DIALOG_H__
 
-#include <vncviewer/CViewOptions.h>
 #include <rfb_win32/Dialog.h>
 
 namespace rfb {
 
   namespace win32 {
 
-    class CView;
+    class CConn;
 
     class OptionsDialog {
     public:
       OptionsDialog();
-      virtual bool showDialog(CView* cfg, bool capture=false);
+      virtual bool showDialog(CConn* cfg, bool capture=false);
 
       static OptionsDialog global;
     protected:
diff --git a/vncviewer/UserPasswdDialog.cxx b/vncviewer/UserPasswdDialog.cxx
index 8ab4ba4..2eea0ea 100644
--- a/vncviewer/UserPasswdDialog.cxx
+++ b/vncviewer/UserPasswdDialog.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -18,12 +18,14 @@
 
 #include <vncviewer/UserPasswdDialog.h>
 #include <vncviewer/resource.h>
+#include <rfb/Exception.h>
 
 using namespace rfb;
 using namespace rfb::win32;
 
 
-UserPasswdDialog::UserPasswdDialog() : Dialog(GetModuleHandle(0)), showUsername(false) {
+UserPasswdDialog::UserPasswdDialog() : Dialog(GetModuleHandle(0)),
+  showUsername(false), showPassword(false) {
 }
 
 
@@ -36,18 +38,18 @@
 }
 
 void UserPasswdDialog::initDialog() {
-  if (username.buf) {
+  if (username.buf)
     setItemString(IDC_USERNAME, username.buf);
-    tstrFree(username.takeBuf());
-  }
-  if (password.buf) {
+  if (password.buf)
     setItemString(IDC_PASSWORD, password.buf);
-    tstrFree(password.takeBuf());
-  }
   if (!showUsername) {
     setItemString(IDC_USERNAME, _T(""));
     enableItem(IDC_USERNAME, false);
   }
+  if (!showPassword) {
+    setItemString(IDC_PASSWORD, _T(""));
+    enableItem(IDC_PASSWORD, false);
+  }
   if (description.buf) {
     TCharArray title(128);
     GetWindowText(handle, title.buf, 128);
@@ -59,26 +61,25 @@
 }
 
 bool UserPasswdDialog::onOk() {
-	username.buf = getItemString(IDC_USERNAME);
-	password.buf = getItemString(IDC_PASSWORD);
+	username.replaceBuf(getItemString(IDC_USERNAME));
+	password.replaceBuf(getItemString(IDC_PASSWORD));
   return true;
 }
 
 
-bool UserPasswdDialog::getUserPasswd(char** user, char** passwd) {
-  bool result = false;
+void UserPasswdDialog::getUserPasswd(char** user, char** passwd) {
   showUsername = user != 0;
+  showPassword = passwd != 0;
   if (user && *user)
-    username.buf = tstrDup(*user);
+    username.replaceBuf(tstrDup(*user));
   if (passwd && *passwd)
-    password.buf = tstrDup(*passwd);
-  if (showDialog()) {
-    if (user)
-      *user = strDup(username.buf);
+    password.replaceBuf(tstrDup(*passwd));
+
+  if (!showDialog())
+    throw rfb::AuthCancelledException();
+
+  if (user)
+    *user = strDup(username.buf);
+  if (passwd)
     *passwd = strDup(password.buf);
-    result = true;
-  }
-  tstrFree(username.takeBuf());
-  tstrFree(password.takeBuf());
-  return result;
 }
diff --git a/vncviewer/UserPasswdDialog.h b/vncviewer/UserPasswdDialog.h
index 998a49f..bf006f4 100644
--- a/vncviewer/UserPasswdDialog.h
+++ b/vncviewer/UserPasswdDialog.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -19,6 +19,10 @@
 // -=- UserPasswdDialog.h
 
 // Username and password dialog for VNC Viewer 4.0
+// Note that the password and username fields are only freed
+// when the dialog instance is deleted - it is important to
+// ensure that the instance is deleted as early as possible, to
+// avoid the password being retained in memory for too long.
 
 #ifndef __RFB_WIN32_USERPASSWD_DIALOG_H__
 #define __RFB_WIN32_USERPASSWD_DIALOG_H__
@@ -38,12 +42,12 @@
       virtual bool showDialog();
       virtual void initDialog();
       virtual bool onOk();
-      virtual bool getUserPasswd(char** user, char** passwd);
+      virtual void getUserPasswd(char** user, char** passwd);
       void setCSecurity(const CSecurity* cs);
     protected:
       TCharArray username;
-      TCharArray password;
-      bool showUsername;
+      TPlainPasswd password;
+      bool showUsername, showPassword;
       TCharArray description;
     };
 
diff --git a/vncviewer/buildTime.cxx b/vncviewer/buildTime.cxx
index bab2e13..9f37b38 100644
--- a/vncviewer/buildTime.cxx
+++ b/vncviewer/buildTime.cxx
@@ -1 +1,18 @@
-const char* buildTime = "Built on " __DATE__ " at " __TIME__;
\ No newline at end of file
+/* Copyright (C) 2005 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.
+ */
+const char* buildTime = "Built on " __DATE__ " at " __TIME__;
diff --git a/vncviewer/cview.cxx b/vncviewer/cview.cxx
deleted file mode 100644
index 03c68e2..0000000
--- a/vncviewer/cview.cxx
+++ /dev/null
@@ -1,1742 +0,0 @@
-/* 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.
- */
-#define WIN32_LEAN_AND_MEAN
-#if (_WIN32_WINNT < 0x0400)
-#define _WIN32_WINNT 0x0400
-#endif
-#include <windows.h>
-#include <winsock2.h>
-#include <tchar.h>
-#include <commctrl.h>
-
-#include <network/TcpSocket.h>
-
-#include <vncviewer/CView.h>
-#include <vncviewer/UserPasswdDialog.h>
-#include <vncviewer/resource.h>
-
-#include <rfb/encodings.h>
-#include <rfb/secTypes.h>
-#include <rfb/CSecurityNone.h>
-#include <rfb/CSecurityVncAuth.h>
-#include <rfb/CMsgWriter.h>
-#include <rfb/Configuration.h>
-#include <rfb/LogWriter.h>
-
-#include <rfb_win32/WMShatter.h>
-
-using namespace rfb;
-using namespace rfb::win32;
-using namespace rdr;
-
-// - Statics & consts
-
-static LogWriter vlog("CView");
-
-const int IDM_FULLSCREEN = ID_FULLSCREEN;
-const int IDM_SEND_MENU_KEY = ID_SEND_MENU_KEY;
-const int IDM_SEND_CAD = ID_SEND_CAD;
-const int IDM_SEND_CTLESC = ID_SEND_CTLESC;
-const int IDM_ABOUT = ID_ABOUT;
-const int IDM_OPTIONS = ID_OPTIONS;
-const int IDM_INFO = ID_INFO;
-const int IDM_NEWCONN = ID_NEW_CONNECTION;
-const int IDM_REQUEST_REFRESH = ID_REQUEST_REFRESH;
-const int IDM_CTRL_KEY = ID_CTRL_KEY;
-const int IDM_ALT_KEY = ID_ALT_KEY;
-const int IDM_FILE_TRANSFER = ID_FILE_TRANSFER;
-const int IDM_CONN_SAVE_AS = ID_CONN_SAVE_AS;
-
-const int TIMER_BUMPSCROLL = 1;
-const int TIMER_POINTER_INTERVAL = 2;
-const int TIMER_POINTER_3BUTTON = 3;
-
-const int HOTKEY_ALTTAB = 0;
-
-
-IntParameter debugDelay("DebugDelay","Milliseconds to display inverted "
-                        "pixel data - a debugging feature", 0);
-
-
-//
-// -=- CViewClass
-
-//
-// Window class used as the basis for all CView instances
-//
-
-class CViewClass {
-public:
-  CViewClass();
-  ~CViewClass();
-  ATOM classAtom;
-  HINSTANCE instance;
-};
-
-LRESULT CALLBACK CViewProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
-  LRESULT result;
-
-  // *** vlog.debug("CViewMsg %x->(%x, %x, %x)", wnd, msg, wParam, lParam);
-
-  if (msg == WM_CREATE)
-    SetWindowLong(wnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams);
-  else if (msg == WM_DESTROY)
-    SetWindowLong(wnd, GWL_USERDATA, 0);
-  CView* _this = (CView*) GetWindowLong(wnd, GWL_USERDATA);
-  if (!_this) {
-    vlog.info("null _this in %x, message %u", wnd, msg);
-    return rfb::win32::SafeDefWindowProc(wnd, msg, wParam, lParam);
-  }
-
-  try {
-    result = _this->processMessage(msg, wParam, lParam);
-  } catch (rdr::Exception& e) {
-    vlog.error("untrapped: %s", e.str());
-  }
-
-  return result;
-};
-
-HCURSOR dotCursor = (HCURSOR)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDC_DOT_CURSOR), IMAGE_CURSOR, 0, 0, LR_SHARED);
-HCURSOR arrowCursor = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED); 
-
-CViewClass::CViewClass() : classAtom(0) {
-  WNDCLASS wndClass;
-  wndClass.style = 0;
-  wndClass.lpfnWndProc = CViewProc;
-  wndClass.cbClsExtra = 0;
-  wndClass.cbWndExtra = 0;
-  wndClass.hInstance = instance = GetModuleHandle(0);
-  wndClass.hIcon = (HICON)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 0, 0, LR_SHARED);
-  if (!wndClass.hIcon)
-    printf("unable to load icon:%ld", GetLastError());
-  wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
-  wndClass.hbrBackground = NULL;
-  wndClass.lpszMenuName = 0;
-  wndClass.lpszClassName = _T("rfb::win32::CViewClass");
-  classAtom = RegisterClass(&wndClass);
-  if (!classAtom) {
-    throw rdr::SystemException("unable to register CView window class", GetLastError());
-  }
-}
-
-CViewClass::~CViewClass() {
-  if (classAtom) {
-    UnregisterClass((const TCHAR*)classAtom, instance);
-  }
-}
-
-CViewClass baseClass;
-
-//
-// -=- FrameClass
-
-//
-// Window class used to display the rfb data
-//
-
-class FrameClass {
-public:
-  FrameClass();
-  ~FrameClass();
-  ATOM classAtom;
-  HINSTANCE instance;
-};
-
-LRESULT CALLBACK FrameProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
-  LRESULT result;
-
-  if (msg == WM_CREATE)
-    SetWindowLong(hwnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams);
-  else if (msg == WM_DESTROY)
-    SetWindowLong(hwnd, GWL_USERDATA, 0);
-  CView* _this = (CView*) GetWindowLong(hwnd, GWL_USERDATA);
-  if (!_this) {
-    vlog.info("null _this in %x, message %u", hwnd, msg);
-    return DefWindowProc(hwnd, msg, wParam, lParam);
-  }
-
-  try {
-    result = _this->processFrameMessage(msg, wParam, lParam);
-  } catch (rdr::Exception& e) {
-    vlog.error("untrapped: %s", e.str());
-  }
-
-  return result;
-}
-
-FrameClass::FrameClass() : classAtom(0) {
-  WNDCLASS wndClass;
-  wndClass.style = 0;
-  wndClass.lpfnWndProc = FrameProc;
-  wndClass.cbClsExtra = 0;
-  wndClass.cbWndExtra = 0;
-  wndClass.hInstance = instance = GetModuleHandle(0);
-  wndClass.hIcon = 0;
-  wndClass.hCursor = NULL;
-  wndClass.hbrBackground = 0;
-  wndClass.lpszMenuName = 0;
-  wndClass.lpszClassName = _T("FrameClass");
-  classAtom = RegisterClass(&wndClass);
-  if (!classAtom) {
-    throw rdr::SystemException("unable to register frame window class",
-                               GetLastError());
-  }
-}
-
-FrameClass::~FrameClass() {
-  if (classAtom) {
-    UnregisterClass((const TCHAR*)classAtom, instance);
-  }
-}
-
-FrameClass frameClass;
-
-//
-// -=- CView instance implementation
-//
-
-RegKey CView::userConfigKey;
-
-
-CView::CView() 
-  : quit_on_destroy(false), buffer(0), sock(0), readyToRead(false),
-    client_size(0, 0, 16, 16), window_size(0, 0, 32, 32),
-    cursorVisible(false), cursorAvailable(false), cursorInBuffer(false),
-    systemCursorVisible(true), trackingMouseLeave(false),
-    hwnd(0), frameHwnd(0), requestUpdate(false), has_focus(false), 
-    palette_changed(false), sameMachine(false), encodingChange(false), 
-    formatChange(false), lastUsedEncoding_(encodingRaw), fullScreenActive(false),
-    bumpScroll(false), manager(0), toolbar(true) {
-
-  // Create the main window
-  const TCHAR* name = _T("VNC Viewer 4.0b");
-  hwnd = CreateWindow((const TCHAR*)baseClass.classAtom, name, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
-    0, 0, 10, 10, 0, 0, baseClass.instance, this);
-  if (!hwnd) {
-    throw rdr::SystemException("unable to create WMNotifier window instance", GetLastError());
-  }
-  vlog.debug("created window \"%s\" (%x)", (const char*)CStr(name), hwnd);
-
-  // Create the viewer toolbar
-  tb.create(getHandle());
-  vlog.debug("created toolbar window \"%s\" (%x)", "ViewerToolBar", tb.getHandle());
-
-  // Create the frame window
-  frameHwnd = CreateWindowEx(WS_EX_CLIENTEDGE, (const TCHAR*)frameClass.classAtom,
-    0, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT,
-    CW_USEDEFAULT, CW_USEDEFAULT, getHandle(), 0, frameClass.instance, this);
-  if (!frameHwnd) {
-    throw rdr::SystemException("unable to create rfb frame window instance", GetLastError());
-  }
-  vlog.debug("created window \"%s\" (%x)", "Frame Window", frameHwnd);
-
-  // Initialise the CPointer pointer handler
-  ptr.setHWND(getFrameHandle());
-  ptr.setIntervalTimerId(TIMER_POINTER_INTERVAL);
-  ptr.set3ButtonTimerId(TIMER_POINTER_3BUTTON);
-
-  // Initialise the bumpscroll timer
-  bumpScrollTimer.setHWND(getHandle());
-  bumpScrollTimer.setId(TIMER_BUMPSCROLL);
-
-  // Grab AltTab
-  setAltTabGrab(options.sendSysKeys);
-
-  // Hook the clipboard
-  clipboard.setNotifier(this);
-
-  // Create the backing buffer
-  buffer = new win32::DIBSectionBuffer(getFrameHandle());
-}
-
-CView::~CView() {
-  vlog.debug("~CView");
-  showSystemCursor();
-  if (hwnd) {
-    setVisible(false);
-    DestroyWindow(hwnd);
-    hwnd = 0;
-  }
-  delete buffer;
-  vlog.debug("~CView done");
-}
-
-bool CView::initialise(network::Socket* s) {
-  // Update the window menu
-  HMENU wndmenu = GetSystemMenu(hwnd, FALSE);
-  AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
-  AppendMenu(wndmenu, MF_STRING, IDM_FULLSCREEN, _T("&Full screen"));
-  AppendMenu(wndmenu, (options.showToolbar ? MF_STRING | MF_CHECKED : MF_STRING), 
-    IDM_SHOW_TOOLBAR, _T("Show tool&bar"));
-  AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
-  AppendMenu(wndmenu, MF_STRING, IDM_CTRL_KEY, _T("Ctr&l"));
-  AppendMenu(wndmenu, MF_STRING, IDM_ALT_KEY, _T("Al&t"));
-  AppendMenu(wndmenu, MF_STRING, IDM_SEND_CAD, _T("Send Ctrl-Alt-&Del"));
-  AppendMenu(wndmenu, MF_STRING, IDM_SEND_CTLESC, _T("Send Ctrl-&Esc"));
-  AppendMenu(wndmenu, MF_STRING, IDM_REQUEST_REFRESH, _T("Refres&h Screen"));
-  AppendMenu(wndmenu, MF_SEPARATOR, 0, 0);
-  if (manager) AppendMenu(wndmenu, MF_STRING, IDM_NEWCONN, _T("Ne&w Connection..."));
-  AppendMenu(wndmenu, MF_STRING, IDM_OPTIONS, _T("&Options..."));
-  AppendMenu(wndmenu, MF_STRING, IDM_INFO, _T("Connection &Info..."));
-  AppendMenu(wndmenu, MF_STRING, IDM_ABOUT, _T("&About..."));
-
-  // Set the server's name for MRU purposes
-  CharArray endpoint(s->getPeerEndpoint());
-  setServerName(endpoint.buf);
-  if (!options.host.buf)
-    options.setHost(endpoint.buf);
-
-  // Initialise the underlying CConnection
-  setStreams(&s->inStream(), &s->outStream());
-
-  // Enable processing of window messages while blocked on I/O
-  s->inStream().setBlockCallback(this);
-
-  // Initialise the viewer options
-  applyOptions(options);
-
-  // - Set which auth schemes we support
-  addSecType(secTypeNone);
-  addSecType(secTypeVncAuth);
-
-  initialiseProtocol();
-  WSAAsyncSelect(s->getFd(), getHandle(), WM_USER, FD_READ | FD_CLOSE);
-  sock = s;
-
-  m_fileTransfer.initialize(&s->inStream(), &s->outStream());
-
-  // Show toolbar if needed
-  toolbar = options.showToolbar;
-  if (options.showToolbar) tb.show();
-  else tb.hide();
-
-  return true;
-}
-
-
-void
-CView::applyOptions(CViewOptions& opt) {
-  // *** CHANGE THIS TO USE CViewOptions::operator= ***
-
-  // - Take the username, password, config filename, and host spec
-  options.setUserName(opt.userName.buf);
-  options.setPassword(opt.password.buf);
-  options.setHost(opt.host.buf);
-  options.setConfigFileName(opt.configFileName.buf);
-  options.setMonitor(opt.monitor.buf);
-
-  // - Set optional features in ConnParams
-  encodingChange |= ((options.useLocalCursor != opt.useLocalCursor) ||
-    (options.useDesktopResize != opt.useDesktopResize));
-  cp.supportsLocalCursor = options.useLocalCursor = opt.useLocalCursor;
-  cp.supportsDesktopResize = options.useDesktopResize = opt.useDesktopResize;
-
-  encodingChange |= ((options.customCompressLevel != opt.customCompressLevel) ||
-		     (options.compressLevel != opt.compressLevel) ||
-		     (options.noJpeg != opt.noJpeg) ||
-		     (options.qualityLevel != opt.qualityLevel));
-  cp.customCompressLevel = options.customCompressLevel = opt.customCompressLevel;
-  cp.compressLevel = options.compressLevel = opt.compressLevel;
-  cp.noJpeg = options.noJpeg = opt.noJpeg;
-  cp.qualityLevel = options.qualityLevel = opt.qualityLevel;
-
-  if (cursorAvailable)
-    hideLocalCursor();
-  cursorAvailable = cursorAvailable && options.useLocalCursor;
-
-  // - Switch full-screen mode on/off
-  options.fullScreen = opt.fullScreen;
-  setFullscreen(options.fullScreen);
-
-  // - Handle format/encoding options
-  encodingChange |= (options.preferredEncoding != opt.preferredEncoding);
-  options.preferredEncoding = opt.preferredEncoding;
-
-  formatChange |= (options.fullColour != opt.fullColour);
-  options.fullColour = opt.fullColour;
-
-  if (!options.fullColour)
-    formatChange |= (options.lowColourLevel != opt.lowColourLevel);
-  options.lowColourLevel = opt.lowColourLevel;
-
-  options.autoSelect = opt.autoSelect;
-
-  // - Sharing
-  options.shared = opt.shared;
-  setShared(options.shared);
-
-  // - Inputs
-  options.sendPtrEvents = opt.sendPtrEvents;
-  options.sendKeyEvents = opt.sendKeyEvents;
-  options.sendSysKeys = opt.sendSysKeys;
-  setAltTabGrab(options.sendSysKeys);
-  options.clientCutText = opt.clientCutText;
-  options.serverCutText = opt.serverCutText;
-  options.emulate3 = opt.emulate3;
-  ptr.enableEmulate3(opt.emulate3);
-  options.pointerEventInterval = opt.pointerEventInterval;
-  ptr.enableInterval(opt.pointerEventInterval);
-  options.menuKey = opt.menuKey;
-
-  // - Protocol version override
-  options.protocol3_3 = opt.protocol3_3;
-  setProtocol3_3(options.protocol3_3);
-
-  // - Bell
-  options.acceptBell = opt.acceptBell;
-
-  // - Show/hide toolbar
-  options.showToolbar = opt.showToolbar;
-}
-
-void
-CView::setFullscreen(bool fs) {
-  // Set the menu fullscreen option tick
-  CheckMenuItem(GetSystemMenu(getHandle(), FALSE), IDM_FULLSCREEN,
-    (options.fullScreen ? MF_CHECKED : 0) | MF_BYCOMMAND);
-
-  // If the fullscreen mode is active then disable the menu point 
-  // "Show toolbar", otherwise enable the menu point.
-  EnableMenuItem(GetSystemMenu(getHandle(), FALSE), IDM_SHOW_TOOLBAR,
-    (options.fullScreen ? MF_GRAYED : MF_ENABLED) | MF_BYCOMMAND);
-
-  // If the window is not visible then we ignore the request.
-  // setVisible() will call us to correct the full-screen state when
-  // the window is visible, to keep things consistent.
-  if (!IsWindowVisible(getHandle()))
-    return;
-
-  if (fs && !fullScreenActive) {
-    fullScreenActive = bumpScroll = true;
-
-    // Un-minimize the window if required
-    if (GetWindowLong(getHandle(), GWL_STYLE) & WS_MINIMIZE)
-      ShowWindow(getHandle(), SW_RESTORE);
-
-    // Save the non-fullscreen window position
-    RECT wrect;
-    GetWindowRect(getHandle(), &wrect);
-    fullScreenOldRect = Rect(wrect.left, wrect.top, wrect.right, wrect.bottom);
-
-    // Find the size of the display the window is on
-    MonitorInfo mi(getHandle());
-
-    // Set the window full-screen
-    DWORD flags = GetWindowLong(getHandle(), GWL_STYLE);
-    fullScreenOldFlags = flags;
-    flags = flags & ~(WS_CAPTION | WS_THICKFRAME | WS_MAXIMIZE | WS_MINIMIZE);
-    vlog.debug("flags=%x", flags);
-
-    if (toolbar) tb.hide();
-    SetWindowLong(getFrameHandle(), GWL_EXSTYLE, 0);
-    SetWindowLong(getHandle(), GWL_STYLE, flags);
-    SetWindowPos(getHandle(), HWND_TOP, mi.rcMonitor.left, mi.rcMonitor.top,
-      mi.rcMonitor.right-mi.rcMonitor.left,
-      mi.rcMonitor.bottom-mi.rcMonitor.top,
-      SWP_FRAMECHANGED);
-  } else if (!fs && fullScreenActive) {
-    fullScreenActive = bumpScroll = false;
-
-    // Set the window non-fullscreen
-    if (toolbar) tb.show();
-    SetWindowLong(getFrameHandle(), GWL_EXSTYLE, WS_EX_CLIENTEDGE);
-    SetWindowLong(getHandle(), GWL_STYLE, fullScreenOldFlags);
-    SetWindowPos(getHandle(), HWND_NOTOPMOST,
-      fullScreenOldRect.tl.x, fullScreenOldRect.tl.y,
-      fullScreenOldRect.width(), fullScreenOldRect.height(),
-      SWP_FRAMECHANGED);
-  }
-
-  // Adjust the viewport offset to cope with change in size between FS
-  // and previous window state.
-  setViewportOffset(scrolloffset);
-}
-
-
-bool CView::setViewportOffset(const Point& tl) {
-/* ***
-  Point np = Point(max(0, min(maxscrolloffset.x, tl.x)),
-    max(0, min(maxscrolloffset.y, tl.y)));
-    */
-  Point np = Point(max(0, min(tl.x, buffer->width()-client_size.width())),
-    max(0, min(tl.y, buffer->height()-client_size.height())));
-  Point delta = np.translate(scrolloffset.negate());
-  if (!np.equals(scrolloffset)) {
-    scrolloffset = np;
-    ScrollWindowEx(getFrameHandle(), -delta.x, -delta.y, 0, 0, 0, 0, SW_INVALIDATE);
-    UpdateWindow(getFrameHandle());
-    return true;
-  }
-  return false;
-}
-
-
-bool CView::processBumpScroll(const Point& pos)
-{
-  if (!bumpScroll) return false;
-  int bumpScrollPixels = 20;
-  bumpScrollDelta = Point();
-
-  if (pos.x == client_size.width()-1)
-    bumpScrollDelta.x = bumpScrollPixels;
-  else if (pos.x == 0)
-    bumpScrollDelta.x = -bumpScrollPixels;
-  if (pos.y == client_size.height()-1)
-    bumpScrollDelta.y = bumpScrollPixels;
-  else if (pos.y == 0)
-    bumpScrollDelta.y = -bumpScrollPixels;
-
-  if (bumpScrollDelta.x || bumpScrollDelta.y) {
-    if (bumpScrollTimer.isActive()) return true;
-    if (setViewportOffset(scrolloffset.translate(bumpScrollDelta))) {
-      bumpScrollTimer.start(25);
-      return true;
-    }
-  }
-
-  bumpScrollTimer.stop();
-  return false;
-}
-
-
-LRESULT
-CView::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
-  switch (msg) {
-
-    // -=- Process standard window messages
-
-  case WM_NOTIFY:
-    if (wParam = (ID_TOOLBAR)) tb.processWM_NOTIFY(wParam, lParam);
-    break;
-
-  case WM_DISPLAYCHANGE:
-    // Display has changed - use new pixel format
-    calculateFullColourPF();
-    break;
-
-    // -=- Window position
-
-    // Prevent the window from being resized to be too large if in normal mode.
-    // If maximized or fullscreen the allow oversized windows.
-
-  case WM_WINDOWPOSCHANGING:
-    {
-      WINDOWPOS* wpos = (WINDOWPOS*)lParam;
-      if (wpos->flags &  SWP_NOSIZE)
-        break;
-
-      // Work out how big the window should ideally be
-      DWORD frame_current_style = GetWindowLong(getFrameHandle(), GWL_STYLE);
-      DWORD frame_style = frame_current_style & ~(WS_VSCROLL | WS_HSCROLL);
-      DWORD frame_ex_style = GetWindowLong(getFrameHandle(), GWL_EXSTYLE);
-
-      RECT r;
-      SetRect(&r, 0, 0, buffer->width(), buffer->height());
-      AdjustWindowRectEx(&r, frame_style, FALSE, frame_ex_style);
-      Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom);
-      if (frame_current_style & WS_VSCROLL)
-        reqd_size.br.x += GetSystemMetrics(SM_CXVSCROLL);
-      if (frame_current_style & WS_HSCROLL)
-        reqd_size.br.y += GetSystemMetrics(SM_CXHSCROLL);
-      r.left = reqd_size.tl.x; r.right  = reqd_size.br.x;
-      r.top  = reqd_size.tl.y; r.bottom = reqd_size.br.y;
-      if (tb.isVisible()) r.bottom += tb.getHeight();
-      AdjustWindowRect(&r, GetWindowLong(getHandle(), GWL_STYLE), FALSE);
-      reqd_size = Rect(r.left, r.top, r.right, r.bottom);
-      RECT current;
-      GetWindowRect(getHandle(), &current);
-
-      // Ensure that the window isn't resized too large
-      // If the window is maximized or full-screen then any size is allowed
-      if (!(GetWindowLong(getHandle(), GWL_STYLE) & WS_MAXIMIZE) && !fullScreenActive) {
-        if (wpos->cx > reqd_size.width()) {
-          wpos->cx = reqd_size.width();
-          wpos->x = current.left;
-        }
-        if (wpos->cy > reqd_size.height()) {
-          wpos->cy = reqd_size.height();
-          wpos->y = current.top;
-        }
-      }
-
-    }
-    break;
-
-    // Resize child windows and update window size info we have cached.
-
-  case WM_SIZE:
-    {
-      Point old_offset = bufferToClient(Point(0, 0));
-
-      // Resize the child windows
-      RECT r;
-      GetClientRect(getHandle(), &r);
-      if (tb.isVisible()) {
-        MoveWindow(getFrameHandle(), 0, tb.getHeight(), r.right,
-          r.bottom - tb.getHeight(), TRUE);
-      } else {
-        MoveWindow(getFrameHandle(), 0, 0, r.right, r.bottom, TRUE);
-      }
-      tb.autoSize();
- 
-      // Update the cached sizing information
-      GetWindowRect(getFrameHandle(), &r);
-      window_size = Rect(r.left, r.top, r.right, r.bottom);
-      GetClientRect(getFrameHandle(), &r);
-      client_size = Rect(r.left, r.top, r.right, r.bottom);
-
-      // Determine whether scrollbars are required
-      calculateScrollBars();
-         
-      // Redraw if required
-      if (!old_offset.equals(bufferToClient(Point(0, 0))))
-        InvalidateRect(getFrameHandle(), 0, TRUE);
-    }
-    break;
-
-    // -=- Bump-scrolling
-
-  case WM_TIMER:
-    switch (wParam) {
-    case TIMER_BUMPSCROLL:
-      if (!setViewportOffset(scrolloffset.translate(bumpScrollDelta)))
-        bumpScrollTimer.stop();
-      break;
-    case TIMER_POINTER_INTERVAL:
-    case TIMER_POINTER_3BUTTON:
-      try {
-        ptr.handleTimer(writer(), wParam);
-      } catch (rdr::Exception& e) {
-        close(e.str());
-      }
-      break;
-    }
-    break;
-
-    // -=- Track whether or not the window has focus
-
-  case WM_SETFOCUS:
-    has_focus = true;
-    // Re-register AltTab hotkey
-    setAltTabGrab(options.sendSysKeys);
-    break;
-  case WM_KILLFOCUS:
-    has_focus = false;
-    cursorOutsideBuffer();
-    // Unregister AltTab hotkey
-    setAltTabGrab(false);
-    // Restore the remote keys to consistent states
-    try {
-      kbd.releaseAllKeys(writer());
-    } catch (rdr::Exception& e) {
-      close(e.str());
-    }
-    break;
-
-    // -=- Handle the extra window menu items
-
-    // Process the items added to the system menu
-  case WM_SYSCOMMAND:
-
-    // - First check whether it's one of our messages
-    switch (wParam) {
-    case IDM_FULLSCREEN:
-      options.fullScreen = !options.fullScreen;
-      setFullscreen(options.fullScreen);
-      return 0;
-    case IDM_SHOW_TOOLBAR:
-      toolbar = !toolbar;
-      CheckMenuItem(GetSystemMenu(getHandle(), FALSE), IDM_SHOW_TOOLBAR,
-        (toolbar ? MF_CHECKED : MF_UNCHECKED) | MF_BYCOMMAND);
-      if (toolbar) tb.show();
-      else tb.hide();
-      return 0;
-    case IDM_CTRL_KEY:
-      writeKeyEvent(VK_CONTROL, 0, !kbd.keyPressed(VK_CONTROL));
-      return 0;
-    case IDM_ALT_KEY:
-      writeKeyEvent(VK_MENU, 0, !kbd.keyPressed(VK_MENU));
-      return 0;
-    case IDM_SEND_MENU_KEY:
-      writeKeyEvent(options.menuKey, 0, true);
-      writeKeyEvent(options.menuKey, 0, false);
-      return 0;
-    case IDM_SEND_CAD:
-      writeKeyEvent(VK_CONTROL, 0, true);
-      writeKeyEvent(VK_MENU, 0, true);
-      writeKeyEvent(VK_DELETE, 0, true);
-      writeKeyEvent(VK_DELETE, 0, false);
-      writeKeyEvent(VK_MENU, 0, false);
-      writeKeyEvent(VK_CONTROL, 0, false);
-      return 0;
-    case IDM_SEND_CTLESC:
-      writeKeyEvent(VK_CONTROL, 0, true);
-      writeKeyEvent(VK_ESCAPE, 0, true);
-      writeKeyEvent(VK_CONTROL, 0, false);
-      writeKeyEvent(VK_ESCAPE, 0, false);
-      return 0;
-    case IDM_REQUEST_REFRESH:
-      try {
-        writer()->writeFramebufferUpdateRequest(Rect(0,0,cp.width,cp.height), false);
-        requestUpdate = false;
-      } catch (rdr::Exception& e) {
-        close(e.str());
-      }
-      return 0;
-    case IDM_NEWCONN:
-      manager->addClient(0);
-      return 0;
-    case IDM_OPTIONS:
-      // Update the monitor device name in the CViewOptions instance
-      {
-        MonitorInfo mi(getHandle());
-        options.setMonitor(mi.szDevice);
-        optionsDialog.showDialog(this);
-        return 0;
-      }
-    case IDM_INFO:
-      infoDialog.showDialog(this);
-      return 0;
-    case IDM_ABOUT:
-      AboutDialog::instance.showDialog();
-      return 0;
-    case IDM_FILE_TRANSFER:
-      m_fileTransfer.show(getHandle());
-      return 0;
-    case IDM_CONN_SAVE_AS:
-      return 0;
-    case ID_CLOSE:
-      PostQuitMessage(0);
-      return 0;
-    };
-
-    // - Not one of our messages, so process it as a system message
-    switch (wParam & 0xfff0) {
-
-      // When restored, ensure that full-screen mode is re-enabled if required.
-    case SC_RESTORE:
-      rfb::win32::SafeDefWindowProc(getHandle(), msg, wParam, lParam);
-      setFullscreen(options.fullScreen);
-      return 0;
-
-      // If we are maximized or minimized then that cancels full-screen mode.
-    case SC_MINIMIZE:
-    case SC_MAXIMIZE:
-      setFullscreen(false);
-      break;
-
-      // If the system menu is shown then make sure it's up to date
-    case SC_KEYMENU:
-    case SC_MOUSEMENU:
-      updateF8Menu(false);
-      break;
-
-    };
-    break;
-
-    // Treat all menu commands as system menu commands
-  case WM_COMMAND:
-    SendMessage(getHandle(), WM_SYSCOMMAND, wParam, lParam);
-    return 0;
-
-  case WM_MENUCHAR:
-    vlog.debug("menuchar");
-    break;
-
-    // -=- Handle keyboard input
-
-  case WM_HOTKEY:
-    if (wParam == HOTKEY_ALTTAB) {
-      writeKeyEvent(VK_TAB, 0, true);
-      writeKeyEvent(VK_TAB, 0, false);
-      return 0;
-    }
-    break;
-
-  case WM_SYSKEYDOWN:
-    // Translate Alt-Space and Alt-F4 to WM_SYSCHAR and WM_CLOSE,
-    // since we are not using TranslateMessage(). 
-    if (!options.sendSysKeys) {
-      switch (wParam) {
-      case VK_SPACE:
-	writeKeyEvent(VK_MENU, 0, false);
-	SendMessage(getHandle(), WM_SYSCHAR, wParam, lParam);
-	return 0; 
-      case VK_F4:
-	SendMessage(getHandle(), WM_CLOSE, wParam, lParam);
-	return 0; 
-      }
-    }
-
-  case WM_SYSKEYUP:
-    // When we have registered for AltTab as a hotkey, SYSKEYUPs for
-    // Tabs are sent (but no SYSKEYDOWNs). AltTab is handled by
-    // WM_HOTKEY, though.
-    if (wParam == VK_TAB) return 0; 
-
-  case WM_KEYUP:
-  case WM_KEYDOWN:
-    // Hook the MenuKey to pop-up the window menu
-    if (options.menuKey && (wParam == options.menuKey)) {
-
-      bool ctrlDown = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0;
-      bool altDown = (GetAsyncKeyState(VK_MENU) & 0x8000) != 0;
-      bool shiftDown = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
-      if (!(ctrlDown || altDown || shiftDown)) {
-
-        // If MenuKey is being released then pop-up the menu
-        if ((msg == WM_KEYDOWN)) {
-          // Make sure it's up to date
-          updateF8Menu(true);
-
-          // Show it under the pointer
-          POINT pt;
-          GetCursorPos(&pt);
-          cursorInBuffer = false;
-          TrackPopupMenu(GetSystemMenu(getHandle(), FALSE),
-            TPM_CENTERALIGN | TPM_VCENTERALIGN, pt.x, pt.y, 0, getHandle(), 0);
-        }
-
-	// Ignore the MenuKey keypress for both press & release events
-	return 0;
-      }
-    }
-
-    writeKeyEvent(wParam, lParam, (msg == WM_KEYDOWN) || (msg == WM_SYSKEYDOWN));
-    return 0;
-
-    // -=- Handle the window closing
-
-  case WM_CLOSE:
-    vlog.debug("WM_CLOSE %x", getHandle());
-    if (quit_on_destroy) {
-      vlog.debug("posting WM_QUIT");
-      PostQuitMessage(0);
-    } else {
-      vlog.debug("not posting WM_QUIT");
-    }
-    break;
-
-    // -=- Process incoming socket data
-
-  case WM_USER:
-    readyToRead = true;
-    break;
-
-  }
-
-  return rfb::win32::SafeDefWindowProc(getHandle(), msg, wParam, lParam);
-}
-
-LRESULT CView::processFrameMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
-  switch (msg) {
-
-    // -=- Palette management
-
-  case WM_PALETTECHANGED:
-    vlog.debug("WM_PALETTECHANGED");
-    if ((HWND)wParam == getFrameHandle()) {
-      vlog.debug("ignoring");
-      break;
-    }
-  case WM_QUERYNEWPALETTE:
-    vlog.debug("re-selecting palette");
-    {
-      WindowDC wdc(getFrameHandle());
-      PaletteSelector pSel(wdc, windowPalette.getHandle());
-      if (pSel.isRedrawRequired()) {
-        InvalidateRect(getFrameHandle(), 0, FALSE);
-        UpdateWindow(getFrameHandle());
-      }
-    }
-    return TRUE;
-
-    // Paint the remote frame buffer
-
-  case WM_PAINT:
-    {
-      PAINTSTRUCT ps;
-      HDC paintDC = BeginPaint(getFrameHandle(), &ps);
-      if (!paintDC)
-        throw SystemException("unable to BeginPaint", GetLastError());
-      Rect pr = Rect(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
-
-      if (!pr.is_empty()) {
-
-        // Draw using the correct palette
-        PaletteSelector pSel(paintDC, windowPalette.getHandle());
-
-        if (buffer->bitmap) {
-          // Update the bitmap's palette
-          if (palette_changed) {
-            palette_changed = false;
-            buffer->refreshPalette();
-          }
-
-          // Get device context
-          BitmapDC bitmapDC(paintDC, buffer->bitmap);
-
-          // Blit the border if required
-          Rect bufpos = bufferToClient(buffer->getRect());
-          if (!pr.enclosed_by(bufpos)) {
-            vlog.debug("draw border");
-            HBRUSH black = (HBRUSH) GetStockObject(BLACK_BRUSH);
-            RECT r;
-            SetRect(&r, 0, 0, bufpos.tl.x, client_size.height()); FillRect(paintDC, &r, black);
-            SetRect(&r, bufpos.tl.x, 0, bufpos.br.x, bufpos.tl.y); FillRect(paintDC, &r, black);
-            SetRect(&r, bufpos.br.x, 0, client_size.width(), client_size.height()); FillRect(paintDC, &r, black);
-            SetRect(&r, bufpos.tl.x, bufpos.br.y, bufpos.br.x, client_size.height()); FillRect(paintDC, &r, black);
-          }
-
-          // Do the blit
-          Point buf_pos = clientToBuffer(pr.tl);
-          if (!BitBlt(paintDC, pr.tl.x, pr.tl.y, pr.width(), pr.height(),
-            bitmapDC, buf_pos.x, buf_pos.y, SRCCOPY))
-            throw SystemException("unable to BitBlt to window", GetLastError());
-
-        } else {
-          // Blit a load of black
-          if (!BitBlt(paintDC, pr.tl.x, pr.tl.y, pr.width(), pr.height(),
-            0, 0, 0, BLACKNESS))
-            throw SystemException("unable to BitBlt to blank window", GetLastError());
-        }
-      }
-
-      EndPaint(getFrameHandle(), &ps);
-
-      // - Request the next update from the server, if required
-      requestNewUpdate();
-    }
-    return 0;
-
-    // Process the frame scroll messages
-
-  case WM_VSCROLL:
-  case WM_HSCROLL: 
-    {
-      Point delta;
-      int newpos = (msg == WM_VSCROLL) ? scrolloffset.y : scrolloffset.x;
-
-      switch (LOWORD(wParam)) {
-      case SB_PAGEUP: newpos -= 50; break;
-      case SB_PAGEDOWN: newpos += 50; break;
-      case SB_LINEUP: newpos -= 5; break;
-      case SB_LINEDOWN: newpos += 5; break;
-      case SB_THUMBTRACK:
-      case SB_THUMBPOSITION: newpos = HIWORD(wParam); break;
-      default: vlog.info("received unknown scroll message");
-      };
-
-      if (msg == WM_HSCROLL)
-        setViewportOffset(Point(newpos, scrolloffset.y));
-      else
-        setViewportOffset(Point(scrolloffset.x, newpos));
-  
-      SCROLLINFO si;
-      si.cbSize = sizeof(si); 
-      si.fMask  = SIF_POS; 
-      si.nPos   = newpos; 
-      SetScrollInfo(getFrameHandle(), (msg == WM_VSCROLL) ? SB_VERT : SB_HORZ, &si, TRUE); 
-    }
-    break;
-
-    // -=- Cursor shape/visibility handling
-
-  case WM_SETCURSOR:
-    if (LOWORD(lParam) != HTCLIENT)
-      break;
-    SetCursor(cursorInBuffer ? dotCursor : arrowCursor);
-    return TRUE;
-
-  case WM_MOUSELEAVE:
-    trackingMouseLeave = false;
-    cursorOutsideBuffer();
-    return 0;
-
-    // -=- Mouse input handling
-
-  case WM_MOUSEMOVE:
-  case WM_LBUTTONUP:
-  case WM_MBUTTONUP:
-  case WM_RBUTTONUP:
-  case WM_LBUTTONDOWN:
-  case WM_MBUTTONDOWN:
-  case WM_RBUTTONDOWN:
-  case WM_MOUSEWHEEL:
-    if (has_focus)
-    {
-      if (!trackingMouseLeave) {
-        TRACKMOUSEEVENT tme;
-        tme.cbSize = sizeof(TRACKMOUSEEVENT);
-        tme.dwFlags = TME_LEAVE;
-        tme.hwndTrack = getFrameHandle();
-        _TrackMouseEvent(&tme);
-        trackingMouseLeave = true;
-      }
-      int mask = 0;
-      if (LOWORD(wParam) & MK_LBUTTON) mask |= 1;
-      if (LOWORD(wParam) & MK_MBUTTON) mask |= 2;
-      if (LOWORD(wParam) & MK_RBUTTON) mask |= 4;
-
-      if (msg == WM_MOUSEWHEEL) {
-        int delta = (short)HIWORD(wParam);
-        int repeats = (abs(delta)+119) / 120;
-        int wheelMask = (delta > 0) ? 8 : 16;
-        vlog.debug("repeats %d, mask %d\n",repeats,wheelMask);
-        for (int i=0; i<repeats; i++) {
-          writePointerEvent(oldpos.x, oldpos.y, mask | wheelMask);
-          writePointerEvent(oldpos.x, oldpos.y, mask);
-        }
-      } else {
-        Point clientPos = Point(LOWORD(lParam), HIWORD(lParam));
-        Point p = clientToBuffer(clientPos);
-
-        // If the mouse is not within the server buffer area, do nothing
-        cursorInBuffer = buffer->getRect().contains(p);
-        if (!cursorInBuffer) {
-          cursorOutsideBuffer();
-          break;
-        }
-
-        // If we're locally rendering the cursor then redraw it
-        if (cursorAvailable) {
-          // - Render the cursor!
-          if (!p.equals(cursorPos)) {
-            hideLocalCursor();
-            cursorPos = p;
-            showLocalCursor();
-            if (cursorVisible)
-              hideSystemCursor();
-          }
-        }
-
-        // If we are doing bump-scrolling then try that first...
-        if (processBumpScroll(clientPos))
-          break;
-
-        // Send a pointer event to the server
-        writePointerEvent(p.x, p.y, mask);
-        oldpos = p;
-      }
-    } else {
-      cursorOutsideBuffer();
-    }
-    break;
-  }
-
-  return rfb::win32::SafeDefWindowProc(getFrameHandle(), msg, wParam, lParam);
-}
-
-void CView::blockCallback() {
-  // - An InStream has blocked on I/O while processing an RFB message
-  //   We re-enable socket event notifications, so we'll know when more
-  //   data is available, then we sit and dispatch window events until
-  //   the notification arrives.
-  readyToRead = false;
-  WSAAsyncSelect(sock->getFd(), getHandle(), WM_USER, FD_READ | FD_CLOSE);
-  MSG msg;
-  while (true) {
-    if (readyToRead) {
-      // - Network event notification.  Return control to I/O routine.
-      WSAAsyncSelect(sock->getFd(), getHandle(), WM_USER, 0);
-      return;
-    }
-
-    DWORD result = GetMessage(&msg, NULL, 0, 0);
-    if (result == 0) {
-      vlog.debug("WM_QUIT");
-      throw QuitMessage(msg.wParam);
-    } else if (result < 0) {
-      throw rdr::SystemException("GetMessage error", GetLastError());
-    }
-
-    // IMPORTANT: We mustn't call TranslateMessage() here, because instead we
-    // call ToAscii() in CKeyboard::keyEvent().  ToAscii() stores dead key
-    // state from one call to the next, which would be messed up by calls to
-    // TranslateMessage() (actually it looks like TranslateMessage() calls
-    // ToAscii() internally).
-    DispatchMessage(&msg);
-  }
-}
-
-
-void
-CView::hideLocalCursor() {
-  // - Blit the cursor backing store over the cursor
-  // *** ALWAYS call this BEFORE changing buffer PF!!!
-  if (cursorVisible) {
-    cursorVisible = false;
-    buffer->imageRect(cursorBackingRect, cursorBacking.data);
-    invalidateBufferRect(cursorBackingRect);
-  }
-}
-
-void
-CView::showLocalCursor() {
-  if (cursorAvailable && !cursorVisible && cursorInBuffer) {
-    if (!cp.pf().equal(cursor.getPF()) ||
-      cursor.getRect().is_empty()) {
-      vlog.info("attempting to render invalid local cursor");
-      cursorAvailable = false;
-      showSystemCursor();
-      return;
-    }
-    cursorVisible = true;
-    
-    cursorBackingRect = cursor.getRect().translate(cursorPos).translate(cursor.hotspot.negate());
-    cursorBackingRect = cursorBackingRect.intersect(buffer->getRect());
-    buffer->getImage(cursorBacking.data, cursorBackingRect);
-
-    renderLocalCursor();
-
-    invalidateBufferRect(cursorBackingRect);
-  }
-}
-
-void CView::cursorOutsideBuffer()
-{
-  cursorInBuffer = false;
-  hideLocalCursor();
-  showSystemCursor();
-}
-
-void
-CView::renderLocalCursor()
-{
-  Rect r = cursor.getRect();
-  r = r.translate(cursorPos).translate(cursor.hotspot.negate());
-  buffer->maskRect(r, cursor.data, cursor.mask.buf);
-}
-
-void
-CView::hideSystemCursor() {
-  if (systemCursorVisible) {
-    vlog.debug("hide system cursor");
-    systemCursorVisible = false;
-    ShowCursor(FALSE);
-  }
-}
-
-void
-CView::showSystemCursor() {
-  if (!systemCursorVisible) {
-    vlog.debug("show system cursor");
-    systemCursorVisible = true;
-    ShowCursor(TRUE);
-  }
-}
-
-void
-CView::setAltTabGrab(bool grab) {
-  BOOL hotKeyResult;
-  static bool grabstate = false;
-
-  // Do not call RegisterHotKey/UnregisterHotKey if not necessary
-  if (grabstate == grab)
-    return;
-
-  grabstate = grab;
-    
-  // Only works for NT/2k/XP
-  if (grab) {
-    hotKeyResult = RegisterHotKey(hwnd, HOTKEY_ALTTAB, MOD_ALT, VK_TAB);
-    if (!hotKeyResult)
-      vlog.debug("RegisterHotkey failed with error %d", GetLastError());
-  } else {
-    hotKeyResult = UnregisterHotKey(hwnd, HOTKEY_ALTTAB);
-  }
-}
-
-bool
-CView::invalidateBufferRect(const Rect& crect) {
-  Rect rect = bufferToClient(crect);
-  if (rect.intersect(client_size).is_empty()) return false;
-  RECT invalid = {rect.tl.x, rect.tl.y, rect.br.x, rect.br.y};
-  InvalidateRect(getFrameHandle(), &invalid, FALSE);
-  return true;
-}
-
-
-void
-CView::notifyClipboardChanged(const char* text, int len) {
-  if (!options.clientCutText) return;
-  if (state() != RFBSTATE_NORMAL) return;
-  try {
-    writer()->writeClientCutText(text, len);
-  } catch (rdr::Exception& e) {
-    close(e.str());
-  }
-}
-
-
-CSecurity* CView::getCSecurity(int secType)
-{
-  switch (secType) {
-  case secTypeNone:
-    return new CSecurityNone();
-  case secTypeVncAuth:
-    return new CSecurityVncAuth(this);
-  default:
-    throw Exception("Unsupported secType?");
-  }
-}
-
-
-void
-CView::setColourMapEntries(int first, int count, U16* rgbs) {
-  vlog.debug("setColourMapEntries: first=%d, count=%d", first, count);
-  int i;
-  for (i=0;i<count;i++) {
-    buffer->setColour(i+first, rgbs[i*3], rgbs[i*3+1], rgbs[i*3+2]);
-  }
-  // *** change to 0, 256?
-  refreshWindowPalette(first, count);
-  palette_changed = true;
-  InvalidateRect(getHandle(), 0, FALSE);
-}
-
-void
-CView::bell() {
-  if (options.acceptBell)
-    MessageBeep(-1);
-}
-
-
-void
-CView::setDesktopSize(int w, int h) {
-  vlog.debug("setDesktopSize %dx%d", w, h);
-
-  // If the locally-rendered cursor is visible then remove it
-  hideLocalCursor();
-
-  // Resize the backing buffer
-  buffer->setSize(w, h);
-
-  // If the window is not maximised or full-screen then resize it
-  if (!(GetWindowLong(getHandle(), GWL_STYLE) & WS_MAXIMIZE) && !fullScreenActive) {
-    // Resize the window to the required size
-    RECT r = {0, 0, w, h};
-    AdjustWindowRectEx(&r, GetWindowLong(getFrameHandle(), GWL_STYLE), FALSE,
-      GetWindowLong(getFrameHandle(), GWL_EXSTYLE));
-    if (tb.isVisible()) r.bottom += tb.getHeight();
-    AdjustWindowRect(&r, GetWindowLong(getHandle(), GWL_STYLE), FALSE);
-    SetWindowPos(getHandle(), 0, 0, 0, r.right-r.left, r.bottom-r.top,
-      SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
-
-    // Move the window to the desired monitor
-    if (options.monitor.buf)
-      moveToMonitor(getHandle(), options.monitor.buf);
-
-    // Clip to the system work area
-    centerWindow(getHandle(), 0, true);
-  } else {
-    // Ensure the screen contents are consistent
-    InvalidateRect(getFrameHandle(), 0, FALSE);
-  }
-
-  // Tell the underlying CConnection
-  CConnection::setDesktopSize(w, h);
-
-  // Enable/disable scrollbars as appropriate
-  calculateScrollBars();
-}
-
-void
-CView::setCursor(const Point& hotspot, const Point& size, void* data, void* mask) {
-  if (!options.useLocalCursor) return;
-  hideLocalCursor();
-
-  cursor.hotspot = hotspot;
-
-  cursor.setSize(size.x, size.y);
-  cursor.setPF(cp.pf());
-  cursor.imageRect(cursor.getRect(), data);
-  memcpy(cursor.mask.buf, mask, cursor.maskLen());
-  cursor.crop();
-
-  cursorBacking.setSize(size.x, size.y);
-  cursorBacking.setPF(cp.pf());
-
-  cursorAvailable = true;
-
-  showLocalCursor();
-}
-
-PixelFormat
-CView::getNativePF() const {
-  vlog.debug("getNativePF()");
-  return WindowDC(getHandle()).getPF();
-}
-
-void
-CView::setVisible(bool visible) {
-  ShowWindow(getHandle(), visible ? SW_SHOW : SW_HIDE);
-  if (visible) {
-    // When the window becomes visible, make it active
-    SetForegroundWindow(getHandle());
-    SetActiveWindow(getHandle());
-    // If the window should be full-screen, then do so
-    setFullscreen(options.fullScreen);
-  } else {
-    // Disable full-screen mode
-    setFullscreen(false);
-  }
-}
-
-void
-CView::close(const char* reason) {
-  setVisible(false);
-  if (reason) {
-    vlog.info("closing - %s", reason);
-    MsgBox(NULL, TStr(reason), MB_ICONINFORMATION | MB_OK);
-  }
-  SendMessage(getHandle(), WM_CLOSE, 0, 0);
-}
-
-
-void
-CView::framebufferUpdateEnd() {
-  if (debugDelay != 0) {
-    vlog.debug("debug delay %d",(int)debugDelay);
-    UpdateWindow(getHandle());
-    Sleep(debugDelay);
-    std::list<rfb::Rect>::iterator i;
-    for (i = debugRects.begin(); i != debugRects.end(); i++) {
-      invertRect(*i);
-    }
-    debugRects.clear();
-  }
-  if (options.autoSelect)
-    autoSelectFormatAndEncoding();
-
-  // Always request the next update
-  requestUpdate = true;
-
-  // Check that at least part of the window has changed
-  if (!GetUpdateRect(getHandle(), 0, FALSE)) {
-    if (!(GetWindowLong(getHandle(), GWL_STYLE) & WS_MINIMIZE))
-      requestNewUpdate();
-  }
-
-  showLocalCursor();
-}
-
-
-// Note: The method below is duplicated in vncviewer_unix/CConn.cxx!
-
-// autoSelectFormatAndEncoding() chooses the format and encoding appropriate
-// to the connection speed:
-//
-//   Above 16Mbps (timing for at least a second), switch to hextile
-//   Otherwise, switch to ZRLE
-//
-//   Above 256Kbps, use full colour mode
-//
-void 
-CView::autoSelectFormatAndEncoding() {
-  int kbitsPerSecond = sock->inStream().kbitsPerSecond();
-  unsigned int newEncoding = options.preferredEncoding;
-  bool newFullColour = options.fullColour;
-  unsigned int timeWaited = sock->inStream().timeWaited();
-
-  // Select best encoding
-  if (kbitsPerSecond > 16000 && timeWaited >= 10000) {
-    newEncoding = encodingHextile;
-  } else {
-    newEncoding = encodingZRLE;
-  }
-
-  if (newEncoding != options.preferredEncoding) {
-    vlog.info("Throughput %d kbit/s - changing to %s encoding",
-              kbitsPerSecond, encodingName(newEncoding));
-    options.preferredEncoding = newEncoding;
-    encodingChange = true;
-  }
-
-  if (kbitsPerSecond == 0) {
-    return;
-  }
-
-  if (cp.beforeVersion(3, 8)) {
-    // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with
-    // cursors "asynchronously". If this happens in the middle of a
-    // pixel format change, the server will encode the cursor with
-    // the old format, but the client will try to decode it
-    // according to the new format. This will lead to a
-    // crash. Therefore, we do not allow automatic format change for
-    // old servers.
-    return;
-  }
-  
-  // Select best color level
-  newFullColour = (kbitsPerSecond > 256);
-  if (newFullColour != options.fullColour) {
-    vlog.info("Throughput %d kbit/s - full color is now %s", 
-	      kbitsPerSecond,
-	      newFullColour ? "enabled" : "disabled");
-    options.fullColour = newFullColour;
-    formatChange = true;
-  } 
-}
-
-void
-CView::requestNewUpdate() {
-  if (!requestUpdate) return;
-
-  if (formatChange) {
-    // Hide the rendered cursor, if any, to prevent
-    // the backing buffer being used in the wrong format
-    hideLocalCursor();
-
-    // Select the required pixel format
-    if (options.fullColour) {
-      buffer->setPF(fullColourPF);
-    } else {
-      switch (options.lowColourLevel) {
-      case 0:
-        buffer->setPF(PixelFormat(8,3,0,1,1,1,1,2,1,0));
-        break;
-      case 1:
-        buffer->setPF(PixelFormat(8,6,0,1,3,3,3,4,2,0));
-        break;
-      case 2:
-        buffer->setPF(PixelFormat(8,8,0,0,0,0,0,0,0,0));
-        break;
-      }
-    }
-
-    // Print the current pixel format
-    char str[256];
-    buffer->getPF().print(str, 256);
-    vlog.info("Using pixel format %s",str);
-
-    // Save the connection pixel format and tell server to use it
-    cp.setPF(buffer->getPF());
-    writer()->writeSetPixelFormat(cp.pf());
-
-    // Correct the local window's palette
-    if (!getNativePF().trueColour)
-      refreshWindowPalette(0, 1 << cp.pf().depth);
-  }
-
-  if (encodingChange) {
-    vlog.info("Using %s encoding",encodingName(options.preferredEncoding));
-    writer()->writeSetEncodings(options.preferredEncoding, true);
-  }
-
-  writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height),
-                                          !formatChange);
-
-  encodingChange = formatChange = requestUpdate = false;
-}
-
-
-void
-CView::writeKeyEvent(rdr::U8 vkey, rdr::U32 flags, bool down) {
-  if (!options.sendKeyEvents) return;
-  try {
-    kbd.keyEvent(writer(), vkey, flags, down);
-  } catch (rdr::Exception& e) {
-    close(e.str());
-  }
-}
-
-void
-CView::writePointerEvent(int x, int y, int buttonMask) {
-  if (!options.sendPtrEvents) return;
-  try {
-    ptr.pointerEvent(writer(), x, y, buttonMask);
-  } catch (rdr::Exception& e) {
-    close(e.str());
-  }
-}
-
-
-void
-CView::refreshWindowPalette(int start, int count) {
-  vlog.debug("refreshWindowPalette(%d, %d)", start, count);
-
-  Colour colours[256];
-  if (count > 256) {
-    vlog.debug("%d palette entries", count);
-    throw rdr::Exception("too many palette entries");
-  }
-
-  // Copy the palette from the DIBSectionBuffer
-  ColourMap* cm = buffer->getColourMap();
-  if (!cm) return;
-  for (int i=0; i<count; i++) {
-    int r, g, b;
-    cm->lookup(i, &r, &g, &b);
-    colours[i].r = r;
-    colours[i].g = g;
-    colours[i].b = b;
-  }
-
-  // Set the window palette
-  windowPalette.setEntries(start, count, colours);
-
-  // Cause the window to be redrawn
-  InvalidateRect(getHandle(), 0, 0);
-}
-
-
-void CView::calculateScrollBars() {
-  // Calculate the required size of window
-  DWORD current_style = GetWindowLong(getFrameHandle(), GWL_STYLE);
-  DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL);
-  DWORD ex_style = GetWindowLong(getFrameHandle(), GWL_EXSTYLE);
-  DWORD old_style;
-  RECT r;
-  SetRect(&r, 0, 0, buffer->width(), buffer->height());
-  AdjustWindowRectEx(&r, style, FALSE, ex_style);
-  Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom);
-
-  if (!bumpScroll) {
-    // We only enable scrollbars if bump-scrolling is not active.
-    // Effectively, this means if full-screen is not active,
-    // but I think it's better to make these things explicit.
-    // Work out whether scroll bars are required
-    do {
-      old_style = style;
-
-      if (!(style & WS_HSCROLL) && (reqd_size.width() > window_size.width())) {
-        style |= WS_HSCROLL;
-        reqd_size.br.y += GetSystemMetrics(SM_CXHSCROLL);
-      }
-      if (!(style & WS_VSCROLL) && (reqd_size.height() > window_size.height())) {
-        style |= WS_VSCROLL;
-        reqd_size.br.x += GetSystemMetrics(SM_CXVSCROLL);
-      }
-    } while (style != old_style);
-  }
-  
-  // Tell Windows to update the window style & cached settings
-  if (style != current_style) {
-    SetWindowLong(getFrameHandle(), GWL_STYLE, style);
-    SetWindowPos(getFrameHandle(), NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
-  }
-
-  // Update the scroll settings
-  SCROLLINFO si;
-  if (style & WS_VSCROLL) {
-    si.cbSize = sizeof(si);
-    si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS;
-    si.nMin   = 0;
-    si.nMax   = buffer->height();
-    si.nPage  = buffer->height() - (reqd_size.height() - window_size.height());
-    maxscrolloffset.y = max(0, si.nMax-si.nPage);
-    scrolloffset.y = min(maxscrolloffset.y, scrolloffset.y);
-    si.nPos   = scrolloffset.y;
-    SetScrollInfo(getFrameHandle(), SB_VERT, &si, TRUE);
-  }
-  if (style & WS_HSCROLL) {
-    si.cbSize = sizeof(si);
-    si.fMask  = SIF_RANGE | SIF_PAGE | SIF_POS;
-    si.nMin   = 0;
-    si.nMax   = buffer->width();
-    si.nPage  = buffer->width() - (reqd_size.width() - window_size.width());
-    maxscrolloffset.x = max(0, si.nMax-si.nPage);
-    scrolloffset.x = min(maxscrolloffset.x, scrolloffset.x);
-    si.nPos   = scrolloffset.x;
-    SetScrollInfo(getFrameHandle(), SB_HORZ, &si, TRUE);
-  }
-
-  // Update the cached client size
-  GetClientRect(getFrameHandle(), &r);
-  client_size = Rect(r.left, r.top, r.right, r.bottom);
-}
-
-
-void
-CView::calculateFullColourPF() {
-  // If the server is palette based then use palette locally
-  // Also, don't bother doing bgr222
-  if (!serverDefaultPF.trueColour || (serverDefaultPF.depth < 6)) {
-    fullColourPF = serverDefaultPF;
-    options.fullColour = true;
-  } else {
-    // If server is trueColour, use lowest depth PF
-    PixelFormat native = getNativePF();
-    if ((serverDefaultPF.bpp < native.bpp) ||
-      ((serverDefaultPF.bpp == native.bpp) &&
-      (serverDefaultPF.depth < native.depth)))
-      fullColourPF = serverDefaultPF;
-    else
-      fullColourPF = getNativePF();
-  }
-  formatChange = true;
-}
-
-
-void
-CView::updateF8Menu(bool hideSystemCommands) {
-  HMENU menu = GetSystemMenu(getHandle(), FALSE);
-
-  if (hideSystemCommands) {  
-    // Gray out menu items that might cause a World Of Pain
-    HMENU menu = GetSystemMenu(getHandle(), FALSE);
-    EnableMenuItem(menu, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
-    EnableMenuItem(menu, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
-    EnableMenuItem(menu, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED);
-    EnableMenuItem(menu, SC_MINIMIZE, MF_BYCOMMAND | MF_ENABLED);
-    EnableMenuItem(menu, SC_MAXIMIZE, MF_BYCOMMAND | MF_ENABLED);
-  }
-
-  // Update the modifier key menu items
-  UINT ctrlCheckFlags = kbd.keyPressed(VK_CONTROL) ? MF_CHECKED : MF_UNCHECKED;
-  UINT altCheckFlags = kbd.keyPressed(VK_MENU) ? MF_CHECKED : MF_UNCHECKED;
-  CheckMenuItem(menu, IDM_CTRL_KEY, MF_BYCOMMAND | ctrlCheckFlags);
-  CheckMenuItem(menu, IDM_ALT_KEY, MF_BYCOMMAND | altCheckFlags);
-
-  // Ensure that the Send <MenuKey> menu item has the correct text
-  if (options.menuKey) {
-    TCharArray menuKeyStr(options.menuKeyName());
-    TCharArray tmp(_tcslen(menuKeyStr.buf) + 6);
-    _stprintf(tmp.buf, _T("Send %s"), menuKeyStr.buf);
-    if (!ModifyMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf))
-      InsertMenu(menu, IDM_SEND_CAD, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf);
-  } else {
-    RemoveMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND);
-  }
-}
-
-
-void
-CView::setName(const char* name) {
-  vlog.debug("setName %s", name);
-  ::SetWindowText(getHandle(), TStr(name));
-  CConnection::setName(name);
-}
-
-
-void CView::serverInit() {
-  CConnection::serverInit();
-
-  // If using AutoSelect with old servers, start in FullColor
-  // mode. See comment in autoSelectFormatAndEncoding. 
-  if (cp.beforeVersion(3, 8) && options.autoSelect) {
-    options.fullColour = true;
-  }
-
-  // Save the server's current format
-  serverDefaultPF = cp.pf();
-
-  // Calculate the full-colour format to use
-  calculateFullColourPF();
-
-  // Request the initial update
-  vlog.info("requesting initial update");
-  formatChange = encodingChange = requestUpdate = true;
-  requestNewUpdate();
-
-  // Show the window
-  setVisible(true);
-}
-
-void
-CView::serverCutText(const char* str, int len) {
-  if (!options.serverCutText) return;
-  CharArray t(len+1);
-  memcpy(t.buf, str, len);
-  t.buf[len] = 0;
-  clipboard.setClipText(t.buf);
-}
-
-
-void CView::beginRect(const Rect& r, unsigned int encoding) {
-  sock->inStream().startTiming();
-}
-
-void CView::endRect(const Rect& r, unsigned int encoding) {
-  sock->inStream().stopTiming();
-  lastUsedEncoding_ = encoding;
-  if (debugDelay != 0) {
-    invertRect(r);
-    debugRects.push_back(r);
-  }
-}
-
-void CView::fillRect(const Rect& r, Pixel pix) {
-  if (cursorBackingRect.overlaps(r)) hideLocalCursor();
-  buffer->fillRect(r, pix);
-  invalidateBufferRect(r);
-}
-void CView::imageRect(const Rect& r, void* pixels) {
-  if (cursorBackingRect.overlaps(r)) hideLocalCursor();
-  buffer->imageRect(r, pixels);
-  invalidateBufferRect(r);
-}
-void CView::copyRect(const Rect& r, int srcX, int srcY) {
-  if (cursorBackingRect.overlaps(r) ||
-      cursorBackingRect.overlaps(Rect(srcX, srcY, srcX+r.width(), srcY+r.height())))
-    hideLocalCursor();
-  buffer->copyRect(r, Point(r.tl.x-srcX, r.tl.y-srcY));
-  invalidateBufferRect(r);
-}
-
-void CView::invertRect(const Rect& r) {
-  int stride;
-  rdr::U8* p = buffer->getPixelsRW(r, &stride);
-  for (int y = 0; y < r.height(); y++) {
-    for (int x = 0; x < r.width(); x++) {
-      switch (buffer->getPF().bpp) {
-      case 8:  ((rdr::U8* )p)[x+y*stride] ^= 0xff;       break;
-      case 16: ((rdr::U16*)p)[x+y*stride] ^= 0xffff;     break;
-      case 32: ((rdr::U32*)p)[x+y*stride] ^= 0xffffffff; break;
-      }
-    }
-  }
-  invalidateBufferRect(r);
-}
-
-bool CView::getUserPasswd(char** user, char** password) {
-  if (!user && options.passwordFile.buf[0]) {
-    FILE* fp = fopen(options.passwordFile.buf, "rb");
-    if (!fp) return false;
-    char data[256];
-    int datalen = fread(data, 1, 256, fp);
-    fclose(fp);
-    if (datalen != 8) return false;
-    vncAuthUnobfuscatePasswd(data);
-    *password = strDup(data);
-    memset(data, 0, strlen(data));
-    return true;
-  }
-
-  if (user && options.userName.buf)
-    *user = strDup(options.userName.buf);
-  if (password && options.password.buf)
-    *password = strDup(options.password.buf);
-  if ((user && !*user) || (password && !*password)) {
-    // Missing username or password - prompt the user
-    UserPasswdDialog userPasswdDialog;
-    userPasswdDialog.setCSecurity(getCurrentCSecurity());
-    if (!userPasswdDialog.getUserPasswd(user, password))
-      return false;
-  }
-  if (user) options.setUserName(*user);
-  if (password) options.setPassword(*password);
-  return true;
-}
-
-bool CView::processFTMsg(int type)
-{
-  return m_fileTransfer.processFTMsg(type);
-}
\ No newline at end of file
diff --git a/vncviewer/cview.h b/vncviewer/cview.h
deleted file mode 100644
index 0dc0066..0000000
--- a/vncviewer/cview.h
+++ /dev/null
@@ -1,312 +0,0 @@
-/* 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.
- */
-
-// -=- CView.h
-
-// An instance of the CView class is created for each VNC Viewer connection.
-
-#ifndef __RFB_WIN32_CVIEW_H__
-#define __RFB_WIN32_CVIEW_H__
-
-#include <network/Socket.h>
-
-#include <rfb/CConnection.h>
-#include <rfb/Cursor.h>
-#include <rfb/UserPasswdGetter.h>
-
-#include <rfb_win32/Clipboard.h>
-#include <rfb_win32/DIBSectionBuffer.h>
-#include <rfb_win32/Win32Util.h>
-#include <rfb_win32/Registry.h>
-#include <rfb_win32/AboutDialog.h>
-#include <rfb_win32/CKeyboard.h>
-#include <rfb_win32/CPointer.h>
-
-#include <vncviewer/InfoDialog.h>
-#include <vncviewer/OptionsDialog.h>
-#include <vncviewer/ViewerToolBar.h>
-#include <vncviewer/CViewOptions.h>
-#include <vncviewer/CViewManager.h>
-#include <vncviewer/FileTransfer.h>
-#include <list>
-
-
-namespace rfb {
-
-  namespace win32 {
-
-    class CView : public CConnection,
-                  public UserPasswdGetter,
-                  rfb::win32::Clipboard::Notifier,
-                  rdr::FdInStreamBlockCallback
-    {
-    public:
-      CView();
-      virtual ~CView();
-
-      bool initialise(network::Socket* s);
-
-      void setManager(CViewManager* m) {manager = m;}
-
-      void applyOptions(CViewOptions& opt);
-      const CViewOptions& getOptions() const {return options;};
-
-      // -=- Window Message handling
-
-      virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam);
-      virtual LRESULT processFrameMessage(UINT msg, WPARAM wParam, LPARAM lParam);
-
-      // -=- Socket blocking handling
-      //     blockCallback will throw QuitMessage(result) when
-      //     it processes a WM_QUIT message.
-      //     The caller may catch that to cope gracefully with
-      //     a request to quit.
-
-      class QuitMessage : public rdr::Exception {
-      public:
-        QuitMessage(WPARAM wp) : rdr::Exception("QuitMessage") {}
-        WPARAM wParam;
-      };
-      virtual void blockCallback();
-
-      // -=- Window interface
-
-      void postQuitOnDestroy(bool qod) {quit_on_destroy = qod;}
-      PixelFormat getNativePF() const;
-      void setVisible(bool visible);
-      void close(const char* reason=0);
-      HWND getHandle() const {return hwnd;}
-      HWND getFrameHandle() const {return frameHwnd;}
-
-      void notifyClipboardChanged(const char* text, int len);
-
-      // -=- Coordinate conversions
-
-      inline Point bufferToClient(const Point& p) {
-        Point pos = p;
-        if (client_size.width() > buffer->width())
-          pos.x += (client_size.width() - buffer->width()) / 2;
-        else if (client_size.width() < buffer->width())
-          pos.x -= scrolloffset.x;
-        if (client_size.height() > buffer->height())
-          pos.y += (client_size.height() - buffer->height()) / 2;
-        else if (client_size.height() < buffer->height())
-          pos.y -= scrolloffset.y;
-        return pos;
-      }
-      inline Rect bufferToClient(const Rect& r) {
-        return Rect(bufferToClient(r.tl), bufferToClient(r.br));
-      }
-
-      inline Point clientToBuffer(const Point& p) {
-        Point pos = p;
-        if (client_size.width() > buffer->width())
-          pos.x -= (client_size.width() - buffer->width()) / 2;
-        else if (client_size.width() < buffer->width())
-          pos.x += scrolloffset.x;
-        if (client_size.height() > buffer->height())
-          pos.y -= (client_size.height() - buffer->height()) / 2;
-        else if (client_size.height() < buffer->height())
-          pos.y += scrolloffset.y;
-        return pos;
-      }
-      inline Rect clientToBuffer(const Rect& r) {
-        return Rect(clientToBuffer(r.tl), clientToBuffer(r.br));
-      }
-
-      void setFullscreen(bool fs);
-
-      bool setViewportOffset(const Point& tl);
-
-      bool processBumpScroll(const Point& cursorPos);
-      void setBumpScroll(bool on);
-
-      int lastUsedEncoding() const { return lastUsedEncoding_; }
-
-      // -=- CConnection interface overrides
-
-      virtual CSecurity* getCSecurity(int secType);
-
-      virtual void setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs);
-      virtual void bell();
-
-      virtual void framebufferUpdateEnd();
-
-      virtual void setDesktopSize(int w, int h);
-      virtual void setCursor(const Point& hotspot, const Point& size, void* data, void* mask);
-      virtual void setName(const char* name);
-      virtual void serverInit();
-
-      virtual void serverCutText(const char* str, int len);
-
-      virtual void beginRect(const Rect& r, unsigned int encoding);
-      virtual void endRect(const Rect& r, unsigned int encoding);
-
-      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);
-
-      void invertRect(const Rect& r);
-
-      // VNCviewer dialog objects
-
-      OptionsDialog optionsDialog;
-
-      friend class InfoDialog;
-      InfoDialog infoDialog;
-
-      // UserPasswdGetter overrides, used to support a pre-supplied VNC password
-      virtual bool getUserPasswd(char** user, char** password);
-
-      // Global user-config registry key
-      static RegKey userConfigKey;
-
-      bool processFTMsg(int type);
-
-    protected:
-
-      // Locally-rendered VNC cursor
-      void hideLocalCursor();
-      void showLocalCursor();
-      void renderLocalCursor();
-
-      // The system-rendered cursor
-      void hideSystemCursor();
-      void showSystemCursor();
-
-      // Grab AltTab?
-      void setAltTabGrab(bool grab);
-
-      // cursorOutsideBuffer() is called whenever we detect that the mouse has
-      // moved outside the desktop.  It restores the system arrow cursor.
-      void cursorOutsideBuffer();
-
-      // Returns true if part of the supplied rect is visible, false otherwise
-      bool invalidateBufferRect(const Rect& crect);
-
-      // Auto-encoding selector
-      void autoSelectFormatAndEncoding();
-
-      // Request an update with appropriate setPixelFormat and setEncodings calls
-      void requestNewUpdate();
-
-      // Update the window palette if the display is palette-based.
-      // Colours are pulled from the DIBSectionBuffer's ColourMap.
-      // Only the specified range of indexes is dealt with.
-      // After the update, the entire window is redrawn.
-      void refreshWindowPalette(int start, int count);
-
-      // Determine whether or not we need to enable/disable scrollbars and set the
-      // window style accordingly
-      void calculateScrollBars();
-
-      // Recalculate the most suitable full-colour pixel format
-      void calculateFullColourPF();
-
-      // Enable/disable/check/uncheck the F8 menu items as appropriate.
-      void updateF8Menu(bool hideSystemCommands);
-
-      // VNCviewer options
-
-      CViewOptions options;
-
-      // Input handling
-      void writeKeyEvent(rdr::U8 vkey, rdr::U32 flags, bool down);
-      void writePointerEvent(int x, int y, int buttonMask);
-      rfb::win32::CKeyboard kbd;
-      rfb::win32::CPointer ptr;
-      Point oldpos;
-
-      // Clipboard handling
-      rfb::win32::Clipboard clipboard;
-
-      // Pixel format and encoding
-      PixelFormat serverDefaultPF;
-      PixelFormat fullColourPF;
-      bool sameMachine;
-      bool encodingChange;
-      bool formatChange;
-      int lastUsedEncoding_;
-
-      // Networking and RFB protocol
-      network::Socket* sock;
-      bool readyToRead;
-      bool requestUpdate;
-
-      // Palette handling
-      LogicalPalette windowPalette;
-      bool palette_changed;
-
-      // - Full-screen mode
-      Rect fullScreenOldRect;
-      DWORD fullScreenOldFlags;
-      bool fullScreenActive;
-
-      // Bump-scrolling (used in full-screen mode)
-      bool bumpScroll;
-      Point bumpScrollDelta;
-      IntervalTimer bumpScrollTimer;
-
-      // Cursor handling
-      Cursor cursor;
-      bool systemCursorVisible;  // Should system-cursor be drawn?
-      bool trackingMouseLeave;
-      bool cursorInBuffer;    // Is cursor position within server buffer? (ONLY for LocalCursor)
-      bool cursorVisible;     // Is cursor currently rendered?
-      bool cursorAvailable;   // Is cursor available for rendering?
-      Point cursorPos;
-      ManagedPixelBuffer cursorBacking;
-      Rect cursorBackingRect;
-
-      // ** Debugging/logging
-      /*
-      int update_rect_count;
-      int update_pixel_count;
-      Rect update_extent;
-      */
-      std::list<Rect> debugRects;
-
-      // ToolBar handling
-      ViewerToolBar tb;
-      bool toolbar;
-
-      // Local window state
-      win32::DIBSectionBuffer* buffer;
-      bool has_focus;
-      bool quit_on_destroy;
-      Rect window_size;
-      Rect client_size;
-      Point scrolloffset;
-      Point maxscrolloffset;
-      HWND hwnd;
-      HWND frameHwnd;
-
-      // Handle back to CViewManager instance, if any
-      CViewManager* manager;
-
-      FileTransfer m_fileTransfer;
-
-    };
-
-  };
-
-};
-
-#endif
-
-
diff --git a/vncviewer/msvcwarning.h b/vncviewer/msvcwarning.h
deleted file mode 100644
index 53a0678..0000000
--- a/vncviewer/msvcwarning.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/* 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 : 4800 ) // forcing bool 'true' or 'false'
-#pragma warning( disable : 4786 ) // debug identifier truncated
diff --git a/vncviewer/resource.h b/vncviewer/resource.h
index 59eee86..5493fd0 100644
--- a/vncviewer/resource.h
+++ b/vncviewer/resource.h
@@ -16,9 +16,9 @@
 #define IDR_TRAY                        112
 #define IDD_CONNECTION_INFO             113
 #define IDD_DEFAULTS                    116
-#define IDD_FILETRANSFER_DLG            120
-#define IDB_TOOLBAR                     121
-#define IDD_FTDIRNAME_DLG               123
+#define IDB_BITMAP                      120
+#define IDD_FILETRANSFER_DLG            121
+#define IDB_TOOLBAR                     122
 #define IDD_FTCONFIRM_DLG               124
 #define IDI_FTUP                        125
 #define IDI_FTDIR                       126
@@ -77,14 +77,10 @@
 #define IDC_MENU_KEY                    1051
 #define IDC_REQUESTED_ENCODING          1052
 #define IDC_LAST_ENCODING               1053
-#define IDC_ENCODING_TIGHT              1054
-#define IDC_FTLOCALPATH                 1054
-#define IDC_CUSTOM_COMPRESSLEVEL        1055
-#define IDC_FTREMOTEPATH                1055
-#define IDC_COMPRESSLEVEL               1056
-#define IDC_FTREMOTELIST                1056
-#define IDC_ALLOW_JPEG                  1057
-#define IDC_FTLOCALRELOAD               1057
+#define IDC_SECURITY_LEVEL              1054
+#define IDC_INFO_ENCRYPTION             1055
+#define IDC_AUTO_RECONNECT              1056
+#define IDC_DISABLE_WINKEYS             1057
 #define IDC_QUALITYLEVEL                1058
 #define IDC_FTLOCALUP                   1058
 #define IDC_SEND_SYSKEYS                1059
@@ -115,6 +111,15 @@
 #define IDC_FTTEXT                      1084
 #define IDC_FTBROWSEPATH                1085
 #define IDC_FTBROWSETREE                1086
+#define IDC_TYPE                        1088
+#define IDC_ENCODING_TIGHT              1089
+#define IDC_FTLOCALPATH                 1090
+#define IDC_CUSTOM_COMPRESSLEVEL        1091
+#define IDC_FTREMOTEPATH                1092
+#define IDC_COMPRESSLEVEL               1093
+#define IDC_FTREMOTELIST                1094
+#define IDC_ALLOW_JPEG                  1095
+#define IDC_FTLOCALRELOAD               1096
 #define ID_TOOLBAR                      40002
 #define ID_CLOSE                        40003
 #define ID_OPTIONS                      40004
@@ -143,7 +148,7 @@
 #ifndef APSTUDIO_READONLY_SYMBOLS
 #define _APS_NEXT_RESOURCE_VALUE        134
 #define _APS_NEXT_COMMAND_VALUE         40028
-#define _APS_NEXT_CONTROL_VALUE         1087
+#define _APS_NEXT_CONTROL_VALUE         1097
 #define _APS_NEXT_SYMED_VALUE           101
 #endif
 #endif
diff --git a/vncviewer/vncviewer.bmp b/vncviewer/vncviewer.bmp
new file mode 100644
index 0000000..4ea9c37
--- /dev/null
+++ b/vncviewer/vncviewer.bmp
Binary files differ
diff --git a/vncviewer/vncviewer.cxx b/vncviewer/vncviewer.cxx
index f0c9f20..3a5214a 100644
--- a/vncviewer/vncviewer.cxx
+++ b/vncviewer/vncviewer.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -25,21 +25,18 @@
 #include <list>
 
 #include <vncviewer/resource.h>
-#include <vncviewer/CViewManager.h>
-#include <vncviewer/CView.h>
+#include <vncviewer/CConn.h>
+#include <vncviewer/CConnThread.h>
 #include <vncviewer/OptionsDialog.h>
-
+#include <vncviewer/ListenServer.h>
+#include <vncviewer/ListenTrayIcon.h>
+#include <network/TcpSocket.h>
 #include <rfb/Logger_stdio.h>
 #include <rfb/Logger_file.h>
 #include <rfb/LogWriter.h>
 #include <rfb/Exception.h>
-
 #include <rfb_win32/RegConfig.h>
-#include <rfb_win32/TrayIcon.h>
-#include <rfb_win32/Win32Util.h>
-#include <rfb_win32/AboutDialog.h>
-
-#include <network/TcpSocket.h>
+#include <rfb_win32/MsgBox.h>
 
 #ifdef _DIALOG_CAPTURE
 #include <extra/LoadBMP.h>
@@ -80,72 +77,6 @@
 
 
 //
-// -=- VNCviewer Tray Icon
-//
-
-class CViewTrayIcon : public TrayIcon {
-public:
-  CViewTrayIcon(CViewManager& mgr) : manager(mgr) {
-    setIcon(IDI_ICON);
-    setToolTip(_T("VNC Viewer"));
-  }
-  virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam) {
-    switch(msg) {
-
-    case WM_USER:
-      switch (lParam) {
-      case WM_LBUTTONDBLCLK:
-        SendMessage(getHandle(), WM_COMMAND, ID_NEW_CONNECTION, 0);
-        break;
-      case WM_RBUTTONUP:
-        HMENU menu = LoadMenu(GetModuleHandle(0), MAKEINTRESOURCE(IDR_TRAY));
-        HMENU trayMenu = GetSubMenu(menu, 0);
-
-        // First item is New Connection, the default
-        SetMenuDefaultItem(trayMenu, ID_NEW_CONNECTION, FALSE);
-
-        // SetForegroundWindow is required, otherwise Windows ignores the
-        // TrackPopupMenu because the window isn't the foreground one, on
-        // some older Windows versions...
-        SetForegroundWindow(getHandle());
-
-        // Display the menu
-        POINT pos;
-        GetCursorPos(&pos);
-        TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, getHandle(), 0);
-        break;
-			} 
-			return 0;
-
-    case WM_COMMAND:
-      switch (LOWORD(wParam)) {
-      case ID_NEW_CONNECTION:
-        manager.addClient(0);
-        break;
-      case ID_OPTIONS:
-        OptionsDialog::global.showDialog(0);
-        break;
-      case ID_ABOUT:
-        AboutDialog::instance.showDialog();
-        break;
-      case ID_CLOSE:
-        SendMessage(getHandle(), WM_CLOSE, 0, 0);
-        break;
-      }
-      return 0;
-
-    case WM_CLOSE:
-      PostQuitMessage(0);
-      return 0;
-    }
-
-    return TrayIcon::processMessage(msg, wParam, lParam);
-  }
-protected:
-  CViewManager& manager;
-};
-
-//
 // -=- processParams
 //     Read in the command-line parameters and interpret them.
 //
@@ -166,7 +97,7 @@
   printf("usage: vncviewer <options> <hostname>[:<display>]\n");
   printf("Command-line options:\n");
   printf("  -help                                - Provide usage information.\n");
-  printf("  -config <file>                       - Load connection settings from VNCViewer 3.3 settings file\n");
+  printf("  -config <file>                       - Load connection settings from VNC Viewer 3.3 settings file\n");
   printf("  -console                             - Run with a console window visible.\n");
   printf("  <setting>=<value>                    - Set the named configuration parameter.\n");
   printf("    (Parameter values specified on the command-line override those specified by other configuration methods.)\n");
@@ -176,6 +107,9 @@
   Logger::listLoggers();
   printf("\nParameters:\n");
   Configuration::listParams();
+  printf("Press Enter/Return key to continue\n");
+  char c = getchar();
+  exit(1);
 }
 
 
@@ -224,6 +158,8 @@
           sprintf(tmp.buf, fmt, argv[i]);
           MsgBox(0, TStr(tmp.buf), MB_ICONSTOP | MB_OK);
           exit(1);
+        } else if (strContains(argv[i], '\\')) {
+          configFiles.push_back(strDup(argv[i]));
         } else {
           hosts.push_back(strDup(argv[i]));
         }
@@ -241,7 +177,6 @@
 //
 
 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prevInst, char* cmdLine, int cmdShow) {
-
   try {
 
     // - Initialise the available loggers
@@ -270,7 +205,7 @@
 
 #ifdef _DIALOG_CAPTURE
     if (captureDialogs) {
-      CView::userConfigKey.openKey(HKEY_CURRENT_USER, _T("Software\\TightVNC\\VNCViewer4"));
+      CConn::userConfigKey.openKey(HKEY_CURRENT_USER, _T("Software\\TightVNC\\VNCViewer4"));
       OptionsDialog::global.showDialog(0, true);
       return 0;
     }
@@ -285,35 +220,27 @@
     // - Connect to the clients
     if (!configFiles.empty() || !hosts.empty() || acceptIncoming) {
       // - Configure the registry configuration reader
-      win32::RegistryReader reg_reader;
-      reg_reader.setKey(HKEY_CURRENT_USER, _T("Software\\TightVNC\\VNCViewer4"));
+      win32::RegConfigThread config;
+      config.start(HKEY_CURRENT_USER, _T("Software\\TightVNC\\VNCViewer4"));
 
       // - Tell the rest of VNC Viewer where to write config data to
-      CView::userConfigKey.openKey(HKEY_CURRENT_USER, _T("Software\\TightVNC\\VNCViewer4"));
-
-      // - Start the Socket subsystem for TCP
-      TcpSocket::initTcpSockets();
-
-      // Create the client connection manager
-      CViewManager view_manager;
+      CConn::userConfigKey.createKey(HKEY_CURRENT_USER, _T("Software\\TightVNC\\VNCViewer4"));
 
       if (acceptIncoming) {
         int port = 5500;
 
         // Listening viewer
-        if (hosts.size() > 1) {
+        if (hosts.size() > 1)
           programUsage();
-          exit(2);
-        }
-        if (!hosts.empty()) {
+        if (!hosts.empty())
           port = atoi(hosts.front());  
-        }
 
-        vlog.debug("opening listener");
+        // Show the tray icon & menu
+        ListenTrayIcon tray;
 
-        CViewTrayIcon tray(view_manager);
-
-        view_manager.addDefaultTCPListener(port);
+        // Listen for reverse connections
+        network::TcpListener sock(port);
+        ListenServer listener(&sock);
 
         // Run the view manager
         // Also processes the tray icon if necessary
@@ -322,13 +249,11 @@
           TranslateMessage(&msg);
           DispatchMessage(&msg);
         }
-
-        vlog.debug("quitting viewer");
       } else {
         // Read each config file in turn
         while (!configFiles.empty()) {
           char* filename = configFiles.front();
-          view_manager.addClient(filename, true);
+          Thread* connThread = new CConnThread(filename, true);
           strFree(filename);
           configFiles.pop_front();
         }
@@ -336,14 +261,14 @@
         // Connect to each client in turn
         while (!hosts.empty()) {
           char* hostinfo = hosts.front();
-          view_manager.addClient(hostinfo);
+          Thread* connThread = new CConnThread(hostinfo);
           strFree(hostinfo);
           hosts.pop_front();
         }
 
         // Run the view manager
         MSG msg;
-        while (GetMessage(&msg, NULL, 0, 0) > 0) {
+        while (CConnThread::getMessage(&msg, NULL, 0, 0) > 0) {
           TranslateMessage(&msg);
           DispatchMessage(&msg);
         }
diff --git a/vncviewer/vncviewer.dsp b/vncviewer/vncviewer.dsp
index 4620dc0..49a17cc 100644
--- a/vncviewer/vncviewer.dsp
+++ b/vncviewer/vncviewer.dsp
@@ -44,7 +44,7 @@
 # PROP Ignore_Export_Lib 0

 # PROP Target_Dir ""

 # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c

-# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /FI"msvcwarning.h" /D "NDEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /c

+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /FI"rdr/msvcwarning.h" /D "NDEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /c

 # ADD BASE RSC /l 0x809 /d "NDEBUG"

 # ADD RSC /l 0x809 /d "NDEBUG"

 BSC32=bscmake.exe

@@ -73,7 +73,7 @@
 # PROP Ignore_Export_Lib 0

 # PROP Target_Dir ""

 # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c

-# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c

+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c

 # ADD BASE RSC /l 0x809 /d "_DEBUG"

 # ADD RSC /l 0x809 /d "_DEBUG"

 BSC32=bscmake.exe

@@ -103,7 +103,7 @@
 # PROP Ignore_Export_Lib 0

 # PROP Target_Dir ""

 # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c

-# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_WINDOWS" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c

+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"rdr/msvcwarning.h" /D "_WINDOWS" /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

@@ -134,19 +134,27 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\CConn.cxx

+# End Source File

+# Begin Source File

+

+SOURCE=.\CConnOptions.cxx

+# End Source File

+# Begin Source File

+

+SOURCE=.\CConnThread.cxx

+# End Source File

+# Begin Source File

+

+SOURCE=.\ConnectingDialog.cxx

+# End Source File

+# Begin Source File

+

 SOURCE=.\ConnectionDialog.cxx

 # End Source File

 # Begin Source File

 

-SOURCE=.\cview.cxx

-# End Source File

-# Begin Source File

-

-SOURCE=.\CViewManager.cxx

-# End Source File

-# Begin Source File

-

-SOURCE=.\CViewOptions.cxx

+SOURCE=.\DesktopWindow.cxx

 # End Source File

 # Begin Source File

 

@@ -198,6 +206,18 @@
 # PROP Default_Filter "h;hpp;hxx;hm;inl"

 # Begin Source File

 

+SOURCE=.\CConn.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\CConnOptions.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\CConnThread.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\ConnectingDialog.h

 # End Source File

 # Begin Source File

@@ -206,15 +226,7 @@
 # End Source File

 # Begin Source File

 

-SOURCE=.\cview.h

-# End Source File

-# Begin Source File

-

-SOURCE=.\CViewManager.h

-# End Source File

-# Begin Source File

-

-SOURCE=.\CViewOptions.h

+SOURCE=.\DesktopWindow.h

 # End Source File

 # Begin Source File

 

@@ -242,6 +254,14 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\ListenServer.h

+# End Source File

+# Begin Source File

+

+SOURCE=.\ListenTrayIcon.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\MRU.h

 # End Source File

 # Begin Source File

@@ -286,6 +306,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\vncviewer.bmp

+# End Source File

+# Begin Source File

+

 SOURCE=.\vncviewer.exe.manifest

 # End Source File

 # Begin Source File

diff --git a/vncviewer/vncviewer.rc b/vncviewer/vncviewer.rc
index 94e3f37..ea5dd65 100644
--- a/vncviewer/vncviewer.rc
+++ b/vncviewer/vncviewer.rc
@@ -13,127 +13,37 @@
 #undef APSTUDIO_READONLY_SYMBOLS
 
 /////////////////////////////////////////////////////////////////////////////
-// English (U.S.) resources
+// English (U.K.) resources
 
-#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
 #ifdef _WIN32
-LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
 #pragma code_page(1252)
 #endif //_WIN32
 
-/////////////////////////////////////////////////////////////////////////////
-//
-// Dialog
-//
-
-IDD_FILETRANSFER_DLG DIALOGEX 0, 0, 530, 282
-STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | 
-    WS_SYSMENU
-EXSTYLE WS_EX_CONTEXTHELP | WS_EX_CONTROLPARENT
-CAPTION "TightVNC File Transfers"
-FONT 8, "MS Sans Serif"
-BEGIN
-    CONTROL         "List1",IDC_FTLOCALLIST,"SysListView32",LVS_REPORT | 
-                    LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | 
-                    WS_TABSTOP,7,40,200,196
-    CONTROL         "List2",IDC_FTREMOTELIST,"SysListView32",LVS_REPORT | 
-                    LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | 
-                    WS_TABSTOP,323,40,200,196
-    PUSHBUTTON      "Upload Files and Folders",IDC_FTUPLOAD,218,66,94,12,
-                    WS_DISABLED
-    PUSHBUTTON      "Download Files and Folders",IDC_FTDOWNLOAD,218,85,94,12,
-                    WS_DISABLED
-    PUSHBUTTON      "Cancel File Transfer",IDC_FTCANCEL,218,167,94,12,
-                    WS_DISABLED
-    PUSHBUTTON      "Close File Transfers",IDC_FTCLOSE,218,217,94,12
-    EDITTEXT        IDC_FTLOCALPATH,7,20,155,12,ES_AUTOHSCROLL | NOT 
-                    WS_TABSTOP
-    CTEXT           "Local Computer",IDC_FTLOCALLABEL,7,7,200,10
-    PUSHBUTTON      "...",IDC_FTLOCALBROWSE,165,20,14,12,NOT WS_TABSTOP
-    PUSHBUTTON      "",IDC_FTLOCALUP,179,20,14,12,BS_ICON | NOT WS_TABSTOP
-    PUSHBUTTON      "",IDC_FTLOCALRELOAD,193,20,14,12,BS_ICON | NOT 
-                    WS_TABSTOP
-    CONTROL         "Progress1",IDC_FTGENERALPROGRESS,"msctls_progress32",
-                    WS_BORDER,55,244,128,10
-    LTEXT           "File Transfer",IDC_STATIC,7,245,40,8
-    COMBOBOX        IDC_FTSTATUS,7,263,516,65,CBS_DROPDOWNLIST | 
-                    CBS_NOINTEGRALHEIGHT | WS_VSCROLL
-    CONTROL         "Progress1",IDC_FTSINGLEPROGRESS,"msctls_progress32",
-                    WS_BORDER,370,244,128,10
-    EDITTEXT        IDC_FTREMOTEPATH,323,20,155,12,ES_AUTOHSCROLL | NOT 
-                    WS_TABSTOP
-    PUSHBUTTON      "...",IDC_FTREMOTEBROWSE,481,20,14,12,NOT WS_TABSTOP
-    PUSHBUTTON      "",IDC_FTREMOTEUP,495,20,14,12,BS_ICON | NOT WS_TABSTOP
-    PUSHBUTTON      "",IDC_FTREMOTERELOAD,509,20,14,12,BS_ICON | NOT 
-                    WS_TABSTOP
-    CTEXT           "TightVNC Server",IDC_FTREMOTELABEL,323,7,200,10
-    LTEXT           "Current File",IDC_STATIC,323,245,36,8
-    CTEXT           "0%",IDC_FTGENERALPERCENT,189,245,18,8
-    CTEXT           "0%",IDC_FTSINGLEPERCENT,505,245,18,8
-END
-
-IDD_FTDIRNAME_DLG DIALOG DISCARDABLE  0, 0, 193, 63
-STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
-CAPTION "Create a New Folder"
-FONT 8, "MS Sans Serif"
-BEGIN
-    DEFPUSHBUTTON   "OK",IDOK,80,42,50,14
-    PUSHBUTTON      "Cancel",IDCANCEL,136,42,50,14
-    EDITTEXT        IDC_FTDIRNAME,7,19,179,14,ES_AUTOHSCROLL
-    LTEXT           "New Folder Name",IDC_STATIC,7,7,93,8
-END
-
-IDD_FTCONFIRM_DLG DIALOG DISCARDABLE  0, 0, 188, 143
-STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
-FONT 8, "MS Sans Serif"
-BEGIN
-    DEFPUSHBUTTON   "Yes",IDOK,69,122,50,14
-    PUSHBUTTON      "No",IDCANCEL,131,122,50,14
-    PUSHBUTTON      "Yes to All",IDC_CONFIRM_YESTOALL,7,122,50,14
-    LTEXT           "Static",IDC_CONFIRM_TEXT,7,7,174,107
-END
-
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// DESIGNINFO
-//
-
 #ifdef APSTUDIO_INVOKED
-GUIDELINES DESIGNINFO DISCARDABLE 
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE 
 BEGIN
-    IDD_FILETRANSFER_DLG, DIALOG
-    BEGIN
-        LEFTMARGIN, 7
-        RIGHTMARGIN, 523
-        VERTGUIDE, 207
-        VERTGUIDE, 265
-        VERTGUIDE, 323
-        TOPMARGIN, 7
-        BOTTOMMARGIN, 275
-        HORZGUIDE, 12
-        HORZGUIDE, 26
-        HORZGUIDE, 40
-        HORZGUIDE, 47
-        HORZGUIDE, 249
-    END
-
-    IDD_FTDIRNAME_DLG, DIALOG
-    BEGIN
-        LEFTMARGIN, 7
-        RIGHTMARGIN, 186
-        TOPMARGIN, 7
-        BOTTOMMARGIN, 56
-    END
-
-    IDD_FTCONFIRM_DLG, DIALOG
-    BEGIN
-        LEFTMARGIN, 7
-        RIGHTMARGIN, 181
-        TOPMARGIN, 7
-        BOTTOMMARGIN, 136
-    END
+    "resource.h\0"
 END
+
+2 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "\r\n"
+    "\0"
+END
+
 #endif    // APSTUDIO_INVOKED
 
 
@@ -144,22 +54,57 @@
 
 // Icon with lowest ID value placed first to ensure application icon
 // remains consistent on all systems.
-IDI_FTUP                ICON    DISCARDABLE     "ftup.ico"
+IDI_ICON                ICON    DISCARDABLE     "vncviewer.ico"
 IDI_FTDIR               ICON    DISCARDABLE     "ftdir.ico"
 IDI_FTFILE              ICON    DISCARDABLE     "ftfile.ico"
 IDI_FTRELOAD            ICON    DISCARDABLE     "ftreload.ico"
-#endif    // English (U.S.) resources
+IDI_FTUP                ICON    DISCARDABLE     "ftup.ico"
+
+#ifndef _MAC
 /////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
 
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 4,1,1,0
+ PRODUCTVERSION 4,1,1,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "080904b0"
+        BEGIN
+            VALUE "Comments", "\0"
+            VALUE "CompanyName", "Constantin Kaplinsky\0"
+            VALUE "FileDescription", "TightVNC Viewer for Win32\0"
+            VALUE "FileVersion", "4.1.1\0"
+            VALUE "InternalName", "free4/vncviewer/win\0"
+            VALUE "LegalCopyright", "Copyright (C) 1998-2006 [many holders]\0"
+            VALUE "LegalTrademarks", "TightVNC\0"
+            VALUE "OriginalFilename", "vncviewer.exe\0"
+            VALUE "PrivateBuild", "\0"
+            VALUE "ProductName", "TightVNC Viewer\0"
+            VALUE "ProductVersion", "4.1.1\0"
+            VALUE "SpecialBuild", "\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x809, 1200
+    END
+END
 
-/////////////////////////////////////////////////////////////////////////////
-// English (U.K.) resources
+#endif    // !_MAC
 
-#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
-#ifdef _WIN32
-LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
-#pragma code_page(1252)
-#endif //_WIN32
 
 /////////////////////////////////////////////////////////////////////////////
 //
@@ -171,14 +116,14 @@
 CAPTION "VNC Viewer : Authentication"
 FONT 8, "MS Sans Serif"
 BEGIN
-    EDITTEXT        IDC_USERNAME,75,6,95,14,ES_AUTOHSCROLL
-    EDITTEXT        IDC_PASSWORD,75,25,95,15,ES_PASSWORD | ES_AUTOHSCROLL | 
+    EDITTEXT        IDC_USERNAME,85,6,100,14,ES_AUTOHSCROLL
+    EDITTEXT        IDC_PASSWORD,85,25,100,15,ES_PASSWORD | ES_AUTOHSCROLL | 
                     ES_WANTRETURN
-    DEFPUSHBUTTON   "OK",IDOK,181,6,53,14
-    PUSHBUTTON      "Cancel",IDCANCEL,181,25,53,15
-    ICON            IDI_ICON,IDI_ICON,7,6,20,20
-    LTEXT           "Username:",IDC_STATIC,35,6,35,14
-    LTEXT           "Password:",IDC_STATIC,35,25,35,15
+    DEFPUSHBUTTON   "OK",IDOK,190,6,45,14
+    PUSHBUTTON      "Cancel",IDCANCEL,190,25,45,15
+    CONTROL         120,IDI_ICON,"Static",SS_BITMAP,7,6,21,20
+    LTEXT           "Username:",IDC_STATIC,45,6,35,14
+    LTEXT           "Password:",IDC_STATIC,45,25,35,15
 END
 
 IDD_CONNECTING_DLG DIALOG DISCARDABLE  0, 0, 185, 47
@@ -186,25 +131,29 @@
 CAPTION "VNC Viewer : Connecting"
 FONT 8, "MS Sans Serif"
 BEGIN
-    PUSHBUTTON      "Cancel",IDCANCEL,128,26,50,14,WS_DISABLED
+    PUSHBUTTON      "Cancel",IDCANCEL,128,26,50,14
     CTEXT           "Attempting to connect to host...",IDC_CONNECTING_TEXT,7,
                     7,171,14,SS_CENTERIMAGE
 END
 
-IDD_CONNECTION_DLG DIALOG DISCARDABLE  0, 0, 241, 54
+IDD_CONNECTION_DLG DIALOG DISCARDABLE  0, 0, 224, 66
 STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION | 
     WS_SYSMENU
 CAPTION "VNC Viewer : Connection Details"
 FONT 8, "MS Sans Serif"
 BEGIN
-    COMBOBOX        IDC_SERVER_EDIT,70,6,110,234,CBS_DROPDOWN | 
+    COMBOBOX        IDC_SERVER_EDIT,85,6,105,234,CBS_DROPDOWN | 
                     CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP
-    PUSHBUTTON      "&About...",IDC_ABOUT,15,30,50,17
-    PUSHBUTTON      "&Options...",IDC_OPTIONS,70,30,50,17
-    DEFPUSHBUTTON   "OK",IDOK,130,30,50,17
-    PUSHBUTTON      "Cancel",IDCANCEL,185,30,50,17
-    ICON            IDI_ICON,IDI_ICON,5,6,20,20
-    LTEXT           "Server:",IDC_STATIC,35,6,30,14
+    PUSHBUTTON      "&About...",IDC_ABOUT,5,45,50,14
+    PUSHBUTTON      "&Options...",IDC_OPTIONS,60,45,50,14
+    DEFPUSHBUTTON   "OK",IDOK,115,45,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,170,45,48,14
+    CONTROL         120,IDI_ICON,"Static",SS_BITMAP | SS_REALSIZEIMAGE,5,6,
+                    20,20
+    RTEXT           "Server:",IDC_STATIC,43,6,37,13,SS_CENTERIMAGE
+    RTEXT           "Encryption:",IDC_STATIC,43,24,37,12,SS_CENTERIMAGE
+    COMBOBOX        IDC_SECURITY_LEVEL,85,24,105,76,CBS_DROPDOWNLIST | 
+                    CBS_SORT | WS_VSCROLL | WS_TABSTOP
 END
 
 IDD_ABOUT DIALOG DISCARDABLE  0, 0, 249, 92
@@ -214,18 +163,19 @@
 FONT 8, "MS Sans Serif"
 BEGIN
     DEFPUSHBUTTON   "OK",IDOK,195,70,47,15
-    ICON            IDI_ICON,IDC_STATIC,7,10,20,20
-    LTEXT           ">appname<",IDC_DESCRIPTION,40,10,125,15
+    CONTROL         120,IDC_STATIC,"Static",SS_BITMAP | SS_REALSIZEIMAGE,7,
+                    10,33,31
+    LTEXT           ">appname<",IDC_DESCRIPTION,46,10,119,15
     LTEXT           ">version<",IDC_VERSION,165,10,77,15
-    LTEXT           ">buildtime<",IDC_BUILDTIME,40,25,202,15
-    LTEXT           ">copyright<",IDC_COPYRIGHT,40,40,202,15
-    LTEXT           "See http://www.tightvnc.com for more information on TightVNC.",
-                    IDC_STATIC,40,55,202,15
+    LTEXT           ">buildtime<",IDC_BUILDTIME,46,25,196,15
+    LTEXT           ">copyright<",IDC_COPYRIGHT,46,40,196,15
+    LTEXT           "Visit www.tightvnc.com for more information on TightVNC.",
+                    IDC_STATIC,46,55,196,15
 END
 
-IDD_FORMAT DIALOG DISCARDABLE  0, 0, 201, 160
+IDD_FORMAT DIALOG DISCARDABLE  0, 0, 201, 161
 STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
-CAPTION "Color/Encoding"
+CAPTION "Colour && Encoding"
 FONT 8, "MS Sans Serif"
 BEGIN
     CONTROL         "&Auto select",IDC_ENCODING_AUTO,"Button",
@@ -269,7 +219,9 @@
                     IDC_CONN_SHARED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,
                     10,199,15
     CONTROL         "Full-screen mode",IDC_FULL_SCREEN,"Button",
-                    BS_AUTOCHECKBOX | WS_TABSTOP,7,25,199,15
+                    BS_AUTOCHECKBOX | WS_TABSTOP,7,25,99,15
+    CONTROL         "Show toolbar",IDC_SHOW_TOOLBAR,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,106,25,100,15
     CONTROL         "Render cursor locally",IDC_LOCAL_CURSOR,"Button",
                     BS_AUTOCHECKBOX | WS_TABSTOP,7,40,199,15
     CONTROL         "Allow dynamic desktop resizing",IDC_DESKTOP_RESIZE,
@@ -277,12 +229,12 @@
     CONTROL         "Only use protocol version 3.3",IDC_PROTOCOL_3_3,"Button",
                     BS_AUTOCHECKBOX | WS_TABSTOP,7,70,199,15
     CONTROL         "Beep when requested to by the server",IDC_ACCEPT_BELL,
-                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,85,199,14
-    CONTROL         "Show toolbar by default",IDC_SHOW_TOOLBAR,"Button",
-                    BS_AUTOCHECKBOX | WS_TABSTOP,7,100,199,15
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,85,199,15
+    CONTROL         "Offer to automatically reconnect",IDC_AUTO_RECONNECT,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,100,199,15
 END
 
-IDD_INPUTS DIALOG DISCARDABLE  0, 0, 186, 138
+IDD_INPUTS DIALOG DISCARDABLE  0, 0, 186, 162
 STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
 CAPTION "Inputs"
 FONT 8, "MS Sans Serif"
@@ -291,30 +243,30 @@
                     BS_AUTOCHECKBOX | WS_TABSTOP,7,10,172,15
     CONTROL         "Send keyboard events to server",IDC_SEND_KEYS,"Button",
                     BS_AUTOCHECKBOX | WS_TABSTOP,7,25,172,15
-    CONTROL         "Send system keys (Alt combinations) to server",
-                    IDC_SEND_SYSKEYS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,
-                    40,172,15
     CONTROL         "Send clipboard changes to server",IDC_CLIENT_CUTTEXT,
-                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,55,172,15
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,40,172,15
     CONTROL         "Accept clipboard changes from server",
                     IDC_SERVER_CUTTEXT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
-                    7,70,172,15
+                    7,55,172,15
     CONTROL         "Enable 3-button mouse emulation",IDC_EMULATE3,"Button",
-                    BS_AUTOCHECKBOX | WS_TABSTOP,7,85,172,15
+                    BS_AUTOCHECKBOX | WS_TABSTOP,7,70,172,15
     CONTROL         "Rate-limit mouse move events",IDC_POINTER_INTERVAL,
-                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,100,172,14
-    LTEXT           "Menu key",IDC_STATIC,7,115,98,15,SS_CENTERIMAGE
-    COMBOBOX        IDC_MENU_KEY,105,115,74,105,CBS_DROPDOWNLIST | CBS_SORT | 
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,86,172,14
+    LTEXT           "Menu key",IDC_STATIC,7,100,98,15,SS_CENTERIMAGE
+    COMBOBOX        IDC_MENU_KEY,105,100,74,105,CBS_DROPDOWNLIST | CBS_SORT | 
                     WS_VSCROLL | WS_TABSTOP
+    CONTROL         "Pass special keys directly to server",
+                    IDC_DISABLE_WINKEYS,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,7,115,172,15
 END
 
-IDD_CONNECTION_INFO DIALOG DISCARDABLE  0, 0, 239, 186
+IDD_CONNECTION_INFO DIALOG DISCARDABLE  0, 0, 239, 199
 STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION | 
     WS_SYSMENU
 CAPTION "VNC Connection Info"
 FONT 8, "MS Sans Serif"
 BEGIN
-    DEFPUSHBUTTON   "OK",IDOK,182,165,50,14
+    DEFPUSHBUTTON   "OK",IDOK,182,178,50,14
     LTEXT           "Desktop Name:",IDC_STATIC,7,10,73,15
     LTEXT           "Host:",IDC_STATIC,7,25,73,15
     LTEXT           "Size:",IDC_STATIC,7,40,73,15
@@ -335,19 +287,83 @@
     LTEXT           "Last Used Encoding:",IDC_STATIC,7,100,73,15
     LTEXT           "",IDC_REQUESTED_ENCODING,80,86,152,15
     LTEXT           "",IDC_LAST_ENCODING,80,100,152,15
+    LTEXT           "Static",IDC_INFO_ENCRYPTION,80,160,152,15
+    LTEXT           "Encryption:",IDC_STATIC,7,160,73,15
 END
 
-IDD_DEFAULTS DIALOG DISCARDABLE  0, 0, 131, 113
+IDD_DEFAULTS DIALOG DISCARDABLE  0, 0, 217, 87
 STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
-CAPTION "Defaults"
+CAPTION "Load / Save"
 FONT 8, "MS Sans Serif"
 BEGIN
-    PUSHBUTTON      "Reload &Defaults",IDC_LOAD_DEFAULTS,7,10,117,15
-    PUSHBUTTON      "&Save As Defaults",IDC_SAVE_DEFAULTS,7,30,117,15
-    PUSHBUTTON      "Reload Configuration &File",IDC_LOAD_CONFIG,7,50,117,15
-    PUSHBUTTON      "Save &Configuration File",IDC_SAVE_CONFIG,7,70,117,15
-    PUSHBUTTON      "Save Configuration File &As ...",IDC_SAVE_CONFIG_AS,7,
-                    90,117,15
+    PUSHBUTTON      "&Reload",IDC_LOAD_CONFIG,15,20,85,15
+    PUSHBUTTON      "&Save",IDC_SAVE_CONFIG,15,40,85,15
+    PUSHBUTTON      "Save &As ...",IDC_SAVE_CONFIG_AS,15,60,85,15
+    PUSHBUTTON      "R&eload",IDC_LOAD_DEFAULTS,120,20,85,15
+    PUSHBUTTON      "S&ave",IDC_SAVE_DEFAULTS,120,40,85,15
+    GROUPBOX        "Configuration File",IDC_STATIC,7,7,100,74
+    GROUPBOX        "Defaults",IDC_STATIC,113,7,97,53
+END
+
+IDD_FILETRANSFER_DLG DIALOGEX 0, 0, 530, 282
+STYLE DS_MODALFRAME | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | 
+    WS_SYSMENU
+EXSTYLE WS_EX_CONTEXTHELP | WS_EX_CONTROLPARENT
+CAPTION "TightVNC File Transfers"
+FONT 8, "MS Sans Serif", 0, 0, 0x1
+BEGIN
+    CONTROL         "List1",IDC_FTLOCALLIST,"SysListView32",LVS_REPORT | 
+                    LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | 
+                    WS_TABSTOP,7,40,200,196
+    CONTROL         "List2",IDC_FTREMOTELIST,"SysListView32",LVS_REPORT | 
+                    LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | 
+                    WS_TABSTOP,323,40,200,196
+    PUSHBUTTON      "Upload Files and Folders",IDC_FTUPLOAD,218,66,94,12,
+                    WS_DISABLED
+    PUSHBUTTON      "Download Files and Folders",IDC_FTDOWNLOAD,218,85,94,12,
+                    WS_DISABLED
+    PUSHBUTTON      "Cancel File Transfer",IDC_FTCANCEL,218,167,94,12,
+                    WS_DISABLED
+    PUSHBUTTON      "Close",IDC_FTCLOSE,218,217,94,12
+    EDITTEXT        IDC_FTLOCALPATH,7,20,155,12,ES_AUTOHSCROLL | NOT 
+                    WS_TABSTOP
+    CTEXT           "Local Computer",IDC_FTLOCALLABEL,7,7,200,10
+    PUSHBUTTON      "...",IDC_FTLOCALBROWSE,165,20,14,12,NOT WS_TABSTOP
+    PUSHBUTTON      "",IDC_FTLOCALUP,179,20,14,12,BS_ICON | NOT WS_TABSTOP
+    PUSHBUTTON      "",IDC_FTLOCALRELOAD,193,20,14,12,BS_ICON | NOT 
+                    WS_TABSTOP
+    CONTROL         "Progress1",IDC_FTGENERALPROGRESS,"msctls_progress32",
+                    WS_BORDER,55,244,128,10
+    LTEXT           "File Transfer",IDC_STATIC,7,245,40,8
+    COMBOBOX        IDC_FTSTATUS,7,263,516,65,CBS_DROPDOWNLIST | 
+                    CBS_NOINTEGRALHEIGHT | WS_VSCROLL
+    CONTROL         "Progress1",IDC_FTSINGLEPROGRESS,"msctls_progress32",
+                    WS_BORDER,370,244,128,10
+    EDITTEXT        IDC_FTREMOTEPATH,323,20,155,12,ES_AUTOHSCROLL | NOT 
+                    WS_TABSTOP
+    PUSHBUTTON      "...",IDC_FTREMOTEBROWSE,481,20,14,12,NOT WS_TABSTOP
+    PUSHBUTTON      "",IDC_FTREMOTEUP,495,20,14,12,BS_ICON | NOT WS_TABSTOP
+    PUSHBUTTON      "",IDC_FTREMOTERELOAD,509,20,14,12,BS_ICON | NOT 
+                    WS_TABSTOP
+    CTEXT           "TightVNC Server",IDC_FTREMOTELABEL,323,7,200,10
+    LTEXT           "Current File",IDC_STATIC,323,245,36,8
+    CTEXT           "0%",IDC_FTGENERALPERCENT,189,245,18,8
+    CTEXT           "0%",IDC_FTSINGLEPERCENT,505,245,18,8
+END
+
+IDD_FTBROWSE DIALOGEX 0, 0, 183, 196
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_CONTROLPARENT
+CAPTION "Browse Folders"
+FONT 8, "MS Sans Serif", 0, 0, 0x1
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,38,175,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,95,175,50,14
+    EDITTEXT        IDC_FTBROWSEPATH,7,7,169,12,ES_AUTOHSCROLL | ES_READONLY | 
+                    NOT WS_TABSTOP
+    CONTROL         "Tree1",IDC_FTBROWSETREE,"SysTreeView32",TVS_HASBUTTONS | 
+                    TVS_HASLINES | TVS_LINESATROOT | TVS_DISABLEDRAGDROP | 
+                    TVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP,7,25,169,143
 END
 
 IDD_FTCANCELING DIALOG DISCARDABLE  0, 0, 193, 63
@@ -361,6 +377,16 @@
                     IDC_STATIC,42,14,133,19
 END
 
+IDD_FTCONFIRM_DLG DIALOG DISCARDABLE  0, 0, 188, 143
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON   "Yes",IDOK,69,122,50,14,WS_GROUP
+    PUSHBUTTON      "No",IDCANCEL,131,122,50,14
+    PUSHBUTTON      "Yes to All",IDC_CONFIRM_YESTOALL,7,122,50,14
+    LTEXT           "Static",IDC_CONFIRM_TEXT,7,7,174,107
+END
+
 IDD_FTCREATEFOLDER DIALOG DISCARDABLE  0, 0, 193, 63
 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
 CAPTION "Create a New Folder"
@@ -369,22 +395,7 @@
     EDITTEXT        IDC_FTFOLDERNAME,7,19,179,14,ES_AUTOHSCROLL
     DEFPUSHBUTTON   "OK",IDOK,80,42,50,14
     PUSHBUTTON      "Cancel",IDCANCEL,136,42,50,14
-    LTEXT           "New Folder Name",IDC_FTTEXT,7,7,179,8
-END
-
-IDD_FTBROWSE DIALOGEX 0, 0, 183, 196
-STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
-EXSTYLE WS_EX_CONTROLPARENT
-CAPTION "Browse Folders"
-FONT 8, "MS Sans Serif"
-BEGIN
-    DEFPUSHBUTTON   "OK",IDOK,38,175,50,14
-    PUSHBUTTON      "Cancel",IDCANCEL,95,175,50,14
-    EDITTEXT        IDC_FTBROWSEPATH,7,7,169,12,ES_AUTOHSCROLL | ES_READONLY | 
-                    NOT WS_TABSTOP
-    CONTROL         "Tree1",IDC_FTBROWSETREE,"SysTreeView32",TVS_HASBUTTONS | 
-                    TVS_HASLINES | TVS_LINESATROOT | TVS_DISABLEDRAGDROP | 
-                    TVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP,7,25,169,143
+    LTEXT           "New folder name:",IDC_FTTEXT,7,7,179,8
 END
 
 
@@ -399,10 +410,12 @@
     IDD_VNC_AUTH_DLG, DIALOG
     BEGIN
         LEFTMARGIN, 7
-        RIGHTMARGIN, 234
-        VERTGUIDE, 35
-        VERTGUIDE, 70
-        VERTGUIDE, 75
+        RIGHTMARGIN, 235
+        VERTGUIDE, 45
+        VERTGUIDE, 80
+        VERTGUIDE, 85
+        VERTGUIDE, 185
+        VERTGUIDE, 190
         TOPMARGIN, 6
         BOTTOMMARGIN, 40
         HORZGUIDE, 20
@@ -423,27 +436,31 @@
     IDD_CONNECTION_DLG, DIALOG
     BEGIN
         LEFTMARGIN, 5
-        RIGHTMARGIN, 235
-        VERTGUIDE, 15
-        VERTGUIDE, 35
-        VERTGUIDE, 65
-        VERTGUIDE, 70
-        VERTGUIDE, 120
-        VERTGUIDE, 130
-        VERTGUIDE, 180
-        VERTGUIDE, 185
+        RIGHTMARGIN, 218
+        VERTGUIDE, 30
+        VERTGUIDE, 43
+        VERTGUIDE, 55
+        VERTGUIDE, 60
+        VERTGUIDE, 80
+        VERTGUIDE, 85
+        VERTGUIDE, 110
+        VERTGUIDE, 115
+        VERTGUIDE, 165
+        VERTGUIDE, 170
+        VERTGUIDE, 190
         TOPMARGIN, 6
-        BOTTOMMARGIN, 47
-        HORZGUIDE, 20
-        HORZGUIDE, 30
-        HORZGUIDE, 40
+        BOTTOMMARGIN, 59
+        HORZGUIDE, 19
+        HORZGUIDE, 24
+        HORZGUIDE, 36
+        HORZGUIDE, 45
     END
 
     IDD_ABOUT, DIALOG
     BEGIN
         LEFTMARGIN, 7
         RIGHTMARGIN, 242
-        VERTGUIDE, 40
+        VERTGUIDE, 46
         VERTGUIDE, 165
         VERTGUIDE, 195
         TOPMARGIN, 7
@@ -467,7 +484,7 @@
         VERTGUIDE, 105
         VERTGUIDE, 190
         TOPMARGIN, 7
-        BOTTOMMARGIN, 94
+        BOTTOMMARGIN, 154
         HORZGUIDE, 10
         HORZGUIDE, 20
         HORZGUIDE, 25
@@ -482,6 +499,7 @@
     BEGIN
         LEFTMARGIN, 7
         RIGHTMARGIN, 206
+        VERTGUIDE, 106
         TOPMARGIN, 7
         BOTTOMMARGIN, 130
         HORZGUIDE, 10
@@ -492,6 +510,7 @@
         HORZGUIDE, 85
         HORZGUIDE, 100
         HORZGUIDE, 115
+        HORZGUIDE, 130
     END
 
     IDD_INPUTS, DIALOG
@@ -500,7 +519,7 @@
         RIGHTMARGIN, 179
         VERTGUIDE, 105
         TOPMARGIN, 7
-        BOTTOMMARGIN, 131
+        BOTTOMMARGIN, 155
         HORZGUIDE, 10
         HORZGUIDE, 25
         HORZGUIDE, 40
@@ -509,6 +528,8 @@
         HORZGUIDE, 85
         HORZGUIDE, 100
         HORZGUIDE, 115
+        HORZGUIDE, 130
+        HORZGUIDE, 145
     END
 
     IDD_CONNECTION_INFO, DIALOG
@@ -517,7 +538,7 @@
         RIGHTMARGIN, 232
         VERTGUIDE, 80
         TOPMARGIN, 7
-        BOTTOMMARGIN, 179
+        BOTTOMMARGIN, 192
         HORZGUIDE, 10
         HORZGUIDE, 25
         HORZGUIDE, 40
@@ -529,24 +550,51 @@
         HORZGUIDE, 130
         HORZGUIDE, 145
         HORZGUIDE, 160
+        HORZGUIDE, 175
     END
 
     IDD_DEFAULTS, DIALOG
     BEGIN
         LEFTMARGIN, 7
-        RIGHTMARGIN, 124
+        RIGHTMARGIN, 210
+        VERTGUIDE, 15
+        VERTGUIDE, 100
+        VERTGUIDE, 107
+        VERTGUIDE, 113
+        VERTGUIDE, 120
+        VERTGUIDE, 205
         TOPMARGIN, 7
-        BOTTOMMARGIN, 106
-        HORZGUIDE, 10
-        HORZGUIDE, 25
-        HORZGUIDE, 30
-        HORZGUIDE, 45
-        HORZGUIDE, 50
-        HORZGUIDE, 65
-        HORZGUIDE, 70
-        HORZGUIDE, 85
-        HORZGUIDE, 90
-        HORZGUIDE, 105
+        BOTTOMMARGIN, 80
+        HORZGUIDE, 20
+        HORZGUIDE, 35
+        HORZGUIDE, 40
+        HORZGUIDE, 55
+        HORZGUIDE, 60
+        HORZGUIDE, 75
+    END
+
+    IDD_FILETRANSFER_DLG, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 523
+        VERTGUIDE, 207
+        VERTGUIDE, 265
+        VERTGUIDE, 323
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 275
+        HORZGUIDE, 12
+        HORZGUIDE, 26
+        HORZGUIDE, 40
+        HORZGUIDE, 47
+        HORZGUIDE, 249
+    END
+
+    IDD_FTBROWSE, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 176
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 189
     END
 
     IDD_FTCANCELING, DIALOG
@@ -557,6 +605,14 @@
         BOTTOMMARGIN, 56
     END
 
+    IDD_FTCONFIRM_DLG, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 181
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 136
+    END
+
     IDD_FTCREATEFOLDER, DIALOG
     BEGIN
         LEFTMARGIN, 7
@@ -564,101 +620,12 @@
         TOPMARGIN, 7
         BOTTOMMARGIN, 56
     END
-
-    IDD_FTBROWSE, DIALOG
-    BEGIN
-        LEFTMARGIN, 7
-        RIGHTMARGIN, 176
-        TOPMARGIN, 7
-        BOTTOMMARGIN, 189
-    END
 END
 #endif    // APSTUDIO_INVOKED
 
 
 /////////////////////////////////////////////////////////////////////////////
 //
-// Icon
-//
-
-// Icon with lowest ID value placed first to ensure application icon
-// remains consistent on all systems.
-IDI_ICON                ICON    DISCARDABLE     "vncviewer.ico"
-
-#ifdef APSTUDIO_INVOKED
-/////////////////////////////////////////////////////////////////////////////
-//
-// TEXTINCLUDE
-//
-
-1 TEXTINCLUDE DISCARDABLE 
-BEGIN
-    "resource.h\0"
-END
-
-2 TEXTINCLUDE DISCARDABLE 
-BEGIN
-    "#include ""afxres.h""\r\n"
-    "\0"
-END
-
-3 TEXTINCLUDE DISCARDABLE 
-BEGIN
-    "\r\n"
-    "\0"
-END
-
-#endif    // APSTUDIO_INVOKED
-
-
-#ifndef _MAC
-/////////////////////////////////////////////////////////////////////////////
-//
-// Version
-//
-
-VS_VERSION_INFO VERSIONINFO
- FILEVERSION 4,0,0,26
- PRODUCTVERSION 4,0,0,26
- FILEFLAGSMASK 0x3fL
-#ifdef _DEBUG
- FILEFLAGS 0x1L
-#else
- FILEFLAGS 0x0L
-#endif
- FILEOS 0x40004L
- FILETYPE 0x1L
- FILESUBTYPE 0x0L
-BEGIN
-    BLOCK "StringFileInfo"
-    BEGIN
-        BLOCK "080904b0"
-        BEGIN
-            VALUE "Comments", "\0"
-            VALUE "CompanyName", "Constantin Kaplinsky\0"
-            VALUE "FileDescription", "TightVNC Viewer for Win32\0"
-            VALUE "FileVersion", "4.0\0"
-            VALUE "InternalName", "VNCViewer 4.0\0"
-            VALUE "LegalCopyright", "Copyright (C) 1998-2004 [many holders]\0"
-            VALUE "LegalTrademarks", "TightVNC\0"
-            VALUE "OriginalFilename", "vncviewer.exe\0"
-            VALUE "PrivateBuild", "\0"
-            VALUE "ProductName", "TightVNC Viewer 4.0\0"
-            VALUE "ProductVersion", "4.0\0"
-            VALUE "SpecialBuild", "\0"
-        END
-    END
-    BLOCK "VarFileInfo"
-    BEGIN
-        VALUE "Translation", 0x809, 1200
-    END
-END
-
-#endif    // !_MAC
-
-
-/////////////////////////////////////////////////////////////////////////////
-//
 // Cursor
 //
 
@@ -722,6 +689,7 @@
 // Bitmap
 //
 
+IDB_BITMAP              BITMAP  DISCARDABLE     "vncviewer.bmp"
 IDB_TOOLBAR             BITMAP  DISCARDABLE     "toolbar.bmp"
 #endif    // English (U.K.) resources
 /////////////////////////////////////////////////////////////////////////////
diff --git a/vncviewer_unix/AboutDialog.h b/vncviewer_unix/AboutDialog.h
index f242753..c4f0a7c 100644
--- a/vncviewer_unix/AboutDialog.h
+++ b/vncviewer_unix/AboutDialog.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/vncviewer_unix/CConn.cxx b/vncviewer_unix/CConn.cxx
index c3d0727..eabe33a 100644
--- a/vncviewer_unix/CConn.cxx
+++ b/vncviewer_unix/CConn.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -29,6 +29,7 @@
 #include <rfb/Hostname.h>
 #include <rfb/LogWriter.h>
 #include <rfb/util.h>
+#include <rfb/Password.h>
 #include <network/TcpSocket.h>
 
 #include "TXViewport.h"
@@ -49,7 +50,7 @@
 StringParameter windowName("name", "The X window name", "");
 
 CConn::CConn(Display* dpy_, int argc_, char** argv_, network::Socket* sock_,
-             char* vncServerName)
+             char* vncServerName, bool reverse)
   : dpy(dpy_), argc(argc_),
     argv(argv_), serverHost(0), serverPort(0), sock(sock_), viewport(0),
     desktop(0), desktopEventHandler(0),
@@ -58,7 +59,8 @@
     autoSelect(::autoSelect), shared(::shared), formatChange(false),
     encodingChange(false), sameMachine(false), fullScreen(::fullScreen),
     ctrlDown(false), altDown(false),
-    menuKeysym(0), menu(dpy, this), options(dpy, this), about(dpy), info(dpy)
+    menuKeysym(0), menu(dpy, this), options(dpy, this), about(dpy), info(dpy),
+    reverseConnection(reverse)
 {
   CharArray menuKeyStr(menuKey.getData());
   menuKeysym = XStringToKeysym(menuKeyStr.buf);
@@ -160,19 +162,32 @@
 void CConn::blockCallback() {
   fd_set rfds;
   do {
-    TXWindow::handleXEvents(dpy);
     struct timeval tv;
-    struct timeval* tvp;
-    if (Timer::getTimeout(&tv))
+    struct timeval* tvp = 0;
+
+    // Process any incoming X events
+    TXWindow::handleXEvents(dpy);
+    
+    // Process expired timers and get the time until the next one
+    int timeoutMs = Timer::checkTimeouts();
+    if (timeoutMs) {
+      tv.tv_sec = timeoutMs / 1000;
+      tv.tv_usec = (timeoutMs % 1000) * 1000;
       tvp = &tv;
-    else
-      tvp = 0;
+    }
+    
+    // If there are X requests pending then poll, don't wait!
+    if (XPending(dpy)) {
+      tv.tv_usec = tv.tv_sec = 0;
+      tvp = &tv;
+    }
+
+    // Wait for X events, VNC traffic, or the next timer expiry
     FD_ZERO(&rfds);
     FD_SET(ConnectionNumber(dpy), &rfds);
     FD_SET(sock->getFd(), &rfds);
     int n = select(FD_SETSIZE, &rfds, 0, 0, tvp);
     if (n < 0) throw rdr::SystemException("select",errno);
-    Timer::callTimers();
   } while (!(FD_ISSET(sock->getFd(), &rfds)));
 }
 
@@ -180,20 +195,18 @@
 // getPasswd() is called by the CSecurity object when it needs us to read a
 // password from the user.
 
-bool CConn::getUserPasswd(char** user, char** password)
+void CConn::getUserPasswd(char** user, char** password)
 {
   CharArray passwordFileStr(passwordFile.getData());
   if (!user && passwordFileStr.buf[0]) {
     FILE* fp = fopen(passwordFileStr.buf, "r");
-    if (!fp) return false;
-    char data[256];
-    int datalen = fread(data, 1, 256, fp);
+    if (!fp) throw rfb::Exception("Opening password file failed");
+    ObfuscatedPasswd obfPwd(256);
+    obfPwd.length = fread(obfPwd.buf, 1, obfPwd.length, fp);
     fclose(fp);
-    if (datalen != 8) return false;
-    vncAuthUnobfuscatePasswd(data);
-    *password = strDup(data);
-    memset(data, 0, strlen(data));
-    return true;
+    PlainPasswd passwd(obfPwd);
+    *password = passwd.takeBuf();
+    return;
   }
 
   const char* secType = secTypeName(getCurrentCSecurity()->getType());
@@ -202,11 +215,10 @@
   CharArray title(titleLen);
   snprintf(title.buf, titleLen, "%s [%s]", titlePrefix, secType);
   PasswdDialog dlg(dpy, title.buf, !user);
-  if (!dlg.show()) return false;
+  if (!dlg.show()) throw rfb::Exception("Authentication cancelled");
   if (user)
     *user = strDup(dlg.userEntry.getText());
   *password = strDup(dlg.passwdEntry.getText());
-  return true;
 }
 
 
@@ -324,9 +336,9 @@
 void CConn::copyRect(const rfb::Rect& r, int sx, int sy) {
   desktop->copyRect(r,sx,sy);
 }
-void CConn::setCursor(const Point& hotspot, const Point& size,
+void CConn::setCursor(int width, int height, const Point& hotspot,
                       void* data, void* mask) {
-  desktop->setCursor(hotspot, size, data, mask);
+  desktop->setCursor(width, height, hotspot, data, mask);
 }
 
 
@@ -444,26 +456,26 @@
   case ID_F8:
     menu.unmap();
     if (!viewOnly) {
-      writer()->writeKeyEvent(menuKeysym, true);
-      writer()->writeKeyEvent(menuKeysym, false);
+      writer()->keyEvent(menuKeysym, true);
+      writer()->keyEvent(menuKeysym, false);
     }
     break;
   case ID_CTRLALTDEL:
     menu.unmap();
     if (!viewOnly) {
-      writer()->writeKeyEvent(XK_Control_L, true);
-      writer()->writeKeyEvent(XK_Alt_L, true);
-      writer()->writeKeyEvent(XK_Delete, true);
-      writer()->writeKeyEvent(XK_Delete, false);
-      writer()->writeKeyEvent(XK_Alt_L, false);
-      writer()->writeKeyEvent(XK_Control_L, false);
+      writer()->keyEvent(XK_Control_L, true);
+      writer()->keyEvent(XK_Alt_L, true);
+      writer()->keyEvent(XK_Delete, true);
+      writer()->keyEvent(XK_Delete, false);
+      writer()->keyEvent(XK_Alt_L, false);
+      writer()->keyEvent(XK_Control_L, false);
     }
     break;
   case ID_CTRL:
     menu.unmap();
     if (!viewOnly) {
       ctrlDown = !ctrlDown;
-      writer()->writeKeyEvent(XK_Control_L, ctrlDown);
+      writer()->keyEvent(XK_Control_L, ctrlDown);
       menu.check(ID_CTRL, ctrlDown);
     }
     break;
@@ -471,7 +483,7 @@
     menu.unmap();
     if (!viewOnly) {
       altDown = !altDown;
-      writer()->writeKeyEvent(XK_Alt_L, altDown);
+      writer()->keyEvent(XK_Alt_L, altDown);
       menu.check(ID_ALT, altDown);
     }
     break;
diff --git a/vncviewer_unix/CConn.h b/vncviewer_unix/CConn.h
index c95951b..a81af48 100644
--- a/vncviewer_unix/CConn.h
+++ b/vncviewer_unix/CConn.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -48,7 +48,7 @@
 public:
 
   CConn(Display* dpy_, int argc_, char** argv_, network::Socket* sock_,
-        char* vncServerName);
+        char* vncServerName, bool reverse=false);
   ~CConn();
 
   // TXDeleteWindowCallback methods
@@ -58,7 +58,7 @@
   void blockCallback();
 
   // UserPasswdGetter methods
-  virtual bool getUserPasswd(char** user, char** password);
+  virtual void getUserPasswd(char** user, char** password);
 
   // TXMenuCallback methods
   void menuSelect(long id, TXMenu* m);
@@ -69,7 +69,7 @@
 
   // TXEventHandler callback method
   virtual void handleEvent(TXWindow* w, XEvent* ev);
-
+  
   // CConnection callback methods
   rfb::CSecurity* getCSecurity(int secType);
   void serverInit();
@@ -83,7 +83,7 @@
   void fillRect(const rfb::Rect& r, rfb::Pixel p);
   void imageRect(const rfb::Rect& r, void* p);
   void copyRect(const rfb::Rect& r, int sx, int sy);
-  void setCursor(const rfb::Point& hotspot, const rfb::Point& size,
+  void setCursor(int width, int height, const rfb::Point& hotspot,
                  void* data, void* mask);
 
 private:
@@ -124,6 +124,7 @@
   OptionsDialog options;
   AboutDialog about;
   InfoDialog info;
+  bool reverseConnection;
 };
 
 #endif
diff --git a/vncviewer_unix/DesktopWindow.cxx b/vncviewer_unix/DesktopWindow.cxx
index d0e4cb5..657572a 100644
--- a/vncviewer_unix/DesktopWindow.cxx
+++ b/vncviewer_unix/DesktopWindow.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -70,7 +70,7 @@
     newSelection(0), gettingInitialSelectionTime(true),
     newServerCutText(false), serverCutText_(0),
     setColourMapEntriesTimer(this), viewport(0),
-    pointerEventTimer(this), lastPointerX(0), lastPointerY(0),
+    pointerEventTimer(this),
     lastButtonMask(0)
 {
   setEventHandler(this);
@@ -125,14 +125,14 @@
   localXCursor = 0;
 }
 
-void DesktopWindow::setCursor(const Point& hotspot, const Point& size,
+void DesktopWindow::setCursor(int width, int height, const Point& hotspot,
                               void* data, void* mask)
 {
   if (!useLocalCursor) return;
 
   hideLocalCursor();
 
-  int mask_len = ((size.x+7)/8) * size.y;
+  int mask_len = ((width+7)/8) * height;
 
   int i;
   for (i = 0; i < mask_len; i++)
@@ -151,11 +151,11 @@
 
   cursor.hotspot = hotspot;
 
-  cursor.setSize(size.x, size.y);
+  cursor.setSize(width, height);
   cursor.setPF(getPF());
   cursor.imageRect(cursor.getRect(), data);
 
-  cursorBacking.setSize(size.x, size.y);
+  cursorBacking.setSize(width, height);
   cursorBacking.setPF(getPF());
 
   delete [] cursor.mask.buf;
@@ -250,8 +250,8 @@
                                         rdr::U16* rgbs)
 {
   im->setColourMapEntries(firstColour, nColours, rgbs);
-  if (!setColourMapEntriesTimer.isSet())
-    setColourMapEntriesTimer.reset(100);
+  if (!setColourMapEntriesTimer.isStarted())
+    setColourMapEntriesTimer.start(100);
 }
 
 void DesktopWindow::serverCutText(const char* str, int len)
@@ -311,41 +311,39 @@
 }
 
 
-void DesktopWindow::timerCallback(Timer* timer)
+bool DesktopWindow::handleTimeout(rfb::Timer* timer)
 {
   if (timer == &setColourMapEntriesTimer) {
     im->updateColourMap();
     im->put(win(), gc, im->getRect());
   } else if (timer == &pointerEventTimer) {
     if (!viewOnly) {
-      cc->writer()->writePointerEvent(lastPointerX, lastPointerY,
-                                      lastButtonMask);
+      cc->writer()->pointerEvent(lastPointerPos, lastButtonMask);
     }
   }
+  return false;
 }
 
 
-void DesktopWindow::handlePointerEvent(int x, int y, int buttonMask)
+void DesktopWindow::handlePointerEvent(const Point& pos, int buttonMask)
 {
   if (!viewOnly) {
     if (pointerEventInterval == 0 || buttonMask != lastButtonMask) {
-      cc->writer()->writePointerEvent(x, y, buttonMask);
+      cc->writer()->pointerEvent(pos, buttonMask);
     } else {
-      if (!pointerEventTimer.isSet())
-        pointerEventTimer.reset(pointerEventInterval);
+      if (!pointerEventTimer.isStarted())
+        pointerEventTimer.start(pointerEventInterval);
     }
-    lastPointerX = x;
-    lastPointerY = y;
+    lastPointerPos = pos;
     lastButtonMask = buttonMask;
   }
   // - If local cursor rendering is enabled then use it
   if (cursorAvailable) {
     // - Render the cursor!
-    Point p(x, y);
-    if (!p.equals(cursorPos)) {
+    if (!pos.equals(cursorPos)) {
       hideLocalCursor();
-      if (im->getRect().contains(p)) {
-        cursorPos = p;
+      if (im->getRect().contains(pos)) {
+        cursorPos = pos;
         showLocalCursor();
       }
     }
@@ -367,18 +365,18 @@
   case MotionNotify:
     while (XCheckTypedWindowEvent(dpy, win(), MotionNotify, ev));
     if (viewport && viewport->bumpScrollEvent(&ev->xmotion)) break;
-    handlePointerEvent(ev->xmotion.x, ev->xmotion.y,
+    handlePointerEvent(Point(ev->xmotion.x, ev->xmotion.y),
                        (ev->xmotion.state & 0x1f00) >> 8);
     break;
 
   case ButtonPress:
-    handlePointerEvent(ev->xbutton.x, ev->xbutton.y,
+    handlePointerEvent(Point(ev->xbutton.x, ev->xbutton.y),
                        (((ev->xbutton.state & 0x1f00) >> 8) |
                         (1 << (ev->xbutton.button-1))));
     break;
 
   case ButtonRelease:
-    handlePointerEvent(ev->xbutton.x, ev->xbutton.y,
+    handlePointerEvent(Point(ev->xbutton.x, ev->xbutton.y),
                        (((ev->xbutton.state & 0x1f00) >> 8) &
                         ~(1 << (ev->xbutton.button-1))));
     break;
@@ -397,20 +395,20 @@
       }
 
       if (fakeShiftPress)
-        cc->writer()->writeKeyEvent(XK_Shift_L, true);
+        cc->writer()->keyEvent(XK_Shift_L, true);
 
       downKeysym[ev->xkey.keycode] = ks;
-      cc->writer()->writeKeyEvent(ks, true);
+      cc->writer()->keyEvent(ks, true);
 
       if (fakeShiftPress)
-        cc->writer()->writeKeyEvent(XK_Shift_L, false);
+        cc->writer()->keyEvent(XK_Shift_L, false);
       break;
     }
 
   case KeyRelease:
     if (!viewOnly) {
       if (downKeysym[ev->xkey.keycode]) {
-        cc->writer()->writeKeyEvent(downKeysym[ev->xkey.keycode], false);
+        cc->writer()->keyEvent(downKeysym[ev->xkey.keycode], false);
         downKeysym[ev->xkey.keycode] = 0;
       }
     }
@@ -440,7 +438,7 @@
     // LeaveNotify is near enough...
     for (int i = 8; i < 256; i++) {
       if (downKeysym[i]) {
-        cc->writer()->writeKeyEvent(downKeysym[i], false);
+        cc->writer()->keyEvent(downKeysym[i], false);
         downKeysym[i] = 0;
       }
     }
@@ -545,7 +543,7 @@
         if (str) {
           if (!viewOnly) {
             vlog.debug("sending cut buffer to server");
-            cc->writer()->writeClientCutText(str, len);
+            cc->writer()->clientCutText(str, len);
           }
           XFree(str);
           return;
@@ -565,7 +563,7 @@
           vlog.debug("sending %s selection to server",
                      ev->selection == XA_PRIMARY ? "primary" :
                      ev->selection == xaCLIPBOARD ? "clipboard" : "unknown" );
-          cc->writer()->writeClientCutText((char*)data, nitems);
+          cc->writer()->clientCutText((char*)data, nitems);
         }
       }
     }
diff --git a/vncviewer_unix/DesktopWindow.h b/vncviewer_unix/DesktopWindow.h
index 9274f9d..a1af750 100644
--- a/vncviewer_unix/DesktopWindow.h
+++ b/vncviewer_unix/DesktopWindow.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -24,15 +24,15 @@
 
 #include <rfb/Cursor.h>
 #include <rfb/Rect.h>
+#include <rfb/Timer.h>
 #include "TXWindow.h"
 #include "TXViewport.h"
 #include "TXImage.h"
-#include "Timer.h"
 
 class CConn;
 
 class DesktopWindow : public TXWindow, public TXEventHandler,
-                      public TimerCallback {
+                      public rfb::Timer::Callback {
 public:
 
   DesktopWindow(Display* dpy, int w, int h,
@@ -47,7 +47,7 @@
   void setPF(const rfb::PixelFormat& pf) { im->setPF(pf); }
 
   // setCursor() sets the shape of the local cursor
-  void setCursor(const rfb::Point& hotspot, const rfb::Point& size,
+  void setCursor(int width, int height, const rfb::Point& hotspot,
                  void* data, void* mask);
 
   // resetLocalCursor() stops the rendering of the local cursor
@@ -97,8 +97,8 @@
   void createXCursors();
   void hideLocalCursor();
   void showLocalCursor();
-  void timerCallback(Timer* timer);
-  void handlePointerEvent(int x, int y, int buttonMask);
+  bool handleTimeout(rfb::Timer* timer);
+  void handlePointerEvent(const rfb::Point& pos, int buttonMask);
 
   CConn* cc;
   TXImage* im;
@@ -118,10 +118,11 @@
   bool newServerCutText;
   char* serverCutText_;
 
-  Timer setColourMapEntriesTimer;
+  rfb::Timer setColourMapEntriesTimer;
   TXViewport* viewport;
-  Timer pointerEventTimer;
-  int lastPointerX, lastPointerY, lastButtonMask;
+  rfb::Timer pointerEventTimer;
+  rfb::Point lastPointerPos;
+  int lastButtonMask;
   rdr::U32 downKeysym[256];
 };
 
diff --git a/vncviewer_unix/InfoDialog.h b/vncviewer_unix/InfoDialog.h
index ef0a4b0..a95f57b 100644
--- a/vncviewer_unix/InfoDialog.h
+++ b/vncviewer_unix/InfoDialog.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/vncviewer_unix/OptionsDialog.h b/vncviewer_unix/OptionsDialog.h
index 0bc1119..68ce8d6 100644
--- a/vncviewer_unix/OptionsDialog.h
+++ b/vncviewer_unix/OptionsDialog.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/vncviewer_unix/PasswdDialog.h b/vncviewer_unix/PasswdDialog.h
index 2654319..7b62b8e 100644
--- a/vncviewer_unix/PasswdDialog.h
+++ b/vncviewer_unix/PasswdDialog.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/vncviewer_unix/ServerDialog.h b/vncviewer_unix/ServerDialog.h
index 81d1291..f6980dc 100644
--- a/vncviewer_unix/ServerDialog.h
+++ b/vncviewer_unix/ServerDialog.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/vncviewer_unix/buildtime.c b/vncviewer_unix/buildtime.c
index a96031c..3f4c369 100644
--- a/vncviewer_unix/buildtime.c
+++ b/vncviewer_unix/buildtime.c
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/vncviewer_unix/parameters.h b/vncviewer_unix/parameters.h
index ffb0afd..6505aa8 100644
--- a/vncviewer_unix/parameters.h
+++ b/vncviewer_unix/parameters.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/vncviewer_unix/vncviewer.cxx b/vncviewer_unix/vncviewer.cxx
index 8f2d444..e9c9ac4 100644
--- a/vncviewer_unix/vncviewer.cxx
+++ b/vncviewer_unix/vncviewer.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -25,6 +25,7 @@
 #include <stdlib.h>
 #include <sys/time.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/wait.h>
 #include <unistd.h>
 #include <errno.h>
@@ -33,6 +34,7 @@
 #include <rfb/LogWriter.h>
 #include <network/TcpSocket.h>
 #include "TXWindow.h"
+#include "TXMsgBox.h"
 #include "CConn.h"
 
 #include <intl/gettext.h>
@@ -265,12 +267,12 @@
   textdomain(PACKAGE);
 
   snprintf(aboutText, sizeof(aboutText), 
-	   _("TightVNC viewer for X version 4.0 - built %s\n"
-	     "Copyright (C) 2002-2004 RealVNC Ltd.\n"
-	     "Copyright (C) 2000-2004 Constantin Kaplinsky\n"
-	     "Copyright (C) 2004-2005 Peter Astrand, Cendio AB\n"
-	     "See http://www.tightvnc.com for information on TightVNC."),
-	     buildtime);
+           _("TightVNC viewer for X version 1.5 - built %s\n"
+             "Copyright (C) 2002-2005 RealVNC Ltd.\n"
+             "Copyright (C) 2000-2004 Constantin Kaplinsky\n"
+             "Copyright (C) 2004-2005 Peter Astrand, Cendio AB\n"
+             "See http://www.tightvnc.com for information on TightVNC."),
+           buildtime);
   fprintf(stderr,"\n%s\n", aboutText);
 
   bind_textdomain_codeset(PACKAGE, "iso-8859-1");
@@ -284,7 +286,7 @@
 
   programName = argv[0];
   char* vncServerName = 0;
-  Display* dpy;
+  Display* dpy = 0;
 
   for (int i = 1; i < argc; i++) {
     if (Configuration::setParam(argv[i]))
@@ -303,6 +305,17 @@
     vncServerName = argv[i];
   }
 
+  // Create .vnc in the user's home directory if it doesn't already exist
+  char* homeDir = getenv("HOME");
+  if (homeDir) {
+    CharArray vncDir(strlen(homeDir)+6);
+    sprintf(vncDir.buf, "%s/.vnc", homeDir);
+    int result =  mkdir(vncDir.buf, 0755);
+    if (result == -1 && errno != EEXIST)
+      vlog.error("Could not create .vnc directory: %s.", strerror(errno));
+  } else
+    vlog.error("Could not create .vnc directory: environment variable $HOME not set.");
+
   if (!::autoSelect.hasBeenSet()) {
     // Default to AutoSelect=0 if -PreferredEncoding or -FullColor is used
     ::autoSelect.setParam(!::preferredEncoding.hasBeenSet() 
@@ -315,8 +328,6 @@
   }
 
   try {
-    TcpSocket::initTcpSockets();
-
     /* Tunnelling support. */
     if (strlen (via.getValueStr ()) > 0) {
       char *gatewayHost = "";
@@ -361,7 +372,7 @@
 
     TXWindow::init(dpy, "Vncviewer");
     xloginIconifier.iconify(dpy);
-    CConn cc(dpy, argc, argv, sock, vncServerName);
+    CConn cc(dpy, argc, argv, sock, vncServerName, listenMode);
 
     // X events are processed whenever reading from the socket would block.
 
@@ -370,8 +381,15 @@
       cc.processMsg();
     }
 
-  } catch (rdr::Exception &e) {
+  } catch (rdr::EndOfStream& e) {
+    vlog.info(e.str());
+  } catch (rdr::Exception& e) {
     vlog.error(e.str());
+    if (dpy) {
+      TXMsgBox msgBox(dpy, e.str(), MB_OK, "VNC Viewer: Information");
+      msgBox.show();
+    }
+    return 1;
   }
 
   return 0;
diff --git a/vncviewer_unix/vncviewer.man b/vncviewer_unix/vncviewer.man
index b567f0a4..a805d32 100644
--- a/vncviewer_unix/vncviewer.man
+++ b/vncviewer_unix/vncviewer.man
@@ -1,4 +1,4 @@
-.TH vncviewer 1 "30 December 2004" "TightVNC" "Virtual Network Computing"
+.TH vncviewer 1 "05 May 2004" "TightVNC" "Virtual Network Computing"
 .SH NAME
 vncviewer \- VNC viewer for X
 .SH SYNOPSIS
@@ -196,9 +196,10 @@
 respectively.
 
 .SH SEE ALSO
-.BR Xvnc (1)
+.BR Xvnc (1),
+.BR vncpasswd (1),
 .BR vncconfig (1),
-.BR vncserver (1),
+.BR vncserver (1)
 .br
 http://www.tightvnc.com
 
diff --git a/winvnc/AddNewClientDialog.h b/winvnc/AddNewClientDialog.h
index d8e0af3..9bf5135 100644
--- a/winvnc/AddNewClientDialog.h
+++ b/winvnc/AddNewClientDialog.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/winvnc/JavaViewer.cxx b/winvnc/JavaViewer.cxx
index fecbcee..15c05c4 100644
--- a/winvnc/JavaViewer.cxx
+++ b/winvnc/JavaViewer.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -16,7 +16,6 @@
  * USA.
  */
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <winvnc/JavaViewer.h>
 #include <winvnc/resource.h>
@@ -39,7 +38,11 @@
 JavaViewerServer::~JavaViewerServer() {
 }
 
-rdr::InStream* JavaViewerServer::getFile(const char* name, const char** contentType) {
+rdr::InStream* JavaViewerServer::getFile(const char* name,
+                                         const char** contentType,
+                                         int* contentLength,
+                                         time_t* lastModified)
+{
   if (strcmp(name, "/") == 0)
     name = "/index.vnc";
 
diff --git a/winvnc/JavaViewer.h b/winvnc/JavaViewer.h
index 20af786..ecda4d3 100644
--- a/winvnc/JavaViewer.h
+++ b/winvnc/JavaViewer.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -35,7 +35,8 @@
     JavaViewerServer(rfb::VNCServerST* desktop);
     virtual ~JavaViewerServer();
 
-    virtual rdr::InStream* getFile(const char* name, const char** contentType);
+    virtual rdr::InStream* getFile(const char* name, const char** contentType,
+                                   int* contentLength, time_t* lastModified);
 
     // rdr::Substitutor callback
     virtual char* substitute(const char* varName);
diff --git a/winvnc/ManagedListener.cxx b/winvnc/ManagedListener.cxx
new file mode 100644
index 0000000..9bf1b9a
--- /dev/null
+++ b/winvnc/ManagedListener.cxx
@@ -0,0 +1,94 @@
+/* Copyright (C) 2005 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 <winvnc/ManagedListener.h>
+#include <rfb/LogWriter.h>
+
+using namespace winvnc;
+using namespace rfb;
+using namespace win32;
+
+static LogWriter vlog("ManagedListener");
+
+
+ManagedListener::ManagedListener(SocketManager* mgr)
+: sock(0), filter(0), manager(mgr), addrChangeNotifier(0), server(0), port(0), localOnly(false) {
+}
+
+ManagedListener::~ManagedListener() {
+  if (sock)
+    manager->remListener(sock);
+  delete filter;
+}
+
+
+void ManagedListener::setServer(network::SocketServer* svr) {
+  if (svr == server)
+    return;
+  vlog.info("set server to %p", svr);
+  server = svr;
+  refresh();
+}
+
+void ManagedListener::setPort(int port_, bool localOnly_) {
+  if ((port_ == port) && (localOnly == localOnly_))
+    return;
+  vlog.info("set port to %d", port_);
+  port = port_;
+  localOnly = localOnly_;
+  refresh();
+}
+
+void ManagedListener::setFilter(const char* filterStr) {
+  vlog.info("set filter to %s", filterStr);
+  delete filter;
+  filter = new network::TcpFilter(filterStr);
+  if (sock && !localOnly)
+    sock->setFilter(filter);
+}
+
+void ManagedListener::setAddressChangeNotifier(SocketManager::AddressChangeNotifier* acn) {
+  if (acn == addrChangeNotifier)
+    return;
+  addrChangeNotifier = acn;
+  refresh();
+}
+
+
+void ManagedListener::refresh() {
+  if (sock)
+    manager->remListener(sock);
+  sock = 0;
+  if (!server)
+    return;
+  try {
+    if (port)
+      sock = new network::TcpListener(port, localOnly);
+  } catch (rdr::Exception& e) {
+    vlog.error(e.str());
+  }
+  if (sock) {
+    if (!localOnly)
+      sock->setFilter(filter);
+    try {
+      manager->addListener(sock, server, addrChangeNotifier);
+    } catch (...) {
+      sock = 0;
+      throw;
+    }
+  }
+}
diff --git a/winvnc/ManagedListener.h b/winvnc/ManagedListener.h
new file mode 100644
index 0000000..e83aa0b
--- /dev/null
+++ b/winvnc/ManagedListener.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2002-2005 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 __VNCSERVER_MANAGED_LISTENER_H__
+#define __VNCSERVER_MANAGED_LISTENER_H__
+
+#include <winsock2.h>
+#include <network/TcpSocket.h>
+#include <rfb_win32/SocketManager.h>
+
+namespace winvnc {
+
+  // -=- ManagedListener
+  //     Wrapper class which simplifies the management of a listening socket
+  //     on a specified port, attached to a SocketManager and SocketServer.
+  //     Reopens sockets & reconfigures filters & callbacks as appropriate.
+  //     Handles addition/removal of Listeners from SocketManager internally.
+
+  class ManagedListener {
+  public:
+    ManagedListener(rfb::win32::SocketManager* mgr);
+    ~ManagedListener();
+    
+    void setServer(network::SocketServer* svr);
+    void setPort(int port, bool localOnly=false);
+    void setFilter(const char* filter);
+    void setAddressChangeNotifier(rfb::win32::SocketManager::AddressChangeNotifier* acn);
+  
+    network::TcpListener* sock;
+  protected:
+    void refresh();
+    network::TcpFilter* filter;
+    rfb::win32::SocketManager* manager;
+    rfb::win32::SocketManager::AddressChangeNotifier* addrChangeNotifier;
+    network::SocketServer* server;
+    int port;
+    bool localOnly;
+  };
+
+};
+
+#endif
diff --git a/winvnc/QueryConnectDialog.cxx b/winvnc/QueryConnectDialog.cxx
index 52d7249..dc50eab 100644
--- a/winvnc/QueryConnectDialog.cxx
+++ b/winvnc/QueryConnectDialog.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/winvnc/QueryConnectDialog.h b/winvnc/QueryConnectDialog.h
index 30dd270..b28de19 100644
--- a/winvnc/QueryConnectDialog.h
+++ b/winvnc/QueryConnectDialog.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/winvnc/STrayIcon.cxx b/winvnc/STrayIcon.cxx
index 229f1ce..0b10fa5 100644
--- a/winvnc/STrayIcon.cxx
+++ b/winvnc/STrayIcon.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -19,6 +19,7 @@
 // -=- WinVNC Version 4.0 Tray Icon implementation
 
 #include <winvnc/STrayIcon.h>
+#include <winvnc/VNCServerService.h>
 #include <winvnc/resource.h>
 
 #include <rfb/LogWriter.h>
@@ -26,7 +27,7 @@
 #include <rfb_win32/LaunchProcess.h>
 #include <rfb_win32/TrayIcon.h>
 #include <rfb_win32/AboutDialog.h>
-#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/MsgBox.h>
 #include <rfb_win32/Service.h>
 #include <rfb_win32/CurrentUser.h>
 #include <winvnc/ControlPanel.h>
@@ -38,6 +39,7 @@
 static LogWriter vlog("STrayIcon");
 
 BoolParameter STrayIconThread::disableOptions("DisableOptions", "Disable the Options entry in the VNC Server tray menu.", false);
+BoolParameter STrayIconThread::disableClose("DisableClose", "Disable the Close entry in the VNC Server tray menu.", false);
 
 
 //
@@ -62,7 +64,7 @@
 public:
   STrayIcon(STrayIconThread& t) : thread(t),
     vncConfig(_T("vncconfig.exe"), isServiceProcess() ? _T("-noconsole -service") : _T("-noconsole")),
-    vncConnect(_T("winvnc4.exe"), _T("-connect")) {
+    vncConnect(_T("winvnc4.exe"), _T("-noconsole -connect")) {
 
     // ***
     SetWindowText(getHandle(), _T("winvnc::IPC_Interface"));
@@ -79,8 +81,9 @@
 
     case WM_USER:
       {
-        bool userKnown = CurrentUserToken().isValid();
+        bool userKnown = CurrentUserToken().canImpersonate();
         bool allowOptions = !STrayIconThread::disableOptions && userKnown;
+        bool allowClose = !STrayIconThread::disableClose && userKnown;
 
         switch (lParam) {
         case WM_LBUTTONDBLCLK:
@@ -97,7 +100,7 @@
           // Enable/disable options as required
           EnableMenuItem(trayMenu, ID_OPTIONS, (!allowOptions ? MF_GRAYED : MF_ENABLED) | MF_BYCOMMAND);
           EnableMenuItem(trayMenu, ID_CONNECT, (!userKnown ? MF_GRAYED : MF_ENABLED) | MF_BYCOMMAND);
-          EnableMenuItem(trayMenu, ID_CLOSE, (isServiceProcess() ? MF_GRAYED : MF_ENABLED) | MF_BYCOMMAND);
+          EnableMenuItem(trayMenu, ID_CLOSE, (!allowClose ? MF_GRAYED : MF_ENABLED) | MF_BYCOMMAND);
 
           thread.server.getClientsInfo(&LCInfo);
           CheckMenuItem(trayMenu, ID_DISABLE_NEW_CLIENTS, (LCInfo.getDisable() ? MF_CHECKED : MF_UNCHECKED) | MF_BYCOMMAND);
@@ -134,8 +137,8 @@
       case ID_OPTIONS:
         {
           CurrentUserToken token;
-          if (token.isValid())
-            vncConfig.start(isServiceProcess() ? (HANDLE)token : 0);
+          if (token.canImpersonate())
+            vncConfig.start(isServiceProcess() ? (HANDLE)token : INVALID_HANDLE_VALUE);
           else
             vlog.error("Options: unknown current user");
         }
@@ -143,8 +146,8 @@
       case ID_CONNECT:
         {
           CurrentUserToken token;
-          if (token.isValid())
-            vncConnect.start(isServiceProcess() ? (HANDLE)token : 0);
+          if (token.canImpersonate())
+            vncConnect.start(isServiceProcess() ? (HANDLE)token : INVALID_HANDLE_VALUE);
           else
             vlog.error("Options: unknown current user");
         }
@@ -153,8 +156,19 @@
         thread.server.disconnectClients("tray menu disconnect");
         break;
       case ID_CLOSE:
-        if (!isServiceProcess())
-          thread.server.stop();
+        if (MsgBox(0, _T("Are you sure you want to close the server?"),
+                   MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) == IDYES) {
+          if (isServiceProcess()) {
+            ImpersonateCurrentUser icu;
+            try {
+              rfb::win32::stopService(VNCServerService::Name);
+            } catch (rdr::Exception& e) {
+              MsgBox(0, TStr(e.str()), MB_ICONERROR | MB_OK);
+            }
+          } else {
+            thread.server.stop();
+          }
+        }
         break;
       case ID_ABOUT:
         AboutDialog::instance.showDialog();
@@ -172,12 +186,10 @@
             CharArray viewer = new char[command->cbData + 1];
             memcpy(viewer.buf, command->lpData, command->cbData);
             viewer.buf[command->cbData] = 0;
-            thread.server.addNewClient(viewer.buf);
+            return thread.server.addNewClient(viewer.buf) ? 1 : 0;
           }
-          break;
         case 2:
-          thread.server.disconnectClients("IPC disconnect");
-          break;
+          return thread.server.disconnectClients("IPC disconnect") ? 1 : 0;
         case 3:
           thread.server.setClientsStatus((rfb::ListConnInfo *)command->cbData);
         case 4:
@@ -231,7 +243,7 @@
 
 STrayIconThread::STrayIconThread(VNCServerWin32& sm, UINT inactiveIcon_, UINT activeIcon_, 
                                  UINT dis_inactiveIcon_, UINT dis_activeIcon_, UINT menu_)
-: server(sm), inactiveIcon(inactiveIcon_), activeIcon(activeIcon_),
+: Thread("TrayIcon"), server(sm), inactiveIcon(inactiveIcon_), activeIcon(activeIcon_),
   dis_inactiveIcon(dis_inactiveIcon_), dis_activeIcon(dis_activeIcon_),menu(menu_),
   windowHandle(0), runTrayIcon(true) {
   start();
diff --git a/winvnc/STrayIcon.h b/winvnc/STrayIcon.h
index ef32eba..309d3f4 100644
--- a/winvnc/STrayIcon.h
+++ b/winvnc/STrayIcon.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -40,6 +40,7 @@
     void setToolTip(const TCHAR* text);
 
     static rfb::BoolParameter disableOptions;
+    static rfb::BoolParameter disableClose;
 
     friend class STrayIcon;
   protected:
@@ -57,4 +58,4 @@
 
 };
 
-#endif
\ No newline at end of file
+#endif
diff --git a/winvnc/VNCServerService.cxx b/winvnc/VNCServerService.cxx
index c967a5a..2ef2ee0 100644
--- a/winvnc/VNCServerService.cxx
+++ b/winvnc/VNCServerService.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/winvnc/VNCServerService.h b/winvnc/VNCServerService.h
index 378ad0c..c7a76cc 100644
--- a/winvnc/VNCServerService.h
+++ b/winvnc/VNCServerService.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -31,9 +31,6 @@
     DWORD serviceMain(int argc, TCHAR* argv[]);
     void stop();
 
-    void osShuttingDown() {}
-    void readParams() {}
-
     static const TCHAR* Name;
   protected:
     VNCServerWin32& server;
diff --git a/winvnc/VNCServerWin32.cxx b/winvnc/VNCServerWin32.cxx
index 5b2adbe..3b0e1a0 100644
--- a/winvnc/VNCServerWin32.cxx
+++ b/winvnc/VNCServerWin32.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -21,8 +21,8 @@
 #include <winvnc/VNCServerWin32.h>
 #include <winvnc/resource.h>
 #include <winvnc/STrayIcon.h>
-
-#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/ComputerName.h>
+#include <rfb_win32/CurrentUser.h>
 #include <rfb_win32/Service.h>
 #include <rfb/SSecurityFactoryStandard.h>
 #include <rfb/Hostname.h>
@@ -39,9 +39,7 @@
 
 const TCHAR* winvnc::VNCServerWin32::RegConfigPath = _T("Software\\TightVNC\\WinVNC4");
 
-const UINT VNCM_REG_CHANGED = WM_USER;
-const UINT VNCM_COMMAND = WM_USER + 1;
-
+// FIXME: Move into an .h file?
 extern const UINT VNCM_FT_DOWNLOAD;
 
 
@@ -50,81 +48,22 @@
 static IntParameter port_number("PortNumber",
   "TCP/IP port on which the server will accept connections", 5900);
 static StringParameter hosts("Hosts",
-  "Filter describing which hosts are allowed access to this server", "+");
-static VncAuthPasswdConfigParameter vncAuthPasswd;
+  "Filter describing which hosts are allowed access to this server", "+0.0.0.0/0.0.0.0");
 static BoolParameter localHost("LocalHost",
   "Only accept connections from via the local loop-back network interface", false);
-
-
-// -=- ManagedListener
-//     Wrapper class which simplifies the management of a listening socket
-//     on a specified port, attached to a SocketManager and SocketServer.
-//     Ensures that socket and filter are deleted and updated appropriately.
-
-class ManagedListener {
-public:
-  ManagedListener(win32::SocketManager* mgr, SocketServer* svr)
-    : sock(0), filter(0), port(0), manager(mgr),
-      server(svr), localOnly(0) {}
-  ~ManagedListener() {setPort(0);}
-  void setPort(int port, bool localOnly=false);
-  void setFilter(const char* filter);
-  TcpListener* sock;
-protected:
-  TcpFilter* filter;
-  win32::SocketManager* manager;
-  SocketServer* server;
-  int port;
-  bool localOnly;
-};
-
-// - If the port number/localHost setting has changed then tell the
-//   SocketManager to shutdown and delete it.  Also remove &
-//   delete the filter.  Then try to open a socket on the new port.
-void ManagedListener::setPort(int newPort, bool newLocalOnly) {
-  if ((port == newPort) && (localOnly == newLocalOnly) && sock) return;
-  if (sock) {
-    vlog.info("Closed TcpListener on port %d", port);
-    sock->setFilter(0);
-    delete filter;
-    manager->remListener(sock);
-    sock = 0;
-    filter = 0;
-  }
-  port = newPort;
-  localOnly = newLocalOnly;
-  if (port != 0) {
-    try {
-      sock = new TcpListener(port, localOnly);
-      vlog.info("Created TcpListener on port %d%s", port,
-                localOnly ? "(localhost)" : "(any)");
-    } catch (rdr::Exception& e) {
-      vlog.error("TcpListener on port %d failed (%s)", port, e.str());
-    }
-  }
-  if (sock)
-    manager->addListener(sock, server);
-}
-
-void ManagedListener::setFilter(const char* newFilter) {
-  if (!sock) return;
-  vlog.info("Updating TcpListener filter");
-  sock->setFilter(0);
-  delete filter;
-  filter = new TcpFilter(newFilter);
-  sock->setFilter(filter);
-}
+static BoolParameter queryOnlyIfLoggedOn("QueryOnlyIfLoggedOn",
+  "Only prompt for a local user to accept incoming connections if there is a user logged on", false);
 
 
 VNCServerWin32::VNCServerWin32()
-  : vncServer(CStr(ComputerName().buf), &desktop),
-    httpServer(0), runServer(false),
-    isDesktopStarted(false),
-    command(NoCommand), commandSig(commandLock),
-    queryConnectDialog(0) {
-  // Create the Java-viewer HTTP server
-  httpServer = new JavaViewerServer(&vncServer);
-
+  : command(NoCommand), commandSig(commandLock),
+    commandEvent(CreateEvent(0, TRUE, FALSE, 0)),
+    vncServer(CStr(ComputerName().buf), &desktop),
+    hostThread(0), runServer(false), isDesktopStarted(false),
+    httpServer(&vncServer), config(&sockMgr), trayIcon(0),
+    rfbSock(&sockMgr), httpSock(&sockMgr),
+    queryConnectDialog(0)
+{
   // Initialise the desktop
   desktop.setStatusLocation(&isDesktopStarted);
 
@@ -134,15 +73,79 @@
   // Register the desktop's event to be handled
   sockMgr.addEvent(desktop.getUpdateEvent(), &desktop);
 
+  // Register the queued command event to be handled
+  sockMgr.addEvent(commandEvent, this);
+
   vncServer.setFTManager((rfb::SFileTransferManager *)&m_FTManager);
 }
 
 VNCServerWin32::~VNCServerWin32() {
+  delete trayIcon;
+
   // Stop the SDisplay from updating our state
   desktop.setStatusLocation(0);
 
-  // Destroy the HTTP server
-  delete httpServer;
+  // Join the Accept/Reject dialog thread
+  if (queryConnectDialog)
+    delete queryConnectDialog->join();
+}
+
+
+void VNCServerWin32::processAddressChange(network::SocketListener* sock_) {
+  if (!trayIcon || (sock_ != rfbSock.sock))
+    return;
+
+  // Tool-tip prefix depends on server mode
+  const TCHAR* prefix = _T("VNC Server (User):");
+  if (isServiceProcess())
+    prefix = _T("VNC Server (Service):");
+
+  // Fetch the list of addresses
+  std::list<char*> addrs;
+  if (rfbSock.sock)
+    rfbSock.sock->getMyAddresses(&addrs);
+  else
+    addrs.push_front(strDup("Not accepting connections"));
+
+  // Allocate space for the new tip
+  std::list<char*>::iterator i, next_i;
+  int length = _tcslen(prefix)+1;
+  for (i=addrs.begin(); i!= addrs.end(); i++)
+    length += strlen(*i) + 1;
+
+  // Build the new tip
+  TCharArray toolTip(length);
+  _tcscpy(toolTip.buf, prefix);
+  for (i=addrs.begin(); i!= addrs.end(); i=next_i) {
+    next_i = i; next_i ++;
+    TCharArray addr = *i;    // Assumes ownership of string
+    _tcscat(toolTip.buf, addr.buf);
+    if (next_i != addrs.end())
+      _tcscat(toolTip.buf, _T(","));
+  }
+  
+  // Pass the new tip to the tray icon
+  vlog.info("Refreshing tray icon");
+  trayIcon->setToolTip(toolTip.buf);
+}
+
+void VNCServerWin32::regConfigChanged() {
+  // -=- Make sure we're listening on the right ports.
+  rfbSock.setServer(&vncServer);
+  rfbSock.setPort(port_number, localHost);
+  httpSock.setServer(&httpServer);
+  httpSock.setPort(http_port, localHost);
+
+  // -=- Update the Java viewer's web page port number.
+  httpServer.setRFBport(rfbSock.sock ? port_number : 0);
+
+  // -=- Update the TCP address filter for both ports, if open.
+  CharArray pattern(hosts.getData());
+  rfbSock.setFilter(pattern.buf);
+  httpSock.setFilter(pattern.buf);
+
+  // -=- Update the tray icon tooltip text with IP addresses
+  processAddressChange(rfbSock.sock);
 }
 
 
@@ -152,87 +155,39 @@
     runServer = true;
   }
 
+  // - Create the tray icon (if possible)
+  trayIcon = new STrayIconThread(*this, IDI_ICON, IDI_CONNECTED,
+                                 IDI_ICON_DISABLE, IDI_CONNECTED_DISABLE,
+                                 IDR_TRAY);
+
   // - Register for notification of configuration changes
+  config.setCallback(this);
   if (isServiceProcess())
     config.setKey(HKEY_LOCAL_MACHINE, RegConfigPath);
   else
     config.setKey(HKEY_CURRENT_USER, RegConfigPath);
-  config.setNotifyThread(Thread::self(), VNCM_REG_CHANGED);
 
-  // - Create the tray icon if possible
-  STrayIconThread trayIcon(*this, IDI_ICON, IDI_CONNECTED, IDI_ICON_DISABLE,
-                            IDI_CONNECTED_DISABLE, IDR_TRAY);
+  // - Set the address-changed handler for the RFB socket
+  rfbSock.setAddressChangeNotifier(this);
 
   DWORD result = 0;
   try {
-    // - Create some managed listening sockets
-    ManagedListener rfb(&sockMgr, &vncServer);
-    ManagedListener http(&sockMgr, httpServer);
+    vlog.debug("Entering message loop");
 
-    // - Continue to operate until WM_QUIT is processed
+    // - Run the server until we're told to quit
     MSG msg;
-    do {
-      // -=- Make sure we're listening on the right ports.
-      rfb.setPort(port_number, localHost);
-      http.setPort(http_port, localHost);
-
-      // -=- Update the Java viewer's web page port number.
-      httpServer->setRFBport(rfb.sock ? port_number : 0);
-
-      // -=- Update the TCP address filter for both ports, if open.
-      CharArray pattern;
-      pattern.buf = hosts.getData();
-      if (!localHost) {
-        rfb.setFilter(pattern.buf);
-        http.setFilter(pattern.buf);
-      }
-
-      // - If there is a listening port then add the address to the
-      //   tray icon's tool-tip text.
-      {
-        const TCHAR* prefix = isServiceProcess() ?
-          _T("VNC Server (Service):") : _T("VNC Server (User):");
-
-        std::list<char*> addrs;
-        if (rfb.sock)
-          rfb.sock->getMyAddresses(&addrs);
-        else
-          addrs.push_front(strDup("Not accepting connections"));
-
-        std::list<char*>::iterator i, next_i;
-        int length = _tcslen(prefix)+1;
-        for (i=addrs.begin(); i!= addrs.end(); i++)
-          length += strlen(*i) + 1;
-
-        TCharArray toolTip(length);
-        _tcscpy(toolTip.buf, prefix);
-        for (i=addrs.begin(); i!= addrs.end(); i=next_i) {
-          next_i = i; next_i ++;
-          TCharArray addr = *i;    // Assumes ownership of string
-          _tcscat(toolTip.buf, addr.buf);
-          if (next_i != addrs.end())
-            _tcscat(toolTip.buf, _T(","));
-        }
-        trayIcon.setToolTip(toolTip.buf);
-      }
-
-      vlog.debug("Entering message loop");
-
-      // - Run the server until the registry changes, or we're told to quit
-      while (sockMgr.getMessage(&msg, NULL, 0, 0)) {
-        if (msg.hwnd == 0) {
-          if (msg.message == VNCM_REG_CHANGED)
-            break;
-          if (msg.message == VNCM_COMMAND)
-            doCommand();
-          if (msg.message == VNCM_FT_DOWNLOAD)
-            m_FTManager.processDownloadMsg(msg);
-        }
-        TranslateMessage(&msg);
-        DispatchMessage(&msg);
-      }
-
-    } while ((msg.message != WM_QUIT) || runServer);
+    int result = 0;
+    while (runServer) {
+      result = sockMgr.getMessage(&msg, NULL, 0, 0);
+      if (result < 0)
+        throw rdr::SystemException("getMessage", GetLastError());
+      if (!isServiceProcess() && (result == 0))
+        break;
+      if (msg.message == VNCM_FT_DOWNLOAD)
+        m_FTManager.processDownloadMsg(msg);
+      TranslateMessage(&msg);
+      DispatchMessage(&msg);
+    }
 
     vlog.debug("Server exited cleanly");
   } catch (rdr::SystemException &s) {
@@ -253,7 +208,8 @@
 void VNCServerWin32::stop() {
   Lock l(runLock);
   runServer = false;
-  PostThreadMessage(hostThread->getThreadId(), WM_QUIT, 0, 0);
+  if (hostThread)
+    PostThreadMessage(hostThread->getThreadId(), WM_QUIT, 0, 0);
 }
 
 
@@ -290,6 +246,8 @@
                                             const char* userName,
                                             char** reason)
 {
+  if (queryOnlyIfLoggedOn && CurrentUserToken().noUserLoggedOn())
+    return VNCServerST::ACCEPT;
   if (queryConnectDialog) {
     *reason = rfb::strDup("Another connection is currently being queried.");
     return VNCServerST::REJECT;
@@ -300,51 +258,49 @@
 }
 
 void VNCServerWin32::queryConnectionComplete() {
-  Thread* qcd = queryConnectDialog;
-  queueCommand(QueryConnectionComplete, 0, 0);
-  delete qcd->join();
+  queueCommand(QueryConnectionComplete, 0, 0, false);
 }
 
 
-bool VNCServerWin32::queueCommand(Command cmd, const void* data, int len) {
+bool VNCServerWin32::queueCommand(Command cmd, const void* data, int len, bool wait) {
   Lock l(commandLock);
-  while (command != NoCommand) commandSig.wait();
+  while (command != NoCommand)
+    commandSig.wait();
   command = cmd;
   commandData = data;
   commandDataLen = len;
-  if (PostThreadMessage(hostThread->getThreadId(), VNCM_COMMAND, 0, 0))
-    while (command != NoCommand) commandSig.wait();
-  else
-    return false;
+  SetEvent(commandEvent);
+  if (wait) {
+    while (command != NoCommand)
+      commandSig.wait();
+    commandSig.signal();
+  }
   return true;
 }
 
-void VNCServerWin32::doCommand() {
-  Lock l(commandLock);
-  if (command == NoCommand) return;
+void VNCServerWin32::processEvent(HANDLE event_) {
+  ResetEvent(event_);
 
-  // Perform the required command
-  switch (command) {
+  if (event_ == commandEvent.h) {
+    // If there is no command queued then return immediately
+    {
+      Lock l(commandLock);
+      if (command == NoCommand)
+        return;
+    }
 
-  case DisconnectClients:
-    // Disconnect all currently active VNC Viewers
-    vncServer.closeClients((const char*)commandData);
-    break;
+    // Perform the required command
+    switch (command) {
 
-  case AddClient:
-    // Make a reverse connection to a VNC Viewer
-    vncServer.addClient((network::Socket*)commandData, true);
-    sockMgr.addSocket((network::Socket*)commandData, &vncServer);
-    break;
+    case DisconnectClients:
+      // Disconnect all currently active VNC Viewers
+      vncServer.closeClients((const char*)commandData);
+      break;
 
-  case QueryConnectionComplete:
-    // The Accept/Reject dialog has completed
-    // Get the result, then clean it up
-    vncServer.approveConnection(queryConnectDialog->getSock(),
-                                queryConnectDialog->isAccepted(),
-                                "Connection rejected by user");
-    queryConnectDialog = 0;
-    break;
+    case AddClient:
+      // Make a reverse connection to a VNC Viewer
+      sockMgr.addSocket((network::Socket*)commandData, &vncServer);
+      break;
   case GetClientsInfo:
     vncServer.getConnInfo((ListConnInfo*)commandData); 
     break;
@@ -352,11 +308,26 @@
     vncServer.setConnStatus((ListConnInfo*)commandData); 
     break;
 
-  default:
-    vlog.error("unknown command %d queued", command);
-  };
+    case QueryConnectionComplete:
+      // The Accept/Reject dialog has completed
+      // Get the result, then clean it up
+      vncServer.approveConnection(queryConnectDialog->getSock(),
+                                  queryConnectDialog->isAccepted(),
+                                  "Connection rejected by user");
+      delete queryConnectDialog->join();
+      queryConnectDialog = 0;
+      break;
 
-  // Clear the command and signal completion
-  command = NoCommand;
-  commandSig.signal();
+    default:
+      vlog.error("unknown command %d queued", command);
+    };
+
+    // Clear the command and signal completion
+    {
+      Lock l(commandLock);
+      command = NoCommand;
+      commandSig.signal();
+    }
+  }
 }
+
diff --git a/winvnc/VNCServerWin32.h b/winvnc/VNCServerWin32.h
index 9cc0f1e..579a6a0 100644
--- a/winvnc/VNCServerWin32.h
+++ b/winvnc/VNCServerWin32.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -29,11 +29,16 @@
 #include <rfb_win32/SFileTransferManagerWin32.h>
 #include <winvnc/QueryConnectDialog.h>
 #include <winvnc/JavaViewer.h>
-//#include <rfb/ListConnInfo.h>
+#include <winvnc/ManagedListener.h>
 
 namespace winvnc {
 
-  class VNCServerWin32 : rfb::VNCServerST::QueryConnectionHandler {
+  class STrayIconThread;
+
+  class VNCServerWin32 : rfb::VNCServerST::QueryConnectionHandler,
+                         rfb::win32::SocketManager::AddressChangeNotifier,
+                         rfb::win32::RegConfig::Callback,
+                         rfb::win32::EventHandler {
   public:
     VNCServerWin32();
     virtual ~VNCServerWin32();
@@ -61,31 +66,43 @@
     // CALLED FROM AcceptConnectDialog THREAD
     void queryConnectionComplete();
 
-    // Overridden VNCServerST::QueryConnectionHandler callback,
-    // used to prompt user to accept or reject a connection.
-    // CALLBACK IN VNCServerST "HOST" THREAD
-    virtual rfb::VNCServerST::queryResult queryConnection(network::Socket* sock,
-                                                          const char* userName,
-                                                          char** reason);
+    // Where to read the configuration settings from
+    static const TCHAR* RegConfigPath;
 
     bool getClientsInfo(rfb::ListConnInfo* LCInfo);
 
     bool setClientsStatus(rfb::ListConnInfo* LCInfo);
 
-    // Where to read the configuration settings from
-    static const TCHAR* RegConfigPath;
+  protected:
+    // VNCServerST::QueryConnectionHandler interface
+    // Callback used to prompt user to accept or reject a connection.
+    // CALLBACK IN VNCServerST "HOST" THREAD
+    virtual rfb::VNCServerST::queryResult queryConnection(network::Socket* sock,
+                                                          const char* userName,
+                                                          char** reason);
+
+    // SocketManager::AddressChangeNotifier interface
+    // Used to keep tray icon up to date
+    virtual void processAddressChange(network::SocketListener* sl);
+
+    // RegConfig::Callback interface
+    // Called via the EventManager whenver RegConfig sees the registry change
+    virtual void regConfigChanged();
+
+    // EventHandler interface
+    // Used to perform queued commands
+    virtual void processEvent(HANDLE event);
 
   protected:
-
     // Perform a particular internal function in the server thread
     typedef enum {NoCommand, DisconnectClients, AddClient, QueryConnectionComplete, SetClientsStatus, GetClientsInfo} Command;
-    bool queueCommand(Command cmd, const void* data, int len);
-    void doCommand();
+    bool queueCommand(Command cmd, const void* data, int len, bool wait=true);
     Command command;
     const void* commandData;
     int commandDataLen;
     rfb::Mutex commandLock;
     rfb::Condition commandSig;
+    rfb::win32::Handle commandEvent;
 
     // VNCServerWin32 Server-internal state
     rfb::win32::SDisplay desktop;
@@ -94,9 +111,16 @@
     rfb::Thread* hostThread;
     bool runServer;
     bool isDesktopStarted;
-    JavaViewerServer* httpServer;
-    rfb::win32::RegistryReader config;
+    JavaViewerServer httpServer;
     rfb::win32::SocketManager sockMgr;
+    rfb::win32::RegConfig config;
+
+    ManagedListener rfbSock;
+    ManagedListener httpSock;
+    STrayIconThread* trayIcon;
+
+    //rfb::SSecurityFactoryStandard securityFactory;
+
     QueryConnectDialog* queryConnectDialog;
     rfb::win32::SFileTransferManagerWin32 m_FTManager;
   };
diff --git a/winvnc/buildTime.cxx b/winvnc/buildTime.cxx
index bab2e13..9f37b38 100644
--- a/winvnc/buildTime.cxx
+++ b/winvnc/buildTime.cxx
@@ -1 +1,18 @@
-const char* buildTime = "Built on " __DATE__ " at " __TIME__;
\ No newline at end of file
+/* Copyright (C) 2005 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.
+ */
+const char* buildTime = "Built on " __DATE__ " at " __TIME__;
diff --git a/winvnc/msvcwarning.h b/winvnc/msvcwarning.h
deleted file mode 100644
index 53a0678..0000000
--- a/winvnc/msvcwarning.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/* 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 : 4800 ) // forcing bool 'true' or 'false'
-#pragma warning( disable : 4786 ) // debug identifier truncated
diff --git a/winvnc/resource.h b/winvnc/resource.h
index 1cb074b..68316be 100644
--- a/winvnc/resource.h
+++ b/winvnc/resource.h
@@ -9,11 +9,12 @@
 #define IDD_ABOUT                       104
 #define IDI_CONNECTED                   105
 #define IDR_VNCVIEWER_JAR               106
-#define IDD_CONTROL_PANEL               106
 #define IDD_QUERY_CONNECT               107
 #define IDD_ADD_NEW_CLIENT              108
-#define IDI_ICON_DISABLE                109
-#define IDI_CONNECTED_DISABLE           110
+#define IDB_BITMAP                      109
+#define IDD_CONTROL_PANEL               110
+#define IDI_ICON_DISABLE                111
+#define IDI_CONNECTED_DISABLE           112
 #define IDC_DESCRIPTION                 1000
 #define IDC_BUILDTIME                   1001
 #define IDC_VERSION                     1002
@@ -45,9 +46,9 @@
 // 
 #ifdef APSTUDIO_INVOKED
 #ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE        111
+#define _APS_NEXT_RESOURCE_VALUE        113
 #define _APS_NEXT_COMMAND_VALUE         40008
-#define _APS_NEXT_CONTROL_VALUE         1024
+#define _APS_NEXT_CONTROL_VALUE         1023
 #define _APS_NEXT_SYMED_VALUE           101
 #endif
 #endif
diff --git a/winvnc/winvnc.bmp b/winvnc/winvnc.bmp
new file mode 100644
index 0000000..90c02f8
--- /dev/null
+++ b/winvnc/winvnc.bmp
Binary files differ
diff --git a/winvnc/winvnc.cxx b/winvnc/winvnc.cxx
index 5ba6ebc..2d01f89 100644
--- a/winvnc/winvnc.cxx
+++ b/winvnc/winvnc.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -31,7 +31,7 @@
 #include <rfb/Logger_file.h>
 #include <rfb/LogWriter.h>
 #include <rfb_win32/AboutDialog.h>
-#include <rfb_win32/Win32Util.h>
+#include <rfb_win32/MsgBox.h>
 #include <network/TcpSocket.h>
 
 using namespace winvnc;
@@ -53,8 +53,7 @@
 //     Read in the command-line parameters and interpret them.
 //
 
-void
-programInfo() {
+static void programInfo() {
   win32::FileVersionInfo inf;
   _tprintf(_T("%s - %s, Version %s\n"),
     inf.getVerString(_T("ProductName")),
@@ -64,8 +63,7 @@
   _tprintf(_T("%s\n\n"), inf.getVerString(_T("LegalCopyright")));
 }
 
-void
-programUsage() {
+static void programUsage() {
   printf("Command-line options:\n");
   printf("  -connect [<host[::port]>]            - Connect an existing WinVNC server to a listening viewer.\n");
   printf("  -disconnect                          - Disconnect all clients from an existing WinVNC server.\n");
@@ -86,8 +84,22 @@
   Configuration::listParams();
 }
 
-void
-processParams(int argc, const char* argv[]) {
+static void MsgBoxOrLog(const char* msg, bool isError=false) {
+  if (close_console) {
+    MsgBox(0, TStr(msg), (isError ? MB_ICONERROR : MB_ICONINFORMATION) | MB_OK);
+  } else {
+    if (isError) {
+      try {
+        vlog.error(msg);
+        return;
+      } catch (...) {
+      }
+    }
+    fprintf(stderr, "%s\n", msg);
+  }
+}
+
+static void processParams(int argc, const char* argv[]) {
   for (int i=1; i<argc; i++) {
     try {
 
@@ -96,6 +108,7 @@
         CharArray host;
         if (i+1 < argc) {
           host.buf = strDup(argv[i+1]);
+          i++;
         } else {
           AddNewClientDialog ancd;
           if (ancd.showDialog())
@@ -103,38 +116,47 @@
         }
         if (host.buf) {
           HWND hwnd = FindWindow(0, _T("winvnc::IPC_Interface"));
+          if (!hwnd)
+            throw rdr::Exception("Unable to locate existing VNC Server.");
           COPYDATASTRUCT copyData;
           copyData.dwData = 1; // *** AddNewClient
           copyData.cbData = strlen(host.buf);
           copyData.lpData = (void*)host.buf;
-          i++;
-          SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)&copyData);
-          printf("Sent connect request to VNC Server...\n");
+          printf("Sending connect request to VNC Server...\n");
+          if (!SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)&copyData))
+            MsgBoxOrLog("Connection failed.", true);
         }
       } else if (strcasecmp(argv[i], "-disconnect") == 0) {
+        runServer = false;
         HWND hwnd = FindWindow(0, _T("winvnc::IPC_Interface"));
+        if (!hwnd)
+          throw rdr::Exception("Unable to locate existing VNC Server.");
         COPYDATASTRUCT copyData;
         copyData.dwData = 2; // *** DisconnectClients
         copyData.lpData = 0;
         copyData.cbData = 0;
-        SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)&copyData);
-        printf("Sent disconnect request to VNC Server...\n");
-        runServer = false;
+        printf("Sending disconnect request to VNC Server...\n");
+        if (!SendMessage(hwnd, WM_COPYDATA, 0, (LPARAM)&copyData))
+          MsgBoxOrLog("Failed to disconnect clients.", true);
       } else if (strcasecmp(argv[i], "-start") == 0) {
         printf("Attempting to start service...\n");
         runServer = false;
         if (rfb::win32::startService(VNCServerService::Name))
-          printf("Started service successfully\n");
+          MsgBoxOrLog("Started service successfully");
       } else if (strcasecmp(argv[i], "-stop") == 0) {
         printf("Attempting to stop service...\n");
         runServer = false;
         if (rfb::win32::stopService(VNCServerService::Name))
-          printf("Stopped service successfully\n");
+          MsgBoxOrLog("Stopped service successfully");
       } else if (strcasecmp(argv[i], "-status") == 0) {
         printf("Querying service status...\n");
         runServer = false;
-        rfb::win32::printServiceStatus(VNCServerService::Name);
-
+        DWORD state = rfb::win32::getServiceState(VNCServerService::Name);
+        CharArray stateStr(rfb::win32::serviceStateName(state));
+        const char* stateMsg = "The %s Service is in the %s state.";
+        CharArray result(strlen(stateStr.buf) + _tcslen(VNCServerService::Name) + strlen(stateMsg) + 1);
+        sprintf(result.buf, stateMsg, (const char*)CStr(VNCServerService::Name), stateStr.buf);
+        MsgBoxOrLog(result.buf);
       } else if (strcasecmp(argv[i], "-service") == 0) {
         printf("Run in service mode\n");
         runAsService = true;
@@ -147,15 +169,18 @@
         if (rfb::win32::registerService(VNCServerService::Name,
                                         _T("VNC Server Version 4"),
                                         argc-(j+1), &argv[j+1]))
-          printf("Registered service successfully\n");
+          MsgBoxOrLog("Registered service successfully");
       } else if (strcasecmp(argv[i], "-unregister") == 0) {
         printf("Attempting to unregister service...\n");
         runServer = false;
         if (rfb::win32::unregisterService(VNCServerService::Name))
-          printf("Unregistered service successfully\n");
+          MsgBoxOrLog("Unregistered service successfully");
 
       } else if (strcasecmp(argv[i], "-noconsole") == 0) {
         close_console = true;
+        vlog.info("closing console");
+        if (!FreeConsole())
+          vlog.info("unable to close console:%u", GetLastError());
 
       } else if ((strcasecmp(argv[i], "-help") == 0) ||
         (strcasecmp(argv[i], "--help") == 0) ||
@@ -183,7 +208,7 @@
       }
 
     } catch (rdr::Exception& e) {
-      vlog.error(e.str());
+      MsgBoxOrLog(e.str(), true);
     }
   }
 }
@@ -213,13 +238,7 @@
 
     // - Run the server if required
     if (runServer) {
-      if (close_console) {
-        vlog.info("closing console");
-        if (!FreeConsole())
-          vlog.info("unable to close console:%u", GetLastError());
-      }
-
-      network::TcpSocket::initTcpSockets();
+      // Start the network subsystem and run the server
       VNCServerWin32 server;
 
       if (runAsService) {
@@ -235,13 +254,7 @@
 
     vlog.debug("WinVNC service destroyed");
   } catch (rdr::Exception& e) {
-    try {
-      vlog.error("Fatal Error: %s", e.str());
-    } catch (...) {
-      fprintf(stderr, "WinVNC: Fatal Error: %s\n", e.str());
-    }
-    if (!runAsService)
-      MsgBox(0, TStr(e.str()), MB_ICONERROR | MB_OK);
+    MsgBoxOrLog(e.str(), true);
   }
 
   vlog.debug("WinVNC process quitting");
diff --git a/winvnc/winvnc.dsp b/winvnc/winvnc.dsp
index 1daa0f1..a3a56e3 100644
--- a/winvnc/winvnc.dsp
+++ b/winvnc/winvnc.dsp
@@ -43,7 +43,7 @@
 # PROP Ignore_Export_Lib 0

 # PROP Target_Dir ""

 # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c

-# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /FI"msvcwarning.h" /D "NDEBUG" /D "_CONSOLE" /D "WIN32" /D "_MBCS" /YX /FD /c

+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /FI"rdr/msvcwarning.h" /D "NDEBUG" /D "_CONSOLE" /D "WIN32" /D "_MBCS" /YX /FD /c

 # ADD BASE RSC /l 0x809 /d "NDEBUG"

 # ADD RSC /l 0x809 /d "NDEBUG"

 BSC32=bscmake.exe

@@ -73,7 +73,7 @@
 # PROP Ignore_Export_Lib 0

 # PROP Target_Dir ""

 # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c

-# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_CONSOLE" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c

+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_CONSOLE" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c

 # ADD BASE RSC /l 0x809 /d "_DEBUG"

 # ADD RSC /l 0x809 /d "_DEBUG"

 BSC32=bscmake.exe

@@ -104,7 +104,7 @@
 # PROP Ignore_Export_Lib 0

 # PROP Target_Dir ""

 # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_CONSOLE" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c

-# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_CONSOLE" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c

+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"rdr/msvcwarning.h" /D "_CONSOLE" /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

@@ -145,6 +145,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\ManagedListener.cxx

+# End Source File

+# Begin Source File

+

 SOURCE=.\QueryConnectDialog.cxx

 # End Source File

 # Begin Source File

@@ -185,6 +189,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\ManagedListener.h

+# End Source File

+# Begin Source File

+

 SOURCE=.\QueryConnectDialog.h

 # End Source File

 # Begin Source File

@@ -221,6 +229,10 @@
 # End Source File

 # Begin Source File

 

+SOURCE=.\winvnc.bmp

+# End Source File

+# Begin Source File

+

 SOURCE=.\winvnc.ico

 # End Source File

 # End Group

@@ -234,10 +246,6 @@
 # End Source File

 # Begin Source File

 

-SOURCE=.\vncviewer.jar

-# End Source File

-# Begin Source File

-

 SOURCE=.\winvnc4.exe.manifest

 # End Source File

 # End Target

diff --git a/winvnc/winvnc.rc b/winvnc/winvnc.rc
index 9965f3d..777d24c 100644
--- a/winvnc/winvnc.rc
+++ b/winvnc/winvnc.rc
@@ -13,89 +13,6 @@
 #undef APSTUDIO_READONLY_SYMBOLS
 
 /////////////////////////////////////////////////////////////////////////////
-// English (U.S.) resources
-
-#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
-#ifdef _WIN32
-LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
-#pragma code_page(1252)
-#endif //_WIN32
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Dialog
-//
-
-IDD_CONTROL_PANEL DIALOG DISCARDABLE  0, 0, 267, 238
-STYLE DS_MODALFRAME | DS_CENTER | DS_CONTEXTHELP | WS_VISIBLE | WS_CAPTION | 
-    WS_SYSMENU
-CAPTION "Control Panel"
-FONT 8, "MS Sans Serif"
-BEGIN
-    CONTROL         "List1",IDC_LIST_CONNECTIONS,"SysListView32",LVS_REPORT | 
-                    LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOSORTHEADER | 
-                    WS_BORDER | WS_TABSTOP,7,25,253,76
-    LTEXT           "Authorised clients list",IDC_STATIC_KLIENTS_LIST,87,7,
-                    74,11,SS_CENTERIMAGE
-    GROUPBOX        "Control of selected clients",
-                    IDC_STATIC_SELECTED_KLIENTS,7,108,124,103
-    PUSHBUTTON      "View-only",IDC_VIEW_ONLY,13,121,111,14
-    PUSHBUTTON      "Full control ",IDC_FULL_CONTROL,13,145,112,14
-    PUSHBUTTON      "Stop updating",IDC_STOP_UPDATE,13,167,111,14
-    PUSHBUTTON      "Kill Clients",IDC_KILL_SEL_CLIENT,13,190,111,14
-    PUSHBUTTON      "Properties",IDC_PROPERTIES,144,121,111,14
-    PUSHBUTTON      "Add New Client",IDC_ADD_CLIENT,144,145,111,14
-    PUSHBUTTON      "Kill All Clients",IDC_KILL_ALL,144,167,111,14
-    CONTROL         "Disable New Clients",IDC_DISABLE_CLIENTS,"Button",
-                    BS_AUTOCHECKBOX | WS_TABSTOP,144,191,111,13
-    PUSHBUTTON      "Close",IDCANCEL,144,217,111,14
-END
-
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// DESIGNINFO
-//
-
-#ifdef APSTUDIO_INVOKED
-GUIDELINES DESIGNINFO DISCARDABLE 
-BEGIN
-    IDD_CONTROL_PANEL, DIALOG
-    BEGIN
-        LEFTMARGIN, 7
-        RIGHTMARGIN, 260
-        VERTGUIDE, 13
-        VERTGUIDE, 124
-        VERTGUIDE, 144
-        VERTGUIDE, 255
-        TOPMARGIN, 7
-        BOTTOMMARGIN, 231
-        HORZGUIDE, 121
-        HORZGUIDE, 135
-        HORZGUIDE, 145
-        HORZGUIDE, 159
-        HORZGUIDE, 181
-        HORZGUIDE, 191
-        HORZGUIDE, 204
-        HORZGUIDE, 217
-    END
-END
-#endif    // APSTUDIO_INVOKED
-
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// Icon
-//
-
-// Icon with lowest ID value placed first to ensure application icon
-// remains consistent on all systems.
-IDI_ICON_DISABLE        ICON    DISCARDABLE     "icon_dis.ico"
-#endif    // English (U.S.) resources
-/////////////////////////////////////////////////////////////////////////////
-
-
-/////////////////////////////////////////////////////////////////////////////
 // English (U.K.) resources
 
 #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
@@ -104,105 +21,6 @@
 #pragma code_page(1252)
 #endif //_WIN32
 
-/////////////////////////////////////////////////////////////////////////////
-//
-// Dialog
-//
-
-IDD_ABOUT DIALOG DISCARDABLE  0, 0, 249, 92
-STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION | 
-    WS_SYSMENU
-CAPTION "About VNC Server for Windows"
-FONT 8, "MS Sans Serif"
-BEGIN
-    DEFPUSHBUTTON   "OK",IDOK,195,70,47,15
-    ICON            IDI_ICON,IDC_STATIC,7,7,20,20
-    LTEXT           ">appname<",IDC_DESCRIPTION,40,7,125,18
-    LTEXT           ">version<",IDC_VERSION,165,7,77,18
-    LTEXT           ">buildtime<",IDC_BUILDTIME,40,25,202,15
-    LTEXT           ">copyright<",IDC_COPYRIGHT,40,40,202,15
-    LTEXT           "See http://www.tightvnc.com for more information on TightVNC.",
-                    IDC_STATIC,40,55,202,15
-END
-
-IDD_QUERY_CONNECT DIALOG DISCARDABLE  0, 0, 164, 93
-STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION | 
-    WS_SYSMENU
-CAPTION "VNC Server : Accept Connection?"
-FONT 8, "MS Sans Serif"
-BEGIN
-    DEFPUSHBUTTON   "&Reject",IDCANCEL,105,72,52,14
-    PUSHBUTTON      "&Accept",IDOK,7,72,53,14
-    RTEXT           "User:",IDC_STATIC,7,10,28,15,SS_CENTERIMAGE
-    RTEXT           "Host:",IDC_STATIC,7,30,28,15,SS_CENTERIMAGE
-    CTEXT           "Seconds until automatic reject:",IDC_STATIC,7,50,113,15,
-                    SS_CENTERIMAGE
-    LTEXT           "-",IDC_QUERY_COUNTDOWN,125,50,32,15,SS_CENTERIMAGE
-    LTEXT           "-",IDC_QUERY_USER,40,10,117,15,SS_CENTERIMAGE
-    LTEXT           "-",IDC_QUERY_HOST,40,30,117,15,SS_CENTERIMAGE
-END
-
-IDD_ADD_NEW_CLIENT DIALOG DISCARDABLE  0, 0, 183, 53
-STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_VISIBLE | 
-    WS_CAPTION | WS_SYSMENU
-CAPTION "VNC Server : Add New Client"
-FONT 8, "MS Sans Serif"
-BEGIN
-    EDITTEXT        IDC_HOST,70,10,105,15,ES_AUTOHSCROLL
-    DEFPUSHBUTTON   "OK",IDOK,70,32,50,14
-    PUSHBUTTON      "Cancel",IDCANCEL,125,32,50,14
-    ICON            IDI_ICON,IDC_STATIC,7,10,21,20,SS_REALSIZEIMAGE
-    CONTROL         "Viewer:",IDC_STATIC,"Static",SS_LEFTNOWORDWRAP | 
-                    SS_CENTERIMAGE | WS_GROUP,35,10,30,15
-END
-
-
-/////////////////////////////////////////////////////////////////////////////
-//
-// DESIGNINFO
-//
-
-#ifdef APSTUDIO_INVOKED
-GUIDELINES DESIGNINFO DISCARDABLE 
-BEGIN
-    IDD_QUERY_CONNECT, DIALOG
-    BEGIN
-        LEFTMARGIN, 7
-        RIGHTMARGIN, 157
-        VERTGUIDE, 35
-        VERTGUIDE, 40
-        VERTGUIDE, 60
-        VERTGUIDE, 120
-        VERTGUIDE, 125
-        TOPMARGIN, 7
-        BOTTOMMARGIN, 86
-        HORZGUIDE, 10
-        HORZGUIDE, 25
-        HORZGUIDE, 30
-        HORZGUIDE, 45
-        HORZGUIDE, 50
-        HORZGUIDE, 65
-    END
-
-    IDD_ADD_NEW_CLIENT, DIALOG
-    BEGIN
-        LEFTMARGIN, 7
-        RIGHTMARGIN, 176
-        VERTGUIDE, 35
-        VERTGUIDE, 65
-        VERTGUIDE, 70
-        VERTGUIDE, 120
-        VERTGUIDE, 125
-        VERTGUIDE, 175
-        TOPMARGIN, 7
-        BOTTOMMARGIN, 46
-        HORZGUIDE, 10
-        HORZGUIDE, 25
-    END
-END
-#endif    // APSTUDIO_INVOKED
-
-
 #ifdef APSTUDIO_INVOKED
 /////////////////////////////////////////////////////////////////////////////
 //
@@ -236,8 +54,8 @@
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 4,0,0,26
- PRODUCTVERSION 4,0,0,26
+ FILEVERSION 4,1,1,0
+ PRODUCTVERSION 4,1,1,0
  FILEFLAGSMASK 0x3fL
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -255,14 +73,14 @@
             VALUE "Comments", "\0"
             VALUE "CompanyName", "Constantin Kaplinsky\0"
             VALUE "FileDescription", "TightVNC Server for Win32\0"
-            VALUE "FileVersion", "4.0\0"
-            VALUE "InternalName", "WinVNC 4.0\0"
-            VALUE "LegalCopyright", "Copyright (C) 1998-2004 [many holders]\0"
+            VALUE "FileVersion", "4.1.1\0"
+            VALUE "InternalName", "winvnc\0"
+            VALUE "LegalCopyright", "Copyright (C) 1998-2005 [many holders]\0"
             VALUE "LegalTrademarks", "TightVNC\0"
             VALUE "OriginalFilename", "winvnc4.exe\0"
             VALUE "PrivateBuild", "\0"
-            VALUE "ProductName", "TightVNC Server 4.0\0"
-            VALUE "ProductVersion", "4.0\0"
+            VALUE "ProductName", "TightVNC Server\0"
+            VALUE "ProductVersion", "4.1.1\0"
             VALUE "SpecialBuild", "\0"
         END
     END
@@ -284,6 +102,7 @@
 // remains consistent on all systems.
 IDI_ICON                ICON    DISCARDABLE     "winvnc.ico"
 IDI_CONNECTED           ICON    DISCARDABLE     "connected.ico"
+IDI_ICON_DISABLE        ICON    DISCARDABLE     "icon_dis.ico"
 IDI_CONNECTED_DISABLE   ICON    DISCARDABLE     "connecte.ico"
 
 /////////////////////////////////////////////////////////////////////////////
@@ -298,11 +117,11 @@
         MENUITEM "Control &Panel",              ID_CONTR0L_PANEL
         MENUITEM SEPARATOR
         MENUITEM "&Options...",                 ID_OPTIONS
-        MENUITEM "Add &New Client",             ID_CONNECT
+        MENUITEM "Add &New Client...",          ID_CONNECT
         MENUITEM "&Disconnect Clients",         ID_DISCONNECT
-        MENUITEM "Disable New &Clients",        ID_DISABLE_NEW_CLIENTS
+        MENUITEM "D&isable New Clients",        ID_DISABLE_NEW_CLIENTS
         MENUITEM SEPARATOR
-        MENUITEM "Close &VNC Server",           ID_CLOSE
+        MENUITEM "&Close VNC Server",           ID_CLOSE
         MENUITEM "&About...",                   ID_ABOUT
     END
 END
@@ -310,6 +129,84 @@
 
 /////////////////////////////////////////////////////////////////////////////
 //
+// Dialog
+//
+
+IDD_ABOUT DIALOG DISCARDABLE  0, 0, 249, 92
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "About VNC Server for Windows"
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON   "OK",IDOK,195,70,47,15
+    CONTROL         109,IDC_STATIC,"Static",SS_BITMAP,5,10,33,31
+    LTEXT           ">appname<",IDC_DESCRIPTION,45,10,125,15
+    LTEXT           ">version<",IDC_VERSION,170,10,72,15
+    LTEXT           ">buildtime<",IDC_BUILDTIME,45,25,202,15
+    LTEXT           ">copyright<",IDC_COPYRIGHT,45,40,202,15
+    LTEXT           "See http://www.tightvnc.com for more information on VNC.",
+                    IDC_STATIC,45,55,202,15
+END
+
+IDD_QUERY_CONNECT DIALOG DISCARDABLE  0, 0, 164, 93
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "VNC Server : Accept Connection?"
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON   "&Reject",IDCANCEL,105,72,52,14
+    PUSHBUTTON      "&Accept",IDOK,7,72,53,14
+    RTEXT           "User:",IDC_STATIC,7,10,28,15,SS_CENTERIMAGE
+    RTEXT           "Host:",IDC_STATIC,7,30,28,15,SS_CENTERIMAGE
+    CTEXT           "Seconds until automatic reject:",IDC_STATIC,7,50,113,15,
+                    SS_CENTERIMAGE
+    LTEXT           "-",IDC_QUERY_COUNTDOWN,125,50,32,15,SS_CENTERIMAGE
+    LTEXT           "-",IDC_QUERY_USER,40,10,117,15,SS_CENTERIMAGE
+    LTEXT           "-",IDC_QUERY_HOST,40,30,117,15,SS_CENTERIMAGE
+END
+
+IDD_ADD_NEW_CLIENT DIALOG DISCARDABLE  0, 0, 177, 52
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_VISIBLE | 
+    WS_CAPTION | WS_SYSMENU
+CAPTION "VNC Server : Add New Client"
+FONT 8, "MS Sans Serif"
+BEGIN
+    EDITTEXT        IDC_HOST,80,10,90,15,ES_AUTOHSCROLL
+    DEFPUSHBUTTON   "OK",IDOK,80,31,40,14
+    PUSHBUTTON      "Cancel",IDCANCEL,125,31,45,14
+    CONTROL         109,IDC_STATIC,"Static",SS_BITMAP | SS_REALSIZEIMAGE,7,
+                    10,33,31
+    RTEXT           "Viewer:",IDC_STATIC,45,10,30,15,SS_CENTERIMAGE
+END
+
+IDD_CONTROL_PANEL DIALOG DISCARDABLE  0, 0, 267, 238
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | DS_CONTEXTHELP | 
+    WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Control Panel"
+FONT 8, "MS Sans Serif"
+BEGIN
+    CONTROL         "List1",IDC_LIST_CONNECTIONS,"SysListView32",LVS_REPORT | 
+                    LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOSORTHEADER | 
+                    WS_BORDER | WS_TABSTOP,7,25,253,76
+    LTEXT           "Authorised clients list",IDC_STATIC_KLIENTS_LIST,87,7,
+                    74,11,SS_CENTERIMAGE
+    GROUPBOX        "Control of selected clients",
+                    IDC_STATIC_SELECTED_KLIENTS,7,108,124,103
+    PUSHBUTTON      "View-only",IDC_VIEW_ONLY,13,121,111,14
+    PUSHBUTTON      "Full control ",IDC_FULL_CONTROL,13,145,112,14
+    PUSHBUTTON      "Stop updating",IDC_STOP_UPDATE,13,167,111,14
+    PUSHBUTTON      "Kill Clients",IDC_KILL_SEL_CLIENT,13,190,111,14
+    PUSHBUTTON      "Properties",IDC_PROPERTIES,144,121,111,14
+    PUSHBUTTON      "Add New Client",IDC_ADD_CLIENT,144,145,111,14
+    PUSHBUTTON      "Kill All Clients",IDC_KILL_ALL,144,167,111,14
+    CONTROL         "Disable New Clients",IDC_DISABLE_CLIENTS,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,144,191,111,13
+    PUSHBUTTON      "Close",IDCANCEL,144,217,111,14
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
 // HTTPFILE
 //
 
@@ -323,6 +220,97 @@
 //
 
 IDR_MANIFEST            24      DISCARDABLE     "winvnc4.exe.manifest"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE 
+BEGIN
+    IDD_ABOUT, DIALOG
+    BEGIN
+        LEFTMARGIN, 5
+        VERTGUIDE, 45
+        VERTGUIDE, 170
+        VERTGUIDE, 195
+        VERTGUIDE, 242
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 85
+        HORZGUIDE, 10
+        HORZGUIDE, 25
+        HORZGUIDE, 40
+        HORZGUIDE, 55
+        HORZGUIDE, 70
+    END
+
+    IDD_QUERY_CONNECT, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 157
+        VERTGUIDE, 35
+        VERTGUIDE, 40
+        VERTGUIDE, 60
+        VERTGUIDE, 120
+        VERTGUIDE, 125
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 86
+        HORZGUIDE, 10
+        HORZGUIDE, 25
+        HORZGUIDE, 30
+        HORZGUIDE, 45
+        HORZGUIDE, 50
+        HORZGUIDE, 65
+    END
+
+    IDD_ADD_NEW_CLIENT, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 170
+        VERTGUIDE, 45
+        VERTGUIDE, 75
+        VERTGUIDE, 80
+        VERTGUIDE, 120
+        VERTGUIDE, 125
+        VERTGUIDE, 170
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 45
+        HORZGUIDE, 10
+        HORZGUIDE, 25
+        HORZGUIDE, 30
+        HORZGUIDE, 45
+    END
+
+    IDD_CONTROL_PANEL, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 260
+        VERTGUIDE, 13
+        VERTGUIDE, 124
+        VERTGUIDE, 144
+        VERTGUIDE, 255
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 231
+        HORZGUIDE, 121
+        HORZGUIDE, 135
+        HORZGUIDE, 145
+        HORZGUIDE, 159
+        HORZGUIDE, 181
+        HORZGUIDE, 191
+        HORZGUIDE, 204
+        HORZGUIDE, 217
+    END
+END
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+IDB_BITMAP              BITMAP  DISCARDABLE     "winvnc.bmp"
 #endif    // English (U.K.) resources
 /////////////////////////////////////////////////////////////////////////////
 
diff --git a/wm_hooks/msvcwarning.h b/wm_hooks/msvcwarning.h
deleted file mode 100644
index d0c98c3..0000000
--- a/wm_hooks/msvcwarning.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/* 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/wm_hooks/wm_hooks.cxx b/wm_hooks/wm_hooks.cxx
index 6923db7..fd01159 100644
--- a/wm_hooks/wm_hooks.cxx
+++ b/wm_hooks/wm_hooks.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -20,7 +20,6 @@
 //
 // Window Message Hooks Dynamic Link library
 
-#define _WIN32_WINNT 0x0400
 #include <tchar.h>
 
 #include <wm_hooks/wm_hooks.h>
@@ -187,6 +186,10 @@
     }
     break;
 
+  case WM_WINDOWPOSCHANGED:
+    NotifyWindow(wnd, msg);
+    break;
+
 	case WM_PAINT:
     // *** could improve this
     NotifyWindowClientArea(wnd, msg);
@@ -329,7 +332,7 @@
   hook_target = thread;
 
   hook_CallWndProc = SetWindowsHookEx(WH_CALLWNDPROC, HookCallWndProc, dll_instance, thread);
-  //hook_CallWndProcRet = SetWindowsHookEx(WH_CALLWNDPROCRET, HookCallWndProcRet, dll_instance, thread);
+  hook_CallWndProcRet = SetWindowsHookEx(WH_CALLWNDPROCRET, HookCallWndProcRet, dll_instance, thread);
   hook_GetMessage = SetWindowsHookEx(WH_GETMESSAGE, HookGetMessage, dll_instance, thread);
   hook_DialogMessage = SetWindowsHookEx(WH_SYSMSGFILTER, HookDialogMessage, dll_instance, thread);
 
@@ -427,7 +430,7 @@
   return success;
 }
 #else
-#pragma message("WARNING: low-level mouse and keyboard hooks not supported")
+#pragma message("  NOTE: low-level mouse and keyboard hooks not supported")
 #endif
 
 // - WM_Hooks_EnableRealInputs
@@ -458,5 +461,5 @@
 
 BOOL WM_Hooks_EnableCursorShape(BOOL enable) {
   enable_cursor_shape = enable;
-  return FALSE;
+  return TRUE;
 }
diff --git a/wm_hooks/wm_hooks.dsp b/wm_hooks/wm_hooks.dsp
index 3eaa9d2..54ff770 100644
--- a/wm_hooks/wm_hooks.dsp
+++ b/wm_hooks/wm_hooks.dsp
@@ -44,7 +44,7 @@
 # PROP Ignore_Export_Lib 0

 # PROP Target_Dir ""

 # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WM_HOOKS_EXPORTS" /YX /FD /c

-# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /D "NDEBUG" /D "_WINDOWS" /D "_USRDLL" /D "WM_HOOKS_EXPORTS" /D "WIN32" /D "_MBCS" /YX /FD /c

+# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /FI"rdr/msvcwarning.h" /D "NDEBUG" /D "_WINDOWS" /D "_USRDLL" /D "WM_HOOKS_EXPORTS" /D "WIN32" /D "_MBCS" /YX /FD /c

 # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32

 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32

 # ADD BASE RSC /l 0x809 /d "NDEBUG"

@@ -70,7 +70,7 @@
 # PROP Ignore_Export_Lib 0

 # PROP Target_Dir ""

 # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WM_HOOKS_EXPORTS" /YX /FD /GZ /c

-# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "WM_HOOKS_EXPORTS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c

+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "WM_HOOKS_EXPORTS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c

 # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32

 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32

 # ADD BASE RSC /l 0x809 /d "_DEBUG"

@@ -98,7 +98,7 @@
 # PROP Ignore_Export_Lib 0

 # PROP Target_Dir ""

 # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "WM_HOOKS_EXPORTS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c

-# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_WINDOWS" /D "_USRDLL" /D "WM_HOOKS_EXPORTS" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c

+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"rdr/msvcwarning.h" /D "_WINDOWS" /D "_USRDLL" /D "WM_HOOKS_EXPORTS" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c

 # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32

 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32

 # ADD BASE RSC /l 0x809 /d "_DEBUG"

diff --git a/wm_hooks/wm_hooks.h b/wm_hooks/wm_hooks.h
index afff4be..f65412e 100644
--- a/wm_hooks/wm_hooks.h
+++ b/wm_hooks/wm_hooks.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -27,7 +27,6 @@
 #ifndef __WM_HOOKS_H__
 #define __WM_HOOKS_H__
 
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 
 #define DLLEXPORT __declspec(dllexport)
diff --git a/wm_hooks/wm_hooks.rc b/wm_hooks/wm_hooks.rc
index e627314..3f171d2 100644
--- a/wm_hooks/wm_hooks.rc
+++ b/wm_hooks/wm_hooks.rc
@@ -54,8 +54,8 @@
 //
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 4,0,0,26
- PRODUCTVERSION 4,0,0,26
+ FILEVERSION 4,1,0,0
+ PRODUCTVERSION 4,1,0,0
  FILEFLAGSMASK 0x3fL
 #ifdef _DEBUG
  FILEFLAGS 0x1L
@@ -72,15 +72,15 @@
         BEGIN
             VALUE "Comments", "\0"
             VALUE "CompanyName", "Constantin Kaplinsky\0"
-            VALUE "FileDescription", "TightVNC Server for Win32 Hooking DLL\0"
-            VALUE "FileVersion", "4.0\0"
-            VALUE "InternalName", "WMHooks 4.0\0"
-            VALUE "LegalCopyright", "Copyright (C) 1998-2004 [many holders]\0"
+            VALUE "FileDescription", "TightVNC Server Hooking DLL for Win32\0"
+            VALUE "FileVersion", "4.1\0"
+            VALUE "InternalName", "\0"
+            VALUE "LegalCopyright", "Copyright (C) 1998-2005 [many holders]\0"
             VALUE "LegalTrademarks", "TightVNC\0"
             VALUE "OriginalFilename", "wm_hooks.dll\0"
             VALUE "PrivateBuild", "\0"
-            VALUE "ProductName", "TightVNC Server 4.0\0"
-            VALUE "ProductVersion", "4.0\0"
+            VALUE "ProductName", "TightVNC Server\0"
+            VALUE "ProductVersion", "4.1\0"
             VALUE "SpecialBuild", "\0"
         END
     END
diff --git a/x0vncserver/Image.cxx b/x0vncserver/Image.cxx
index ef72378..d78a98e 100644
--- a/x0vncserver/Image.cxx
+++ b/x0vncserver/Image.cxx
@@ -92,6 +92,8 @@
   // fprintf(stderr, "~Image() called\n");
 
   imageCleanup.images.remove(this);
+
+  // XDestroyImage will free xim->data if necessary
   if (xim != NULL)
     XDestroyImage(xim);
 }
diff --git a/x0vncserver/Makefile.in b/x0vncserver/Makefile.in
index b6bd0ba..e69b89a 100644
--- a/x0vncserver/Makefile.in
+++ b/x0vncserver/Makefile.in
@@ -1,18 +1,21 @@
 
 SRCS = Image.cxx TimeMillis.cxx PollingScheduler.cxx PollingManager.cxx \
-       x0vncserver.cxx
+       x0vncserver.cxx ../vncconfig_unix/QueryConnectDialog.cxx
 
 OBJS = $(SRCS:.cxx=.o)
 
 program = x0vncserver
 
-DEP_LIBS = ../rfb/librfb.a ../network/libnetwork.a ../rdr/librdr.a
+DEP_LIBS = ../rfb/librfb.a \
+           ../network/libnetwork.a \
+           ../rdr/librdr.a \
+	   ../tx/libtx.a
 
 EXTRA_LIBS = @ZLIB_LIB@ @JPEG_LIB@ @INET_LIB@ @X_PRE_LIBS@ @X_LIBS@ \
              @XTEST_LIB@ -lXext -lX11 @X_EXTRA_LIBS@
 
 # X_CFLAGS are really CPPFLAGS
-DIR_CPPFLAGS = -I$(top_srcdir) \
+DIR_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/tx -I$(top_srcdir)/vncconfig_unix \
   @XTEST_DEFINE@ @READDISPLAY_DEFINE@ @MITSHM_DEFINE@ @X_CFLAGS@
 
 all:: $(program)
diff --git a/x0vncserver/x0vncserver.cxx b/x0vncserver/x0vncserver.cxx
index ac65c0b..4ee44e0 100644
--- a/x0vncserver/x0vncserver.cxx
+++ b/x0vncserver/x0vncserver.cxx
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- * Copyright (C) 2004-2005 Constantin Kaplinsky.  All Rights Reserved.
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright (C) 2004-2006 Constantin Kaplinsky.  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
@@ -30,8 +30,11 @@
 #include <rfb/VNCServerST.h>
 #include <rfb/Configuration.h>
 #include <rfb/SSecurityFactoryStandard.h>
-
+#include <rfb/Timer.h>
 #include <network/TcpSocket.h>
+#include <tx/TXWindow.h>
+
+#include <vncconfig_unix/QueryConnectDialog.h>
 
 #include <signal.h>
 #include <X11/X.h>
@@ -65,8 +68,11 @@
                          "IRIX or Solaris", true);
 StringParameter displayname("display", "The X display", "");
 IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",5900);
+IntParameter queryConnectTimeout("QueryConnectTimeout",
+                                 "Number of seconds to show the Accept Connection dialog before "
+                                 "rejecting the connection",
+                                 10);
 StringParameter hostsFile("HostsFile", "File with IP access control rules", "");
-VncAuthPasswdFileParameter vncAuthPasswdFile;
 
 static void CleanupSignalHandler(int sig)
 {
@@ -77,12 +83,56 @@
 }
 
 
-class XDesktop : public SDesktop, public rfb::ColourMap
+class QueryConnHandler : public VNCServerST::QueryConnectionHandler,
+                         public QueryResultCallback {
+public:
+  QueryConnHandler(Display* dpy, VNCServerST* vs)
+    : display(dpy), server(vs), queryConnectDialog(0), queryConnectSock(0) {}
+  ~QueryConnHandler() { delete queryConnectDialog; }
+
+  // -=- VNCServerST::QueryConnectionHandler interface
+  virtual VNCServerST::queryResult queryConnection(network::Socket* sock,
+                                                   const char* userName,
+                                                   char** reason) {
+    if (queryConnectSock) {
+      *reason = strDup("Another connection is currently being queried.");
+      return VNCServerST::REJECT;
+    }
+    if (!userName) userName = "(anonymous)";
+    queryConnectSock = sock;
+    CharArray address(sock->getPeerAddress());
+    delete queryConnectDialog;
+    queryConnectDialog = new QueryConnectDialog(display, address.buf,
+                                                userName, queryConnectTimeout,
+                                                this);
+    queryConnectDialog->map();
+    return VNCServerST::PENDING;
+  }
+
+  // -=- QueryResultCallback interface
+  virtual void queryApproved() {
+    server->approveConnection(queryConnectSock, true, 0);
+    queryConnectSock = 0;
+  }
+  virtual void queryRejected() {
+    server->approveConnection(queryConnectSock, false,
+                              "Connection rejected by local user");
+    queryConnectSock = 0;
+  }
+private:
+  Display* display;
+  VNCServerST* server;
+  QueryConnectDialog* queryConnectDialog;
+  network::Socket* queryConnectSock;
+};
+
+
+class XDesktop : public SDesktop, public ColourMap
 {
 public:
   XDesktop(Display* dpy_)
-    : dpy(dpy_), pb(0), server(0), oldButtonMask(0), haveXtest(false),
-      maxButtons(0)
+    : dpy(dpy_), pb(0), server(0), image(0), pollmgr(0),
+      oldButtonMask(0), haveXtest(false), maxButtons(0), running(false)
   {
 #ifdef HAVE_XTEST
     int xtestEventBase;
@@ -102,6 +152,15 @@
     }
 #endif
 
+  }
+  virtual ~XDesktop() {
+    stop();
+  }
+
+  // -=- SDesktop interface
+
+  virtual void start(VNCServer* vs) {
+
     // Determine actual number of buttons of the X pointer device.
     unsigned char btnMap[8];
     int numButtons = XGetPointerMapping(dpy, btnMap, 8);
@@ -117,6 +176,7 @@
     image->get(DefaultRootWindow(dpy));
 
     pollmgr = new PollingManager(dpy, image, &factory);
+    pollmgr->setVNCServer(vs);
 
     pf.bpp = image->xim->bits_per_pixel;
     pf.depth = image->xim->depth;
@@ -131,25 +191,34 @@
 
     pb = new FullFramePixelBuffer(pf, dpyWidth, dpyHeight,
                                   (rdr::U8*)image->xim->data, this);
-  }
-  virtual ~XDesktop() {
-    delete pb;
-    delete pollmgr;
+    server = vs;
+    server->setPixelBuffer(pb);
+
+    running = true;
   }
 
-  void setVNCServer(VNCServer* s) {
-    server = s;
-    pollmgr->setVNCServer(s);
-    server->setPixelBuffer(pb);
+  virtual void stop() {
+    running = false;
+
+    delete pb;
+    delete pollmgr;
+    delete image;
+
+    pb = 0;
+    pollmgr = 0;
+    image = 0;
+  }
+
+  inline bool isRunning() {
+    return running;
   }
 
   inline void poll() {
-    pollmgr->poll();
+    if (pollmgr)
+      pollmgr->poll();
   }
 
-  // -=- SDesktop interface
-
-  virtual void pointerEvent(const Point& pos, rdr::U8 buttonMask) {
+  virtual void pointerEvent(const Point& pos, int buttonMask) {
     pollmgr->setPointerPos(pos);
 #ifdef HAVE_XTEST
     if (!haveXtest) return;
@@ -185,7 +254,7 @@
     return Point(pb->width(), pb->height());
   }
 
-  // rfb::ColourMap callbacks
+  // -=- ColourMap callbacks
   virtual void lookup(int index, int* r, int* g, int* b) {
     XColor xc;
     xc.pixel = index;
@@ -209,6 +278,7 @@
   int oldButtonMask;
   bool haveXtest;
   int maxButtons;
+  bool running;
 };
 
 
@@ -311,7 +381,6 @@
 
 };
 
-
 char* programName;
 
 static void usage()
@@ -365,11 +434,12 @@
   signal(SIGTERM, CleanupSignalHandler);
 
   try {
+    TXWindow::init(dpy,"x0vncserver");
     XDesktop desktop(dpy);
     VNCServerST server("x0vncserver", &desktop);
-    desktop.setVNCServer(&server);
+    QueryConnHandler qcHandler(dpy, &server);
+    server.setQueryConnectionHandler(&qcHandler);
 
-    TcpSocket::initTcpSockets();
     TcpListener listener((int)rfbport);
     vlog.info("Listening on port %d", (int)rfbport);
 
@@ -379,21 +449,27 @@
 
     PollingScheduler sched((int)pollingCycle, (int)maxProcessorUsage);
 
-    fd_set rfds;
-    struct timeval tv;
-
     while (true) {
+      struct timeval tv;
+      fd_set rfds;
+      std::list<Socket*> sockets;
+      std::list<Socket*>::iterator i;
+
+      // Process any incoming X events
+      TXWindow::handleXEvents(dpy);
 
       FD_ZERO(&rfds);
       FD_SET(listener.getFd(), &rfds);
-
-      std::list<Socket*> sockets;
       server.getSockets(&sockets);
-      std::list<Socket*>::iterator i;
       int clients_connected = 0;
       for (i = sockets.begin(); i != sockets.end(); i++) {
-        FD_SET((*i)->getFd(), &rfds);
-        clients_connected++;
+        if ((*i)->isShutdown()) {
+          server.removeSocket(*i);
+          delete (*i);
+        } else {
+          FD_SET((*i)->getFd(), &rfds);
+          clients_connected++;
+        }
       }
 
       if (clients_connected) {
@@ -411,6 +487,7 @@
       }
       tv.tv_sec = 0;
 
+      // Do the wait...
       sched.sleepStarted();
       int n = select(FD_SETSIZE, &rfds, 0, 0, &tv);
       sched.sleepFinished();
@@ -424,30 +501,33 @@
         }
       }
 
+      // Accept new VNC connections
       if (FD_ISSET(listener.getFd(), &rfds)) {
         Socket* sock = listener.accept();
         if (sock) {
-          server.addClient(sock);
+          server.addSocket(sock);
         } else {
           vlog.status("Client connection rejected");
         }
       }
 
+      Timer::checkTimeouts();
+      server.checkTimeouts();
+
+      // Client list could have been changed.
       server.getSockets(&sockets);
 
       // Nothing more to do if there are no client connections.
       if (sockets.empty())
         continue;
 
+      // Process events on existing VNC connections
       for (i = sockets.begin(); i != sockets.end(); i++) {
-        if (FD_ISSET((*i)->getFd(), &rfds)) {
+        if (FD_ISSET((*i)->getFd(), &rfds))
           server.processSocketEvent(*i);
-        }
       }
 
-      server.checkTimeouts();
-
-      if (sched.goodTimeToPoll()) {
+      if (desktop.isRunning() && sched.goodTimeToPoll()) {
         sched.newPass();
         desktop.poll();
       }
diff --git a/x0vncserver/x0vncserver.man b/x0vncserver/x0vncserver.man
index d9f43b5..da9ba94 100644
--- a/x0vncserver/x0vncserver.man
+++ b/x0vncserver/x0vncserver.man
@@ -1,4 +1,4 @@
-.TH x0vncserver 1 "30 December 2004" "TightVNC" "Virtual Network Computing"
+.TH x0vncserver 1 "17 Apr 2006" "TightVNC" "Virtual Network Computing"
 .SH NAME
 x0vncserver \- VNC server which continuously polls an X display
 .SH SYNOPSIS
diff --git a/xc.patch b/xc.patch
index fc0f41f..a5927b9 100644
--- a/xc.patch
+++ b/xc.patch
@@ -34,7 +34,7 @@
 + XVNCOBJS = $(XVNCDDXDIR)/stubs.o $(XVNCDDXDIR)/miinitext.o
 + XVNCSYSLIBS = $(FONTLIBS) $(SYSLIBS)
 + ServerTarget(Xvnc,$(XVNCDIRS),$(XVNCOBJS), \
-+ 	$(XVNCLIBS) $(LOADABLEEXTS) $(LIBCWRAPPER),$(XVNCSYSLIBS))
++ 	$(LIBCWRAPPER) $(XVNCLIBS) $(LOADABLEEXTS),$(XVNCSYSLIBS))
   
   
   #if defined(Xsun24Server) && Xsun24Server
diff --git a/xc/config/cf/vnc.def b/xc/config/cf/vnc.def
index e35e194..c933f4d 100644
--- a/xc/config/cf/vnc.def
+++ b/xc/config/cf/vnc.def
@@ -17,6 +17,7 @@
 #define HasGcc3 YES
 #endif
 
+#define HasFreetype2 NO
 #define BuildVNCExt YES
 #define VNCExtDefines -DVNCEXT
 #define SiteExtensionDefines VNCExtDefines
diff --git a/xc/programs/Xserver/Xvnc.man b/xc/programs/Xserver/Xvnc.man
index 0e9f44f..128bc93 100644
--- a/xc/programs/Xserver/Xvnc.man
+++ b/xc/programs/Xserver/Xvnc.man
@@ -1,4 +1,4 @@
-.TH Xvnc 1 "30 December 2004" "TightVNC" "Virtual Network Computing"
+.TH Xvnc 1 "17 Apr 2006" "TightVNC" "Virtual Network Computing"
 .SH NAME
 Xvnc \- the X VNC server 
 .SH SYNOPSIS
@@ -182,6 +182,17 @@
 (default is 0, which means that idle connections will never be dropped).
 
 .TP
+.B \-QueryConnect
+Prompts the user of the desktop to explicitly accept or reject incoming
+connections.  This is most useful when using the vnc.so module or
+\fBx0vncserver\fP(1) program to access an existing X desktop via VNC.
+
+The \fBvncconfig\fP(1) program must be running on the desktop in order for
+QueryConnect to be supported by the \fBvnc.so\fP(1) module or
+\fBXvnc\fP(1) program.  The \fBx0vncserver\fP(1) program does not require
+\fBvncconfig\fP(1) to be running.
+
+.TP
 .B \-localhost
 Only allow connections from the same machine. Useful if you use SSH and want to
 stop non-SSH connections from any other hosts. See the guide to using VNC with
@@ -195,6 +206,20 @@
 specific source file if you know the name of its "LogWriter".  Default is
 \fB*:stderr:30\fP.
 
+.TP
+.B \-RemapKeys \fImapping
+Sets up a keyboard mapping.
+.I mapping
+is a comma-separated string of character mappings, each of the form
+.IR char -> char ,
+or
+.IR char <> char ,
+where
+.I char
+is a hexadecimal keysym. For example, to exchange the " and @ symbols you would specify the following:
+.IP "" 10
+RemapKeys=0x22<>0x40
+
 .SH USAGE WITH INETD
 By configuring the \fBinetd\fP(1) service appropriately, Xvnc can be launched
 on demand when a connection comes in, rather than having to be started
diff --git a/xc/programs/Xserver/vnc/RegionHelper.h b/xc/programs/Xserver/vnc/RegionHelper.h
index 786622d..61dc89f 100644
--- a/xc/programs/Xserver/vnc/RegionHelper.h
+++ b/xc/programs/Xserver/vnc/RegionHelper.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -22,6 +22,13 @@
 // automatically freeing them in the destructor.  It also fixes a problem with
 // REGION_INIT when given an empty rectangle.
 
+// REGION_NULL was introduced in the Xorg tree as the way to initialise an
+// empty region.  If it's not already defined do it the old way.  Note that the
+// old way causes a segfault in the new tree...
+#ifndef REGION_NULL
+#define REGION_NULL(pScreen,pReg) REGION_INIT(pScreen,pReg,NullBox,0)
+#endif
+
 class RegionHelper {
 public:
 
@@ -54,7 +61,7 @@
 
   void init(BoxPtr rect, int size) {
     reg = &regRec;
-    if (!rect || (rect->x2 == rect->x1 || rect->y2 == rect->y1)) {
+    if (!rect || (rect && (rect->x2 == rect->x1 || rect->y2 == rect->y1))) {
       REGION_NULL(pScreen, reg);
     } else {
       REGION_INIT(pScreen, reg, rect, size);
diff --git a/xc/programs/Xserver/vnc/XserverDesktop.cc b/xc/programs/Xserver/vnc/XserverDesktop.cc
index f53caec..9e5ea0f 100644
--- a/xc/programs/Xserver/vnc/XserverDesktop.cc
+++ b/xc/programs/Xserver/vnc/XserverDesktop.cc
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -67,6 +67,11 @@
 rfb::BoolParameter alwaysSetDeferUpdateTimer("AlwaysSetDeferUpdateTimer",
                   "Always reset the defer update timer on every change",false);
 
+IntParameter queryConnectTimeout("QueryConnectTimeout",
+                                 "Number of seconds to show the Accept Connection dialog before "
+                                 "rejecting the connection",
+                                 10);
+
 static KeyCode KeysymToKeycode(KeySymsPtr keymap, KeySym ks, int* col);
 
 static rdr::U8 reverseBits[] = {
@@ -95,12 +100,14 @@
 };
 
 
-class MyHTTPServer : public rfb::HTTPServer {
+class FileHTTPServer : public rfb::HTTPServer {
 public:
-  MyHTTPServer(XserverDesktop* d) : desktop(d) {}
-  virtual ~MyHTTPServer() {}
+  FileHTTPServer(XserverDesktop* d) : desktop(d) {}
+  virtual ~FileHTTPServer() {}
 
-  virtual rdr::InStream* getFile(const char* name, const char** contentType) {
+  virtual rdr::InStream* getFile(const char* name, const char** contentType,
+                                 int* contentLength, time_t* lastModified)
+  {
     if (name[0] != '/' || strstr(name, "..") != 0) {
       vlog.info("http request was for invalid file name");
       return 0;
@@ -113,12 +120,17 @@
     sprintf(fname.buf, "%s%s", httpDirStr.buf, name);
     int fd = open(fname.buf, O_RDONLY);
     if (fd < 0) return 0;
-
     rdr::InStream* is = new rdr::FdInStream(fd, -1, 0, true);
     *contentType = guessContentType(name, *contentType);
     if (strlen(name) > 4 && strcasecmp(&name[strlen(name)-4], ".vnc") == 0) {
       is = new rdr::SubstitutingInStream(is, desktop, 20);
       *contentType = "text/html";
+    } else {
+      struct stat st;
+      if (fstat(fd, &st) == 0) {
+        *contentLength = st.st_size;
+        *lastModified = st.st_mtime;
+      }
     }
     return is;
   }
@@ -136,8 +148,8 @@
     listener(listener_), httpListener(httpListener_),
     cmap(0), deferredUpdateTimerSet(false),
     grabbing(false), ignoreHooks_(false), directFbptr(fbptr != 0),
-    oldButtonMask(0), cursorX(0), cursorY(0),
-    oldCursorX(0), oldCursorY(0)
+    oldButtonMask(0),
+    queryConnectId(0)
 {
   int i;
   format.depth = pScreen->rootDepth;
@@ -186,9 +198,10 @@
 
   server = new VNCServerST(name, this);
   server->setPixelBuffer(this);
+  server->setQueryConnectionHandler(this);
 
   if (httpListener)
-    httpServer = new MyHTTPServer(this);
+    httpServer = new FileHTTPServer(this);
 }
 
 XserverDesktop::~XserverDesktop()
@@ -259,6 +272,24 @@
   return 0;
 }
 
+rfb::VNCServerST::queryResult
+XserverDesktop::queryConnection(network::Socket* sock,
+                                const char* userName,
+                                char** reason) {
+  if (queryConnectId) {
+    *reason = strDup("Another connection is currently being queried.");
+    return rfb::VNCServerST::REJECT;
+  }
+  queryConnectAddress.replaceBuf(sock->getPeerAddress());
+  if (!userName)
+    userName = "(anonymous)";
+  queryConnectUsername.replaceBuf(strDup(userName));
+  queryConnectId = sock;
+  vncQueryConnect(this, sock);
+  return rfb::VNCServerST::PENDING;
+}
+
+
 void XserverDesktop::setColormap(ColormapPtr cmap_)
 {
   if (cmap != cmap_) {
@@ -372,7 +403,7 @@
     }
 
     server->setCursor(cursor->bits->width, cursor->bits->height,
-                      cursor->bits->xhot, cursor->bits->yhot,
+                      Point(cursor->bits->xhot, cursor->bits->yhot),
                       cursorData, cursorMask);
     server->tryUpdate();
     delete [] cursorData;
@@ -414,6 +445,20 @@
   return 0;
 }
 
+void XserverDesktop::deferUpdate()
+{
+  if (deferUpdateTime != 0) {
+    if (!deferredUpdateTimerSet || alwaysSetDeferUpdateTimer) {
+      deferredUpdateTimerSet = true;
+      deferredUpdateTimer = TimerSet(deferredUpdateTimer, 0,
+                                     deferUpdateTime,
+                                     deferredUpdateTimerCallback, this);
+    }
+  } else {
+    server->tryUpdate();
+  }
+}
+
 void XserverDesktop::add_changed(RegionPtr reg)
 {
   if (ignoreHooks_) return;
@@ -424,12 +469,7 @@
                                      REGION_NUM_RECTS(reg),
                                      (ShortRect*)REGION_RECTS(reg));
     server->add_changed(rfbReg);
-    if (!deferredUpdateTimerSet || alwaysSetDeferUpdateTimer) {
-      deferredUpdateTimer = TimerSet(deferredUpdateTimer, 0,
-                                     deferUpdateTime,
-                                     deferredUpdateTimerCallback, this);
-      deferredUpdateTimerSet = true;
-    }
+    deferUpdate();
   } catch (rdr::Exception& e) {
     vlog.error("XserverDesktop::add_changed: %s",e.str());
   }
@@ -445,12 +485,7 @@
                                      REGION_NUM_RECTS(dst),
                                      (ShortRect*)REGION_RECTS(dst));
     server->add_copied(rfbReg, rfb::Point(dx, dy));
-    if (!deferredUpdateTimerSet || alwaysSetDeferUpdateTimer) {
-      deferredUpdateTimer = TimerSet(deferredUpdateTimer, 0,
-                                     deferUpdateTime,
-                                     deferredUpdateTimerCallback, this);
-      deferredUpdateTimerSet = true;
-    }
+    deferUpdate();
   } catch (rdr::Exception& e) {
     vlog.error("XserverDesktop::add_copied: %s",e.str());
   }
@@ -458,11 +493,10 @@
 
 void XserverDesktop::positionCursor()
 {
-  if (cursorX != oldCursorX || cursorY != oldCursorY) {
-    oldCursorX = cursorX;
-    oldCursorY = cursorY;
-    (*pScreen->SetCursorPosition) (pScreen, cursorX, cursorY, FALSE);
-    server->setCursorPos(cursorX, cursorY);
+  if (!cursorPos.equals(oldCursorPos)) {
+    oldCursorPos = cursorPos;
+    (*pScreen->SetCursorPosition) (pScreen, cursorPos.x, cursorPos.y, FALSE);
+    server->setCursorPos(cursorPos);
     server->tryUpdate();
   }
 }
@@ -474,10 +508,9 @@
     if (screenWithCursor == pScreen) {
       int x, y;
       GetSpritePosition(&x, &y);
-      if (x != cursorX || y != cursorY) {
-        cursorX = oldCursorX = x;
-        cursorY = oldCursorY = y;
-        server->setCursorPos(x, y);
+      if (x != cursorPos.x || y != cursorPos.y) {
+        cursorPos = oldCursorPos = Point(x, y);
+        server->setCursorPos(cursorPos);
         server->tryUpdate();
       }
     }
@@ -491,12 +524,27 @@
     server->getSockets(&sockets);
     std::list<Socket*>::iterator i;
     for (i = sockets.begin(); i != sockets.end(); i++) {
-      FD_SET((*i)->getFd(), fds);
+      int fd = (*i)->getFd();
+      if ((*i)->isShutdown()) {
+        vlog.debug("client gone, sock %d",fd);
+        server->removeSocket(*i);
+        vncClientGone(fd);
+        delete (*i);
+      } else {
+        FD_SET(fd, fds);
+      }
     }
     if (httpServer) {
       httpServer->getSockets(&sockets);
       for (i = sockets.begin(); i != sockets.end(); i++) {
-        FD_SET((*i)->getFd(), fds);
+        int fd = (*i)->getFd();
+        if ((*i)->isShutdown()) {
+          vlog.debug("http client gone, sock %d",fd);
+          httpServer->removeSocket(*i);
+          delete (*i);
+        } else {
+          FD_SET(fd, fds);
+        }
       }
     }
   } catch (rdr::Exception& e) {
@@ -517,7 +565,7 @@
         if (FD_ISSET(listener->getFd(), fds)) {
           FD_CLR(listener->getFd(), fds);
           Socket* sock = listener->accept();
-          server->addClient(sock);
+          server->addSocket(sock);
           vlog.debug("new client, sock %d",sock->getFd());
         }
       }
@@ -526,7 +574,7 @@
         if (FD_ISSET(httpListener->getFd(), fds)) {
           FD_CLR(httpListener->getFd(), fds);
           Socket* sock = httpListener->accept();
-          httpServer->addClient(sock);
+          httpServer->addSocket(sock);
           vlog.debug("new http client, sock %d",sock->getFd());
         }
       }
@@ -538,10 +586,7 @@
         int fd = (*i)->getFd();
         if (FD_ISSET(fd, fds)) {
           FD_CLR(fd, fds);
-          if (!server->processSocketEvent(*i)) {
-            vlog.debug("client gone, sock %d",fd);
-            vncClientGone(fd);
-          }
+          server->processSocketEvent(*i);
         }
       }
 
@@ -551,9 +596,7 @@
           int fd = (*i)->getFd();
           if (FD_ISSET(fd, fds)) {
             FD_CLR(fd, fds);
-            if (!httpServer->processSocketEvent(*i)) {
-              vlog.debug("http client gone, sock %d",fd);
-            }
+            httpServer->processSocketEvent(*i);
           }
         }
       }
@@ -576,7 +619,7 @@
 void XserverDesktop::addClient(Socket* sock, bool reverse)
 {
   vlog.debug("new client, sock %d reverse %d",sock->getFd(),reverse);
-  server->addClient(sock, reverse);
+  server->addSocket(sock, reverse);
 }
 
 void XserverDesktop::disconnectClients()
@@ -586,12 +629,36 @@
 }
 
 
+int XserverDesktop::getQueryTimeout(void* opaqueId,
+                                    const char** address,
+                                    const char** username)
+{
+  if (opaqueId && queryConnectId == opaqueId) {
+    vlog.info("address=%s, username=%s, timeout=%d",
+              queryConnectAddress.buf, queryConnectUsername.buf,
+              (int)queryConnectTimeout);
+    if (address) *address = queryConnectAddress.buf;
+    if (username) *username = queryConnectUsername.buf;
+    return queryConnectTimeout;
+  }
+  return 0;
+}
+
+void XserverDesktop::approveConnection(void* opaqueId, bool accept,
+                                       const char* rejectMsg)
+{
+  if (queryConnectId == opaqueId) {
+    server->approveConnection((network::Socket*)opaqueId, accept, rejectMsg);
+    queryConnectId = 0;
+  }
+}
+
 ///////////////////////////////////////////////////////////////////////////
 //
 // SDesktop callbacks
 
 
-void XserverDesktop::pointerEvent(const Point& pos, rdr::U8 buttonMask)
+void XserverDesktop::pointerEvent(const Point& pos, int buttonMask)
 {
   xEvent ev;
   DevicePtr dev = LookupPointerDevice();
@@ -609,7 +676,7 @@
   ev.u.keyButtonPointer.rootY = pos.y;
   ev.u.keyButtonPointer.time = GetTimeInMillis();
 
-  if (pos.x != cursorX || pos.y != cursorY)
+  if (!pos.equals(cursorPos))
     (*dev->processInputProc)(&ev, (DeviceIntPtr)dev, 1);
 
   for (int i = 0; i < 5; i++) {
@@ -622,8 +689,7 @@
     }
   }
 
-  cursorX = pos.x;
-  cursorY = pos.y;
+  cursorPos = pos;
   oldButtonMask = buttonMask;
 }
 
@@ -662,16 +728,23 @@
 
 void XserverDesktop::lookup(int index, int* r, int* g, int* b)
 {
-  EntryPtr pent;
-  pent = (EntryPtr)&cmap->red[index];
-  if (pent->fShared) {
-    *r = pent->co.shco.red->color;
-    *g = pent->co.shco.green->color;
-    *b = pent->co.shco.blue->color;
+  if ((cmap->c_class | DynamicClass) == DirectColor) {
+    VisualPtr v = cmap->pVisual;
+    *r = cmap->red  [(index & v->redMask  ) >> v->offsetRed  ].co.local.red;
+    *g = cmap->green[(index & v->greenMask) >> v->offsetGreen].co.local.green;
+    *b = cmap->blue [(index & v->blueMask ) >> v->offsetBlue ].co.local.blue;
   } else {
-    *r = pent->co.local.red;
-    *g = pent->co.local.green;
-    *b = pent->co.local.blue;
+    EntryPtr pent;
+    pent = (EntryPtr)&cmap->red[index];
+    if (pent->fShared) {
+      *r = pent->co.shco.red->color;
+      *g = pent->co.shco.green->color;
+      *b = pent->co.shco.blue->color;
+    } else {
+      *r = pent->co.local.red;
+      *g = pent->co.local.green;
+      *b = pent->co.local.blue;
+    }
   }
 }
 
diff --git a/xc/programs/Xserver/vnc/XserverDesktop.h b/xc/programs/Xserver/vnc/XserverDesktop.h
index c983ece..880acc2 100644
--- a/xc/programs/Xserver/vnc/XserverDesktop.h
+++ b/xc/programs/Xserver/vnc/XserverDesktop.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -23,8 +23,10 @@
 #define __XSERVERDESKTOP_H__
 
 #include <rfb/SDesktop.h>
+#include <rfb/HTTPServer.h>
 #include <rfb/PixelBuffer.h>
 #include <rfb/Configuration.h>
+#include <rfb/VNCServerST.h>
 #include <rdr/SubstitutingInStream.h>
 
 extern "C" {
@@ -39,10 +41,10 @@
 }
 
 namespace network { class TcpListener; class Socket; }
-class MyHTTPServer;
 
 class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer,
-                       public rfb::ColourMap, public rdr::Substitutor {
+                       public rfb::ColourMap, public rdr::Substitutor,
+                       public rfb::VNCServerST::QueryConnectionHandler {
 public:
 
   XserverDesktop(ScreenPtr pScreen, network::TcpListener* listener,
@@ -66,8 +68,24 @@
   void addClient(network::Socket* sock, bool reverse);
   void disconnectClients();
 
+  // QueryConnect methods called from X server code
+  // getQueryTimeout()
+  //   Returns the timeout associated with a particular
+  //   connection, identified by an opaque Id passed to the
+  //   X code earlier.  Also optionally gets the address and
+  //   name associated with that connection.
+  //   Returns zero if the Id is not recognised.
+  int getQueryTimeout(void* opaqueId,
+                      const char** address=0,
+                      const char** username=0);
+
+  // approveConnection()
+  //   Used by X server code to supply the result of a query.
+  void approveConnection(void* opaqueId, bool accept,
+                         const char* rejectMsg=0);
+
   // rfb::SDesktop callbacks
-  virtual void pointerEvent(const rfb::Point& pos, rdr::U8 buttonMask);
+  virtual void pointerEvent(const rfb::Point& pos, int buttonMask);
   virtual void keyEvent(rdr::U32 key, bool down);
   virtual void clientCutText(const char* str, int len);
   virtual rfb::Point getFbSize() { return rfb::Point(width(), height()); }
@@ -81,14 +99,20 @@
   // rdr::Substitutor callback
   virtual char* substitute(const char* varName);
 
+  // rfb::VNCServerST::QueryConnectionHandler callback
+  virtual rfb::VNCServerST::queryResult queryConnection(network::Socket* sock,
+                                                        const char* userName,
+                                                        char** reason);
+
 private:
   void setColourMapEntries(int firstColour, int nColours);
   static CARD32 deferredUpdateTimerCallback(OsTimerPtr timer, CARD32 now,
                                             pointer arg);
+  void deferUpdate();
   ScreenPtr pScreen;
   OsTimerPtr deferredUpdateTimer, dummyTimer;
   rfb::VNCServerST* server;
-  MyHTTPServer* httpServer;
+  rfb::HTTPServer* httpServer;
   network::TcpListener* listener;
   network::TcpListener* httpListener;
   ColormapPtr cmap;
@@ -97,6 +121,10 @@
   bool ignoreHooks_;
   bool directFbptr;
   int oldButtonMask;
-  int cursorX, cursorY, oldCursorX, oldCursorY;
+  rfb::Point cursorPos, oldCursorPos;
+
+  void* queryConnectId;
+  rfb::CharArray queryConnectAddress;
+  rfb::CharArray queryConnectUsername;
 };
 #endif
diff --git a/xc/programs/Xserver/vnc/Xvnc/Imakefile b/xc/programs/Xserver/vnc/Xvnc/Imakefile
index 2d9f431..93885ae 100644
--- a/xc/programs/Xserver/vnc/Xvnc/Imakefile
+++ b/xc/programs/Xserver/vnc/Xvnc/Imakefile
@@ -3,6 +3,10 @@
       VNCLIBS = VncExtLibs
    VNCINCLUDE = -I$(VNCTOP) -I$(VNCTOP)/vncconfig_unix
 
+#if defined(XFree86Version) && XFree86Version < 4000
+   VNCDEFINES = -DNO_INIT_BACKING_STORE
+#endif
+
 #define CplusplusSource
 
 #include <Server.tmpl>
@@ -54,14 +58,14 @@
 
 DEFINES = $(OS_DEFINES) $(SHMDEF) $(MMAPDEF) $(FB_DEFINES) \
           $(VENDOR_STRING) $(VENDOR_RELEASE) $(STD_DEFINES) ServerOSDefines \
-          -UXFree86LOADER
+          $(VNCDEFINES) -UXFree86LOADER
 
 #ifdef XFree86Version
 /* 
  * Make sure XINPUT, XF86VidTune, etc arent defined for the miinitext.o 
  * used by Xvnc 
  */
-EXT_DEFINES = ExtensionDefines -UXINPUT -UXF86VIDMODE -UXFreeXDGA -UXF86MISC
+EXT_DEFINES = ExtensionDefines -UXF86VIDMODE -UXFreeXDGA -UXF86MISC
 #endif
 
 
@@ -83,7 +87,7 @@
 SpecialCplusplusObjectRule(xvnc,$(ICONFIGFILES) xvnc,$(EXT_DEFINES) $(NO_OPERATOR_NAMES))
 
 LinkSourceFile(miinitext.c,$(SERVERSRC)/mi)
-SpecialCObjectRule(miinitext,$(ICONFIGFILES),$(EXT_DEFINES) $(PAN_DEFINES) -DNO_HW_ONLY_EXTS -DNO_MODULE_EXTS $(EXT_MODULE_DEFINES) -UXFree86LOADER)
+SpecialCObjectRule(miinitext,$(ICONFIGFILES),$(EXT_DEFINES) $(PAN_DEFINES) -DNO_MODULE_EXTS $(EXT_MODULE_DEFINES) -UXFree86LOADER)
 
 /* InstallManPage(Xvfb,$(MANDIR)) */
 DependTarget()
diff --git a/xc/programs/Xserver/vnc/Xvnc/buildtime.c b/xc/programs/Xserver/vnc/Xvnc/buildtime.c
index a96031c..3f4c369 100644
--- a/xc/programs/Xserver/vnc/Xvnc/buildtime.c
+++ b/xc/programs/Xserver/vnc/Xvnc/buildtime.c
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/xc/programs/Xserver/vnc/Xvnc/xvnc.cc b/xc/programs/Xserver/vnc/Xvnc/xvnc.cc
index e5e9290..fcb73e6 100644
--- a/xc/programs/Xserver/vnc/Xvnc/xvnc.cc
+++ b/xc/programs/Xserver/vnc/Xvnc/xvnc.cc
@@ -1,24 +1,5 @@
-/* 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.
- */
-
-/*
-
-Copyright (c) 1993  X Consortium
+/* Copyright (c) 1993  X Consortium
+   Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
 
 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the
@@ -106,7 +87,10 @@
 #undef and
 }
 
-#define XVNCVERSION "4.0"
+#define XVNCVERSION "Free Edition 4.1.1"
+#define XVNCCOPYRIGHT ("Copyright (C) 2002-2005 RealVNC Ltd.\n" \
+                       "See http://www.realvnc.com for information on VNC.\n")
+
 
 extern char *display;
 extern int monitorResolution;
@@ -216,10 +200,16 @@
     else return 32;
 }
 
+
 extern "C" {
-void
-ddxGiveUp()
-{
+
+  /* ddxInitGlobals - called by |InitGlobals| from os/util.c in XOrg */
+  void ddxInitGlobals(void)
+  {
+  }
+
+  void ddxGiveUp()
+  {
     int i;
 
     /* clean up the framebuffers */
@@ -315,7 +305,7 @@
 void 
 ddxUseMsg()
 {
-    ErrorF("\nXvnc version %s - built %s\n", XVNCVERSION, buildtime);
+    ErrorF("\nXvnc %s - built %s\n%s", XVNCVERSION, buildtime, XVNCCOPYRIGHT);
     ErrorF("Underlying X server release %d, %s\n\n", VENDOR_RELEASE,
            VENDOR_STRING);
     ErrorF("-screen scrn WxHxD     set screen's width, height, depth\n");
@@ -1152,7 +1142,12 @@
     pvfb->closeScreen = pScreen->CloseScreen;
     pScreen->CloseScreen = vfbCloseScreen;
 
-    return ret;
+#ifndef NO_INIT_BACKING_STORE
+  miInitializeBackingStore(pScreen);
+  pScreen->backingStoreSupport = Always;
+#endif
+
+  return ret;
 
 } /* end vfbScreenInit */
 
@@ -1164,9 +1159,9 @@
 void
 InitOutput(ScreenInfo *screenInfo, int argc, char **argv)
 {
-    ErrorF("\nXvnc version %s - built %s\n", XVNCVERSION, buildtime);
-    ErrorF("Underlying X server release %d, %s\n\n", VENDOR_RELEASE,
-	   VENDOR_STRING);
+  ErrorF("\nXvnc %s - built %s\n%s", XVNCVERSION, buildtime, XVNCCOPYRIGHT);
+  ErrorF("Underlying X server release %d, %s\n\n", VENDOR_RELEASE,
+         VENDOR_STRING);
     int i;
     int NumFormats = 0;
 
diff --git a/xc/programs/Xserver/vnc/vncExtInit.cc b/xc/programs/Xserver/vnc/vncExtInit.cc
index ccaf5b8..9cf9d21 100644
--- a/xc/programs/Xserver/vnc/vncExtInit.cc
+++ b/xc/programs/Xserver/vnc/vncExtInit.cc
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -81,6 +81,11 @@
 static char* clientCutText = 0;
 static int clientCutTextLen = 0;
 
+static XserverDesktop* queryConnectDesktop = 0;
+static void* queryConnectId = 0;
+static int queryConnectTimeout = 0;
+static OsTimerPtr queryConnectTimer = 0;
+
 static struct VncInputSelect* vncInputSelectHead = 0;
 struct VncInputSelect {
   VncInputSelect(ClientPtr c, Window w, int m) : client(c), window(w), mask(m)
@@ -102,9 +107,8 @@
 static char* vncPasswdFile = 0;
 int vncInetdSock = -1;
 
-rfb::VncAuthPasswdFileParameter vncAuthPasswdFile;
 rfb::AliasParameter rfbauth("rfbauth", "Alias for PasswordFile",
-                            &vncAuthPasswdFile.param);
+                            &SSecurityFactoryStandard::vncAuthPasswdFile);
 rfb::StringParameter httpDir("httpd",
                              "Directory containing files to serve via HTTP",
                              "");
@@ -315,6 +319,74 @@
   }
 }
 
+
+static CARD32 queryConnectTimerCallback(OsTimerPtr timer,
+                                        CARD32 now, pointer arg)
+{
+  if (queryConnectTimeout)
+    queryConnectDesktop->approveConnection(queryConnectId, false, "The attempt to prompt the user to accept the connection failed");
+  // Re-notify clients, causing them to discover that we're done
+  vncQueryConnect(queryConnectDesktop, queryConnectId);
+  return 0;
+}
+
+void vncQueryConnect(XserverDesktop* desktop, void* opaqueId)
+{
+  // Only one query can be processed at any one time
+  if (queryConnectTimeout && ((desktop != queryConnectDesktop) ||
+                              (opaqueId != queryConnectId))) {
+    desktop->approveConnection(opaqueId, false,
+                               "Another connection is currently being queried.");
+    return;
+  }
+
+  // Get the query timeout.  If it's zero, there is no query.
+  queryConnectTimeout = desktop->getQueryTimeout(opaqueId);
+  queryConnectId = queryConnectTimeout ? opaqueId : 0;
+  queryConnectDesktop = queryConnectTimeout ? desktop : 0;
+
+  // Notify clients
+  bool notified = false;
+  xVncExtQueryConnectNotifyEvent ev;
+  ev.type = vncEventBase + VncExtQueryConnectNotify;
+  for (VncInputSelect* cur = vncInputSelectHead; cur; cur = cur->next) {
+    if (cur->mask & VncExtQueryConnectMask) {
+      ev.sequenceNumber = cur->client->sequence;
+      ev.window = cur->window;
+      if (cur->client->swapped) {
+        int n;
+        swaps(&ev.sequenceNumber, n);
+        swapl(&ev.window, n);
+      }
+      WriteToClient(cur->client, sizeof(xVncExtQueryConnectNotifyEvent),
+                    (char *)&ev);
+      notified = true;
+    }
+  }
+
+  // If we're being asked to query a connection (rather than to cancel
+  //   a query), and haven't been able to notify clients then reject it.
+  if (queryConnectTimeout && !notified) {
+    queryConnectTimeout = 0;
+    queryConnectId = 0;
+    queryConnectDesktop = 0;
+    desktop->approveConnection(opaqueId, false,
+                               "Unable to query the local user to accept the connection.");
+    return;
+  }    
+
+  // Set a timer so that if no-one ever responds, we will eventually 
+  //   reject the connection
+  //   NB: We don't set a timer if sock is null, since that indicates
+  //       that pending queries should be cancelled.
+  if (queryConnectDesktop)
+    queryConnectTimer = TimerSet(queryConnectTimer, 0,
+                                 queryConnectTimeout*2000,
+                                 queryConnectTimerCallback, 0);
+  else
+    TimerCancel(queryConnectTimer);
+}
+
 static void SendSelectionChangeEvent(Atom selection)
 {
   xVncExtSelectionChangeNotifyEvent ev;
@@ -469,14 +541,12 @@
 
   int nParams = 0;
   int len = 0;
-  rfb::VoidParameter* current = rfb::Configuration::head;
-  while (current) {
-    int l = strlen(current->getName());
+  for (ParameterIterator i(Configuration::global()); i.param; i.next()) {
+    int l = strlen(i.param->getName());
     if (l <= 255) {
       nParams++;
       len += l + 1;
     }
-    current = current->_next;
   }
   rep.length = (len + 3) >> 2;
   rep.nParams = nParams;
@@ -488,15 +558,13 @@
   WriteToClient(client, sizeof(xVncExtListParamsReply), (char *)&rep);
   rdr::U8* data = new rdr::U8[len];
   rdr::U8* ptr = data;
-  current = rfb::Configuration::head;
-  while (current) {
-    int l = strlen(current->getName());
+  for (ParameterIterator i(Configuration::global()); i.param; i.next()) {
+    int l = strlen(i.param->getName());
     if (l <= 255) {
       *ptr++ = l;
-      memcpy(ptr, current->getName(), l);
+      memcpy(ptr, i.param->getName(), l);
       ptr += l;
     }
-    current = current->_next;
   }
   WriteToClient(client, len, (char*)data);
   delete [] data;
@@ -662,6 +730,82 @@
   return ProcVncExtConnect(client);
 }
 
+
+static int ProcVncExtGetQueryConnect(ClientPtr client)
+{
+  REQUEST(xVncExtGetQueryConnectReq);
+  REQUEST_SIZE_MATCH(xVncExtGetQueryConnectReq);
+
+  const char *qcAddress=0, *qcUsername=0;
+  int qcTimeout;
+  if (queryConnectDesktop)
+    qcTimeout = queryConnectDesktop->getQueryTimeout(queryConnectId,
+                                                     &qcAddress, &qcUsername);
+  else
+    qcTimeout = 0;
+
+  xVncExtGetQueryConnectReply rep;
+  int n;
+  rep.type = X_Reply;
+  rep.sequenceNumber = client->sequence;
+  rep.timeout = qcTimeout;
+  rep.addrLen = qcTimeout ? strlen(qcAddress) : 0;
+  rep.userLen = qcTimeout ? strlen(qcUsername) : 0;
+  rep.opaqueId = (CARD32)queryConnectId;
+  rep.length = (rep.userLen + rep.addrLen + 3) >> 2;
+  if (client->swapped) {
+    swaps(&rep.sequenceNumber, n);
+    swapl(&rep.userLen, n);
+    swapl(&rep.addrLen, n);
+    swapl(&rep.timeout, n);
+    swapl(&rep.opaqueId, n);
+  }
+  WriteToClient(client, sizeof(xVncExtGetQueryConnectReply), (char *)&rep);
+  if (qcTimeout)
+    WriteToClient(client, strlen(qcAddress), (char*)qcAddress);
+  if (qcTimeout)
+    WriteToClient(client, strlen(qcUsername), (char*)qcUsername);
+  return (client->noClientException);
+}
+
+static int SProcVncExtGetQueryConnect(ClientPtr client)
+{
+  register char n;
+  REQUEST(xVncExtGetQueryConnectReq);
+  swaps(&stuff->length, n);
+  REQUEST_SIZE_MATCH(xVncExtGetQueryConnectReq);
+  return ProcVncExtGetQueryConnect(client);
+}
+
+
+static int ProcVncExtApproveConnect(ClientPtr client)
+{
+  REQUEST(xVncExtApproveConnectReq);
+  REQUEST_SIZE_MATCH(xVncExtApproveConnectReq);
+  if (queryConnectId == (void*)stuff->opaqueId) {
+    for (int scr = 0; scr < screenInfo.numScreens; scr++) {
+      if (desktop[scr]) {
+        desktop[scr]->approveConnection(queryConnectId, stuff->approve,
+                                        "Connection rejected by local user");
+      }
+    }
+    // Inform other clients of the event and tidy up
+    vncQueryConnect(queryConnectDesktop, queryConnectId);
+  }
+  return (client->noClientException);
+}
+
+static int SProcVncExtApproveConnect(ClientPtr client)
+{
+  register char n;
+  REQUEST(xVncExtApproveConnectReq);
+  swaps(&stuff->length, n);
+  swapl(&stuff->opaqueId, n);
+  REQUEST_SIZE_MATCH(xVncExtApproveConnectReq);
+  return ProcVncExtApproveConnect(client);
+}
+
+
 static int ProcVncExtDispatch(ClientPtr client)
 {
   REQUEST(xReq);
@@ -682,6 +826,10 @@
     return ProcVncExtSelectInput(client);
   case X_VncExtConnect:
     return ProcVncExtConnect(client);
+  case X_VncExtGetQueryConnect:
+    return ProcVncExtGetQueryConnect(client);
+  case X_VncExtApproveConnect:
+    return ProcVncExtApproveConnect(client);
   default:
     return BadRequest;
   }
@@ -707,6 +855,10 @@
     return SProcVncExtSelectInput(client);
   case X_VncExtConnect:
     return SProcVncExtConnect(client);
+  case X_VncExtGetQueryConnect:
+    return SProcVncExtGetQueryConnect(client);
+  case X_VncExtApproveConnect:
+    return SProcVncExtApproveConnect(client);
   default:
     return BadRequest;
   }
diff --git a/xc/programs/Xserver/vnc/vncExtInit.h b/xc/programs/Xserver/vnc/vncExtInit.h
index 947f34d..45453e1 100644
--- a/xc/programs/Xserver/vnc/vncExtInit.h
+++ b/xc/programs/Xserver/vnc/vncExtInit.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -19,8 +19,10 @@
 #define __VNCEXTINIT_H__
 
 #include <rfb/Configuration.h>
+#include "XserverDesktop.h"
 
 extern void vncClientCutText(const char* str, int len);
+extern void vncQueryConnect(XserverDesktop* desktop, void* opaqueId);
 extern void vncClientGone(int fd);
 extern void vncBell();
 extern void* vncFbptr[];
diff --git a/xc/programs/Xserver/vnc/vncHooks.cc b/xc/programs/Xserver/vnc/vncHooks.cc
index 34c7aca..ce8e7f0 100644
--- a/xc/programs/Xserver/vnc/vncHooks.cc
+++ b/xc/programs/Xserver/vnc/vncHooks.cc
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -1342,10 +1342,10 @@
 static void GetTextBoundingRect(DrawablePtr pDrawable, FontPtr font, int x,
                                 int y, int nchars, BoxPtr box)
 {
-  int ascent = max(FONTASCENT(font), FONTMAXBOUNDS(font, ascent));
-  int descent = max(FONTDESCENT(font), FONTMAXBOUNDS(font, descent));
-  int charWidth = max(FONTMAXBOUNDS(font,rightSideBearing),
-                      FONTMAXBOUNDS(font,characterWidth));
+  int ascent = __rfbmax(FONTASCENT(font), FONTMAXBOUNDS(font, ascent));
+  int descent = __rfbmax(FONTDESCENT(font), FONTMAXBOUNDS(font, descent));
+  int charWidth = __rfbmax(FONTMAXBOUNDS(font,rightSideBearing),
+                           FONTMAXBOUNDS(font,characterWidth));
 
   box->x1 = pDrawable->x + x;
   box->y1 = pDrawable->y + y - ascent;
diff --git a/xc/programs/Xserver/vnc/vncHooks.h b/xc/programs/Xserver/vnc/vncHooks.h
index c2ca825..c556ef3 100644
--- a/xc/programs/Xserver/vnc/vncHooks.h
+++ b/xc/programs/Xserver/vnc/vncHooks.h
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
diff --git a/xc/programs/Xserver/vnc/xf86vncModule.cc b/xc/programs/Xserver/vnc/xf86vncModule.cc
index 297f776..ef8ea50 100644
--- a/xc/programs/Xserver/vnc/xf86vncModule.cc
+++ b/xc/programs/Xserver/vnc/xf86vncModule.cc
@@ -1,5 +1,5 @@
-/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
- *    
+/* Copyright (C) 2002-2005 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
@@ -34,6 +34,8 @@
 #undef bool
 #undef new
 
+using namespace rfb;
+
 extern void vncExtensionInit();
 static void vncExtensionInitWithParams(INITARGS);
 
@@ -81,11 +83,10 @@
   for (int scr = 0; scr < screenInfo.numScreens; scr++) {
     ScrnInfoPtr pScrn = xf86Screens[scr];
 
-    rfb::VoidParameter* p;
-    for (p = rfb::Configuration::head; p; p = p->_next) {
-      char* val = xf86FindOptionValue(pScrn->options, p->getName());
+    for (ParameterIterator i(Configuration::global()); i.param; i.next()) {
+      char* val = xf86FindOptionValue(pScrn->options, i.param->getName());
       if (val)
-        p->setParam(val);
+        i.param->setParam(val);
     }
   }