diff --git a/acinclude.m4 b/acinclude.m4 index 2ebdc27f..62009432 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -513,3 +513,33 @@ else $2 fi ])dnl ACX_PTHREAD + +dnl enable maximum compiler warnings +dnl we only know how to do this for g++ +AC_DEFUN([ACX_CXX_WARNINGS], [ + AC_MSG_CHECKING([for C++ compiler warning flags]) + if test "$GXX" = "yes"; then + acx_cxx_warnings="-Wall" + fi + if test -n "$acx_cxx_warnings"; then + CXXFLAGS="$CXXFLAGS $acx_cxx_warnings" + else + acx_cxx_warnings="unknown" + fi + AC_MSG_RESULT($acx_cxx_warnings) +])dnl ACX_CXX_WARNINGS + +dnl enable compiler warnings are errors +dnl we only know how to do this for g++ +AC_DEFUN([ACX_CXX_WARNINGS_ARE_ERRORS], [ + AC_MSG_CHECKING([for C++ compiler warning are errors flags]) + if test "$GXX" = "yes"; then + acx_cxx_warnings_are_errors="-Werror" + fi + if test -n "$acx_cxx_warnings_are_errors"; then + CXXFLAGS="$CXXFLAGS $acx_cxx_warnings_are_errors" + else + acx_cxx_warnings_are_errors="unknown" + fi + AC_MSG_RESULT($acx_cxx_warnings_are_errors) +])dnl ACX_CXX_WARNINGS_ARE_ERRORS diff --git a/cmd/launcher/LaunchUtil.cpp b/cmd/launcher/LaunchUtil.cpp index 46c8ef97..240fd1d9 100644 --- a/cmd/launcher/LaunchUtil.cpp +++ b/cmd/launcher/LaunchUtil.cpp @@ -14,6 +14,7 @@ #include "CConfig.h" #include "LaunchUtil.h" +#include "CMSWindowsUtil.h" #include "CArch.h" #include "resource.h" #include "stdfstream.h" @@ -23,32 +24,13 @@ CString getString(DWORD id) { - char buffer[1024]; - buffer[0] = '\0'; - LoadString(s_instance, id, buffer, sizeof(buffer) / sizeof(buffer[0])); - return buffer; + return CMSWindowsUtil::getString(s_instance, id); } CString getErrorString(DWORD error) { - char* buffer; - if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_IGNORE_INSERTS | - FORMAT_MESSAGE_FROM_SYSTEM, - 0, - error, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR)&buffer, - 0, - NULL) == 0) { - return getString(IDS_ERROR); - } - else { - CString result(buffer); - LocalFree(buffer); - return result; - } + return CMSWindowsUtil::getErrorString(s_instance, error, IDS_ERROR); } void diff --git a/cmd/launcher/launcher.rc b/cmd/launcher/launcher.rc index a8bf8c7b..0a5ceb01 100644 --- a/cmd/launcher/launcher.rc +++ b/cmd/launcher/launcher.rc @@ -332,8 +332,8 @@ BEGIN "Synergy is not configured to start automatically." IDS_INSTALL_LABEL "Install" IDS_UNINSTALL_LABEL "Uninstall" - IDS_INSTALL_GENERIC_ERROR "Install failed: %{1}." - IDS_UNINSTALL_GENERIC_ERROR "Uninstall failed: %{1}." + IDS_INSTALL_GENERIC_ERROR "Install failed: %{1}" + IDS_UNINSTALL_GENERIC_ERROR "Uninstall failed: %{1}" IDS_INSTALL_TITLE "Installed Auto-Start" IDS_INSTALLED_SYSTEM "Installed auto-start. Synergy will now automatically start each time you start your computer." IDS_INSTALLED_USER "Installed auto-start. Synergy will now automatically start each time you log in." @@ -348,6 +348,7 @@ BEGIN IDS_SERVER_IS_CLIENT "Please enter the computer name of the synergy server, not\nthe name of this computer, in the Server Host Name field." IDS_ADD_SCREEN "Add Screen" IDS_EDIT_SCREEN "Edit Screen %{1}" + IDS_ERROR_CODE "Error code: %{1}" END #endif // English (U.S.) resources diff --git a/cmd/launcher/resource.h b/cmd/launcher/resource.h index 2885b2f0..3ef98665 100644 --- a/cmd/launcher/resource.h +++ b/cmd/launcher/resource.h @@ -41,6 +41,7 @@ #define IDS_ADD_SCREEN 37 #define IDS_EDIT_SCREEN 38 #define IDS_INVALID_TIME 39 +#define IDS_ERROR_CODE 39 #define IDD_MAIN 101 #define IDD_ADD 102 #define IDD_WAIT 103 diff --git a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp index 3bfd5816..c53ea1c8 100644 --- a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +++ b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp @@ -21,18 +21,19 @@ #include "CArchTaskBarWindows.h" #include "resource.h" -static const UINT g_stateToIconID[CMSWindowsClientTaskBarReceiver::kMaxState] = +// +// CMSWindowsClientTaskBarReceiver +// + +const UINT CMSWindowsClientTaskBarReceiver::s_stateToIconID[kMaxState] = { IDI_TASKBAR_NOT_RUNNING, IDI_TASKBAR_NOT_WORKING, IDI_TASKBAR_NOT_CONNECTED, + IDI_TASKBAR_NOT_CONNECTED, IDI_TASKBAR_CONNECTED }; -// -// CMSWindowsClientTaskBarReceiver -// - CMSWindowsClientTaskBarReceiver::CMSWindowsClientTaskBarReceiver( HINSTANCE appInstance, const CBufferedLogOutputter* logBuffer) : CClientTaskBarReceiver(), @@ -41,7 +42,7 @@ CMSWindowsClientTaskBarReceiver::CMSWindowsClientTaskBarReceiver( m_logBuffer(logBuffer) { for (UInt32 i = 0; i < kMaxState; ++i) { - m_icon[i] = loadIcon(g_stateToIconID[i]); + m_icon[i] = loadIcon(s_stateToIconID[i]); } m_menu = LoadMenu(m_appInstance, MAKEINTRESOURCE(IDR_TASKBAR)); @@ -171,7 +172,7 @@ CMSWindowsClientTaskBarReceiver::primaryAction() const IArchTaskBarReceiver::Icon CMSWindowsClientTaskBarReceiver::getIcon() const { - return reinterpret_cast(m_icon[getState()]); + return reinterpret_cast(m_icon[getStatus()]); } void diff --git a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h index 56a3f161..72d33be5 100644 --- a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h +++ b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h @@ -58,6 +58,7 @@ private: HMENU m_menu; HICON m_icon[kMaxState]; const CBufferedLogOutputter* m_logBuffer; + static const UINT s_stateToIconID[]; }; #endif diff --git a/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp index 13fac23f..681f9be5 100644 --- a/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp +++ b/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp @@ -19,7 +19,8 @@ // CXWindowsClientTaskBarReceiver // -CXWindowsClientTaskBarReceiver::CXWindowsClientTaskBarReceiver() +CXWindowsClientTaskBarReceiver::CXWindowsClientTaskBarReceiver( + const CBufferedLogOutputter*) { // add ourself to the task bar ARCH->addReceiver(this); diff --git a/cmd/synergyc/CXWindowsClientTaskBarReceiver.h b/cmd/synergyc/CXWindowsClientTaskBarReceiver.h index fab3a649..fa9da471 100644 --- a/cmd/synergyc/CXWindowsClientTaskBarReceiver.h +++ b/cmd/synergyc/CXWindowsClientTaskBarReceiver.h @@ -17,10 +17,12 @@ #include "CClientTaskBarReceiver.h" +class CBufferedLogOutputter; + //! Implementation of CClientTaskBarReceiver for X Windows class CXWindowsClientTaskBarReceiver : public CClientTaskBarReceiver { public: - CXWindowsClientTaskBarReceiver(); + CXWindowsClientTaskBarReceiver(const CBufferedLogOutputter*); virtual ~CXWindowsClientTaskBarReceiver(); // IArchTaskBarReceiver overrides diff --git a/cmd/synergyc/Makefile.am b/cmd/synergyc/Makefile.am index 6b2510ce..4f53508b 100644 --- a/cmd/synergyc/Makefile.am +++ b/cmd/synergyc/Makefile.am @@ -48,6 +48,7 @@ synergyc_LDADD = \ $(DEPTH)/lib/io/libio.a \ $(DEPTH)/lib/mt/libmt.a \ $(DEPTH)/lib/base/libbase.a \ + $(DEPTH)/lib/common/libcommon.a \ $(DEPTH)/lib/arch/libarch.a \ $(X_LIBS) \ $(X_PRE_LIBS) \ diff --git a/cmd/synergyc/resource.h b/cmd/synergyc/resource.h index 1da78601..37f085d6 100644 --- a/cmd/synergyc/resource.h +++ b/cmd/synergyc/resource.h @@ -3,6 +3,8 @@ // Used by synergyc.rc // #define IDS_FAILED 1 +#define IDS_INIT_FAILED 2 +#define IDS_UNCAUGHT_EXCEPTION 3 #define IDI_SYNERGY 101 #define IDI_TASKBAR_NOT_RUNNING 102 #define IDI_TASKBAR_NOT_WORKING 103 diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 1515acac..7ca94409 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -26,6 +26,7 @@ #include "CFunctionEventJob.h" #include "CLog.h" #include "CString.h" +#include "CStringUtil.h" #include "LogOutputters.h" #include "CArch.h" #include "XArch.h" @@ -33,8 +34,9 @@ #define DAEMON_RUNNING(running_) #if WINDOWS_LIKE -#include "CMSWindowsScreen.h" #include "CArchMiscWindows.h" +#include "CMSWindowsScreen.h" +#include "CMSWindowsUtil.h" #include "CMSWindowsClientTaskBarReceiver.h" #include "resource.h" #undef DAEMON_RUNNING @@ -51,6 +53,9 @@ #define DAEMON_NAME "synergyc" #endif +typedef int (*StartupFunc)(int, char**); +static void parse(int argc, const char* const* argv); + // // program arguments // @@ -64,7 +69,8 @@ public: m_backend(false), m_restartable(true), m_daemon(true), - m_logFilter(NULL) + m_logFilter(NULL), + m_serverAddress(NULL) { s_instance = this; } ~CArgs() { s_instance = NULL; } @@ -76,7 +82,7 @@ public: bool m_daemon; const char* m_logFilter; CString m_name; - CNetworkAddress m_serverAddress; + CNetworkAddress* m_serverAddress; }; CArgs* CArgs::s_instance = NULL; @@ -97,6 +103,18 @@ createScreen() #endif } +static +CClientTaskBarReceiver* +createTaskBarReceiver(const CBufferedLogOutputter* logBuffer) +{ +#if WINDOWS_LIKE + return new CMSWindowsClientTaskBarReceiver( + CMSWindowsScreen::getInstance(), logBuffer); +#elif UNIX_LIKE + return new CXWindowsClientTaskBarReceiver(logBuffer); +#endif +} + // // platform independent main @@ -292,7 +310,7 @@ startClient() try { clientScreen = openClientScreen(); s_client = openClient(ARG->m_name, - ARG->m_serverAddress, clientScreen); + *ARG->m_serverAddress, clientScreen); s_clientScreen = clientScreen; LOG((CLOG_NOTE "started client")); s_client->connect(); @@ -338,7 +356,7 @@ stopClient() static int -realMain() +mainLoop() { // start the client. if this return false then we've failed and // we shouldn't retry. @@ -350,8 +368,8 @@ realMain() // run event loop. if startClient() failed we're supposed to retry // later. the timer installed by startClient() will take care of // that. - DAEMON_RUNNING(true); CEvent event; + DAEMON_RUNNING(true); EVENTQUEUE->getEvent(event); while (event.getType() != CEvent::kQuit) { EVENTQUEUE->dispatchEvent(event); @@ -369,45 +387,65 @@ realMain() return kExitSuccess; } -/* static -void -realMainEntry(void* vresult) +int +daemonMainLoop(int, const char**) { - *reinterpret_cast(vresult) = realMain(); + CSystemLogger sysLogger(DAEMON_NAME); + return mainLoop(); } static int -runMainInThread(void) +standardStartup(int argc, char** argv) { - int result = 0; - CThread appThread(new CFunctionJob(&realMainEntry, &result)); - try { -#if WINDOWS_LIKE - MSG msg; - while (appThread.waitForEvent(-1.0) == CThread::kEvent) { - // check for a quit event - if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - if (msg.message == WM_QUIT) { - CThread::getCurrentThread().cancel(); - } - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } -#else - appThread.wait(-1.0); -#endif - return result; + // parse command line + parse(argc, argv); + + // daemonize if requested + if (ARG->m_daemon) { + return ARCH->daemonize(DAEMON_NAME, &daemonMainLoop); } - catch (XThread&) { - appThread.cancel(); - appThread.wait(-1.0); - throw; + else { + return mainLoop(); } } -*/ + +static +int +run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup) +{ + // general initialization + CSocketMultiplexer multiplexer; + CEventQueue eventQueue; + ARG->m_serverAddress = new CNetworkAddress; + ARG->m_pname = ARCH->getBasename(argv[0]); + + // install caller's output filter + if (outputter != NULL) { + CLOG->insert(outputter); + } + + // save log messages + CBufferedLogOutputter logBuffer(1000); + CLOG->insert(&logBuffer, true); + + // make the task bar receiver. the user can control this app + // through the task bar. + s_taskBarReceiver = createTaskBarReceiver(&logBuffer); + + // run + int result = startup(argc, argv); + + // done with task bar receiver + delete s_taskBarReceiver; + + // done with log buffer + CLOG->remove(&logBuffer); + + delete ARG->m_serverAddress; + return result; +} // @@ -588,7 +626,7 @@ parse(int argc, const char* const* argv) // save server address try { - ARG->m_serverAddress = CNetworkAddress(argv[i], kDefaultPort); + *ARG->m_serverAddress = CNetworkAddress(argv[i], kDefaultPort); } catch (XSocketAddress& e) { LOG((CLOG_PRINT "%s: %s" BYE, @@ -676,192 +714,106 @@ byeThrow(int x) static int -daemonStartup(int argc, const char** argv) +daemonNTMainLoop(int argc, const char** argv) { - CSystemLogger sysLogger(DAEMON_NAME); - - // have to cancel this thread to quit - s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); - - // catch errors that would normally exit - bye = &byeThrow; - - // parse command line parse(argc, argv); - - // cannot run as backend if running as a service ARG->m_backend = false; - - // run as a service - return CArchMiscWindows::runDaemon(realMain); + return CArchMiscWindows::runDaemon(mainLoop); } static int -daemonStartup95(int, const char**) +daemonNTStartup(int, char**) { CSystemLogger sysLogger(DAEMON_NAME); - return runMainInThread(); + bye = &byeThrow; + return ARCH->daemonize(DAEMON_NAME, &daemonNTMainLoop); } static -int -run(int argc, char** argv) +void +showError(HINSTANCE instance, const char* title, UINT id, const char* arg) { - // windows NT family starts services using no command line options. - // since i'm not sure how to tell the difference between that and - // a user providing no options we'll assume that if there are no - // arguments and we're on NT then we're being invoked as a service. - // users on NT can use `--daemon' or `--no-daemon' to force us out - // of the service code path. - if (argc <= 1 && !CArchMiscWindows::isWindows95Family()) { - try { - return ARCH->daemonize(DAEMON_NAME, &daemonStartup); - } - catch (XArchDaemon& e) { - LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); - } - return kExitFailed; - } - - // parse command line - parse(argc, argv); - - // daemonize if requested - if (ARG->m_daemon) { - // start as a daemon - if (CArchMiscWindows::isWindows95Family()) { - try { - return ARCH->daemonize(DAEMON_NAME, &daemonStartup95); - } - catch (XArchDaemon& e) { - LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); - } - return kExitFailed; - } - else { - // cannot start a service from the command line so just - // run normally (except with log messages redirected). - CSystemLogger sysLogger(DAEMON_NAME); - return runMainInThread(); - } - } - else { - // run - return runMainInThread(); - } + CString fmt = CMSWindowsUtil::getString(instance, id); + CString msg = CStringUtil::format(fmt.c_str(), arg); + MessageBox(NULL, msg.c_str(), title, MB_OK | MB_ICONWARNING); } int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { - CArch arch(instance); - CLOG; - CArgs args; - - // save instance - CMSWindowsScreen::init(instance); - - // get program name - ARG->m_pname = ARCH->getBasename(__argv[0]); - - // send PRINT and FATAL output to a message box - CLOG->insert(new CMessageBoxOutputter); - - // save log messages - CBufferedLogOutputter logBuffer(1000); - CLOG->insert(&logBuffer, true); - - // make the task bar receiver. the user can control this app - // through the task bar. - s_taskBarReceiver = new CMSWindowsClientTaskBarReceiver(instance, - &logBuffer); - s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); - - int result; try { - // run in foreground or as a daemon - result = run(__argc, __argv); + CArch arch(instance); + CMSWindowsScreen::init(instance); + CLOG; +// FIXME +// CThread::getCurrentThread().setPriority(-14); + CArgs args; + + // windows NT family starts services using no command line options. + // since i'm not sure how to tell the difference between that and + // a user providing no options we'll assume that if there are no + // arguments and we're on NT then we're being invoked as a service. + // users on NT can use `--daemon' or `--no-daemon' to force us out + // of the service code path. + StartupFunc startup = &standardStartup; + if (__argc <= 1 && !CArchMiscWindows::isWindows95Family()) { + startup = &daemonNTStartup; + } + + // send PRINT and FATAL output to a message box + int result = run(__argc, __argv, new CMessageBoxOutputter, startup); + + // let user examine any messages if we're running as a backend + // by putting up a dialog box before exiting. + if (args.m_backend && s_hasImportantLogMessages) { + showError(instance, args.m_pname, IDS_FAILED, ""); + } + + delete CLOG; + return result; + } + catch (XBase& e) { + showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, e.what()); + throw; + } + catch (XArch& e) { + showError(instance, __argv[0], IDS_INIT_FAILED, e.what().c_str()); + return kExitFailed; } catch (...) { - // note that we don't rethrow thread cancellation. we'll - // be exiting soon so it doesn't matter. what we'd like - // is for everything after this try/catch to be in a - // finally block. - result = kExitFailed; + showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, ""); + throw; } - - // done with task bar receiver - delete s_taskBarReceiver; - - // done with log buffer - CLOG->remove(&logBuffer); - - // let user examine any messages if we're running as a backend - // by putting up a dialog box before exiting. - if (ARG->m_backend && s_hasImportantLogMessages) { - char msg[1024]; - msg[0] = '\0'; - LoadString(instance, IDS_FAILED, msg, sizeof(msg) / sizeof(msg[0])); - MessageBox(NULL, msg, ARG->m_pname, MB_OK | MB_ICONWARNING); - } - - delete CLOG; - return result; } #elif UNIX_LIKE -static -int -daemonStartup(int, const char**) -{ - CSystemLogger sysLogger(DAEMON_NAME); - return realMain(); -} - int main(int argc, char** argv) { - CArch arch; - CLOG; - - // go really fast - CThread::getCurrentThread().setPriority(-14); - - CSocketMultiplexer multiplexer; - CEventQueue eventQueue; - - // get program name CArgs args; - ARG->m_pname = ARCH->getBasename(argv[0]); - - // make the task bar receiver. the user can control this app - // through the task bar. - s_taskBarReceiver = new CXWindowsClientTaskBarReceiver; - - // parse command line - parse(argc, argv); - - // daemonize if requested - int result; - if (ARG->m_daemon) { - try { - result = ARCH->daemonize(DAEMON_NAME, &daemonStartup); - } - catch (XArchDaemon&) { - LOG((CLOG_CRIT "failed to daemonize")); - result = kExitFailed; - } + try { + int result; + CArch arch; + CLOG; + CArgs args; + result = run(argc, argv, NULL, &standardStartup); + delete CLOG; + return result; } - else { - result = realMain(); + catch (XBase& e) { + LOG((CLOG_CRIT "Uncaught exception: %s\n", e.what())); + throw; + } + catch (XArch& e) { + LOG((CLOG_CRIT "Initialization failed: %s" BYE, e.what().c_str())); + return kExitFailed; + } + catch (...) { + LOG((CLOG_CRIT "Uncaught exception: \n")); + throw; } - - // done with task bar receiver - delete s_taskBarReceiver; - - return result; } #else diff --git a/cmd/synergyc/synergyc.rc b/cmd/synergyc/synergyc.rc index 9fd00206..596ce309 100644 --- a/cmd/synergyc/synergyc.rc +++ b/cmd/synergyc/synergyc.rc @@ -99,6 +99,8 @@ END STRINGTABLE DISCARDABLE BEGIN IDS_FAILED "Synergy is about to quit with errors or warnings. Please check the log then click OK." + IDS_INIT_FAILED "Synergy failed to initialize: %{1}" + IDS_UNCAUGHT_EXCEPTION "Uncaught exception: %{1}" END #endif // English (U.S.) resources diff --git a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp index ca6525ef..5f5e5a24 100644 --- a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +++ b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp @@ -21,7 +21,11 @@ #include "CArchTaskBarWindows.h" #include "resource.h" -static const UINT g_stateToIconID[CMSWindowsServerTaskBarReceiver::kMaxState] = +// +// CMSWindowsServerTaskBarReceiver +// + +const UINT CMSWindowsServerTaskBarReceiver::s_stateToIconID[kMaxState] = { IDI_TASKBAR_NOT_RUNNING, IDI_TASKBAR_NOT_WORKING, @@ -29,10 +33,6 @@ static const UINT g_stateToIconID[CMSWindowsServerTaskBarReceiver::kMaxState] = IDI_TASKBAR_CONNECTED }; -// -// CMSWindowsServerTaskBarReceiver -// - CMSWindowsServerTaskBarReceiver::CMSWindowsServerTaskBarReceiver( HINSTANCE appInstance, const CBufferedLogOutputter* logBuffer) : CServerTaskBarReceiver(), @@ -41,7 +41,7 @@ CMSWindowsServerTaskBarReceiver::CMSWindowsServerTaskBarReceiver( m_logBuffer(logBuffer) { for (UInt32 i = 0; i < kMaxState; ++i) { - m_icon[i] = loadIcon(g_stateToIconID[i]); + m_icon[i] = loadIcon(s_stateToIconID[i]); } m_menu = LoadMenu(m_appInstance, MAKEINTRESOURCE(IDR_TASKBAR)); @@ -77,12 +77,7 @@ CMSWindowsServerTaskBarReceiver::showStatus() std::string status = getToolTip(); // get the connect clients, if any - typedef std::vector CClientList; - CClientList clients; - CServer* server = getServer(); - if (server != NULL) { - server->getClients(clients); - } + const CClients& clients = getClients(); // done getting status unlock(); @@ -92,7 +87,7 @@ CMSWindowsServerTaskBarReceiver::showStatus() SendMessage(child, WM_SETTEXT, 0, (LPARAM)status.c_str()); child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_CLIENTS); SendMessage(child, LB_RESETCONTENT, 0, 0); - for (CClientList::const_iterator index = clients.begin(); + for (CClients::const_iterator index = clients.begin(); index != clients.end(); ) { const char* client = index->c_str(); if (++index == clients.end()) { @@ -191,7 +186,7 @@ CMSWindowsServerTaskBarReceiver::primaryAction() const IArchTaskBarReceiver::Icon CMSWindowsServerTaskBarReceiver::getIcon() const { - return reinterpret_cast(m_icon[getState()]); + return reinterpret_cast(m_icon[getStatus()]); } void diff --git a/cmd/synergys/CMSWindowsServerTaskBarReceiver.h b/cmd/synergys/CMSWindowsServerTaskBarReceiver.h index d1c3dc65..ab679077 100644 --- a/cmd/synergys/CMSWindowsServerTaskBarReceiver.h +++ b/cmd/synergys/CMSWindowsServerTaskBarReceiver.h @@ -58,6 +58,7 @@ private: HMENU m_menu; HICON m_icon[kMaxState]; const CBufferedLogOutputter* m_logBuffer; + static const UINT s_stateToIconID[]; }; #endif diff --git a/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp b/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp index b3e683a2..861d2f8c 100644 --- a/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp +++ b/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp @@ -19,7 +19,8 @@ // CXWindowsServerTaskBarReceiver // -CXWindowsServerTaskBarReceiver::CXWindowsServerTaskBarReceiver() +CXWindowsServerTaskBarReceiver::CXWindowsServerTaskBarReceiver( + const CBufferedLogOutputter*) { // add ourself to the task bar ARCH->addReceiver(this); diff --git a/cmd/synergys/CXWindowsServerTaskBarReceiver.h b/cmd/synergys/CXWindowsServerTaskBarReceiver.h index ad114145..73234123 100644 --- a/cmd/synergys/CXWindowsServerTaskBarReceiver.h +++ b/cmd/synergys/CXWindowsServerTaskBarReceiver.h @@ -17,10 +17,12 @@ #include "CServerTaskBarReceiver.h" +class CBufferedLogOutputter; + //! Implementation of CServerTaskBarReceiver for X Windows class CXWindowsServerTaskBarReceiver : public CServerTaskBarReceiver { public: - CXWindowsServerTaskBarReceiver(); + CXWindowsServerTaskBarReceiver(const CBufferedLogOutputter*); virtual ~CXWindowsServerTaskBarReceiver(); // IArchTaskBarReceiver overrides diff --git a/cmd/synergys/Makefile.am b/cmd/synergys/Makefile.am index f2c30e76..5ef69af9 100644 --- a/cmd/synergys/Makefile.am +++ b/cmd/synergys/Makefile.am @@ -48,6 +48,7 @@ synergys_LDADD = \ $(DEPTH)/lib/io/libio.a \ $(DEPTH)/lib/mt/libmt.a \ $(DEPTH)/lib/base/libbase.a \ + $(DEPTH)/lib/common/libcommon.a \ $(DEPTH)/lib/arch/libarch.a \ $(X_LIBS) \ $(X_PRE_LIBS) \ diff --git a/cmd/synergys/resource.h b/cmd/synergys/resource.h index 5ac24707..c52355cf 100644 --- a/cmd/synergys/resource.h +++ b/cmd/synergys/resource.h @@ -3,6 +3,8 @@ // Used by synergys.rc // #define IDS_FAILED 1 +#define IDS_INIT_FAILED 2 +#define IDS_UNCAUGHT_EXCEPTION 3 #define IDI_SYNERGY 101 #define IDI_TASKBAR_NOT_RUNNING 102 #define IDI_TASKBAR_NOT_WORKING 103 diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 98d1402d..d1900fea 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -28,6 +28,8 @@ #include "CEventQueue.h" #include "CFunctionEventJob.h" #include "CLog.h" +#include "CString.h" +#include "CStringUtil.h" #include "LogOutputters.h" #include "CArch.h" #include "XArch.h" @@ -36,8 +38,9 @@ #define DAEMON_RUNNING(running_) #if WINDOWS_LIKE -#include "CMSWindowsScreen.h" #include "CArchMiscWindows.h" +#include "CMSWindowsScreen.h" +#include "CMSWindowsUtil.h" #include "CMSWindowsServerTaskBarReceiver.h" #include "resource.h" #undef DAEMON_RUNNING @@ -63,6 +66,10 @@ #define SYS_CONFIG_NAME "synergy.conf" #endif +typedef int (*StartupFunc)(int, char**); +static void parse(int argc, const char* const* argv); +static void loadConfig(); + // // program arguments // @@ -90,8 +97,8 @@ public: const char* m_configFile; const char* m_logFilter; CString m_name; - CNetworkAddress m_synergyAddress; - CConfig m_config; + CNetworkAddress* m_synergyAddress; + CConfig* m_config; }; CArgs* CArgs::s_instance = NULL; @@ -112,6 +119,18 @@ createScreen() #endif } +static +CServerTaskBarReceiver* +createTaskBarReceiver(const CBufferedLogOutputter* logBuffer) +{ +#if WINDOWS_LIKE + return new CMSWindowsServerTaskBarReceiver( + CMSWindowsScreen::getInstance(), logBuffer); +#elif UNIX_LIKE + return new CXWindowsServerTaskBarReceiver(logBuffer); +#endif +} + // // platform independent main @@ -299,11 +318,11 @@ startServer() CPrimaryClient* primaryClient = NULL; CClientListener* listener = NULL; try { - CString name = ARG->m_config.getCanonicalName(ARG->m_name); + CString name = ARG->m_config->getCanonicalName(ARG->m_name); serverScreen = openServerScreen(); primaryClient = openPrimaryClient(name, serverScreen); - listener = openClientListener(ARG->m_config.getSynergyAddress()); - s_server = openServer(ARG->m_config, primaryClient); + listener = openClientListener(ARG->m_config->getSynergyAddress()); + s_server = openServer(*ARG->m_config, primaryClient); s_serverScreen = serverScreen; s_primaryClient = primaryClient; s_listener = listener; @@ -372,26 +391,26 @@ stopServer() static int -realMain() +mainLoop() { // if configuration has no screens then add this system // as the default - if (ARG->m_config.begin() == ARG->m_config.end()) { - ARG->m_config.addScreen(ARG->m_name); + if (ARG->m_config->begin() == ARG->m_config->end()) { + ARG->m_config->addScreen(ARG->m_name); } // set the contact address, if provided, in the config. // otherwise, if the config doesn't have an address, use // the default. - if (ARG->m_synergyAddress.isValid()) { - ARG->m_config.setSynergyAddress(ARG->m_synergyAddress); + if (ARG->m_synergyAddress->isValid()) { + ARG->m_config->setSynergyAddress(*ARG->m_synergyAddress); } - else if (!ARG->m_config.getSynergyAddress().isValid()) { - ARG->m_config.setSynergyAddress(CNetworkAddress(kDefaultPort)); + else if (!ARG->m_config->getSynergyAddress().isValid()) { + ARG->m_config->setSynergyAddress(CNetworkAddress(kDefaultPort)); } // canonicalize the primary screen name - CString primaryName = ARG->m_config.getCanonicalName(ARG->m_name); + CString primaryName = ARG->m_config->getCanonicalName(ARG->m_name); if (primaryName.empty()) { LOG((CLOG_CRIT "unknown screen name `%s'", ARG->m_name.c_str())); return kExitFailed; @@ -407,8 +426,8 @@ realMain() // run event loop. if startServer() failed we're supposed to retry // later. the timer installed by startServer() will take care of // that. - DAEMON_RUNNING(true); CEvent event; + DAEMON_RUNNING(true); EVENTQUEUE->getEvent(event); while (event.getType() != CEvent::kQuit) { EVENTQUEUE->dispatchEvent(event); @@ -426,43 +445,71 @@ realMain() return kExitSuccess; } -/* XXX static -void -realMainEntry(void* vresult) +int +daemonMainLoop(int, const char**) { - *reinterpret_cast(vresult) = realMain(); + CSystemLogger sysLogger(DAEMON_NAME); + return mainLoop(); } static int -runMainInThread(void) +standardStartup(int argc, char** argv) { - int result = 0; - CThread appThread(new CFunctionJob(&realMainEntry, &result)); - try { -#if WINDOWS_LIKE - MSG msg; - while (appThread.waitForEvent(-1.0) == CThread::kEvent) { - // check for a quit event - if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - if (msg.message == WM_QUIT) { - CThread::getCurrentThread().cancel(); - } - } - } -#else - appThread.wait(-1.0); -#endif - return result; + // parse command line + parse(argc, argv); + + // load configuration + loadConfig(); + + // daemonize if requested + if (ARG->m_daemon) { + return ARCH->daemonize(DAEMON_NAME, &daemonMainLoop); } - catch (XThread&) { - appThread.cancel(); - appThread.wait(-1.0); - throw; + else { + return mainLoop(); } } -*/ + +static +int +run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup) +{ + // general initialization + CSocketMultiplexer multiplexer; + CEventQueue eventQueue; + ARG->m_synergyAddress = new CNetworkAddress; + ARG->m_config = new CConfig; + ARG->m_pname = ARCH->getBasename(argv[0]); + + // install caller's output filter + if (outputter != NULL) { + CLOG->insert(outputter); + } + + // save log messages + CBufferedLogOutputter logBuffer(1000); + CLOG->insert(&logBuffer, true); + + // make the task bar receiver. the user can control this app + // through the task bar. + s_taskBarReceiver = createTaskBarReceiver(&logBuffer); + + // run + int result = startup(argc, argv); + + // done with task bar receiver + delete s_taskBarReceiver; + + // done with log buffer + CLOG->remove(&logBuffer); + + delete ARG->m_config; + delete ARG->m_synergyAddress; + return result; +} + // // command line parsing @@ -606,7 +653,7 @@ parse(int argc, const char* const* argv) else if (isArg(i, argc, argv, "-a", "--address", 1)) { // save listen address try { - ARG->m_synergyAddress = CNetworkAddress(argv[i + 1], + *ARG->m_synergyAddress = CNetworkAddress(argv[i + 1], kDefaultPort); } catch (XSocketAddress& e) { @@ -723,7 +770,7 @@ loadConfig(const char* pathname) if (!configStream) { throw XConfigRead("cannot open file"); } - configStream >> ARG->m_config; + configStream >> *ARG->m_config; LOG((CLOG_DEBUG "configuration read successfully")); return true; } @@ -828,197 +875,107 @@ byeThrow(int x) static int -daemonStartup(int argc, const char** argv) +daemonNTMainLoop(int argc, const char** argv) { - CSystemLogger sysLogger(DAEMON_NAME); - - // catch errors that would normally exit - bye = &byeThrow; - - // parse command line parse(argc, argv); - - // cannot run as backend if running as a service ARG->m_backend = false; - - // load configuration loadConfig(); - - // run as a service - return CArchMiscWindows::runDaemon(realMain); + return CArchMiscWindows::runDaemon(mainLoop); } static int -daemonStartup95(int, const char**) +daemonNTStartup(int, char**) { CSystemLogger sysLogger(DAEMON_NAME); - return runMainInThread(); + bye = &byeThrow; + return ARCH->daemonize(DAEMON_NAME, &daemonNTMainLoop); } static -int -run(int argc, char** argv) +void +showError(HINSTANCE instance, const char* title, UINT id, const char* arg) { - // windows NT family starts services using no command line options. - // since i'm not sure how to tell the difference between that and - // a user providing no options we'll assume that if there are no - // arguments and we're on NT then we're being invoked as a service. - // users on NT can use `--daemon' or `--no-daemon' to force us out - // of the service code path. - if (argc <= 1 && !CArchMiscWindows::isWindows95Family()) { - try { - return ARCH->daemonize(DAEMON_NAME, &daemonStartup); - } - catch (XArchDaemon& e) { - LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); - } - return kExitFailed; - } - - // parse command line - parse(argc, argv); - - // load configuration - loadConfig(); - - // daemonize if requested - if (ARG->m_daemon) { - // start as a daemon - if (CArchMiscWindows::isWindows95Family()) { - try { - return ARCH->daemonize(DAEMON_NAME, &daemonStartup95); - } - catch (XArchDaemon& e) { - LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname)); - } - return kExitFailed; - } - else { - // cannot start a service from the command line so just - // run normally (except with log messages redirected). - CSystemLogger sysLogger(DAEMON_NAME); - return runMainInThread(); - } - } - else { - // run - return runMainInThread(); - } + CString fmt = CMSWindowsUtil::getString(instance, id); + CString msg = CStringUtil::format(fmt.c_str(), arg); + MessageBox(NULL, msg.c_str(), title, MB_OK | MB_ICONWARNING); } int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) { - CArch arch(instance); - CLOG; - CArgs args; - - // save instance - CMSWindowsScreen::init(instance); - - // get program name - ARG->m_pname = ARCH->getBasename(__argv[0]); - - // send PRINT and FATAL output to a message box - CLOG->insert(new CMessageBoxOutputter); - - // save log messages - CBufferedLogOutputter logBuffer(1000); - CLOG->insert(&logBuffer, true); - - // make the task bar receiver. the user can control this app - // through the task bar. - s_taskBarReceiver = new CMSWindowsServerTaskBarReceiver(instance, - &logBuffer); - - int result; try { - // run in foreground or as a daemon - result = run(__argc, __argv); + CArch arch(instance); + CMSWindowsScreen::init(instance); + CLOG; +// FIXME +// CThread::getCurrentThread().setPriority(-14); + CArgs args; + + // windows NT family starts services using no command line options. + // since i'm not sure how to tell the difference between that and + // a user providing no options we'll assume that if there are no + // arguments and we're on NT then we're being invoked as a service. + // users on NT can use `--daemon' or `--no-daemon' to force us out + // of the service code path. + StartupFunc startup = &standardStartup; + if (__argc <= 1 && !CArchMiscWindows::isWindows95Family()) { + startup = &daemonNTStartup; + } + + // send PRINT and FATAL output to a message box + int result = run(__argc, __argv, new CMessageBoxOutputter, startup); + + // let user examine any messages if we're running as a backend + // by putting up a dialog box before exiting. + if (args.m_backend && s_hasImportantLogMessages) { + showError(instance, args.m_pname, IDS_FAILED, ""); + } + + delete CLOG; + return result; + } + catch (XBase& e) { + showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, e.what()); + throw; + } + catch (XArch& e) { + showError(instance, __argv[0], IDS_INIT_FAILED, e.what().c_str()); + return kExitFailed; } catch (...) { - // note that we don't rethrow thread cancellation. we'll - // be exiting soon so it doesn't matter. what we'd like - // is for everything after this try/catch to be in a - // finally block. - result = kExitFailed; + showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, ""); + throw; } - - // done with task bar receiver - delete s_taskBarReceiver; - - // done with log buffer - CLOG->remove(&logBuffer); - - // let user examine any messages if we're running as a backend - // by putting up a dialog box before exiting. - if (ARG->m_backend && s_hasImportantLogMessages) { - char msg[1024]; - msg[0] = '\0'; - LoadString(instance, IDS_FAILED, msg, sizeof(msg) / sizeof(msg[0])); - MessageBox(NULL, msg, ARG->m_pname, MB_OK | MB_ICONWARNING); - } - - delete CLOG; - return result; } #elif UNIX_LIKE -static -int -daemonStartup(int, const char**) -{ - CSystemLogger sysLogger(DAEMON_NAME); - return realMain(); -} - int main(int argc, char** argv) { - CArch arch; - CLOG; - - // go really fast - CThread::getCurrentThread().setPriority(-14); - - CSocketMultiplexer multiplexer; - CEventQueue eventQueue; - - // get program name CArgs args; - ARG->m_pname = ARCH->getBasename(argv[0]); - - // make the task bar receiver. the user can control this app - // through the task bar. - s_taskBarReceiver = new CXWindowsServerTaskBarReceiver; - - // parse command line - parse(argc, argv); - - // load configuration - loadConfig(); - - // daemonize if requested - int result; - if (ARG->m_daemon) { - try { - result = ARCH->daemonize(DAEMON_NAME, &daemonStartup); - } - catch (XArchDaemon&) { - LOG((CLOG_CRIT "failed to daemonize")); - result = kExitFailed; - } + try { + int result; + CArch arch; + CLOG; + CArgs args; + result = run(argc, argv, NULL, &standardStartup); + delete CLOG; + return result; } - else { - result = realMain(); + catch (XBase& e) { + LOG((CLOG_CRIT "Uncaught exception: %s\n", e.what())); + throw; + } + catch (XArch& e) { + LOG((CLOG_CRIT "Initialization failed: %s" BYE, e.what().c_str())); + return kExitFailed; + } + catch (...) { + LOG((CLOG_CRIT "Uncaught exception: \n")); + throw; } - - // done with task bar receiver - delete s_taskBarReceiver; - - return result; } #else diff --git a/cmd/synergys/synergys.rc b/cmd/synergys/synergys.rc index 9f190754..d528c4dd 100644 --- a/cmd/synergys/synergys.rc +++ b/cmd/synergys/synergys.rc @@ -102,6 +102,8 @@ END STRINGTABLE DISCARDABLE BEGIN IDS_FAILED "Synergy is about to quit with errors or warnings. Please check the log then click OK." + IDS_INIT_FAILED "Synergy failed to initialize: %{1}" + IDS_UNCAUGHT_EXCEPTION "Uncaught exception: %{1}" END #endif // English (U.S.) resources diff --git a/configure.in b/configure.in index 999fb1b7..5bde3874 100644 --- a/configure.in +++ b/configure.in @@ -94,6 +94,9 @@ dnl use AC_REPLACE_FUNCS() for stuff in string.h dnl checks for system services +dnl enable maximum compiler warnings and warnings are errors. +ACX_CXX_WARNINGS +ACX_CXX_WARNINGS_ARE_ERRORS dnl adjust variables for X11 and pthreads CXXFLAGS="$CXXFLAGS $SYNERGY_CXXFLAGS $X_CFLAGS $PTHREAD_CFLAGS -D_BSD_SOURCE -D_XOPEN_SOURCE=500" diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp index d07bb57e..d8640960 100644 --- a/lib/arch/CArch.cpp +++ b/lib/arch/CArch.cpp @@ -351,18 +351,6 @@ CArch::wait(CArchThread thread, double timeout) return m_mt->wait(thread, timeout); } -IArchMultithread::EWaitResult -CArch::waitForEvent(CArchThread thread, double timeout) -{ - return m_mt->waitForEvent(thread, timeout); -} - -void -CArch::unblockThread(CArchThread thread) -{ - m_mt->unblockThread(thread); -} - bool CArch::isSameThread(CArchThread thread1, CArchThread thread2) { @@ -390,13 +378,13 @@ CArch::getIDOfThread(CArchThread thread) void CArch::setInterruptHandler(InterruptFunc func, void* userData) { - return m_mt->setInterruptHandler(func, userData); + m_mt->setInterruptHandler(func, userData); } void CArch::interrupt() { - return m_mt->interrupt(); + m_mt->interrupt(); } CArchSocket @@ -459,6 +447,12 @@ CArch::pollSocket(CPollEntry pe[], int num, double timeout) return m_net->pollSocket(pe, num, timeout); } +void +CArch::unblockPollSocket(CArchThread thread) +{ + m_net->unblockPollSocket(thread); +} + size_t CArch::readSocket(CArchSocket s, void* buf, size_t len) { diff --git a/lib/arch/CArch.h b/lib/arch/CArch.h index 18c1cd08..f82c27cf 100644 --- a/lib/arch/CArch.h +++ b/lib/arch/CArch.h @@ -116,8 +116,6 @@ public: virtual void setPriorityOfThread(CArchThread, int n); virtual void testCancelThread(); virtual bool wait(CArchThread, double timeout); - virtual EWaitResult waitForEvent(CArchThread, double timeout); - virtual void unblockThread(CArchThread thread); virtual bool isSameThread(CArchThread, CArchThread); virtual bool isExitedThread(CArchThread); virtual void* getResultOfThread(CArchThread); @@ -136,6 +134,7 @@ public: virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); virtual void connectSocket(CArchSocket s, CArchNetAddress name); virtual int pollSocket(CPollEntry[], int num, double timeout); + virtual void unblockPollSocket(CArchThread thread); virtual size_t readSocket(CArchSocket s, void* buf, size_t len); virtual size_t writeSocket(CArchSocket s, const void* buf, size_t len); diff --git a/lib/arch/CArchConsoleWindows.cpp b/lib/arch/CArchConsoleWindows.cpp index f479b378..2cdedc71 100644 --- a/lib/arch/CArchConsoleWindows.cpp +++ b/lib/arch/CArchConsoleWindows.cpp @@ -110,7 +110,7 @@ CArchConsoleWindows::getNewlineForConsole() BOOL WINAPI CArchConsoleWindows::signalHandler(DWORD) { - // terminate thread and skip remaining handlers - ARCH->cancelThread(s_thread); + // terminate app and skip remaining handlers + ARCH->interrupt(); return TRUE; } diff --git a/lib/arch/CArchDaemonWindows.cpp b/lib/arch/CArchDaemonWindows.cpp index 54dc5b40..edca9385 100644 --- a/lib/arch/CArchDaemonWindows.cpp +++ b/lib/arch/CArchDaemonWindows.cpp @@ -24,10 +24,9 @@ CArchDaemonWindows* CArchDaemonWindows::s_daemon = NULL; -CArchDaemonWindows::CArchDaemonWindows() : - m_daemonThread(NULL) +CArchDaemonWindows::CArchDaemonWindows() { - // do nothing + m_quitMessage = RegisterWindowMessage("SynergyDaemonExit"); } CArchDaemonWindows::~CArchDaemonWindows() @@ -55,6 +54,17 @@ CArchDaemonWindows::daemonRunning(bool running) } } +UINT +CArchDaemonWindows::getDaemonQuitMessage() +{ + if (s_daemon != NULL) { + return s_daemon->doGetDaemonQuitMessage(); + } + else { + return 0; + } +} + void CArchDaemonWindows::daemonFailed(int result) { @@ -437,122 +447,89 @@ CArchDaemonWindows::openUserStartupKey() return CArchMiscWindows::openKey(HKEY_CURRENT_USER, s_keyNames); } +bool +CArchDaemonWindows::isRunState(DWORD state) +{ + switch (state) { + case SERVICE_START_PENDING: + case SERVICE_CONTINUE_PENDING: + case SERVICE_RUNNING: + return true; + + default: + return false; + } +} + int CArchDaemonWindows::doRunDaemon(RunFunc run) { // should only be called from DaemonFunc assert(m_serviceMutex != NULL); - assert(run != NULL); + assert(run != NULL); + // create message queue for this thread + MSG dummy; + PeekMessage(&dummy, NULL, 0, 0, PM_NOREMOVE); + + int result = 0; ARCH->lockMutex(m_serviceMutex); - try { - int result; - m_serviceHandlerWaiting = false; - m_serviceRunning = false; - for (;;) { - // mark server as running - setStatus(SERVICE_RUNNING); - - // run callback in another thread - m_serviceRunning = true; - m_daemonThread = ARCH->newThread( - &CArchDaemonWindows::runDaemonThreadEntry, run); - ARCH->wait(m_daemonThread, -1.0); - result = reinterpret_cast( - ARCH->getResultOfThread(m_daemonThread)); - m_serviceRunning = false; - - // notify handler that the server stopped. if handler - // isn't waiting then we stopped unexpectedly and we - // quit. - if (m_serviceHandlerWaiting) { - m_serviceHandlerWaiting = false; - ARCH->broadcastCondVar(m_serviceCondVar); - } - else { - break; - } - - // wait until we're told what to do next - while (m_serviceState != SERVICE_RUNNING && - m_serviceState != SERVICE_STOPPED) { - ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); - } - - // exit loop if we've been told to stop - if (m_serviceState == SERVICE_STOPPED) { - break; - } - - // done with callback thread - ARCH->closeThread(m_daemonThread); - m_daemonThread = NULL; - } - - // prevent daemonHandler from changing state - m_serviceState = SERVICE_STOPPED; - - // tell service control that the service is stopped. - // FIXME -- hopefully this will ensure that our handler won't - // be called again but i can't find documentation that - // verifies that. if it does it'll crash on the mutex that - // we're about to destroy. - setStatus(m_serviceState); - - // clean up - if (m_daemonThread != NULL) { - ARCH->closeThread(m_daemonThread); - m_daemonThread = NULL; - } - ARCH->unlockMutex(m_serviceMutex); - - return result; - } - catch (...) { - // FIXME -- report error - - // prevent serviceHandler from changing state - m_serviceState = SERVICE_STOPPED; - - // set status - setStatusError(0); - - // wake up serviceHandler if it's waiting then wait for it - if (m_serviceHandlerWaiting) { - m_serviceHandlerWaiting = false; - ARCH->broadcastCondVar(m_serviceCondVar); + m_daemonThreadID = GetCurrentThreadId(); + while (m_serviceState != SERVICE_STOPPED) { + // wait until we're told to start + while (!isRunState(m_serviceState) && + m_serviceState != SERVICE_STOP_PENDING) { ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); - // serviceHandler has exited by now } - ARCH->unlockMutex(m_serviceMutex); - throw; + // run unless told to stop + if (m_serviceState != SERVICE_STOP_PENDING) { + ARCH->unlockMutex(m_serviceMutex); + try { + result = run(); + } + catch (...) { + ARCH->lockMutex(m_serviceMutex); + setStatusError(0); + m_serviceState = SERVICE_STOPPED; + setStatus(m_serviceState); + ARCH->broadcastCondVar(m_serviceCondVar); + ARCH->unlockMutex(m_serviceMutex); + throw; + } + ARCH->lockMutex(m_serviceMutex); + } + + // notify of new state + if (m_serviceState == SERVICE_PAUSE_PENDING) { + m_serviceState = SERVICE_PAUSED; + } + else { + m_serviceState = SERVICE_STOPPED; + } + setStatus(m_serviceState); + ARCH->broadcastCondVar(m_serviceCondVar); } + ARCH->unlockMutex(m_serviceMutex); + return result; } void CArchDaemonWindows::doDaemonRunning(bool running) { + ARCH->lockMutex(m_serviceMutex); if (running) { - ARCH->unlockMutex(m_serviceMutex); - } - else { - ARCH->lockMutex(m_serviceMutex); + m_serviceState = SERVICE_RUNNING; + setStatus(m_serviceState); + ARCH->broadcastCondVar(m_serviceCondVar); } + ARCH->unlockMutex(m_serviceMutex); } -void* -CArchDaemonWindows::runDaemonThread(RunFunc run) +UINT +CArchDaemonWindows::doGetDaemonQuitMessage() { - return reinterpret_cast(run()); -} - -void* -CArchDaemonWindows::runDaemonThreadEntry(void* vrun) -{ - assert(s_daemon != NULL); - - return s_daemon->runDaemonThread(reinterpret_cast(vrun)); + return m_quitMessage; } void @@ -583,6 +560,8 @@ CArchDaemonWindows::setStatus(DWORD state, DWORD step, DWORD waitHint) void CArchDaemonWindows::setStatusError(DWORD error) { + assert(s_daemon != NULL); + SERVICE_STATUS status; status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS; @@ -605,11 +584,10 @@ CArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn) const char** argv = const_cast(argvIn); // create synchronization objects - m_serviceMutex = ARCH->newMutex(); - m_serviceCondVar = ARCH->newCondVar(); - m_serviceState = SERVICE_RUNNING; - - // register our service handler functiom + m_serviceMutex = ARCH->newMutex(); + m_serviceCondVar = ARCH->newCondVar(); + + // register our service handler function m_statusHandle = RegisterServiceCtrlHandler(argv[0], &CArchDaemonWindows::serviceHandlerEntry); if (m_statusHandle == NULL) { @@ -621,7 +599,8 @@ CArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn) } // tell service control manager that we're starting - setStatus(SERVICE_START_PENDING, 0, 10000); + m_serviceState = SERVICE_START_PENDING; + setStatus(m_serviceState, 0, 10000); // if no arguments supplied then try getting them from the registry. // the first argument doesn't count because it's the service name. @@ -726,58 +705,40 @@ CArchDaemonWindows::serviceHandler(DWORD ctrl) ARCH->lockMutex(m_serviceMutex); // ignore request if service is already stopped - if (m_serviceState == SERVICE_STOPPED) { - setStatus(m_serviceState); + if (s_daemon == NULL || m_serviceState == SERVICE_STOPPED) { + if (s_daemon != NULL) { + setStatus(m_serviceState); + } ARCH->unlockMutex(m_serviceMutex); return; } switch (ctrl) { case SERVICE_CONTROL_PAUSE: - // update state m_serviceState = SERVICE_PAUSE_PENDING; setStatus(m_serviceState, 0, 5000); - - // stop run callback if running and wait for it to finish - if (m_serviceRunning) { - m_serviceHandlerWaiting = true; - ARCH->cancelThread(m_daemonThread); + PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0); + while (isRunState(m_serviceState)) { ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); } - - // update state if service hasn't stopped while we were waiting - if (m_serviceState != SERVICE_STOPPED) { - m_serviceState = SERVICE_PAUSED; - } - ARCH->broadcastCondVar(m_serviceCondVar); break; case SERVICE_CONTROL_CONTINUE: - // required status update - setStatus(m_serviceState); - - // update state but let main loop send RUNNING notification - m_serviceState = SERVICE_RUNNING; + // FIXME -- maybe should flush quit messages from queue + m_serviceState = SERVICE_CONTINUE_PENDING; + setStatus(m_serviceState, 0, 5000); ARCH->broadcastCondVar(m_serviceCondVar); - ARCH->unlockMutex(m_serviceMutex); - return; + break; case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: - // update state m_serviceState = SERVICE_STOP_PENDING; setStatus(m_serviceState, 0, 5000); - - // stop run callback if running and wait for it to finish - if (m_serviceRunning) { - m_serviceHandlerWaiting = true; - ARCH->cancelThread(m_daemonThread); + PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0); + ARCH->broadcastCondVar(m_serviceCondVar); + while (isRunState(m_serviceState)) { ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); } - - // update state - m_serviceState = SERVICE_STOPPED; - ARCH->broadcastCondVar(m_serviceCondVar); break; default: @@ -785,12 +746,10 @@ CArchDaemonWindows::serviceHandler(DWORD ctrl) // fall through case SERVICE_CONTROL_INTERROGATE: + setStatus(m_serviceState); break; } - // send update - setStatus(m_serviceState); - ARCH->unlockMutex(m_serviceMutex); } diff --git a/lib/arch/CArchDaemonWindows.h b/lib/arch/CArchDaemonWindows.h index 57fe74fe..4fa31bc4 100644 --- a/lib/arch/CArchDaemonWindows.h +++ b/lib/arch/CArchDaemonWindows.h @@ -42,7 +42,7 @@ public: (i.e. after initialization) and \c daemonRunning(false) when it leaves the main loop. The \c runFunc is called in a new thread and when the daemon must exit the main loop due to some external control the - thread is cancelled on behalf of the client. This function returns + getDaemonQuitMessage() is posted to the thread. This function returns what \c runFunc returns. \c runFunc should call \c daemonFailed() if the daemon fails. */ @@ -63,6 +63,14 @@ public: */ static void daemonFailed(int result); + //! Get daemon quit message + /*! + The windows NT daemon tells daemon thread to exit by posting this + message to it. The thread must, of course, have a message queue + for this to work. + */ + static UINT getDaemonQuitMessage(); + // IArchDaemon overrides virtual void installDaemon(const char* name, const char* description, @@ -81,13 +89,13 @@ private: int doRunDaemon(RunFunc runFunc); void doDaemonRunning(bool running); + UINT doGetDaemonQuitMessage(); static void setStatus(DWORD state); static void setStatus(DWORD state, DWORD step, DWORD waitHint); static void setStatusError(DWORD error); - void* runDaemonThread(RunFunc); - static void* runDaemonThreadEntry(void*); + static bool isRunState(DWORD state); void serviceMain(DWORD, LPTSTR*); static void WINAPI serviceMainEntry(DWORD, LPTSTR*); @@ -113,11 +121,13 @@ private: bool m_serviceHandlerWaiting; bool m_serviceRunning; - CArchThread m_daemonThread; + DWORD m_daemonThreadID; DaemonFunc m_daemonFunc; int m_daemonResult; SERVICE_STATUS_HANDLE m_statusHandle; + + UINT m_quitMessage; }; #endif diff --git a/lib/arch/CArchMiscWindows.cpp b/lib/arch/CArchMiscWindows.cpp index 1fb3d7bd..d2b0842c 100644 --- a/lib/arch/CArchMiscWindows.cpp +++ b/lib/arch/CArchMiscWindows.cpp @@ -19,9 +19,12 @@ // CArchMiscWindows // +CArchMiscWindows::CDialogs* CArchMiscWindows::s_dialogs = NULL; + void CArchMiscWindows::init() { + s_dialogs = new CDialogs; isWindows95Family(); } @@ -64,6 +67,12 @@ CArchMiscWindows::daemonFailed(int result) CArchDaemonWindows::daemonFailed(result); } +UINT +CArchMiscWindows::getDaemonQuitMessage() +{ + return CArchDaemonWindows::getDaemonQuitMessage(); +} + HKEY CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName) { @@ -196,3 +205,27 @@ CArchMiscWindows::readValueInt(HKEY key, const TCHAR* name) } return value; } + +void +CArchMiscWindows::addDialog(HWND hwnd) +{ + s_dialogs->insert(hwnd); +} + +void +CArchMiscWindows::removeDialog(HWND hwnd) +{ + s_dialogs->erase(hwnd); +} + +bool +CArchMiscWindows::processDialog(MSG* msg) +{ + for (CDialogs::const_iterator index = s_dialogs->begin(); + index != s_dialogs->end(); ++index) { + if (IsDialogMessage(*index, msg)) { + return true; + } + } + return false; +} diff --git a/lib/arch/CArchMiscWindows.h b/lib/arch/CArchMiscWindows.h index 766553ff..749e52f5 100644 --- a/lib/arch/CArchMiscWindows.h +++ b/lib/arch/CArchMiscWindows.h @@ -19,6 +19,7 @@ #include "common.h" #include "stdstring.h" +#include "stdset.h" #include //! Miscellaneous win32 functions. @@ -53,6 +54,12 @@ public: */ static void daemonFailed(int result); + //! Get daemon quit message + /*! + Delegates to CArchDaemonWindows. + */ + static UINT getDaemonQuitMessage(); + //! Open and return a registry key, closing the parent key static HKEY openKey(HKEY parent, const TCHAR* child); @@ -83,6 +90,24 @@ public: //! Read a DWORD value from the registry static DWORD readValueInt(HKEY, const TCHAR* name); + + //! Add a dialog + static void addDialog(HWND); + + //! Remove a dialog + static void removeDialog(HWND); + + //! Process dialog message + /*! + Checks if the message is destined for a dialog. If so the message + is passed to the dialog and returns true, otherwise returns false. + */ + static bool processDialog(MSG*); + +private: + typedef std::set CDialogs; + + static CDialogs* s_dialogs; }; #endif diff --git a/lib/arch/CArchMultithreadPosix.cpp b/lib/arch/CArchMultithreadPosix.cpp index 1d5680d6..403cc82b 100644 --- a/lib/arch/CArchMultithreadPosix.cpp +++ b/lib/arch/CArchMultithreadPosix.cpp @@ -134,6 +134,18 @@ CArchMultithreadPosix::~CArchMultithreadPosix() s_instance = NULL; } +void +CArchMultithreadPosix::unblockThread(CArchThread thread) +{ + pthread_kill(thread->m_thread, SIGWAKEUP); +} + +CArchMultithreadPosix* +CArchMultithreadPosix::getInstance() +{ + return s_instance; +} + CArchCond CArchMultithreadPosix::newCondVar() { @@ -517,19 +529,6 @@ CArchMultithreadPosix::wait(CArchThread target, double timeout) } } -IArchMultithread::EWaitResult -CArchMultithreadPosix::waitForEvent(CArchThread, double /*timeout*/) -{ - // not implemented - return kTimeout; -} - -void -CArchMultithreadPosix::unblockThread(CArchThread thread) -{ - pthread_kill(thread->m_thread, SIGWAKEUP); -} - bool CArchMultithreadPosix::isSameThread(CArchThread thread1, CArchThread thread2) { @@ -575,7 +574,7 @@ CArchMultithreadPosix::interrupt() lockMutex(m_threadMutex); if (m_signalFunc != NULL) { m_signalFunc(m_signalUserData); - pthread_kill(m_mainThread->m_thread, SIGWAKEUP); + unblockThread(m_mainThread); } else { ARCH->cancelThread(m_mainThread); diff --git a/lib/arch/CArchMultithreadPosix.h b/lib/arch/CArchMultithreadPosix.h index 4b87f0a0..3921c8e9 100644 --- a/lib/arch/CArchMultithreadPosix.h +++ b/lib/arch/CArchMultithreadPosix.h @@ -37,6 +37,19 @@ public: CArchMultithreadPosix(); virtual ~CArchMultithreadPosix(); + //! @name manipulators + //@{ + + void unblockThread(CArchThread thread); + + //@} + //! @name accessors + //@{ + + static CArchMultithreadPosix* getInstance(); + + //@} + // IArchMultithread overrides virtual CArchCond newCondVar(); virtual void closeCondVar(CArchCond); @@ -55,8 +68,6 @@ public: virtual void setPriorityOfThread(CArchThread, int n); virtual void testCancelThread(); virtual bool wait(CArchThread, double timeout); - virtual EWaitResult waitForEvent(CArchThread, double timeout); - virtual void unblockThread(CArchThread thread); virtual bool isSameThread(CArchThread, CArchThread); virtual bool isExitedThread(CArchThread); virtual void* getResultOfThread(CArchThread); diff --git a/lib/arch/CArchMultithreadWindows.cpp b/lib/arch/CArchMultithreadWindows.cpp index f156021f..384722c4 100644 --- a/lib/arch/CArchMultithreadWindows.cpp +++ b/lib/arch/CArchMultithreadWindows.cpp @@ -50,6 +50,7 @@ public: bool m_cancelling; HANDLE m_exit; void* m_result; + void* m_networkData; }; CArchThreadImpl::CArchThreadImpl() : @@ -59,7 +60,8 @@ CArchThreadImpl::CArchThreadImpl() : m_func(NULL), m_userData(NULL), m_cancelling(false), - m_result(NULL) + m_result(NULL), + m_networkData(NULL) { m_exit = CreateEvent(NULL, TRUE, FALSE, NULL); m_cancel = CreateEvent(NULL, TRUE, FALSE, NULL); @@ -78,7 +80,9 @@ CArchThreadImpl::~CArchThreadImpl() CArchMultithreadWindows* CArchMultithreadWindows::s_instance = NULL; -CArchMultithreadWindows::CArchMultithreadWindows() +CArchMultithreadWindows::CArchMultithreadWindows() : + m_signalFunc(NULL), + m_signalUserData(NULL) { assert(s_instance == NULL); s_instance = this; @@ -88,10 +92,10 @@ CArchMultithreadWindows::CArchMultithreadWindows() // create thread for calling (main) thread and add it to our // list. no need to lock the mutex since we're the only thread. - CArchThreadImpl* mainThread = new CArchThreadImpl; - mainThread->m_thread = NULL; - mainThread->m_id = GetCurrentThreadId(); - insert(mainThread); + m_mainThread = new CArchThreadImpl; + m_mainThread->m_thread = NULL; + m_mainThread->m_id = GetCurrentThreadId(); + insert(m_mainThread); } CArchMultithreadWindows::~CArchMultithreadWindows() @@ -108,6 +112,24 @@ CArchMultithreadWindows::~CArchMultithreadWindows() delete m_threadMutex; } +void +CArchMultithreadWindows::setNetworkDataForCurrentThread(void* data) +{ + lockMutex(m_threadMutex); + CArchThreadImpl* thread = findNoRef(GetCurrentThreadId()); + thread->m_networkData = data; + unlockMutex(m_threadMutex); +} + +void* +CArchMultithreadWindows::getNetworkDataForThread(CArchThread thread) +{ + lockMutex(m_threadMutex); + void* data = thread->m_networkData; + unlockMutex(m_threadMutex); + return data; +} + HANDLE CArchMultithreadWindows::getCancelEventForCurrentThread() { @@ -183,7 +205,7 @@ CArchMultithreadWindows::waitCondVar(CArchCond cond, // make a list of the condition variable events and the cancel event // for the current thread. - HANDLE handles[3]; + HANDLE handles[4]; handles[0] = cond->m_events[CArchCondImpl::kSignal]; handles[1] = cond->m_events[CArchCondImpl::kBroadcast]; handles[2] = getCancelEventForCurrentThread(); @@ -446,8 +468,8 @@ CArchMultithreadWindows::wait(CArchThread target, double timeout) t = (DWORD)(1000.0 * timeout); } - // wait for this thread to be cancelled or for the target thread to - // terminate. + // wait for this thread to be cancelled or woken up or for the + // target thread to terminate. HANDLE handles[2]; handles[0] = target->m_exit; handles[1] = self->m_cancel; @@ -478,137 +500,6 @@ CArchMultithreadWindows::wait(CArchThread target, double timeout) } } -IArchMultithread::EWaitResult -CArchMultithreadWindows::waitForEvent(CArchThread target, double timeout) -{ - // find current thread. ref the target so it can't go away while - // we're watching it. - lockMutex(m_threadMutex); - CArchThreadImpl* self = findNoRef(GetCurrentThreadId()); - assert(self != NULL); - if (target != NULL) { - refThread(target); - } - unlockMutex(m_threadMutex); - - // see if we've been cancelled before checking if any events - // are pending. - DWORD result = WaitForSingleObject(self->m_cancel, 0); - if (result == WAIT_OBJECT_0) { - if (target != NULL) { - closeThread(target); - } - testCancelThreadImpl(self); - } - - // check if messages are available first. if we don't do this then - // MsgWaitForMultipleObjects() will block even if the queue isn't - // empty if the messages in the queue were there before the last - // call to GetMessage()/PeekMessage(). - if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) { - return kEvent; - } - - // convert timeout - DWORD t; - if (timeout < 0.0) { - t = INFINITE; - } - else { - t = (DWORD)(1000.0 * timeout); - } - - // wait for this thread to be cancelled or for the target thread to - // terminate. - DWORD n = (target == NULL || target == self) ? 1 : 2; - HANDLE handles[2]; - handles[0] = self->m_cancel; - handles[1] = (n == 2) ? target->m_exit : NULL; - result = MsgWaitForMultipleObjects(n, handles, FALSE, t, QS_ALLINPUT); - - // cancel takes priority - if (result != WAIT_OBJECT_0 + 0 && - WaitForSingleObject(handles[0], 0) == WAIT_OBJECT_0) { - result = WAIT_OBJECT_0 + 0; - } - - // release target - if (target != NULL) { - closeThread(target); - } - - // handle result - switch (result) { - case WAIT_OBJECT_0 + 0: - // this thread was cancelled. does not return. - testCancelThreadImpl(self); - - case WAIT_OBJECT_0 + 1: - // target thread terminated - if (n == 2) { - return kExit; - } - // fall through - - case WAIT_OBJECT_0 + 2: - // message is available - return kEvent; - - default: - // timeout or error - return kTimeout; - } -} - -/* -bool -CArchMultithreadWindows::waitForEvent(double timeout) -{ - // check if messages are available first. if we don't do this then - // MsgWaitForMultipleObjects() will block even if the queue isn't - // empty if the messages in the queue were there before the last - // call to GetMessage()/PeekMessage(). - if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) { - return true; - } - - // find current thread - lockMutex(m_threadMutex); - CArchThreadImpl* self = findNoRef(GetCurrentThreadId()); - unlockMutex(m_threadMutex); - assert(self != NULL); - - // convert timeout - DWORD t; - if (timeout < 0.0) { - t = INFINITE; - } - else { - t = (DWORD)(1000.0 * timeout); - } - - // wait for this thread to be cancelled or for a message - HANDLE handles[1]; - handles[0] = self->m_cancel; - DWORD result = MsgWaitForMultipleObjects(1, handles, FALSE, t, QS_ALLINPUT); - - // handle result - switch (result) { - case WAIT_OBJECT_0 + 1: - // message is available - return true; - - case WAIT_OBJECT_0 + 0: - // this thread was cancelled. does not return. - testCancelThreadImpl(self); - - default: - // timeout or error - return false; - } -} -*/ - bool CArchMultithreadWindows::isSameThread(CArchThread thread1, CArchThread thread2) { @@ -637,6 +528,29 @@ CArchMultithreadWindows::getIDOfThread(CArchThread thread) return static_cast(thread->m_id); } +void +CArchMultithreadWindows::setInterruptHandler(InterruptFunc func, void* userData) +{ + lockMutex(m_threadMutex); + m_signalFunc = func; + m_signalUserData = userData; + unlockMutex(m_threadMutex); +} + +void +CArchMultithreadWindows::interrupt() +{ + lockMutex(m_threadMutex); + if (m_signalFunc != NULL) { + m_signalFunc(m_signalUserData); + ARCH->unblockPollSocket(m_mainThread); + } + else { + ARCH->cancelThread(m_mainThread); + } + unlockMutex(m_threadMutex); +} + CArchThreadImpl* CArchMultithreadWindows::find(DWORD id) { diff --git a/lib/arch/CArchMultithreadWindows.h b/lib/arch/CArchMultithreadWindows.h index 2a8d5462..aecd4173 100644 --- a/lib/arch/CArchMultithreadWindows.h +++ b/lib/arch/CArchMultithreadWindows.h @@ -43,12 +43,20 @@ public: CArchMultithreadWindows(); virtual ~CArchMultithreadWindows(); + // + // manipulators + // + + void setNetworkDataForCurrentThread(void*); + // // accessors // HANDLE getCancelEventForCurrentThread(); + void* getNetworkDataForThread(CArchThread); + static CArchMultithreadWindows* getInstance(); // IArchMultithread overrides @@ -69,11 +77,12 @@ public: virtual void setPriorityOfThread(CArchThread, int n); virtual void testCancelThread(); virtual bool wait(CArchThread, double timeout); - virtual EWaitResult waitForEvent(CArchThread, double timeout); virtual bool isSameThread(CArchThread, CArchThread); virtual bool isExitedThread(CArchThread); virtual void* getResultOfThread(CArchThread); virtual ThreadID getIDOfThread(CArchThread); + virtual void setInterruptHandler(InterruptFunc, void*); + virtual void interrupt(); private: CArchThreadImpl* find(DWORD id); @@ -96,6 +105,10 @@ private: CArchMutex m_threadMutex; CThreadList m_threadList; + CArchThread m_mainThread; + + InterruptFunc m_signalFunc; + void* m_signalUserData; }; #endif diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index 29f71586..487a2eb6 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -424,6 +424,12 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout) #endif +void +CArchNetworkBSD::unblockPollSocket(CArchThread thread) +{ + CArchMultithreadPosix::getInstance()->unblockThread(thread); +} + size_t CArchNetworkBSD::readSocket(CArchSocket s, void* buf, size_t len) { @@ -435,8 +441,8 @@ CArchNetworkBSD::readSocket(CArchSocket s, void* buf, size_t len) if (n == -1) { if (errno == EINTR) { // interrupted system call - ARCH->testCancelThread(); - continue; + n = 0; + break; } else if (errno == EAGAIN) { n = 0; @@ -460,8 +466,8 @@ CArchNetworkBSD::writeSocket(CArchSocket s, const void* buf, size_t len) if (n == -1) { if (errno == EINTR) { // interrupted system call - ARCH->testCancelThread(); - continue; + n = 0; + break; } else if (errno == EAGAIN) { // no buffer space diff --git a/lib/arch/CArchNetworkBSD.h b/lib/arch/CArchNetworkBSD.h index 18f942b8..b4ed7097 100644 --- a/lib/arch/CArchNetworkBSD.h +++ b/lib/arch/CArchNetworkBSD.h @@ -60,6 +60,7 @@ public: virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); virtual void connectSocket(CArchSocket s, CArchNetAddress name); virtual int pollSocket(CPollEntry[], int num, double timeout); + virtual void unblockPollSocket(CArchThread thread); virtual size_t readSocket(CArchSocket s, void* buf, size_t len); virtual size_t writeSocket(CArchSocket s, const void* buf, size_t len); diff --git a/lib/arch/CArchNetworkWinsock.cpp b/lib/arch/CArchNetworkWinsock.cpp index d7b58e87..547546bb 100644 --- a/lib/arch/CArchNetworkWinsock.cpp +++ b/lib/arch/CArchNetworkWinsock.cpp @@ -15,7 +15,9 @@ #include "CArchNetworkWinsock.h" #include "CArch.h" +#include "IArchMultithread.h" #include "XArchWindows.h" +#include static const int s_family[] = { PF_UNSPEC, @@ -49,6 +51,13 @@ static struct hostent FAR * (PASCAL FAR *gethostbyaddr_winsock)(const char FAR * static struct hostent FAR * (PASCAL FAR *gethostbyname_winsock)(const char FAR * name); static int (PASCAL FAR *WSACleanup_winsock)(void); static int (PASCAL FAR *WSAFDIsSet_winsock)(SOCKET, fd_set FAR * fdset); +static WSAEVENT (PASCAL FAR *WSACreateEvent_winsock)(void); +static BOOL (PASCAL FAR *WSACloseEvent_winsock)(WSAEVENT); +static BOOL (PASCAL FAR *WSASetEvent_winsock)(WSAEVENT); +static BOOL (PASCAL FAR *WSAResetEvent_winsock)(WSAEVENT); +static int (PASCAL FAR *WSAEventSelect_winsock)(SOCKET, WSAEVENT, long); +static DWORD (PASCAL FAR *WSAWaitForMultipleEvents_winsock)(DWORD, const WSAEVENT FAR*, BOOL, DWORD, BOOL); +static int (PASCAL FAR *WSAEnumNetworkEvents_winsock)(SOCKET, WSAEVENT, LPWSANETWORKEVENTS); #undef FD_ISSET #define FD_ISSET(fd, set) WSAFDIsSet_winsock((SOCKET)(fd), (fd_set FAR *)(set)) @@ -68,13 +77,23 @@ netGetProcAddress(HMODULE module, LPCSTR name) return func; } +CArchNetAddressImpl* +CArchNetAddressImpl::alloc(size_t size) +{ + size_t totalSize = size + ADDR_HDR_SIZE; + CArchNetAddressImpl* addr = (CArchNetAddressImpl*)malloc(totalSize); + addr->m_len = size; + return addr; +} + + // // CArchNetworkWinsock // CArchNetworkWinsock::CArchNetworkWinsock() { - static const char* s_library[] = { "ws2_32.dll", "wsock32.dll" }; + static const char* s_library[] = { "ws2_32.dll" }; assert(WSACleanup_winsock == NULL); assert(s_networkModule == NULL); @@ -110,14 +129,16 @@ CArchNetworkWinsock::~CArchNetworkWinsock() void CArchNetworkWinsock::init(HMODULE module) { - assert(module != NULL); + if (module == NULL) { + throw XArchNetworkSupport(""); + } // get startup function address int (PASCAL FAR *startup)(WORD, LPWSADATA); setfunc(startup, WSAStartup, int(PASCAL FAR*)(WORD, LPWSADATA)); // startup network library - WORD version = MAKEWORD(1 /*major*/, 1 /*minor*/); + WORD version = MAKEWORD(2 /*major*/, 0 /*minor*/); WSADATA data; int err = startup(version, &data); if (data.wVersion != version) { @@ -152,6 +173,13 @@ CArchNetworkWinsock::init(HMODULE module) setfunc(gethostbyname_winsock, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); setfunc(WSACleanup_winsock, WSACleanup, int (PASCAL FAR *)(void)); setfunc(WSAFDIsSet_winsock, __WSAFDIsSet, int (PASCAL FAR *)(SOCKET, fd_set FAR *)); + setfunc(WSACreateEvent_winsock, WSACreateEvent, WSAEVENT (PASCAL FAR *)(void)); + setfunc(WSACloseEvent_winsock, WSACloseEvent, BOOL (PASCAL FAR *)(WSAEVENT)); + setfunc(WSASetEvent_winsock, WSASetEvent, BOOL (PASCAL FAR *)(WSAEVENT)); + setfunc(WSAResetEvent_winsock, WSAResetEvent, BOOL (PASCAL FAR *)(WSAEVENT)); + setfunc(WSAEventSelect_winsock, WSAEventSelect, int (PASCAL FAR *)(SOCKET, WSAEVENT, long)); + setfunc(WSAWaitForMultipleEvents_winsock, WSAWaitForMultipleEvents, DWORD (PASCAL FAR *)(DWORD, const WSAEVENT FAR*, BOOL, DWORD, BOOL)); + setfunc(WSAEnumNetworkEvents_winsock, WSAEnumNetworkEvents, int (PASCAL FAR *)(SOCKET, WSAEVENT, LPWSANETWORKEVENTS)); s_networkModule = module; } @@ -171,6 +199,8 @@ CArchNetworkWinsock::newSocket(EAddressFamily family, ESocketType type) socket->m_socket = fd; socket->m_connected = false; socket->m_refCount = 1; + socket->m_event = WSACreateEvent_winsock(); + socket->m_pollWrite = false; return socket; } @@ -202,7 +232,7 @@ CArchNetworkWinsock::closeSocket(CArchSocket s) if (close_winsock(s->m_socket) == SOCKET_ERROR) { // close failed int err = getsockerror_winsock(); - if (err == EINTR) { + if (err == WSAEINTR) { // interrupted system call ARCH->testCancelThread(); continue; @@ -215,6 +245,7 @@ CArchNetworkWinsock::closeSocket(CArchSocket s) throwError(err); } } while (false); + WSACloseEvent_winsock(s->m_event); delete s; } } @@ -270,35 +301,24 @@ CArchNetworkWinsock::acceptSocket(CArchSocket s, CArchNetAddress* addr) { assert(s != NULL); - // if user passed NULL in addr then use scratch space - CArchNetAddress dummy; - if (addr == NULL) { - addr = &dummy; - } - - // create new socket and address + // create new socket and temporary address CArchSocketImpl* socket = new CArchSocketImpl; - *addr = new CArchNetAddressImpl; + CArchNetAddress tmp = CArchNetAddressImpl::alloc(sizeof(struct sockaddr)); // accept on socket SOCKET fd; do { - fd = accept_winsock(s->m_socket, &(*addr)->m_addr, &(*addr)->m_len); + fd = accept_winsock(s->m_socket, &tmp->m_addr, &tmp->m_len); if (fd == INVALID_SOCKET) { int err = getsockerror_winsock(); - if (err == EINTR) { + delete socket; + free(tmp); + *addr = NULL; + if (err == WSAEINTR) { // interrupted system call ARCH->testCancelThread(); - continue; + return NULL; } - if (err == WSAECONNABORTED) { - // connection was aborted; try again - ARCH->testCancelThread(); - continue; - } - delete socket; - delete *addr; - *addr = NULL; throwError(err); } } while (false); @@ -307,12 +327,15 @@ CArchNetworkWinsock::acceptSocket(CArchSocket s, CArchNetAddress* addr) socket->m_socket = fd; socket->m_connected = true; socket->m_refCount = 1; + socket->m_event = WSACreateEvent_winsock(); + socket->m_pollWrite = true; - // discard address if not requested - if (addr == &dummy) { - ARCH->closeAddr(dummy); + // copy address if requested + if (addr != NULL) { + *addr = ARCH->copyAddr(tmp); } + free(tmp); return socket; } @@ -325,7 +348,7 @@ CArchNetworkWinsock::connectSocket(CArchSocket s, CArchNetAddress addr) do { if (connect_winsock(s->m_socket, &addr->m_addr, addr->m_len) == SOCKET_ERROR) { - if (getsockerror_winsock() == EINTR) { + if (getsockerror_winsock() == WSAEINTR) { // interrupted system call ARCH->testCancelThread(); continue; @@ -354,95 +377,162 @@ CArchNetworkWinsock::connectSocket(CArchSocket s, CArchNetAddress addr) int CArchNetworkWinsock::pollSocket(CPollEntry pe[], int num, double timeout) { - int i, n; + int i; + DWORD n; - do { - // prepare sets for select - n = 0; - fd_set readSet, writeSet, errSet; - fd_set* readSetP = NULL; - fd_set* writeSetP = NULL; - fd_set* errSetP = NULL; - FD_ZERO(&readSet); - FD_ZERO(&writeSet); - FD_ZERO(&errSet); - for (i = 0; i < num; ++i) { - // reset return flags - pe[i].m_revents = 0; + // prepare sockets and wait list + bool canWrite = false; + WSAEVENT* events = (WSAEVENT*)alloca((num + 1) * sizeof(WSAEVENT)); + for (i = 0, n = 0; i < num; ++i) { + // reset return flags + pe[i].m_revents = 0; - // set invalid flag if socket is bogus then go to next socket - if (pe[i].m_socket == NULL) { - pe[i].m_revents |= kPOLLNVAL; - continue; - } + // set invalid flag if socket is bogus then go to next socket + if (pe[i].m_socket == NULL) { + pe[i].m_revents |= kPOLLNVAL; + continue; + } - if (pe[i].m_events & kPOLLIN) { - FD_SET(pe[i].m_socket->m_socket, &readSet); - readSetP = &readSet; - n = 1; - } - if (pe[i].m_events & kPOLLOUT) { - FD_SET(pe[i].m_socket->m_socket, &writeSet); - writeSetP = &writeSet; - n = 1; - } - if (true) { - FD_SET(pe[i].m_socket->m_socket, &errSet); - errSetP = &errSet; - n = 1; + // select desired events + long socketEvents = 0; + if ((pe[i].m_events & kPOLLIN) != 0) { + socketEvents |= FD_READ | FD_ACCEPT | FD_CLOSE; + } + if ((pe[i].m_events & kPOLLOUT) != 0) { + socketEvents |= FD_WRITE | FD_CONNECT | FD_CLOSE; + + // if m_pollWrite is false then we assume the socket is + // writable. winsock doesn't signal writability except + // when the state changes from unwritable. + if (!pe[i].m_socket->m_pollWrite) { + canWrite = true; + pe[i].m_revents |= kPOLLOUT; } } - // if there are no sockets then don't block forever - if (n == 0 && timeout < 0.0) { - timeout = 0.0; + // if no events then ignore socket + if (socketEvents == 0) { + continue; } - // prepare timeout for select - struct timeval timeout2; - struct timeval* timeout2P; - if (timeout < 0) { - timeout2P = NULL; + // select socket for desired events + WSAEventSelect_winsock(pe[i].m_socket->m_socket, + pe[i].m_socket->m_event, socketEvents); + + // add socket event to wait list + events[n++] = pe[i].m_socket->m_event; + } + + // if no sockets then return immediately + if (n == 0) { + return 0; + } + + // add the unblock event + CArchMultithreadWindows* mt = CArchMultithreadWindows::getInstance(); + CArchThread thread = mt->newCurrentThread(); + WSAEVENT* unblockEvent = (WSAEVENT*)mt->getNetworkDataForThread(thread); + if (unblockEvent == NULL) { + unblockEvent = new WSAEVENT; + *unblockEvent = WSACreateEvent_winsock(); + mt->setNetworkDataForCurrentThread(unblockEvent); + } + events[n++] = *unblockEvent; + + // prepare timeout + DWORD t = (timeout < 0.0) ? INFINITE : (DWORD)(1000.0 * timeout); + if (canWrite) { + // if we know we can write then don't block + t = 0; + } + + // wait + DWORD result = WSAWaitForMultipleEvents_winsock(n, events, FALSE, t, FALSE); + + // reset the unblock event + WSAResetEvent_winsock(*unblockEvent); + + // handle results + if (result == WSA_WAIT_FAILED) { + if (getsockerror_winsock() == WSAEINTR) { + // interrupted system call + ARCH->testCancelThread(); + return 0; } - else { - timeout2P = &timeout2; - timeout2.tv_sec = static_cast(timeout); - timeout2.tv_usec = static_cast(1.0e+6 * - (timeout - timeout2.tv_sec)); + throwError(getsockerror_winsock()); + } + if (result == WSA_WAIT_TIMEOUT && !canWrite) { + return 0; + } + if (result == WSA_WAIT_EVENT_0 + n - 1) { + // the unblock event was signalled + return 0; + } + for (i = 0, n = 0; i < num; ++i) { + // skip events we didn't check + if (pe[i].m_socket == NULL || + (pe[i].m_events & (kPOLLIN | kPOLLOUT)) == 0) { + continue; } - // do the select - n = select_winsock(0, readSetP, writeSetP, errSetP, timeout2P); + // get events + WSANETWORKEVENTS info; + if (WSAEnumNetworkEvents_winsock(pe[i].m_socket->m_socket, + pe[i].m_socket->m_event, &info) == SOCKET_ERROR) { + continue; + } + if ((info.lNetworkEvents & FD_READ) != 0) { + pe[i].m_revents |= kPOLLIN; + } + if ((info.lNetworkEvents & FD_ACCEPT) != 0) { + pe[i].m_revents |= kPOLLIN; + } + if ((info.lNetworkEvents & FD_WRITE) != 0) { + pe[i].m_revents |= kPOLLOUT; - // handle results - if (n == SOCKET_ERROR) { - if (getsockerror_winsock() == EINTR) { - // interrupted system call - ARCH->testCancelThread(); - continue; + // socket is now writable so don't bothing polling for + // writable until it becomes unwritable. + pe[i].m_socket->m_pollWrite = false; + } + if ((info.lNetworkEvents & FD_CONNECT) != 0) { + if (info.iErrorCode[FD_CONNECT_BIT] != 0) { + pe[i].m_revents |= kPOLLERR; + } + else { + pe[i].m_revents |= kPOLLOUT; + pe[i].m_socket->m_pollWrite = false; } - throwError(getsockerror_winsock()); } - n = 0; - for (i = 0; i < num; ++i) { - if (pe[i].m_socket != NULL) { - if (FD_ISSET(pe[i].m_socket->m_socket, &readSet)) { + if ((info.lNetworkEvents & FD_CLOSE) != 0) { + if (info.iErrorCode[FD_CLOSE_BIT] != 0) { + pe[i].m_revents |= kPOLLERR; + } + else { + if ((pe[i].m_events & kPOLLIN) != 0) { pe[i].m_revents |= kPOLLIN; } - if (FD_ISSET(pe[i].m_socket->m_socket, &writeSet)) { + if ((pe[i].m_events & kPOLLOUT) != 0) { pe[i].m_revents |= kPOLLOUT; } - if (FD_ISSET(pe[i].m_socket->m_socket, &errSet)) { - pe[i].m_revents |= kPOLLERR; - } - } - if (pe[i].m_revents != 0) { - ++n; } } - } while (false); + if (pe[i].m_revents != 0) { + ++n; + } + } - return n; + return (int)n; +} + +void +CArchNetworkWinsock::unblockPollSocket(CArchThread thread) +{ + // set the unblock event + CArchMultithreadWindows* mt = CArchMultithreadWindows::getInstance(); + WSAEVENT* unblockEvent = (WSAEVENT*)mt->getNetworkDataForThread(thread); + if (unblockEvent != NULL) { + WSASetEvent_winsock(*unblockEvent); + } } size_t @@ -454,10 +544,14 @@ CArchNetworkWinsock::readSocket(CArchSocket s, void* buf, size_t len) do { n = recv_winsock(s->m_socket, buf, len, 0); if (n == SOCKET_ERROR) { - if (getsockerror_winsock() == EINTR) { + if (getsockerror_winsock() == WSAEINTR) { // interrupted system call - ARCH->testCancelThread(); - continue; + n = 0; + break; + } + else if (getsockerror_winsock() == WSAEWOULDBLOCK) { + n = 0; + break; } throwError(getsockerror_winsock()); } @@ -475,10 +569,15 @@ CArchNetworkWinsock::writeSocket(CArchSocket s, const void* buf, size_t len) do { n = send_winsock(s->m_socket, buf, len, 0); if (n == SOCKET_ERROR) { - if (getsockerror_winsock() == EINTR) { + if (getsockerror_winsock() == WSAEINTR) { // interrupted system call - ARCH->testCancelThread(); - continue; + n = 0; + break; + } + else if (getsockerror_winsock() == WSAEWOULDBLOCK) { + s->m_pollWrite = true; + n = 0; + break; } throwError(getsockerror_winsock()); } @@ -559,26 +658,20 @@ CArchNetworkWinsock::getHostName() CArchNetAddress CArchNetworkWinsock::newAnyAddr(EAddressFamily family) { - // allocate address - CArchNetAddressImpl* addr = new CArchNetAddressImpl; - - // fill it in + CArchNetAddressImpl* addr = NULL; switch (family) { case kINET: { - struct sockaddr_in* ipAddr = - reinterpret_cast(&addr->m_addr); + addr = CArchNetAddressImpl::alloc(sizeof(struct sockaddr_in)); + struct sockaddr_in* ipAddr = TYPED_ADDR(struct sockaddr_in, addr); ipAddr->sin_family = AF_INET; ipAddr->sin_port = 0; ipAddr->sin_addr.s_addr = INADDR_ANY; - addr->m_len = sizeof(struct sockaddr_in); break; } default: - delete addr; assert(0 && "invalid family"); } - return addr; } @@ -587,26 +680,27 @@ CArchNetworkWinsock::copyAddr(CArchNetAddress addr) { assert(addr != NULL); - // allocate and copy address - return new CArchNetAddressImpl(*addr); + CArchNetAddressImpl* copy = CArchNetAddressImpl::alloc(addr->m_len); + memcpy(TYPED_ADDR(void, copy), TYPED_ADDR(void, addr), addr->m_len); + return copy; } CArchNetAddress CArchNetworkWinsock::nameToAddr(const std::string& name) { // allocate address - CArchNetAddressImpl* addr = new CArchNetAddressImpl; + CArchNetAddressImpl* addr = NULL; // try to convert assuming an IPv4 dot notation address struct sockaddr_in inaddr; memset(&inaddr, 0, sizeof(inaddr)); + inaddr.sin_family = AF_INET; + inaddr.sin_port = 0; inaddr.sin_addr.s_addr = inet_addr_winsock(name.c_str()); if (inaddr.sin_addr.s_addr != INADDR_NONE) { // it's a dot notation address - addr->m_len = sizeof(struct sockaddr_in); - inaddr.sin_family = AF_INET; - inaddr.sin_port = 0; - memcpy(&addr->m_addr, &inaddr, addr->m_len); + addr = CArchNetAddressImpl::alloc(sizeof(struct sockaddr_in)); + memcpy(TYPED_ADDR(void, addr), &inaddr, addr->m_len); } else { @@ -616,16 +710,8 @@ CArchNetworkWinsock::nameToAddr(const std::string& name) delete addr; throwNameError(getsockerror_winsock()); } - - // copy over address (only IPv4 currently supported) - if (info->h_addrtype == AF_INET) { - addr->m_len = sizeof(struct sockaddr_in); - inaddr.sin_family = info->h_addrtype; - inaddr.sin_port = 0; - memcpy(&inaddr.sin_addr, info->h_addr_list[0], - sizeof(inaddr.sin_addr)); - memcpy(&addr->m_addr, &inaddr, addr->m_len); - } + addr = CArchNetAddressImpl::alloc(info->h_length); + memcpy(TYPED_ADDR(void, addr), info->h_addr_list[0], info->h_length); } return addr; @@ -636,7 +722,7 @@ CArchNetworkWinsock::closeAddr(CArchNetAddress addr) { assert(addr != NULL); - delete addr; + free(addr); } std::string @@ -734,8 +820,8 @@ CArchNetworkWinsock::isAnyAddr(CArchNetAddress addr) case kINET: { struct sockaddr_in* ipAddr = reinterpret_cast(&addr->m_addr); - return (ipAddr->sin_addr.s_addr == INADDR_ANY && - addr->m_len == sizeof(struct sockaddr_in)); + return (addr->m_len == sizeof(struct sockaddr_in) && + ipAddr->sin_addr.s_addr == INADDR_ANY); } default: @@ -744,6 +830,13 @@ CArchNetworkWinsock::isAnyAddr(CArchNetAddress addr) } } +bool +CArchNetworkWinsock::isEqualAddr(CArchNetAddress a, CArchNetAddress b) +{ + return (a == b || (a->m_len == b->m_len && + memcmp(&a->m_addr, &b->m_addr, a->m_len) == 0)); +} + void CArchNetworkWinsock::throwError(int err) { @@ -786,8 +879,10 @@ CArchNetworkWinsock::throwError(int err) case WSAENOTCONN: throw XArchNetworkNotConnected(new XArchEvalWinsock(err)); - case WSAENETRESET: case WSAEDISCON: + throw XArchNetworkShutdown(new XArchEvalWinsock(err)); + + case WSAENETRESET: case WSAECONNABORTED: case WSAECONNRESET: throw XArchNetworkDisconnected(new XArchEvalWinsock(err)); diff --git a/lib/arch/CArchNetworkWinsock.h b/lib/arch/CArchNetworkWinsock.h index 58ce9cad..fd11dba9 100644 --- a/lib/arch/CArchNetworkWinsock.h +++ b/lib/arch/CArchNetworkWinsock.h @@ -33,16 +33,20 @@ public: SOCKET m_socket; bool m_connected; int m_refCount; + WSAEVENT m_event; + bool m_pollWrite; }; class CArchNetAddressImpl { public: - CArchNetAddressImpl() : m_len(sizeof(m_addr)) { } + static CArchNetAddressImpl* alloc(size_t); public: - struct sockaddr m_addr; int m_len; + struct sockaddr m_addr; }; +#define ADDR_HDR_SIZE offsetof(CArchNetAddressImpl, m_addr) +#define TYPED_ADDR(type_, addr_) (reinterpret_cast(&addr_->m_addr)) //! Win32 implementation of IArchNetwork class CArchNetworkWinsock : public IArchNetwork { @@ -61,6 +65,7 @@ public: virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); virtual void connectSocket(CArchSocket s, CArchNetAddress name); virtual int pollSocket(CPollEntry[], int num, double timeout); + virtual void unblockPollSocket(CArchThread thread); virtual size_t readSocket(CArchSocket s, void* buf, size_t len); virtual size_t writeSocket(CArchSocket s, const void* buf, size_t len); @@ -78,6 +83,7 @@ public: virtual void setAddrPort(CArchNetAddress, int port); virtual int getAddrPort(CArchNetAddress); virtual bool isAnyAddr(CArchNetAddress); + virtual bool isEqualAddr(CArchNetAddress, CArchNetAddress); private: void init(HMODULE); diff --git a/lib/arch/CArchTaskBarWindows.cpp b/lib/arch/CArchTaskBarWindows.cpp index 987ac6c0..fccba001 100644 --- a/lib/arch/CArchTaskBarWindows.cpp +++ b/lib/arch/CArchTaskBarWindows.cpp @@ -41,71 +41,67 @@ CArchTaskBarWindows::CArchTaskBarWindows(void* appInstance) : // save app instance s_appInstance = reinterpret_cast(appInstance); - // we need a mutex - m_mutex = ARCH->newMutex(); + // register the task bar restart message + m_taskBarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); - // and a condition variable which uses the above mutex - m_ready = false; - m_condVar = ARCH->newCondVar(); + // register a window class + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = CS_NOCLOSE; + classInfo.lpfnWndProc = &CArchTaskBarWindows::staticWndProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = sizeof(CArchTaskBarWindows*); + classInfo.hInstance = s_appInstance; + classInfo.hIcon = NULL; + classInfo.hCursor = NULL; + classInfo.hbrBackground = NULL; + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = TEXT("SynergyTaskBar"); + classInfo.hIconSm = NULL; + m_windowClass = RegisterClassEx(&classInfo); - // we're going to want to get a result from the thread we're - // about to create to know if it initialized successfully. - // so we lock the condition variable. - ARCH->lockMutex(m_mutex); - - // open a window and run an event loop in a separate thread. - // this has to happen in a separate thread because if we - // create a window on the current desktop with the current - // thread then the current thread won't be able to switch - // desktops if it needs to. - m_thread = ARCH->newThread(&CArchTaskBarWindows::threadEntry, this); - - // wait for child thread - while (!m_ready) { - ARCH->waitCondVar(m_condVar, m_mutex, -1.0); - } - - // ready - ARCH->unlockMutex(m_mutex); + // create window + m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, + reinterpret_cast(m_windowClass), + TEXT("Synergy Task Bar"), + WS_POPUP, + 0, 0, 1, 1, + NULL, + NULL, + s_appInstance, + reinterpret_cast(this)); } CArchTaskBarWindows::~CArchTaskBarWindows() { - if (m_thread != NULL) { - ARCH->cancelThread(m_thread); - ARCH->wait(m_thread, -1.0); - ARCH->closeThread(m_thread); + if (m_hwnd != NULL) { + removeAllIcons(); + DestroyWindow(m_hwnd); } - ARCH->closeCondVar(m_condVar); - ARCH->closeMutex(m_mutex); + UnregisterClass((LPCTSTR)m_windowClass, s_appInstance); + s_instance = NULL; } void CArchTaskBarWindows::addDialog(HWND hwnd) { - // add dialog to added dialogs list - ARCH->lockMutex(s_instance->m_mutex); - s_instance->m_addedDialogs.insert(std::make_pair(hwnd, true)); - ARCH->unlockMutex(s_instance->m_mutex); + CArchMiscWindows::addDialog(hwnd); } void CArchTaskBarWindows::removeDialog(HWND hwnd) { - // mark dialog as removed - ARCH->lockMutex(s_instance->m_mutex); - CDialogs::iterator index = s_instance->m_dialogs.find(hwnd); - if (index != s_instance->m_dialogs.end()) { - index->second = false; - } - s_instance->m_addedDialogs.erase(hwnd); - ARCH->unlockMutex(s_instance->m_mutex); + CArchMiscWindows::removeDialog(hwnd); } void CArchTaskBarWindows::addReceiver(IArchTaskBarReceiver* receiver) { + if (m_hwnd == NULL) { + return; + } + // ignore bogus receiver if (receiver == NULL) { return; @@ -180,53 +176,43 @@ CArchTaskBarWindows::recycleID(UINT id) void CArchTaskBarWindows::addIcon(UINT id) { - ARCH->lockMutex(m_mutex); CIDToReceiverMap::const_iterator index = m_idTable.find(id); if (index != m_idTable.end()) { modifyIconNoLock(index->second, NIM_ADD); } - ARCH->unlockMutex(m_mutex); } void CArchTaskBarWindows::removeIcon(UINT id) { - ARCH->lockMutex(m_mutex); removeIconNoLock(id); - ARCH->unlockMutex(m_mutex); } void CArchTaskBarWindows::updateIcon(UINT id) { - ARCH->lockMutex(m_mutex); CIDToReceiverMap::const_iterator index = m_idTable.find(id); if (index != m_idTable.end()) { modifyIconNoLock(index->second, NIM_MODIFY); } - ARCH->unlockMutex(m_mutex); } void CArchTaskBarWindows::addAllIcons() { - ARCH->lockMutex(m_mutex); for (CReceiverToInfoMap::const_iterator index = m_receivers.begin(); index != m_receivers.end(); ++index) { modifyIconNoLock(index, NIM_ADD); } - ARCH->unlockMutex(m_mutex); } void CArchTaskBarWindows::removeAllIcons() { - ARCH->lockMutex(m_mutex); for (CReceiverToInfoMap::const_iterator index = m_receivers.begin(); index != m_receivers.end(); ++index) { removeIconNoLock(index->second.m_id); } - ARCH->unlockMutex(m_mutex); } void @@ -319,49 +305,6 @@ CArchTaskBarWindows::handleIconMessage( } } -bool -CArchTaskBarWindows::processDialogs(MSG* msg) -{ - // only one thread can be in this method on any particular object - // at any given time. that's not a problem since only our event - // loop calls this method and there's just one of those. - - ARCH->lockMutex(m_mutex); - - // remove removed dialogs - m_dialogs.erase(false); - - // merge added dialogs into the dialog list - for (CDialogs::const_iterator index = m_addedDialogs.begin(); - index != m_addedDialogs.end(); ++index) { - m_dialogs.insert(std::make_pair(index->first, index->second)); - } - m_addedDialogs.clear(); - - ARCH->unlockMutex(m_mutex); - - // check message against all dialogs until one handles it. - // note that we don't hold a lock while checking because - // the message is processed and may make calls to this - // object. that's okay because addDialog() and - // removeDialog() don't change the map itself (just the - // values of some elements). - ARCH->lockMutex(m_mutex); - for (CDialogs::const_iterator index = m_dialogs.begin(); - index != m_dialogs.end(); ++index) { - if (index->second) { - ARCH->unlockMutex(m_mutex); - if (IsDialogMessage(index->first, msg)) { - return true; - } - ARCH->lockMutex(m_mutex); - } - } - ARCH->unlockMutex(m_mutex); - - return false; -} - LRESULT CArchTaskBarWindows::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) @@ -432,87 +375,3 @@ CArchTaskBarWindows::staticWndProc(HWND hwnd, UINT msg, return DefWindowProc(hwnd, msg, wParam, lParam); } } - -void -CArchTaskBarWindows::threadMainLoop() -{ - // register the task bar restart message - m_taskBarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); - - // register a window class - WNDCLASSEX classInfo; - classInfo.cbSize = sizeof(classInfo); - classInfo.style = CS_NOCLOSE; - classInfo.lpfnWndProc = &CArchTaskBarWindows::staticWndProc; - classInfo.cbClsExtra = 0; - classInfo.cbWndExtra = sizeof(CArchTaskBarWindows*); - classInfo.hInstance = s_appInstance; - classInfo.hIcon = NULL; - classInfo.hCursor = NULL; - classInfo.hbrBackground = NULL; - classInfo.lpszMenuName = NULL; - classInfo.lpszClassName = TEXT("SynergyTaskBar"); - classInfo.hIconSm = NULL; - ATOM windowClass = RegisterClassEx(&classInfo); - - // create window - m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, - reinterpret_cast(windowClass), - TEXT("Synergy Task Bar"), - WS_POPUP, - 0, 0, 1, 1, - NULL, - NULL, - s_appInstance, - reinterpret_cast(this)); - - // signal ready - ARCH->lockMutex(m_mutex); - m_ready = true; - ARCH->broadcastCondVar(m_condVar); - ARCH->unlockMutex(m_mutex); - - // handle failure - if (m_hwnd == NULL) { - UnregisterClass((LPCTSTR)windowClass, s_appInstance); - return; - } - - try { - // main loop - MSG msg; - for (;;) { - // wait for message - if (ARCH->waitForEvent(NULL, -1.0) != IArchMultithread::kEvent) { - continue; - } - - // peek for message and remove it. we don't GetMessage() - // because we should never block here, only in waitForEvent(). - if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - continue; - } - - // check message against dialogs - if (!processDialogs(&msg)) { - // process message - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } - } - catch (XThread&) { - // clean up - removeAllIcons(); - DestroyWindow(m_hwnd); - UnregisterClass((LPCTSTR)windowClass, s_appInstance); - throw; - } -} - -void* -CArchTaskBarWindows::threadEntry(void* self) -{ - reinterpret_cast(self)->threadMainLoop(); - return NULL; -} diff --git a/lib/arch/CArchTaskBarWindows.h b/lib/arch/CArchTaskBarWindows.h index 67e9af17..5877637c 100644 --- a/lib/arch/CArchTaskBarWindows.h +++ b/lib/arch/CArchTaskBarWindows.h @@ -18,7 +18,6 @@ #define WIN32_LEAN_AND_MEAN #include "IArchTaskBar.h" -#include "IArchMultithread.h" #include "stdmap.h" #include "stdvector.h" #include @@ -78,21 +77,13 @@ private: LRESULT wndProc(HWND, UINT, WPARAM, LPARAM); static LRESULT CALLBACK staticWndProc(HWND, UINT, WPARAM, LPARAM); - void threadMainLoop(); - static void* threadEntry(void*); private: static CArchTaskBarWindows* s_instance; static HINSTANCE s_appInstance; - // multithread data - CArchMutex m_mutex; - CArchCond m_condVar; - bool m_ready; - int m_result; - CArchThread m_thread; - // child thread data + ATOM m_windowClass; HWND m_hwnd; UINT m_taskBarRestart; diff --git a/lib/arch/IArchMultithread.h b/lib/arch/IArchMultithread.h index c06d4cfa..477f42a7 100644 --- a/lib/arch/IArchMultithread.h +++ b/lib/arch/IArchMultithread.h @@ -67,13 +67,6 @@ synergy. Each architecture must implement this interface. */ class IArchMultithread : public IInterface { public: - //! Result of waitForEvent() - enum EWaitResult { - kEvent, //!< An event is pending - kExit, //!< Thread exited - kTimeout //!< Wait timed out - }; - //! Type of thread entry point typedef void* (*ThreadFunc)(void*); //! Type of thread identifier @@ -213,30 +206,6 @@ public: */ virtual bool wait(CArchThread thread, double timeout) = 0; - //! Wait for a user event - /*! - Waits for up to \c timeout seconds for a pending user event or - \c thread to exit (normally or by cancellation). Waits forever - if \c timeout < 0. Returns kEvent if an event occurred, kExit - if \c thread exited, or kTimeout if the timeout expired. If - \c thread is NULL then it doesn't wait for any thread to exit - and it will not return kExit. - - This method is not required by all platforms. - - (Cancellation point) - */ - virtual EWaitResult waitForEvent(CArchThread thread, double timeout) = 0; - - //! Unblock thread in system call - /*! - Cause a thread that's in a blocking system call to return. This - call may return before the thread is unblocked. If the thread is - not in a blocking system call, this call has no effect. This does - not cause a lockMutex() or waitCondVar() to return prematurely. - */ - virtual void unblockThread(CArchThread thread) = 0; - //! Compare threads /*! Returns true iff two thread objects refer to the same thread. diff --git a/lib/arch/IArchNetwork.h b/lib/arch/IArchNetwork.h index 21758800..245748ec 100644 --- a/lib/arch/IArchNetwork.h +++ b/lib/arch/IArchNetwork.h @@ -18,6 +18,9 @@ #include "IInterface.h" #include "stdstring.h" +class CArchThreadImpl; +typedef CArchThreadImpl* CArchThread; + /*! \class CArchSocketImpl \brief Internal socket data. @@ -179,12 +182,21 @@ public: the \c m_revents members of the entries. \c kPOLLERR and \c kPOLLNVAL are set in \c m_revents as appropriate. If a socket indicates \c kPOLLERR then \c throwErrorOnSocket() can be used to determine - the type of error. + the type of error. Returns 0 immediately regardless of the \c timeout + if no valid sockets are selected for testing. (Cancellation point) */ virtual int pollSocket(CPollEntry[], int num, double timeout) = 0; + //! Unblock thread in pollSocket() + /*! + Cause a thread that's in a pollSocket() call to return. This + call may return before the thread is unblocked. If the thread is + not in a pollSocket() call this call has no effect. + */ + virtual void unblockPollSocket(CArchThread thread) = 0; + //! Read data from socket /*! Read up to \c len bytes from socket \c s in \c buf and return the diff --git a/lib/arch/arch.dsp b/lib/arch/arch.dsp index 126a11e3..72fcc909 100644 --- a/lib/arch/arch.dsp +++ b/lib/arch/arch.dsp @@ -119,10 +119,6 @@ SOURCE=.\CArchFileWindows.h # End Source File # Begin Source File -SOURCE=.\CArchImpl.h -# End Source File -# Begin Source File - SOURCE=.\CArchLogWindows.h # End Source File # Begin Source File diff --git a/lib/base/CEventQueue.cpp b/lib/base/CEventQueue.cpp index 879a97ce..d3ff27bc 100644 --- a/lib/base/CEventQueue.cpp +++ b/lib/base/CEventQueue.cpp @@ -169,6 +169,10 @@ retry: event = removeEvent(dataID); return true; } + + default: + assert(0 && "invalid event type"); + return false; } } @@ -498,7 +502,7 @@ CEventQueue::CTimer::reset() m_time = m_timeout; } -CEventQueue::CTimer::CTimer& +CEventQueue::CTimer& CEventQueue::CTimer::operator-=(double dt) { m_time -= dt; diff --git a/lib/base/CPriorityQueue.h b/lib/base/CPriorityQueue.h index c79c7e34..8a7fd7e7 100644 --- a/lib/base/CPriorityQueue.h +++ b/lib/base/CPriorityQueue.h @@ -26,7 +26,11 @@ it sorts by std::greater, it has a forward iterator through the elements (which can appear in any order), and its contents can be swapped. */ template , +#if WINDOWS_LIKE + class Compare = std::greater > +#else class Compare = std::greater > +#endif class CPriorityQueue { public: typedef typename Container::value_type value_type; diff --git a/lib/base/CSimpleEventQueueBuffer.cpp b/lib/base/CSimpleEventQueueBuffer.cpp index f01d2722..8f2dbd14 100644 --- a/lib/base/CSimpleEventQueueBuffer.cpp +++ b/lib/base/CSimpleEventQueueBuffer.cpp @@ -53,7 +53,7 @@ CSimpleEventQueueBuffer::waitForEvent(double timeout) } IEventQueueBuffer::Type -CSimpleEventQueueBuffer::getEvent(CEvent& event, UInt32& dataID) +CSimpleEventQueueBuffer::getEvent(CEvent&, UInt32& dataID) { CArchMutexLock lock(m_queueMutex); if (!m_queueReady) { diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index 2935e195..88ae2c9d 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -39,7 +39,8 @@ CServerProxy::CServerProxy(CClient* client, IStream* stream) : m_seqNum(0), m_compressMouse(false), m_ignoreMouse(false), - m_heartRate(0.0) + m_heartRate(0.0), + m_parser(&CServerProxy::parseHandshakeMessage) { assert(m_client != NULL); assert(m_stream != NULL); @@ -52,7 +53,7 @@ CServerProxy::CServerProxy(CClient* client, IStream* stream) : EVENTQUEUE->adoptHandler(IStream::getInputReadyEvent(), m_stream->getEventTarget(), new TMethodEventJob(this, - &CServerProxy::handleMessage)); + &CServerProxy::handleData)); // send heartbeat installHeartBeat(kHeartRate); @@ -61,6 +62,8 @@ CServerProxy::CServerProxy(CClient* client, IStream* stream) : CServerProxy::~CServerProxy() { installHeartBeat(-1.0); + EVENTQUEUE->removeHandler(IStream::getInputReadyEvent(), + m_stream->getEventTarget()); } CEvent::Type @@ -87,136 +90,193 @@ CServerProxy::installHeartBeat(double heartRate) } void -CServerProxy::handleMessage(const CEvent&, void*) +CServerProxy::handleData(const CEvent&, void*) { - while (m_stream->isReady()) { - // read next code - UInt8 code[4]; - UInt32 n = m_stream->read(code, sizeof(code)); - if (n == 0) { - break; - } + // handle messages until there are no more. first read message code. + UInt8 code[4]; + UInt32 n = m_stream->read(code, 4); + while (n != 0) { + // verify we got an entire code if (n != 4) { - // client sent an incomplete message - LOG((CLOG_ERR "incomplete message from server")); + LOG((CLOG_ERR "incomplete message from server: %d bytes", n)); m_client->disconnect("incomplete message from server"); return; } // parse message LOG((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3])); - if (memcmp(code, kMsgDMouseMove, 4) == 0) { - mouseMove(); - } - - else if (memcmp(code, kMsgDMouseWheel, 4) == 0) { - mouseWheel(); - } - - else if (memcmp(code, kMsgDKeyDown, 4) == 0) { - keyDown(); - } - - else if (memcmp(code, kMsgDKeyUp, 4) == 0) { - keyUp(); - } - - else if (memcmp(code, kMsgDMouseDown, 4) == 0) { - mouseDown(); - } - - else if (memcmp(code, kMsgDMouseUp, 4) == 0) { - mouseUp(); - } - - else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) { - keyRepeat(); - } - - else if (memcmp(code, kMsgCNoop, 4) == 0) { - // accept and discard no-op - } - - else if (memcmp(code, kMsgCEnter, 4) == 0) { - enter(); - } - - else if (memcmp(code, kMsgCLeave, 4) == 0) { - leave(); - } - - else if (memcmp(code, kMsgCClipboard, 4) == 0) { - grabClipboard(); - } - - else if (memcmp(code, kMsgCScreenSaver, 4) == 0) { - screensaver(); - } - - else if (memcmp(code, kMsgQInfo, 4) == 0) { - queryInfo(); - } - - else if (memcmp(code, kMsgCInfoAck, 4) == 0) { - infoAcknowledgment(); - } - - else if (memcmp(code, kMsgDClipboard, 4) == 0) { - setClipboard(); - } - - else if (memcmp(code, kMsgCResetOptions, 4) == 0) { - resetOptions(); - } - - else if (memcmp(code, kMsgDSetOptions, 4) == 0) { - setOptions(); - } - - else if (memcmp(code, kMsgCClose, 4) == 0) { - // server wants us to hangup - LOG((CLOG_DEBUG1 "recv close")); - m_client->disconnect(NULL); + switch ((this->*m_parser)(code)) { + case kOkay: break; - } - else if (memcmp(code, kMsgEIncompatible, 4) == 0) { - SInt32 major, minor; - CProtocolUtil::readf(m_stream, - kMsgEIncompatible + 4, &major, &minor); - LOG((CLOG_ERR "server has incompatible version %d.%d", major, minor)); - m_client->disconnect("server has incompatible version"); + case kUnknown: + LOG((CLOG_ERR "invalid message from server")); + m_client->disconnect("invalid message from server"); + return; + + case kDisconnect: return; } - else if (memcmp(code, kMsgEBusy, 4) == 0) { - LOG((CLOG_ERR "server already has a connected client with name \"%s\"", m_client->getName().c_str())); - m_client->disconnect("server already has a connected client with our name"); - return; - } - - else if (memcmp(code, kMsgEUnknown, 4) == 0) { - LOG((CLOG_ERR "server refused client with name \"%s\"", m_client->getName().c_str())); - m_client->disconnect("server refused client with our name"); - return; - } - - else if (memcmp(code, kMsgEBad, 4) == 0) { - LOG((CLOG_ERR "server disconnected due to a protocol error")); - m_client->disconnect("server reported a protocol error"); - return; - } - - else { - // unknown message - LOG((CLOG_ERR "unknown message: %d %d %d %d [%c%c%c%c]", code[0], code[1], code[2], code[3], code[0], code[1], code[2], code[3])); - m_client->disconnect("unknown message from server"); - return; - } + // next message + n = m_stream->read(code, 4); } + flushCompressedMouse(); } +CServerProxy::EResult +CServerProxy::parseHandshakeMessage(const UInt8* code) +{ + if (memcmp(code, kMsgQInfo, 4) == 0) { + queryInfo(); + } + + else if (memcmp(code, kMsgCInfoAck, 4) == 0) { + infoAcknowledgment(); + } + + else if (memcmp(code, kMsgCResetOptions, 4) == 0) { + resetOptions(); + + // handshake is complete + m_parser = &CServerProxy::parseMessage; + EVENTQUEUE->addEvent(CEvent(getHandshakeCompleteEvent(), this)); + } + + else if (memcmp(code, kMsgCNoop, 4) == 0) { + // accept and discard no-op + } + + else if (memcmp(code, kMsgCClose, 4) == 0) { + // server wants us to hangup + LOG((CLOG_DEBUG1 "recv close")); + m_client->disconnect(NULL); + return kDisconnect; + } + + else if (memcmp(code, kMsgEIncompatible, 4) == 0) { + SInt32 major, minor; + CProtocolUtil::readf(m_stream, + kMsgEIncompatible + 4, &major, &minor); + LOG((CLOG_ERR "server has incompatible version %d.%d", major, minor)); + m_client->disconnect("server has incompatible version"); + return kDisconnect; + } + + else if (memcmp(code, kMsgEBusy, 4) == 0) { + LOG((CLOG_ERR "server already has a connected client with name \"%s\"", m_client->getName().c_str())); + m_client->disconnect("server already has a connected client with our name"); + return kDisconnect; + } + + else if (memcmp(code, kMsgEUnknown, 4) == 0) { + LOG((CLOG_ERR "server refused client with name \"%s\"", m_client->getName().c_str())); + m_client->disconnect("server refused client with our name"); + return kDisconnect; + } + + else if (memcmp(code, kMsgEBad, 4) == 0) { + LOG((CLOG_ERR "server disconnected due to a protocol error")); + m_client->disconnect("server reported a protocol error"); + return kDisconnect; + } + else { + return kUnknown; + } + + return kOkay; +} + +CServerProxy::EResult +CServerProxy::parseMessage(const UInt8* code) +{ + if (memcmp(code, kMsgDMouseMove, 4) == 0) { + mouseMove(); + } + + else if (memcmp(code, kMsgDMouseWheel, 4) == 0) { + mouseWheel(); + } + + else if (memcmp(code, kMsgDKeyDown, 4) == 0) { + keyDown(); + } + + else if (memcmp(code, kMsgDKeyUp, 4) == 0) { + keyUp(); + } + + else if (memcmp(code, kMsgDMouseDown, 4) == 0) { + mouseDown(); + } + + else if (memcmp(code, kMsgDMouseUp, 4) == 0) { + mouseUp(); + } + + else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) { + keyRepeat(); + } + + else if (memcmp(code, kMsgCNoop, 4) == 0) { + // accept and discard no-op + } + + else if (memcmp(code, kMsgCEnter, 4) == 0) { + enter(); + } + + else if (memcmp(code, kMsgCLeave, 4) == 0) { + leave(); + } + + else if (memcmp(code, kMsgCClipboard, 4) == 0) { + grabClipboard(); + } + + else if (memcmp(code, kMsgCScreenSaver, 4) == 0) { + screensaver(); + } + + else if (memcmp(code, kMsgQInfo, 4) == 0) { + queryInfo(); + } + + else if (memcmp(code, kMsgCInfoAck, 4) == 0) { + infoAcknowledgment(); + } + + else if (memcmp(code, kMsgDClipboard, 4) == 0) { + setClipboard(); + } + + else if (memcmp(code, kMsgCResetOptions, 4) == 0) { + resetOptions(); + } + + else if (memcmp(code, kMsgDSetOptions, 4) == 0) { + setOptions(); + } + + else if (memcmp(code, kMsgCClose, 4) == 0) { + // server wants us to hangup + LOG((CLOG_DEBUG1 "recv close")); + m_client->disconnect(NULL); + return kDisconnect; + } + else if (memcmp(code, kMsgEBad, 4) == 0) { + LOG((CLOG_ERR "server disconnected due to a protocol error")); + m_client->disconnect("server reported a protocol error"); + return kDisconnect; + } + else { + return kUnknown; + } + + return kOkay; +} + void CServerProxy::handleHeartBeat(const CEvent&, void*) { diff --git a/lib/client/CServerProxy.h b/lib/client/CServerProxy.h index f54cbe60..3a06784e 100644 --- a/lib/client/CServerProxy.h +++ b/lib/client/CServerProxy.h @@ -42,9 +42,9 @@ public: //! @name manipulators //@{ - virtual void onInfoChanged(); - virtual bool onGrabClipboard(ClipboardID); - virtual void onClipboardChanged(ClipboardID, const IClipboard*); + void onInfoChanged(); + bool onGrabClipboard(ClipboardID); + void onClipboardChanged(ClipboardID, const IClipboard*); //@} //! @name accessors @@ -59,6 +59,11 @@ public: //@} +protected: + enum EResult { kOkay, kUnknown, kDisconnect }; + EResult parseHandshakeMessage(const UInt8* code); + EResult parseMessage(const UInt8* code); + private: // if compressing mouse motion then send the last motion now void flushCompressedMouse(); @@ -72,7 +77,7 @@ private: KeyModifierMask translateModifierMask(KeyModifierMask) const; // event handlers - void handleMessage(const CEvent&, void*); + void handleData(const CEvent&, void*); void handleHeartBeat(const CEvent&, void*); // message handlers @@ -94,6 +99,8 @@ private: void infoAcknowledgment(); private: + typedef EResult (CServerProxy::*MessageParser)(const UInt8*); + CClient* m_client; IStream* m_stream; CEventQueueTimer* m_timer; @@ -108,6 +115,8 @@ private: KeyModifierID m_modifierTranslationTable[kKeyModifierIDLast]; double m_heartRate; + MessageParser m_parser; + static CEvent::Type s_handshakeCompleteEvent; }; diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am index cfaafeb0..34aaec8f 100644 --- a/lib/common/Makefile.am +++ b/lib/common/Makefile.am @@ -19,7 +19,6 @@ EXTRA_DIST = \ common.dsp \ BasicTypes.h \ IInterface.h \ - Version.h \ common.h \ stdbitset.h \ stddeque.h \ @@ -40,5 +39,11 @@ MAINTAINERCLEANFILES = \ Makefile.in \ $(NULL) +noinst_LIBRARIES = libcommon.a +libcommon_a_SOURCES = \ + Version.cpp \ + Version.h \ + $(NULL) + INCLUDES = \ $(NULL) diff --git a/lib/common/Version.h b/lib/common/Version.h index 9f1af7ae..26049a6d 100644 --- a/lib/common/Version.h +++ b/lib/common/Version.h @@ -23,17 +23,17 @@ #endif // important strings -static const char* kApplication = "synergy"; -static const char* kCopyright = "Copyright (C) 2002 Chris Schoeneman"; -static const char* kContact = "Chris Schoeneman, crs23@bigfoot.com"; -static const char* kWebsite = "http://synergy2.sourceforge.net/"; +extern const char* kApplication; +extern const char* kCopyright; +extern const char* kContact; +extern const char* kWebsite; // build version. follows linux kernel style: an even minor number implies // a release version, odd implies development version. -static const char* kVersion = VERSION; +extern const char* kVersion; // application version -static const char* kAppVersion = "synergy " VERSION; +extern const char* kAppVersion; // exit codes static const int kExitSuccess = 0; // successful completion diff --git a/lib/common/common.dsp b/lib/common/common.dsp index 38e1a686..a95905d2 100644 --- a/lib/common/common.dsp +++ b/lib/common/common.dsp @@ -85,6 +85,10 @@ LIB32=link.exe -lib # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\Version.cpp +# End Source File # End Group # Begin Group "Header Files" diff --git a/lib/mt/CThread.cpp b/lib/mt/CThread.cpp index cd88113d..195b7b67 100644 --- a/lib/mt/CThread.cpp +++ b/lib/mt/CThread.cpp @@ -79,6 +79,12 @@ CThread::setPriority(int n) ARCH->setPriorityOfThread(m_thread, n); } +void +CThread::unblockPollSocket() +{ + ARCH->unblockPollSocket(m_thread); +} + CThread CThread::getCurrentThread() { @@ -97,24 +103,6 @@ CThread::wait(double timeout) const return ARCH->wait(m_thread, timeout); } -CThread::EWaitResult -CThread::waitForEvent(double timeout) const -{ - // IArchMultithread EWaitResults map directly to our EWaitResults - static const EWaitResult s_map[] = { - kEvent, - kExit, - kTimeout - }; - return s_map[ARCH->waitForEvent(m_thread, timeout)]; -} - -void -CThread::unblock() const -{ - ARCH->unblockThread(m_thread); -} - void* CThread::getResult() const { diff --git a/lib/mt/CThread.h b/lib/mt/CThread.h index 011f8611..896d3778 100644 --- a/lib/mt/CThread.h +++ b/lib/mt/CThread.h @@ -39,13 +39,6 @@ documentation. // note -- do not derive from this class class CThread { public: - //! Result of waitForEvent() - enum EWaitResult { - kEvent, //!< An event is pending - kExit, //!< Thread exited - kTimeout //!< Wait timed out - }; - //! Run \c adoptedJob in a new thread /*! Create and start a new thread executing the \c adoptedJob. The @@ -130,6 +123,13 @@ public: */ void setPriority(int n); + //! Force pollSocket() to return + /*! + Forces a currently blocked pollSocket() in the thread to return + immediately. + */ + void unblockPollSocket(); + //@} //! @name accessors //@{ @@ -164,31 +164,6 @@ public: */ bool wait(double timeout = -1.0) const; - //! Wait for an event (win32) - /*! - Wait for the message queue to contain a message or for the thread - to exit for up to \c timeout seconds. This returns immediately if - any message is available (including messages that were already in - the queue during the last call to \c GetMessage() or - \c PeekMessage() or waitForEvent(). Returns kEvent if a message - is available, kExit if the thread exited, and kTimeout otherwise. - This will wait forever if \c timeout < 0.0. - - This method is available under win32 only. - - (cancellation point) - */ - EWaitResult waitForEvent(double timeout = -1.0) const; - - //! Unblock thread in system call - /*! - Cause a thread that's in a blocking system call to return. This - call may return before the thread is unblocked. If the thread is - not in a blocking system call, this call has no effect. This does - not cause CMutex::lock() or CCondVar::wait() to return prematurely. - */ - void unblock() const; - //! Get the exit result /*! Returns the exit result. This does an implicit wait(). It returns diff --git a/lib/net/CSocketMultiplexer.cpp b/lib/net/CSocketMultiplexer.cpp index 2ed2dc35..1a9e11b6 100644 --- a/lib/net/CSocketMultiplexer.cpp +++ b/lib/net/CSocketMultiplexer.cpp @@ -54,6 +54,7 @@ CSocketMultiplexer::CSocketMultiplexer() : CSocketMultiplexer::~CSocketMultiplexer() { m_thread->cancel(); + m_thread->unblockPollSocket(); m_thread->wait(); delete m_thread; delete m_polling; @@ -87,7 +88,7 @@ CSocketMultiplexer::addSocket(ISocket* socket, ISocketMultiplexerJob* job) *m_pollable = false; // break thread out of poll - m_thread->unblock(); + m_thread->unblockPollSocket(); // wait for poll to finish while (*m_polling) { @@ -129,7 +130,7 @@ CSocketMultiplexer::removeSocket(ISocket* socket) *m_pollable = false; // break thread out of poll - m_thread->unblock(); + m_thread->unblockPollSocket(); // wait until thread finishes poll while (*m_polling) { @@ -160,11 +161,12 @@ CSocketMultiplexer::serviceThread(void*) // service the connections for (;;) { + CThread::testCancel(); { CLock lock(m_mutex); // wait until pollable - while (!*m_pollable) { + while (!(bool)*m_pollable) { m_pollable->wait(); } @@ -281,7 +283,6 @@ CSocketMultiplexer::CJobCursor CSocketMultiplexer::nextCursor(CJobCursor cursor) { CLock lock(m_mutex); - ISocketMultiplexerJob* job = NULL; CJobCursor j = m_socketJobs.end(); CJobCursor i = cursor; while (++i != m_socketJobs.end()) { diff --git a/lib/net/IDataSocket.cpp b/lib/net/IDataSocket.cpp index ff10f4ec..dc4b08ed 100644 --- a/lib/net/IDataSocket.cpp +++ b/lib/net/IDataSocket.cpp @@ -34,3 +34,18 @@ IDataSocket::getConnectionFailedEvent() return CEvent::registerTypeOnce(s_failedEvent, "IDataSocket::failed"); } + +void +IDataSocket::close() +{ + // this is here to work around a VC++6 bug. see the header file. + assert(0 && "bad call"); +} + +void* +IDataSocket::getEventTarget() const +{ + // this is here to work around a VC++6 bug. see the header file. + assert(0 && "bad call"); + return NULL; +} diff --git a/lib/net/IDataSocket.h b/lib/net/IDataSocket.h index bc0f2d3f..85b85480 100644 --- a/lib/net/IDataSocket.h +++ b/lib/net/IDataSocket.h @@ -65,9 +65,13 @@ public: //@} // ISocket overrides + // close() and getEventTarget() aren't pure to work around a bug + // in VC++6. it claims the methods are unused locals and warns + // that it's removing them. it's presumably tickled by inheriting + // methods with identical signatures from both superclasses. virtual void bind(const CNetworkAddress&) = 0; - virtual void close() = 0; - virtual void* getEventTarget() const = 0; + virtual void close(); + virtual void* getEventTarget() const; // IStream overrides virtual UInt32 read(void* buffer, UInt32 n) = 0; diff --git a/lib/platform/CMSWindowsDesktop.h b/lib/platform/CMSWindowsDesktop.h index 6f1d6afe..908f5c87 100644 --- a/lib/platform/CMSWindowsDesktop.h +++ b/lib/platform/CMSWindowsDesktop.h @@ -21,6 +21,12 @@ //! Encapsulate Microsoft Windows desktop class CMSWindowsDesktop { public: + //! Open the desktop + /*! + Opens the desktop named \p name. The caller must close the desktop. + */ + static HDESK openDesktop(const CString& name); + //! Open the input desktop /*! Opens the input desktop. The caller must close the desktop. diff --git a/lib/platform/CMSWindowsEventQueueBuffer.cpp b/lib/platform/CMSWindowsEventQueueBuffer.cpp index e69de29b..99366e1c 100644 --- a/lib/platform/CMSWindowsEventQueueBuffer.cpp +++ b/lib/platform/CMSWindowsEventQueueBuffer.cpp @@ -0,0 +1,129 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package 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. + */ + +#include "CMSWindowsEventQueueBuffer.h" +#include "CThread.h" +#include "IEventQueue.h" +#include "CArchMiscWindows.h" + +// +// CEventQueueTimer +// + +class CEventQueueTimer { }; + + +// +// CMSWindowsEventQueueBuffer +// + +CMSWindowsEventQueueBuffer::CMSWindowsEventQueueBuffer() +{ + // remember thread. we'll be posting messages to it. + m_thread = GetCurrentThreadId(); + + // create a message type for custom events + m_userEvent = RegisterWindowMessage("SYNERGY_USER_EVENT"); + + // get message type for daemon quit + m_daemonQuit = CArchMiscWindows::getDaemonQuitMessage(); + + // make sure this thread has a message queue + MSG dummy; + PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE); +} + +CMSWindowsEventQueueBuffer::~CMSWindowsEventQueueBuffer() +{ + // do nothing +} + +void +CMSWindowsEventQueueBuffer::waitForEvent(double timeout) +{ + // check if messages are available first. if we don't do this then + // MsgWaitForMultipleObjects() will block even if the queue isn't + // empty if the messages in the queue were there before the last + // call to GetMessage()/PeekMessage(). + if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) { + return; + } + + // convert timeout + DWORD t; + if (timeout < 0.0) { + t = INFINITE; + } + else { + t = (DWORD)(1000.0 * timeout); + } + + // wait for a message. we cannot be interrupted by thread + // cancellation but that's okay because we're run in the main + // thread and we never cancel that thread. + HANDLE dummy[1]; + MsgWaitForMultipleObjects(0, dummy, FALSE, t, QS_ALLINPUT); +} + +IEventQueueBuffer::Type +CMSWindowsEventQueueBuffer::getEvent(CEvent& event, UInt32& dataID) +{ + // BOOL. yeah, right. + BOOL result = GetMessage(&m_event, NULL, 0, 0); + if (result == -1) { + return kNone; + } + else if (result == 0) { + event = CEvent(CEvent::kQuit); + return kSystem; + } + else if (m_event.message == m_daemonQuit) { + event = CEvent(CEvent::kQuit); + return kSystem; + } + else if (m_event.message == m_userEvent) { + dataID = static_cast(m_event.wParam); + return kUser; + } + else { + event = CEvent(CEvent::kSystem, + IEventQueue::getSystemTarget(), &m_event); + return kSystem; + } +} + +bool +CMSWindowsEventQueueBuffer::addEvent(UInt32 dataID) +{ + return (PostThreadMessage(m_thread, m_userEvent, + static_cast(dataID), 0) != 0); +} + +bool +CMSWindowsEventQueueBuffer::isEmpty() const +{ + return (HIWORD(GetQueueStatus(QS_ALLINPUT)) == 0); +} + +CEventQueueTimer* +CMSWindowsEventQueueBuffer::newTimer(double, bool) const +{ + return new CEventQueueTimer; +} + +void +CMSWindowsEventQueueBuffer::deleteTimer(CEventQueueTimer* timer) const +{ + delete timer; +} diff --git a/lib/platform/CMSWindowsEventQueueBuffer.h b/lib/platform/CMSWindowsEventQueueBuffer.h index e69de29b..28d8a2f6 100644 --- a/lib/platform/CMSWindowsEventQueueBuffer.h +++ b/lib/platform/CMSWindowsEventQueueBuffer.h @@ -0,0 +1,44 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2004 Chris Schoeneman + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package 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. + */ + +#ifndef CMSWINDOWSEVENTQUEUEBUFFER_H +#define CMSWINDOWSEVENTQUEUEBUFFER_H + +#include "IEventQueueBuffer.h" +#define WIN32_LEAN_AND_MEAN +#include + +//! Event queue buffer for Win32 +class CMSWindowsEventQueueBuffer : public IEventQueueBuffer { +public: + CMSWindowsEventQueueBuffer(); + virtual ~CMSWindowsEventQueueBuffer(); + + // IEventQueueBuffer overrides + virtual void waitForEvent(double timeout); + virtual Type getEvent(CEvent& event, UInt32& dataID); + virtual bool addEvent(UInt32 dataID); + virtual bool isEmpty() const; + virtual CEventQueueTimer* + newTimer(double duration, bool oneShot) const; + virtual void deleteTimer(CEventQueueTimer*) const; + +private: + DWORD m_thread; + UINT m_userEvent; + MSG m_event; + UINT m_daemonQuit; +}; + +#endif diff --git a/lib/platform/CMSWindowsKeyMapper.cpp b/lib/platform/CMSWindowsKeyMapper.cpp index dbd981e2..de2435a2 100644 --- a/lib/platform/CMSWindowsKeyMapper.cpp +++ b/lib/platform/CMSWindowsKeyMapper.cpp @@ -741,7 +741,7 @@ CMSWindowsKeyMapper::update(IKeyState* keyState) for (size_t j = 0; j < CModifierKeys::s_maxKeys; ++j) { if (s_modifiers[i].m_keys[j] != 0) { SHORT s = GetKeyState(s_modifiers[i].m_keys[j]); - m_keys[s_modifiers[i].m_keys[j]] = static_cast(s); + m_keys[s_modifiers[i].m_keys[j] & 0xffu] = static_cast(s); if (keyState != NULL) { if ((s & 0x01) != 0) { keyState->setToggled(s_modifiers[i].m_mask); diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index f9e1a334..21c06a23 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -15,17 +15,18 @@ #include "CMSWindowsScreen.h" #include "CMSWindowsClipboard.h" #include "CMSWindowsDesktop.h" +#include "CMSWindowsEventQueueBuffer.h" #include "CMSWindowsScreenSaver.h" #include "CClipboard.h" -#include "IScreenReceiver.h" -#include "IPrimaryScreenReceiver.h" #include "XScreen.h" -#include "CThread.h" #include "CLock.h" +#include "CThread.h" #include "CFunctionJob.h" #include "CLog.h" #include "CString.h" #include "CStringUtil.h" +#include "IEventQueue.h" +#include "TMethodEventJob.h" #include "TMethodJob.h" #include "CArch.h" #include "CArchMiscWindows.h" @@ -33,6 +34,25 @@ #include #include +// ; +#define SYNERGY_MSG_SWITCH SYNERGY_HOOK_LAST_MSG + 1 +// ; +#define SYNERGY_MSG_ENTER SYNERGY_HOOK_LAST_MSG + 2 +// ; +#define SYNERGY_MSG_LEAVE SYNERGY_HOOK_LAST_MSG + 3 +// wParam = flags, HIBYTE(lParam) = virtual key, LOBYTE(lParam) = scan code +#define SYNERGY_MSG_FAKE_KEY SYNERGY_HOOK_LAST_MSG + 4 + // flags, XBUTTON id +#define SYNERGY_MSG_FAKE_BUTTON SYNERGY_HOOK_LAST_MSG + 5 +// x; y +#define SYNERGY_MSG_FAKE_MOVE SYNERGY_HOOK_LAST_MSG + 6 +// delta; +#define SYNERGY_MSG_FAKE_WHEEL SYNERGY_HOOK_LAST_MSG + 7 +// POINT*; +#define SYNERGY_MSG_CURSOR_POS SYNERGY_HOOK_LAST_MSG + 8 +// IKeyState*; +#define SYNERGY_MSG_SYNC_KEYS SYNERGY_HOOK_LAST_MSG + 9 + // // add backwards compatible multihead support (and suppress bogus warning) // @@ -91,15 +111,11 @@ // HINSTANCE CMSWindowsScreen::s_instance = NULL; -CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL; +CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL; -CMSWindowsScreen::CMSWindowsScreen( - IScreenReceiver* receiver, - IPrimaryScreenReceiver* primaryReceiver) : - m_isPrimary(primaryReceiver != NULL), +CMSWindowsScreen::CMSWindowsScreen(bool isPrimary) : + m_isPrimary(isPrimary), m_is95Family(CArchMiscWindows::isWindows95Family()), - m_receiver(receiver), - m_primaryReceiver(primaryReceiver), m_isOnScreen(m_isPrimary), m_class(0), m_cursor(NULL), @@ -109,44 +125,87 @@ CMSWindowsScreen::CMSWindowsScreen( m_xCenter(0), m_yCenter(0), m_multimon(false), m_xCursor(0), m_yCursor(0), + m_sequenceNumber(0), m_mark(0), m_markReceived(0), - m_threadID(0), - m_lastThreadID(0), - m_timer(0), - m_oneShotTimer(0), + m_timer(NULL), m_screensaver(NULL), m_screensaverNotify(false), m_nextClipboardWindow(NULL), m_ownClipboard(false), - m_desk(NULL), - m_deskName(), - m_inaccessibleDesktop(false), + m_activeDesk(NULL), + m_activeDeskName(), m_hookLibrary(NULL), - m_lowLevel(false), - m_keyState(NULL) + m_keyState(NULL), + m_mutex(), + m_deskReady(&m_mutex, false) { + assert(s_instance != NULL); assert(s_screen == NULL); - assert(m_receiver != NULL); s_screen = this; + try { + m_hookLibrary = openHookLibrary("synrgyhk"); + m_cursor = createBlankCursor(); + m_class = createWindowClass(); + m_deskClass = createDeskWindowClass(m_isPrimary); + updateScreenShape(); + m_window = createWindow(m_class, "Synergy"); + m_screensaver = new CMSWindowsScreenSaver(); + LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_multimon ? "(multi-monitor)" : "")); + LOG((CLOG_DEBUG "window is 0x%08x", m_window)); + } + catch (...) { + delete m_screensaver; + destroyWindow(m_window); + destroyClass(m_deskClass); + destroyClass(m_class); + destroyCursor(m_cursor); + closeHookLibrary(m_hookLibrary); + m_screensaver = NULL; + m_class = 0; + m_cursor = NULL; + m_hookLibrary = NULL; + s_screen = NULL; + throw; + } - // make sure this thread has a message queue - MSG dummy; - PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE); + // install our clipboard snooper + m_nextClipboardWindow = SetClipboardViewer(m_window); + + // install event handlers + EVENTQUEUE->adoptHandler(CEvent::kSystem, IEventQueue::getSystemTarget(), + new TMethodEventJob(this, + &CMSWindowsScreen::handleSystemEvent)); + + // install the platform event queue + EVENTQUEUE->adoptBuffer(new CMSWindowsEventQueueBuffer); } CMSWindowsScreen::~CMSWindowsScreen() { assert(s_screen != NULL); - assert(m_class == 0); + disable(); + EVENTQUEUE->adoptBuffer(NULL); + EVENTQUEUE->removeHandler(CEvent::kSystem, IEventQueue::getSystemTarget()); + removeDesks(); + ChangeClipboardChain(m_window, m_nextClipboardWindow); + delete m_screensaver; + destroyWindow(m_window); + destroyClass(m_deskClass); + destroyClass(m_class); + destroyCursor(m_cursor); + closeHookLibrary(m_hookLibrary); s_screen = NULL; } void CMSWindowsScreen::init(HINSTANCE instance) { + assert(s_instance == NULL); + assert(instance != NULL); + s_instance = instance; } @@ -157,129 +216,11 @@ CMSWindowsScreen::getInstance() } void -CMSWindowsScreen::open(IKeyState* keyState) +CMSWindowsScreen::setKeyState(IKeyState* keyState) { - assert(s_instance != NULL); - assert(m_class == 0); - assert(m_hookLibrary == NULL); - - try { - // load the hook library - m_hookLibrary = LoadLibrary("synrgyhk"); - if (m_hookLibrary == NULL) { - LOG((CLOG_ERR "Failed to load hook library; synrgyhk.dll is missing")); - throw XScreenOpenFailure(); - } - m_setSides = (SetSidesFunc)GetProcAddress(m_hookLibrary, "setSides"); - m_setZone = (SetZoneFunc)GetProcAddress(m_hookLibrary, "setZone"); - m_setMode = (SetModeFunc)GetProcAddress(m_hookLibrary, "setMode"); - m_install = (InstallFunc)GetProcAddress(m_hookLibrary, "install"); - m_uninstall = (UninstallFunc)GetProcAddress(m_hookLibrary, "uninstall"); - m_init = (InitFunc)GetProcAddress(m_hookLibrary, "init"); - m_cleanup = (CleanupFunc)GetProcAddress(m_hookLibrary, "cleanup"); - m_installScreensaver = - (InstallScreenSaverFunc)GetProcAddress( - m_hookLibrary, "installScreenSaver"); - m_uninstallScreensaver = - (UninstallScreenSaverFunc)GetProcAddress( - m_hookLibrary, "uninstallScreenSaver"); - if (m_setSides == NULL || - m_setZone == NULL || - m_setMode == NULL || - m_install == NULL || - m_uninstall == NULL || - m_init == NULL || - m_cleanup == NULL || - m_installScreensaver == NULL || - m_uninstallScreensaver == NULL) { - LOG((CLOG_ERR "Invalid hook library; use a newer synrgyhk.dll")); - throw XScreenOpenFailure(); - } - - // save thread id. this is mainly to ensure that mainLoop() - // is called by the same thread that called open(). these - // threads must be the same to get the right message queue. - m_threadID = GetCurrentThreadId(); - - // initialize hook library - if (m_isPrimary && m_init(m_threadID) == 0) { - LOG((CLOG_ERR "Cannot initialize hook library; is synergy already running?")); - throw XScreenOpenFailure(); - } - - // create the transparent cursor - m_cursor = createBlankCursor(); - - // register a window class - WNDCLASSEX classInfo; - classInfo.cbSize = sizeof(classInfo); - classInfo.style = CS_DBLCLKS | CS_NOCLOSE; - classInfo.lpfnWndProc = &CMSWindowsScreen::wndProc; - classInfo.cbClsExtra = 0; - classInfo.cbWndExtra = 0; - classInfo.hInstance = s_instance; - classInfo.hIcon = NULL; - classInfo.hCursor = m_cursor; - classInfo.hbrBackground = NULL; - classInfo.lpszMenuName = NULL; - classInfo.lpszClassName = "Synergy"; - classInfo.hIconSm = NULL; - m_class = RegisterClassEx(&classInfo); - - // get screen shape - updateScreenShape(); - - // initialize the screen saver - m_screensaver = new CMSWindowsScreenSaver(); - - // initialize marks - m_mark = 0; - m_markReceived = 0; - } - catch (...) { - close(); - throw; - } - - // save the IKeyState m_keyState = keyState; -} - -void -CMSWindowsScreen::close() -{ - assert(s_instance != NULL); - - // done with m_keyState - m_keyState = NULL; - - // done with screen saver - delete m_screensaver; - - // unregister the window class - if (m_class != 0) { - UnregisterClass((LPCTSTR)m_class, s_instance); - } - - // done with cursor - if (m_cursor != NULL) { - DestroyCursor(m_cursor); - } - - // done with hook library - if (m_hookLibrary != NULL) { - if (m_isPrimary) { - m_cleanup(); - } - FreeLibrary(m_hookLibrary); - } - - // reset state - m_screensaver = NULL; - m_cursor = NULL; - m_class = 0; - m_hookLibrary = NULL; - m_threadID = 0; + m_keyMapper.update(m_keyState); + memset(m_buttons, 0, sizeof(m_buttons)); } void @@ -298,92 +239,40 @@ CMSWindowsScreen::enable() m_setMode(kHOOK_WATCH_JUMP_ZONE); } - // create the window - if (!switchDesktop(CMSWindowsDesktop::openInputDesktop())) { - throw XScreenOpenFailure(); - } + // set the active desk and (re)install the hooks + checkDesk(); - // poll input desktop to see if it changes. windows doesn't - // inform us when the desktop has changed but we need to - // open a new window when that happens so we poll. this is - // also used for polling other stuff. - m_timer = SetTimer(NULL, 0, 200, NULL); + // install the desk timer. this timer periodically checks + // which desk is active and reinstalls the hooks as necessary. + // we wouldn't need this if windows notified us of a desktop + // change but as far as i can tell it doesn't. + m_timer = EVENTQUEUE->newTimer(0.2, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, m_timer, + new TMethodEventJob( + this, &CMSWindowsScreen::handleCheckDesk)); } void CMSWindowsScreen::disable() { - // remove timers - if (m_timer != 0) { - KillTimer(NULL, m_timer); - } - if (m_oneShotTimer != 0) { - KillTimer(NULL, m_oneShotTimer); + // remove timer + if (m_timer != NULL) { + EVENTQUEUE->removeHandler(CEvent::kTimer, m_timer); + EVENTQUEUE->deleteTimer(m_timer); + m_timer = NULL; } - // reset state - m_timer = 0; - m_oneShotTimer = 0; - - // done with window - switchDesktop(NULL); - assert(m_window == NULL); - assert(m_desk == NULL); -} - -void -CMSWindowsScreen::mainLoop() -{ - // must call mainLoop() from same thread as openDesktop() - assert(m_threadID == GetCurrentThreadId()); - - // event loop - MSG msg; - for (;;) { - // wait for an event in a cancellable way - if (CThread::getCurrentThread().waitForEvent(-1.0) != CThread::kEvent) { - continue; - } - if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - continue; - } - - // handle quit message - if (msg.message == WM_QUIT) { - if (msg.wParam == 0) { - // force termination - CThread::getCurrentThread().cancel(); - } - else { - // just exit the main loop - break; - } - } - - // dispatch message - if (!onPreDispatch(msg.hwnd, msg.message, msg.wParam, msg.lParam)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } + // disable hooks + if (m_isPrimary) { + m_setMode(kHOOK_DISABLE); } } -void -CMSWindowsScreen::exitMainLoop() -{ - // close down cleanly - PostThreadMessage(m_threadID, WM_QUIT, 1, 0); -} - void CMSWindowsScreen::enter() { + sendDeskMessage(SYNERGY_MSG_ENTER, 0, 0); if (m_isPrimary) { - // show the cursor - showCursor(true); - ShowWindow(m_window, SW_HIDE); - m_cursorThread = 0; - // enable special key sequences on win95 family enableSpecialKeys(true); @@ -393,10 +282,6 @@ CMSWindowsScreen::enter() // all messages prior to now are invalid nextMark(); } - else { - // show the cursor - ShowWindow(m_window, SW_HIDE); - } // now on screen m_isOnScreen = true; @@ -405,17 +290,9 @@ CMSWindowsScreen::enter() bool CMSWindowsScreen::leave() { - if (m_isPrimary) { - // we don't need a window to capture input but we need a window - // to hide the cursor when using low-level hooks. also take the - // activation so we use our keyboard layout, not the layout of - // whatever window was active. - if (m_lowLevel) { - SetWindowPos(m_window, HWND_TOPMOST, m_xCenter, m_yCenter, 1, 1, - SWP_NOACTIVATE); - ShowWindow(m_window, SW_SHOW); - } + sendDeskMessage(SYNERGY_MSG_LEAVE, 0, 0); + if (m_isPrimary) { /* XXX // update keys m_keyMapper.update(NULL); @@ -430,25 +307,8 @@ CMSWindowsScreen::leave() // all messages prior to now are invalid nextMark(); - // watch jump zones + // capture events m_setMode(kHOOK_RELAY_EVENTS); - - // hide the cursor if using low level hooks - if (m_lowLevel) { - HWND hwnd = GetForegroundWindow(); - m_cursorThread = GetWindowThreadProcessId(hwnd, NULL); - showCursor(false); - } - } - else { - // move hider window under the cursor center - MoveWindow(m_window, m_xCenter, m_yCenter, 1, 1, FALSE); - - // raise and show the hider window - ShowWindow(m_window, SW_SHOWNA); - - // warp the mouse to the cursor center - fakeMouseMove(m_xCenter, m_yCenter); } // now off screen @@ -493,8 +353,8 @@ CMSWindowsScreen::checkClipboards() if (m_ownClipboard && !CMSWindowsClipboard::isOwnedBySynergy()) { LOG((CLOG_DEBUG "clipboard changed: lost ownership and no notification received")); m_ownClipboard = false; - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); + sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardClipboard); + sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardSelection); } } @@ -554,17 +414,28 @@ CMSWindowsScreen::setOptions(const COptionsList&) void CMSWindowsScreen::updateKeys() { - syncDesktop(); - m_keyMapper.update(m_keyState); + sendDeskMessage(SYNERGY_MSG_SYNC_KEYS, 0, 0); memset(m_buttons, 0, sizeof(m_buttons)); } +void +CMSWindowsScreen::setSequenceNumber(UInt32 seqNum) +{ + m_sequenceNumber = seqNum; +} + bool CMSWindowsScreen::isPrimary() const { return m_isPrimary; } +void* +CMSWindowsScreen::getEventTarget() const +{ + return const_cast(this); +} + bool CMSWindowsScreen::getClipboard(ClipboardID, IClipboard* dst) const { @@ -588,15 +459,10 @@ void CMSWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const { POINT pos; - syncDesktop(); - if (GetCursorPos(&pos)) { - x = pos.x; - y = pos.y; - } - else { - x = m_xCenter; - y = m_yCenter; - } + sendDeskMessage(SYNERGY_MSG_CURSOR_POS, + reinterpret_cast(&pos), 0); + x = pos.x; + y = pos.y; } void @@ -604,6 +470,7 @@ CMSWindowsScreen::reconfigure(UInt32 activeSides) { assert(m_isPrimary); + LOG((CLOG_DEBUG "active sides: %x", activeSides)); m_setSides(activeSides); } @@ -625,18 +492,6 @@ CMSWindowsScreen::warpCursor(SInt32 x, SInt32 y) m_yCursor = y; } -UInt32 -CMSWindowsScreen::addOneShotTimer(double timeout) -{ - // FIXME -- support multiple one-shot timers - if (m_oneShotTimer != 0) { - KillTimer(NULL, m_oneShotTimer); - } - m_oneShotTimer = SetTimer(NULL, 0, - static_cast(1000.0 * timeout), NULL); - return 0; -} - SInt32 CMSWindowsScreen::getJumpZoneSize() const { @@ -665,6 +520,13 @@ CMSWindowsScreen::isAnyMouseButtonDown() const return false; } +void +CMSWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const +{ + x = m_xCenter; + y = m_yCenter; +} + const char* CMSWindowsScreen::getKeyName(KeyButton virtualKey) const { @@ -682,9 +544,9 @@ CMSWindowsScreen::fakeKeyEvent(KeyButton virtualKey, bool press) const flags |= KEYEVENTF_KEYUP; } const UINT code = m_keyMapper.keyToScanCode(&virtualKey); - syncDesktop(); - keybd_event(static_cast(virtualKey & 0xffu), - static_cast(code), flags, 0); + sendDeskMessage(SYNERGY_MSG_FAKE_KEY, flags, + MAKEWORD(static_cast(code), + static_cast(virtualKey & 0xffu))); } bool @@ -692,8 +554,8 @@ CMSWindowsScreen::fakeCtrlAltDel() const { if (!m_is95Family) { // to fake ctrl+alt+del on the NT family we broadcast a suitable - // hotkey to all windows on the winlogon desktop. however, we - // the current thread must be on that desktop to do the broadcast + // hotkey to all windows on the winlogon desktop. however, the + // current thread must be on that desktop to do the broadcast // and we can't switch just any thread because some own windows // or hooks. so start a new thread to do the real work. CThread cad(new CFunctionJob(&CMSWindowsScreen::ctrlAltDelThread)); @@ -720,102 +582,23 @@ CMSWindowsScreen::fakeCtrlAltDel() const void CMSWindowsScreen::fakeMouseButton(ButtonID id, bool press) const { - // map button id to button flag DWORD data; DWORD flags = mapButtonToEvent(id, press, &data); - - // send event - if (flags != 0) { - syncDesktop(); - mouse_event(flags, 0, 0, data, 0); - } + sendDeskMessage(SYNERGY_MSG_FAKE_BUTTON, flags, data); } void CMSWindowsScreen::fakeMouseMove(SInt32 x, SInt32 y) const { - // motion is simple (i.e. it's on the primary monitor) if there - // is only one monitor. - bool simple = !m_multimon; - if (!simple) { - // also simple if motion is within the primary monitor - simple = (x >= 0 && x < GetSystemMetrics(SM_CXSCREEN) && - y >= 0 && y < GetSystemMetrics(SM_CYSCREEN)); - } - - // move the mouse directly to target position if motion is simple - syncDesktop(); - if (simple) { - // when using absolute positioning with mouse_event(), - // the normalized device coordinates range over only - // the primary screen. - SInt32 w = GetSystemMetrics(SM_CXSCREEN); - SInt32 h = GetSystemMetrics(SM_CYSCREEN); - mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, - (DWORD)((65536.0 * x) / w), - (DWORD)((65536.0 * y) / h), - 0, 0); - } - - // windows 98 (and Me?) is broken. you cannot set the absolute - // position of the mouse except on the primary monitor but you - // can do relative moves onto any monitor. this is, in microsoft's - // words, "by design." apparently the designers of windows 2000 - // we're a little less lazy and did it right. - // - // microsoft recommends in Q193003 to absolute position the cursor - // somewhere on the primary monitor then relative move to the - // desired location. this doesn't work for us because when the - // user drags a scrollbar, a window, etc. it causes the dragged - // item to jump back a forth between the position on the primary - // monitor and the desired position. while it always ends up in - // the right place, the effect is disconcerting. - // - // instead we'll get the cursor's current position and do just a - // relative move from there to the desired position. relative - // moves are subject to cursor acceleration which we don't want. - // so we disable acceleration, do the relative move, then restore - // acceleration. there's a slight chance we'll end up in the - // wrong place if the user moves the cursor using this system's - // mouse while simultaneously moving the mouse on the server - // system. that defeats the purpose of synergy so we'll assume - // that won't happen. even if it does, the next mouse move will - // correct the position. - else { - // save mouse speed & acceleration - int oldSpeed[4]; - bool accelChanged = - SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed, 0) && - SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0); - - // use 1:1 motion - if (accelChanged) { - int newSpeed[4] = { 0, 0, 0, 1 }; - accelChanged = - SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) || - SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0); - } - - // get current mouse position - POINT pos; - GetCursorPos(&pos); - - // move relative to mouse position - mouse_event(MOUSEEVENTF_MOVE, x - pos.x, y - pos.y, 0, 0); - - // restore mouse speed & acceleration - if (accelChanged) { - SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0); - SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0); - } - } + sendDeskMessage(SYNERGY_MSG_FAKE_MOVE, + static_cast(x), + static_cast(y)); } void CMSWindowsScreen::fakeMouseWheel(SInt32 delta) const { - syncDesktop(); - mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0); + sendDeskMessage(SYNERGY_MSG_FAKE_WHEEL, delta, 0); } KeyButton @@ -827,171 +610,190 @@ CMSWindowsScreen::mapKey(IKeyState::Keystrokes& keys, return m_keyMapper.mapKey(keys, keyState, id, desiredMask, isAutoRepeat); } -void -CMSWindowsScreen::updateScreenShape() +HINSTANCE +CMSWindowsScreen::openHookLibrary(const char* name) { - // get shape - m_x = GetSystemMetrics(SM_XVIRTUALSCREEN); - m_y = GetSystemMetrics(SM_YVIRTUALSCREEN); - m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN); - m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN); - LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); + // load the hook library + HINSTANCE hookLibrary = LoadLibrary(name); + if (hookLibrary == NULL) { + LOG((CLOG_ERR "Failed to load hook library; %s.dll is missing", name)); + throw XScreenOpenFailure(); + } - // get center for cursor - m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1; - m_yCenter = GetSystemMetrics(SM_CYSCREEN) >> 1; + // look up functions + m_setSides = (SetSidesFunc)GetProcAddress(hookLibrary, "setSides"); + m_setZone = (SetZoneFunc)GetProcAddress(hookLibrary, "setZone"); + m_setMode = (SetModeFunc)GetProcAddress(hookLibrary, "setMode"); + m_install = (InstallFunc)GetProcAddress(hookLibrary, "install"); + m_uninstall = (UninstallFunc)GetProcAddress(hookLibrary, "uninstall"); + m_init = (InitFunc)GetProcAddress(hookLibrary, "init"); + m_cleanup = (CleanupFunc)GetProcAddress(hookLibrary, "cleanup"); + m_installScreensaver = + (InstallScreenSaverFunc)GetProcAddress( + hookLibrary, "installScreenSaver"); + m_uninstallScreensaver = + (UninstallScreenSaverFunc)GetProcAddress( + hookLibrary, "uninstallScreenSaver"); + if (m_setSides == NULL || + m_setZone == NULL || + m_setMode == NULL || + m_install == NULL || + m_uninstall == NULL || + m_init == NULL || + m_cleanup == NULL || + m_installScreensaver == NULL || + m_uninstallScreensaver == NULL) { + LOG((CLOG_ERR "Invalid hook library; use a newer %s.dll", name)); + throw XScreenOpenFailure(); + } - // check for multiple monitors - m_multimon = (m_w != GetSystemMetrics(SM_CXSCREEN) || - m_h != GetSystemMetrics(SM_CYSCREEN)); + // initialize hook library + if (m_init(GetCurrentThreadId()) == 0) { + LOG((CLOG_ERR "Cannot initialize hook library; is synergy already running?")); + throw XScreenOpenFailure(); + } + + return hookLibrary; } -bool -CMSWindowsScreen::switchDesktop(HDESK desk) +void +CMSWindowsScreen::closeHookLibrary(HINSTANCE hookLibrary) const { - // assume we don't own the clipboard until later - m_ownClipboard = false; - - // destroy old window - if (m_window != NULL) { - // first remove clipboard snooper - ChangeClipboardChain(m_window, m_nextClipboardWindow); - m_nextClipboardWindow = NULL; - - // uninstall hooks. we can't change the thread desktop - // with hooks installed. - if (m_isPrimary) { - m_uninstall(); - } - - // now destroy window. we can't change the thread desktop - // with a window. - DestroyWindow(m_window); - m_window = NULL; - - // done with desk - CMSWindowsDesktop::closeDesktop(m_desk); - m_desk = NULL; - m_deskName = ""; + if (hookLibrary != NULL) { + m_cleanup(); + FreeLibrary(hookLibrary); } +} - // if no new desktop then we're done - if (desk == NULL) { - LOG((CLOG_DEBUG "disconnecting desktop")); - return true; +HCURSOR +CMSWindowsScreen::createBlankCursor() const +{ + // create a transparent cursor + int cw = GetSystemMetrics(SM_CXCURSOR); + int ch = GetSystemMetrics(SM_CYCURSOR); + UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)]; + UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)]; + memset(cursorAND, 0xff, ch * ((cw + 31) >> 2)); + memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2)); + HCURSOR c = CreateCursor(s_instance, 0, 0, cw, ch, cursorAND, cursorXOR); + delete[] cursorXOR; + delete[] cursorAND; + return c; +} + +void +CMSWindowsScreen::destroyCursor(HCURSOR cursor) const +{ + if (cursor != NULL) { + DestroyCursor(cursor); } +} - // uninstall screen saver hooks - if (m_screensaverNotify) { - m_uninstallScreensaver(); +ATOM +CMSWindowsScreen::createWindowClass() const +{ + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = CS_DBLCLKS | CS_NOCLOSE; + classInfo.lpfnWndProc = &CMSWindowsScreen::wndProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = 0; + classInfo.hInstance = s_instance; + classInfo.hIcon = NULL; + classInfo.hCursor = NULL; + classInfo.hbrBackground = NULL; + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = "Synergy"; + classInfo.hIconSm = NULL; + return RegisterClassEx(&classInfo); +} + +ATOM +CMSWindowsScreen::createDeskWindowClass(bool isPrimary) const +{ + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = CS_DBLCLKS | CS_NOCLOSE; + classInfo.lpfnWndProc = isPrimary ? + &CMSWindowsScreen::primaryDeskProc : + &CMSWindowsScreen::secondaryDeskProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = 0; + classInfo.hInstance = s_instance; + classInfo.hIcon = NULL; + classInfo.hCursor = m_cursor; + classInfo.hbrBackground = NULL; + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = "SynergyDesk"; + classInfo.hIconSm = NULL; + return RegisterClassEx(&classInfo); +} + +void +CMSWindowsScreen::destroyClass(ATOM windowClass) const +{ + if (windowClass != 0) { + UnregisterClass((LPCTSTR)windowClass, s_instance); } +} - // set the desktop. can only do this when there are no windows - // and hooks on the current desktop owned by this thread. - if (!CMSWindowsDesktop::setDesktop(desk)) { - LOG((CLOG_ERR "failed to set desktop: %d", GetLastError())); - CMSWindowsDesktop::closeDesktop(desk); - return false; - } - - // create the window - m_window = CreateWindowEx(WS_EX_TOPMOST | +HWND +CMSWindowsScreen::createWindow(ATOM windowClass, const char* name) const +{ + HWND window = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, - (LPCTSTR)m_class, - "Synergy", + (LPCTSTR)windowClass, + name, WS_POPUP, 0, 0, 1, 1, NULL, NULL, - getInstance(), + s_instance, NULL); - if (m_window == NULL) { + if (window == NULL) { LOG((CLOG_ERR "failed to create window: %d", GetLastError())); - CMSWindowsDesktop::closeDesktop(desk); - return false; + throw XScreenOpenFailure(); } - - // reinstall screen saver hooks - if (m_screensaverNotify) { - m_installScreensaver(); - } - - if (m_isPrimary) { - // we don't ever want our window to activate - EnableWindow(m_window, FALSE); - - // install hooks - switch (m_install()) { - case kHOOK_FAILED: - // FIXME -- can't install hook so we won't work; report error - m_lowLevel = false; - break; - - case kHOOK_OKAY: - m_lowLevel = false; - break; - - case kHOOK_OKAY_LL: - m_lowLevel = true; - break; - } - - if (m_isOnScreen) { - // all messages prior to now are invalid - // FIXME -- is this necessary; couldn't we lose key releases? - nextMark(); - } - } - else { - // update key state - updateKeys(); - - // hide cursor if this screen isn't active - if (!m_isOnScreen) { - // move hider window under the cursor center - MoveWindow(m_window, m_xCenter, m_yCenter, 1, 1, FALSE); - - // raise and show the hider window - ShowWindow(m_window, SW_SHOWNA); - - // warp the mouse to the cursor center - fakeMouseMove(m_xCenter, m_yCenter); - } - } - - // install our clipboard snooper - m_nextClipboardWindow = SetClipboardViewer(m_window); - - // check if we own the clipboard - m_ownClipboard = CMSWindowsClipboard::isOwnedBySynergy(); - - // save new desktop - m_desk = desk; - m_deskName = CMSWindowsDesktop::getDesktopName(desk); - LOG((CLOG_DEBUG "switched to desktop \"%s\" with window 0x%08x", m_deskName.c_str(), (UInt32)m_window)); - - return true; + return window; } void -CMSWindowsScreen::syncDesktop() const +CMSWindowsScreen::destroyWindow(HWND hwnd) const { - // change calling thread's desktop - if (!CMSWindowsDesktop::setDesktop(m_desk)) { -// LOG((CLOG_WARN "failed to set desktop: %d", GetLastError())); + if (hwnd != NULL) { + DestroyWindow(hwnd); } +} - // attach input queues if not already attached. this has a habit - // of sucking up more and more CPU each time it's called (even if - // the threads are already attached). since we only expect one - // thread to call this more than once we can save just the last - // attached thread. - DWORD threadID = GetCurrentThreadId(); - if (threadID != m_lastThreadID && threadID != m_threadID) { - CMSWindowsScreen* self = const_cast(this); - self->m_lastThreadID = threadID; - AttachThreadInput(threadID, m_threadID, TRUE); +void +CMSWindowsScreen::sendEvent(CEvent::Type type, void* data) +{ + EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), data)); +} + +void +CMSWindowsScreen::sendClipboardEvent(CEvent::Type type, ClipboardID id) +{ + CClipboardInfo* info = (CClipboardInfo*)malloc(sizeof(CClipboardInfo)); + info->m_id = id; + info->m_sequenceNumber = m_sequenceNumber; + sendEvent(type, info); +} + +void +CMSWindowsScreen::handleSystemEvent(const CEvent& event, void*) +{ + MSG* msg = reinterpret_cast(event.getData()); + assert(msg != NULL); + + if (CArchMiscWindows::processDialog(msg)) { + return; } + if (onPreDispatch(msg->hwnd, msg->message, msg->wParam, msg->lParam)) { + return; + } + TranslateMessage(msg); + DispatchMessage(msg); } bool @@ -1002,9 +804,6 @@ CMSWindowsScreen::onPreDispatch(HWND hwnd, switch (message) { case SYNERGY_MSG_SCREEN_SAVER: return onScreensaver(wParam != 0); - - case WM_TIMER: - return onTimer(static_cast(wParam)); } if (m_isPrimary) { @@ -1050,7 +849,7 @@ CMSWindowsScreen::onPreDispatchPrimary(HWND, KeyID key = m_keyMapper.mapKeyFromEvent(wParam, lParam, &mask, NULL); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); - m_primaryReceiver->onKeyUp(key, mask, button); + sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask, button, 1)); m_keyMapper.updateKey(static_cast(wParam), false); } if (m_keyMapper.isPressed(VK_RWIN) && @@ -1068,7 +867,7 @@ CMSWindowsScreen::onPreDispatchPrimary(HWND, KeyID key = m_keyMapper.mapKeyFromEvent(wParam, lParam, &mask, NULL); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); - m_primaryReceiver->onKeyUp(key, mask, button); + sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask, button, 1)); m_keyMapper.updateKey(static_cast(wParam), false); } } @@ -1119,10 +918,10 @@ CMSWindowsScreen::onPreDispatchPrimary(HWND, } bool -CMSWindowsScreen::onEvent(HWND hwnd, - UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result) +CMSWindowsScreen::onEvent(HWND, UINT msg, + WPARAM wParam, LPARAM lParam, LRESULT* result) { - switch (message) { + switch (msg) { case WM_QUERYENDSESSION: if (m_is95Family) { *result = TRUE; @@ -1133,22 +932,18 @@ CMSWindowsScreen::onEvent(HWND hwnd, case WM_ENDSESSION: if (m_is95Family) { if (wParam == TRUE && lParam == 0) { - exitMainLoop(); + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); } return true; } break; - case WM_PAINT: - ValidateRect(hwnd, NULL); - return true; - case WM_DRAWCLIPBOARD: LOG((CLOG_DEBUG "clipboard was taken")); // first pass on the message if (m_nextClipboardWindow != NULL) { - SendMessage(m_nextClipboardWindow, message, wParam, lParam); + SendMessage(m_nextClipboardWindow, msg, wParam, lParam); } // now handle the message @@ -1160,16 +955,13 @@ CMSWindowsScreen::onEvent(HWND hwnd, LOG((CLOG_DEBUG "clipboard chain: new next: 0x%08x", m_nextClipboardWindow)); } else if (m_nextClipboardWindow != NULL) { - LOG((CLOG_DEBUG "clipboard chain: forward: %d 0x%08x 0x%08x", message, wParam, lParam)); - SendMessage(m_nextClipboardWindow, message, wParam, lParam); + LOG((CLOG_DEBUG "clipboard chain: forward: %d 0x%08x 0x%08x", msg, wParam, lParam)); + SendMessage(m_nextClipboardWindow, msg, wParam, lParam); } return true; case WM_DISPLAYCHANGE: return onDisplayChange(); - - case WM_ACTIVATEAPP: - return onActivate(wParam != FALSE); } return false; @@ -1234,7 +1026,8 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) scanCode = m_keyMapper.keyToScanCode(&button); button = static_cast(scanCode); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_primaryReceiver->onKeyUp(key, mask2, button); + sendEvent(getKeyUpEvent(), + CKeyInfo::alloc(key, mask2, button, 1)); } if (ctrlR) { key = kKeyControl_R; @@ -1242,7 +1035,8 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) scanCode = m_keyMapper.keyToScanCode(&button); button = static_cast(scanCode); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_primaryReceiver->onKeyUp(key, mask2, button); + sendEvent(getKeyUpEvent(), + CKeyInfo::alloc(key, mask2, button, 1)); } if (altL) { key = kKeyAlt_L; @@ -1250,7 +1044,8 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) scanCode = m_keyMapper.keyToScanCode(&button); button = static_cast(scanCode); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_primaryReceiver->onKeyUp(key, mask2, button); + sendEvent(getKeyUpEvent(), + CKeyInfo::alloc(key, mask2, button, 1)); } if (altR) { key = kKeyAlt_R; @@ -1258,7 +1053,8 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) scanCode = m_keyMapper.keyToScanCode(&button); button = static_cast(scanCode); LOG((CLOG_DEBUG1 "event: fake key release key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_primaryReceiver->onKeyUp(key, mask2, button); + sendEvent(getKeyUpEvent(), + CKeyInfo::alloc(key, mask2, button, 1)); } } @@ -1267,14 +1063,16 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) SInt32 repeat = (SInt32)(lParam & 0xffff); if (!wasDown) { LOG((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x button=0x%04x", key, mask, button)); - m_primaryReceiver->onKeyDown(key, mask, button); + sendEvent(getKeyDownEvent(), + CKeyInfo::alloc(key, mask, button, 1)); if (repeat > 0) { --repeat; } } if (repeat >= 1) { LOG((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d button=0x%04x", key, mask, repeat, button)); - m_primaryReceiver->onKeyRepeat(key, mask, repeat, button); + sendEvent(getKeyRepeatEvent(), + CKeyInfo::alloc(key, mask, button, repeat)); } // restore ctrl and alt state @@ -1292,7 +1090,8 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) scanCode = m_keyMapper.keyToScanCode(&button); button = static_cast(scanCode); LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_primaryReceiver->onKeyDown(key, mask2, button); + sendEvent(getKeyDownEvent(), + CKeyInfo::alloc(key, mask2, button, 1)); mask2 |= KeyModifierControl; } if (ctrlR) { @@ -1301,7 +1100,8 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) scanCode = m_keyMapper.keyToScanCode(&button); button = static_cast(scanCode); LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_primaryReceiver->onKeyDown(key, mask2, button); + sendEvent(getKeyDownEvent(), + CKeyInfo::alloc(key, mask2, button, 1)); mask2 |= KeyModifierControl; } if (altL) { @@ -1310,7 +1110,8 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) scanCode = m_keyMapper.keyToScanCode(&button); button = static_cast(scanCode); LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_primaryReceiver->onKeyDown(key, mask2, button); + sendEvent(getKeyDownEvent(), + CKeyInfo::alloc(key, mask2, button, 1)); mask2 |= KeyModifierAlt; } if (altR) { @@ -1319,7 +1120,8 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) scanCode = m_keyMapper.keyToScanCode(&button); button = static_cast(scanCode); LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask2, button)); - m_primaryReceiver->onKeyDown(key, mask2, button); + sendEvent(getKeyDownEvent(), + CKeyInfo::alloc(key, mask2, button, 1)); mask2 |= KeyModifierAlt; } } @@ -1334,13 +1136,15 @@ CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam) if (m_is95Family && !isModifier(wParam) && m_keyMapper.isPressed(static_cast(wParam))) { LOG((CLOG_DEBUG1 "event: fake key press key=%d mask=0x%04x button=0x%04x", key, mask, button)); - m_primaryReceiver->onKeyDown(key, mask, button); + sendEvent(getKeyDownEvent(), + CKeyInfo::alloc(key, mask, button, 1)); m_keyMapper.updateKey(static_cast(wParam), true); } // do key up LOG((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x button=0x%04x", key, mask, button)); - m_primaryReceiver->onKeyUp(key, mask, button); + sendEvent(getKeyUpEvent(), + CKeyInfo::alloc(key, mask, button, 1)); } } else { @@ -1383,7 +1187,7 @@ CMSWindowsScreen::onMouseButton(WPARAM wParam, LPARAM lParam) case WM_NCXBUTTONDBLCLK: LOG((CLOG_DEBUG1 "event: button press button=%d", button)); if (button != kButtonNone) { - m_primaryReceiver->onMouseDown(button); + sendEvent(getButtonDownEvent(), CButtonInfo::alloc(button)); } pressed = true; break; @@ -1398,7 +1202,7 @@ CMSWindowsScreen::onMouseButton(WPARAM wParam, LPARAM lParam) case WM_NCXBUTTONUP: LOG((CLOG_DEBUG1 "event: button release button=%d", button)); if (button != kButtonNone) { - m_primaryReceiver->onMouseUp(button); + sendEvent(getButtonUpEvent(), CButtonInfo::alloc(button)); } pressed = false; break; @@ -1438,7 +1242,8 @@ CMSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) if (m_isOnScreen) { // motion on primary screen - m_primaryReceiver->onMouseMovePrimary(m_xCursor, m_yCursor); + sendEvent(getMotionOnPrimaryEvent(), + CMotionInfo::alloc(m_xCursor, m_yCursor)); } else { // motion on secondary screen. warp mouse back to @@ -1459,7 +1264,7 @@ CMSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) } else { // send motion - m_primaryReceiver->onMouseMoveSecondary(x, y); + sendEvent(getMotionOnSecondaryEvent(), CMotionInfo::alloc(x, y)); } } @@ -1472,7 +1277,7 @@ CMSWindowsScreen::onMouseWheel(SInt32 delta) // ignore message if posted prior to last mark change if (!ignore()) { LOG((CLOG_DEBUG1 "event: button wheel delta=%d", delta)); - m_primaryReceiver->onMouseWheel(delta); + sendEvent(getWheelEvent(), CWheelInfo::alloc(delta)); } return true; } @@ -1496,55 +1301,11 @@ CMSWindowsScreen::onScreensaver(bool activated) if (activated) { if (m_screensaver->checkStarted(SYNERGY_MSG_SCREEN_SAVER, FALSE, 0)) { - m_primaryReceiver->onScreensaver(true); + sendEvent(getScreensaverActivatedEvent()); } } else { - m_primaryReceiver->onScreensaver(false); - } - - return true; -} - -bool -CMSWindowsScreen::onTimer(UINT timerID) -{ - if (timerID == m_timer) { - // if current desktop is not the input desktop then switch to it. - HDESK desk = CMSWindowsDesktop::openInputDesktop(); - if (desk == m_desk || - CMSWindowsDesktop::getDesktopName(desk) == m_deskName || - m_screensaver->isActive()) { - // same desktop or screensaver is active. don't switch - // desktops when the screensaver is active. we'd most - // likely switch to the screensaver desktop which would - // have the side effect of forcing the screensaver to stop. - CMSWindowsDesktop::closeDesktop(desk); - } - else { - switchDesktop(desk); - } - - // if the desktop was inaccessible and isn't anymore then - // update our key state. - if (desk != NULL && m_inaccessibleDesktop) { - LOG((CLOG_DEBUG "desktop is now accessible")); - m_inaccessibleDesktop = false; - updateKeys(); - } - - // note if desktop was accessible but isn't anymore - else if (desk == NULL && !m_inaccessibleDesktop) { - m_inaccessibleDesktop = true; - LOG((CLOG_DEBUG "desktop is now inaccessible")); - } - } - - else if (timerID == m_oneShotTimer) { - // one shot timer expired - KillTimer(NULL, m_oneShotTimer); - m_oneShotTimer = 0; - m_primaryReceiver->onOneShotTimerExpired(0); + sendEvent(getScreensaverDeactivatedEvent()); } return true; @@ -1573,17 +1334,8 @@ CMSWindowsScreen::onDisplayChange() } } - // collect new screen info - CClientInfo info; - info.m_x = m_x; - info.m_y = m_y; - info.m_w = m_w; - info.m_h = m_h; - info.m_zoneSize = getJumpZoneSize(); - getCursorPos(info.m_mx, info.m_my); - // send new screen info - m_receiver->onInfoChanged(info); + sendEvent(getShapeChangedEvent()); } return true; @@ -1599,8 +1351,8 @@ CMSWindowsScreen::onClipboardChange() if (m_ownClipboard) { LOG((CLOG_DEBUG "clipboard changed: lost ownership")); m_ownClipboard = false; - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); + sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardClipboard); + sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardSelection); } } else { @@ -1611,22 +1363,11 @@ CMSWindowsScreen::onClipboardChange() return true; } -bool -CMSWindowsScreen::onActivate(bool activated) -{ - if (!m_isPrimary && activated) { - // some other app activated. hide the hider window. - ShowWindow(m_window, SW_HIDE); - } - - return false; -} - void CMSWindowsScreen::warpCursorNoFlush(SInt32 x, SInt32 y) { // send an event that we can recognize before the mouse warp - PostThreadMessage(m_threadID, SYNERGY_MSG_PRE_WARP, x, y); + PostThreadMessage(GetCurrentThreadId(), SYNERGY_MSG_PRE_WARP, x, y); // warp mouse. hopefully this inserts a mouse motion event // between the previous message and the following message. @@ -1651,7 +1392,7 @@ CMSWindowsScreen::warpCursorNoFlush(SInt32 x, SInt32 y) ARCH->sleep(0.0); // send an event that we can recognize after the mouse warp - PostThreadMessage(m_threadID, SYNERGY_MSG_POST_WARP, 0, 0); + PostThreadMessage(GetCurrentThreadId(), SYNERGY_MSG_POST_WARP, 0, 0); } void @@ -1661,7 +1402,7 @@ CMSWindowsScreen::nextMark() ++m_mark; // mark point in message queue where the mark was changed - PostThreadMessage(m_threadID, SYNERGY_MSG_MARK, m_mark, 0); + PostThreadMessage(GetCurrentThreadId(), SYNERGY_MSG_MARK, m_mark, 0); } bool @@ -1670,34 +1411,23 @@ CMSWindowsScreen::ignore() const return (m_mark != m_markReceived); } -HCURSOR -CMSWindowsScreen::createBlankCursor() const -{ - // create a transparent cursor - int cw = GetSystemMetrics(SM_CXCURSOR); - int ch = GetSystemMetrics(SM_CYCURSOR); - UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)]; - UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)]; - memset(cursorAND, 0xff, ch * ((cw + 31) >> 2)); - memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2)); - HCURSOR c = CreateCursor(s_instance, 0, 0, cw, ch, cursorAND, cursorXOR); - delete[] cursorXOR; - delete[] cursorAND; - return c; -} - void -CMSWindowsScreen::showCursor(bool show) const +CMSWindowsScreen::updateScreenShape() { - if (m_cursorThread != 0) { - if (m_threadID != m_cursorThread) { - AttachThreadInput(m_threadID, m_cursorThread, TRUE); - } - ShowCursor(show ? TRUE : FALSE); - if (m_threadID != m_cursorThread) { - AttachThreadInput(m_threadID, m_cursorThread, FALSE); - } - } + // get shape + m_x = GetSystemMetrics(SM_XVIRTUALSCREEN); + m_y = GetSystemMetrics(SM_YVIRTUALSCREEN); + m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN); + m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN); + + // get center for cursor + m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1; + m_yCenter = GetSystemMetrics(SM_CYSCREEN) >> 1; + + // check for multiple monitors + m_multimon = (m_w != GetSystemMetrics(SM_CXSCREEN) || + m_h != GetSystemMetrics(SM_CYSCREEN)); + LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_multimon ? "(multi-monitor)" : "")); } void @@ -1867,3 +1597,417 @@ CMSWindowsScreen::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) return result; } + +LRESULT CALLBACK +CMSWindowsScreen::primaryDeskProc( + HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + // FIXME + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +LRESULT CALLBACK +CMSWindowsScreen::secondaryDeskProc( + HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_MOUSEMOVE: + case WM_ACTIVATE: + case WM_ACTIVATEAPP: + // hide window + ShowWindow(hwnd, SW_HIDE); + break; + } + + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +void +CMSWindowsScreen::deskMouseMove(SInt32 x, SInt32 y) const +{ + // motion is simple (i.e. it's on the primary monitor) if there + // is only one monitor. + bool simple = !m_multimon; + if (!simple) { + // also simple if motion is within the primary monitor + simple = (x >= 0 && x < GetSystemMetrics(SM_CXSCREEN) && + y >= 0 && y < GetSystemMetrics(SM_CYSCREEN)); + } + + // move the mouse directly to target position if motion is simple + if (simple) { + // when using absolute positioning with mouse_event(), + // the normalized device coordinates range over only + // the primary screen. + SInt32 w = GetSystemMetrics(SM_CXSCREEN); + SInt32 h = GetSystemMetrics(SM_CYSCREEN); + mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, + (DWORD)((65536.0 * x) / w), + (DWORD)((65536.0 * y) / h), + 0, 0); + } + + // windows 98 (and Me?) is broken. you cannot set the absolute + // position of the mouse except on the primary monitor but you + // can do relative moves onto any monitor. this is, in microsoft's + // words, "by design." apparently the designers of windows 2000 + // we're a little less lazy and did it right. + // + // microsoft recommends in Q193003 to absolute position the cursor + // somewhere on the primary monitor then relative move to the + // desired location. this doesn't work for us because when the + // user drags a scrollbar, a window, etc. it causes the dragged + // item to jump back and forth between the position on the primary + // monitor and the desired position. while it always ends up in + // the right place, the effect is disconcerting. + // + // instead we'll get the cursor's current position and do just a + // relative move from there to the desired position. relative + // moves are subject to cursor acceleration which we don't want. + // so we disable acceleration, do the relative move, then restore + // acceleration. there's a slight chance we'll end up in the + // wrong place if the user moves the cursor using this system's + // mouse while simultaneously moving the mouse on the server + // system. that defeats the purpose of synergy so we'll assume + // that won't happen. even if it does, the next mouse move will + // correct the position. + else { + // save mouse speed & acceleration + int oldSpeed[4]; + bool accelChanged = + SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed, 0) && + SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0); + + // use 1:1 motion + if (accelChanged) { + int newSpeed[4] = { 0, 0, 0, 1 }; + accelChanged = + SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) || + SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0); + } + + // move relative to mouse position + POINT pos; + GetCursorPos(&pos); + mouse_event(MOUSEEVENTF_MOVE, x - pos.x, y - pos.y, 0, 0); + + // restore mouse speed & acceleration + if (accelChanged) { + SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0); + SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0); + } + } +} + +void +CMSWindowsScreen::deskEnter(CDesk* desk, DWORD& cursorThreadID) +{ + if (m_isPrimary) { + if (desk->m_lowLevel) { + if (cursorThreadID != 0) { + AttachThreadInput(desk->m_threadID, cursorThreadID, TRUE); + ShowCursor(TRUE); + AttachThreadInput(desk->m_threadID, cursorThreadID, FALSE); + cursorThreadID = 0; + } + } + } + ShowWindow(desk->m_window, SW_HIDE); +} + +void +CMSWindowsScreen::deskLeave(CDesk* desk, DWORD& cursorThreadID) +{ + if (m_isPrimary) { + // we don't need a window to capture input but we need a window + // to hide the cursor when using low-level hooks. also take the + // activation so we use our keyboard layout, not the layout of + // whatever window was active. + if (desk->m_lowLevel) { + SetWindowPos(desk->m_window, HWND_TOPMOST, + m_xCenter, m_yCenter, 1, 1, SWP_NOACTIVATE); + if (cursorThreadID == 0) { + HWND hwnd = GetForegroundWindow(); + cursorThreadID = GetWindowThreadProcessId(hwnd, NULL); + if (cursorThreadID != 0) { + AttachThreadInput(desk->m_threadID, cursorThreadID, TRUE); + ShowCursor(FALSE); + AttachThreadInput(desk->m_threadID, cursorThreadID, FALSE); + } + } + } + } + else { + // move hider window under the cursor center + MoveWindow(desk->m_window, m_xCenter, m_yCenter, 1, 1, FALSE); + + // raise and show the hider window + ShowWindow(desk->m_window, SW_SHOWNA); + + // warp the mouse to the cursor center + deskMouseMove(m_xCenter, m_yCenter); + } +} + +void +CMSWindowsScreen::deskThread(void* vdesk) +{ + MSG msg; + + // id of thread that had cursor when we were last told to hide it + DWORD cursorThreadID = 0; + + // use given desktop for this thread + CDesk* desk = reinterpret_cast(vdesk); + desk->m_threadID = GetCurrentThreadId(); + desk->m_window = NULL; + if (desk->m_desk != NULL && SetThreadDesktop(desk->m_desk) != 0) { + // create a message queue + PeekMessage(&msg, NULL, 0,0, PM_NOREMOVE); + + // create a window. we use this window to hide the cursor. + try { + desk->m_window = createWindow(m_deskClass, "SynergyDesk"); + LOG((CLOG_DEBUG "desk %s window is 0x%08x", desk->m_name.c_str(), desk->m_window)); + } + catch (...) { + // ignore + LOG((CLOG_DEBUG "can't create desk window for %s", desk->m_name.c_str())); + } + + // a window on the primary screen should never activate + if (m_isPrimary && desk->m_window != NULL) { + EnableWindow(desk->m_window, FALSE); + } + } + + // tell main thread that we're ready + { + CLock lock(&m_mutex); + m_deskReady = true; + m_deskReady.broadcast(); + } + + while (GetMessage(&msg, NULL, 0, 0)) { + switch (msg.message) { + default: + TranslateMessage(&msg); + DispatchMessage(&msg); + continue; + + case SYNERGY_MSG_SWITCH: + if (m_isPrimary) { + m_uninstall(); + if (m_screensaverNotify) { + m_uninstallScreensaver(); + m_installScreensaver(); + } + switch (m_install()) { + case kHOOK_FAILED: + // we won't work on this desk + desk->m_lowLevel = false; + break; + + case kHOOK_OKAY: + desk->m_lowLevel = false; + break; + + case kHOOK_OKAY_LL: + desk->m_lowLevel = true; + break; + } + } + break; + + case SYNERGY_MSG_ENTER: + deskEnter(desk, cursorThreadID); + break; + + case SYNERGY_MSG_LEAVE: + deskLeave(desk, cursorThreadID); + break; + + case SYNERGY_MSG_FAKE_KEY: + keybd_event(HIBYTE(msg.lParam), LOBYTE(msg.lParam), msg.wParam, 0); + break; + + case SYNERGY_MSG_FAKE_BUTTON: + if (msg.wParam != 0) { + mouse_event(msg.wParam, 0, 0, msg.lParam, 0); + } + break; + + case SYNERGY_MSG_FAKE_MOVE: + deskMouseMove(static_cast(msg.wParam), + static_cast(msg.lParam)); + break; + + case SYNERGY_MSG_FAKE_WHEEL: + mouse_event(MOUSEEVENTF_WHEEL, 0, 0, msg.wParam, 0); + break; + + case SYNERGY_MSG_CURSOR_POS: { + POINT* pos = reinterpret_cast(msg.wParam); + if (!GetCursorPos(pos)) { + pos->x = m_xCenter; + pos->y = m_yCenter; + } + break; + } + + case SYNERGY_MSG_SYNC_KEYS: + m_keyMapper.update(m_keyState); + break; + } + + // notify that message was processed + CLock lock(&m_mutex); + m_deskReady = true; + m_deskReady.broadcast(); + } + + // clean up + if (desk->m_window != NULL) { + DestroyWindow(desk->m_window); + } + if (desk->m_desk != NULL) { + CMSWindowsDesktop::closeDesktop(desk->m_desk); + } +} + +CMSWindowsScreen::CDesk* +CMSWindowsScreen::addDesk(const CString& name, HDESK hdesk) +{ + CDesk* desk = new CDesk; + desk->m_name = name; + desk->m_desk = hdesk; + desk->m_targetID = GetCurrentThreadId(); + desk->m_thread = new CThread(new TMethodJob( + this, &CMSWindowsScreen::deskThread, desk)); + waitForDesk(); + m_desks.insert(std::make_pair(name, desk)); + return desk; +} + +void +CMSWindowsScreen::removeDesks() +{ + for (CDesks::iterator index = m_desks.begin(); + index != m_desks.end(); ++index) { + CDesk* desk = index->second; + PostThreadMessage(desk->m_threadID, WM_QUIT, 0, 0); + desk->m_thread->wait(); + delete desk->m_thread; + delete desk; + } + m_desks.clear(); +} + +void +CMSWindowsScreen::checkDesk() +{ + // get current desktop. if we already know about it then return. + CDesk* desk; + HDESK hdesk = CMSWindowsDesktop::openInputDesktop(); + CString name = CMSWindowsDesktop::getDesktopName(hdesk); + CDesks::const_iterator index = m_desks.find(name); + if (index == m_desks.end()) { + desk = addDesk(name, hdesk); + // hold on to hdesk until thread exits so the desk can't + // be removed by the system + } + else { + CMSWindowsDesktop::closeDesktop(hdesk); + desk = index->second; + } + + // if active desktop changed then tell the old and new desk threads + // about the change. don't switch desktops when the screensaver is + // active becaue we'd most likely switch to the screensaver desktop + // which would have the side effect of forcing the screensaver to + // stop. + // FIXME -- really not switch if screensaver is active? + if (name != m_activeDeskName && !m_screensaver->isActive()) { + // show cursor on previous desk + if (!m_isOnScreen) { + sendDeskMessage(SYNERGY_MSG_ENTER, 0, 0); + } + + // check for desk accessibility change. we don't get events + // from an inaccessible desktop so when we switch from an + // inaccessible desktop to an accessible one we have to + // update the keyboard state. + LOG((CLOG_DEBUG "switched to desk \"%s\"", name.c_str())); + bool isAccessible = isDeskAccessible(desk); + if (isDeskAccessible(m_activeDesk) != isAccessible) { + if (isAccessible) { + LOG((CLOG_DEBUG "desktop is now accessible")); + updateKeys(); + } + else { + LOG((CLOG_DEBUG "desktop is now inaccessible")); + } + } + + // switch desk + m_activeDesk = desk; + m_activeDeskName = name; + sendDeskMessage(SYNERGY_MSG_SWITCH, 0, 0); + + // hide cursor on new desk + if (!m_isOnScreen) { + sendDeskMessage(SYNERGY_MSG_LEAVE, 0, 0); + } + } +} + +// FIXME -- may want some of following when we switch desks. calling +// nextMark() for isPrimary may lead to loss of events. updateKeys() +// is to catch any key events lost between switching and detecting the +// switch. neither are strictly necessary. +/* + if (m_isPrimary) { + if (m_isOnScreen) { + // all messages prior to now are invalid + // FIXME -- is this necessary; couldn't we lose key releases? + nextMark(); + } + } + else { + // update key state + updateKeys(); + } +*/ + +bool +CMSWindowsScreen::isDeskAccessible(const CDesk* desk) const +{ + return (desk != NULL && desk->m_desk != NULL); +} + +void +CMSWindowsScreen::sendDeskMessage(UINT msg, WPARAM wParam, LPARAM lParam) const +{ + if (m_activeDesk != NULL && m_activeDesk->m_window != NULL) { + PostThreadMessage(m_activeDesk->m_threadID, msg, wParam, lParam); + waitForDesk(); + } +} + +void +CMSWindowsScreen::waitForDesk() const +{ + CMSWindowsScreen* self = const_cast(this); + + CLock lock(&m_mutex); + while (!(bool)m_deskReady) { + m_deskReady.wait(); + } + self->m_deskReady = false; +} + +void +CMSWindowsScreen::handleCheckDesk(const CEvent&, void*) +{ + checkDesk(); +} diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index 29be457b..ddae1f02 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -18,19 +18,20 @@ #include "IPlatformScreen.h" #include "CMSWindowsKeyMapper.h" #include "CSynergyHook.h" +#include "CCondVar.h" #include "CMutex.h" #include "CString.h" #define WIN32_LEAN_AND_MEAN #include +class CEventQueueTimer; class CMSWindowsScreenSaver; -class IScreenReceiver; -class IPrimaryScreenReceiver; +class CThread; //! Implementation of IPlatformScreen for Microsoft Windows class CMSWindowsScreen : public IPlatformScreen { public: - CMSWindowsScreen(IScreenReceiver*, IPrimaryScreenReceiver*); + CMSWindowsScreen(bool isPrimary); virtual ~CMSWindowsScreen(); //! @name manipulators @@ -56,12 +57,9 @@ public: //@} // IPlatformScreen overrides - virtual void open(IKeyState*); - virtual void close(); + virtual void setKeyState(IKeyState*); virtual void enable(); virtual void disable(); - virtual void mainLoop(); - virtual void exitMainLoop(); virtual void enter(); virtual bool leave(); virtual bool setClipboard(ClipboardID, const IClipboard*); @@ -72,17 +70,22 @@ public: virtual void resetOptions(); virtual void setOptions(const COptionsList& options); virtual void updateKeys(); + virtual void setSequenceNumber(UInt32); virtual bool isPrimary() const; - virtual bool getClipboard(ClipboardID, IClipboard*) const; - virtual void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; - virtual void getCursorPos(SInt32&, SInt32&) const; + + // IScreen overrides + virtual void* getEventTarget() const; + virtual bool getClipboard(ClipboardID id, IClipboard*) const; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + virtual void getCursorPos(SInt32& x, SInt32& y) const; // IPrimaryScreen overrides virtual void reconfigure(UInt32 activeSides); virtual void warpCursor(SInt32 x, SInt32 y); - virtual UInt32 addOneShotTimer(double timeout); virtual SInt32 getJumpZoneSize() const; virtual bool isAnyMouseButtonDown() const; + virtual void getCursorCenter(SInt32& x, SInt32& y) const; virtual const char* getKeyName(KeyButton) const; // ISecondaryScreen overrides @@ -97,17 +100,31 @@ public: bool isAutoRepeat) const; private: - // update screen size cache - void updateScreenShape(); + class CDesk { + public: + CString m_name; + CThread* m_thread; + DWORD m_threadID; + DWORD m_targetID; + HDESK m_desk; + HWND m_window; + bool m_lowLevel; + }; + typedef std::map CDesks; - // switch to the given desktop. this destroys the window and unhooks - // all hooks, switches the desktop, then creates the window and rehooks - // all hooks (because you can't switch the thread's desktop if it has - // any windows or hooks). - bool switchDesktop(HDESK desk); - - // make sure we're on the expected desktop - void syncDesktop() const; +// FIXME -- comment + HINSTANCE openHookLibrary(const char* name); + void closeHookLibrary(HINSTANCE hookLibrary) const; + HCURSOR createBlankCursor() const; + void destroyCursor(HCURSOR cursor) const; + ATOM createWindowClass() const; + ATOM createDeskWindowClass(bool isPrimary) const; + void destroyClass(ATOM windowClass) const; + HWND createWindow(ATOM windowClass, const char* name) const; + void destroyWindow(HWND) const; + void sendEvent(CEvent::Type type, void* = NULL); + void sendClipboardEvent(CEvent::Type type, ClipboardID id); + void handleSystemEvent(const CEvent& event, void*); // handle message before it gets dispatched. returns true iff // the message should not be dispatched. @@ -128,10 +145,8 @@ private: bool onMouseMove(SInt32 x, SInt32 y); bool onMouseWheel(SInt32 delta); bool onScreensaver(bool activated); - bool onTimer(UINT timerID); bool onDisplayChange(); bool onClipboardChange(); - bool onActivate(bool activated); // XXX // warp cursor without discarding queued events @@ -144,11 +159,8 @@ private: bool ignore() const; // XXX - // create the transparent cursor - HCURSOR createBlankCursor() const; - - // show/hide the cursor - void showCursor(bool) const; + // update screen size cache + void updateScreenShape(); // enable/disable special key combinations so we can catch/pass them void enableSpecialKeys(bool) const; @@ -169,6 +181,22 @@ private: // our window proc static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM); + // our desk window procs + static LRESULT CALLBACK primaryDeskProc(HWND, UINT, WPARAM, LPARAM); + static LRESULT CALLBACK secondaryDeskProc(HWND, UINT, WPARAM, LPARAM); + + void deskMouseMove(SInt32 x, SInt32 y) const; + void deskEnter(CDesk* desk, DWORD& cursorThreadID); + void deskLeave(CDesk* desk, DWORD& cursorThreadID); + void deskThread(void* vdesk); + CDesk* addDesk(const CString& name, HDESK hdesk); + void removeDesks(); + void checkDesk(); + bool isDeskAccessible(const CDesk* desk) const; + void sendDeskMessage(UINT, WPARAM, LPARAM) const; + void waitForDesk() const; + void handleCheckDesk(const CEvent& event, void*); + private: static HINSTANCE s_instance; @@ -178,17 +206,13 @@ private: // true if windows 95/98/me bool m_is95Family; - // receivers - IScreenReceiver* m_receiver; - IPrimaryScreenReceiver* m_primaryReceiver; - // true if mouse has entered the screen bool m_isOnScreen; // our resources ATOM m_class; + ATOM m_deskClass; HCURSOR m_cursor; - HWND m_window; // screen shape stuff SInt32 m_x, m_y; @@ -201,6 +225,8 @@ private: // last mouse position SInt32 m_xCursor, m_yCursor; + UInt32 m_sequenceNumber; + // used to discard queued messages that are no longer needed UInt32 m_mark; UInt32 m_markReceived; @@ -208,33 +234,27 @@ private: // the main loop's thread id DWORD m_threadID; - // the thread id of the last attached thread - DWORD m_lastThreadID; - // the timer used to check for desktop switching - UINT m_timer; - - // the one shot timer - UINT m_oneShotTimer; + CEventQueueTimer* m_timer; // screen saver stuff CMSWindowsScreenSaver* m_screensaver; bool m_screensaverNotify; - // clipboard stuff + // clipboard stuff. our window is used mainly as a clipboard + // owner and as a link in the clipboard viewer chain. + HWND m_window; HWND m_nextClipboardWindow; bool m_ownClipboard; // the current desk and it's name - HDESK m_desk; - CString m_deskName; + CDesk* m_activeDesk; + CString m_activeDeskName; - // true when the current desktop is inaccessible. while - // the desktop is inaccessible we won't receive user input - // and we'll lose track of the keyboard state. when the - // desktop becomes accessible again we'll notify the event - // handler of that. - bool m_inaccessibleDesktop; + // one desk per desktop and a cond var to communicate with it + CMutex m_mutex; + CCondVar m_deskReady; + CDesks m_desks; // hook library stuff HINSTANCE m_hookLibrary; @@ -247,7 +267,6 @@ private: SetModeFunc m_setMode; InstallScreenSaverFunc m_installScreensaver; UninstallScreenSaverFunc m_uninstallScreensaver; - bool m_lowLevel; // keyboard stuff IKeyState* m_keyState; @@ -256,9 +275,6 @@ private: // map of button state BYTE m_buttons[1 + kButtonExtra0 + 1]; - // stuff for hiding the cursor - DWORD m_cursorThread; - static CMSWindowsScreen* s_screen; }; diff --git a/lib/platform/CSynergyHook.cpp b/lib/platform/CSynergyHook.cpp index 33022070..4b58ac8d 100644 --- a/lib/platform/CSynergyHook.cpp +++ b/lib/platform/CSynergyHook.cpp @@ -76,13 +76,10 @@ static DWORD g_threadID = 0; static HHOOK g_keyboard = NULL; static HHOOK g_mouse = NULL; static HHOOK g_getMessage = NULL; -static HANDLE g_hookThreadLL = NULL; -static DWORD g_hookThreadIDLL = 0; -static HANDLE g_hookEventLL = NULL; static HHOOK g_keyboardLL = NULL; static HHOOK g_mouseLL = NULL; static bool g_screenSaver = false; -static EHookMode g_mode = kHOOK_WATCH_JUMP_ZONE; +static EHookMode g_mode = kHOOK_DISABLE; static UInt32 g_zoneSides = 0; static SInt32 g_zoneSize = 0; static SInt32 g_xScreen = 0; @@ -141,6 +138,7 @@ restoreCursor() g_cursorThread = 0; } +#if !NO_GRAB_KEYBOARD static bool keyboardHookHandler(WPARAM wParam, LPARAM lParam) @@ -170,6 +168,7 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam) return false; } +#endif static bool @@ -231,7 +230,7 @@ mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data) PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); return true; } - else { + else if (g_mode == kHOOK_WATCH_JUMP_ZONE) { // check for mouse inside jump zone bool inside = false; if (!inside && (g_zoneSides & kLeftMask) != 0) { @@ -259,6 +258,7 @@ mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data) return false; } +#if !NO_GRAB_KEYBOARD static LRESULT CALLBACK keyboardHook(int code, WPARAM wParam, LPARAM lParam) @@ -272,6 +272,7 @@ keyboardHook(int code, WPARAM wParam, LPARAM lParam) return CallNextHookEx(g_keyboard, code, wParam, lParam); } +#endif static LRESULT CALLBACK @@ -354,6 +355,7 @@ getMessageHook(int code, WPARAM wParam, LPARAM lParam) // side, key repeats are not reported to us. // +#if !NO_GRAB_KEYBOARD static LRESULT CALLBACK keyboardLLHook(int code, WPARAM wParam, LPARAM lParam) @@ -385,6 +387,7 @@ keyboardLLHook(int code, WPARAM wParam, LPARAM lParam) return CallNextHookEx(g_keyboardLL, code, wParam, lParam); } +#endif // // low-level mouse hook -- this allows us to capture and handle mouse @@ -411,92 +414,6 @@ mouseLLHook(int code, WPARAM wParam, LPARAM lParam) return CallNextHookEx(g_mouseLL, code, wParam, lParam); } -static -DWORD WINAPI -getLowLevelProc(void*) -{ - // thread proc for low-level keyboard/mouse hooks. this does - // nothing but install the hook, process events, and uninstall - // the hook. - - // force this thread to have a message queue - MSG msg; - PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); - -#if !NO_GRAB_KEYBOARD - // install low-level keyboard hook - g_keyboardLL = SetWindowsHookEx(WH_KEYBOARD_LL, - &keyboardLLHook, - g_hinstance, - 0); - if (g_keyboardLL == NULL) { - // indicate failure and exit - g_hookThreadIDLL = 0; - SetEvent(g_hookEventLL); - return 1; - } -#else - // keep compiler quiet - &keyboardLLHook; -#endif - - // install low-level mouse hook - g_mouseLL = SetWindowsHookEx(WH_MOUSE_LL, - &mouseLLHook, - g_hinstance, - 0); - if (g_mouseLL == NULL) { - // indicate failure and exit - if (g_keyboardLL != NULL) { - UnhookWindowsHookEx(g_keyboardLL); - g_keyboardLL = NULL; - } - g_hookThreadIDLL = 0; - SetEvent(g_hookEventLL); - return 1; - } - - // ready - SetEvent(g_hookEventLL); - - // message loop - bool done = false; - while (!done) { - switch (GetMessage(&msg, NULL, 0, 0)) { - case -1: - break; - - case 0: - done = true; - break; - - default: - TranslateMessage(&msg); - DispatchMessage(&msg); - break; - } - } - - // uninstall hook - UnhookWindowsHookEx(g_mouseLL); - UnhookWindowsHookEx(g_keyboardLL); - g_mouseLL = NULL; - g_keyboardLL = NULL; - - return 0; -} - -#else // (_WIN32_WINNT < 0x0400) - -static -DWORD WINAPI -getLowLevelProc(void*) -{ - g_hookThreadIDLL = 0; - SetEvent(g_hookEventLL); - return 1; -} - #endif static @@ -559,12 +476,8 @@ DllMain(HINSTANCE instance, DWORD reason, LPVOID) } else if (reason == DLL_PROCESS_DETACH) { if (g_processID == GetCurrentProcessId()) { - if (g_keyboard != NULL || - g_mouse != NULL || - g_getMessage != NULL) { - uninstall(); - uninstallScreenSaver(); - } + uninstall(); + uninstallScreenSaver(); g_processID = 0; g_hinstance = NULL; } @@ -601,9 +514,6 @@ init(DWORD threadID) g_keyboard = NULL; g_mouse = NULL; g_getMessage = NULL; - g_hookThreadLL = NULL; - g_hookThreadIDLL = 0; - g_hookEventLL = NULL; g_keyboardLL = NULL; g_mouseLL = NULL; g_screenSaver = false; @@ -614,7 +524,7 @@ init(DWORD threadID) g_threadID = threadID; // set defaults - g_mode = kHOOK_WATCH_JUMP_ZONE; + g_mode = kHOOK_DISABLE; g_zoneSides = 0; g_zoneSize = 0; g_xScreen = 0; @@ -663,75 +573,51 @@ install() 0); } - // install low-level keyboard/mouse hooks, if possible. since these - // hooks are called in the context of the installing thread and that - // thread must have a message loop but we don't want the caller's - // message loop to do the work, we'll fire up a separate thread - // just for the hooks. note that low-level hooks are only available - // on windows NT SP3 and above. - g_hookEventLL = CreateEvent(NULL, TRUE, FALSE, NULL); - if (g_hookEventLL != NULL) { - g_hookThreadLL = CreateThread(NULL, 0, &getLowLevelProc, 0, - CREATE_SUSPENDED, &g_hookThreadIDLL); - if (g_hookThreadLL != NULL) { - // start the thread and wait for it to initialize - ResumeThread(g_hookThreadLL); - WaitForSingleObject(g_hookEventLL, INFINITE); - ResetEvent(g_hookEventLL); - - // the thread clears g_hookThreadIDLL if it failed - if (g_hookThreadIDLL == 0) { - CloseHandle(g_hookThreadLL); - g_hookThreadLL = NULL; - } - } - if (g_hookThreadLL == NULL) { - CloseHandle(g_hookEventLL); - g_hookEventLL = NULL; - } - } - - // install non-low-level hooks if the low-level hooks are not installed - if (g_hookThreadLL == NULL) { + // install keyboard hook #if !NO_GRAB_KEYBOARD +#if (_WIN32_WINNT >= 0x0400) + g_keyboardLL = SetWindowsHookEx(WH_KEYBOARD_LL, + &keyboardLLHook, + g_hinstance, + 0); +#endif + if (g_keyboardLL == NULL) { g_keyboard = SetWindowsHookEx(WH_KEYBOARD, &keyboardHook, g_hinstance, 0); -#else - // keep compiler quiet - &keyboardHook; + } #endif + + // install mouse hook +#if (_WIN32_WINNT >= 0x0400) + g_mouseLL = SetWindowsHookEx(WH_MOUSE_LL, + &mouseLLHook, + g_hinstance, + 0); +#endif + if (g_mouseLL == NULL) { g_mouse = SetWindowsHookEx(WH_MOUSE, &mouseHook, g_hinstance, 0); } - // check for any failures. uninstall all hooks on failure. - if (g_hookThreadLL == NULL && + // check that we got all the hooks we wanted + if ((g_getMessage == NULL && g_wheelSupport == kWheelOld) || #if !NO_GRAB_KEYBOARD - (g_keyboard == NULL || g_mouse == NULL)) { -#else - (g_mouse == NULL)) { + (g_keyboardLL == NULL && g_keyboard == NULL) || #endif - if (g_keyboard != NULL) { - UnhookWindowsHookEx(g_keyboard); - g_keyboard = NULL; - } - if (g_mouse != NULL) { - UnhookWindowsHookEx(g_mouse); - g_mouse = NULL; - } - if (g_getMessage != NULL && !g_screenSaver) { - UnhookWindowsHookEx(g_getMessage); - g_getMessage = NULL; - } - g_threadID = NULL; + (g_mouseLL == NULL && g_mouse == NULL)) { + uninstall(); return kHOOK_FAILED; } - return (g_hookThreadLL == NULL) ? kHOOK_OKAY : kHOOK_OKAY_LL; + if (g_keyboardLL != NULL || g_mouseLL != NULL) { + return kHOOK_OKAY_LL; + } + + return kHOOK_OKAY; } int @@ -740,14 +626,13 @@ uninstall(void) assert(g_hinstance != NULL); // uninstall hooks - if (g_hookThreadLL != NULL) { - PostThreadMessage(g_hookThreadIDLL, WM_QUIT, 0, 0); - WaitForSingleObject(g_hookThreadLL, INFINITE); - CloseHandle(g_hookEventLL); - CloseHandle(g_hookThreadLL); - g_hookEventLL = NULL; - g_hookThreadLL = NULL; - g_hookThreadIDLL = 0; + if (g_keyboardLL != NULL) { + UnhookWindowsHookEx(g_keyboardLL); + g_keyboardLL = NULL; + } + if (g_mouseLL != NULL) { + UnhookWindowsHookEx(g_mouseLL); + g_mouseLL = NULL; } if (g_keyboard != NULL) { UnhookWindowsHookEx(g_keyboard); diff --git a/lib/platform/CSynergyHook.h b/lib/platform/CSynergyHook.h index 6953fd70..a9ab2b0c 100644 --- a/lib/platform/CSynergyHook.h +++ b/lib/platform/CSynergyHook.h @@ -40,6 +40,7 @@ #define SYNERGY_MSG_SCREEN_SAVER WM_APP + 0x0018 // activated; #define SYNERGY_MSG_INPUT_FIRST SYNERGY_MSG_KEY #define SYNERGY_MSG_INPUT_LAST SYNERGY_MSG_PRE_WARP +#define SYNERGY_HOOK_LAST_MSG SYNERGY_MSG_SCREEN_SAVER extern "C" { @@ -50,6 +51,7 @@ enum EHookResult { }; enum EHookMode { + kHOOK_DISABLE, kHOOK_WATCH_JUMP_ZONE, kHOOK_RELAY_EVENTS }; diff --git a/lib/platform/CXWindowsClipboard.cpp b/lib/platform/CXWindowsClipboard.cpp index 0606c3ef..52140624 100644 --- a/lib/platform/CXWindowsClipboard.cpp +++ b/lib/platform/CXWindowsClipboard.cpp @@ -740,7 +740,7 @@ CXWindowsClipboard::motifFillCache() // save it motifFormats.insert(std::make_pair(motifFormat->m_type, data)); } - const UInt32 numMotifFormats = motifFormats.size(); + //const UInt32 numMotifFormats = motifFormats.size(); // try each converter in order (because they're in order of // preference). diff --git a/lib/platform/CXWindowsEventQueueBuffer.cpp b/lib/platform/CXWindowsEventQueueBuffer.cpp index 7592bc89..ea450e17 100644 --- a/lib/platform/CXWindowsEventQueueBuffer.cpp +++ b/lib/platform/CXWindowsEventQueueBuffer.cpp @@ -143,6 +143,7 @@ CXWindowsEventQueueBuffer::addEvent(UInt32 dataID) // force waitForEvent() to return XFlush(m_display); + return true; } bool @@ -154,7 +155,7 @@ CXWindowsEventQueueBuffer::isEmpty() const CEventQueueTimer* CXWindowsEventQueueBuffer::newTimer(double, bool) const { - return new CEventQueueTimer(); + return new CEventQueueTimer; } void diff --git a/lib/platform/CXWindowsKeyMapper.cpp b/lib/platform/CXWindowsKeyMapper.cpp index e1e4ab9f..948f1e26 100644 --- a/lib/platform/CXWindowsKeyMapper.cpp +++ b/lib/platform/CXWindowsKeyMapper.cpp @@ -308,8 +308,8 @@ CXWindowsKeyMapper::updateKeysymMap(Display* display, IKeyState* keyState) } // get modifier map from server - XModifierKeymap* modifiers = XGetModifierMapping(display); - int keysPerModifier = modifiers->max_keypermod; + XModifierKeymap* modifiers = XGetModifierMapping(display); + unsigned int keysPerModifier = modifiers->max_keypermod; // clear state m_keysymMap.clear(); @@ -330,7 +330,6 @@ CXWindowsKeyMapper::updateKeysymMap(Display* display, IKeyState* keyState) // the keysym map. also collect all keycodes for each modifier. for (unsigned int i = 0; i < 8; ++i) { // no keycodes for this modifier yet - bool hasKeycode = false; KeyModifierMask mask = 0; IKeyState::KeyButtons modifierKeys; diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index 3b4f6104..8415a39d 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -128,8 +128,8 @@ CXWindowsScreen::CXWindowsScreen(bool isPrimary) : m_isOnScreen(m_isPrimary), m_x(0), m_y(0), m_w(0), m_h(0), - m_xCursor(0), m_yCursor(0), m_xCenter(0), m_yCenter(0), + m_xCursor(0), m_yCursor(0), m_keyState(NULL), m_keyMapper(), m_im(NULL), diff --git a/lib/platform/Makefile.am b/lib/platform/Makefile.am index 1aabff53..daef7e63 100644 --- a/lib/platform/Makefile.am +++ b/lib/platform/Makefile.am @@ -28,6 +28,7 @@ EXTRA_DIST = \ CMSWindowsKeyMapper.cpp \ CMSWindowsScreen.cpp \ CMSWindowsScreenSaver.cpp \ + CMSWindowsUtil.cpp \ CSynergyHook.cpp \ CMSWindowsClipboard.h \ CMSWindowsClipboardAnyTextConverter.h \ @@ -38,6 +39,7 @@ EXTRA_DIST = \ CMSWindowsKeyMapper.h \ CMSWindowsScreen.h \ CMSWindowsScreenSaver.h \ + CMSWindowsUtil.h \ CSynergyHook.h \ $(NULL) diff --git a/lib/platform/platform.dsp b/lib/platform/platform.dsp index 4dc638dd..123e513b 100644 --- a/lib/platform/platform.dsp +++ b/lib/platform/platform.dsp @@ -121,6 +121,10 @@ SOURCE=.\CMSWindowsScreen.cpp SOURCE=.\CMSWindowsScreenSaver.cpp # End Source File +# Begin Source File + +SOURCE=.\CMSWindowsUtil.cpp +# End Source File # End Group # Begin Group "Header Files" @@ -161,6 +165,10 @@ SOURCE=.\CMSWindowsScreen.h SOURCE=.\CMSWindowsScreenSaver.h # End Source File +# Begin Source File + +SOURCE=.\CMSWindowsUtil.h +# End Source File # End Group # End Target # End Project diff --git a/lib/server/CClientListener.cpp b/lib/server/CClientListener.cpp index 9a811a36..45f3a713 100644 --- a/lib/server/CClientListener.cpp +++ b/lib/server/CClientListener.cpp @@ -47,13 +47,13 @@ CClientListener::CClientListener(const CNetworkAddress& address, LOG((CLOG_DEBUG1 "binding listen socket")); m_listen->bind(address); } - catch (XSocketAddressInUse& e) { + catch (XSocketAddressInUse&) { delete m_listen; delete m_socketFactory; delete m_streamFilterFactory; throw; } - catch (XBase& e) { + catch (XBase&) { delete m_listen; delete m_socketFactory; delete m_streamFilterFactory; diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index 654ef383..d129ab96 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -37,8 +37,8 @@ CEvent::Type CServer::s_errorEvent = CEvent::kUnknown; CEvent::Type CServer::s_disconnectedEvent = CEvent::kUnknown; CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : - m_active(primaryClient), m_primaryClient(primaryClient), + m_active(primaryClient), m_seqNum(0), m_config(config), m_activeSaver(NULL), @@ -117,9 +117,6 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : // add connection addClient(m_primaryClient); - // tell it about the active sides - m_primaryClient->reconfigure(getActivePrimarySides()); - // tell primary client about its options sendOptions(m_primaryClient); @@ -171,8 +168,7 @@ bool CServer::setConfig(const CConfig& config) { // refuse configuration if it doesn't include the primary screen - if (m_primaryClient != NULL && - !config.isScreen(m_primaryClient->getName())) { + if (!config.isScreen(m_primaryClient->getName())) { return false; } @@ -185,9 +181,7 @@ CServer::setConfig(const CConfig& config) processOptions(); // tell primary screen about reconfiguration - if (m_primaryClient != NULL) { - m_primaryClient->reconfigure(getActivePrimarySides()); - } + m_primaryClient->reconfigure(getActivePrimarySides()); // tell all (connected) clients about current options for (CClientList::const_iterator index = m_clients.begin(); @@ -296,18 +290,17 @@ CServer::getName(const IClient* client) const UInt32 CServer::getActivePrimarySides() const { - CString primaryName = getName(m_primaryClient); UInt32 sides = 0; - if (!m_config.getNeighbor(primaryName, kLeft).empty()) { + if (getNeighbor(m_primaryClient, kLeft) != NULL) { sides |= kLeftMask; } - if (!m_config.getNeighbor(primaryName, kRight).empty()) { + if (getNeighbor(m_primaryClient, kRight) != NULL) { sides |= kRightMask; } - if (!m_config.getNeighbor(primaryName, kTop).empty()) { + if (getNeighbor(m_primaryClient, kTop) != NULL) { sides |= kTopMask; } - if (!m_config.getNeighbor(primaryName, kBottom).empty()) { + if (getNeighbor(m_primaryClient, kBottom) != NULL) { sides |= kBottomMask; } return sides; @@ -546,6 +539,10 @@ CServer::getNeighbor(IClient* src, assert(lastGoodScreen != NULL); y += dy; break; + + case kNoDirection: + assert(0 && "bad direction"); + return NULL; } // save destination screen @@ -582,6 +579,10 @@ CServer::getNeighbor(IClient* src, y < dy + getJumpZoneSize(dst)) y = dy + getJumpZoneSize(dst); break; + + case kNoDirection: + assert(0 && "bad direction"); + return NULL; } } @@ -623,6 +624,10 @@ CServer::getNeighbor(IClient* src, } x += dx; break; + + case kNoDirection: + assert(0 && "bad direction"); + return NULL; } return dst; @@ -999,13 +1004,13 @@ CServer::handleWheelEvent(const CEvent& event, void*) } void -CServer::handleScreensaverActivatedEvent(const CEvent& event, void*) +CServer::handleScreensaverActivatedEvent(const CEvent&, void*) { onScreensaver(true); } void -CServer::handleScreensaverDeactivatedEvent(const CEvent& event, void*) +CServer::handleScreensaverDeactivatedEvent(const CEvent&, void*) { onScreensaver(false); } @@ -1212,7 +1217,6 @@ CServer::onMouseMovePrimary(SInt32 x, SInt32 y) LOG((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y)); // mouse move on primary (server's) screen - assert(m_primaryClient != NULL); assert(m_active == m_primaryClient); // save position @@ -1429,6 +1433,10 @@ CServer::addClient(IClient* client) // add to list m_clientSet.insert(client); m_clients.insert(std::make_pair(name, client)); + + // tell primary client about the active sides + m_primaryClient->reconfigure(getActivePrimarySides()); + return true; } @@ -1452,6 +1460,7 @@ CServer::removeClient(IClient* client) // remove from list m_clients.erase(i); m_clientSet.erase(client); + return true; } @@ -1477,7 +1486,7 @@ CServer::closeClient(IClient* client, const char* msg) // install timer. wait timeout seconds for client to close. double timeout = 5.0; - CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(5.0, NULL); + CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(timeout, NULL); EVENTQUEUE->adoptHandler(CEvent::kTimer, timer, new TMethodEventJob(this, &CServer::handleClientCloseTimeout, client)); @@ -1577,6 +1586,9 @@ CServer::forceLeaveClient(IClient* client) if (m_activeSaver == client) { m_activeSaver = NULL; } + + // tell primary client about the active sides + m_primaryClient->reconfigure(getActivePrimarySides()); } diff --git a/lib/synergy/CProtocolUtil.cpp b/lib/synergy/CProtocolUtil.cpp index ecaf4f39..de7351b9 100644 --- a/lib/synergy/CProtocolUtil.cpp +++ b/lib/synergy/CProtocolUtil.cpp @@ -89,7 +89,7 @@ CProtocolUtil::vwritef(IStream* stream, } } -bool +void CProtocolUtil::vreadf(IStream* stream, const char* fmt, va_list args) { assert(stream != NULL); diff --git a/lib/synergy/CProtocolUtil.h b/lib/synergy/CProtocolUtil.h index 2ea1609f..d4019b0b 100644 --- a/lib/synergy/CProtocolUtil.h +++ b/lib/synergy/CProtocolUtil.h @@ -71,7 +71,7 @@ public: private: static void vwritef(IStream*, const char* fmt, UInt32 size, va_list); - static bool vreadf(IStream*, + static void vreadf(IStream*, const char* fmt, va_list); static UInt32 getLength(const char* fmt, va_list); diff --git a/lib/synergy/CScreen.cpp b/lib/synergy/CScreen.cpp index 9bbd4a82..a4164f55 100644 --- a/lib/synergy/CScreen.cpp +++ b/lib/synergy/CScreen.cpp @@ -27,8 +27,8 @@ CScreen::CScreen(IPlatformScreen* platformScreen) : m_isPrimary(platformScreen->isPrimary()), m_enabled(false), m_entered(m_isPrimary), - m_toggleKeys(0), - m_screenSaverSync(true) + m_screenSaverSync(true), + m_toggleKeys(0) { assert(m_screen != NULL); @@ -376,7 +376,7 @@ CScreen::setOptions(const COptionsList& options) void CScreen::setSequenceNumber(UInt32 seqNum) { - return m_screen->setSequenceNumber(seqNum); + m_screen->setSequenceNumber(seqNum); } bool diff --git a/lib/synergy/IScreen.h b/lib/synergy/IScreen.h index 7af925cf..2f1b4450 100644 --- a/lib/synergy/IScreen.h +++ b/lib/synergy/IScreen.h @@ -73,8 +73,7 @@ public: //! Get shape changed event type /*! Returns the shape changed event type. This is sent whenever the - screen's shape changes, the cursor center moves, or the jump zone - size changes. + screen's shape changes. */ static CEvent::Type getShapeChangedEvent();