diff options
author | Constantin Kaplinsky <const@tightvnc.com> | 2004-10-08 09:43:57 +0000 |
---|---|---|
committer | Constantin Kaplinsky <const@tightvnc.com> | 2004-10-08 09:43:57 +0000 |
commit | 47ed8d321c32c6b741cff1f4ff686165c4f269f4 (patch) | |
tree | da413648adbff4ff10c8ee26124673f8e7cf238a /vncviewer | |
parent | 266bb36cd47555280fffd3aab1ed86683e26d748 (diff) | |
download | tigervnc-47ed8d321c32c6b741cff1f4ff686165c4f269f4.tar.gz tigervnc-47ed8d321c32c6b741cff1f4ff686165c4f269f4.zip |
Initial revision
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@2 3789f03b-4d11-0410-bbf8-ca57d06f2519
Diffstat (limited to 'vncviewer')
-rw-r--r-- | vncviewer/CViewManager.cxx | 252 | ||||
-rw-r--r-- | vncviewer/CViewManager.h | 64 | ||||
-rw-r--r-- | vncviewer/CViewOptions.cxx | 364 | ||||
-rw-r--r-- | vncviewer/CViewOptions.h | 87 | ||||
-rw-r--r-- | vncviewer/ConnectingDialog.h | 95 | ||||
-rw-r--r-- | vncviewer/ConnectionDialog.cxx | 70 | ||||
-rw-r--r-- | vncviewer/ConnectionDialog.h | 52 | ||||
-rw-r--r-- | vncviewer/InfoDialog.cxx | 65 | ||||
-rw-r--r-- | vncviewer/InfoDialog.h | 48 | ||||
-rw-r--r-- | vncviewer/MRU.h | 140 | ||||
-rw-r--r-- | vncviewer/OptionsDialog.cxx | 309 | ||||
-rw-r--r-- | vncviewer/OptionsDialog.h | 49 | ||||
-rw-r--r-- | vncviewer/UserPasswdDialog.cxx | 84 | ||||
-rw-r--r-- | vncviewer/UserPasswdDialog.h | 54 | ||||
-rw-r--r-- | vncviewer/buildTime.cxx | 1 | ||||
-rw-r--r-- | vncviewer/cursor1.cur | bin | 0 -> 326 bytes | |||
-rw-r--r-- | vncviewer/cview.cxx | 1468 | ||||
-rw-r--r-- | vncviewer/cview.h | 296 | ||||
-rw-r--r-- | vncviewer/msvcwarning.h | 19 | ||||
-rw-r--r-- | vncviewer/resource.h | 80 | ||||
-rw-r--r-- | vncviewer/vncviewer.cxx | 370 | ||||
-rw-r--r-- | vncviewer/vncviewer.dsp | 229 | ||||
-rw-r--r-- | vncviewer/vncviewer.exe.manifest | 22 | ||||
-rw-r--r-- | vncviewer/vncviewer.ico | bin | 0 -> 8478 bytes | |||
-rw-r--r-- | vncviewer/vncviewer.rc | 500 |
25 files changed, 4718 insertions, 0 deletions
diff --git a/vncviewer/CViewManager.cxx b/vncviewer/CViewManager.cxx new file mode 100644 index 00000000..09ed1fe8 --- /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 00000000..3d11dd96 --- /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 00000000..089c4c43 --- /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], ']', §ion.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 00000000..9120bde2 --- /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 00000000..b146ced6 --- /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 00000000..c083444c --- /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 00000000..554c86fb --- /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 00000000..0d2313a5 --- /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 00000000..7a64d383 --- /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 00000000..9e993956 --- /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 00000000..ab45f8ce --- /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 00000000..eec9b96a --- /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 00000000..8ab4ba4c --- /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 00000000..998a49f1 --- /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 00000000..bab2e137 --- /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 Binary files differnew file mode 100644 index 00000000..20a713f7 --- /dev/null +++ b/vncviewer/cursor1.cur diff --git a/vncviewer/cview.cxx b/vncviewer/cview.cxx new file mode 100644 index 00000000..7d5653df --- /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(), ¤t); + + // 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 00000000..2bee1c48 --- /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 00000000..53a0678d --- /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 00000000..351a2b0a --- /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 00000000..59e2d00b --- /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 00000000..25d358f8 --- /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 00000000..557e4563 --- /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 Binary files differnew file mode 100644 index 00000000..98e5578d --- /dev/null +++ b/vncviewer/vncviewer.ico diff --git a/vncviewer/vncviewer.rc b/vncviewer/vncviewer.rc new file mode 100644 index 00000000..bd9ab6d0 --- /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 + |