From: Constantin Kaplinsky Date: Tue, 16 May 2006 08:48:31 +0000 (+0000) Subject: Win32 vncviewer merged with VNC 4.1.1 code. The merge is incomplete - the code compil... X-Git-Tag: v0.0.90~384^2~312^2~6 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=ac30668d41335ff9fb606332888a6ec0df83f3ce;p=tigervnc.git Win32 vncviewer merged with VNC 4.1.1 code. The merge is incomplete - the code compiles but does not always work properly yet. git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/branches/merge-with-vnc-4.1.1@552 3789f03b-4d11-0410-bbf8-ca57d06f2519 --- diff --git a/vncviewer/CConn.cxx b/vncviewer/CConn.cxx new file mode 100644 index 00000000..2d90730a --- /dev/null +++ b/vncviewer/CConn.cxx @@ -0,0 +1,708 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace rfb; +using namespace rfb::win32; +using namespace rdr; + +// - Statics & consts + +static LogWriter vlog("CConn"); + + +const int IDM_FULLSCREEN = ID_FULLSCREEN; +const int IDM_SEND_MENU_KEY = ID_SEND_MENU_KEY; +const int IDM_SEND_CAD = ID_SEND_CAD; +const int IDM_SEND_CTLESC = ID_SEND_CTLESC; +const int IDM_ABOUT = ID_ABOUT; +const int IDM_OPTIONS = ID_OPTIONS; +const int IDM_INFO = ID_INFO; +const int IDM_NEWCONN = ID_NEW_CONNECTION; +const int IDM_REQUEST_REFRESH = ID_REQUEST_REFRESH; +const int IDM_CTRL_KEY = ID_CTRL_KEY; +const int IDM_ALT_KEY = ID_ALT_KEY; +const int IDM_FILE_TRANSFER = ID_FILE_TRANSFER; +const int IDM_CONN_SAVE_AS = ID_CONN_SAVE_AS; + + +static IntParameter debugDelay("DebugDelay","Milliseconds to display inverted " + "pixel data - a debugging feature", 0); + + +// +// -=- CConn implementation +// + +RegKey CConn::userConfigKey; + + +CConn::CConn() + : window(0), sock(0), sockEvent(CreateEvent(0, TRUE, FALSE, 0)), requestUpdate(false), + sameMachine(false), encodingChange(false), formatChange(false), + reverseConnection(false), lastUsedEncoding_(encodingRaw), isClosed_(false) { +} + +CConn::~CConn() { + delete window; +} + +bool CConn::initialise(network::Socket* s, bool reverse) { + // Set the server's name for MRU purposes + CharArray endpoint(s->getPeerEndpoint()); + setServerName(endpoint.buf); + if (!options.host.buf) + options.setHost(endpoint.buf); + + // Initialise the underlying CConnection + setStreams(&s->inStream(), &s->outStream()); + + // Enable processing of window messages while blocked on I/O + s->inStream().setBlockCallback(this); + + // Initialise the viewer options + applyOptions(options); + + // - Set which auth schemes we support, in order of preference + addSecType(secTypeVncAuth); + addSecType(secTypeNone); + + // Start the RFB protocol + sock = s; + reverseConnection = reverse; + initialiseProtocol(); + + m_fileTransfer.initialize(&s->inStream(), &s->outStream()); + + return true; +} + + +void +CConn::applyOptions(CConnOptions& opt) { + // - If any encoding-related settings have changed then we must + // notify the server of the new settings + encodingChange |= ((options.useLocalCursor != opt.useLocalCursor) || + (options.useDesktopResize != opt.useDesktopResize) || + (options.customCompressLevel != opt.customCompressLevel) || + (options.compressLevel != opt.compressLevel) || + (options.noJpeg != opt.noJpeg) || + (options.qualityLevel != opt.qualityLevel) || + (options.preferredEncoding != opt.preferredEncoding)); + + // - If the preferred pixel format has changed then notify the server + formatChange |= (options.fullColour != opt.fullColour); + if (!opt.fullColour) + formatChange |= (options.lowColourLevel != opt.lowColourLevel); + + // - Save the new set of options + options = opt; + + // - Set optional features in ConnParams + cp.supportsLocalCursor = options.useLocalCursor; + cp.supportsDesktopResize = options.useDesktopResize; + cp.customCompressLevel = options.customCompressLevel; + cp.compressLevel = options.compressLevel; + cp.noJpeg = options.noJpeg; + cp.qualityLevel = options.qualityLevel; + + // - Configure connection sharing on/off + setShared(options.shared); + + // - Whether to use protocol 3.3 for legacy compatibility + setProtocol3_3(options.protocol3_3); + + // - Apply settings that affect the window, if it is visible + if (window) { + window->setMonitor(options.monitor.buf); + window->setFullscreen(options.fullScreen); + window->setEmulate3(options.emulate3); + window->setPointerEventInterval(options.pointerEventInterval); + window->setMenuKey(options.menuKey); + window->setDisableWinKeys(options.disableWinKeys); + window->setShowToolbar(options.showToolbar); + if (!options.useLocalCursor) + window->setCursor(0, 0, Point(), 0, 0); + } +} + + +void +CConn::displayChanged() { + // Display format has changed - recalculate the full-colour pixel format + calculateFullColourPF(); +} + +void +CConn::paintCompleted() { + // A repaint message has just completed - request next update if necessary + requestNewUpdate(); +} + +bool +CConn::sysCommand(WPARAM wParam, LPARAM lParam) { + // - If it's one of our (F8 Menu) messages + switch (wParam) { + case IDM_FULLSCREEN: + options.fullScreen = !window->isFullscreen(); + window->setFullscreen(options.fullScreen); + return true; + case IDM_SHOW_TOOLBAR: + options.showToolbar = !options.showToolbar; + window->setShowToolbar(options.showToolbar); + // FIXME: Update menu in DesktopWindow? + // FIXME: Actually show or hide the toolbar? + return true; + case IDM_CTRL_KEY: + window->kbd.keyEvent(this, VK_CONTROL, 0, !window->kbd.keyPressed(VK_CONTROL)); + return true; + case IDM_ALT_KEY: + window->kbd.keyEvent(this, VK_MENU, 0, !window->kbd.keyPressed(VK_MENU)); + return true; + case IDM_SEND_MENU_KEY: + window->kbd.keyEvent(this, options.menuKey, 0, true); + window->kbd.keyEvent(this, options.menuKey, 0, false); + return true; + case IDM_SEND_CAD: + window->kbd.keyEvent(this, VK_CONTROL, 0, true); + window->kbd.keyEvent(this, VK_MENU, 0, true); + window->kbd.keyEvent(this, VK_DELETE, 0x1000000, true); + window->kbd.keyEvent(this, VK_DELETE, 0x1000000, false); + window->kbd.keyEvent(this, VK_MENU, 0, false); + window->kbd.keyEvent(this, VK_CONTROL, 0, false); + return true; + case IDM_SEND_CTLESC: + window->kbd.keyEvent(this, VK_CONTROL, 0, true); + window->kbd.keyEvent(this, VK_ESCAPE, 0, true); + window->kbd.keyEvent(this, VK_ESCAPE, 0, false); + window->kbd.keyEvent(this, VK_CONTROL, 0, false); + return true; + case IDM_REQUEST_REFRESH: + try { + writer()->writeFramebufferUpdateRequest(Rect(0,0,cp.width,cp.height), false); + requestUpdate = false; + } catch (rdr::Exception& e) { + close(e.str()); + } + return true; + case IDM_NEWCONN: + { + Thread* newThread = new CConnThread; + } + return true; + case IDM_OPTIONS: + // Update the monitor device name in the CConnOptions instance + options.monitor.replaceBuf(window->getMonitor()); + showOptionsDialog(); + return true; + case IDM_INFO: + infoDialog.showDialog(this); + return true; + case IDM_ABOUT: + AboutDialog::instance.showDialog(); + return true; + case IDM_FILE_TRANSFER: + m_fileTransfer.show(window->getHandle()); + return true; + case IDM_CONN_SAVE_AS: + return true; + case ID_CLOSE: + // FIXME: Remove the corresponding toolbar button. + return true; + }; + return false; +} + + +void +CConn::closeWindow() { + vlog.info("window closed"); + close(); +} + + +void +CConn::refreshMenu(bool enableSysItems) { + HMENU menu = GetSystemMenu(window->getHandle(), FALSE); + + if (!enableSysItems) { + // Gray out menu items that might cause a World Of Pain + EnableMenuItem(menu, SC_SIZE, MF_BYCOMMAND | MF_GRAYED); + EnableMenuItem(menu, SC_MOVE, MF_BYCOMMAND | MF_GRAYED); + EnableMenuItem(menu, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED); + EnableMenuItem(menu, SC_MINIMIZE, MF_BYCOMMAND | MF_ENABLED); + EnableMenuItem(menu, SC_MAXIMIZE, MF_BYCOMMAND | MF_ENABLED); + } + + // Update the modifier key menu items + UINT ctrlCheckFlags = window->kbd.keyPressed(VK_CONTROL) ? MF_CHECKED : MF_UNCHECKED; + UINT altCheckFlags = window->kbd.keyPressed(VK_MENU) ? MF_CHECKED : MF_UNCHECKED; + CheckMenuItem(menu, IDM_CTRL_KEY, MF_BYCOMMAND | ctrlCheckFlags); + CheckMenuItem(menu, IDM_ALT_KEY, MF_BYCOMMAND | altCheckFlags); + + // Ensure that the Send menu item has the correct text + if (options.menuKey) { + TCharArray menuKeyStr(options.menuKeyName()); + TCharArray tmp(_tcslen(menuKeyStr.buf) + 6); + _stprintf(tmp.buf, _T("Send %s"), menuKeyStr.buf); + if (!ModifyMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf)) + InsertMenu(menu, IDM_SEND_CAD, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf); + } else { + RemoveMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND); + } + + // Set the menu fullscreen option tick + CheckMenuItem(menu, IDM_FULLSCREEN, (window->isFullscreen() ? MF_CHECKED : 0) | MF_BYCOMMAND); + + // In the full-screen mode, "Show toolbar" should be grayed. + int toolbarFlags = options.fullScreen ? MF_GRAYED : MF_ENABLED; + EnableMenuItem(menu, IDM_SHOW_TOOLBAR, MF_BYCOMMAND | toolbarFlags); +} + + +void +CConn::blockCallback() { + // - An InStream has blocked on I/O while processing an RFB message + // We re-enable socket event notifications, so we'll know when more + // data is available, then we sit and dispatch window events until + // the notification arrives. + if (!isClosed()) { + if (WSAEventSelect(sock->getFd(), sockEvent, FD_READ | FD_CLOSE) == SOCKET_ERROR) + throw rdr::SystemException("Unable to wait for sokcet data", WSAGetLastError()); + } + while (true) { + // If we have closed then we can't block waiting for data + if (isClosed()) + throw rdr::EndOfStream(); + + // Wait for socket data, or a message to process + DWORD result = MsgWaitForMultipleObjects(1, &sockEvent.h, FALSE, INFINITE, QS_ALLINPUT); + if (result == WAIT_OBJECT_0) { + // - Network event notification. Return control to I/O routine. + break; + } else if (result == WAIT_FAILED) { + // - The wait operation failed - raise an exception + throw rdr::SystemException("blockCallback wait error", GetLastError()); + } + + // - There should be a message in the message queue + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + // IMPORTANT: We mustn't call TranslateMessage() here, because instead we + // call ToAscii() in CKeyboard::keyEvent(). ToAscii() stores dead key + // state from one call to the next, which would be messed up by calls to + // TranslateMessage() (actually it looks like TranslateMessage() calls + // ToAscii() internally). + DispatchMessage(&msg); + } + } + + // Before we return control to the InStream, reset the network event + WSAEventSelect(sock->getFd(), sockEvent, 0); + ResetEvent(sockEvent); +} + + +void CConn::keyEvent(rdr::U32 key, bool down) { + if (!options.sendKeyEvents) return; + try { + writer()->keyEvent(key, down); + } catch (rdr::Exception& e) { + close(e.str()); + } +} +void CConn::pointerEvent(const Point& pos, int buttonMask) { + if (!options.sendPtrEvents) return; + try { + writer()->pointerEvent(pos, buttonMask); + } catch (rdr::Exception& e) { + close(e.str()); + } +} +void CConn::clientCutText(const char* str, int len) { + if (!options.clientCutText) return; + if (state() != RFBSTATE_NORMAL) return; + try { + writer()->clientCutText(str, len); + } catch (rdr::Exception& e) { + close(e.str()); + } +} + + +CSecurity* CConn::getCSecurity(int secType) +{ + switch (secType) { + case secTypeNone: + return new CSecurityNone(); + case secTypeVncAuth: + return new CSecurityVncAuth(this); + default: + throw Exception("Unsupported secType?"); + } +} + + +void +CConn::setColourMapEntries(int first, int count, U16* rgbs) { + vlog.debug("setColourMapEntries: first=%d, count=%d", first, count); + int i; + for (i=0;isetColour(i+first, rgbs[i*3], rgbs[i*3+1], rgbs[i*3+2]); + // *** change to 0, 256? + window->refreshWindowPalette(first, count); +} + +void +CConn::bell() { + if (options.acceptBell) + MessageBeep(-1); +} + + +void +CConn::setDesktopSize(int w, int h) { + vlog.debug("setDesktopSize %dx%d", w, h); + + // Resize the window's buffer + if (window) + window->setSize(w, h); + + // Tell the underlying CConnection + CConnection::setDesktopSize(w, h); +} + +void +CConn::setCursor(int w, int h, const Point& hotspot, void* data, void* mask) { + if (!options.useLocalCursor) return; + + // Set the window to use the new cursor + window->setCursor(w, h, hotspot, data, mask); +} + + +void +CConn::close(const char* reason) { + // If already closed then ignore this + if (isClosed()) + return; + + // Hide the window, if it exists + if (window) + ShowWindow(window->getHandle(), SW_HIDE); + + // Save the reason & flag that we're closed & shutdown the socket + isClosed_ = true; + closeReason_.replaceBuf(strDup(reason)); + sock->shutdown(); +} + + +void +CConn::showOptionsDialog() { + optionsDialog.showDialog(this); +} + + +void +CConn::framebufferUpdateEnd() { + if (debugDelay != 0) { + vlog.debug("debug delay %d",(int)debugDelay); + UpdateWindow(window->getHandle()); + Sleep(debugDelay); + std::list::iterator i; + for (i = debugRects.begin(); i != debugRects.end(); i++) { + window->invertRect(*i); + } + debugRects.clear(); + } + if (options.autoSelect) + autoSelectFormatAndEncoding(); + + // Always request the next update + requestUpdate = true; + + // Check that at least part of the window has changed + if (!GetUpdateRect(window->getHandle(), 0, FALSE)) { + if (!(GetWindowLong(window->getHandle(), GWL_STYLE) & WS_MINIMIZE)) + requestNewUpdate(); + } + + // Make sure the local cursor is shown + window->showCursor(); +} + + +// Note: The method below is duplicated in vncviewer_unix/CConn.cxx! + +// autoSelectFormatAndEncoding() chooses the format and encoding appropriate +// to the connection speed: +// +// Above 16Mbps (timing for at least a second), switch to hextile +// Otherwise, switch to ZRLE +// +// Above 256Kbps, use full colour mode +// +void +CConn::autoSelectFormatAndEncoding() { + int kbitsPerSecond = sock->inStream().kbitsPerSecond(); + unsigned int newEncoding = options.preferredEncoding; + + bool newFullColour = options.fullColour; + unsigned int timeWaited = sock->inStream().timeWaited(); + + // Select best encoding + if (kbitsPerSecond > 16000 && timeWaited >= 10000) { + newEncoding = encodingHextile; + } else { + newEncoding = encodingZRLE; + } + + if (newEncoding != options.preferredEncoding) { + vlog.info("Throughput %d kbit/s - changing to %s encoding", + kbitsPerSecond, encodingName(newEncoding)); + options.preferredEncoding = newEncoding; + encodingChange = true; + } + + if (kbitsPerSecond == 0) { + return; + } + + if (cp.beforeVersion(3, 8)) { + // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with + // cursors "asynchronously". If this happens in the middle of a + // pixel format change, the server will encode the cursor with + // the old format, but the client will try to decode it + // according to the new format. This will lead to a + // crash. Therefore, we do not allow automatic format change for + // old servers. + return; + } + + // Select best color level + newFullColour = (kbitsPerSecond > 256); + if (newFullColour != options.fullColour) { + vlog.info("Throughput %d kbit/s - full color is now %s", + kbitsPerSecond, + newFullColour ? "enabled" : "disabled"); + options.fullColour = newFullColour; + formatChange = true; + } +} + +void +CConn::requestNewUpdate() { + if (!requestUpdate) return; + + if (formatChange) { + // Select the required pixel format + if (options.fullColour) { + window->setPF(fullColourPF); + } else { + switch (options.lowColourLevel) { + case 0: + window->setPF(PixelFormat(8,3,0,1,1,1,1,2,1,0)); + break; + case 1: + window->setPF(PixelFormat(8,6,0,1,3,3,3,4,2,0)); + break; + case 2: + window->setPF(PixelFormat(8,8,0,0,0,0,0,0,0,0)); + break; + } + } + + // Print the current pixel format + char str[256]; + window->getPF().print(str, 256); + vlog.info("Using pixel format %s",str); + + // Save the connection pixel format and tell server to use it + cp.setPF(window->getPF()); + writer()->writeSetPixelFormat(cp.pf()); + + // Correct the local window's palette + if (!window->getNativePF().trueColour) + window->refreshWindowPalette(0, 1 << cp.pf().depth); + } + + if (encodingChange) { + vlog.info("Using %s encoding",encodingName(options.preferredEncoding)); + writer()->writeSetEncodings(options.preferredEncoding, true); + } + + writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height), + !formatChange); + + encodingChange = formatChange = requestUpdate = false; +} + + +void +CConn::calculateFullColourPF() { + // If the server is palette based then use palette locally + // Also, don't bother doing bgr222 + if (!serverDefaultPF.trueColour || (serverDefaultPF.depth < 6)) { + fullColourPF = serverDefaultPF; + options.fullColour = true; + } else { + // If server is trueColour, use lowest depth PF + PixelFormat native = window->getNativePF(); + if ((serverDefaultPF.bpp < native.bpp) || + ((serverDefaultPF.bpp == native.bpp) && + (serverDefaultPF.depth < native.depth))) + fullColourPF = serverDefaultPF; + else + fullColourPF = window->getNativePF(); + } + formatChange = true; +} + + +void +CConn::setName(const char* name) { + if (window) + window->setName(name); + CConnection::setName(name); +} + + +void CConn::serverInit() { + CConnection::serverInit(); + + // If using AutoSelect with old servers, start in FullColor + // mode. See comment in autoSelectFormatAndEncoding. + if (cp.beforeVersion(3, 8) && options.autoSelect) { + options.fullColour = true; + } + + // Show the window + window = new DesktopWindow(this); + window->setName(cp.name()); + window->setSize(cp.width, cp.height); + applyOptions(options); + + // Save the server's current format + serverDefaultPF = cp.pf(); + + // Calculate the full-colour format to use + calculateFullColourPF(); + + // Request the initial update + vlog.info("requesting initial update"); + formatChange = encodingChange = requestUpdate = true; + requestNewUpdate(); + + // Update the window menu + HMENU wndmenu = GetSystemMenu(window->getHandle(), FALSE); + int toolbarChecked = options.showToolbar ? MF_CHECKED : 0; + + AppendMenu(wndmenu, MF_SEPARATOR, 0, 0); + AppendMenu(wndmenu, MF_STRING, IDM_FULLSCREEN, _T("&Full screen")); + AppendMenu(wndmenu, MF_STRING | toolbarChecked, IDM_SHOW_TOOLBAR, + _T("Show tool&bar")); + AppendMenu(wndmenu, MF_SEPARATOR, 0, 0); + AppendMenu(wndmenu, MF_STRING, IDM_CTRL_KEY, _T("Ctr&l")); + AppendMenu(wndmenu, MF_STRING, IDM_ALT_KEY, _T("Al&t")); + AppendMenu(wndmenu, MF_STRING, IDM_SEND_CAD, _T("Send Ctrl-Alt-&Del")); + AppendMenu(wndmenu, MF_STRING, IDM_SEND_CTLESC, _T("Send Ctrl-&Esc")); + AppendMenu(wndmenu, MF_STRING, IDM_REQUEST_REFRESH, _T("Refres&h Screen")); + AppendMenu(wndmenu, MF_SEPARATOR, 0, 0); + AppendMenu(wndmenu, MF_STRING, IDM_NEWCONN, _T("Ne&w Connection...")); + AppendMenu(wndmenu, MF_STRING, IDM_OPTIONS, _T("&Options...")); + AppendMenu(wndmenu, MF_STRING, IDM_INFO, _T("Connection &Info...")); + AppendMenu(wndmenu, MF_STRING, IDM_ABOUT, _T("&About...")); +} + +void +CConn::serverCutText(const char* str, int len) { + if (!options.serverCutText) return; + window->serverCutText(str, len); +} + + +void CConn::beginRect(const Rect& r, unsigned int encoding) { + sock->inStream().startTiming(); +} + +void CConn::endRect(const Rect& r, unsigned int encoding) { + sock->inStream().stopTiming(); + lastUsedEncoding_ = encoding; + if (debugDelay != 0) { + window->invertRect(r); + debugRects.push_back(r); + } +} + +void CConn::fillRect(const Rect& r, Pixel pix) { + window->fillRect(r, pix); +} +void CConn::imageRect(const Rect& r, void* pixels) { + window->imageRect(r, pixels); +} +void CConn::copyRect(const Rect& r, int srcX, int srcY) { + window->copyRect(r, srcX, srcY); +} + +void CConn::getUserPasswd(char** user, char** password) { +/* + if (!user && options.passwordFile.buf[0]) { + FILE* fp = fopen(options.passwordFile.buf, "rb"); + if (!fp) return false; + char data[256]; + int datalen = fread(data, 1, 256, fp); + fclose(fp); + if (datalen != 8) return false; + vncAuthUnobfuscatePasswd(data); + *password = strDup(data); + memset(data, 0, strlen(data)); + return true; + } +*/ + if (user && options.userName.buf) + *user = strDup(options.userName.buf); + if (password && options.password.buf) + *password = strDup(options.password.buf); + if ((user && !*user) || (password && !*password)) { + // Missing username or password - prompt the user + UserPasswdDialog userPasswdDialog; + userPasswdDialog.setCSecurity(getCurrentCSecurity()); + userPasswdDialog.getUserPasswd(user, password); + } + if (user) options.setUserName(*user); + if (password) options.setPassword(*password); +} + +bool CConn::processFTMsg(int type) { + return m_fileTransfer.processFTMsg(type); +} diff --git a/vncviewer/CConn.h b/vncviewer/CConn.h new file mode 100644 index 00000000..29023f39 --- /dev/null +++ b/vncviewer/CConn.h @@ -0,0 +1,165 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- CConn.h + +// Windows-specific implementation of CConnection + +#ifndef __RFB_WIN32_CCONN_H__ +#define __RFB_WIN32_CCONN_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace rfb { + + namespace win32 { + + class CConn : public CConnection, + UserPasswdGetter, + DesktopWindow::Callback, + rdr::FdInStreamBlockCallback + { + public: + CConn(); + ~CConn(); + + // - Start the VNC session on the supplied socket + // The socket must already be connected to a host + bool initialise(network::Socket* s, bool reverse=false); + + // - Set/get the session options + void applyOptions(CConnOptions& opt); + const CConnOptions& getOptions() const { return options; }; + + // - Show the options dialog for the connection + void showOptionsDialog(); + + // - Close the socket & set the reason for closure + void close(const char* reason=0); + bool isClosed() const { return isClosed_; } + const char* closeReason() const { return closeReason_.buf; } + + // - Last received encoding, for the Info dialog + int lastUsedEncoding() const { return lastUsedEncoding_; } + + // - Get at the DesktopWindow, if any + DesktopWindow* getWindow() { return window; } + + // - Get at the underlying Socket + network::Socket* getSocket() { return sock; } + + // - Get the server's preferred format + const PixelFormat& getServerDefaultPF() const { return serverDefaultPF; } + + // Global user-config registry key + static RegKey userConfigKey; + + bool processFTMsg(int type); + + protected: + // InputHandler interface (via DesktopWindow::Callback) + void keyEvent(rdr::U32 key, bool down); + void pointerEvent(const Point& pos, int buttonMask); + void clientCutText(const char* str, int len); + + // DesktopWindow::Callback interface + void displayChanged(); + void paintCompleted(); + bool sysCommand(WPARAM wParam, LPARAM lParam); + void closeWindow(); + void refreshMenu(bool enableSysCommands); + + // CConnection interface + CSecurity* getCSecurity(int secType); + void setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs); + void bell(); + void framebufferUpdateEnd(); + void setDesktopSize(int w, int h); + void setCursor(int w, int h, const Point& hotspot, void* data, void* mask); + void setName(const char* name); + void serverInit(); + void serverCutText(const char* str, int len); + void beginRect(const Rect& r, unsigned int encoding); + void endRect(const Rect& r, unsigned int encoding); + void fillRect(const Rect& r, Pixel pix); + void imageRect(const Rect& r, void* pixels); + void copyRect(const Rect& r, int srcX, int srcY); + + // rdr::FdInStreamBlockCallback interface + void blockCallback(); + + // UserPasswdGetter interface + // (overridden to allow a pre-supplied username & password) + void getUserPasswd(char** user, char** password); + + // CConn-specific internal interface + void autoSelectFormatAndEncoding(); + void requestNewUpdate(); + void calculateFullColourPF(); + + // The desktop window + DesktopWindow* window; + + // Info and Options dialogs + OptionsDialog optionsDialog; + InfoDialog infoDialog; + + // VNC Viewer options + CConnOptions options; + + // Pixel format and encoding + PixelFormat serverDefaultPF; + PixelFormat fullColourPF; + bool sameMachine; + bool encodingChange; + bool formatChange; + int lastUsedEncoding_; + + // Networking and RFB protocol + network::Socket* sock; + Handle sockEvent; + bool reverseConnection; + bool requestUpdate; + + // Debugging/logging + std::list debugRects; + CharArray closeReason_; + bool isClosed_; + + FileTransfer m_fileTransfer; + }; + + }; + +}; + +#endif + + diff --git a/vncviewer/CConnOptions.cxx b/vncviewer/CConnOptions.cxx new file mode 100644 index 00000000..0c35accd --- /dev/null +++ b/vncviewer/CConnOptions.cxx @@ -0,0 +1,457 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace rfb; +using namespace rfb::win32; + +static StringParameter passwordFile("PasswordFile", + "Password file for VNC authentication", ""); + +// - Settings stored in the registry & in .vnc files, by Save Defaults and +// Save Configuration respectively. + +static BoolParameter useLocalCursor("UseLocalCursor", "Render the mouse cursor locally", true); +static BoolParameter useDesktopResize("UseDesktopResize", "Support dynamic desktop resizing", true); + +static BoolParameter fullColour("FullColor", + "Use full color", true); +static AliasParameter fullColourAlias("FullColour", "Alias for FullColor", &fullColour); + +static IntParameter lowColourLevel("LowColorLevel", + "Color level to use on slow connections. " + "0 = Very Low (8 colors), 1 = Low (64 colors), 2 = Medium (256 colors)", + 2); +static AliasParameter lowColourLevelAlias("LowColourLevel", "Alias for LowColorLevel", &lowColourLevel); + +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 encoding to use (Tight, ZRLE, Hextile or" + " Raw)", "Tight"); +static BoolParameter autoSelect("AutoSelect", + "Auto select pixel format and encoding. " + "Default if PreferredEncoding and FullColor are not specified.", + 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 sendSysKeys("SendSysKeys", + "Send system keys (Alt combinations) to the server.", true); + +static BoolParameter clientCutText("ClientCutText", + "Send clipboard changes to the server.", true); +static BoolParameter serverCutText("ServerCutText", + "Accept clipboard changes from the server.", true); + +static BoolParameter disableWinKeys("DisableWinKeys", + "Pass special Windows keys directly to the server.", true); + +static BoolParameter protocol3_3("Protocol3.3", + "Only use protocol version 3.3", false); + +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 BoolParameter showToolbar("ShowToolbar", "Show toolbar by default.", 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"); +static BoolParameter autoReconnect("AutoReconnect", "Offer to reconnect to the remote server if the connection" + "is dropped because an error occurs.", true); + +static BoolParameter customCompressLevel("CustomCompressLevel", + "Use custom compression level. " + "Default if CompressLevel is specified.", false); + +static IntParameter compressLevel("CompressLevel", + "Use specified compression level" + "0 = Low, 9 = High", + 6); + +static BoolParameter noJpeg("NoJPEG", + "Disable lossy JPEG compression in Tight encoding.", + false); + +static IntParameter qualityLevel("QualityLevel", + "JPEG quality level. " + "0 = Low, 9 = High", + 6); + +CConnOptions::CConnOptions() +: useLocalCursor(::useLocalCursor), useDesktopResize(::useDesktopResize), +autoSelect(::autoSelect), fullColour(::fullColour), fullScreen(::fullScreen), +shared(::sharedConnection), sendPtrEvents(::sendPtrEvents), sendKeyEvents(::sendKeyEvents), sendSysKeys(::sendSysKeys), +preferredEncoding(encodingZRLE), clientCutText(::clientCutText), serverCutText(::serverCutText), +disableWinKeys(::disableWinKeys), protocol3_3(::protocol3_3), acceptBell(::acceptBell), +lowColourLevel(::lowColourLevel), pointerEventInterval(ptrEventInterval), +emulate3(::emulate3), monitor(::monitor.getData()), showToolbar(::showToolbar), +customCompressLevel(::customCompressLevel), compressLevel(::compressLevel), +noJpeg(::noJpeg), qualityLevel(::qualityLevel), passwordFile(::passwordFile.getData()), +autoReconnect(::autoReconnect) +{ + if (autoSelect) { + preferredEncoding = encodingZRLE; + } else { + CharArray encodingName(::preferredEncoding.getData()); + preferredEncoding = encodingNum(encodingName.buf); + } + setMenuKey(CharArray(::menuKey.getData()).buf); + + if (!::autoSelect.hasBeenSet()) { + // Default to AutoSelect=0 if -PreferredEncoding or -FullColor is used + autoSelect = (!::preferredEncoding.hasBeenSet() + && !::fullColour.hasBeenSet() + && !::fullColourAlias.hasBeenSet()); + } + if (!::customCompressLevel.hasBeenSet()) { + // Default to CustomCompressLevel=1 if CompressLevel is used. + customCompressLevel = ::compressLevel.hasBeenSet(); + } +} + + +void CConnOptions::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) { + ObfuscatedPasswd obfPwd; + rdr::HexInStream::hexStrToBin(value.buf, (char**)&obfPwd.buf, &obfPwd.length); + PlainPasswd passwd(obfPwd); + password.replaceBuf(passwd.takeBuf()); + } + } else if (stricmp(section.buf, "Options") == 0) { + // V4 options + 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, "SendSysKeys") == 0) { + sendSysKeys = atoi(value.buf); + } else if (stricmp(name.buf, "SendCutText") == 0) { + clientCutText = atoi(value.buf); + } else if (stricmp(name.buf, "AcceptCutText") == 0) { + serverCutText = atoi(value.buf); + } else if (stricmp(name.buf, "DisableWinKeys") == 0) { + disableWinKeys = atoi(value.buf); + } else if (stricmp(name.buf, "AcceptBell") == 0) { + acceptBell = atoi(value.buf); + } else if (stricmp(name.buf, "Emulate3") == 0) { + emulate3 = atoi(value.buf); + } else if (stricmp(name.buf, "ShowToolbar") == 0) { + showToolbar = 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); + } else if (stricmp(name.buf, "AutoReconnect") == 0) { + autoReconnect = atoi(value.buf); + + } else if (stricmp(name.buf, "CustomCompressLevel") == 0) { + customCompressLevel = atoi(value.buf); + } else if (stricmp(name.buf, "CompressLevel") == 0) { + compressLevel = atoi(value.buf); + } else if (stricmp(name.buf, "NoJPEG") == 0) { + noJpeg = atoi(value.buf); + } else if (stricmp(name.buf, "QualityLevel") == 0) { + qualityLevel = atoi(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); + } + } + + // If AutoSelect is enabled then override the preferred encoding + if (autoSelect) + preferredEncoding = encodingZRLE; + + setConfigFileName(filename); + } catch (rdr::Exception&) { + if (f) fclose(f); + throw; + } +} + +void CConnOptions::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) { + ObfuscatedPasswd obfPwd(password); + CharArray obfuscatedHex = rdr::HexOutStream::binToHexStr(obfPwd.buf, obfPwd.length); + 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, "SendSysKeys=%d\n", (int)sendSysKeys); + fprintf(f, "SendCutText=%d\n", (int)clientCutText); + fprintf(f, "AcceptCutText=%d\n", (int)serverCutText); + fprintf(f, "DisableWinKeys=%d\n", (int)disableWinKeys); + fprintf(f, "AcceptBell=%d\n", (int)acceptBell); + fprintf(f, "Emulate3=%d\n", (int)emulate3); + fprintf(f, "ShowToolbar=%d\n", (int)showToolbar); + fprintf(f, "PointerEventInterval=%d\n", pointerEventInterval); + if (monitor.buf) + fprintf(f, "Monitor=%s\n", monitor.buf); + fprintf(f, "MenuKey=%s\n", CharArray(menuKeyName()).buf); + fprintf(f, "AutoReconnect=%d\n", (int)autoReconnect); + fprintf(f, "CustomCompressLevel=%d\n", customCompressLevel); + fprintf(f, "CompressLevel=%d\n", compressLevel); + fprintf(f, "NoJPEG=%d\n", noJpeg); + fprintf(f, "QualityLevel=%d\n", qualityLevel); + fclose(f); f=0; + + setConfigFileName(filename); + } catch (rdr::Exception&) { + if (f) fclose(f); + throw; + } +} + + +void CConnOptions::writeDefaults() { + RegKey key; + key.createKey(HKEY_CURRENT_USER, _T("Software\\TightVNC\\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("SendSysKeys"), sendSysKeys); + key.setBool(_T("ClientCutText"), clientCutText); + key.setBool(_T("ServerCutText"), serverCutText); + key.setBool(_T("DisableWinKeys"), disableWinKeys); + key.setBool(_T("Protocol3.3"), protocol3_3); + key.setBool(_T("AcceptBell"), acceptBell); + key.setBool(_T("ShowToolbar"), showToolbar); + 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); + key.setBool(_T("AutoReconnect"), autoReconnect); + key.setInt(_T("CustomCompressLevel"), customCompressLevel); + key.setInt(_T("CompressLevel"), compressLevel); + key.setInt(_T("NoJPEG"), noJpeg); + key.setInt(_T("QualityLevel"), qualityLevel); +} + + +void CConnOptions::setUserName(const char* user) {userName.replaceBuf(strDup(user));} +void CConnOptions::setPassword(const char* pwd) {password.replaceBuf(strDup(pwd));} +void CConnOptions::setConfigFileName(const char* cfn) {configFileName.replaceBuf(strDup(cfn));} +void CConnOptions::setHost(const char* h) {host.replaceBuf(strDup(h));} +void CConnOptions::setMonitor(const char* m) {monitor.replaceBuf(strDup(m));} + +void CConnOptions::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* CConnOptions::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(); +} + + +CConnOptions& CConnOptions::operator=(const CConnOptions& 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; + sendSysKeys = o.sendSysKeys; + clientCutText = o.clientCutText; + serverCutText = o.serverCutText; + disableWinKeys = o.disableWinKeys; + emulate3 = o.emulate3; + pointerEventInterval = o.pointerEventInterval; + protocol3_3 = o.protocol3_3; + acceptBell = o.acceptBell; + showToolbar = o.showToolbar; + setUserName(o.userName.buf); + setPassword(o.password.buf); + setConfigFileName(o.configFileName.buf); + setHost(o.host.buf); + setMonitor(o.monitor.buf); + menuKey = o.menuKey; + autoReconnect = o.autoReconnect; + customCompressLevel = o.customCompressLevel; + compressLevel = o.compressLevel; + noJpeg = o.noJpeg; + qualityLevel = o.qualityLevel; + + return *this; +} diff --git a/vncviewer/CConnOptions.h b/vncviewer/CConnOptions.h new file mode 100644 index 00000000..52c3996e --- /dev/null +++ b/vncviewer/CConnOptions.h @@ -0,0 +1,98 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * 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. + */ + +// -=- CConnOptions.h + +// Definition of the CConnOptions class, responsible for storing the +// current & requested VNC Viewer options. + +#ifndef __RFB_WIN32_CCONN_OPTIONS_H__ +#define __RFB_WIN32_CCONN_OPTIONS_H__ + +#include + +namespace rfb { + + namespace win32 { + + // + // -=- Options structure. Each viewer option has a corresponding + // entry in CConnOptions. The viewer options are set by calling + // CConn::applyOptions(...) + // The CConnOptions structure automatically picks up the default + // value of each option from the Configuration system + // The readFromFile and writeFromFile methods can be used to load + // and save VNC configuration files. readFromFile is backwards + // compatible with 3.3 releases, while writeToFile is not. + + class CConnOptions { + public: + CConnOptions(); + CConnOptions(const CConnOptions& o) {operator=(o);} + CConnOptions& operator=(const CConnOptions& 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 sendSysKeys; + bool showToolbar; + bool clientCutText; + bool serverCutText; + bool disableWinKeys; + bool emulate3; + int pointerEventInterval; + bool protocol3_3; + bool acceptBell; + CharArray userName; + void setUserName(const char* user); + PlainPasswd 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(); + bool autoReconnect; + + bool customCompressLevel; + int compressLevel; + bool noJpeg; + int qualityLevel; + + CharArray passwordFile; + }; + + + }; + +}; + +#endif diff --git a/vncviewer/CConnThread.cxx b/vncviewer/CConnThread.cxx new file mode 100644 index 00000000..cfd26952 --- /dev/null +++ b/vncviewer/CConnThread.cxx @@ -0,0 +1,198 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- CConnThread.cxx + +// A CConnThread instance is created for each new connection. +// The CConnThread creates the corresponding CConn instance +// and manages it. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace rfb; +using namespace win32; + +static LogWriter vlog("CConnThread"); + +static std::set threads; +static Mutex threadsLock; +static Handle noMoreThreads(CreateEvent(0, TRUE, FALSE, 0)); + + +CConnThread::CConnThread() : Thread("CConnThread"), isConfig(false), + sock(0), reverse(false) { + vlog.info("CConnThread (dialog)"); + setDeleteAfterRun(); + Lock l(threadsLock); + threads.insert(this); + start(); +} + +CConnThread::CConnThread(const char* hostOrConfig_, bool isConfig_) + : Thread("CConnThread"), hostOrConfig(strDup(hostOrConfig_)), + isConfig(isConfig_), sock(0), reverse(false) { + vlog.info("CConnThread (host/port)"); + setDeleteAfterRun(); + Lock l(threadsLock); + threads.insert(this); + start(); +} + +CConnThread::CConnThread(network::Socket* sock_, bool reverse_) + : Thread("CConnThread"), isConfig(false), sock(sock_), reverse(reverse_) { + vlog.info("CConnThread (reverse connection)"); + setDeleteAfterRun(); + Lock l(threadsLock); + threads.insert(this); + start(); +} + +CConnThread::~CConnThread() { + Lock l(threadsLock); + threads.erase(this); + if (threads.empty()) + SetEvent(noMoreThreads); + delete sock; +} + + +void CConnThread::run() { + CConnOptions options; + bool reconnect; + + do { + { + CConn conn; + reconnect = false; + + // If there is no socket object then set the host & port info + if (!sock && !options.host.buf) { + try { + if (isConfig) { + // A configuration file name was specified - load it + CharArray filename = hostOrConfig.takeBuf(); + options.readFromFile(filename.buf); + } else { + // An actual hostname (and possibly port) was specified + options.host.replaceBuf(hostOrConfig.takeBuf()); + } + + if (!options.host.buf) { + // No host was specified - prompt for one + ConnectionDialog connDlg(&conn); + if (!connDlg.showDialog()) + return; + options = conn.getOptions(); + options.setHost(CStr(connDlg.hostname.buf)); + } + } catch (rdr::Exception& e) { + MsgBox(0, TStr(e.str()), MB_ICONERROR | MB_OK); + return; + } + } + + // Apply the connection options to the CConn + conn.applyOptions(options); + + if (!sock) { + // There is no existing connection - better make one + const char* hostAndPort = conn.getOptions().host.buf; + + try { + ConnectingDialog dlg; + sock = dlg.connect(hostAndPort); + + // If the connection was cancelled by the user, just quit + if (!sock) + return; + } catch(rdr::Exception& e) { + MsgBox(NULL, TStr(e.str()), MB_ICONERROR | MB_OK); + return; + } + + // Try to add the caller to the MRU + MRU::addToMRU(hostAndPort); + } + + // Run the RFB protocol over the connected socket + conn.initialise(sock, reverse); + while (!conn.isClosed()) { + try { + conn.getInStream()->check(1,1); + conn.processMsg(); + } catch (rdr::EndOfStream) { + if (conn.state() == CConnection::RFBSTATE_NORMAL) + conn.close(); + else + conn.close("The connection closed unexpectedly"); + } catch (rfb::AuthCancelledException) { + conn.close(); + } catch (rfb::AuthFailureException& e) { + // Clear the password, in case we auto-reconnect + options = conn.getOptions(); + options.password.replaceBuf(0); + conn.applyOptions(options); + conn.close(e.str()); + } catch (rdr::Exception& e) { + conn.close(e.str()); + } + } + + // If there is a cause for closing the connection logged then display it + if (conn.closeReason()) { + reconnect = !reverse && conn.getOptions().autoReconnect; + if (!reconnect) { + MsgBox(0, TStr(conn.closeReason()), MB_ICONINFORMATION | MB_OK); + } else { + options = conn.getOptions(); + const char* format = "%s\nDo you wish to attempt to reconnect to %s?"; + CharArray message(strlen(conn.closeReason()) + strlen(format) + + strlen(conn.getOptions().host.buf)); + sprintf(message.buf, format, conn.closeReason(), conn.getOptions().host.buf); + if (MsgBox(0, TStr(message.buf), MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) != IDYES) + reconnect = false; + } + } + } // Exit the CConn's scope, implicitly destroying it & making it safe to delete the TcpSocket + + // Clean up the old socket, if any + delete sock; sock = 0; + } while (reconnect); +} + + +BOOL CConnThread::getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg) { + while (!PeekMessage(msg, hwnd, minMsg, maxMsg, PM_REMOVE)) { + DWORD result = MsgWaitForMultipleObjects(1, &noMoreThreads.h, FALSE, INFINITE, QS_ALLINPUT); + if (result == WAIT_OBJECT_0) + return FALSE; + else if (result == WAIT_FAILED) + throw rdr::SystemException("CConnThread::getMessage wait failed", GetLastError()); + } + return msg->message != WM_QUIT; +} diff --git a/vncviewer/CConnThread.h b/vncviewer/CConnThread.h new file mode 100644 index 00000000..7a8451c1 --- /dev/null +++ b/vncviewer/CConnThread.h @@ -0,0 +1,57 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- CConnThread.h + +// CConn-managing Thread implementation. + +#ifndef __RFB_WIN32_CCONN_THREAD_H__ +#define __RFB_WIN32_CCONN_THREAD_H__ + +#include +#include +#include + +namespace rfb { + + namespace win32 { + + class CConnThread : public Thread { + public: + CConnThread(); + CConnThread(const char* hostOrConfig, bool isConfig=false); + CConnThread(network::Socket* sock, bool reverse=false); + ~CConnThread(); + + void run(); + + // Special getMessage call that returns FALSE if message is WM_QUIT, + // OR if there are no more CConnThreads running. + static BOOL getMessage(MSG* msg, HWND hwnd, UINT minMsg, UINT maxMsg); + protected: + CharArray hostOrConfig; + bool isConfig; + network::Socket* sock; + bool reverse; + }; + + }; + +}; + +#endif // __RFB_WIN32_CCONN_THREAD_H__ diff --git a/vncviewer/CViewManager.cxx b/vncviewer/CViewManager.cxx deleted file mode 100644 index 09ed1fe8..00000000 --- a/vncviewer/CViewManager.cxx +++ /dev/null @@ -1,252 +0,0 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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::iterator i; - for (i=socks.begin(); i!=socks.end(); i++) { - if (wParam == (*i)->getFd()) { - network::Socket* new_sock = (*i)->accept(); - CharArray connname; - connname.buf = new_sock->getPeerEndpoint(); - vlog.debug("accepted connection: %s", connname); - CViewThread* thread = new CViewThread(new_sock, *this); - addThread(thread); - thread->start(); - break; - } - } - break; - } - return MsgWindow::processMessage(msg, wParam, lParam); -} diff --git a/vncviewer/CViewManager.h b/vncviewer/CViewManager.h deleted file mode 100644 index 3d11dd96..00000000 --- a/vncviewer/CViewManager.h +++ /dev/null @@ -1,64 +0,0 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ - -// -=- CViewManager.h - -// Creates and manages threads to run CView instances. - -#ifndef __RFB_WIN32_CVIEW_MANAGER_H__ -#define __RFB_WIN32_CVIEW_MANAGER_H__ - -#include -#include -#include -#include - -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 socks; - std::list threads; - Mutex threadsMutex; - Condition threadsSig; - Thread* mainThread; - }; - - }; - -}; - -#endif diff --git a/vncviewer/CViewOptions.cxx b/vncviewer/CViewOptions.cxx deleted file mode 100644 index 76a624b2..00000000 --- a/vncviewer/CViewOptions.cxx +++ /dev/null @@ -1,437 +0,0 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace rfb; -using namespace rfb::win32; - -static StringParameter passwordFile("PasswordFile", - "Password file for VNC authentication", ""); - -static BoolParameter useLocalCursor("UseLocalCursor", "Render the mouse cursor locally", true); -static BoolParameter useDesktopResize("UseDesktopResize", "Support dynamic desktop resizing", true); - -static BoolParameter fullColour("FullColor", - "Use full color", true); -static AliasParameter fullColourAlias("FullColour", "Alias for FullColor", &fullColour); - -static IntParameter lowColourLevel("LowColorLevel", - "Color level to use on slow connections. " - "0 = Very Low (8 colors), 1 = Low (64 colors), 2 = Medium (256 colors)", - 2); -static AliasParameter lowColourLevelAlias("LowColourLevel", "Alias for LowColorLevel", &lowColourLevel); - -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 encoding to use (Tight, ZRLE, Hextile or" - " Raw)", "Tight"); -static BoolParameter autoSelect("AutoSelect", - "Auto select pixel format and encoding. " - "Default if PreferredEncoding and FullColor are not specified.", - 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 sendSysKeys("SendSysKeys", - "Send system keys (Alt combinations) to the server.", true); - -static BoolParameter clientCutText("ClientCutText", - "Send clipboard changes to the server.", true); -static BoolParameter serverCutText("ServerCutText", - "Accept clipboard changes from the server.", true); - -static BoolParameter 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 BoolParameter showToolbar("ShowToolbar", "Show toolbar by default.", 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"); - -static BoolParameter customCompressLevel("CustomCompressLevel", - "Use custom compression level. " - "Default if CompressLevel is specified.", false); - -static IntParameter compressLevel("CompressLevel", - "Use specified compression level" - "0 = Low, 9 = High", - 6); - -static BoolParameter noJpeg("NoJPEG", - "Disable lossy JPEG compression in Tight encoding.", - false); - -static IntParameter qualityLevel("QualityLevel", - "JPEG quality level. " - "0 = Low, 9 = High", - 6); - -CViewOptions::CViewOptions() -: useLocalCursor(::useLocalCursor), useDesktopResize(::useDesktopResize), -autoSelect(::autoSelect), fullColour(::fullColour), fullScreen(::fullScreen), -shared(::sharedConnection), sendPtrEvents(::sendPtrEvents), sendKeyEvents(::sendKeyEvents), sendSysKeys(::sendSysKeys), -preferredEncoding(encodingZRLE), clientCutText(::clientCutText), serverCutText(::serverCutText), -protocol3_3(::protocol3_3), acceptBell(::acceptBell), showToolbar(::showToolbar), lowColourLevel(::lowColourLevel), -pointerEventInterval(ptrEventInterval), emulate3(::emulate3), monitor(::monitor.getData()), -customCompressLevel(::customCompressLevel), compressLevel(::compressLevel), -noJpeg(::noJpeg), qualityLevel(::qualityLevel), passwordFile(::passwordFile.getData()) -{ - CharArray encodingName(::preferredEncoding.getData()); - preferredEncoding = encodingNum(encodingName.buf); - setMenuKey(CharArray(::menuKey.getData()).buf); - - if (!::autoSelect.hasBeenSet()) { - // Default to AutoSelect=0 if -PreferredEncoding or -FullColor is used - autoSelect = (!::preferredEncoding.hasBeenSet() - && !::fullColour.hasBeenSet() - && !::fullColourAlias.hasBeenSet()); - } - if (!::customCompressLevel.hasBeenSet()) { - // Default to CustomCompressLevel=1 if CompressLevel is used. - customCompressLevel = ::compressLevel.hasBeenSet(); - } -} - - -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, "SendSysKeys") == 0) { - sendSysKeys = atoi(value.buf); - } else if (stricmp(name.buf, "SendCutText") == 0) { - clientCutText = atoi(value.buf); - } else if (stricmp(name.buf, "AcceptCutText") == 0) { - serverCutText = atoi(value.buf); - } else if (stricmp(name.buf, "AcceptBell") == 0) { - acceptBell = atoi(value.buf); - } else if (stricmp(name.buf, "Emulate3") == 0) { - emulate3 = atoi(value.buf); - } else if (stricmp(name.buf, "ShowToolbar") == 0) { - showToolbar = 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); - } else if (stricmp(name.buf, "CustomCompressLevel") == 0) { - customCompressLevel = atoi(value.buf); - } else if (stricmp(name.buf, "CompressLevel") == 0) { - compressLevel = atoi(value.buf); - } else if (stricmp(name.buf, "NoJPEG") == 0) { - noJpeg = atoi(value.buf); - } else if (stricmp(name.buf, "QualityLevel") == 0) { - qualityLevel = atoi(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, "SendSysKeys=%d\n", (int)sendSysKeys); - fprintf(f, "SendCutText=%d\n", (int)clientCutText); - fprintf(f, "AcceptCutText=%d\n", (int)serverCutText); - fprintf(f, "AcceptBell=%d\n", (int)acceptBell); - fprintf(f, "Emulate3=%d\n", (int)emulate3); - fprintf(f, "ShowToolbar=%d\n", (int)showToolbar); - fprintf(f, "PointerEventInterval=%d\n", pointerEventInterval); - if (monitor.buf) - fprintf(f, "Monitor=%s\n", monitor.buf); - fprintf(f, "MenuKey=%s\n", CharArray(menuKeyName()).buf); - fprintf(f, "CustomCompressLevel=%d\n", customCompressLevel); - fprintf(f, "CompressLevel=%d\n", compressLevel); - fprintf(f, "NoJPEG=%d\n", noJpeg); - fprintf(f, "QualityLevel=%d\n", qualityLevel); - 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\\TightVNC\\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("SendSysKeys"), sendSysKeys); - 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("ShowToolbar"), showToolbar); - 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); - key.setInt(_T("CustomCompressLevel"), customCompressLevel); - key.setInt(_T("CompressLevel"), compressLevel); - key.setInt(_T("NoJPEG"), noJpeg); - key.setInt(_T("QualityLevel"), qualityLevel); -} - - -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; - sendSysKeys = o.sendSysKeys; - clientCutText = o.clientCutText; - serverCutText = o.serverCutText; - emulate3 = o.emulate3; - pointerEventInterval = o.pointerEventInterval; - protocol3_3 = o.protocol3_3; - acceptBell = o.acceptBell; - showToolbar = o.showToolbar; - setUserName(o.userName.buf); - setPassword(o.password.buf); - setConfigFileName(o.configFileName.buf); - setHost(o.host.buf); - setMonitor(o.monitor.buf); - menuKey = o.menuKey; - customCompressLevel = o.customCompressLevel; - compressLevel = o.compressLevel; - noJpeg = o.noJpeg; - qualityLevel = o.qualityLevel; - - return *this; -} diff --git a/vncviewer/CViewOptions.h b/vncviewer/CViewOptions.h deleted file mode 100644 index febd2842..00000000 --- a/vncviewer/CViewOptions.h +++ /dev/null @@ -1,96 +0,0 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ - -// -=- 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 - -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 sendSysKeys; - bool showToolbar; - 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(); - - bool customCompressLevel; - int compressLevel; - bool noJpeg; - int qualityLevel; - - CharArray passwordFile; - }; - - - }; - -}; - -#endif diff --git a/vncviewer/ConnectingDialog.cxx b/vncviewer/ConnectingDialog.cxx new file mode 100644 index 00000000..60fcb661 --- /dev/null +++ b/vncviewer/ConnectingDialog.cxx @@ -0,0 +1,160 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- ConnectingDialog.cxx + +#include +#include +#include +#include +#include +#include +#include + +using namespace rfb; +using namespace rfb::win32; + + +// ConnectingDialog callback +static BOOL CALLBACK ConnectingDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { + bool* activePtr = (bool*)GetWindowLong(hwnd, GWL_USERDATA); + switch (uMsg) { + case WM_INITDIALOG: + SetWindowLong(hwnd, GWL_USERDATA, lParam); + return TRUE; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDCANCEL: + if (activePtr) + *activePtr = false; + return TRUE; + } + break; + case WM_DESTROY: + if (activePtr) + *activePtr = false; + return TRUE; + } + return 0; +} + + +// Global map, used by ConnectingDialog::Threads to call back to their owning +// ConnectingDialogs, while coping with the fact that the owner may already have quit. +static std::map dialogs; +static int nextDialogId = 0; +static Mutex dialogsLock; + + +// ConnectingDialog::Thread +// Attempts to connect to the specified host. If the connection succeeds, the +// socket is saved in the owning ConnectingDialog, if still available, and the +// event is signalled. If the connection fails, the Exception text is returned +// to the dialog. If the dialog is already gone, the Exception/socket are discarded. +// NB: This thread class cleans itself up on exit - DO NOT join()! +class ConnectingDialog::Thread : public rfb::Thread { +public: + Thread(int dialogId_, const char* hostAndPort) : dialogId(dialogId_) { + setDeleteAfterRun(); + getHostAndPort(hostAndPort, &host.buf, &port); + } + virtual void run() { + try { + returnSock(new network::TcpSocket(host.buf, port)); + } catch (rdr::Exception& e) { + returnException(e); + } + } + void returnSock(network::Socket* s) { + Lock l(dialogsLock); + if (dialogs.count(dialogId)) { + dialogs[dialogId]->newSocket = s; + SetEvent(dialogs[dialogId]->readyEvent); + } else { + delete s; + } + } + void returnException(const rdr::Exception& e) { + Lock l(dialogsLock); + if (dialogs.count(dialogId)) { + dialogs[dialogId]->errMsg.replaceBuf(strDup(e.str())); + SetEvent(dialogs[dialogId]->readyEvent); + } + }; + CharArray host; + int port; + int dialogId; +}; + + +ConnectingDialog::ConnectingDialog() : dialog(0), readyEvent(CreateEvent(0, TRUE, FALSE, 0)), + newSocket(0), dialogId(0) { +} + +network::Socket* ConnectingDialog::connect(const char* hostAndPort) { + Thread* connectThread = 0; + bool active = true; + errMsg.replaceBuf(0); + newSocket = 0; + + // Get a unique dialog identifier and create the dialog window + { + Lock l(dialogsLock); + dialogId = ++nextDialogId; + dialogs[dialogId] = this; + dialog = CreateDialogParam(GetModuleHandle(0), + MAKEINTRESOURCE(IDD_CONNECTING_DLG), 0, &ConnectingDlgProc, (long)&active); + ShowWindow(dialog, SW_SHOW); + ResetEvent(readyEvent); + } + + // Create and start the connection thread + try { + connectThread = new Thread(dialogId, hostAndPort); + connectThread->start(); + } catch (rdr::Exception& e) { + errMsg.replaceBuf(strDup(e.str())); + active = false; + } + + // Process window messages until the connection thread signals readyEvent, or the dialog is cancelled + while (active && (MsgWaitForMultipleObjects(1, &readyEvent.h, FALSE, INFINITE, QS_ALLINPUT) == WAIT_OBJECT_0 + 1)) { + MSG msg; + while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) + DispatchMessage(&msg); + } + + // Remove this dialog from the table + // NB: If the dialog was cancelled then the thread is still running, and will only + // discover that we're gone when it looks up our unique Id in the dialog table. + { + Lock l(dialogsLock); + dialogs.erase(dialogId); + } + + // Close the dialog window + DestroyWindow(dialog); dialog=0; + + // Throw the exception, if there was one + if (errMsg.buf) + throw rdr::Exception(errMsg.buf); + + // Otherwise, return the socket + // NB: The socket will be null if the dialog was cancelled + return newSocket; +} diff --git a/vncviewer/ConnectingDialog.h b/vncviewer/ConnectingDialog.h index b146ced6..c38b3a1b 100644 --- a/vncviewer/ConnectingDialog.h +++ b/vncviewer/ConnectingDialog.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -18,78 +18,48 @@ // -=- ConnectingDialog.h -// Dialog to indicate to the user that the viewer is attempting to make an -// outgoing connection. +// ConnectingDialog instances are used to display a status dialog while a +// connection attempt is in progress. The connection attempt is performed +// in a background thread by the ConnectingDialog, to allow the status dialog +// to remain interactive. If the dialog is cancelled then it will close and +// the connection dialog will eventually tidy itself up. #ifndef __RFB_WIN32_CONNECTING_DLG_H__ #define __RFB_WIN32_CONNECTING_DLG_H__ -#include -#include -#include +#include +#include +#include +#include namespace rfb { namespace win32 { - BOOL CALLBACK ConnectingDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { - switch (uMsg) { - case WM_INITDIALOG: - { - SetWindowLong(hwnd, GWL_USERDATA, lParam); - return TRUE; - } - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDCANCEL: - network::Socket* sock = (network::Socket*) GetWindowLong(hwnd, GWL_USERDATA); - sock->shutdown(); - EndDialog(hwnd, FALSE); - return TRUE; - } - break; - case WM_DESTROY: - EndDialog(hwnd, TRUE); - return TRUE; - } - return 0; - } - - // *** hacky bit - should use async connect so dialog behaves properly - class ConnectingDialog : public Thread { + class ConnectingDialog { public: - ConnectingDialog() : Thread("ConnectingDialog") { - dialog = 0; - active = true; - start(); - } - virtual ~ConnectingDialog() { - // *** join() required here because otherwise ~Thread calls Thread::join() - join(); - } - virtual void run() { - dialog = CreateDialogParam(GetModuleHandle(0), - MAKEINTRESOURCE(IDD_CONNECTING_DLG), 0, &ConnectingDlgProc, 0); - ShowWindow(dialog, SW_SHOW); - MSG msg; - while (active && GetMessage(&msg, dialog, 0, 0)) { - DispatchMessage(&msg); - } - DestroyWindow(dialog); - } - virtual Thread* join() { - active = false; - if (dialog) - PostMessage(dialog, WM_QUIT, 0, 0); - return Thread::join(); - } + ConnectingDialog(); + + // connect + // Show a Connecting dialog and attempt to connect to the specified host + // in the background. + // If the connection succeeds then the Socket is returned. + // If an error occurs, an Exception is thrown. + // If the dialog is cancelled then null is returned. + network::Socket* connect(const char* hostAndPort); protected: HWND dialog; - bool active; + network::Socket* newSocket; + CharArray errMsg; + Handle readyEvent; + int dialogId; + + class Thread; + friend class Thread; }; }; }; -#endif \ No newline at end of file +#endif diff --git a/vncviewer/ConnectionDialog.cxx b/vncviewer/ConnectionDialog.cxx index c083444c..e7c6b0a5 100644 --- a/vncviewer/ConnectionDialog.cxx +++ b/vncviewer/ConnectionDialog.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -15,9 +15,11 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ + #include -#include +#include #include +#include #include @@ -25,7 +27,7 @@ using namespace rfb; using namespace rfb::win32; -ConnectionDialog::ConnectionDialog(CView* view_) : Dialog(GetModuleHandle(0)), view(view_) { +ConnectionDialog::ConnectionDialog(CConn* conn_) : Dialog(GetModuleHandle(0)), conn(conn_) { } @@ -47,6 +49,13 @@ void ConnectionDialog::initDialog() { // Select the first item in the list SendMessage(box, CB_SETCURSEL, 0, 0); + + // Fill out the Security: drop-down and select the preferred option + HWND security = GetDlgItem(handle, IDC_SECURITY_LEVEL); + LRESULT n = SendMessage(security, CB_ADDSTRING, 0, (LPARAM)_T("Always Off")); + if (n != CB_ERR) + SendMessage(security, CB_SETCURSEL, n, 0); + enableItem(IDC_SECURITY_LEVEL, false); } @@ -63,7 +72,7 @@ bool ConnectionDialog::onCommand(int id, int cmd) { AboutDialog::instance.showDialog(); return true; case IDC_OPTIONS: - view->optionsDialog.showDialog(view); + conn->showOptionsDialog(); return true; }; return false; diff --git a/vncviewer/ConnectionDialog.h b/vncviewer/ConnectionDialog.h index 554c86fb..f739280c 100644 --- a/vncviewer/ConnectionDialog.h +++ b/vncviewer/ConnectionDialog.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -31,18 +31,18 @@ namespace rfb { namespace win32 { - class CView; + class CConn; class ConnectionDialog : Dialog { public: - ConnectionDialog(CView* view); + ConnectionDialog(CConn* view); virtual bool showDialog(); virtual void initDialog(); virtual bool onOk(); virtual bool onCommand(int id, int cmd); TCharArray hostname; protected: - CView* view; + CConn* conn; }; }; diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx new file mode 100644 index 00000000..311a5865 --- /dev/null +++ b/vncviewer/DesktopWindow.cxx @@ -0,0 +1,1076 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace rfb; +using namespace rfb::win32; + + +// - Statics & consts + +static LogWriter vlog("DesktopWindow"); + +const int TIMER_BUMPSCROLL = 1; +const int TIMER_POINTER_INTERVAL = 2; +const int TIMER_POINTER_3BUTTON = 3; + + +// +// -=- DesktopWindowClass + +// +// Window class used as the basis for all DesktopWindow instances +// + +class DesktopWindowClass { +public: + DesktopWindowClass(); + ~DesktopWindowClass(); + ATOM classAtom; + HINSTANCE instance; +}; + +LRESULT CALLBACK DesktopWindowProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { + LRESULT result; + if (msg == WM_CREATE) + SetWindowLong(wnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams); + else if (msg == WM_DESTROY) + SetWindowLong(wnd, GWL_USERDATA, 0); + DesktopWindow* _this = (DesktopWindow*) GetWindowLong(wnd, GWL_USERDATA); + if (!_this) { + vlog.info("null _this in %x, message %u", wnd, msg); + return rfb::win32::SafeDefWindowProc(wnd, msg, wParam, lParam); + } + + try { + result = _this->processMessage(msg, wParam, lParam); + } catch (rdr::Exception& e) { + vlog.error("untrapped: %s", e.str()); + } + + return result; +}; + +static HCURSOR dotCursor = (HCURSOR)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDC_DOT_CURSOR), IMAGE_CURSOR, 0, 0, LR_SHARED); +static HCURSOR arrowCursor = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED); + +DesktopWindowClass::DesktopWindowClass() : classAtom(0) { + WNDCLASS wndClass; + wndClass.style = 0; + wndClass.lpfnWndProc = DesktopWindowProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = instance = GetModuleHandle(0); + wndClass.hIcon = (HICON)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 0, 0, LR_SHARED); + if (!wndClass.hIcon) + printf("unable to load icon:%ld", GetLastError()); + wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndClass.hbrBackground = NULL; + wndClass.lpszMenuName = 0; + wndClass.lpszClassName = _T("rfb::win32::DesktopWindowClass"); + classAtom = RegisterClass(&wndClass); + if (!classAtom) { + throw rdr::SystemException("unable to register DesktopWindow window class", GetLastError()); + } +} + +DesktopWindowClass::~DesktopWindowClass() { + if (classAtom) { + UnregisterClass((const TCHAR*)classAtom, instance); + } +} + +DesktopWindowClass baseClass; + +// +// -=- FrameClass + +// +// Window class used for child windows that display pixel data +// + +class FrameClass { +public: + FrameClass(); + ~FrameClass(); + ATOM classAtom; + HINSTANCE instance; +}; + +LRESULT CALLBACK FrameProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { + LRESULT result; + if (msg == WM_CREATE) + SetWindowLong(wnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams); + else if (msg == WM_DESTROY) + SetWindowLong(wnd, GWL_USERDATA, 0); + DesktopWindow* _this = (DesktopWindow*) GetWindowLong(wnd, GWL_USERDATA); + if (!_this) { + vlog.info("null _this in %x, message %u", wnd, msg); + return rfb::win32::SafeDefWindowProc(wnd, msg, wParam, lParam); + } + + try { + result = _this->processFrameMessage(msg, wParam, lParam); + } catch (rdr::Exception& e) { + vlog.error("untrapped: %s", e.str()); + } + + return result; +} + +FrameClass::FrameClass() : classAtom(0) { + WNDCLASS wndClass; + wndClass.style = 0; + wndClass.lpfnWndProc = FrameProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = instance = GetModuleHandle(0); + wndClass.hIcon = 0; + wndClass.hCursor = NULL; + wndClass.hbrBackground = NULL; + wndClass.lpszMenuName = 0; + wndClass.lpszClassName = _T("rfb::win32::FrameClass"); + classAtom = RegisterClass(&wndClass); + if (!classAtom) { + throw rdr::SystemException("unable to register Frame window class", GetLastError()); + } +} + +FrameClass::~FrameClass() { + if (classAtom) { + UnregisterClass((const TCHAR*)classAtom, instance); + } +} + +FrameClass frameClass; + + +// +// -=- DesktopWindow instance implementation +// + +DesktopWindow::DesktopWindow(Callback* cb) + : buffer(0), + showToolbar(true), + client_size(0, 0, 16, 16), window_size(0, 0, 32, 32), + cursorVisible(false), cursorAvailable(false), cursorInBuffer(false), + systemCursorVisible(true), trackingMouseLeave(false), + handle(0), frameHandle(0), has_focus(false), palette_changed(false), + fullscreenActive(false), fullscreenRestore(false), + bumpScroll(false), callback(cb) { + + // Create the window + const char* name = "DesktopWindow"; + handle = CreateWindow((const TCHAR*)baseClass.classAtom, TStr(name), + WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, + 0, 0, 10, 10, 0, 0, baseClass.instance, this); + if (!handle) + throw rdr::SystemException("unable to create WMNotifier window instance", GetLastError()); + vlog.debug("created window \"%s\" (%x)", name, handle); + + // Create the toolbar + tb.create(getHandle()); + vlog.debug("created toolbar window \"%s\" (%x)", "ViewerToolBar", tb.getHandle()); + + // Create the frame window + frameHandle = CreateWindowEx(WS_EX_CLIENTEDGE, (const TCHAR*)frameClass.classAtom, + 0, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, getHandle(), 0, frameClass.instance, this); + if (!frameHandle) { + throw rdr::SystemException("unable to create rfb frame window instance", GetLastError()); + } + vlog.debug("created window \"%s\" (%x)", "Frame Window", frameHandle); + + // Initialise the CPointer pointer handler + ptr.setHWND(frameHandle); + ptr.setIntervalTimerId(TIMER_POINTER_INTERVAL); + ptr.set3ButtonTimerId(TIMER_POINTER_3BUTTON); + + // Initialise the bumpscroll timer + bumpScrollTimer.setHWND(handle); + bumpScrollTimer.setId(TIMER_BUMPSCROLL); + + // Hook the clipboard + clipboard.setNotifier(this); + + // Create the backing buffer + buffer = new win32::DIBSectionBuffer(frameHandle); + + // Show the window + centerWindow(handle, 0); + ShowWindow(handle, SW_SHOW); +} + +DesktopWindow::~DesktopWindow() { + vlog.debug("~DesktopWindow"); + showSystemCursor(); + if (handle) { + disableLowLevelKeyEvents(handle); + DestroyWindow(handle); + handle = 0; + } + delete buffer; + vlog.debug("~DesktopWindow done"); +} + + +void DesktopWindow::setFullscreen(bool fs) { + if (fs && !fullscreenActive) { + fullscreenActive = bumpScroll = true; + + // Un-minimize the window if required + if (GetWindowLong(handle, GWL_STYLE) & WS_MINIMIZE) + ShowWindow(handle, SW_RESTORE); + + // Save the current window position + GetWindowRect(handle, &fullscreenOldRect); + + // Find the size of the display the window is on + MonitorInfo mi(handle); + + // Hide the toolbar + if (showToolbar) + tb.hide(); + SetWindowLong(frameHandle, GWL_EXSTYLE, 0); + + // Set the window full-screen + DWORD flags = GetWindowLong(handle, GWL_STYLE); + fullscreenOldFlags = flags; + flags = flags & ~(WS_CAPTION | WS_THICKFRAME | WS_MAXIMIZE | WS_MINIMIZE); + vlog.debug("flags=%x", flags); + + SetWindowLong(handle, GWL_STYLE, flags); + SetWindowPos(handle, HWND_TOP, mi.rcMonitor.left, mi.rcMonitor.top, + mi.rcMonitor.right-mi.rcMonitor.left, + mi.rcMonitor.bottom-mi.rcMonitor.top, + SWP_FRAMECHANGED); + } else if (!fs && fullscreenActive) { + fullscreenActive = bumpScroll = false; + + // Show the toolbar + if (showToolbar) + tb.show(); + SetWindowLong(frameHandle, GWL_EXSTYLE, WS_EX_CLIENTEDGE); + + // Set the window non-fullscreen + SetWindowLong(handle, GWL_STYLE, fullscreenOldFlags); + + // Set the window position + SetWindowPos(handle, HWND_NOTOPMOST, + fullscreenOldRect.left, fullscreenOldRect.top, + fullscreenOldRect.right - fullscreenOldRect.left, + fullscreenOldRect.bottom - fullscreenOldRect.top, + SWP_FRAMECHANGED); + } + + // Adjust the viewport offset to cope with change in size between FS + // and previous window state. + setViewportOffset(scrolloffset); +} + +void DesktopWindow::setDisableWinKeys(bool dwk) { + // Enable low-level event hooking, so we get special keys directly + if (dwk) + enableLowLevelKeyEvents(handle); + else + disableLowLevelKeyEvents(handle); +} + + +void DesktopWindow::setMonitor(const char* monitor) { + MonitorInfo mi(monitor); + mi.moveTo(handle); +} + +char* DesktopWindow::getMonitor() const { + MonitorInfo mi(handle); + return strDup(mi.szDevice); +} + + +bool DesktopWindow::setViewportOffset(const Point& tl) { + Point np = Point(max(0, min(tl.x, buffer->width()-client_size.width())), + max(0, min(tl.y, buffer->height()-client_size.height()))); + Point delta = np.translate(scrolloffset.negate()); + if (!np.equals(scrolloffset)) { + scrolloffset = np; + ScrollWindowEx(frameHandle, -delta.x, -delta.y, 0, 0, 0, 0, SW_INVALIDATE); + UpdateWindow(frameHandle); + return true; + } + return false; +} + + +bool DesktopWindow::processBumpScroll(const Point& pos) +{ + if (!bumpScroll) return false; + int bumpScrollPixels = 20; + bumpScrollDelta = Point(); + + if (pos.x == client_size.width()-1) + bumpScrollDelta.x = bumpScrollPixels; + else if (pos.x == 0) + bumpScrollDelta.x = -bumpScrollPixels; + if (pos.y == client_size.height()-1) + bumpScrollDelta.y = bumpScrollPixels; + else if (pos.y == 0) + bumpScrollDelta.y = -bumpScrollPixels; + + if (bumpScrollDelta.x || bumpScrollDelta.y) { + if (bumpScrollTimer.isActive()) return true; + if (setViewportOffset(scrolloffset.translate(bumpScrollDelta))) { + bumpScrollTimer.start(25); + return true; + } + } + + bumpScrollTimer.stop(); + return false; +} + + +LRESULT +DesktopWindow::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + + // -=- Process standard window messages + + case WM_NOTIFY: + if (wParam == ID_TOOLBAR) + tb.processWM_NOTIFY(wParam, lParam); + break; + + case WM_DISPLAYCHANGE: + // Display format has changed - notify callback + callback->displayChanged(); + break; + + // -=- Window position + + // Prevent the window from being resized to be too large if in normal mode. + // If maximized or fullscreen the allow oversized windows. + + case WM_WINDOWPOSCHANGING: + { + WINDOWPOS* wpos = (WINDOWPOS*)lParam; + if (wpos->flags & SWP_NOSIZE) + break; + + // Work out how big the window should ideally be + DWORD current_style = GetWindowLong(frameHandle, GWL_STYLE); + DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL); + DWORD style_ex = GetWindowLong(frameHandle, GWL_EXSTYLE); + + RECT r; + SetRect(&r, 0, 0, buffer->width(), buffer->height()); + AdjustWindowRectEx(&r, style, FALSE, style_ex); + Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom); + if (current_style & WS_VSCROLL) + reqd_size.br.x += GetSystemMetrics(SM_CXVSCROLL); + if (current_style & WS_HSCROLL) + reqd_size.br.y += GetSystemMetrics(SM_CXHSCROLL); + + SetRect(&r, reqd_size.tl.x, reqd_size.br.x, reqd_size.tl.y, reqd_size.br.y); + if (tb.isVisible()) + r.bottom += tb.getHeight(); + AdjustWindowRect(&r, GetWindowLong(handle, GWL_STYLE), FALSE); + reqd_size = Rect(r.left, r.top, r.right, r.bottom); + + RECT current; + GetWindowRect(handle, ¤t); + + if (!(GetWindowLong(handle, GWL_STYLE) & WS_MAXIMIZE) && !fullscreenActive) { + // Ensure that the window isn't resized too large + if (wpos->cx > reqd_size.width()) { + wpos->cx = reqd_size.width(); + wpos->x = current.left; + } + if (wpos->cy > reqd_size.height()) { + wpos->cy = reqd_size.height(); + wpos->y = current.top; + } + } + } + break; + + // Resize child windows and update window size info we have cached. + + case WM_SIZE: + { + Point old_offset = desktopToClient(Point(0, 0)); + RECT r; + + // Resize child windows + GetClientRect(getHandle(), &r); + if (tb.isVisible()) { + MoveWindow(getFrameHandle(), 0, tb.getHeight(), + r.right, r.bottom - tb.getHeight(), TRUE); + } else { + MoveWindow(getFrameHandle(), 0, 0, r.right, r.bottom, TRUE); + } + tb.autoSize(); + + // Update the cached sizing information + GetWindowRect(frameHandle, &r); + window_size = Rect(r.left, r.top, r.right, r.bottom); + GetClientRect(frameHandle, &r); + client_size = Rect(r.left, r.top, r.right, r.bottom); + + // Determine whether scrollbars are required + calculateScrollBars(); + + // Redraw if required + if ((!old_offset.equals(desktopToClient(Point(0, 0))))) + InvalidateRect(frameHandle, 0, TRUE); + } + break; + + // -=- Bump-scrolling + + case WM_TIMER: + switch (wParam) { + case TIMER_BUMPSCROLL: + if (!setViewportOffset(scrolloffset.translate(bumpScrollDelta))) + bumpScrollTimer.stop(); + break; + case TIMER_POINTER_INTERVAL: + case TIMER_POINTER_3BUTTON: + ptr.handleTimer(callback, wParam); + break; + } + break; + + // -=- Track whether or not the window has focus + + case WM_SETFOCUS: + has_focus = true; + break; + case WM_KILLFOCUS: + has_focus = false; + cursorOutsideBuffer(); + // Restore the keyboard to a consistent state + kbd.releaseAllKeys(callback); + break; + + // -=- Handle the extra window menu items + + // Pass system menu messages to the callback and only attempt + // to process them ourselves if the callback returns false. + case WM_SYSCOMMAND: + // Call the supplied callback + if (callback->sysCommand(wParam, lParam)) + break; + + // - Not processed by the callback, so process it as a system message + switch (wParam & 0xfff0) { + + // When restored, ensure that full-screen mode is re-enabled if required. + case SC_RESTORE: + { + if (GetWindowLong(handle, GWL_STYLE) & WS_MINIMIZE) { + rfb::win32::SafeDefWindowProc(handle, msg, wParam, lParam); + setFullscreen(fullscreenRestore); + } + else if (fullscreenActive) + setFullscreen(false); + else + rfb::win32::SafeDefWindowProc(handle, msg, wParam, lParam); + + return 0; + } + + // If we are maximized or minimized then that cancels full-screen mode. + case SC_MINIMIZE: + case SC_MAXIMIZE: + fullscreenRestore = fullscreenActive; + setFullscreen(false); + break; + + // If the menu is about to be shown, make sure it's up to date + case SC_KEYMENU: + case SC_MOUSEMENU: + callback->refreshMenu(true); + break; + + }; + break; + + // Treat all menu commands as system menu commands + case WM_COMMAND: + SendMessage(handle, WM_SYSCOMMAND, wParam, lParam); + return 0; + + // -=- Handle keyboard input + + case WM_KEYUP: + case WM_KEYDOWN: + // Hook the MenuKey to pop-up the window menu + if (menuKey && (wParam == menuKey)) { + + bool ctrlDown = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0; + bool altDown = (GetAsyncKeyState(VK_MENU) & 0x8000) != 0; + bool shiftDown = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0; + if (!(ctrlDown || altDown || shiftDown)) { + + // If MenuKey is being released then pop-up the menu + if ((msg == WM_KEYDOWN)) { + // Make sure it's up to date + callback->refreshMenu(false); + + // Show it under the pointer + POINT pt; + GetCursorPos(&pt); + cursorInBuffer = false; + TrackPopupMenu(GetSystemMenu(handle, FALSE), + TPM_CENTERALIGN | TPM_VCENTERALIGN, pt.x, pt.y, 0, handle, 0); + } + + // Ignore the MenuKey keypress for both press & release events + return 0; + } + } + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + kbd.keyEvent(callback, wParam, lParam, (msg == WM_KEYDOWN) || (msg == WM_SYSKEYDOWN)); + return 0; + + // -=- Handle the window closing + + case WM_CLOSE: + vlog.debug("WM_CLOSE %x", handle); + callback->closeWindow(); + break; + + } + + return rfb::win32::SafeDefWindowProc(handle, msg, wParam, lParam); +} + +LRESULT +DesktopWindow::processFrameMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + switch (msg) { + + // -=- Paint the remote frame buffer + + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC paintDC = BeginPaint(frameHandle, &ps); + if (!paintDC) + throw rdr::SystemException("unable to BeginPaint", GetLastError()); + Rect pr = Rect(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom); + + if (!pr.is_empty()) { + + // Draw using the correct palette + PaletteSelector pSel(paintDC, windowPalette.getHandle()); + + if (buffer->bitmap) { + // Update the bitmap's palette + if (palette_changed) { + palette_changed = false; + buffer->refreshPalette(); + } + + // Get device context + BitmapDC bitmapDC(paintDC, buffer->bitmap); + + // Blit the border if required + Rect bufpos = desktopToClient(buffer->getRect()); + if (!pr.enclosed_by(bufpos)) { + vlog.debug("draw border"); + HBRUSH black = (HBRUSH) GetStockObject(BLACK_BRUSH); + RECT r; + SetRect(&r, 0, 0, bufpos.tl.x, client_size.height()); FillRect(paintDC, &r, black); + SetRect(&r, bufpos.tl.x, 0, bufpos.br.x, bufpos.tl.y); FillRect(paintDC, &r, black); + SetRect(&r, bufpos.br.x, 0, client_size.width(), client_size.height()); FillRect(paintDC, &r, black); + SetRect(&r, bufpos.tl.x, bufpos.br.y, bufpos.br.x, client_size.height()); FillRect(paintDC, &r, black); + } + + // Do the blit + Point buf_pos = clientToDesktop(pr.tl); + + if (!BitBlt(paintDC, pr.tl.x, pr.tl.y, pr.width(), pr.height(), + bitmapDC, buf_pos.x, buf_pos.y, SRCCOPY)) + throw rdr::SystemException("unable to BitBlt to window", GetLastError()); + } + } + + EndPaint(frameHandle, &ps); + + // - Notify the callback that a paint message has finished processing + callback->paintCompleted(); + } + return 0; + + // -=- Palette management + + case WM_PALETTECHANGED: + vlog.debug("WM_PALETTECHANGED"); + if ((HWND)wParam == frameHandle) { + vlog.debug("ignoring"); + break; + } + case WM_QUERYNEWPALETTE: + vlog.debug("re-selecting palette"); + { + WindowDC wdc(frameHandle); + PaletteSelector pSel(wdc, windowPalette.getHandle()); + if (pSel.isRedrawRequired()) { + InvalidateRect(frameHandle, 0, FALSE); + UpdateWindow(frameHandle); + } + } + return TRUE; + + case WM_VSCROLL: + case WM_HSCROLL: + { + Point delta; + int newpos = (msg == WM_VSCROLL) ? scrolloffset.y : scrolloffset.x; + + switch (LOWORD(wParam)) { + case SB_PAGEUP: newpos -= 50; break; + case SB_PAGEDOWN: newpos += 50; break; + case SB_LINEUP: newpos -= 5; break; + case SB_LINEDOWN: newpos += 5; break; + case SB_THUMBTRACK: + case SB_THUMBPOSITION: newpos = HIWORD(wParam); break; + default: vlog.info("received unknown scroll message"); + }; + + if (msg == WM_HSCROLL) + setViewportOffset(Point(newpos, scrolloffset.y)); + else + setViewportOffset(Point(scrolloffset.x, newpos)); + + SCROLLINFO si; + si.cbSize = sizeof(si); + si.fMask = SIF_POS; + si.nPos = newpos; + SetScrollInfo(frameHandle, (msg == WM_VSCROLL) ? SB_VERT : SB_HORZ, &si, TRUE); + } + break; + + // -=- Cursor shape/visibility handling + + case WM_SETCURSOR: + if (LOWORD(lParam) != HTCLIENT) + break; + SetCursor(cursorInBuffer ? dotCursor : arrowCursor); + return TRUE; + + case WM_MOUSELEAVE: + trackingMouseLeave = false; + cursorOutsideBuffer(); + return 0; + + // -=- Mouse input handling + + case WM_MOUSEMOVE: + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: +#ifdef WM_MOUSEWHEEL + case WM_MOUSEWHEEL: +#endif + if (has_focus) + { + if (!trackingMouseLeave) { + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = frameHandle; + _TrackMouseEvent(&tme); + trackingMouseLeave = true; + } + int mask = 0; + if (LOWORD(wParam) & MK_LBUTTON) mask |= 1; + if (LOWORD(wParam) & MK_MBUTTON) mask |= 2; + if (LOWORD(wParam) & MK_RBUTTON) mask |= 4; + +#ifdef WM_MOUSEWHEEL + if (msg == WM_MOUSEWHEEL) { + int delta = (short)HIWORD(wParam); + int repeats = (abs(delta)+119) / 120; + int wheelMask = (delta > 0) ? 8 : 16; + vlog.debug("repeats %d, mask %d\n",repeats,wheelMask); + for (int i=0; igetRect().contains(p); + if (!cursorInBuffer) { + cursorOutsideBuffer(); + break; + } + + // If we're locally rendering the cursor then redraw it + if (cursorAvailable) { + // - Render the cursor! + if (!p.equals(cursorPos)) { + hideLocalCursor(); + cursorPos = p; + showLocalCursor(); + if (cursorVisible) + hideSystemCursor(); + } + } + + // If we are doing bump-scrolling then try that first... + if (processBumpScroll(clientPos)) + break; + + // Send a pointer event to the server + ptr.pointerEvent(callback, p, mask); + oldpos = p; +#ifdef WM_MOUSEWHEEL + } +#endif + } else { + cursorOutsideBuffer(); + } + break; + } + + return rfb::win32::SafeDefWindowProc(frameHandle, msg, wParam, lParam); +} + + +void +DesktopWindow::hideLocalCursor() { + // - Blit the cursor backing store over the cursor + // *** ALWAYS call this BEFORE changing buffer PF!!! + if (cursorVisible) { + cursorVisible = false; + buffer->imageRect(cursorBackingRect, cursorBacking.data); + invalidateDesktopRect(cursorBackingRect); + } +} + +void +DesktopWindow::showLocalCursor() { + if (cursorAvailable && !cursorVisible && cursorInBuffer) { + if (!buffer->getPF().equal(cursor.getPF()) || + cursor.getRect().is_empty()) { + vlog.info("attempting to render invalid local cursor"); + cursorAvailable = false; + showSystemCursor(); + return; + } + cursorVisible = true; + + cursorBackingRect = cursor.getRect().translate(cursorPos).translate(cursor.hotspot.negate()); + cursorBackingRect = cursorBackingRect.intersect(buffer->getRect()); + buffer->getImage(cursorBacking.data, cursorBackingRect); + + renderLocalCursor(); + + invalidateDesktopRect(cursorBackingRect); + } +} + +void DesktopWindow::cursorOutsideBuffer() +{ + cursorInBuffer = false; + hideLocalCursor(); + showSystemCursor(); +} + +void +DesktopWindow::renderLocalCursor() +{ + Rect r = cursor.getRect(); + r = r.translate(cursorPos).translate(cursor.hotspot.negate()); + buffer->maskRect(r, cursor.data, cursor.mask.buf); +} + +void +DesktopWindow::hideSystemCursor() { + if (systemCursorVisible) { + vlog.debug("hide system cursor"); + systemCursorVisible = false; + ShowCursor(FALSE); + } +} + +void +DesktopWindow::showSystemCursor() { + if (!systemCursorVisible) { + vlog.debug("show system cursor"); + systemCursorVisible = true; + ShowCursor(TRUE); + } +} + + +bool +DesktopWindow::invalidateDesktopRect(const Rect& crect) { + Rect rect = desktopToClient(crect); + if (rect.intersect(client_size).is_empty()) return false; + RECT invalid = {rect.tl.x, rect.tl.y, rect.br.x, rect.br.y}; + InvalidateRect(frameHandle, &invalid, FALSE); + return true; +} + + +void +DesktopWindow::notifyClipboardChanged(const char* text, int len) { + callback->clientCutText(text, len); +} + + +void +DesktopWindow::setPF(const PixelFormat& pf) { + // If the cursor is the wrong format then clear it + if (!pf.equal(buffer->getPF())) + setCursor(0, 0, Point(), 0, 0); + + // Update the desktop buffer + buffer->setPF(pf); + + // Redraw the window + InvalidateRect(handle, 0, 0); +} + +void +DesktopWindow::setSize(int w, int h) { + vlog.debug("setSize %dx%d", w, h); + + // If the locally-rendered cursor is visible then remove it + hideLocalCursor(); + + // Resize the backing buffer + buffer->setSize(w, h); + + // If the window is not maximised or full-screen then resize it + if (!(GetWindowLong(handle, GWL_STYLE) & WS_MAXIMIZE) && !fullscreenActive) { + // Resize the window to the required size + RECT r = {0, 0, w, h}; + AdjustWindowRectEx(&r, GetWindowLong(frameHandle, GWL_STYLE), FALSE, + GetWindowLong(frameHandle, GWL_EXSTYLE)); + if (tb.isVisible()) + r.bottom += tb.getHeight(); + AdjustWindowRect(&r, GetWindowLong(handle, GWL_STYLE), FALSE); + + // Resize about the center of the window, and clip to current monitor + MonitorInfo mi(handle); + resizeWindow(handle, r.right-r.left, r.bottom-r.top); + mi.clipTo(handle); + } else { + // Ensure the screen contents are consistent + InvalidateRect(frameHandle, 0, FALSE); + } + + // Enable/disable scrollbars as appropriate + calculateScrollBars(); +} + +void +DesktopWindow::setCursor(int w, int h, const Point& hotspot, void* data, void* mask) { + hideLocalCursor(); + + cursor.hotspot = hotspot; + + cursor.setSize(w, h); + cursor.setPF(buffer->getPF()); + cursor.imageRect(cursor.getRect(), data); + memcpy(cursor.mask.buf, mask, cursor.maskLen()); + cursor.crop(); + + cursorBacking.setSize(w, h); + cursorBacking.setPF(buffer->getPF()); + + cursorAvailable = true; + + showLocalCursor(); +} + +PixelFormat +DesktopWindow::getNativePF() const { + vlog.debug("getNativePF()"); + return WindowDC(handle).getPF(); +} + + +void +DesktopWindow::refreshWindowPalette(int start, int count) { + vlog.debug("refreshWindowPalette(%d, %d)", start, count); + + Colour colours[256]; + if (count > 256) { + vlog.debug("%d palette entries", count); + throw rdr::Exception("too many palette entries"); + } + + // Copy the palette from the DIBSectionBuffer + ColourMap* cm = buffer->getColourMap(); + if (!cm) return; + for (int i=0; ilookup(i, &r, &g, &b); + colours[i].r = r; + colours[i].g = g; + colours[i].b = b; + } + + // Set the window palette + windowPalette.setEntries(start, count, colours); + + // Cause the window to be redrawn + palette_changed = true; + InvalidateRect(handle, 0, FALSE); +} + + +void DesktopWindow::calculateScrollBars() { + // Calculate the required size of window + DWORD current_style = GetWindowLong(frameHandle, GWL_STYLE); + DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL); + DWORD style_ex = GetWindowLong(frameHandle, GWL_EXSTYLE); + DWORD old_style; + RECT r; + SetRect(&r, 0, 0, buffer->width(), buffer->height()); + AdjustWindowRectEx(&r, style, FALSE, style_ex); + Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom); + + if (!bumpScroll) { + // We only enable scrollbars if bump-scrolling is not active. + // Effectively, this means if full-screen is not active, + // but I think it's better to make these things explicit. + + // Work out whether scroll bars are required + do { + old_style = style; + + if (!(style & WS_HSCROLL) && (reqd_size.width() > window_size.width())) { + style |= WS_HSCROLL; + reqd_size.br.y += GetSystemMetrics(SM_CXHSCROLL); + } + if (!(style & WS_VSCROLL) && (reqd_size.height() > window_size.height())) { + style |= WS_VSCROLL; + reqd_size.br.x += GetSystemMetrics(SM_CXVSCROLL); + } + } while (style != old_style); + } + + // Tell Windows to update the window style & cached settings + if (style != current_style) { + SetWindowLong(frameHandle, GWL_STYLE, style); + SetWindowPos(frameHandle, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); + } + + // Update the scroll settings + SCROLLINFO si; + if (style & WS_VSCROLL) { + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + si.nMin = 0; + si.nMax = buffer->height(); + si.nPage = buffer->height() - (reqd_size.height() - window_size.height()); + maxscrolloffset.y = max(0, si.nMax-si.nPage); + scrolloffset.y = min(maxscrolloffset.y, scrolloffset.y); + si.nPos = scrolloffset.y; + SetScrollInfo(frameHandle, SB_VERT, &si, TRUE); + } + if (style & WS_HSCROLL) { + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + si.nMin = 0; + si.nMax = buffer->width(); + si.nPage = buffer->width() - (reqd_size.width() - window_size.width()); + maxscrolloffset.x = max(0, si.nMax-si.nPage); + scrolloffset.x = min(maxscrolloffset.x, scrolloffset.x); + si.nPos = scrolloffset.x; + SetScrollInfo(frameHandle, SB_HORZ, &si, TRUE); + } + + // Update the cached client size + GetClientRect(frameHandle, &r); + client_size = Rect(r.left, r.top, r.right, r.bottom); +} + + +void +DesktopWindow::setName(const char* name) { + SetWindowText(handle, TStr(name)); +} + + +void +DesktopWindow::serverCutText(const char* str, int len) { + CharArray t(len+1); + memcpy(t.buf, str, len); + t.buf[len] = 0; + clipboard.setClipText(t.buf); +} + + +void DesktopWindow::fillRect(const Rect& r, Pixel pix) { + if (cursorBackingRect.overlaps(r)) hideLocalCursor(); + buffer->fillRect(r, pix); + invalidateDesktopRect(r); +} +void DesktopWindow::imageRect(const Rect& r, void* pixels) { + if (cursorBackingRect.overlaps(r)) hideLocalCursor(); + buffer->imageRect(r, pixels); + invalidateDesktopRect(r); +} +void DesktopWindow::copyRect(const Rect& r, int srcX, int srcY) { + if (cursorBackingRect.overlaps(r) || + cursorBackingRect.overlaps(Rect(srcX, srcY, srcX+r.width(), srcY+r.height()))) + hideLocalCursor(); + buffer->copyRect(r, Point(r.tl.x-srcX, r.tl.y-srcY)); + invalidateDesktopRect(r); +} + +void DesktopWindow::invertRect(const Rect& r) { + int stride; + rdr::U8* p = buffer->getPixelsRW(r, &stride); + for (int y = 0; y < r.height(); y++) { + for (int x = 0; x < r.width(); x++) { + switch (buffer->getPF().bpp) { + case 8: ((rdr::U8* )p)[x+y*stride] ^= 0xff; break; + case 16: ((rdr::U16*)p)[x+y*stride] ^= 0xffff; break; + case 32: ((rdr::U32*)p)[x+y*stride] ^= 0xffffffff; break; + } + } + } + invalidateDesktopRect(r); +} diff --git a/vncviewer/DesktopWindow.h b/vncviewer/DesktopWindow.h new file mode 100644 index 00000000..dd1bcdca --- /dev/null +++ b/vncviewer/DesktopWindow.h @@ -0,0 +1,254 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- DesktopWindow.h + +// Each VNC connection instance (CConn) creates a DesktopWindow the +// server initialisation message has been received. The CConn is +// responsible for all RFB-specific and network issues. The +// DesktopWindow is responsible for all GUI management issues. +// +// DesktopWindow provides a FullFramePixelBuffer interface for the +// CConn to render updates into. It also requires a callback object +// to be supplied, which will be notified when various events occur. + +#ifndef __RFB_WIN32_DESKTOP_WINDOW_H__ +#define __RFB_WIN32_DESKTOP_WINDOW_H__ + +#include +#include +#include +#include +#include +#include +#include + + +namespace rfb { + + namespace win32 { + + class DesktopWindow : rfb::win32::Clipboard::Notifier { + public: + class Callback; + + DesktopWindow(Callback* cb_); + ~DesktopWindow(); + + // - Window message handling procedure + LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam); + + // - Window message handling procedure for the framebuffer window + LRESULT processFrameMessage(UINT msg, WPARAM wParam, LPARAM lParam); + + // - Determine the native pixel format of the window + // This can (and often will) differ from the PixelBuffer format + PixelFormat getNativePF() const; + + // - Get the underlying window handle + // This is used by F8Menu to modify the window's menu + HWND getHandle() const {return handle;} + + // - Get the framebuffer window handle + HWND getFrameHandle() const {return frameHandle;} + + // - Set the window title + void setName(const char* name); + + // - Set the key that causes the system/F8 menu to be displayed + void setMenuKey(rdr::U8 key) { menuKey = key; } + + // - Pointer event handling + void setEmulate3(bool em3) { ptr.enableEmulate3(em3); } + void setPointerEventInterval(int interval) { ptr.enableInterval(interval); } + + // - Set the pixel format, size etc of the underlying PixelBuffer + void setPF(const PixelFormat& pf); + PixelFormat getPF() const { return buffer->getPF(); } + void setSize(int w, int h); + void setColour(int i, int r, int g, int b) {buffer->setColour(i, r, g, b);} + + // - Set the cursor to render when the pointer is within the desktop buffer + void setCursor(int w, int h, const Point& hotspot, void* data, void* mask); + void showCursor() { showLocalCursor(); } + + // - Set whether to show the toolbar + // FIXME: Actually show/hide the toolbar. + void setShowToolbar(bool st) { showToolbar = st; } + + // - Set the window fullscreen / determine whether it is fullscreen + void setFullscreen(bool fs); + bool isFullscreen() { return fullscreenActive; } + + // - Set whether to disable special Windows keys & pass them straight to server + void setDisableWinKeys(bool dwk); + + // - Set/get which monitor the window should be displayed on + void setMonitor(const char* monitor); + char* getMonitor() const; + + // - Set the local clipboard + void serverCutText(const char* str, int len); + + // - Draw into the desktop buffer & update the window + void fillRect(const Rect& r, Pixel pix); + void imageRect(const Rect& r, void* pixels); + void copyRect(const Rect& r, int srcX, int srcY); + + void invertRect(const Rect& r); + + // - Update the window palette if the display is palette-based. + // Colours are pulled from the desktop buffer's ColourMap. + // Only the specified range of indexes is dealt with. + // After the update, the entire window is redrawn. + void refreshWindowPalette(int start, int count); + + // Clipboard::Notifier interface + void notifyClipboardChanged(const char* text, int len); + + // DesktopWindow Callback interface + class Callback : public InputHandler { + public: + virtual ~Callback() {} + virtual void displayChanged() = 0; + virtual void paintCompleted() = 0; + virtual bool sysCommand(WPARAM wParam, LPARAM lParam) = 0; + virtual void closeWindow() = 0; + virtual void refreshMenu(bool enableSysItems) = 0; + }; + + // Currently accessible so that the CConn can releaseAllKeys & check + // whether Ctrl and Alt are down... + rfb::win32::CKeyboard kbd; + + protected: + // Routines to convert between Desktop and client (window) coordinates + Point desktopToClient(const Point& p) { + Point pos = p; + if (client_size.width() > buffer->width()) + pos.x += (client_size.width() - buffer->width()) / 2; + else if (client_size.width() < buffer->width()) + pos.x -= scrolloffset.x; + if (client_size.height() > buffer->height()) + pos.y += (client_size.height() - buffer->height()) / 2; + else if (client_size.height() < buffer->height()) + pos.y -= scrolloffset.y; + return pos; + } + Rect desktopToClient(const Rect& r) { + return Rect(desktopToClient(r.tl), desktopToClient(r.br)); + } + Point clientToDesktop(const Point& p) { + Point pos = p; + if (client_size.width() > buffer->width()) + pos.x -= (client_size.width() - buffer->width()) / 2; + else if (client_size.width() < buffer->width()) + pos.x += scrolloffset.x; + if (client_size.height() > buffer->height()) + pos.y -= (client_size.height() - buffer->height()) / 2; + else if (client_size.height() < buffer->height()) + pos.y += scrolloffset.y; + return pos; + } + Rect clientToDesktop(const Rect& r) { + return Rect(clientToDesktop(r.tl), clientToDesktop(r.br)); + } + + // Internal routine used by the scrollbars & bump scroller to select + // the portion of the Desktop to display + bool setViewportOffset(const Point& tl); + + // Bump scroll handling. Bump scrolling is used if the window is + // in fullscreen mode and the Desktop is larger than the window + bool processBumpScroll(const Point& cursorPos); + void setBumpScroll(bool on); + bool bumpScroll; + Point bumpScrollDelta; + IntervalTimer bumpScrollTimer; + + // Locally-rendered VNC cursor + void hideLocalCursor(); + void showLocalCursor(); + void renderLocalCursor(); + + // The system-rendered cursor + void hideSystemCursor(); + void showSystemCursor(); + + // cursorOutsideBuffer() is called whenever we detect that the mouse has + // moved outside the desktop. It restores the system arrow cursor. + void cursorOutsideBuffer(); + + // Returns true if part of the supplied rect is visible, false otherwise + bool invalidateDesktopRect(const Rect& crect); + + // Determine whether or not we need to enable/disable scrollbars and set the + // window style accordingly + void calculateScrollBars(); + + // Win32-specific input handling + rfb::win32::CPointer ptr; + Point oldpos; + rfb::win32::Clipboard clipboard; + + // Palette handling + LogicalPalette windowPalette; + bool palette_changed; + + // - Full-screen mode + RECT fullscreenOldRect; + DWORD fullscreenOldFlags; + bool fullscreenActive; + bool fullscreenRestore; + + // Cursor handling + Cursor cursor; + bool systemCursorVisible; // Should system-cursor be drawn? + bool trackingMouseLeave; + bool cursorInBuffer; // Is cursor position within server buffer? (ONLY for LocalCursor) + bool cursorVisible; // Is cursor currently rendered? + bool cursorAvailable; // Is cursor available for rendering? + Point cursorPos; + ManagedPixelBuffer cursorBacking; + Rect cursorBackingRect; + + // ToolBar handling + ViewerToolBar tb; + bool showToolbar; + + // Local window state + win32::DIBSectionBuffer* buffer; + bool has_focus; + Rect window_size; + Rect client_size; + Point scrolloffset; + Point maxscrolloffset; + HWND handle; + HWND frameHandle; + rdr::U8 menuKey; + + Callback* callback; + }; + + }; + +}; + +#endif // __RFB_WIN32_DESKTOP_WINDOW_H__ + + diff --git a/vncviewer/InfoDialog.cxx b/vncviewer/InfoDialog.cxx index 0d2313a5..e74896dc 100644 --- a/vncviewer/InfoDialog.cxx +++ b/vncviewer/InfoDialog.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -17,7 +17,7 @@ */ #include #include -#include +#include #include #include #include @@ -29,37 +29,37 @@ using namespace rfb::win32; static LogWriter vlog("Info"); -bool InfoDialog::showDialog(CView* vw) { - view = vw; +bool InfoDialog::showDialog(CConn* cc) { + conn = cc; return Dialog::showDialog(MAKEINTRESOURCE(IDD_CONNECTION_INFO)); } void InfoDialog::initDialog() { char buf[256]; - setItemString(IDC_INFO_NAME, TStr(view->cp.name())); + setItemString(IDC_INFO_NAME, TStr(conn->cp.name())); - setItemString(IDC_INFO_HOST, TCharArray(view->sock->getPeerAddress()).buf); + setItemString(IDC_INFO_HOST, TCharArray(conn->getSocket()->getPeerAddress()).buf); - Rect bufRect = view->buffer->getRect(); - sprintf(buf, "%dx%d", bufRect.width(), bufRect.height()); + sprintf(buf, "%dx%d", conn->cp.width, conn->cp.height); setItemString(IDC_INFO_SIZE, TStr(buf)); - view->cp.pf().print(buf, 256); + conn->cp.pf().print(buf, 256); setItemString(IDC_INFO_PF, TStr(buf)); - view->serverDefaultPF.print(buf, 256); + conn->getServerDefaultPF().print(buf, 256); setItemString(IDC_INFO_DEF_PF, TStr(buf)); - setItemString(IDC_REQUESTED_ENCODING, TStr(encodingName(view->getOptions().preferredEncoding))); - setItemString(IDC_LAST_ENCODING, TStr(encodingName(view->lastUsedEncoding()))); + setItemString(IDC_REQUESTED_ENCODING, TStr(encodingName(conn->getOptions().preferredEncoding))); + setItemString(IDC_LAST_ENCODING, TStr(encodingName(conn->lastUsedEncoding()))); - sprintf(buf, "%d kbits/s", view->sock->inStream().kbitsPerSecond()); + sprintf(buf, "%d kbits/s", conn->getSocket()->inStream().kbitsPerSecond()); setItemString(IDC_INFO_LINESPEED, TStr(buf)); - sprintf(buf, "%d.%d", view->cp.majorVersion, view->cp.minorVersion); + sprintf(buf, "%d.%d", conn->cp.majorVersion, conn->cp.minorVersion); setItemString(IDC_INFO_VERSION, TStr(buf)); - int secType = view->getCurrentCSecurity()->getType(); - setItemString(IDC_INFO_SECURITY, TStr(secTypeName(secType))); + const CSecurity* cSec = conn->getCurrentCSecurity(); + setItemString(IDC_INFO_SECURITY, TStr(secTypeName(cSec->getType()))); + setItemString(IDC_INFO_ENCRYPTION, TStr(cSec->description())); } diff --git a/vncviewer/InfoDialog.h b/vncviewer/InfoDialog.h index 7a64d383..752d53c5 100644 --- a/vncviewer/InfoDialog.h +++ b/vncviewer/InfoDialog.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -30,15 +30,15 @@ namespace rfb { namespace win32 { - class CView; + class CConn; class InfoDialog : Dialog { public: - InfoDialog() : Dialog(GetModuleHandle(0)), view(0) {} - virtual bool showDialog(CView* vw); + InfoDialog() : Dialog(GetModuleHandle(0)), conn(0) {} + virtual bool showDialog(CConn* vw); virtual void initDialog(); protected: - CView* view; + CConn* conn; }; }; diff --git a/vncviewer/ListenServer.h b/vncviewer/ListenServer.h new file mode 100644 index 00000000..4d1590c0 --- /dev/null +++ b/vncviewer/ListenServer.h @@ -0,0 +1,56 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- ListenServer.h + +#ifndef __RFB_WIN32_LISTEN_SERVER_H__ +#define __RFB_WIN32_LISTEN_SERVER_H__ + +#include +#include +#include +#include +#include + + +namespace rfb { + namespace win32 { + + class ListenServer : MsgWindow { + public: + ListenServer(network::SocketListener* l) : MsgWindow(_T("rfb::win32::ListenServer")), sock(l) { + if (WSAAsyncSelect(l->getFd(), getHandle(), WM_USER, FD_ACCEPT) == SOCKET_ERROR) + throw rdr::SystemException("unable to monitor listen socket", WSAGetLastError()); + } + + LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + if (msg == WM_USER) { + network::Socket* newConn = sock->accept(); + Thread* newThread = new CConnThread(newConn, true); + return 0; + } + return MsgWindow::processMessage(msg, wParam, lParam); + } + protected: + network::SocketListener* sock; + }; + + }; +}; + +#endif \ No newline at end of file diff --git a/vncviewer/ListenTrayIcon.h b/vncviewer/ListenTrayIcon.h new file mode 100644 index 00000000..7e334d91 --- /dev/null +++ b/vncviewer/ListenTrayIcon.h @@ -0,0 +1,95 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// -=- ListenTrayIcon.h + +#ifndef __RFB_WIN32_LISTEN_TRAY_ICON_H__ +#define __RFB_WIN32_LISTEN_TRAY_ICON_H__ + +#include +#include + +namespace rfb { + namespace win32 { + + class ListenTrayIcon : public TrayIcon { + public: + ListenTrayIcon() { + setIcon(IDI_ICON); + setToolTip(_T("VNC Viewer")); + } + virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + switch(msg) { + + case WM_USER: + switch (lParam) { + case WM_LBUTTONDBLCLK: + SendMessage(getHandle(), WM_COMMAND, ID_NEW_CONNECTION, 0); + break; + case WM_RBUTTONUP: + HMENU menu = LoadMenu(GetModuleHandle(0), MAKEINTRESOURCE(IDR_TRAY)); + HMENU trayMenu = GetSubMenu(menu, 0); + + // First item is New Connection, the default + SetMenuDefaultItem(trayMenu, ID_NEW_CONNECTION, FALSE); + + // SetForegroundWindow is required, otherwise Windows ignores the + // TrackPopupMenu because the window isn't the foreground one, on + // some older Windows versions... + SetForegroundWindow(getHandle()); + + // Display the menu + POINT pos; + GetCursorPos(&pos); + TrackPopupMenu(trayMenu, 0, pos.x, pos.y, 0, getHandle(), 0); + break; + } + return 0; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case ID_NEW_CONNECTION: + { + Thread* connThread = new CConnThread(); + break; + } + case ID_OPTIONS: + OptionsDialog::global.showDialog(0); + break; + case ID_ABOUT: + AboutDialog::instance.showDialog(); + break; + case ID_CLOSE: + SendMessage(getHandle(), WM_CLOSE, 0, 0); + break; + } + return 0; + + case WM_CLOSE: + PostQuitMessage(0); + return 0; + } + + return TrayIcon::processMessage(msg, wParam, lParam); + } + }; + + }; +}; + +#endif // __RFB_WIN32_LISTEN_TRAY_ICON_H__ \ No newline at end of file diff --git a/vncviewer/MRU.h b/vncviewer/MRU.h index f065a06e..ae703b3a 100644 --- a/vncviewer/MRU.h +++ b/vncviewer/MRU.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -18,13 +18,10 @@ #ifndef __VIEWER_MRU_H__ #define __VIEWER_MRU_H__ -#define WIN32_LEAN_AND_MEAN #include #include #include - #include - #include #include @@ -85,7 +82,7 @@ namespace rfb { TCharArray keyname = rdr::HexOutStream::binToHexStr(&order[i], 1); try { TCharArray hostname = key.getString(keyname.buf); - if (strcmp(name, CStr(hostname.buf)) == 0) { + if (stricmp(name, CStr(hostname.buf)) == 0) { keycode = order[i]; found = true; break; @@ -109,8 +106,6 @@ namespace rfb { orderlen = 0; } - printf("keycode=%d\n", (int)keycode); - orderlen++; int i, j=orderlen-1; for (i=0; i -#include - #include -#include +#include #include #include -#include +#include +#include #include #include +#include +#include using namespace rfb; using namespace rfb::win32; @@ -38,22 +34,22 @@ static LogWriter vlog("Options"); struct OptionsInfo { - CView* view; - CViewOptions options; + CConn* view; + CConnOptions options; }; OptionsDialog rfb::win32::OptionsDialog::global; -class VNCviewerOptions : public PropSheet { +class ViewerOptions : public PropSheet { public: - VNCviewerOptions(OptionsInfo& info_, std::list pages) + ViewerOptions(OptionsInfo& info_, std::list pages) : PropSheet(GetModuleHandle(0), info_.view ? _T("VNC Viewer Options") : _T("VNC Viewer Defaults"), pages), info(info_), changed(false) { } - ~VNCviewerOptions() { + ~ViewerOptions() { if (changed) { if (info.view) // Apply the settings to the supplied session object @@ -121,7 +117,7 @@ public: dlg->options.preferredEncoding = encodingHextile; if (isItemChecked(IDC_ENCODING_RAW)) dlg->options.preferredEncoding = encodingRaw; - ((VNCviewerOptions*)propSheet)->setChanged(); + ((ViewerOptions*)propSheet)->setChanged(); return true; } virtual bool onCommand(int id, int cmd) { @@ -165,6 +161,7 @@ public: enableItem(IDC_PROTOCOL_3_3, (!dlg->view) || (dlg->view->state() != CConnection::RFBSTATE_NORMAL)); setItemChecked(IDC_PROTOCOL_3_3, dlg->options.protocol3_3); setItemChecked(IDC_ACCEPT_BELL, dlg->options.acceptBell); + setItemChecked(IDC_AUTO_RECONNECT, dlg->options.autoReconnect); setItemChecked(IDC_SHOW_TOOLBAR, dlg->options.showToolbar); } virtual bool onOk() { @@ -174,8 +171,9 @@ public: dlg->options.useDesktopResize = isItemChecked(IDC_DESKTOP_RESIZE); dlg->options.protocol3_3 = isItemChecked(IDC_PROTOCOL_3_3); dlg->options.acceptBell = isItemChecked(IDC_ACCEPT_BELL); + dlg->options.autoReconnect = isItemChecked(IDC_AUTO_RECONNECT); dlg->options.showToolbar = isItemChecked(IDC_SHOW_TOOLBAR); - ((VNCviewerOptions*)propSheet)->setChanged(); + ((ViewerOptions*)propSheet)->setChanged(); return true; } protected: @@ -193,6 +191,8 @@ public: setItemChecked(IDC_SEND_SYSKEYS, dlg->options.sendSysKeys); setItemChecked(IDC_CLIENT_CUTTEXT, dlg->options.clientCutText); setItemChecked(IDC_SERVER_CUTTEXT, dlg->options.serverCutText); + setItemChecked(IDC_DISABLE_WINKEYS, dlg->options.disableWinKeys && !osVersion.isPlatformWindows); + enableItem(IDC_DISABLE_WINKEYS, !osVersion.isPlatformWindows); setItemChecked(IDC_EMULATE3, dlg->options.emulate3); setItemChecked(IDC_POINTER_INTERVAL, dlg->options.pointerEventInterval != 0); @@ -216,6 +216,7 @@ public: dlg->options.sendSysKeys = isItemChecked(IDC_SEND_SYSKEYS); dlg->options.clientCutText = isItemChecked(IDC_CLIENT_CUTTEXT); dlg->options.serverCutText = isItemChecked(IDC_SERVER_CUTTEXT); + dlg->options.disableWinKeys = isItemChecked(IDC_DISABLE_WINKEYS); dlg->options.emulate3 = isItemChecked(IDC_EMULATE3); dlg->options.pointerEventInterval = isItemChecked(IDC_POINTER_INTERVAL) ? 200 : 0; @@ -229,14 +230,13 @@ public: else dlg->options.setMenuKey(CStr(keyName.buf)); - ((VNCviewerOptions*)propSheet)->setChanged(); + ((ViewerOptions*)propSheet)->setChanged(); return true; } protected: OptionsInfo* dlg; }; - class DefaultsPage : public PropSheetPage { public: DefaultsPage(OptionsInfo* dlg_) @@ -247,10 +247,9 @@ public: enableItem(IDC_SAVE_CONFIG, dlg->options.configFileName.buf); } virtual bool onCommand(int id, int cmd) { - HWND hwnd = dlg->view ? dlg->view->getHandle() : 0; switch (id) { case IDC_LOAD_DEFAULTS: - dlg->options = CViewOptions(); + dlg->options = CConnOptions(); break; case IDC_SAVE_DEFAULTS: propSheet->commitPages(); @@ -262,7 +261,7 @@ public: case IDC_SAVE_CONFIG: propSheet->commitPages(); dlg->options.writeToFile(dlg->options.configFileName.buf); - MsgBox(hwnd, _T("Options saved successfully"), + MsgBox(handle, _T("Options saved successfully"), MB_OK | MB_ICONINFORMATION); return 0; case IDC_SAVE_CONFIG_AS: @@ -281,7 +280,7 @@ public: #else ofn.lStructSize = sizeof(ofn); #endif - ofn.hwndOwner = hwnd; + ofn.hwndOwner = handle; ofn.lpstrFilter = _T("VNC Connection Options\000*.vnc\000"); ofn.lpstrFile = newFilename; currentDir[0] = 0; @@ -298,7 +297,7 @@ public: // Save the Options dlg->options.writeToFile(CStr(newFilename)); - MsgBox(hwnd, _T("Options saved successfully"), + MsgBox(handle, _T("Options saved successfully"), MB_OK | MB_ICONINFORMATION); return 0; }; @@ -313,7 +312,7 @@ protected: OptionsDialog::OptionsDialog() : visible(false) { } -bool OptionsDialog::showDialog(CView* view, bool capture) { +bool OptionsDialog::showDialog(CConn* view, bool capture) { if (visible) return false; visible = true; @@ -331,8 +330,9 @@ bool OptionsDialog::showDialog(CView* view, bool capture) { DefaultsPage defPage(&info); if (view) pages.push_back(&defPage); // Show the property sheet - VNCviewerOptions dialog(info, pages); - dialog.showPropSheet(view ? view->getHandle() : 0, false, false, capture); + ViewerOptions dialog(info, pages); + dialog.showPropSheet(view && view->getWindow() ? view->getWindow()->getHandle() : 0, + false, false, capture); visible = false; return dialog.changed; diff --git a/vncviewer/OptionsDialog.h b/vncviewer/OptionsDialog.h index eec9b96a..fcddc71c 100644 --- a/vncviewer/OptionsDialog.h +++ b/vncviewer/OptionsDialog.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -23,19 +23,18 @@ #ifndef __RFB_WIN32_OPTIONS_DIALOG_H__ #define __RFB_WIN32_OPTIONS_DIALOG_H__ -#include #include namespace rfb { namespace win32 { - class CView; + class CConn; class OptionsDialog { public: OptionsDialog(); - virtual bool showDialog(CView* cfg, bool capture=false); + virtual bool showDialog(CConn* cfg, bool capture=false); static OptionsDialog global; protected: diff --git a/vncviewer/UserPasswdDialog.cxx b/vncviewer/UserPasswdDialog.cxx index 8ab4ba4c..2eea0ea0 100644 --- a/vncviewer/UserPasswdDialog.cxx +++ b/vncviewer/UserPasswdDialog.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -18,12 +18,14 @@ #include #include +#include using namespace rfb; using namespace rfb::win32; -UserPasswdDialog::UserPasswdDialog() : Dialog(GetModuleHandle(0)), showUsername(false) { +UserPasswdDialog::UserPasswdDialog() : Dialog(GetModuleHandle(0)), + showUsername(false), showPassword(false) { } @@ -36,18 +38,18 @@ bool UserPasswdDialog::showDialog() { } void UserPasswdDialog::initDialog() { - if (username.buf) { + if (username.buf) setItemString(IDC_USERNAME, username.buf); - tstrFree(username.takeBuf()); - } - if (password.buf) { + if (password.buf) setItemString(IDC_PASSWORD, password.buf); - tstrFree(password.takeBuf()); - } if (!showUsername) { setItemString(IDC_USERNAME, _T("")); enableItem(IDC_USERNAME, false); } + if (!showPassword) { + setItemString(IDC_PASSWORD, _T("")); + enableItem(IDC_PASSWORD, false); + } if (description.buf) { TCharArray title(128); GetWindowText(handle, title.buf, 128); @@ -59,26 +61,25 @@ void UserPasswdDialog::initDialog() { } bool UserPasswdDialog::onOk() { - username.buf = getItemString(IDC_USERNAME); - password.buf = getItemString(IDC_PASSWORD); + username.replaceBuf(getItemString(IDC_USERNAME)); + password.replaceBuf(getItemString(IDC_PASSWORD)); return true; } -bool UserPasswdDialog::getUserPasswd(char** user, char** passwd) { - bool result = false; +void UserPasswdDialog::getUserPasswd(char** user, char** passwd) { showUsername = user != 0; + showPassword = passwd != 0; if (user && *user) - username.buf = tstrDup(*user); + username.replaceBuf(tstrDup(*user)); if (passwd && *passwd) - password.buf = tstrDup(*passwd); - if (showDialog()) { - if (user) - *user = strDup(username.buf); + password.replaceBuf(tstrDup(*passwd)); + + if (!showDialog()) + throw rfb::AuthCancelledException(); + + if (user) + *user = strDup(username.buf); + if (passwd) *passwd = strDup(password.buf); - result = true; - } - tstrFree(username.takeBuf()); - tstrFree(password.takeBuf()); - return result; } diff --git a/vncviewer/UserPasswdDialog.h b/vncviewer/UserPasswdDialog.h index 998a49f1..bf006f4d 100644 --- a/vncviewer/UserPasswdDialog.h +++ b/vncviewer/UserPasswdDialog.h @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -19,6 +19,10 @@ // -=- UserPasswdDialog.h // Username and password dialog for VNC Viewer 4.0 +// Note that the password and username fields are only freed +// when the dialog instance is deleted - it is important to +// ensure that the instance is deleted as early as possible, to +// avoid the password being retained in memory for too long. #ifndef __RFB_WIN32_USERPASSWD_DIALOG_H__ #define __RFB_WIN32_USERPASSWD_DIALOG_H__ @@ -38,12 +42,12 @@ namespace rfb { virtual bool showDialog(); virtual void initDialog(); virtual bool onOk(); - virtual bool getUserPasswd(char** user, char** passwd); + virtual void getUserPasswd(char** user, char** passwd); void setCSecurity(const CSecurity* cs); protected: TCharArray username; - TCharArray password; - bool showUsername; + TPlainPasswd password; + bool showUsername, showPassword; TCharArray description; }; diff --git a/vncviewer/buildTime.cxx b/vncviewer/buildTime.cxx index bab2e137..9f37b387 100644 --- a/vncviewer/buildTime.cxx +++ b/vncviewer/buildTime.cxx @@ -1 +1,18 @@ -const char* buildTime = "Built on " __DATE__ " at " __TIME__; \ No newline at end of file +/* Copyright (C) 2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ +const char* buildTime = "Built on " __DATE__ " at " __TIME__; diff --git a/vncviewer/cview.cxx b/vncviewer/cview.cxx deleted file mode 100644 index 03c68e20..00000000 --- a/vncviewer/cview.cxx +++ /dev/null @@ -1,1742 +0,0 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ -#define WIN32_LEAN_AND_MEAN -#if (_WIN32_WINNT < 0x0400) -#define _WIN32_WINNT 0x0400 -#endif -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace rfb; -using namespace rfb::win32; -using namespace rdr; - -// - Statics & consts - -static LogWriter vlog("CView"); - -const int IDM_FULLSCREEN = ID_FULLSCREEN; -const int IDM_SEND_MENU_KEY = ID_SEND_MENU_KEY; -const int IDM_SEND_CAD = ID_SEND_CAD; -const int IDM_SEND_CTLESC = ID_SEND_CTLESC; -const int IDM_ABOUT = ID_ABOUT; -const int IDM_OPTIONS = ID_OPTIONS; -const int IDM_INFO = ID_INFO; -const int IDM_NEWCONN = ID_NEW_CONNECTION; -const int IDM_REQUEST_REFRESH = ID_REQUEST_REFRESH; -const int IDM_CTRL_KEY = ID_CTRL_KEY; -const int IDM_ALT_KEY = ID_ALT_KEY; -const int IDM_FILE_TRANSFER = ID_FILE_TRANSFER; -const int IDM_CONN_SAVE_AS = ID_CONN_SAVE_AS; - -const int TIMER_BUMPSCROLL = 1; -const int TIMER_POINTER_INTERVAL = 2; -const int TIMER_POINTER_3BUTTON = 3; - -const int HOTKEY_ALTTAB = 0; - - -IntParameter debugDelay("DebugDelay","Milliseconds to display inverted " - "pixel data - a debugging feature", 0); - - -// -// -=- CViewClass - -// -// Window class used as the basis for all CView instances -// - -class CViewClass { -public: - CViewClass(); - ~CViewClass(); - ATOM classAtom; - HINSTANCE instance; -}; - -LRESULT CALLBACK CViewProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) { - LRESULT result; - - // *** vlog.debug("CViewMsg %x->(%x, %x, %x)", wnd, msg, wParam, lParam); - - if (msg == WM_CREATE) - SetWindowLong(wnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams); - else if (msg == WM_DESTROY) - SetWindowLong(wnd, GWL_USERDATA, 0); - CView* _this = (CView*) GetWindowLong(wnd, GWL_USERDATA); - if (!_this) { - vlog.info("null _this in %x, message %u", wnd, msg); - return rfb::win32::SafeDefWindowProc(wnd, msg, wParam, lParam); - } - - try { - result = _this->processMessage(msg, wParam, lParam); - } catch (rdr::Exception& e) { - vlog.error("untrapped: %s", e.str()); - } - - return result; -}; - -HCURSOR dotCursor = (HCURSOR)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDC_DOT_CURSOR), IMAGE_CURSOR, 0, 0, LR_SHARED); -HCURSOR arrowCursor = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED); - -CViewClass::CViewClass() : classAtom(0) { - WNDCLASS wndClass; - wndClass.style = 0; - wndClass.lpfnWndProc = CViewProc; - wndClass.cbClsExtra = 0; - wndClass.cbWndExtra = 0; - wndClass.hInstance = instance = GetModuleHandle(0); - wndClass.hIcon = (HICON)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 0, 0, LR_SHARED); - if (!wndClass.hIcon) - printf("unable to load icon:%ld", GetLastError()); - wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); - wndClass.hbrBackground = NULL; - wndClass.lpszMenuName = 0; - wndClass.lpszClassName = _T("rfb::win32::CViewClass"); - classAtom = RegisterClass(&wndClass); - if (!classAtom) { - throw rdr::SystemException("unable to register CView window class", GetLastError()); - } -} - -CViewClass::~CViewClass() { - if (classAtom) { - UnregisterClass((const TCHAR*)classAtom, instance); - } -} - -CViewClass baseClass; - -// -// -=- FrameClass - -// -// Window class used to display the rfb data -// - -class FrameClass { -public: - FrameClass(); - ~FrameClass(); - ATOM classAtom; - HINSTANCE instance; -}; - -LRESULT CALLBACK FrameProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - LRESULT result; - - if (msg == WM_CREATE) - SetWindowLong(hwnd, GWL_USERDATA, (long)((CREATESTRUCT*)lParam)->lpCreateParams); - else if (msg == WM_DESTROY) - SetWindowLong(hwnd, GWL_USERDATA, 0); - CView* _this = (CView*) GetWindowLong(hwnd, GWL_USERDATA); - if (!_this) { - vlog.info("null _this in %x, message %u", hwnd, msg); - return DefWindowProc(hwnd, msg, wParam, lParam); - } - - try { - result = _this->processFrameMessage(msg, wParam, lParam); - } catch (rdr::Exception& e) { - vlog.error("untrapped: %s", e.str()); - } - - return result; -} - -FrameClass::FrameClass() : classAtom(0) { - WNDCLASS wndClass; - wndClass.style = 0; - wndClass.lpfnWndProc = FrameProc; - wndClass.cbClsExtra = 0; - wndClass.cbWndExtra = 0; - wndClass.hInstance = instance = GetModuleHandle(0); - wndClass.hIcon = 0; - wndClass.hCursor = NULL; - wndClass.hbrBackground = 0; - wndClass.lpszMenuName = 0; - wndClass.lpszClassName = _T("FrameClass"); - classAtom = RegisterClass(&wndClass); - if (!classAtom) { - throw rdr::SystemException("unable to register frame window class", - GetLastError()); - } -} - -FrameClass::~FrameClass() { - if (classAtom) { - UnregisterClass((const TCHAR*)classAtom, instance); - } -} - -FrameClass frameClass; - -// -// -=- CView instance implementation -// - -RegKey CView::userConfigKey; - - -CView::CView() - : quit_on_destroy(false), buffer(0), sock(0), readyToRead(false), - client_size(0, 0, 16, 16), window_size(0, 0, 32, 32), - cursorVisible(false), cursorAvailable(false), cursorInBuffer(false), - systemCursorVisible(true), trackingMouseLeave(false), - hwnd(0), frameHwnd(0), requestUpdate(false), has_focus(false), - palette_changed(false), sameMachine(false), encodingChange(false), - formatChange(false), lastUsedEncoding_(encodingRaw), fullScreenActive(false), - bumpScroll(false), manager(0), toolbar(true) { - - // Create the main window - const TCHAR* name = _T("VNC Viewer 4.0b"); - hwnd = CreateWindow((const TCHAR*)baseClass.classAtom, name, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, - 0, 0, 10, 10, 0, 0, baseClass.instance, this); - if (!hwnd) { - throw rdr::SystemException("unable to create WMNotifier window instance", GetLastError()); - } - vlog.debug("created window \"%s\" (%x)", (const char*)CStr(name), hwnd); - - // Create the viewer toolbar - tb.create(getHandle()); - vlog.debug("created toolbar window \"%s\" (%x)", "ViewerToolBar", tb.getHandle()); - - // Create the frame window - frameHwnd = CreateWindowEx(WS_EX_CLIENTEDGE, (const TCHAR*)frameClass.classAtom, - 0, WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, - CW_USEDEFAULT, CW_USEDEFAULT, getHandle(), 0, frameClass.instance, this); - if (!frameHwnd) { - throw rdr::SystemException("unable to create rfb frame window instance", GetLastError()); - } - vlog.debug("created window \"%s\" (%x)", "Frame Window", frameHwnd); - - // Initialise the CPointer pointer handler - ptr.setHWND(getFrameHandle()); - ptr.setIntervalTimerId(TIMER_POINTER_INTERVAL); - ptr.set3ButtonTimerId(TIMER_POINTER_3BUTTON); - - // Initialise the bumpscroll timer - bumpScrollTimer.setHWND(getHandle()); - bumpScrollTimer.setId(TIMER_BUMPSCROLL); - - // Grab AltTab - setAltTabGrab(options.sendSysKeys); - - // Hook the clipboard - clipboard.setNotifier(this); - - // Create the backing buffer - buffer = new win32::DIBSectionBuffer(getFrameHandle()); -} - -CView::~CView() { - vlog.debug("~CView"); - showSystemCursor(); - if (hwnd) { - setVisible(false); - DestroyWindow(hwnd); - hwnd = 0; - } - delete buffer; - vlog.debug("~CView done"); -} - -bool CView::initialise(network::Socket* s) { - // Update the window menu - HMENU wndmenu = GetSystemMenu(hwnd, FALSE); - AppendMenu(wndmenu, MF_SEPARATOR, 0, 0); - AppendMenu(wndmenu, MF_STRING, IDM_FULLSCREEN, _T("&Full screen")); - AppendMenu(wndmenu, (options.showToolbar ? MF_STRING | MF_CHECKED : MF_STRING), - IDM_SHOW_TOOLBAR, _T("Show tool&bar")); - AppendMenu(wndmenu, MF_SEPARATOR, 0, 0); - AppendMenu(wndmenu, MF_STRING, IDM_CTRL_KEY, _T("Ctr&l")); - AppendMenu(wndmenu, MF_STRING, IDM_ALT_KEY, _T("Al&t")); - AppendMenu(wndmenu, MF_STRING, IDM_SEND_CAD, _T("Send Ctrl-Alt-&Del")); - AppendMenu(wndmenu, MF_STRING, IDM_SEND_CTLESC, _T("Send Ctrl-&Esc")); - AppendMenu(wndmenu, MF_STRING, IDM_REQUEST_REFRESH, _T("Refres&h Screen")); - AppendMenu(wndmenu, MF_SEPARATOR, 0, 0); - if (manager) AppendMenu(wndmenu, MF_STRING, IDM_NEWCONN, _T("Ne&w Connection...")); - AppendMenu(wndmenu, MF_STRING, IDM_OPTIONS, _T("&Options...")); - AppendMenu(wndmenu, MF_STRING, IDM_INFO, _T("Connection &Info...")); - AppendMenu(wndmenu, MF_STRING, IDM_ABOUT, _T("&About...")); - - // Set the server's name for MRU purposes - CharArray endpoint(s->getPeerEndpoint()); - setServerName(endpoint.buf); - if (!options.host.buf) - options.setHost(endpoint.buf); - - // Initialise the underlying CConnection - setStreams(&s->inStream(), &s->outStream()); - - // Enable processing of window messages while blocked on I/O - s->inStream().setBlockCallback(this); - - // Initialise the viewer options - applyOptions(options); - - // - Set which auth schemes we support - addSecType(secTypeNone); - addSecType(secTypeVncAuth); - - initialiseProtocol(); - WSAAsyncSelect(s->getFd(), getHandle(), WM_USER, FD_READ | FD_CLOSE); - sock = s; - - m_fileTransfer.initialize(&s->inStream(), &s->outStream()); - - // Show toolbar if needed - toolbar = options.showToolbar; - if (options.showToolbar) tb.show(); - else tb.hide(); - - return true; -} - - -void -CView::applyOptions(CViewOptions& opt) { - // *** CHANGE THIS TO USE CViewOptions::operator= *** - - // - Take the username, password, config filename, and host spec - options.setUserName(opt.userName.buf); - options.setPassword(opt.password.buf); - options.setHost(opt.host.buf); - options.setConfigFileName(opt.configFileName.buf); - options.setMonitor(opt.monitor.buf); - - // - Set optional features in ConnParams - encodingChange |= ((options.useLocalCursor != opt.useLocalCursor) || - (options.useDesktopResize != opt.useDesktopResize)); - cp.supportsLocalCursor = options.useLocalCursor = opt.useLocalCursor; - cp.supportsDesktopResize = options.useDesktopResize = opt.useDesktopResize; - - encodingChange |= ((options.customCompressLevel != opt.customCompressLevel) || - (options.compressLevel != opt.compressLevel) || - (options.noJpeg != opt.noJpeg) || - (options.qualityLevel != opt.qualityLevel)); - cp.customCompressLevel = options.customCompressLevel = opt.customCompressLevel; - cp.compressLevel = options.compressLevel = opt.compressLevel; - cp.noJpeg = options.noJpeg = opt.noJpeg; - cp.qualityLevel = options.qualityLevel = opt.qualityLevel; - - if (cursorAvailable) - hideLocalCursor(); - cursorAvailable = cursorAvailable && options.useLocalCursor; - - // - Switch full-screen mode on/off - options.fullScreen = opt.fullScreen; - setFullscreen(options.fullScreen); - - // - Handle format/encoding options - encodingChange |= (options.preferredEncoding != opt.preferredEncoding); - options.preferredEncoding = opt.preferredEncoding; - - formatChange |= (options.fullColour != opt.fullColour); - options.fullColour = opt.fullColour; - - if (!options.fullColour) - formatChange |= (options.lowColourLevel != opt.lowColourLevel); - options.lowColourLevel = opt.lowColourLevel; - - options.autoSelect = opt.autoSelect; - - // - Sharing - options.shared = opt.shared; - setShared(options.shared); - - // - Inputs - options.sendPtrEvents = opt.sendPtrEvents; - options.sendKeyEvents = opt.sendKeyEvents; - options.sendSysKeys = opt.sendSysKeys; - setAltTabGrab(options.sendSysKeys); - options.clientCutText = opt.clientCutText; - options.serverCutText = opt.serverCutText; - options.emulate3 = opt.emulate3; - ptr.enableEmulate3(opt.emulate3); - options.pointerEventInterval = opt.pointerEventInterval; - ptr.enableInterval(opt.pointerEventInterval); - options.menuKey = opt.menuKey; - - // - Protocol version override - options.protocol3_3 = opt.protocol3_3; - setProtocol3_3(options.protocol3_3); - - // - Bell - options.acceptBell = opt.acceptBell; - - // - Show/hide toolbar - options.showToolbar = opt.showToolbar; -} - -void -CView::setFullscreen(bool fs) { - // Set the menu fullscreen option tick - CheckMenuItem(GetSystemMenu(getHandle(), FALSE), IDM_FULLSCREEN, - (options.fullScreen ? MF_CHECKED : 0) | MF_BYCOMMAND); - - // If the fullscreen mode is active then disable the menu point - // "Show toolbar", otherwise enable the menu point. - EnableMenuItem(GetSystemMenu(getHandle(), FALSE), IDM_SHOW_TOOLBAR, - (options.fullScreen ? MF_GRAYED : MF_ENABLED) | MF_BYCOMMAND); - - // If the window is not visible then we ignore the request. - // setVisible() will call us to correct the full-screen state when - // the window is visible, to keep things consistent. - if (!IsWindowVisible(getHandle())) - return; - - if (fs && !fullScreenActive) { - fullScreenActive = bumpScroll = true; - - // Un-minimize the window if required - if (GetWindowLong(getHandle(), GWL_STYLE) & WS_MINIMIZE) - ShowWindow(getHandle(), SW_RESTORE); - - // Save the non-fullscreen window position - RECT wrect; - GetWindowRect(getHandle(), &wrect); - fullScreenOldRect = Rect(wrect.left, wrect.top, wrect.right, wrect.bottom); - - // Find the size of the display the window is on - MonitorInfo mi(getHandle()); - - // Set the window full-screen - DWORD flags = GetWindowLong(getHandle(), GWL_STYLE); - fullScreenOldFlags = flags; - flags = flags & ~(WS_CAPTION | WS_THICKFRAME | WS_MAXIMIZE | WS_MINIMIZE); - vlog.debug("flags=%x", flags); - - if (toolbar) tb.hide(); - SetWindowLong(getFrameHandle(), GWL_EXSTYLE, 0); - SetWindowLong(getHandle(), GWL_STYLE, flags); - SetWindowPos(getHandle(), HWND_TOP, mi.rcMonitor.left, mi.rcMonitor.top, - mi.rcMonitor.right-mi.rcMonitor.left, - mi.rcMonitor.bottom-mi.rcMonitor.top, - SWP_FRAMECHANGED); - } else if (!fs && fullScreenActive) { - fullScreenActive = bumpScroll = false; - - // Set the window non-fullscreen - if (toolbar) tb.show(); - SetWindowLong(getFrameHandle(), GWL_EXSTYLE, WS_EX_CLIENTEDGE); - SetWindowLong(getHandle(), GWL_STYLE, fullScreenOldFlags); - SetWindowPos(getHandle(), HWND_NOTOPMOST, - fullScreenOldRect.tl.x, fullScreenOldRect.tl.y, - fullScreenOldRect.width(), fullScreenOldRect.height(), - SWP_FRAMECHANGED); - } - - // Adjust the viewport offset to cope with change in size between FS - // and previous window state. - setViewportOffset(scrolloffset); -} - - -bool CView::setViewportOffset(const Point& tl) { -/* *** - Point np = Point(max(0, min(maxscrolloffset.x, tl.x)), - max(0, min(maxscrolloffset.y, tl.y))); - */ - Point np = Point(max(0, min(tl.x, buffer->width()-client_size.width())), - max(0, min(tl.y, buffer->height()-client_size.height()))); - Point delta = np.translate(scrolloffset.negate()); - if (!np.equals(scrolloffset)) { - scrolloffset = np; - ScrollWindowEx(getFrameHandle(), -delta.x, -delta.y, 0, 0, 0, 0, SW_INVALIDATE); - UpdateWindow(getFrameHandle()); - return true; - } - return false; -} - - -bool CView::processBumpScroll(const Point& pos) -{ - if (!bumpScroll) return false; - int bumpScrollPixels = 20; - bumpScrollDelta = Point(); - - if (pos.x == client_size.width()-1) - bumpScrollDelta.x = bumpScrollPixels; - else if (pos.x == 0) - bumpScrollDelta.x = -bumpScrollPixels; - if (pos.y == client_size.height()-1) - bumpScrollDelta.y = bumpScrollPixels; - else if (pos.y == 0) - bumpScrollDelta.y = -bumpScrollPixels; - - if (bumpScrollDelta.x || bumpScrollDelta.y) { - if (bumpScrollTimer.isActive()) return true; - if (setViewportOffset(scrolloffset.translate(bumpScrollDelta))) { - bumpScrollTimer.start(25); - return true; - } - } - - bumpScrollTimer.stop(); - return false; -} - - -LRESULT -CView::processMessage(UINT msg, WPARAM wParam, LPARAM lParam) { - switch (msg) { - - // -=- Process standard window messages - - case WM_NOTIFY: - if (wParam = (ID_TOOLBAR)) tb.processWM_NOTIFY(wParam, lParam); - break; - - case WM_DISPLAYCHANGE: - // Display has changed - use new pixel format - calculateFullColourPF(); - break; - - // -=- Window position - - // Prevent the window from being resized to be too large if in normal mode. - // If maximized or fullscreen the allow oversized windows. - - case WM_WINDOWPOSCHANGING: - { - WINDOWPOS* wpos = (WINDOWPOS*)lParam; - if (wpos->flags & SWP_NOSIZE) - break; - - // Work out how big the window should ideally be - DWORD frame_current_style = GetWindowLong(getFrameHandle(), GWL_STYLE); - DWORD frame_style = frame_current_style & ~(WS_VSCROLL | WS_HSCROLL); - DWORD frame_ex_style = GetWindowLong(getFrameHandle(), GWL_EXSTYLE); - - RECT r; - SetRect(&r, 0, 0, buffer->width(), buffer->height()); - AdjustWindowRectEx(&r, frame_style, FALSE, frame_ex_style); - Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom); - if (frame_current_style & WS_VSCROLL) - reqd_size.br.x += GetSystemMetrics(SM_CXVSCROLL); - if (frame_current_style & WS_HSCROLL) - reqd_size.br.y += GetSystemMetrics(SM_CXHSCROLL); - r.left = reqd_size.tl.x; r.right = reqd_size.br.x; - r.top = reqd_size.tl.y; r.bottom = reqd_size.br.y; - if (tb.isVisible()) r.bottom += tb.getHeight(); - AdjustWindowRect(&r, GetWindowLong(getHandle(), GWL_STYLE), FALSE); - reqd_size = Rect(r.left, r.top, r.right, r.bottom); - RECT current; - GetWindowRect(getHandle(), ¤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; - - // Resize child windows and update window size info we have cached. - - case WM_SIZE: - { - Point old_offset = bufferToClient(Point(0, 0)); - - // Resize the child windows - RECT r; - GetClientRect(getHandle(), &r); - if (tb.isVisible()) { - MoveWindow(getFrameHandle(), 0, tb.getHeight(), r.right, - r.bottom - tb.getHeight(), TRUE); - } else { - MoveWindow(getFrameHandle(), 0, 0, r.right, r.bottom, TRUE); - } - tb.autoSize(); - - // Update the cached sizing information - GetWindowRect(getFrameHandle(), &r); - window_size = Rect(r.left, r.top, r.right, r.bottom); - GetClientRect(getFrameHandle(), &r); - client_size = Rect(r.left, r.top, r.right, r.bottom); - - // Determine whether scrollbars are required - calculateScrollBars(); - - // Redraw if required - if (!old_offset.equals(bufferToClient(Point(0, 0)))) - InvalidateRect(getFrameHandle(), 0, TRUE); - } - break; - - // -=- Bump-scrolling - - case WM_TIMER: - switch (wParam) { - case TIMER_BUMPSCROLL: - if (!setViewportOffset(scrolloffset.translate(bumpScrollDelta))) - bumpScrollTimer.stop(); - break; - case TIMER_POINTER_INTERVAL: - case TIMER_POINTER_3BUTTON: - try { - ptr.handleTimer(writer(), wParam); - } catch (rdr::Exception& e) { - close(e.str()); - } - break; - } - break; - - // -=- Track whether or not the window has focus - - case WM_SETFOCUS: - has_focus = true; - // Re-register AltTab hotkey - setAltTabGrab(options.sendSysKeys); - break; - case WM_KILLFOCUS: - has_focus = false; - cursorOutsideBuffer(); - // Unregister AltTab hotkey - setAltTabGrab(false); - // Restore the remote keys to consistent states - try { - kbd.releaseAllKeys(writer()); - } catch (rdr::Exception& e) { - close(e.str()); - } - break; - - // -=- Handle the extra window menu items - - // Process the items added to the system menu - case WM_SYSCOMMAND: - - // - First check whether it's one of our messages - switch (wParam) { - case IDM_FULLSCREEN: - options.fullScreen = !options.fullScreen; - setFullscreen(options.fullScreen); - return 0; - case IDM_SHOW_TOOLBAR: - toolbar = !toolbar; - CheckMenuItem(GetSystemMenu(getHandle(), FALSE), IDM_SHOW_TOOLBAR, - (toolbar ? MF_CHECKED : MF_UNCHECKED) | MF_BYCOMMAND); - if (toolbar) tb.show(); - else tb.hide(); - return 0; - case IDM_CTRL_KEY: - writeKeyEvent(VK_CONTROL, 0, !kbd.keyPressed(VK_CONTROL)); - return 0; - case IDM_ALT_KEY: - writeKeyEvent(VK_MENU, 0, !kbd.keyPressed(VK_MENU)); - return 0; - case IDM_SEND_MENU_KEY: - writeKeyEvent(options.menuKey, 0, true); - writeKeyEvent(options.menuKey, 0, false); - return 0; - case IDM_SEND_CAD: - writeKeyEvent(VK_CONTROL, 0, true); - writeKeyEvent(VK_MENU, 0, true); - writeKeyEvent(VK_DELETE, 0, true); - writeKeyEvent(VK_DELETE, 0, false); - writeKeyEvent(VK_MENU, 0, false); - writeKeyEvent(VK_CONTROL, 0, false); - return 0; - case IDM_SEND_CTLESC: - writeKeyEvent(VK_CONTROL, 0, true); - writeKeyEvent(VK_ESCAPE, 0, true); - writeKeyEvent(VK_CONTROL, 0, false); - writeKeyEvent(VK_ESCAPE, 0, false); - return 0; - case IDM_REQUEST_REFRESH: - try { - writer()->writeFramebufferUpdateRequest(Rect(0,0,cp.width,cp.height), false); - requestUpdate = false; - } catch (rdr::Exception& e) { - close(e.str()); - } - return 0; - case IDM_NEWCONN: - manager->addClient(0); - return 0; - case IDM_OPTIONS: - // Update the monitor device name in the CViewOptions instance - { - MonitorInfo mi(getHandle()); - options.setMonitor(mi.szDevice); - optionsDialog.showDialog(this); - return 0; - } - case IDM_INFO: - infoDialog.showDialog(this); - return 0; - case IDM_ABOUT: - AboutDialog::instance.showDialog(); - return 0; - case IDM_FILE_TRANSFER: - m_fileTransfer.show(getHandle()); - return 0; - case IDM_CONN_SAVE_AS: - return 0; - case ID_CLOSE: - PostQuitMessage(0); - return 0; - }; - - // - Not one of our messages, so process it as a system message - switch (wParam & 0xfff0) { - - // When restored, ensure that full-screen mode is re-enabled if required. - case SC_RESTORE: - rfb::win32::SafeDefWindowProc(getHandle(), msg, wParam, lParam); - setFullscreen(options.fullScreen); - return 0; - - // If we are maximized or minimized then that cancels full-screen mode. - case SC_MINIMIZE: - case SC_MAXIMIZE: - setFullscreen(false); - break; - - // If the system menu is shown then make sure it's up to date - case SC_KEYMENU: - case SC_MOUSEMENU: - updateF8Menu(false); - break; - - }; - break; - - // Treat all menu commands as system menu commands - case WM_COMMAND: - SendMessage(getHandle(), WM_SYSCOMMAND, wParam, lParam); - return 0; - - case WM_MENUCHAR: - vlog.debug("menuchar"); - break; - - // -=- Handle keyboard input - - case WM_HOTKEY: - if (wParam == HOTKEY_ALTTAB) { - writeKeyEvent(VK_TAB, 0, true); - writeKeyEvent(VK_TAB, 0, false); - return 0; - } - break; - - case WM_SYSKEYDOWN: - // Translate Alt-Space and Alt-F4 to WM_SYSCHAR and WM_CLOSE, - // since we are not using TranslateMessage(). - if (!options.sendSysKeys) { - switch (wParam) { - case VK_SPACE: - writeKeyEvent(VK_MENU, 0, false); - SendMessage(getHandle(), WM_SYSCHAR, wParam, lParam); - return 0; - case VK_F4: - SendMessage(getHandle(), WM_CLOSE, wParam, lParam); - return 0; - } - } - - case WM_SYSKEYUP: - // When we have registered for AltTab as a hotkey, SYSKEYUPs for - // Tabs are sent (but no SYSKEYDOWNs). AltTab is handled by - // WM_HOTKEY, though. - if (wParam == VK_TAB) return 0; - - case WM_KEYUP: - case WM_KEYDOWN: - // Hook the MenuKey to pop-up the window menu - if (options.menuKey && (wParam == options.menuKey)) { - - bool ctrlDown = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0; - bool altDown = (GetAsyncKeyState(VK_MENU) & 0x8000) != 0; - bool shiftDown = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0; - if (!(ctrlDown || altDown || shiftDown)) { - - // If MenuKey is being released then pop-up the menu - if ((msg == WM_KEYDOWN)) { - // Make sure it's up to date - updateF8Menu(true); - - // Show it under the pointer - POINT pt; - GetCursorPos(&pt); - cursorInBuffer = false; - TrackPopupMenu(GetSystemMenu(getHandle(), FALSE), - TPM_CENTERALIGN | TPM_VCENTERALIGN, pt.x, pt.y, 0, getHandle(), 0); - } - - // Ignore the MenuKey keypress for both press & release events - return 0; - } - } - - writeKeyEvent(wParam, lParam, (msg == WM_KEYDOWN) || (msg == WM_SYSKEYDOWN)); - return 0; - - // -=- Handle the window closing - - case WM_CLOSE: - vlog.debug("WM_CLOSE %x", getHandle()); - if (quit_on_destroy) { - vlog.debug("posting WM_QUIT"); - PostQuitMessage(0); - } else { - vlog.debug("not posting WM_QUIT"); - } - break; - - // -=- Process incoming socket data - - case WM_USER: - readyToRead = true; - break; - - } - - return rfb::win32::SafeDefWindowProc(getHandle(), msg, wParam, lParam); -} - -LRESULT CView::processFrameMessage(UINT msg, WPARAM wParam, LPARAM lParam) { - switch (msg) { - - // -=- Palette management - - case WM_PALETTECHANGED: - vlog.debug("WM_PALETTECHANGED"); - if ((HWND)wParam == getFrameHandle()) { - vlog.debug("ignoring"); - break; - } - case WM_QUERYNEWPALETTE: - vlog.debug("re-selecting palette"); - { - WindowDC wdc(getFrameHandle()); - PaletteSelector pSel(wdc, windowPalette.getHandle()); - if (pSel.isRedrawRequired()) { - InvalidateRect(getFrameHandle(), 0, FALSE); - UpdateWindow(getFrameHandle()); - } - } - return TRUE; - - // Paint the remote frame buffer - - case WM_PAINT: - { - PAINTSTRUCT ps; - HDC paintDC = BeginPaint(getFrameHandle(), &ps); - if (!paintDC) - throw SystemException("unable to BeginPaint", GetLastError()); - Rect pr = Rect(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom); - - if (!pr.is_empty()) { - - // Draw using the correct palette - PaletteSelector pSel(paintDC, windowPalette.getHandle()); - - if (buffer->bitmap) { - // Update the bitmap's palette - if (palette_changed) { - palette_changed = false; - buffer->refreshPalette(); - } - - // Get device context - BitmapDC bitmapDC(paintDC, buffer->bitmap); - - // Blit the border if required - Rect bufpos = bufferToClient(buffer->getRect()); - if (!pr.enclosed_by(bufpos)) { - vlog.debug("draw border"); - HBRUSH black = (HBRUSH) GetStockObject(BLACK_BRUSH); - RECT r; - SetRect(&r, 0, 0, bufpos.tl.x, client_size.height()); FillRect(paintDC, &r, black); - SetRect(&r, bufpos.tl.x, 0, bufpos.br.x, bufpos.tl.y); FillRect(paintDC, &r, black); - SetRect(&r, bufpos.br.x, 0, client_size.width(), client_size.height()); FillRect(paintDC, &r, black); - SetRect(&r, bufpos.tl.x, bufpos.br.y, bufpos.br.x, client_size.height()); FillRect(paintDC, &r, black); - } - - // Do the blit - Point buf_pos = clientToBuffer(pr.tl); - if (!BitBlt(paintDC, pr.tl.x, pr.tl.y, pr.width(), pr.height(), - bitmapDC, buf_pos.x, buf_pos.y, SRCCOPY)) - throw SystemException("unable to BitBlt to window", GetLastError()); - - } else { - // Blit a load of black - if (!BitBlt(paintDC, pr.tl.x, pr.tl.y, pr.width(), pr.height(), - 0, 0, 0, BLACKNESS)) - throw SystemException("unable to BitBlt to blank window", GetLastError()); - } - } - - EndPaint(getFrameHandle(), &ps); - - // - Request the next update from the server, if required - requestNewUpdate(); - } - return 0; - - // Process the frame scroll messages - - case WM_VSCROLL: - case WM_HSCROLL: - { - Point delta; - int newpos = (msg == WM_VSCROLL) ? scrolloffset.y : scrolloffset.x; - - switch (LOWORD(wParam)) { - case SB_PAGEUP: newpos -= 50; break; - case SB_PAGEDOWN: newpos += 50; break; - case SB_LINEUP: newpos -= 5; break; - case SB_LINEDOWN: newpos += 5; break; - case SB_THUMBTRACK: - case SB_THUMBPOSITION: newpos = HIWORD(wParam); break; - default: vlog.info("received unknown scroll message"); - }; - - if (msg == WM_HSCROLL) - setViewportOffset(Point(newpos, scrolloffset.y)); - else - setViewportOffset(Point(scrolloffset.x, newpos)); - - SCROLLINFO si; - si.cbSize = sizeof(si); - si.fMask = SIF_POS; - si.nPos = newpos; - SetScrollInfo(getFrameHandle(), (msg == WM_VSCROLL) ? SB_VERT : SB_HORZ, &si, TRUE); - } - break; - - // -=- Cursor shape/visibility handling - - case WM_SETCURSOR: - if (LOWORD(lParam) != HTCLIENT) - break; - SetCursor(cursorInBuffer ? dotCursor : arrowCursor); - return TRUE; - - case WM_MOUSELEAVE: - trackingMouseLeave = false; - cursorOutsideBuffer(); - return 0; - - // -=- Mouse input handling - - case WM_MOUSEMOVE: - case WM_LBUTTONUP: - case WM_MBUTTONUP: - case WM_RBUTTONUP: - case WM_LBUTTONDOWN: - case WM_MBUTTONDOWN: - case WM_RBUTTONDOWN: - case WM_MOUSEWHEEL: - if (has_focus) - { - if (!trackingMouseLeave) { - TRACKMOUSEEVENT tme; - tme.cbSize = sizeof(TRACKMOUSEEVENT); - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = getFrameHandle(); - _TrackMouseEvent(&tme); - trackingMouseLeave = true; - } - int mask = 0; - if (LOWORD(wParam) & MK_LBUTTON) mask |= 1; - if (LOWORD(wParam) & MK_MBUTTON) mask |= 2; - if (LOWORD(wParam) & MK_RBUTTON) mask |= 4; - - if (msg == WM_MOUSEWHEEL) { - int delta = (short)HIWORD(wParam); - int repeats = (abs(delta)+119) / 120; - int wheelMask = (delta > 0) ? 8 : 16; - vlog.debug("repeats %d, mask %d\n",repeats,wheelMask); - for (int i=0; igetRect().contains(p); - if (!cursorInBuffer) { - cursorOutsideBuffer(); - break; - } - - // If we're locally rendering the cursor then redraw it - if (cursorAvailable) { - // - Render the cursor! - if (!p.equals(cursorPos)) { - hideLocalCursor(); - cursorPos = p; - showLocalCursor(); - if (cursorVisible) - hideSystemCursor(); - } - } - - // If we are doing bump-scrolling then try that first... - if (processBumpScroll(clientPos)) - break; - - // Send a pointer event to the server - writePointerEvent(p.x, p.y, mask); - oldpos = p; - } - } else { - cursorOutsideBuffer(); - } - break; - } - - return rfb::win32::SafeDefWindowProc(getFrameHandle(), msg, wParam, lParam); -} - -void CView::blockCallback() { - // - An InStream has blocked on I/O while processing an RFB message - // We re-enable socket event notifications, so we'll know when more - // data is available, then we sit and dispatch window events until - // the notification arrives. - readyToRead = false; - WSAAsyncSelect(sock->getFd(), getHandle(), WM_USER, FD_READ | FD_CLOSE); - MSG msg; - while (true) { - if (readyToRead) { - // - Network event notification. Return control to I/O routine. - WSAAsyncSelect(sock->getFd(), getHandle(), WM_USER, 0); - return; - } - - DWORD result = GetMessage(&msg, NULL, 0, 0); - if (result == 0) { - vlog.debug("WM_QUIT"); - throw QuitMessage(msg.wParam); - } else if (result < 0) { - throw rdr::SystemException("GetMessage error", GetLastError()); - } - - // IMPORTANT: We mustn't call TranslateMessage() here, because instead we - // call ToAscii() in CKeyboard::keyEvent(). ToAscii() stores dead key - // state from one call to the next, which would be messed up by calls to - // TranslateMessage() (actually it looks like TranslateMessage() calls - // ToAscii() internally). - DispatchMessage(&msg); - } -} - - -void -CView::hideLocalCursor() { - // - Blit the cursor backing store over the cursor - // *** ALWAYS call this BEFORE changing buffer PF!!! - if (cursorVisible) { - cursorVisible = false; - buffer->imageRect(cursorBackingRect, cursorBacking.data); - invalidateBufferRect(cursorBackingRect); - } -} - -void -CView::showLocalCursor() { - if (cursorAvailable && !cursorVisible && cursorInBuffer) { - if (!cp.pf().equal(cursor.getPF()) || - cursor.getRect().is_empty()) { - vlog.info("attempting to render invalid local cursor"); - cursorAvailable = false; - showSystemCursor(); - return; - } - cursorVisible = true; - - cursorBackingRect = cursor.getRect().translate(cursorPos).translate(cursor.hotspot.negate()); - cursorBackingRect = cursorBackingRect.intersect(buffer->getRect()); - buffer->getImage(cursorBacking.data, cursorBackingRect); - - renderLocalCursor(); - - invalidateBufferRect(cursorBackingRect); - } -} - -void CView::cursorOutsideBuffer() -{ - cursorInBuffer = false; - hideLocalCursor(); - showSystemCursor(); -} - -void -CView::renderLocalCursor() -{ - Rect r = cursor.getRect(); - r = r.translate(cursorPos).translate(cursor.hotspot.negate()); - buffer->maskRect(r, cursor.data, cursor.mask.buf); -} - -void -CView::hideSystemCursor() { - if (systemCursorVisible) { - vlog.debug("hide system cursor"); - systemCursorVisible = false; - ShowCursor(FALSE); - } -} - -void -CView::showSystemCursor() { - if (!systemCursorVisible) { - vlog.debug("show system cursor"); - systemCursorVisible = true; - ShowCursor(TRUE); - } -} - -void -CView::setAltTabGrab(bool grab) { - BOOL hotKeyResult; - static bool grabstate = false; - - // Do not call RegisterHotKey/UnregisterHotKey if not necessary - if (grabstate == grab) - return; - - grabstate = grab; - - // Only works for NT/2k/XP - if (grab) { - hotKeyResult = RegisterHotKey(hwnd, HOTKEY_ALTTAB, MOD_ALT, VK_TAB); - if (!hotKeyResult) - vlog.debug("RegisterHotkey failed with error %d", GetLastError()); - } else { - hotKeyResult = UnregisterHotKey(hwnd, HOTKEY_ALTTAB); - } -} - -bool -CView::invalidateBufferRect(const Rect& crect) { - Rect rect = bufferToClient(crect); - if (rect.intersect(client_size).is_empty()) return false; - RECT invalid = {rect.tl.x, rect.tl.y, rect.br.x, rect.br.y}; - InvalidateRect(getFrameHandle(), &invalid, FALSE); - return true; -} - - -void -CView::notifyClipboardChanged(const char* text, int len) { - if (!options.clientCutText) return; - if (state() != RFBSTATE_NORMAL) return; - try { - writer()->writeClientCutText(text, len); - } catch (rdr::Exception& e) { - close(e.str()); - } -} - - -CSecurity* CView::getCSecurity(int secType) -{ - switch (secType) { - case secTypeNone: - return new CSecurityNone(); - case secTypeVncAuth: - return new CSecurityVncAuth(this); - default: - throw Exception("Unsupported secType?"); - } -} - - -void -CView::setColourMapEntries(int first, int count, U16* rgbs) { - vlog.debug("setColourMapEntries: first=%d, count=%d", first, count); - int i; - for (i=0;isetColour(i+first, rgbs[i*3], rgbs[i*3+1], rgbs[i*3+2]); - } - // *** change to 0, 256? - refreshWindowPalette(first, count); - palette_changed = true; - InvalidateRect(getHandle(), 0, FALSE); -} - -void -CView::bell() { - if (options.acceptBell) - MessageBeep(-1); -} - - -void -CView::setDesktopSize(int w, int h) { - vlog.debug("setDesktopSize %dx%d", w, h); - - // If the locally-rendered cursor is visible then remove it - hideLocalCursor(); - - // Resize the backing buffer - buffer->setSize(w, h); - - // If the window is not maximised or full-screen then resize it - if (!(GetWindowLong(getHandle(), GWL_STYLE) & WS_MAXIMIZE) && !fullScreenActive) { - // Resize the window to the required size - RECT r = {0, 0, w, h}; - AdjustWindowRectEx(&r, GetWindowLong(getFrameHandle(), GWL_STYLE), FALSE, - GetWindowLong(getFrameHandle(), GWL_EXSTYLE)); - if (tb.isVisible()) r.bottom += tb.getHeight(); - AdjustWindowRect(&r, GetWindowLong(getHandle(), GWL_STYLE), FALSE); - SetWindowPos(getHandle(), 0, 0, 0, r.right-r.left, r.bottom-r.top, - SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER); - - // Move the window to the desired monitor - if (options.monitor.buf) - moveToMonitor(getHandle(), options.monitor.buf); - - // Clip to the system work area - centerWindow(getHandle(), 0, true); - } else { - // Ensure the screen contents are consistent - InvalidateRect(getFrameHandle(), 0, FALSE); - } - - // Tell the underlying CConnection - CConnection::setDesktopSize(w, h); - - // Enable/disable scrollbars as appropriate - calculateScrollBars(); -} - -void -CView::setCursor(const Point& hotspot, const Point& size, void* data, void* mask) { - if (!options.useLocalCursor) return; - hideLocalCursor(); - - cursor.hotspot = hotspot; - - cursor.setSize(size.x, size.y); - cursor.setPF(cp.pf()); - cursor.imageRect(cursor.getRect(), data); - memcpy(cursor.mask.buf, mask, cursor.maskLen()); - cursor.crop(); - - cursorBacking.setSize(size.x, size.y); - cursorBacking.setPF(cp.pf()); - - cursorAvailable = true; - - showLocalCursor(); -} - -PixelFormat -CView::getNativePF() const { - vlog.debug("getNativePF()"); - return WindowDC(getHandle()).getPF(); -} - -void -CView::setVisible(bool visible) { - ShowWindow(getHandle(), visible ? SW_SHOW : SW_HIDE); - if (visible) { - // When the window becomes visible, make it active - SetForegroundWindow(getHandle()); - SetActiveWindow(getHandle()); - // If the window should be full-screen, then do so - setFullscreen(options.fullScreen); - } else { - // Disable full-screen mode - setFullscreen(false); - } -} - -void -CView::close(const char* reason) { - setVisible(false); - if (reason) { - vlog.info("closing - %s", reason); - MsgBox(NULL, TStr(reason), MB_ICONINFORMATION | MB_OK); - } - SendMessage(getHandle(), WM_CLOSE, 0, 0); -} - - -void -CView::framebufferUpdateEnd() { - if (debugDelay != 0) { - vlog.debug("debug delay %d",(int)debugDelay); - UpdateWindow(getHandle()); - Sleep(debugDelay); - std::list::iterator i; - for (i = debugRects.begin(); i != debugRects.end(); i++) { - invertRect(*i); - } - debugRects.clear(); - } - if (options.autoSelect) - autoSelectFormatAndEncoding(); - - // Always request the next update - requestUpdate = true; - - // Check that at least part of the window has changed - if (!GetUpdateRect(getHandle(), 0, FALSE)) { - if (!(GetWindowLong(getHandle(), GWL_STYLE) & WS_MINIMIZE)) - requestNewUpdate(); - } - - showLocalCursor(); -} - - -// Note: The method below is duplicated in vncviewer_unix/CConn.cxx! - -// autoSelectFormatAndEncoding() chooses the format and encoding appropriate -// to the connection speed: -// -// Above 16Mbps (timing for at least a second), switch to hextile -// Otherwise, switch to ZRLE -// -// Above 256Kbps, use full colour mode -// -void -CView::autoSelectFormatAndEncoding() { - int kbitsPerSecond = sock->inStream().kbitsPerSecond(); - unsigned int newEncoding = options.preferredEncoding; - bool newFullColour = options.fullColour; - unsigned int timeWaited = sock->inStream().timeWaited(); - - // Select best encoding - if (kbitsPerSecond > 16000 && timeWaited >= 10000) { - newEncoding = encodingHextile; - } else { - newEncoding = encodingZRLE; - } - - if (newEncoding != options.preferredEncoding) { - vlog.info("Throughput %d kbit/s - changing to %s encoding", - kbitsPerSecond, encodingName(newEncoding)); - options.preferredEncoding = newEncoding; - encodingChange = true; - } - - if (kbitsPerSecond == 0) { - return; - } - - if (cp.beforeVersion(3, 8)) { - // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with - // cursors "asynchronously". If this happens in the middle of a - // pixel format change, the server will encode the cursor with - // the old format, but the client will try to decode it - // according to the new format. This will lead to a - // crash. Therefore, we do not allow automatic format change for - // old servers. - return; - } - - // Select best color level - newFullColour = (kbitsPerSecond > 256); - if (newFullColour != options.fullColour) { - vlog.info("Throughput %d kbit/s - full color is now %s", - kbitsPerSecond, - newFullColour ? "enabled" : "disabled"); - options.fullColour = newFullColour; - formatChange = true; - } -} - -void -CView::requestNewUpdate() { - if (!requestUpdate) return; - - if (formatChange) { - // Hide the rendered cursor, if any, to prevent - // the backing buffer being used in the wrong format - hideLocalCursor(); - - // Select the required pixel format - if (options.fullColour) { - buffer->setPF(fullColourPF); - } else { - switch (options.lowColourLevel) { - case 0: - buffer->setPF(PixelFormat(8,3,0,1,1,1,1,2,1,0)); - break; - case 1: - buffer->setPF(PixelFormat(8,6,0,1,3,3,3,4,2,0)); - break; - case 2: - buffer->setPF(PixelFormat(8,8,0,0,0,0,0,0,0,0)); - break; - } - } - - // Print the current pixel format - char str[256]; - buffer->getPF().print(str, 256); - vlog.info("Using pixel format %s",str); - - // Save the connection pixel format and tell server to use it - cp.setPF(buffer->getPF()); - writer()->writeSetPixelFormat(cp.pf()); - - // Correct the local window's palette - if (!getNativePF().trueColour) - refreshWindowPalette(0, 1 << cp.pf().depth); - } - - if (encodingChange) { - vlog.info("Using %s encoding",encodingName(options.preferredEncoding)); - writer()->writeSetEncodings(options.preferredEncoding, true); - } - - writer()->writeFramebufferUpdateRequest(Rect(0, 0, cp.width, cp.height), - !formatChange); - - encodingChange = formatChange = requestUpdate = false; -} - - -void -CView::writeKeyEvent(rdr::U8 vkey, rdr::U32 flags, bool down) { - if (!options.sendKeyEvents) return; - try { - kbd.keyEvent(writer(), vkey, flags, down); - } catch (rdr::Exception& e) { - close(e.str()); - } -} - -void -CView::writePointerEvent(int x, int y, int buttonMask) { - if (!options.sendPtrEvents) return; - try { - ptr.pointerEvent(writer(), x, y, buttonMask); - } catch (rdr::Exception& e) { - close(e.str()); - } -} - - -void -CView::refreshWindowPalette(int start, int count) { - vlog.debug("refreshWindowPalette(%d, %d)", start, count); - - Colour colours[256]; - if (count > 256) { - vlog.debug("%d palette entries", count); - throw rdr::Exception("too many palette entries"); - } - - // Copy the palette from the DIBSectionBuffer - ColourMap* cm = buffer->getColourMap(); - if (!cm) return; - for (int i=0; ilookup(i, &r, &g, &b); - colours[i].r = r; - colours[i].g = g; - colours[i].b = b; - } - - // Set the window palette - windowPalette.setEntries(start, count, colours); - - // Cause the window to be redrawn - InvalidateRect(getHandle(), 0, 0); -} - - -void CView::calculateScrollBars() { - // Calculate the required size of window - DWORD current_style = GetWindowLong(getFrameHandle(), GWL_STYLE); - DWORD style = current_style & ~(WS_VSCROLL | WS_HSCROLL); - DWORD ex_style = GetWindowLong(getFrameHandle(), GWL_EXSTYLE); - DWORD old_style; - RECT r; - SetRect(&r, 0, 0, buffer->width(), buffer->height()); - AdjustWindowRectEx(&r, style, FALSE, ex_style); - Rect reqd_size = Rect(r.left, r.top, r.right, r.bottom); - - if (!bumpScroll) { - // We only enable scrollbars if bump-scrolling is not active. - // Effectively, this means if full-screen is not active, - // but I think it's better to make these things explicit. - // Work out whether scroll bars are required - do { - old_style = style; - - if (!(style & WS_HSCROLL) && (reqd_size.width() > window_size.width())) { - style |= WS_HSCROLL; - reqd_size.br.y += GetSystemMetrics(SM_CXHSCROLL); - } - if (!(style & WS_VSCROLL) && (reqd_size.height() > window_size.height())) { - style |= WS_VSCROLL; - reqd_size.br.x += GetSystemMetrics(SM_CXVSCROLL); - } - } while (style != old_style); - } - - // Tell Windows to update the window style & cached settings - if (style != current_style) { - SetWindowLong(getFrameHandle(), GWL_STYLE, style); - SetWindowPos(getFrameHandle(), NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); - } - - // Update the scroll settings - SCROLLINFO si; - if (style & WS_VSCROLL) { - si.cbSize = sizeof(si); - si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; - si.nMin = 0; - si.nMax = buffer->height(); - si.nPage = buffer->height() - (reqd_size.height() - window_size.height()); - maxscrolloffset.y = max(0, si.nMax-si.nPage); - scrolloffset.y = min(maxscrolloffset.y, scrolloffset.y); - si.nPos = scrolloffset.y; - SetScrollInfo(getFrameHandle(), SB_VERT, &si, TRUE); - } - if (style & WS_HSCROLL) { - si.cbSize = sizeof(si); - si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; - si.nMin = 0; - si.nMax = buffer->width(); - si.nPage = buffer->width() - (reqd_size.width() - window_size.width()); - maxscrolloffset.x = max(0, si.nMax-si.nPage); - scrolloffset.x = min(maxscrolloffset.x, scrolloffset.x); - si.nPos = scrolloffset.x; - SetScrollInfo(getFrameHandle(), SB_HORZ, &si, TRUE); - } - - // Update the cached client size - GetClientRect(getFrameHandle(), &r); - client_size = Rect(r.left, r.top, r.right, r.bottom); -} - - -void -CView::calculateFullColourPF() { - // If the server is palette based then use palette locally - // Also, don't bother doing bgr222 - if (!serverDefaultPF.trueColour || (serverDefaultPF.depth < 6)) { - fullColourPF = serverDefaultPF; - options.fullColour = true; - } else { - // If server is trueColour, use lowest depth PF - PixelFormat native = getNativePF(); - if ((serverDefaultPF.bpp < native.bpp) || - ((serverDefaultPF.bpp == native.bpp) && - (serverDefaultPF.depth < native.depth))) - fullColourPF = serverDefaultPF; - else - fullColourPF = getNativePF(); - } - formatChange = true; -} - - -void -CView::updateF8Menu(bool hideSystemCommands) { - HMENU menu = GetSystemMenu(getHandle(), FALSE); - - if (hideSystemCommands) { - // Gray out menu items that might cause a World Of Pain - HMENU menu = GetSystemMenu(getHandle(), FALSE); - EnableMenuItem(menu, SC_SIZE, MF_BYCOMMAND | MF_GRAYED); - EnableMenuItem(menu, SC_MOVE, MF_BYCOMMAND | MF_GRAYED); - EnableMenuItem(menu, SC_RESTORE, MF_BYCOMMAND | MF_ENABLED); - EnableMenuItem(menu, SC_MINIMIZE, MF_BYCOMMAND | MF_ENABLED); - EnableMenuItem(menu, SC_MAXIMIZE, MF_BYCOMMAND | MF_ENABLED); - } - - // Update the modifier key menu items - UINT ctrlCheckFlags = kbd.keyPressed(VK_CONTROL) ? MF_CHECKED : MF_UNCHECKED; - UINT altCheckFlags = kbd.keyPressed(VK_MENU) ? MF_CHECKED : MF_UNCHECKED; - CheckMenuItem(menu, IDM_CTRL_KEY, MF_BYCOMMAND | ctrlCheckFlags); - CheckMenuItem(menu, IDM_ALT_KEY, MF_BYCOMMAND | altCheckFlags); - - // Ensure that the Send menu item has the correct text - if (options.menuKey) { - TCharArray menuKeyStr(options.menuKeyName()); - TCharArray tmp(_tcslen(menuKeyStr.buf) + 6); - _stprintf(tmp.buf, _T("Send %s"), menuKeyStr.buf); - if (!ModifyMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf)) - InsertMenu(menu, IDM_SEND_CAD, MF_BYCOMMAND | MF_STRING, IDM_SEND_MENU_KEY, tmp.buf); - } else { - RemoveMenu(menu, IDM_SEND_MENU_KEY, MF_BYCOMMAND); - } -} - - -void -CView::setName(const char* name) { - vlog.debug("setName %s", name); - ::SetWindowText(getHandle(), TStr(name)); - CConnection::setName(name); -} - - -void CView::serverInit() { - CConnection::serverInit(); - - // If using AutoSelect with old servers, start in FullColor - // mode. See comment in autoSelectFormatAndEncoding. - if (cp.beforeVersion(3, 8) && options.autoSelect) { - options.fullColour = true; - } - - // Save the server's current format - serverDefaultPF = cp.pf(); - - // Calculate the full-colour format to use - calculateFullColourPF(); - - // Request the initial update - vlog.info("requesting initial update"); - formatChange = encodingChange = requestUpdate = true; - requestNewUpdate(); - - // Show the window - setVisible(true); -} - -void -CView::serverCutText(const char* str, int len) { - if (!options.serverCutText) return; - CharArray t(len+1); - memcpy(t.buf, str, len); - t.buf[len] = 0; - clipboard.setClipText(t.buf); -} - - -void CView::beginRect(const Rect& r, unsigned int encoding) { - sock->inStream().startTiming(); -} - -void CView::endRect(const Rect& r, unsigned int encoding) { - sock->inStream().stopTiming(); - lastUsedEncoding_ = encoding; - if (debugDelay != 0) { - invertRect(r); - debugRects.push_back(r); - } -} - -void CView::fillRect(const Rect& r, Pixel pix) { - if (cursorBackingRect.overlaps(r)) hideLocalCursor(); - buffer->fillRect(r, pix); - invalidateBufferRect(r); -} -void CView::imageRect(const Rect& r, void* pixels) { - if (cursorBackingRect.overlaps(r)) hideLocalCursor(); - buffer->imageRect(r, pixels); - invalidateBufferRect(r); -} -void CView::copyRect(const Rect& r, int srcX, int srcY) { - if (cursorBackingRect.overlaps(r) || - cursorBackingRect.overlaps(Rect(srcX, srcY, srcX+r.width(), srcY+r.height()))) - hideLocalCursor(); - buffer->copyRect(r, Point(r.tl.x-srcX, r.tl.y-srcY)); - invalidateBufferRect(r); -} - -void CView::invertRect(const Rect& r) { - int stride; - rdr::U8* p = buffer->getPixelsRW(r, &stride); - for (int y = 0; y < r.height(); y++) { - for (int x = 0; x < r.width(); x++) { - switch (buffer->getPF().bpp) { - case 8: ((rdr::U8* )p)[x+y*stride] ^= 0xff; break; - case 16: ((rdr::U16*)p)[x+y*stride] ^= 0xffff; break; - case 32: ((rdr::U32*)p)[x+y*stride] ^= 0xffffffff; break; - } - } - } - invalidateBufferRect(r); -} - -bool CView::getUserPasswd(char** user, char** password) { - if (!user && options.passwordFile.buf[0]) { - FILE* fp = fopen(options.passwordFile.buf, "rb"); - if (!fp) return false; - char data[256]; - int datalen = fread(data, 1, 256, fp); - fclose(fp); - if (datalen != 8) return false; - vncAuthUnobfuscatePasswd(data); - *password = strDup(data); - memset(data, 0, strlen(data)); - return true; - } - - if (user && options.userName.buf) - *user = strDup(options.userName.buf); - if (password && options.password.buf) - *password = strDup(options.password.buf); - if ((user && !*user) || (password && !*password)) { - // Missing username or password - prompt the user - UserPasswdDialog userPasswdDialog; - userPasswdDialog.setCSecurity(getCurrentCSecurity()); - if (!userPasswdDialog.getUserPasswd(user, password)) - return false; - } - if (user) options.setUserName(*user); - if (password) options.setPassword(*password); - return true; -} - -bool CView::processFTMsg(int type) -{ - return m_fileTransfer.processFTMsg(type); -} \ No newline at end of file diff --git a/vncviewer/cview.h b/vncviewer/cview.h deleted file mode 100644 index 0dc00662..00000000 --- a/vncviewer/cview.h +++ /dev/null @@ -1,312 +0,0 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ - -// -=- CView.h - -// An instance of the CView class is created for each VNC Viewer connection. - -#ifndef __RFB_WIN32_CVIEW_H__ -#define __RFB_WIN32_CVIEW_H__ - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - - -namespace rfb { - - namespace win32 { - - class CView : public CConnection, - public UserPasswdGetter, - rfb::win32::Clipboard::Notifier, - rdr::FdInStreamBlockCallback - { - public: - CView(); - virtual ~CView(); - - bool initialise(network::Socket* s); - - void setManager(CViewManager* m) {manager = m;} - - void applyOptions(CViewOptions& opt); - const CViewOptions& getOptions() const {return options;}; - - // -=- Window Message handling - - virtual LRESULT processMessage(UINT msg, WPARAM wParam, LPARAM lParam); - virtual LRESULT processFrameMessage(UINT msg, WPARAM wParam, LPARAM lParam); - - // -=- Socket blocking handling - // blockCallback will throw QuitMessage(result) when - // it processes a WM_QUIT message. - // The caller may catch that to cope gracefully with - // a request to quit. - - class QuitMessage : public rdr::Exception { - public: - QuitMessage(WPARAM wp) : rdr::Exception("QuitMessage") {} - WPARAM wParam; - }; - virtual void blockCallback(); - - // -=- Window interface - - void postQuitOnDestroy(bool qod) {quit_on_destroy = qod;} - PixelFormat getNativePF() const; - void setVisible(bool visible); - void close(const char* reason=0); - HWND getHandle() const {return hwnd;} - HWND getFrameHandle() const {return frameHwnd;} - - void notifyClipboardChanged(const char* text, int len); - - // -=- Coordinate conversions - - inline Point bufferToClient(const Point& p) { - Point pos = p; - if (client_size.width() > buffer->width()) - pos.x += (client_size.width() - buffer->width()) / 2; - else if (client_size.width() < buffer->width()) - pos.x -= scrolloffset.x; - if (client_size.height() > buffer->height()) - pos.y += (client_size.height() - buffer->height()) / 2; - else if (client_size.height() < buffer->height()) - pos.y -= scrolloffset.y; - return pos; - } - inline Rect bufferToClient(const Rect& r) { - return Rect(bufferToClient(r.tl), bufferToClient(r.br)); - } - - inline Point clientToBuffer(const Point& p) { - Point pos = p; - if (client_size.width() > buffer->width()) - pos.x -= (client_size.width() - buffer->width()) / 2; - else if (client_size.width() < buffer->width()) - pos.x += scrolloffset.x; - if (client_size.height() > buffer->height()) - pos.y -= (client_size.height() - buffer->height()) / 2; - else if (client_size.height() < buffer->height()) - pos.y += scrolloffset.y; - return pos; - } - inline Rect clientToBuffer(const Rect& r) { - return Rect(clientToBuffer(r.tl), clientToBuffer(r.br)); - } - - void setFullscreen(bool fs); - - bool setViewportOffset(const Point& tl); - - bool processBumpScroll(const Point& cursorPos); - void setBumpScroll(bool on); - - int lastUsedEncoding() const { return lastUsedEncoding_; } - - // -=- CConnection interface overrides - - virtual CSecurity* getCSecurity(int secType); - - virtual void setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs); - virtual void bell(); - - virtual void framebufferUpdateEnd(); - - virtual void setDesktopSize(int w, int h); - virtual void setCursor(const Point& hotspot, const Point& size, void* data, void* mask); - virtual void setName(const char* name); - virtual void serverInit(); - - virtual void serverCutText(const char* str, int len); - - virtual void beginRect(const Rect& r, unsigned int encoding); - virtual void endRect(const Rect& r, unsigned int encoding); - - virtual void fillRect(const Rect& r, Pixel pix); - virtual void imageRect(const Rect& r, void* pixels); - virtual void copyRect(const Rect& r, int srcX, int srcY); - - void invertRect(const Rect& r); - - // VNCviewer dialog objects - - OptionsDialog optionsDialog; - - friend class InfoDialog; - InfoDialog infoDialog; - - // UserPasswdGetter overrides, used to support a pre-supplied VNC password - virtual bool getUserPasswd(char** user, char** password); - - // Global user-config registry key - static RegKey userConfigKey; - - bool processFTMsg(int type); - - protected: - - // Locally-rendered VNC cursor - void hideLocalCursor(); - void showLocalCursor(); - void renderLocalCursor(); - - // The system-rendered cursor - void hideSystemCursor(); - void showSystemCursor(); - - // Grab AltTab? - void setAltTabGrab(bool grab); - - // cursorOutsideBuffer() is called whenever we detect that the mouse has - // moved outside the desktop. It restores the system arrow cursor. - void cursorOutsideBuffer(); - - // Returns true if part of the supplied rect is visible, false otherwise - bool invalidateBufferRect(const Rect& crect); - - // Auto-encoding selector - void autoSelectFormatAndEncoding(); - - // Request an update with appropriate setPixelFormat and setEncodings calls - void requestNewUpdate(); - - // Update the window palette if the display is palette-based. - // Colours are pulled from the DIBSectionBuffer's ColourMap. - // Only the specified range of indexes is dealt with. - // After the update, the entire window is redrawn. - void refreshWindowPalette(int start, int count); - - // Determine whether or not we need to enable/disable scrollbars and set the - // window style accordingly - void calculateScrollBars(); - - // Recalculate the most suitable full-colour pixel format - void calculateFullColourPF(); - - // Enable/disable/check/uncheck the F8 menu items as appropriate. - void updateF8Menu(bool hideSystemCommands); - - // VNCviewer options - - CViewOptions options; - - // Input handling - void writeKeyEvent(rdr::U8 vkey, rdr::U32 flags, bool down); - void writePointerEvent(int x, int y, int buttonMask); - rfb::win32::CKeyboard kbd; - rfb::win32::CPointer ptr; - Point oldpos; - - // Clipboard handling - rfb::win32::Clipboard clipboard; - - // Pixel format and encoding - PixelFormat serverDefaultPF; - PixelFormat fullColourPF; - bool sameMachine; - bool encodingChange; - bool formatChange; - int lastUsedEncoding_; - - // Networking and RFB protocol - network::Socket* sock; - bool readyToRead; - bool requestUpdate; - - // Palette handling - LogicalPalette windowPalette; - bool palette_changed; - - // - Full-screen mode - Rect fullScreenOldRect; - DWORD fullScreenOldFlags; - bool fullScreenActive; - - // Bump-scrolling (used in full-screen mode) - bool bumpScroll; - Point bumpScrollDelta; - IntervalTimer bumpScrollTimer; - - // Cursor handling - Cursor cursor; - bool systemCursorVisible; // Should system-cursor be drawn? - bool trackingMouseLeave; - bool cursorInBuffer; // Is cursor position within server buffer? (ONLY for LocalCursor) - bool cursorVisible; // Is cursor currently rendered? - bool cursorAvailable; // Is cursor available for rendering? - Point cursorPos; - ManagedPixelBuffer cursorBacking; - Rect cursorBackingRect; - - // ** Debugging/logging - /* - int update_rect_count; - int update_pixel_count; - Rect update_extent; - */ - std::list debugRects; - - // ToolBar handling - ViewerToolBar tb; - bool toolbar; - - // Local window state - win32::DIBSectionBuffer* buffer; - bool has_focus; - bool quit_on_destroy; - Rect window_size; - Rect client_size; - Point scrolloffset; - Point maxscrolloffset; - HWND hwnd; - HWND frameHwnd; - - // Handle back to CViewManager instance, if any - CViewManager* manager; - - FileTransfer m_fileTransfer; - - }; - - }; - -}; - -#endif - - diff --git a/vncviewer/msvcwarning.h b/vncviewer/msvcwarning.h deleted file mode 100644 index 53a0678d..00000000 --- a/vncviewer/msvcwarning.h +++ /dev/null @@ -1,19 +0,0 @@ -/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, - * USA. - */ -#pragma warning( disable : 4800 ) // forcing bool 'true' or 'false' -#pragma warning( disable : 4786 ) // debug identifier truncated diff --git a/vncviewer/resource.h b/vncviewer/resource.h index 59eee86e..b3c7c8ce 100644 --- a/vncviewer/resource.h +++ b/vncviewer/resource.h @@ -16,8 +16,9 @@ #define IDR_TRAY 112 #define IDD_CONNECTION_INFO 113 #define IDD_DEFAULTS 116 -#define IDD_FILETRANSFER_DLG 120 -#define IDB_TOOLBAR 121 +#define IDB_BITMAP 120 +#define IDD_FILETRANSFER_DLG 121 +#define IDB_TOOLBAR 122 #define IDD_FTDIRNAME_DLG 123 #define IDD_FTCONFIRM_DLG 124 #define IDI_FTUP 125 @@ -77,14 +78,10 @@ #define IDC_MENU_KEY 1051 #define IDC_REQUESTED_ENCODING 1052 #define IDC_LAST_ENCODING 1053 -#define IDC_ENCODING_TIGHT 1054 -#define IDC_FTLOCALPATH 1054 -#define IDC_CUSTOM_COMPRESSLEVEL 1055 -#define IDC_FTREMOTEPATH 1055 -#define IDC_COMPRESSLEVEL 1056 -#define IDC_FTREMOTELIST 1056 -#define IDC_ALLOW_JPEG 1057 -#define IDC_FTLOCALRELOAD 1057 +#define IDC_SECURITY_LEVEL 1054 +#define IDC_INFO_ENCRYPTION 1055 +#define IDC_AUTO_RECONNECT 1056 +#define IDC_DISABLE_WINKEYS 1057 #define IDC_QUALITYLEVEL 1058 #define IDC_FTLOCALUP 1058 #define IDC_SEND_SYSKEYS 1059 @@ -115,6 +112,15 @@ #define IDC_FTTEXT 1084 #define IDC_FTBROWSEPATH 1085 #define IDC_FTBROWSETREE 1086 +#define IDC_TYPE 1088 +#define IDC_ENCODING_TIGHT 1089 +#define IDC_FTLOCALPATH 1090 +#define IDC_CUSTOM_COMPRESSLEVEL 1091 +#define IDC_FTREMOTEPATH 1092 +#define IDC_COMPRESSLEVEL 1093 +#define IDC_FTREMOTELIST 1094 +#define IDC_ALLOW_JPEG 1095 +#define IDC_FTLOCALRELOAD 1096 #define ID_TOOLBAR 40002 #define ID_CLOSE 40003 #define ID_OPTIONS 40004 @@ -143,7 +149,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 134 #define _APS_NEXT_COMMAND_VALUE 40028 -#define _APS_NEXT_CONTROL_VALUE 1087 +#define _APS_NEXT_CONTROL_VALUE 1097 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/vncviewer/vncviewer.bmp b/vncviewer/vncviewer.bmp new file mode 100644 index 00000000..4ea9c378 Binary files /dev/null and b/vncviewer/vncviewer.bmp differ diff --git a/vncviewer/vncviewer.cxx b/vncviewer/vncviewer.cxx index f0c9f208..3a5214a3 100644 --- a/vncviewer/vncviewer.cxx +++ b/vncviewer/vncviewer.cxx @@ -1,5 +1,5 @@ -/* Copyright (C) 2002-2004 RealVNC Ltd. All Rights Reserved. - * +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -25,21 +25,18 @@ #include #include -#include -#include +#include +#include #include - +#include +#include +#include #include #include #include #include - #include -#include -#include -#include - -#include +#include #ifdef _DIALOG_CAPTURE #include @@ -79,72 +76,6 @@ 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. @@ -166,7 +97,7 @@ programUsage() { printf("usage: vncviewer [:]\n"); printf("Command-line options:\n"); printf(" -help - Provide usage information.\n"); - printf(" -config - Load connection settings from VNCViewer 3.3 settings file\n"); + printf(" -config - Load connection settings from VNC Viewer 3.3 settings file\n"); printf(" -console - Run with a console window visible.\n"); printf(" = - Set the named configuration parameter.\n"); printf(" (Parameter values specified on the command-line override those specified by other configuration methods.)\n"); @@ -176,6 +107,9 @@ programUsage() { Logger::listLoggers(); printf("\nParameters:\n"); Configuration::listParams(); + printf("Press Enter/Return key to continue\n"); + char c = getchar(); + exit(1); } @@ -224,6 +158,8 @@ processParams(int argc, char* argv[]) { sprintf(tmp.buf, fmt, argv[i]); MsgBox(0, TStr(tmp.buf), MB_ICONSTOP | MB_OK); exit(1); + } else if (strContains(argv[i], '\\')) { + configFiles.push_back(strDup(argv[i])); } else { hosts.push_back(strDup(argv[i])); } @@ -241,7 +177,6 @@ processParams(int argc, char* argv[]) { // int WINAPI WinMain(HINSTANCE inst, HINSTANCE prevInst, char* cmdLine, int cmdShow) { - try { // - Initialise the available loggers @@ -270,7 +205,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prevInst, char* cmdLine, int cmdSho #ifdef _DIALOG_CAPTURE if (captureDialogs) { - CView::userConfigKey.openKey(HKEY_CURRENT_USER, _T("Software\\TightVNC\\VNCViewer4")); + CConn::userConfigKey.openKey(HKEY_CURRENT_USER, _T("Software\\TightVNC\\VNCViewer4")); OptionsDialog::global.showDialog(0, true); return 0; } @@ -285,35 +220,27 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prevInst, char* cmdLine, int cmdSho // - Connect to the clients if (!configFiles.empty() || !hosts.empty() || acceptIncoming) { // - Configure the registry configuration reader - win32::RegistryReader reg_reader; - reg_reader.setKey(HKEY_CURRENT_USER, _T("Software\\TightVNC\\VNCViewer4")); + win32::RegConfigThread config; + config.start(HKEY_CURRENT_USER, _T("Software\\TightVNC\\VNCViewer4")); // - Tell the rest of VNC Viewer where to write config data to - CView::userConfigKey.openKey(HKEY_CURRENT_USER, _T("Software\\TightVNC\\VNCViewer4")); - - // - Start the Socket subsystem for TCP - TcpSocket::initTcpSockets(); - - // Create the client connection manager - CViewManager view_manager; + CConn::userConfigKey.createKey(HKEY_CURRENT_USER, _T("Software\\TightVNC\\VNCViewer4")); if (acceptIncoming) { int port = 5500; // Listening viewer - if (hosts.size() > 1) { + if (hosts.size() > 1) programUsage(); - exit(2); - } - if (!hosts.empty()) { + if (!hosts.empty()) port = atoi(hosts.front()); - } - vlog.debug("opening listener"); + // Show the tray icon & menu + ListenTrayIcon tray; - CViewTrayIcon tray(view_manager); - - view_manager.addDefaultTCPListener(port); + // Listen for reverse connections + network::TcpListener sock(port); + ListenServer listener(&sock); // Run the view manager // Also processes the tray icon if necessary @@ -322,13 +249,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prevInst, char* cmdLine, int cmdSho TranslateMessage(&msg); DispatchMessage(&msg); } - - vlog.debug("quitting viewer"); } else { // Read each config file in turn while (!configFiles.empty()) { char* filename = configFiles.front(); - view_manager.addClient(filename, true); + Thread* connThread = new CConnThread(filename, true); strFree(filename); configFiles.pop_front(); } @@ -336,14 +261,14 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prevInst, char* cmdLine, int cmdSho // Connect to each client in turn while (!hosts.empty()) { char* hostinfo = hosts.front(); - view_manager.addClient(hostinfo); + Thread* connThread = new CConnThread(hostinfo); strFree(hostinfo); hosts.pop_front(); } // Run the view manager MSG msg; - while (GetMessage(&msg, NULL, 0, 0) > 0) { + while (CConnThread::getMessage(&msg, NULL, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); } diff --git a/vncviewer/vncviewer.dsp b/vncviewer/vncviewer.dsp index 071b3e40..3c96f009 100644 --- a/vncviewer/vncviewer.dsp +++ b/vncviewer/vncviewer.dsp @@ -44,7 +44,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c -# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /FI"msvcwarning.h" /D "NDEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /FI"rdr/msvcwarning.h" /D "NDEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /c # ADD BASE RSC /l 0x809 /d "NDEBUG" # ADD RSC /l 0x809 /d "NDEBUG" BSC32=bscmake.exe @@ -73,7 +73,7 @@ PreLink_Cmds=cl /c /nologo /FoRelease\ /FdRelease\ /MT buildTime.cxx # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"rdr/msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c # ADD BASE RSC /l 0x809 /d "_DEBUG" # ADD RSC /l 0x809 /d "_DEBUG" BSC32=bscmake.exe @@ -103,7 +103,7 @@ PreLink_Cmds=cl /c /nologo /FoDebug\ /FdDebug\ /MTd buildTime.cxx # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_DEBUG" /D "_WINDOWS" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c -# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"msvcwarning.h" /D "_WINDOWS" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ".." /FI"rdr/msvcwarning.h" /D "_WINDOWS" /D "_DEBUG" /D "WIN32" /D "_UNICODE" /D "UNICODE" /YX /FD /GZ /c # ADD BASE RSC /l 0x809 /d "_DEBUG" # ADD RSC /l 0x809 /d "_DEBUG" BSC32=bscmake.exe @@ -134,19 +134,27 @@ SOURCE=.\buildTime.cxx # End Source File # Begin Source File -SOURCE=.\ConnectionDialog.cxx +SOURCE=.\CConn.cxx +# End Source File +# Begin Source File + +SOURCE=.\CConnOptions.cxx +# End Source File +# Begin Source File + +SOURCE=.\CConnThread.cxx # End Source File # Begin Source File -SOURCE=.\cview.cxx +SOURCE=.\ConnectingDialog.cxx # End Source File # Begin Source File -SOURCE=.\CViewManager.cxx +SOURCE=.\ConnectionDialog.cxx # End Source File # Begin Source File -SOURCE=.\CViewOptions.cxx +SOURCE=.\DesktopWindow.cxx # End Source File # Begin Source File @@ -206,23 +214,27 @@ SOURCE=.\vncviewer.rc # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File -SOURCE=.\ConnectingDialog.h +SOURCE=.\CConn.h # End Source File # Begin Source File -SOURCE=.\ConnectionDialog.h +SOURCE=.\CConnOptions.h +# End Source File +# Begin Source File + +SOURCE=.\CConnThread.h # End Source File # Begin Source File -SOURCE=.\cview.h +SOURCE=.\ConnectingDialog.h # End Source File # Begin Source File -SOURCE=.\CViewManager.h +SOURCE=.\ConnectionDialog.h # End Source File # Begin Source File -SOURCE=.\CViewOptions.h +SOURCE=.\DesktopWindow.h # End Source File # Begin Source File @@ -258,6 +270,14 @@ SOURCE=.\InfoDialog.h # End Source File # Begin Source File +SOURCE=.\ListenServer.h +# End Source File +# Begin Source File + +SOURCE=.\ListenTrayIcon.h +# End Source File +# Begin Source File + SOURCE=.\MRU.h # End Source File # Begin Source File @@ -302,6 +322,10 @@ SOURCE=.\toolbar.bmp # End Source File # Begin Source File +SOURCE=.\vncviewer.bmp +# End Source File +# Begin Source File + SOURCE=.\vncviewer.exe.manifest # End Source File # Begin Source File