Initial revision


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@2 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/vncviewer/CViewManager.cxx b/vncviewer/CViewManager.cxx
new file mode 100644
index 0000000..09ed1fe
--- /dev/null
+++ b/vncviewer/CViewManager.cxx
@@ -0,0 +1,252 @@
+/* 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
new file mode 100644
index 0000000..3d11dd9
--- /dev/null
+++ b/vncviewer/CViewManager.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- 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/CViewOptions.cxx b/vncviewer/CViewOptions.cxx
new file mode 100644
index 0000000..089c4c4
--- /dev/null
+++ b/vncviewer/CViewOptions.cxx
@@ -0,0 +1,364 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#include <vncviewer/CViewOptions.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/Registry.h>
+#include <rdr/HexInStream.h>
+#include <rdr/HexOutStream.h>
+#include <stdlib.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+
+static BoolParameter useLocalCursor("UseLocalCursor", "Render the mouse cursor locally", true);
+static BoolParameter useDesktopResize("UseDesktopResize", "Support dynamic desktop resizing", true);
+
+static BoolParameter fullColour("FullColour",
+                         "Use full colour (default is to use low colour "
+                         "unless auto select decides the link is fast enough)",
+                         false);
+static IntParameter lowColourLevel("LowColourLevel",
+                         "Colour level to use on slow connections. "
+                         "0 = Very Low (8 colours), 1 = Low (64 colours), 2 = Medium (256 colours)",
+                         1);
+static BoolParameter fullScreen("FullScreen",
+                         "Use the whole display to show the remote desktop."
+                         "(Press F8 to access the viewer menu)",
+                         false);
+static StringParameter preferredEncoding("PreferredEncoding",
+                         "Preferred graphical encoding to use - overridden by AutoSelect if set. "
+                         "(ZRLE, Hextile or Raw)", "ZRLE");
+
+static BoolParameter autoSelect("AutoSelect", "Auto select pixel format and encoding", true);
+static BoolParameter sharedConnection("Shared",
+                         "Allow existing connections to the server to continue."
+                         "(Default is to disconnect all other clients)",
+                         false);
+
+static BoolParameter sendPtrEvents("SendPointerEvents",
+                         "Send pointer (mouse) events to the server.", true);
+static BoolParameter sendKeyEvents("SendKeyEvents",
+                         "Send key presses (and releases) 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 protocol3_3("Protocol3.3",
+                         "Only use protocol version 3.3", false);
+
+static IntParameter ptrEventInterval("PointerEventInterval",
+                         "The interval to delay between sending one pointer event "
+                         "and the next.", 0);
+static BoolParameter emulate3("Emulate3",
+                         "Emulate middle mouse button when left and right buttons "
+                         "are used simulatenously.", false);
+
+static BoolParameter acceptBell("AcceptBell",
+                         "Produce a system beep when requested to by the server.",
+                         true);
+
+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");
+
+
+CViewOptions::CViewOptions()
+: useLocalCursor(::useLocalCursor), useDesktopResize(::useDesktopResize),
+autoSelect(::autoSelect), fullColour(::fullColour), fullScreen(::fullScreen),
+shared(::sharedConnection), sendPtrEvents(::sendPtrEvents), sendKeyEvents(::sendKeyEvents),
+preferredEncoding(encodingZRLE), clientCutText(::clientCutText), serverCutText(::serverCutText),
+protocol3_3(::protocol3_3), acceptBell(::acceptBell), lowColourLevel(::lowColourLevel),
+pointerEventInterval(ptrEventInterval), emulate3(::emulate3), monitor(::monitor.getData())
+{
+  CharArray encodingName(::preferredEncoding.getData());
+  preferredEncoding = encodingNum(encodingName.buf);
+  setMenuKey(CharArray(::menuKey.getData()).buf);
+}
+
+
+void CViewOptions::readFromFile(const char* filename) {
+  FILE* f = fopen(filename, "r");
+  if (!f)
+    throw rdr::Exception("Failed to read configuration file");
+
+  try { 
+    char line[4096];
+    CharArray section;
+
+    CharArray hostTmp;
+    int portTmp = 0;
+
+    while (!feof(f)) {
+      // Read the next line
+      if (!fgets(line, sizeof(line), f)) {
+        if (feof(f))
+          break;
+        throw rdr::SystemException("fgets", ferror(f));
+      }
+      int len=strlen(line);
+      if (line[len-1] == '\n') {
+        line[len-1] = 0;
+        len--;
+      }
+
+      // Process the line
+      if (line[0] == ';') {
+        // Comment
+      } else if (line[0] == '[') {
+        // Entering a new section
+        if (!strSplit(&line[1], ']', &section.buf, 0))
+          throw rdr::Exception("bad Section");
+      } else {
+        // Reading an option
+        CharArray name;
+        CharArray value;
+        if (!strSplit(line, '=', &name.buf, &value.buf))
+          throw rdr::Exception("bad Name/Value pair");
+
+        if (stricmp(section.buf, "Connection") == 0) {
+          if (stricmp(name.buf, "Host") == 0) {
+            hostTmp.replaceBuf(value.takeBuf());
+          } else if (stricmp(name.buf, "Port") == 0) {
+            portTmp = atoi(value.buf);
+          } 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;
+            }
+          }
+        } else if (stricmp(section.buf, "Options") == 0) {
+            // V4 options
+          if (stricmp(name.buf, "UseLocalCursor") == 0) {
+            useLocalCursor = atoi(value.buf);
+          } else if (stricmp(name.buf, "UseDesktopResize") == 0) {
+            useDesktopResize = atoi(value.buf);
+          } else if (stricmp(name.buf, "FullScreen") == 0) {
+            fullScreen = atoi(value.buf);
+          } else if (stricmp(name.buf, "FullColour") == 0) {
+            fullColour = atoi(value.buf);
+          } else if (stricmp(name.buf, "LowColourLevel") == 0) {
+            lowColourLevel = atoi(value.buf);
+          } else if (stricmp(name.buf, "PreferredEncoding") == 0) {
+            preferredEncoding = encodingNum(value.buf);
+          } else if ((stricmp(name.buf, "AutoDetect") == 0) ||
+                     (stricmp(name.buf, "AutoSelect") == 0)) {
+            autoSelect = atoi(value.buf);
+          } else if (stricmp(name.buf, "Shared") == 0) {
+            shared = atoi(value.buf);
+          } else if (stricmp(name.buf, "SendPtrEvents") == 0) {
+            sendPtrEvents = atoi(value.buf);
+          } else if (stricmp(name.buf, "SendKeyEvents") == 0) {
+            sendKeyEvents = 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, "Emulate3") == 0) {
+            emulate3 = atoi(value.buf);
+          } else if (stricmp(name.buf, "PointerEventInterval") == 0) {
+            pointerEventInterval = atoi(value.buf);
+          } else if (stricmp(name.buf, "Monitor") == 0) {
+            monitor.replaceBuf(value.takeBuf());
+          } else if (stricmp(name.buf, "MenuKey") == 0) {
+            setMenuKey(value.buf);
+
+            // Legacy options
+          } else if (stricmp(name.buf, "Preferred_Encoding") == 0) {
+            preferredEncoding = atoi(value.buf);
+          } else if (stricmp(name.buf, "8bit") == 0) {
+            fullColour = !atoi(value.buf);
+          } else if (stricmp(name.buf, "FullScreen") == 0) {
+            fullScreen = atoi(value.buf);
+          } else if (stricmp(name.buf, "ViewOnly") == 0) {
+            sendPtrEvents = sendKeyEvents = !atoi(value.buf);
+          } else if (stricmp(name.buf, "DisableClipboard") == 0) {
+            clientCutText = serverCutText = !atoi(value.buf);
+          }
+        }
+      }
+    }
+    fclose(f); f=0;
+
+    // Process the Host and Port
+    if (hostTmp.buf) {
+      int hostLen = strlen(hostTmp.buf) + 2 + 17;
+      host.replaceBuf(new char[hostLen]);
+      strCopy(host.buf, hostTmp.buf, hostLen);
+      if (portTmp) {
+        strncat(host.buf, "::", hostLen-1);
+        char tmp[16];
+        sprintf(tmp, "%d", portTmp);
+        strncat(host.buf, tmp, hostLen-1);
+      }
+    }
+
+    setConfigFileName(filename);
+  } catch (rdr::Exception&) {
+    if (f) fclose(f);
+    throw;
+  }
+}
+
+void CViewOptions::writeToFile(const char* filename) {
+  FILE* f = fopen(filename, "w");
+  if (!f)
+    throw rdr::Exception("Failed to write configuration file");
+
+  try {
+    // - Split server into host and port and save
+    fprintf(f, "[Connection]\n");
+
+    fprintf(f, "Host=%s\n", host.buf);
+    if (userName.buf)
+      fprintf(f, "UserName=%s\n", userName.buf);
+    if (password.buf) {
+      // - Warn the user before saving the password
+      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);
+        fprintf(f, "Password=%s\n", obfuscatedHex.buf);
+      }
+    }
+
+    // - Save the other options
+    fprintf(f, "[Options]\n");
+
+    fprintf(f, "UseLocalCursor=%d\n", (int)useLocalCursor);
+    fprintf(f, "UseDesktopResize=%d\n", (int)useDesktopResize);
+    fprintf(f, "FullScreen=%d\n", (int)fullScreen);
+    fprintf(f, "FullColour=%d\n", (int)fullColour);
+    fprintf(f, "LowColourLevel=%d\n", lowColourLevel);
+    fprintf(f, "PreferredEncoding=%s\n", encodingName(preferredEncoding));
+    fprintf(f, "AutoSelect=%d\n", (int)autoSelect);
+    fprintf(f, "Shared=%d\n", (int)shared);
+    fprintf(f, "SendPtrEvents=%d\n", (int)sendPtrEvents);
+    fprintf(f, "SendKeyEvents=%d\n", (int)sendKeyEvents);
+    fprintf(f, "SendCutText=%d\n", (int)clientCutText);
+    fprintf(f, "AcceptCutText=%d\n", (int)serverCutText);
+    fprintf(f, "Emulate3=%d\n", (int)emulate3);
+    fprintf(f, "PointerEventInterval=%d\n", pointerEventInterval);
+    if (monitor.buf)
+      fprintf(f, "Monitor=%s\n", monitor.buf);
+    fprintf(f, "MenuKey=%s\n", CharArray(menuKeyName()).buf);
+    fclose(f); f=0;
+
+    setConfigFileName(filename);
+  } catch (rdr::Exception&) {
+    if (f) fclose(f);
+    throw;
+  }
+}
+
+
+void CViewOptions::writeDefaults() {
+  RegKey key;
+  key.createKey(HKEY_CURRENT_USER, _T("Software\\RealVNC\\VNCviewer4"));
+  key.setBool(_T("UseLocalCursor"), useLocalCursor);
+  key.setBool(_T("UseDesktopResize"), useDesktopResize);
+  key.setBool(_T("FullScreen"), fullScreen);
+  key.setBool(_T("FullColour"), fullColour);
+  key.setInt(_T("LowColourLevel"), lowColourLevel);
+  key.setString(_T("PreferredEncoding"), TStr(encodingName(preferredEncoding)));
+  key.setBool(_T("AutoSelect"), autoSelect);
+  key.setBool(_T("Shared"), shared);
+  key.setBool(_T("SendPointerEvents"), sendPtrEvents);
+  key.setBool(_T("SendKeyEvents"), sendKeyEvents);
+  key.setBool(_T("ClientCutText"), clientCutText);
+  key.setBool(_T("ServerCutText"), serverCutText);
+  key.setBool(_T("Protocol3.3"), protocol3_3);
+  key.setBool(_T("AcceptBell"), acceptBell);
+  key.setBool(_T("Emulate3"), emulate3);
+  key.setInt(_T("PointerEventInterval"), pointerEventInterval);
+  if (monitor.buf)
+    key.setString(_T("Monitor"), TStr(monitor.buf));
+  key.setString(_T("MenuKey"), TCharArray(menuKeyName()).buf);
+}
+
+
+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 CViewOptions::setMenuKey(const char* keyName) {
+  if (!keyName[0]) {
+    menuKey = 0;
+  } else {
+    menuKey = VK_F8;
+    if (keyName[0] == 'F') {
+      UINT fKey = atoi(&keyName[1]);
+      if (fKey >= 1 && fKey <= 12)
+        menuKey = fKey-1 + VK_F1;
+    }
+  }
+}
+char* CViewOptions::menuKeyName() {
+  int fNum = (menuKey-VK_F1)+1;
+  if (fNum<1 || fNum>12)
+    return strDup("");
+  CharArray menuKeyStr(4);
+  sprintf(menuKeyStr.buf, "F%d", fNum);
+  return menuKeyStr.takeBuf();
+}
+
+
+CViewOptions& CViewOptions::operator=(const CViewOptions& o) {
+  useLocalCursor = o.useLocalCursor;
+  useDesktopResize = o.useDesktopResize;
+  fullScreen = o.fullScreen;
+  fullColour = o.fullColour;
+  lowColourLevel = o.lowColourLevel;
+  preferredEncoding = o.preferredEncoding;
+  autoSelect = o.autoSelect;
+  shared = o.shared;
+  sendPtrEvents = o.sendPtrEvents;
+  sendKeyEvents = o.sendKeyEvents;
+  clientCutText = o.clientCutText;
+  serverCutText = o.serverCutText;
+  emulate3 = o.emulate3;
+  pointerEventInterval = o.pointerEventInterval;
+  protocol3_3 = o.protocol3_3;
+  acceptBell = o.acceptBell;
+  setUserName(o.userName.buf);
+  setPassword(o.password.buf);
+  setConfigFileName(o.configFileName.buf);
+  setHost(o.host.buf);
+  setMonitor(o.monitor.buf);
+  menuKey = o.menuKey;
+  return *this;
+}
\ No newline at end of file
diff --git a/vncviewer/CViewOptions.h b/vncviewer/CViewOptions.h
new file mode 100644
index 0000000..9120bde
--- /dev/null
+++ b/vncviewer/CViewOptions.h
@@ -0,0 +1,87 @@
+/* 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.
+ */
+
+// -=- CViewOptions.h
+
+// Definition of the CViewOptions class, responsible for storing the
+// current & requested VNCviewer options.
+
+#ifndef __RFB_WIN32_CVIEW_OPTIONS_H__
+#define __RFB_WIN32_CVIEW_OPTIONS_H__
+
+#include <rfb/util.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    //
+    // -=- 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
+    //     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 {
+    public:
+      CViewOptions();
+      CViewOptions(const CViewOptions& o) {operator=(o);}
+      CViewOptions& operator=(const CViewOptions& o);
+      void readFromFile(const char* filename_);
+      void writeToFile(const char* filename_);
+      void writeDefaults();
+      bool useLocalCursor;
+      bool useDesktopResize;
+      bool fullScreen;
+      bool fullColour;
+      int lowColourLevel;
+      int preferredEncoding;
+      bool autoSelect;
+      bool shared;
+      bool sendPtrEvents;
+      bool sendKeyEvents;
+      bool clientCutText;
+      bool serverCutText;
+      bool emulate3;
+      int pointerEventInterval;
+      bool protocol3_3;
+      bool acceptBell;
+      CharArray userName;
+      void setUserName(const char* user);
+      CharArray password;
+      void setPassword(const char* pwd);
+      CharArray configFileName;
+      void setConfigFileName(const char* cfn);
+      CharArray host;
+      void setHost(const char* h);
+      CharArray monitor;
+      void setMonitor(const char* m);
+      unsigned int menuKey;
+      void setMenuKey(const char* keyName);
+      char* menuKeyName();
+    };
+
+
+  };
+
+};
+
+#endif
diff --git a/vncviewer/ConnectingDialog.h b/vncviewer/ConnectingDialog.h
new file mode 100644
index 0000000..b146ced
--- /dev/null
+++ b/vncviewer/ConnectingDialog.h
@@ -0,0 +1,95 @@
+/* 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.
+ */
+
+// -=- ConnectingDialog.h
+
+// Dialog to indicate to the user that the viewer is attempting to make an
+// outgoing connection.
+
+#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>
+
+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 {
+    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();
+      }
+    protected:
+      HWND dialog;
+      bool active;
+    };
+
+  };
+
+};
+
+#endif
\ No newline at end of file
diff --git a/vncviewer/ConnectionDialog.cxx b/vncviewer/ConnectionDialog.cxx
new file mode 100644
index 0000000..c083444
--- /dev/null
+++ b/vncviewer/ConnectionDialog.cxx
@@ -0,0 +1,70 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <vncviewer/ConnectionDialog.h>
+#include <vncviewer/CView.h>
+#include <vncviewer/resource.h>
+
+#include <tchar.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+
+ConnectionDialog::ConnectionDialog(CView* view_) : Dialog(GetModuleHandle(0)), view(view_) {
+}
+
+
+bool ConnectionDialog::showDialog() {
+  return Dialog::showDialog(MAKEINTRESOURCE(IDD_CONNECTION_DLG));
+}
+
+void ConnectionDialog::initDialog() {
+  HWND box = GetDlgItem(handle, IDC_SERVER_EDIT);
+
+  std::list<char*> mru = MRU::getEntries();
+  std::list<char*>::iterator i;
+
+  // Locate the combo-box
+  // NB: TCharArray converts the supplied char* and assumes ownership!
+  for (i=mru.begin(); i!=mru.end(); i++) {
+    int index = SendMessage(box, CB_ADDSTRING, 0, (LPARAM)TCharArray(*i).buf);
+  }
+
+  // Select the first item in the list
+  SendMessage(box, CB_SETCURSEL, 0, 0);
+}
+
+
+bool ConnectionDialog::onOk() {
+  delete [] hostname.buf;
+  hostname.buf = 0;
+  hostname.buf = getItemString(IDC_SERVER_EDIT);
+  return hostname.buf[0] != 0;
+}
+
+bool ConnectionDialog::onCommand(int id, int cmd) {
+  switch (id) {
+  case IDC_ABOUT:
+    AboutDialog::instance.showDialog();
+    return true;
+  case IDC_OPTIONS:
+    view->optionsDialog.showDialog(view);
+    return true;
+  };
+  return false;
+}
diff --git a/vncviewer/ConnectionDialog.h b/vncviewer/ConnectionDialog.h
new file mode 100644
index 0000000..554c86f
--- /dev/null
+++ b/vncviewer/ConnectionDialog.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+// -=- ConnectionDialog.h
+
+// Connection dialog for VNC Viewer 4.0
+
+#ifndef __RFB_WIN32_CONN_DIALOG_H__
+#define __RFB_WIN32_CONN_DIALOG_H__
+
+#include <rfb_win32/Dialog.h>
+#include <vncviewer/MRU.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class CView;
+
+    class ConnectionDialog : Dialog {
+    public:
+      ConnectionDialog(CView* view);
+      virtual bool showDialog();
+      virtual void initDialog();
+      virtual bool onOk();
+      virtual bool onCommand(int id, int cmd);
+      TCharArray hostname;
+    protected:
+      CView* view;
+    };
+
+  };
+
+};
+
+#endif
diff --git a/vncviewer/InfoDialog.cxx b/vncviewer/InfoDialog.cxx
new file mode 100644
index 0000000..0d2313a
--- /dev/null
+++ b/vncviewer/InfoDialog.cxx
@@ -0,0 +1,65 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#include <vncviewer/InfoDialog.h>
+#include <vncviewer/resource.h>
+#include <vncviewer/CView.h>
+#include <rfb/secTypes.h>
+#include <rfb/encodings.h>
+#include <rfb/CSecurity.h>
+#include <rfb/LogWriter.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("Info");
+
+
+bool InfoDialog::showDialog(CView* vw) {
+  view = vw;
+  return Dialog::showDialog(MAKEINTRESOURCE(IDD_CONNECTION_INFO));
+}
+
+void InfoDialog::initDialog() {
+  char buf[256];
+
+  setItemString(IDC_INFO_NAME, TStr(view->cp.name()));
+
+  setItemString(IDC_INFO_HOST, TCharArray(view->sock->getPeerAddress()).buf);
+
+  Rect bufRect = view->buffer->getRect();
+  sprintf(buf, "%dx%d", bufRect.width(), bufRect.height());
+  setItemString(IDC_INFO_SIZE, TStr(buf));
+
+  view->cp.pf().print(buf, 256);
+  setItemString(IDC_INFO_PF, TStr(buf));
+
+  view->serverDefaultPF.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())));
+
+  sprintf(buf, "%d kbits/s", view->sock->inStream().kbitsPerSecond());
+  setItemString(IDC_INFO_LINESPEED, TStr(buf));
+
+  sprintf(buf, "%d.%d", view->cp.majorVersion, view->cp.minorVersion);
+  setItemString(IDC_INFO_VERSION, TStr(buf));
+
+  int secType = view->getCurrentCSecurity()->getType();
+  setItemString(IDC_INFO_SECURITY, TStr(secTypeName(secType)));
+}
diff --git a/vncviewer/InfoDialog.h b/vncviewer/InfoDialog.h
new file mode 100644
index 0000000..7a64d38
--- /dev/null
+++ b/vncviewer/InfoDialog.h
@@ -0,0 +1,48 @@
+/* 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.
+ */
+
+// -=- InfoDialog.h
+
+// Info dialog for VNC Viewer 4.0
+
+#ifndef __RFB_WIN32_INFO_DIALOG_H__
+#define __RFB_WIN32_INFO_DIALOG_H__
+
+#include <rfb_win32/Dialog.h>
+#include <rfb/util.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class CView;
+
+    class InfoDialog : Dialog {
+    public:
+      InfoDialog() : Dialog(GetModuleHandle(0)), view(0) {}
+      virtual bool showDialog(CView* vw);
+      virtual void initDialog();
+    protected:
+      CView* view;
+    };
+
+  };
+
+};
+
+#endif
diff --git a/vncviewer/MRU.h b/vncviewer/MRU.h
new file mode 100644
index 0000000..9e99395
--- /dev/null
+++ b/vncviewer/MRU.h
@@ -0,0 +1,140 @@
+/* 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 __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>
+
+namespace rfb {
+
+  namespace win32 {
+
+    namespace MRU {
+
+      static const RegKey RegRoot = HKEY_CURRENT_USER;
+      static const TCHAR* RegPath = _T("Software\\RealVNC\\VNCViewer4\\MRU");
+      static const int MaxMRUEntries = 256;
+      static const int MRUEntries = 10;
+
+      static std::list<char*> getEntries() {
+        std::list<char*> mru;
+
+        try {
+          RegKey key;
+          key.openKey(RegRoot, RegPath);
+
+          CharArray order;
+          int length;
+          key.getBinary(_T("Order"), (void**)&order.buf, &length);
+
+          for (int i=0; i<length; i++) {
+            TCharArray keyname = rdr::HexOutStream::binToHexStr(&order.buf[i], 1);
+            try {
+              TCharArray entry = key.getString(keyname.buf);
+              mru.push_back(strDup(entry.buf));
+            } catch (rdr::Exception) {
+            }
+          }
+        } catch (rdr::Exception) {
+        }
+
+        return mru;
+      }
+
+      static void addToMRU(const char* name) {
+        RegKey key;
+        key.createKey(RegRoot, RegPath);
+
+        BYTE keycode;
+        CharArray old_order;
+        char order[MaxMRUEntries];
+        int orderlen;
+        
+        try {
+          key.getBinary(_T("Order"), (void**)&old_order.buf, &orderlen);
+          if (orderlen)
+            memcpy(order, old_order.buf, orderlen);
+
+          std::set<int> ordercodes;
+          keycode = 0;
+          bool found = false;
+          for (int i=0; i<orderlen; i++) {
+            TCharArray keyname = rdr::HexOutStream::binToHexStr(&order[i], 1);
+            try {
+              TCharArray hostname = key.getString(keyname.buf);
+              if (strcmp(name, CStr(hostname.buf)) == 0) {
+                keycode = order[i];
+                found = true;
+                break;
+              }
+            } catch (rdr::Exception) {
+            }
+            ordercodes.insert(order[i]);
+          }
+
+          if (!found) {
+            if (orderlen <= MRUEntries) {
+              while (ordercodes.find(keycode) != ordercodes.end()) keycode++;
+            } else {
+              keycode = order[orderlen-1];
+              orderlen--;
+            }
+          }
+
+        } catch (rdr::Exception) {
+          keycode = 0;
+          orderlen = 0;
+        }
+
+        printf("keycode=%d\n", (int)keycode);
+
+        orderlen++;
+        int i, j=orderlen-1;
+        for (i=0; i<orderlen-1; i++) {
+          if (order[i] == keycode) {
+            j = i;
+            orderlen--;
+            break;
+          }
+        }
+        for (i=j; i>0; i--)
+          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);
+      }
+
+    };
+
+  };
+
+};
+
+#endif
\ No newline at end of file
diff --git a/vncviewer/OptionsDialog.cxx b/vncviewer/OptionsDialog.cxx
new file mode 100644
index 0000000..ab45f8c
--- /dev/null
+++ b/vncviewer/OptionsDialog.cxx
@@ -0,0 +1,309 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#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/resource.h>
+#include <rfb_win32/Registry.h>
+#include <rfb/LogWriter.h>
+#include <rfb/encodings.h>
+#include <rfb/CConnection.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+static LogWriter vlog("Options");
+
+
+struct OptionsInfo {
+  CView* view;
+  CViewOptions options;
+};
+
+
+OptionsDialog rfb::win32::OptionsDialog::global;
+
+
+class VNCviewerOptions : public PropSheet {
+public:
+  VNCviewerOptions(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() {
+    if (changed) {
+      if (info.view)
+        // Apply the settings to the supplied session object
+        info.view->applyOptions(info.options);
+      else {
+        // Commit the settings to the user's registry area
+        info.options.writeDefaults();
+      }
+    }
+  }
+
+  void setChanged() {changed = true;}
+
+  bool changed;
+  OptionsInfo& info;
+};
+      
+
+class FormatPage : public PropSheetPage {
+public:
+  FormatPage(OptionsInfo* dlg_)
+    : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_FORMAT)), dlg(dlg_) {
+  }
+  virtual void initDialog() {
+    setItemChecked(IDC_ENCODING_AUTO, dlg->options.autoSelect);
+    setItemChecked(IDC_FORMAT_FULLCOLOUR, dlg->options.fullColour);
+    if (!dlg->options.fullColour) {
+      switch (dlg->options.lowColourLevel) {
+      case 0: setItemChecked(IDC_FORMAT_VERYLOWCOLOUR, true); break;
+      case 1: setItemChecked(IDC_FORMAT_LOWCOLOUR, true); break;
+      case 2: setItemChecked(IDC_FORMAT_MEDIUMCOLOUR, true); break;
+      }
+    }
+    switch (dlg->options.preferredEncoding) {
+    case encodingZRLE: setItemChecked(IDC_ENCODING_ZRLE, true); break;
+    case encodingHextile: setItemChecked(IDC_ENCODING_HEXTILE, true); break;
+    case encodingRaw: setItemChecked(IDC_ENCODING_RAW, true); break;
+    }
+    onCommand(IDC_ENCODING_AUTO, 0 /* ? */); // Force enableItem status to refresh
+  }
+  virtual bool onOk() {
+    dlg->options.autoSelect = isItemChecked(IDC_ENCODING_AUTO);
+    dlg->options.fullColour = isItemChecked(IDC_FORMAT_FULLCOLOUR);
+    if (isItemChecked(IDC_FORMAT_VERYLOWCOLOUR))
+      dlg->options.lowColourLevel = 0;
+    if (isItemChecked(IDC_FORMAT_LOWCOLOUR))
+      dlg->options.lowColourLevel = 1;
+    if (isItemChecked(IDC_FORMAT_MEDIUMCOLOUR))
+      dlg->options.lowColourLevel = 2;
+    dlg->options.preferredEncoding = encodingZRLE;
+    if (isItemChecked(IDC_ENCODING_HEXTILE))
+      dlg->options.preferredEncoding = encodingHextile;
+    if (isItemChecked(IDC_ENCODING_RAW))
+      dlg->options.preferredEncoding = encodingRaw;
+    ((VNCviewerOptions*)propSheet)->setChanged();
+    return true;
+  }
+  virtual bool onCommand(int id, int cmd) {
+    if (id == IDC_ENCODING_AUTO) {
+      bool ok = !isItemChecked(IDC_ENCODING_AUTO);
+      enableItem(IDC_ENCODING_ZRLE, ok);
+      enableItem(IDC_ENCODING_HEXTILE, ok);
+      enableItem(IDC_ENCODING_RAW, ok);
+      return true;
+    }
+    return false;
+  }
+protected:
+  OptionsInfo* dlg;
+};
+
+class MiscPage : public PropSheetPage {
+public:
+  MiscPage(OptionsInfo* dlg_)
+    : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_MISC)), dlg(dlg_) {
+  }
+  virtual void initDialog() {
+    setItemChecked(IDC_CONN_SHARED, dlg->options.shared);
+    enableItem(IDC_CONN_SHARED, (!dlg->view) || (dlg->view->state() != CConnection::RFBSTATE_NORMAL));
+    setItemChecked(IDC_FULL_SCREEN, dlg->options.fullScreen);
+    setItemChecked(IDC_LOCAL_CURSOR, dlg->options.useLocalCursor);
+    setItemChecked(IDC_DESKTOP_RESIZE, dlg->options.useDesktopResize);
+    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);
+  }
+  virtual bool onOk() {
+    dlg->options.shared = isItemChecked(IDC_CONN_SHARED);
+    dlg->options.fullScreen = isItemChecked(IDC_FULL_SCREEN);
+    dlg->options.useLocalCursor = isItemChecked(IDC_LOCAL_CURSOR);
+    dlg->options.useDesktopResize = isItemChecked(IDC_DESKTOP_RESIZE);
+    dlg->options.protocol3_3 = isItemChecked(IDC_PROTOCOL_3_3);
+    dlg->options.acceptBell = isItemChecked(IDC_ACCEPT_BELL);
+    ((VNCviewerOptions*)propSheet)->setChanged();
+    return true;
+  }
+protected:
+  OptionsInfo* dlg;
+};
+
+class InputsPage : public PropSheetPage {
+public:
+  InputsPage(OptionsInfo* dlg_)
+    : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_INPUTS)), dlg(dlg_) {
+  }
+  virtual void initDialog() {
+    setItemChecked(IDC_SEND_POINTER, dlg->options.sendPtrEvents);
+    setItemChecked(IDC_SEND_KEYS, dlg->options.sendKeyEvents);
+    setItemChecked(IDC_CLIENT_CUTTEXT, dlg->options.clientCutText);
+    setItemChecked(IDC_SERVER_CUTTEXT, dlg->options.serverCutText);
+    setItemChecked(IDC_EMULATE3, dlg->options.emulate3);
+    setItemChecked(IDC_POINTER_INTERVAL, dlg->options.pointerEventInterval != 0);
+
+    // Populate the Menu Key tab
+    HWND menuKey = GetDlgItem(handle, IDC_MENU_KEY);
+    SendMessage(menuKey, CB_RESETCONTENT, 0, 0);
+    SendMessage(menuKey, CB_ADDSTRING, 0, (LPARAM)_T("none"));
+    if (!dlg->options.menuKey)
+      SendMessage(menuKey, CB_SETCURSEL, 0, 0);
+    for (int i=0; i<12; i++) {
+      TCHAR buf[4];
+      _stprintf(buf, _T("F%d"), i+1);
+      int index = SendMessage(menuKey, CB_ADDSTRING, 0, (LPARAM)buf);
+      if (i == (dlg->options.menuKey - VK_F1))
+        SendMessage(menuKey, CB_SETCURSEL, index, 0);
+    }
+  }
+  virtual bool onOk() {
+    dlg->options.sendPtrEvents = isItemChecked(IDC_SEND_POINTER);
+    dlg->options.sendKeyEvents = isItemChecked(IDC_SEND_KEYS);
+    dlg->options.clientCutText = isItemChecked(IDC_CLIENT_CUTTEXT);
+    dlg->options.serverCutText = isItemChecked(IDC_SERVER_CUTTEXT);
+    dlg->options.emulate3 = isItemChecked(IDC_EMULATE3);
+    dlg->options.pointerEventInterval = 
+      isItemChecked(IDC_POINTER_INTERVAL) ? 200 : 0;
+
+    HWND mkHwnd = GetDlgItem(handle, IDC_MENU_KEY);
+    int index = SendMessage(mkHwnd, CB_GETCURSEL, 0, 0);
+    TCharArray keyName(SendMessage(mkHwnd, CB_GETLBTEXTLEN, index, 0)+1);
+    SendMessage(mkHwnd, CB_GETLBTEXT, index, (LPARAM)keyName.buf);
+    if (_tcscmp(keyName.buf, _T("none")) == 0)
+      dlg->options.setMenuKey("");
+    else
+      dlg->options.setMenuKey(CStr(keyName.buf));
+
+    ((VNCviewerOptions*)propSheet)->setChanged();
+    return true;
+  }
+protected:
+  OptionsInfo* dlg;
+};
+
+
+class DefaultsPage : public PropSheetPage {
+public:
+  DefaultsPage(OptionsInfo* dlg_)
+    : PropSheetPage(GetModuleHandle(0), MAKEINTRESOURCE(IDD_DEFAULTS)), dlg(dlg_) {
+  }
+  virtual void initDialog() {
+    enableItem(IDC_LOAD_CONFIG, dlg->options.configFileName.buf);
+    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();
+      break;
+    case IDC_SAVE_DEFAULTS:
+      propSheet->commitPages();
+      dlg->options.writeDefaults();
+      break;
+    case IDC_LOAD_CONFIG:
+      dlg->options.readFromFile(dlg->options.configFileName.buf);
+      break;
+    case IDC_SAVE_CONFIG:
+      propSheet->commitPages();
+      dlg->options.writeToFile(dlg->options.configFileName.buf);
+      MsgBox(hwnd, _T("Options saved successfully"),
+             MB_OK | MB_ICONINFORMATION);
+      return 0;
+    case IDC_SAVE_CONFIG_AS:
+      propSheet->commitPages();
+      // Get a filename to save to
+      TCHAR newFilename[4096];
+      TCHAR currentDir[4096];
+      if (dlg->options.configFileName.buf)
+        _tcscpy(newFilename, TStr(dlg->options.configFileName.buf));
+      else
+        newFilename[0] = 0;
+      OPENFILENAME ofn;
+      memset(&ofn, 0, sizeof(ofn));
+#ifdef OPENFILENAME_SIZE_VERSION_400
+      ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+#else
+      ofn.lStructSize = sizeof(ofn);
+#endif
+      ofn.hwndOwner = hwnd;
+      ofn.lpstrFilter = _T("VNC Connection Options\000*.vnc\000");
+      ofn.lpstrFile = newFilename;
+      currentDir[0] = 0;
+      GetCurrentDirectory(4096, currentDir);
+      ofn.lpstrInitialDir = currentDir;
+      ofn.nMaxFile = 4096;
+      ofn.lpstrDefExt = _T(".vnc");
+      ofn.Flags = OFN_NOREADONLYRETURN | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
+      if (!GetSaveFileName(&ofn)) {
+        if (CommDlgExtendedError())
+          throw rdr::Exception("GetSaveFileName failed");
+        return 0;
+      }
+
+      // Save the Options
+      dlg->options.writeToFile(CStr(newFilename));
+      MsgBox(hwnd, _T("Options saved successfully"),
+             MB_OK | MB_ICONINFORMATION);
+      return 0;
+    };
+    propSheet->reInitPages();
+    return true;
+  }
+protected:
+  OptionsInfo* dlg;
+};
+
+
+OptionsDialog::OptionsDialog() : visible(false) {
+}
+
+bool OptionsDialog::showDialog(CView* view, bool capture) {
+  if (visible) return false;
+  visible = true;
+
+  // Grab the current properties
+  OptionsInfo info;
+  if (view)
+    info.options = view->getOptions();
+  info.view = view;
+
+  // Build a list of pages to display
+  std::list<PropSheetPage*> pages;
+  FormatPage formatPage(&info); pages.push_back(&formatPage);
+  InputsPage inputsPage(&info); pages.push_back(&inputsPage);
+  MiscPage miscPage(&info); pages.push_back(&miscPage);
+  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);
+
+  visible = false;
+  return dialog.changed;
+}
diff --git a/vncviewer/OptionsDialog.h b/vncviewer/OptionsDialog.h
new file mode 100644
index 0000000..eec9b96
--- /dev/null
+++ b/vncviewer/OptionsDialog.h
@@ -0,0 +1,49 @@
+/* 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.
+ */
+
+// -=- OptionsDialog.h
+
+// Options dialog for VNC Viewer 4.0
+
+#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 OptionsDialog {
+    public:
+      OptionsDialog();
+      virtual bool showDialog(CView* cfg, bool capture=false);
+
+      static OptionsDialog global;
+    protected:
+      bool visible;
+    };
+
+  };
+
+};
+
+#endif
diff --git a/vncviewer/UserPasswdDialog.cxx b/vncviewer/UserPasswdDialog.cxx
new file mode 100644
index 0000000..8ab4ba4
--- /dev/null
+++ b/vncviewer/UserPasswdDialog.cxx
@@ -0,0 +1,84 @@
+/* Copyright (C) 2002-2004 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+
+#include <vncviewer/UserPasswdDialog.h>
+#include <vncviewer/resource.h>
+
+using namespace rfb;
+using namespace rfb::win32;
+
+
+UserPasswdDialog::UserPasswdDialog() : Dialog(GetModuleHandle(0)), showUsername(false) {
+}
+
+
+void UserPasswdDialog::setCSecurity(const CSecurity* cs) {
+  description.replaceBuf(tstrDup(cs->description()));
+}
+
+bool UserPasswdDialog::showDialog() {
+  return Dialog::showDialog(MAKEINTRESOURCE(IDD_VNC_AUTH_DLG));
+}
+
+void UserPasswdDialog::initDialog() {
+  if (username.buf) {
+    setItemString(IDC_USERNAME, username.buf);
+    tstrFree(username.takeBuf());
+  }
+  if (password.buf) {
+    setItemString(IDC_PASSWORD, password.buf);
+    tstrFree(password.takeBuf());
+  }
+  if (!showUsername) {
+    setItemString(IDC_USERNAME, _T(""));
+    enableItem(IDC_USERNAME, false);
+  }
+  if (description.buf) {
+    TCharArray title(128);
+    GetWindowText(handle, title.buf, 128);
+    _tcsncat(title.buf, _T(" ["), 128);
+    _tcsncat(title.buf, description.buf, 128);
+    _tcsncat(title.buf, _T("]"), 128);
+    SetWindowText(handle, title.buf);
+  }
+}
+
+bool UserPasswdDialog::onOk() {
+	username.buf = getItemString(IDC_USERNAME);
+	password.buf = getItemString(IDC_PASSWORD);
+  return true;
+}
+
+
+bool UserPasswdDialog::getUserPasswd(char** user, char** passwd) {
+  bool result = false;
+  showUsername = user != 0;
+  if (user && *user)
+    username.buf = tstrDup(*user);
+  if (passwd && *passwd)
+    password.buf = tstrDup(*passwd);
+  if (showDialog()) {
+    if (user)
+      *user = strDup(username.buf);
+    *passwd = strDup(password.buf);
+    result = true;
+  }
+  tstrFree(username.takeBuf());
+  tstrFree(password.takeBuf());
+  return result;
+}
diff --git a/vncviewer/UserPasswdDialog.h b/vncviewer/UserPasswdDialog.h
new file mode 100644
index 0000000..998a49f
--- /dev/null
+++ b/vncviewer/UserPasswdDialog.h
@@ -0,0 +1,54 @@
+/* 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.
+ */
+
+// -=- UserPasswdDialog.h
+
+// Username and password dialog for VNC Viewer 4.0
+
+#ifndef __RFB_WIN32_USERPASSWD_DIALOG_H__
+#define __RFB_WIN32_USERPASSWD_DIALOG_H__
+
+#include <rfb_win32/Dialog.h>
+#include <rfb_win32/TCharArray.h>
+#include <rfb/CSecurity.h>
+#include <rfb/UserPasswdGetter.h>
+
+namespace rfb {
+
+  namespace win32 {
+
+    class UserPasswdDialog : Dialog, public UserPasswdGetter {
+    public:
+      UserPasswdDialog();
+      virtual bool showDialog();
+      virtual void initDialog();
+      virtual bool onOk();
+      virtual bool getUserPasswd(char** user, char** passwd);
+      void setCSecurity(const CSecurity* cs);
+    protected:
+      TCharArray username;
+      TCharArray password;
+      bool showUsername;
+      TCharArray description;
+    };
+
+  };
+
+};
+
+#endif
diff --git a/vncviewer/buildTime.cxx b/vncviewer/buildTime.cxx
new file mode 100644
index 0000000..bab2e13
--- /dev/null
+++ b/vncviewer/buildTime.cxx
@@ -0,0 +1 @@
+const char* buildTime = "Built on " __DATE__ " at " __TIME__;
\ No newline at end of file
diff --git a/vncviewer/cursor1.cur b/vncviewer/cursor1.cur
new file mode 100644
index 0000000..20a713f
--- /dev/null
+++ b/vncviewer/cursor1.cur
Binary files differ
diff --git a/vncviewer/cview.cxx b/vncviewer/cview.cxx
new file mode 100644
index 0000000..7d5653d
--- /dev/null
+++ b/vncviewer/cview.cxx
@@ -0,0 +1,1468 @@
+/* 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 = 1;
+const int IDM_SEND_MENU_KEY = 2;
+const int IDM_SEND_CAD = 3;
+const int IDM_ABOUT = 4;
+const int IDM_OPTIONS = 5;
+const int IDM_INFO = 6;
+const int IDM_NEWCONN = 7;
+const int IDM_REQUEST_REFRESH = 9;
+const int IDM_CTRL_KEY = 10;
+const int IDM_ALT_KEY = 11;
+
+const int TIMER_BUMPSCROLL = 1;
+const int TIMER_POINTER_INTERVAL = 2;
+const int TIMER_POINTER_3BUTTON = 3;
+
+
+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 = NULL;
+  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;
+
+
+//
+// -=- 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), requestUpdate(false), has_focus(false), palette_changed(false),
+    sameMachine(false), encodingChange(false), formatChange(false),
+    lastUsedEncoding_(encodingRaw), fullScreenActive(false),
+    bumpScroll(false), manager(0) {
+
+  // Create the window
+  const TCHAR* name = _T("VNC Viewer 4.0b");
+  hwnd = CreateWindow((const TCHAR*)baseClass.classAtom, name, WS_OVERLAPPEDWINDOW,
+    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);
+
+  // Initialise the CPointer pointer handler
+  ptr.setHWND(getHandle());
+  ptr.setIntervalTimerId(TIMER_POINTER_INTERVAL);
+  ptr.set3ButtonTimerId(TIMER_POINTER_3BUTTON);
+
+  // Initialise the bumpscroll timer
+  bumpScrollTimer.setHWND(getHandle());
+  bumpScrollTimer.setId(TIMER_BUMPSCROLL);
+
+  // Hook the clipboard
+  clipboard.setNotifier(this);
+
+  // Create the backing buffer
+  buffer = new win32::DIBSectionBuffer(getHandle());
+}
+
+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, 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_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;
+
+  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;
+  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.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;
+}
+
+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 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);
+
+    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
+    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(getHandle(), -delta.x, -delta.y, 0, 0, 0, 0, SW_INVALIDATE);
+    UpdateWindow(getHandle());
+    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_DISPLAYCHANGE:
+    // Display has changed - use new pixel format
+    calculateFullColourPF();
+    break;
+
+  case WM_PAINT:
+    {
+      PAINTSTRUCT ps;
+      HDC paintDC = BeginPaint(getHandle(), &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(getHandle(), &ps);
+
+      // - Request the next update from the server, if required
+      requestNewUpdate();
+    }
+    return 0;
+
+    // -=- Palette management
+
+  case WM_PALETTECHANGED:
+    vlog.debug("WM_PALETTECHANGED");
+    if ((HWND)wParam == getHandle()) {
+      vlog.debug("ignoring");
+      break;
+    }
+  case WM_QUERYNEWPALETTE:
+    vlog.debug("re-selecting palette");
+    {
+      WindowDC wdc(getHandle());
+      PaletteSelector pSel(wdc, windowPalette.getHandle());
+      if (pSel.isRedrawRequired()) {
+        InvalidateRect(getHandle(), 0, FALSE);
+        UpdateWindow(getHandle());
+      }
+    }
+    return TRUE;
+
+    // -=- 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(getHandle(), GWL_STYLE);
+      DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL);
+      RECT r;
+      SetRect(&r, 0, 0, buffer->width(), buffer->height());
+      AdjustWindowRect(&r, style, FALSE);
+      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);
+      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;
+
+    // Add scrollbars if required and update window size info we have cached.
+
+  case WM_SIZE:
+    {
+      Point old_offset = bufferToClient(Point(0, 0));
+
+      // Update the cached sizing information
+      RECT r;
+      GetWindowRect(getHandle(), &r);
+      window_size = Rect(r.left, r.top, r.right, r.bottom);
+      GetClientRect(getHandle(), &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(getHandle(), 0, TRUE);
+    }
+    break;
+
+  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(getHandle(), (msg == WM_VSCROLL) ? SB_VERT : SB_HORZ, &si, 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;
+
+    // -=- 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 = hwnd;
+        _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;
+
+    // -=- Track whether or not the window has focus
+
+  case WM_SETFOCUS:
+    has_focus = true;
+    break;
+  case WM_KILLFOCUS:
+    has_focus = false;
+    cursorOutsideBuffer();
+    // 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_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_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;
+    };
+
+    // - 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_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;
+      }
+    }
+	case WM_SYSKEYDOWN:
+	case WM_SYSKEYUP:
+    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);
+}
+
+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);
+  }
+}
+
+
+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(getHandle(), &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};
+    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(getHandle(), 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();
+}
+
+// autoSelectFormatAndEncoding() chooses the format and encoding appropriate
+// to the connection speed:
+//   Above 16Mbps (timing for at least a second), same machine, switch to raw
+//   Above 3Mbps, switch to hextile
+//   Below 1.5Mbps, switch to ZRLE
+//   Above 1Mbps, switch to full colour mode
+void
+CView::autoSelectFormatAndEncoding() {
+  int kbitsPerSecond = sock->inStream().kbitsPerSecond();
+  unsigned int newEncoding = options.preferredEncoding;
+
+  if (kbitsPerSecond > 16000 && sameMachine &&
+      sock->inStream().timeWaited() >= 10000) {
+    newEncoding = encodingRaw;
+  } else if (kbitsPerSecond > 3000) {
+    newEncoding = encodingHextile;
+  } else if (kbitsPerSecond < 1500) {
+    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 > 1000) {
+    if (!options.fullColour) {
+      vlog.info("Throughput %d kbit/s - changing to full colour",
+                kbitsPerSecond);
+      options.fullColour = true;
+      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(getHandle(), GWL_STYLE);
+  DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL);
+  DWORD old_style;
+  RECT r;
+  SetRect(&r, 0, 0, buffer->width(), buffer->height());
+  AdjustWindowRect(&r, style, FALSE);
+  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(getHandle(), GWL_STYLE, style);
+    SetWindowPos(getHandle(), 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(getHandle(), 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(getHandle(), SB_HORZ, &si, TRUE);
+  }
+}
+
+
+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();
+
+  // 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.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;
+}
+
diff --git a/vncviewer/cview.h b/vncviewer/cview.h
new file mode 100644
index 0000000..2bee1c4
--- /dev/null
+++ b/vncviewer/cview.h
@@ -0,0 +1,296 @@
+/* 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/CViewOptions.h>
+#include <vncviewer/CViewManager.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);
+
+      // -=- 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;}
+
+      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;
+
+    protected:
+
+      // 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 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;
+
+      // 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;
+
+      // Handle back to CViewManager instance, if any
+      CViewManager* manager;
+
+    };
+
+  };
+
+};
+
+#endif
+
+
diff --git a/vncviewer/msvcwarning.h b/vncviewer/msvcwarning.h
new file mode 100644
index 0000000..53a0678
--- /dev/null
+++ b/vncviewer/msvcwarning.h
@@ -0,0 +1,19 @@
+/* Copyright (C) 2002-2003 RealVNC Ltd.  All Rights Reserved.
+ *    
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ * USA.
+ */
+#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false'
+#pragma warning( disable : 4786 ) // debug identifier truncated
diff --git a/vncviewer/resource.h b/vncviewer/resource.h
new file mode 100644
index 0000000..351a2b0
--- /dev/null
+++ b/vncviewer/resource.h
@@ -0,0 +1,80 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by vncviewer.rc
+//
+#define IDR_MANIFEST                    1
+#define IDI_ICON                        101
+#define IDD_VNC_AUTH_DLG                102
+#define IDD_CONNECTING_DLG              103
+#define IDD_CONNECTION_DLG              104
+#define IDC_DOT_CURSOR                  105
+#define IDD_ABOUT                       107
+#define IDD_FORMAT                      108
+#define IDD_MISC                        109
+#define IDD_INPUTS                      110
+#define IDD_SERVER_KEYS                 111
+#define IDR_TRAY                        112
+#define IDD_CONNECTION_INFO             113
+#define IDD_DEFAULTS                    116
+#define IDC_PASSWORD                    1000
+#define IDC_CONNECTING_TEXT             1001
+#define IDC_SERVER_EDIT                 1002
+#define IDC_USERNAME                    1005
+#define IDC_VERSION                     1008
+#define IDC_BUILDTIME                   1009
+#define IDC_ENCODING_AUTO               1010
+#define IDC_FORMAT_FULLCOLOUR           1011
+#define IDC_ENCODING_ZRLE               1012
+#define IDC_ENCODING_HEXTILE            1013
+#define IDC_CONN_SHARED                 1013
+#define IDC_ENCODING_RAW                1014
+#define IDC_FULL_SCREEN                 1014
+#define IDC_SEND_POINTER                1015
+#define IDC_SEND_KEYS                   1016
+#define IDC_CLIENT_CUTTEXT              1017
+#define IDC_SERVER_CUTTEXT              1018
+#define IDC_LOCAL_CURSOR                1019
+#define IDC_DESKTOP_RESIZE              1020
+#define IDC_COPYRIGHT                   1021
+#define IDC_DESCRIPTION                 1022
+#define IDC_OPTIONS                     1023
+#define IDC_ABOUT                       1024
+#define IDC_LIST1                       1025
+#define IDC_INFO_NAME                   1026
+#define IDC_INFO_HOST                   1027
+#define IDC_INFO_SIZE                   1028
+#define IDC_INFO_PF                     1029
+#define IDC_INFO_DEF_PF                 1030
+#define IDC_INFO_LINESPEED              1031
+#define IDC_INFO_VERSION                1032
+#define IDC_PROTOCOL_3_3                1034
+#define IDC_ACCEPT_BELL                 1035
+#define IDC_FORMAT_VERYLOWCOLOUR        1036
+#define IDC_FORMAT_LOWCOLOUR            1037
+#define IDC_FORMAT_MEDIUMCOLOUR         1038
+#define IDC_LOAD_DEFAULTS               1040
+#define IDC_SAVE_DEFAULTS               1041
+#define IDC_LOAD_CONFIG                 1042
+#define IDC_EMULATE3                    1043
+#define IDC_POINTER_INTERVAL            1044
+#define IDC_SAVE_CONFIG                 1045
+#define IDC_INFO_SECURITY               1046
+#define IDC_SAVE_CONFIG_AS              1048
+#define IDC_MENU_KEY                    1051
+#define IDC_REQUESTED_ENCODING          1052
+#define IDC_LAST_ENCODING               1053
+#define ID_CLOSE                        40002
+#define ID_OPTIONS                      40003
+#define ID_NEW_CONNECTION               40004
+#define ID_ABOUT                        40005
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        120
+#define _APS_NEXT_COMMAND_VALUE         40006
+#define _APS_NEXT_CONTROL_VALUE         1054
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/vncviewer/vncviewer.cxx b/vncviewer/vncviewer.cxx
new file mode 100644
index 0000000..59e2d00
--- /dev/null
+++ b/vncviewer/vncviewer.cxx
@@ -0,0 +1,370 @@
+/* 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.
+ */
+
+// -=- VNC Viewer for Win32
+
+#include <string.h>
+#ifdef WIN32
+#define strcasecmp _stricmp
+#endif
+#include <list>
+
+#include <vncviewer/resource.h>
+#include <vncviewer/CViewManager.h>
+#include <vncviewer/CView.h>
+#include <vncviewer/OptionsDialog.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>
+
+#ifdef _DIALOG_CAPTURE
+#include <extra/LoadBMP.h>
+#endif
+
+using namespace rfb;
+using namespace rfb::win32;
+using namespace rdr;
+using namespace network;
+
+static LogWriter vlog("main");
+
+TStr rfb::win32::AppName("VNC Viewer");
+
+
+#ifdef _DIALOG_CAPTURE
+BoolParameter captureDialogs("CaptureDialogs", "", false);
+#endif
+
+//
+// -=- Listener
+//     Class to handle listening on a particular port for incoming connections
+//     from servers, and spawning of clients
+//
+
+static BoolParameter acceptIncoming("Listen", "Accept incoming connections from VNC servers.", false);
+
+
+//
+// -=- AboutDialog global values
+//
+
+const WORD rfb::win32::AboutDialog::DialogId = IDD_ABOUT;
+const WORD rfb::win32::AboutDialog::Copyright = IDC_COPYRIGHT;
+const WORD rfb::win32::AboutDialog::Version = IDC_VERSION;
+const WORD rfb::win32::AboutDialog::BuildTime = IDC_BUILDTIME;
+const WORD rfb::win32::AboutDialog::Description = IDC_DESCRIPTION;
+
+
+//
+// -=- 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.
+//
+
+void
+programInfo() {
+  win32::FileVersionInfo inf;
+  _tprintf(_T("%s - %s, Version %s\n"),
+    inf.getVerString(_T("ProductName")),
+    inf.getVerString(_T("FileDescription")),
+    inf.getVerString(_T("FileVersion")));
+  printf("%s\n", buildTime);
+  _tprintf(_T("%s\n\n"), inf.getVerString(_T("LegalCopyright")));
+}
+
+void
+programUsage() {
+  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("  -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");
+  printf("\nLog names:\n");
+  LogWriter::listLogWriters();
+  printf("\nLog destinations:\n");
+  Logger::listLoggers();
+  printf("\nParameters:\n");
+  Configuration::listParams();
+}
+
+
+bool print_usage = false;
+bool close_console = true;
+std::list<char*> hosts;
+std::list<char*> configFiles;
+
+void
+processParams(int argc, char* argv[]) {
+  for (int i=1; i<argc; i++) {
+    try {
+
+      if (strcasecmp(argv[i], "-console") == 0) {
+        close_console = false;
+
+      } else if (((strcasecmp(argv[i], "-config") == 0) ||
+                  (strcasecmp(argv[i], "/config") == 0)) && (i < argc-1)) {
+        configFiles.push_back(strDup(argv[i+1]));
+        i++;
+
+      } else if ((strcasecmp(argv[i], "-help") == 0) ||
+                 (strcasecmp(argv[i], "--help") == 0) ||
+                 (strcasecmp(argv[i], "-h") == 0) ||
+                 (strcasecmp(argv[i], "/?") == 0)) {
+        print_usage = true;
+        close_console = false;
+        break;
+
+      } else {
+        // Try to process <option>=<value>, or -<bool>
+        if (Configuration::setParam(argv[i], true))
+          continue;
+        // Try to process -<option> <value>
+        if ((argv[i][0] == '-') && (i+1 < argc)) {
+          if (Configuration::setParam(&argv[i][1], argv[i+1], true)) {
+            i++;
+            continue;
+          }
+        }
+        // If it's -<option> then it's not recognised - error
+        // If it's <host> then add it to the list to connect to.
+        if ((argv[i][0] == '-') || (argv[i][0] == '/')) {
+          const char* fmt = "The option %s was not recognized.  Use -help to see VNC Viewer usage";
+          CharArray tmp(strlen(argv[i])+strlen(fmt)+1);
+          sprintf(tmp.buf, fmt, argv[i]);
+          MsgBox(0, TStr(tmp.buf), MB_ICONSTOP | MB_OK);
+          exit(1);
+        } else {
+          hosts.push_back(strDup(argv[i]));
+        }
+      }
+
+    } catch (rdr::Exception& e) {
+      vlog.error(e.str());
+    }
+  }
+}
+
+
+//
+// -=- main
+//
+
+int WINAPI WinMain(HINSTANCE inst, HINSTANCE prevInst, char* cmdLine, int cmdShow) {
+
+  try {
+
+    // - Initialise the available loggers
+    initStdIOLoggers();
+    initFileLogger("C:\\temp\\vncviewer4.log");
+
+    // - By default, just log errors to stderr
+    logParams.setDefault("*:stderr:0");
+
+    // - Process the command-line
+    int argc = __argc;
+    char** argv = __argv;
+    processParams(argc, argv);
+
+    // - By default the console will be closed
+    if (close_console) {
+      if (!FreeConsole())
+        vlog.info("unable to close console:%u", GetLastError());
+    } else {
+      AllocConsole();
+      freopen("CONIN$","rb",stdin);
+      freopen("CONOUT$","wb",stdout);
+      freopen("CONOUT$","wb",stderr);
+      setbuf(stderr, 0);
+    }
+
+#ifdef _DIALOG_CAPTURE
+    if (captureDialogs) {
+      CView::userConfigKey.openKey(HKEY_CURRENT_USER, _T("Software\\RealVNC\\VNCViewer4"));
+      OptionsDialog::global.showDialog(0, true);
+      return 0;
+    }
+#endif
+
+    // - If no clients are specified, bring up a connection dialog
+    if (configFiles.empty() && hosts.empty() && !acceptIncoming && !print_usage)
+      hosts.push_back(0);
+
+    programInfo();
+
+    // - 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\\RealVNC\\VNCViewer4"));
+
+      // - Tell the rest of VNC Viewer where to write config data to
+      CView::userConfigKey.openKey(HKEY_CURRENT_USER, _T("Software\\RealVNC\\VNCViewer4"));
+
+      // - Start the Socket subsystem for TCP
+      TcpSocket::initTcpSockets();
+
+      // Create the client connection manager
+      CViewManager view_manager;
+
+      if (acceptIncoming) {
+        int port = 5500;
+
+        // Listening viewer
+        if (hosts.size() > 1) {
+          programUsage();
+          exit(2);
+        }
+        if (!hosts.empty()) {
+          port = atoi(hosts.front());  
+        }
+
+        vlog.debug("opening listener");
+
+        CViewTrayIcon tray(view_manager);
+
+        view_manager.addDefaultTCPListener(port);
+
+        // Run the view manager
+        // Also processes the tray icon if necessary
+        MSG msg;
+        while (GetMessage(&msg, NULL, 0, 0) > 0) {
+          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);
+          strFree(filename);
+          configFiles.pop_front();
+        }
+
+        // Connect to each client in turn
+        while (!hosts.empty()) {
+          char* hostinfo = hosts.front();
+          view_manager.addClient(hostinfo);
+          strFree(hostinfo);
+          hosts.pop_front();
+        }
+
+        // Run the view manager
+        MSG msg;
+        while (GetMessage(&msg, NULL, 0, 0) > 0) {
+          TranslateMessage(&msg);
+          DispatchMessage(&msg);
+        }
+
+        vlog.debug("quitting viewer");
+      }
+
+    }
+
+    // - If necessary, print the program's usage info
+    if (print_usage)
+      programUsage();
+
+    if (!close_console) {
+      printf("Press Enter/Return key to continue\n");
+      char c = getchar();
+    }
+
+  } catch (rdr::Exception& e) {
+    MsgBox(0, TStr(e.str()), MB_ICONSTOP | MB_OK);
+  }
+
+  return 0;
+}
diff --git a/vncviewer/vncviewer.dsp b/vncviewer/vncviewer.dsp
new file mode 100644
index 0000000..25d358f
--- /dev/null
+++ b/vncviewer/vncviewer.dsp
@@ -0,0 +1,229 @@
+# Microsoft Developer Studio Project File - Name="vncviewer" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+CFG=vncviewer - Win32 Debug Unicode
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "vncviewer.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "vncviewer.mak" CFG="vncviewer - Win32 Debug Unicode"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "vncviewer - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "vncviewer - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE "vncviewer - Win32 Debug Unicode" (based on "Win32 (x86) Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "vncviewer - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\Release"
+# PROP Intermediate_Dir "Release"
+# PROP 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 BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
+# ADD LINK32 user32.lib gdi32.lib advapi32.lib ws2_32.lib version.lib comctl32.lib shell32.lib comdlg32.lib /nologo /subsystem:windows /machine:I386
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PreLink_Desc=Updating buildTime
+PreLink_Cmds=cl /c /nologo /FoRelease\ /FdRelease\ /MT buildTime.cxx
+# End Special Build Tool
+
+!ELSEIF  "$(CFG)" == "vncviewer - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP 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" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 user32.lib gdi32.lib advapi32.lib ws2_32.lib version.lib comctl32.lib shell32.lib comdlg32.lib /nologo /subsystem:windows /incremental:no /debug /machine:I386 /pdbtype:sept
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PreLink_Desc=Updating buildTime
+PreLink_Cmds=cl /c /nologo /FoDebug\ /FdDebug\ /MTd buildTime.cxx
+# End Special Build Tool
+
+!ELSEIF  "$(CFG)" == "vncviewer - Win32 Debug Unicode"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "vncviewer___Win32_Debug_Unicode"
+# PROP BASE Intermediate_Dir "vncviewer___Win32_Debug_Unicode"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\Debug_Unicode"
+# PROP Intermediate_Dir "Debug_Unicode"
+# 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 BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 user32.lib gdi32.lib advapi32.lib ws2_32.lib version.lib comctl32.lib shell32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 user32.lib gdi32.lib advapi32.lib ws2_32.lib version.lib comctl32.lib shell32.lib comdlg32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PreLink_Desc=Updating buildTime
+PreLink_Cmds=cl /c /nologo /FoDebug_Unicode\ /FdDebug_Unicode\ /MTd buildTime.cxx
+# End Special Build Tool
+
+!ENDIF 
+
+# Begin Target
+
+# Name "vncviewer - Win32 Release"
+# Name "vncviewer - Win32 Debug"
+# Name "vncviewer - Win32 Debug Unicode"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\buildTime.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
+# End Source File
+# Begin Source File
+
+SOURCE=.\InfoDialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\OptionsDialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\UserPasswdDialog.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncviewer.cxx
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncviewer.rc
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\ConnectingDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ConnectionDialog.h
+# 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
+# End Source File
+# Begin Source File
+
+SOURCE=.\InfoDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MRU.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\OptionsDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\UserPasswdDialog.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\cursor1.cur
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncviewer.exe.manifest
+# End Source File
+# Begin Source File
+
+SOURCE=.\vncviewer.ico
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/vncviewer/vncviewer.exe.manifest b/vncviewer/vncviewer.exe.manifest
new file mode 100644
index 0000000..557e456
--- /dev/null
+++ b/vncviewer/vncviewer.exe.manifest
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<assemblyIdentity
+   version="4.0.0.26"
+   processorArchitecture="X86"
+   name="RealVNC.vncviewer.exe"
+   type="win32"
+/>
+<description>.NET control deployment tool</description>
+<dependency>
+   <dependentAssembly>
+     <assemblyIdentity
+       type="win32"
+       name="Microsoft.Windows.Common-Controls"
+       version="6.0.0.0"
+       processorArchitecture="X86"
+       publicKeyToken="6595b64144ccf1df"
+       language="*"
+     />
+   </dependentAssembly>
+</dependency>
+</assembly>
diff --git a/vncviewer/vncviewer.ico b/vncviewer/vncviewer.ico
new file mode 100644
index 0000000..98e5578
--- /dev/null
+++ b/vncviewer/vncviewer.ico
Binary files differ
diff --git a/vncviewer/vncviewer.rc b/vncviewer/vncviewer.rc
new file mode 100644
index 0000000..bd9ab6d
--- /dev/null
+++ b/vncviewer/vncviewer.rc
@@ -0,0 +1,500 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.K.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+#pragma code_page(1252)
+#endif //_WIN32
+
+#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
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_ICON                ICON    DISCARDABLE     "vncviewer.ico"
+
+#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", "RealVNC Ltd.\0"
+            VALUE "FileDescription", "VNC Viewer for Win32\0"
+            VALUE "FileVersion", "4.0\0"
+            VALUE "InternalName", "VNCViewer 4.0\0"
+            VALUE "LegalCopyright", "Copyright © RealVNC Ltd. 2002-2004\0"
+            VALUE "LegalTrademarks", "RealVNC\0"
+            VALUE "OriginalFilename", "vncviewer.exe\0"
+            VALUE "PrivateBuild", "\0"
+            VALUE "ProductName", "VNC 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
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_VNC_AUTH_DLG DIALOG DISCARDABLE  0, 0, 241, 46
+STYLE DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION
+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 | 
+                    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
+END
+
+IDD_CONNECTING_DLG DIALOG DISCARDABLE  0, 0, 185, 47
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION
+CAPTION "VNC Viewer : Connecting"
+FONT 8, "MS Sans Serif"
+BEGIN
+    PUSHBUTTON      "Cancel",IDCANCEL,128,26,50,14,WS_DISABLED
+    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
+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 | 
+                    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
+END
+
+IDD_ABOUT DIALOG DISCARDABLE  0, 0, 249, 92
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "About VNC Viewer for Windows"
+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
+    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.realvnc.com for more information on VNC.",
+                    IDC_STATIC,40,55,202,15
+END
+
+IDD_FORMAT DIALOG DISCARDABLE  0, 0, 201, 101
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Colour/Encoding"
+FONT 8, "MS Sans Serif"
+BEGIN
+    CONTROL         "&Auto select",IDC_ENCODING_AUTO,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,7,7,88,13
+    GROUPBOX        "Preferred encoding",IDC_STATIC,7,25,83,60
+    CONTROL         "ZRLE",IDC_ENCODING_ZRLE,"Button",BS_AUTORADIOBUTTON | 
+                    WS_GROUP,10,35,75,14
+    CONTROL         "Hextile",IDC_ENCODING_HEXTILE,"Button",
+                    BS_AUTORADIOBUTTON,10,49,75,16
+    CONTROL         "Raw",IDC_ENCODING_RAW,"Button",BS_AUTORADIOBUTTON,10,65,
+                    75,15
+    GROUPBOX        "Colour level",IDC_STATIC,95,10,99,75
+    CONTROL         "&Full (all available colours)",IDC_FORMAT_FULLCOLOUR,
+                    "Button",BS_AUTORADIOBUTTON | WS_GROUP,100,20,90,15
+    CONTROL         "&Medium (256 colours)",IDC_FORMAT_MEDIUMCOLOUR,"Button",
+                    BS_AUTORADIOBUTTON,100,35,90,14
+    CONTROL         "&Low (64 colours)",IDC_FORMAT_LOWCOLOUR,"Button",
+                    BS_AUTORADIOBUTTON,100,49,90,16
+    CONTROL         "&Very low (8 colours)",IDC_FORMAT_VERYLOWCOLOUR,"Button",
+                    BS_AUTORADIOBUTTON,100,65,90,15
+END
+
+IDD_MISC DIALOG DISCARDABLE  0, 0, 213, 137
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Misc"
+FONT 8, "MS Sans Serif"
+BEGIN
+    CONTROL         "Shared connection (do not disconnect other viewers)",
+                    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
+    CONTROL         "Render cursor locally",IDC_LOCAL_CURSOR,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,7,40,199,15
+    CONTROL         "Allow dynamic desktop resizing",IDC_DESKTOP_RESIZE,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,55,199,15
+    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,86,199,15
+END
+
+IDD_INPUTS DIALOG DISCARDABLE  0, 0, 186, 138
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Inputs"
+FONT 8, "MS Sans Serif"
+BEGIN
+    CONTROL         "Send pointer events to server",IDC_SEND_POINTER,"Button",
+                    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 clipboard changes to server",IDC_CLIENT_CUTTEXT,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,40,172,15
+    CONTROL         "Accept clipboard changes from server",
+                    IDC_SERVER_CUTTEXT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    7,55,172,15
+    CONTROL         "Enable 3-button mouse emulation",IDC_EMULATE3,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,7,70,172,15
+    CONTROL         "Rate-limit mouse move events",IDC_POINTER_INTERVAL,
+                    "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
+END
+
+IDD_CONNECTION_INFO DIALOG DISCARDABLE  0, 0, 239, 186
+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
+    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
+    LTEXT           "Pixel Format:",IDC_STATIC,7,55,73,15
+    LTEXT           "Server Default:",IDC_STATIC,7,70,73,15
+    LTEXT           "Line Speed Estimate:",IDC_STATIC,7,115,73,15
+    LTEXT           "Protocol Version:",IDC_STATIC,7,130,73,15
+    LTEXT           "",IDC_INFO_NAME,80,10,152,15
+    LTEXT           "",IDC_INFO_HOST,80,25,152,15
+    LTEXT           "",IDC_INFO_SIZE,80,40,152,15
+    LTEXT           "",IDC_INFO_PF,80,55,152,15
+    LTEXT           "",IDC_INFO_DEF_PF,80,70,152,15
+    LTEXT           "",IDC_INFO_LINESPEED,80,115,152,15
+    LTEXT           "",IDC_INFO_VERSION,80,130,152,15
+    LTEXT           "Security Method:",IDC_STATIC,7,145,73,15
+    LTEXT           "",IDC_INFO_SECURITY,80,145,152,15
+    LTEXT           "Requested Encoding:",IDC_STATIC,7,85,73,15
+    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
+END
+
+IDD_DEFAULTS DIALOG DISCARDABLE  0, 0, 131, 113
+STYLE DS_MODALFRAME | DS_CONTROL | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Defaults"
+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
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE 
+BEGIN
+    IDD_VNC_AUTH_DLG, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 234
+        VERTGUIDE, 35
+        VERTGUIDE, 70
+        VERTGUIDE, 75
+        TOPMARGIN, 6
+        BOTTOMMARGIN, 40
+        HORZGUIDE, 20
+        HORZGUIDE, 25
+        HORZGUIDE, 40
+    END
+
+    IDD_CONNECTING_DLG, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 178
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 40
+        HORZGUIDE, 21
+        HORZGUIDE, 26
+    END
+
+    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
+        TOPMARGIN, 6
+        BOTTOMMARGIN, 47
+        HORZGUIDE, 20
+        HORZGUIDE, 30
+        HORZGUIDE, 40
+    END
+
+    IDD_ABOUT, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 242
+        VERTGUIDE, 40
+        VERTGUIDE, 165
+        VERTGUIDE, 195
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 85
+        HORZGUIDE, 10
+        HORZGUIDE, 25
+        HORZGUIDE, 40
+        HORZGUIDE, 55
+        HORZGUIDE, 70
+    END
+
+    IDD_FORMAT, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 194
+        VERTGUIDE, 10
+        VERTGUIDE, 85
+        VERTGUIDE, 90
+        VERTGUIDE, 95
+        VERTGUIDE, 100
+        VERTGUIDE, 105
+        VERTGUIDE, 190
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 94
+        HORZGUIDE, 10
+        HORZGUIDE, 20
+        HORZGUIDE, 25
+        HORZGUIDE, 35
+        HORZGUIDE, 49
+        HORZGUIDE, 65
+        HORZGUIDE, 80
+        HORZGUIDE, 85
+    END
+
+    IDD_MISC, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 206
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 130
+        HORZGUIDE, 10
+        HORZGUIDE, 25
+        HORZGUIDE, 40
+        HORZGUIDE, 55
+        HORZGUIDE, 70
+        HORZGUIDE, 85
+        HORZGUIDE, 100
+    END
+
+    IDD_INPUTS, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 179
+        VERTGUIDE, 105
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 131
+        HORZGUIDE, 10
+        HORZGUIDE, 25
+        HORZGUIDE, 40
+        HORZGUIDE, 55
+        HORZGUIDE, 70
+        HORZGUIDE, 85
+        HORZGUIDE, 100
+        HORZGUIDE, 115
+    END
+
+    IDD_CONNECTION_INFO, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 232
+        VERTGUIDE, 80
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 179
+        HORZGUIDE, 10
+        HORZGUIDE, 25
+        HORZGUIDE, 40
+        HORZGUIDE, 55
+        HORZGUIDE, 70
+        HORZGUIDE, 85
+        HORZGUIDE, 100
+        HORZGUIDE, 115
+        HORZGUIDE, 130
+        HORZGUIDE, 145
+        HORZGUIDE, 160
+    END
+
+    IDD_DEFAULTS, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 124
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 106
+        HORZGUIDE, 10
+        HORZGUIDE, 25
+        HORZGUIDE, 30
+        HORZGUIDE, 45
+        HORZGUIDE, 50
+        HORZGUIDE, 65
+        HORZGUIDE, 70
+        HORZGUIDE, 85
+        HORZGUIDE, 90
+        HORZGUIDE, 105
+    END
+END
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Cursor
+//
+
+IDC_DOT_CURSOR          CURSOR  DISCARDABLE     "cursor1.cur"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog Info
+//
+
+IDD_CONNECTION_DLG DLGINIT
+BEGIN
+    IDC_SERVER_EDIT, 0x403, 16, 0
+0x796d, 0x616d, 0x6863, 0x6e69, 0x2e65, 0x726f, 0x3a67, 0x0031, 
+    0
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_TRAY MENU DISCARDABLE 
+BEGIN
+    POPUP "Tray Menu"
+    BEGIN
+        MENUITEM "&New Connection...",          ID_NEW_CONNECTION
+        MENUITEM SEPARATOR
+        MENUITEM "Default &Options...",         ID_OPTIONS
+        MENUITEM SEPARATOR
+        MENUITEM "&Close Daemon",               ID_CLOSE
+        MENUITEM "&About...",                   ID_ABOUT
+    END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// 24
+//
+
+IDR_MANIFEST            24      DISCARDABLE     "vncviewer.exe.manifest"
+#endif    // English (U.K.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+