diff --git a/CMakeLists.txt b/CMakeLists.txt index 9dd3a349..cd6a37c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ # Version number for Synergy+ SET(VERSION_MAJOR 1) -SET(VERSION_MINOR 3) -SET(VERSION_REV 5) +SET(VERSION_MINOR 4) +SET(VERSION_REV 0) SET(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") @@ -44,11 +44,28 @@ INCLUDE(${cmake_dir}/CMakeLists_cpack.txt) IF(WIN32) # add /analyze in order to unconver potential bugs in the source code # Details: http://msdn.microsoft.com/en-us/library/fwkeyyhe.aspx - SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /analyze") - SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /analyze") + # add /FR to generate browse information (ncb files) usefull for using IDE + + #define _BIND_TO_CURRENT_CRT_VERSION 1 + #define _BIND_TO_CURRENT_ATL_VERSION 1 + #define _BIND_TO_CURRENT_MFC_VERSION 1 + #define _BIND_TO_CURRENT_OPENMP_VERSION 1 + # next line replaced the previous 4 ones: + #define _BIND_TO_CURRENT_VCLIBS_VERSION 1; + + # compiler: /MP - use multi cores to compile + # added _SECURE_SCL=1 for finding bugs with iterators - http://msdn.microsoft.com/en-us/library/aa985965.aspx + + # common args between all vs builds + SET(VS_ARGS "/FR /MP /D _BIND_TO_CURRENT_VCLIBS_VERSION=1 /D _SECURE_SCL=1 ${VS_ARGS_EXTRA}") + + # we may use `cmake -D VS_ARGS_EXTRA="/analyze"` for example to specify + # analyze mode (since we don't always want to use it; e.g. on non-team + # or non-x86 compiler editions where there's no support) + SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${VS_ARGS}") + SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${VS_ARGS}") - # this line removes "/D NDEBUG" from release, we want them in order to find bugs even on release builds. - SET(CMAKE_CXX_FLAGS_RELEASE "/MD /O2 /Ob2") ENDIF(WIN32) - +# this line removes "/D NDEBUG" from release, we want them in order to find bugs even on release builds. +SET(CMAKE_CXX_FLAGS_RELEASE "/MD /O2 /Ob2") diff --git a/cmake/CMakeLists_cpack.txt b/cmake/CMakeLists_cpack.txt index 4762dcdf..418e30e6 100644 --- a/cmake/CMakeLists_cpack.txt +++ b/cmake/CMakeLists_cpack.txt @@ -17,6 +17,18 @@ INSTALL( TARGETS ${cpack_targets} RUNTIME DESTINATION bin) +IF(WIN32) + INSTALL( + FILES + bin/Release/QtNetwork4.dll + bin/Release/QtGui4.dll + bin/Release/QtCore4.dll + bin/Release/qsynergy.exe + bin/Release/mingwm10.dll + bin/Release/libgcc_s_dw2-1.dll + DESTINATION bin) +ENDIF(WIN32) + # The default CPack behaviour is not to append the system processor # type, which is undesirable in our case, since we want to support # both 32-bit and 64-bit processors. @@ -62,7 +74,7 @@ IF(WIN32) SET(CPACK_NSIS_MUI_UNIICON ${WIN32_ICON}) SET(CPACK_NSIS_INSTALLED_ICON_NAME launcher) SET(CPACK_PACKAGE_INSTALL_DIRECTORY "Synergy+") - SET(CPACK_PACKAGE_EXECUTABLES launcher;Synergy+) + SET(CPACK_PACKAGE_EXECUTABLES qsynergy;Synergy+) ENDIF(WIN32) # For source package, leave out temp and Mercurial stuff. diff --git a/cmake/CMakeLists_lib.txt b/cmake/CMakeLists_lib.txt index ff74ec14..7fb53de3 100644 --- a/cmake/CMakeLists_lib.txt +++ b/cmake/CMakeLists_lib.txt @@ -1,12 +1,15 @@ SET(root_lib ${root_dir}/lib) SET(src_lib_arch + ${root_lib}/arch/CArchAppUtil.cpp ${root_lib}/arch/CArch.cpp ${root_lib}/arch/CArchDaemonNone.cpp ${root_lib}/arch/XArch.cpp + ${root_lib}/arch/CArchConsoleStd.cpp ) SET(src_lib_arch_unix + ${root_lib}/arch/CArchAppUtilUnix.cpp ${root_lib}/arch/CArchConsoleUnix.cpp ${root_lib}/arch/CArchDaemonUnix.cpp ${root_lib}/arch/CArchFileUnix.cpp @@ -22,6 +25,7 @@ SET(src_lib_arch_unix ) SET(src_lib_arch_windows + ${root_lib}/arch/CArchAppUtilWindows.cpp ${root_lib}/arch/CArchConsoleWindows.cpp ${root_lib}/arch/CArchDaemonWindows.cpp ${root_lib}/arch/CArchFileWindows.cpp @@ -38,6 +42,8 @@ SET(src_lib_arch_windows ) SET(inc_lib_arch_windows + ${root_lib}/arch/CArchAppUtil.h + ${root_lib}/arch/CArchAppUtilWindows.h ${root_lib}/arch/CArchConsoleWindows.h ${root_lib}/arch/CArchDaemonWindows.h ${root_lib}/arch/CArchFileWindows.h @@ -50,6 +56,8 @@ SET(inc_lib_arch_windows ${root_lib}/arch/CArchSystemWindows.h ${root_lib}/arch/CArchTaskBarWindows.h ${root_lib}/arch/CArchTimeWindows.h + ${root_lib}/arch/CArchConsoleStd.h + ${root_lib}/arch/IArchAppUtil.h ${root_lib}/arch/XArchWindows.h ) @@ -264,6 +272,11 @@ SET(inc_lib_server ) SET(src_lib_synergy + ${root_lib}/synergy/CClientTaskBarReceiver.cpp + ${root_lib}/synergy/CServerTaskBarReceiver.cpp + ${root_lib}/synergy/CApp.cpp + ${root_lib}/synergy/CClientApp.cpp + ${root_lib}/synergy/CServerApp.cpp ${root_lib}/synergy/CClipboard.cpp ${root_lib}/synergy/CKeyMap.cpp ${root_lib}/synergy/CKeyState.cpp @@ -282,6 +295,11 @@ SET(src_lib_synergy ) SET(inc_lib_synergy + ${root_lib}/synergy/CClientTaskBarReceiver.h + ${root_lib}/synergy/CServerTaskBarReceiver.h + ${root_lib}/synergy/CApp.h + ${root_lib}/synergy/CClientApp.h + ${root_lib}/synergy/CServerApp.h ${root_lib}/synergy/CClipboard.h ${root_lib}/synergy/CKeyMap.h ${root_lib}/synergy/CKeyState.h @@ -324,9 +342,17 @@ IF(UNIX) LIST(APPEND src_lib ${src_lib_arch_unix}) IF(APPLE) - LIST(APPEND src_lib ${src_lib_platform_carbon}) + LIST(APPEND src_lib + ${src_lib_platform_carbon} + ${inc_lib_synergy_carbon} + ${src_lib_synergy_carbon} + ) ELSE(APPLE) - LIST(APPEND src_lib ${src_lib_platform_xwindows}) + LIST(APPEND src_lib + ${src_lib_platform_xwindows} + ${inc_lib_synergy_xwindows} + ${src_lib_synergy_xwindows} + ) ENDIF(APPLE) ENDIF(UNIX) @@ -345,6 +371,8 @@ IF(WIN32) ${src_lib_arch_windows} ${inc_lib_platform_mswindows} ${src_lib_platform_mswindows} + ${inc_lib_synergy_mswindows} + ${src_lib_synergy_mswindows} ) ENDIF(WIN32) diff --git a/cmake/CMakeLists_synergyc.txt b/cmake/CMakeLists_synergyc.txt index 4bf000c1..5bd3a0b5 100644 --- a/cmake/CMakeLists_synergyc.txt +++ b/cmake/CMakeLists_synergyc.txt @@ -1,7 +1,6 @@ SET(root_cmd_synergyc ${root_dir}/cmd/synergyc) SET(src_cmd_synergyc_common - ${root_cmd_synergyc}/CClientTaskBarReceiver.cpp ${root_cmd_synergyc}/synergyc.cpp ) @@ -66,5 +65,5 @@ SET(inc_dirs_cmd_synergyc ) INCLUDE_DIRECTORIES(${inc_dirs_cmd_synergyc}) -ADD_EXECUTABLE(synergyc WIN32 ${src_cmd_synergyc}) +ADD_EXECUTABLE(synergyc ${src_cmd_synergyc}) TARGET_LINK_LIBRARIES(synergyc synergy ${libs}) diff --git a/cmake/CMakeLists_synergys.txt b/cmake/CMakeLists_synergys.txt index d1768f6b..22ea2fe1 100644 --- a/cmake/CMakeLists_synergys.txt +++ b/cmake/CMakeLists_synergys.txt @@ -1,7 +1,6 @@ SET(root_cmd_synergys ${root_dir}/cmd/synergys) SET(src_cmd_synergys_common - ${root_cmd_synergys}/CServerTaskBarReceiver.cpp ${root_cmd_synergys}/synergys.cpp ) @@ -14,7 +13,6 @@ SET(src_cmd_synergys_mswindows ) SET(inc_cmd_synergys_mswindows - ${root_cmd_synergys}/CServerTaskBarReceiver.h ${root_cmd_synergys}/CXWindowsServerTaskBarReceiver.h ${root_cmd_synergys}/resource.h ${root_cmd_synergys}/COSXServerTaskBarReceiver.h @@ -69,5 +67,5 @@ SET(inc_dirs_cmd_synergys ) INCLUDE_DIRECTORIES(${inc_dirs_cmd_synergys}) -ADD_EXECUTABLE(synergys WIN32 ${src_cmd_synergys}) +ADD_EXECUTABLE(synergys ${src_cmd_synergys}) TARGET_LINK_LIBRARIES(synergys synergy ${libs}) diff --git a/cmd/launcher/CAutoStart.cpp b/cmd/launcher/CAutoStart.cpp index eb18a6b0..b57fb559 100644 --- a/cmd/launcher/CAutoStart.cpp +++ b/cmd/launcher/CAutoStart.cpp @@ -21,8 +21,8 @@ #include "LaunchUtil.h" #include "resource.h" -static const char* CLIENT_DAEMON_NAME = "Synergy Client"; -static const char* SERVER_DAEMON_NAME = "Synergy Server"; +static const char* CLIENT_DAEMON_NAME = "Synergy+ Client"; +static const char* SERVER_DAEMON_NAME = "Synergy+ Server"; static const char* CLIENT_DAEMON_INFO = "Uses a shared mouse and keyboard."; static const char* SERVER_DAEMON_INFO = "Shares this system's mouse and keyboard with others."; diff --git a/cmd/launcher/launcher.cpp b/cmd/launcher/launcher.cpp index 55109545..043293ae 100644 --- a/cmd/launcher/launcher.cpp +++ b/cmd/launcher/launcher.cpp @@ -675,7 +675,9 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR cmdLine, int nCmdShow) { - CArch arch(instance); + CArchMiscWindows::setInstanceWin32(instance); + + CArch arch; CLOG; CArgs args; diff --git a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp index 6f41bc17..689cc307 100644 --- a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +++ b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp @@ -19,7 +19,9 @@ #include "BasicTypes.h" #include "CArch.h" #include "CArchTaskBarWindows.h" +#include "CArchMiscWindows.h" #include "resource.h" +#include "CMSWindowsScreen.h" // // CMSWindowsClientTaskBarReceiver @@ -343,3 +345,20 @@ CMSWindowsClientTaskBarReceiver::staticDlgProc(HWND hwnd, return (msg == WM_INITDIALOG) ? TRUE : FALSE; } } + +IArchTaskBarReceiver* +createTaskBarReceiver(const CBufferedLogOutputter* logBuffer) +{ + CArchMiscWindows::setIcons( + (HICON)LoadImage(CArchMiscWindows::instanceWin32(), + MAKEINTRESOURCE(IDI_SYNERGY), + IMAGE_ICON, + 32, 32, LR_SHARED), + (HICON)LoadImage(CArchMiscWindows::instanceWin32(), + MAKEINTRESOURCE(IDI_SYNERGY), + IMAGE_ICON, + 16, 16, LR_SHARED)); + + return new CMSWindowsClientTaskBarReceiver( + CMSWindowsScreen::getInstance(), logBuffer); +} diff --git a/cmd/synergyc/COSXClientTaskBarReceiver.cpp b/cmd/synergyc/COSXClientTaskBarReceiver.cpp index c380ac4d..08d1fad7 100644 --- a/cmd/synergyc/COSXClientTaskBarReceiver.cpp +++ b/cmd/synergyc/COSXClientTaskBarReceiver.cpp @@ -54,3 +54,10 @@ COSXClientTaskBarReceiver::getIcon() const { return NULL; } + +IArchTaskBarReceiver* +createTaskBarReceiver(const CBufferedLogOutputter* logBuffer) +{ + return new COSXClientTaskBarReceiver(logBuffer); +} + diff --git a/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp index 681f9be5..965f698e 100644 --- a/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp +++ b/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp @@ -54,3 +54,9 @@ CXWindowsClientTaskBarReceiver::getIcon() const { return NULL; } + +IArchTaskBarReceiver* +createTaskBarReceiver(const CBufferedLogOutputter* logBuffer) +{ + return new CXWindowsClientTaskBarReceiver(logBuffer); +} diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 69959ba6..f1707e90 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -12,943 +12,21 @@ * GNU General Public License for more details. */ -#include "CClient.h" -#include "CScreen.h" -#include "ProtocolTypes.h" -#include "Version.h" -#include "XScreen.h" -#include "CNetworkAddress.h" -#include "CSocketMultiplexer.h" -#include "CTCPSocketFactory.h" -#include "XSocket.h" -#include "CThread.h" -#include "CEventQueue.h" -#include "CFunctionEventJob.h" -#include "CFunctionJob.h" -#include "CLog.h" -#include "CString.h" -#include "CStringUtil.h" -#include "LogOutputters.h" -#include "CArch.h" -#include "XArch.h" -#include +#include "CClientApp.h" -#define DAEMON_RUNNING(running_) #if WINAPI_MSWINDOWS -#include "CArchMiscWindows.h" -#include "CMSWindowsScreen.h" -#include "CMSWindowsUtil.h" #include "CMSWindowsClientTaskBarReceiver.h" -#include "resource.h" -#undef DAEMON_RUNNING -#define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_) #elif WINAPI_XWINDOWS -#include "CXWindowsScreen.h" #include "CXWindowsClientTaskBarReceiver.h" #elif WINAPI_CARBON -#include "COSXScreen.h" #include "COSXClientTaskBarReceiver.h" -#endif - -// platform dependent name of a daemon -#if SYSAPI_WIN32 -#define DAEMON_NAME "Synergy+ Client" -#elif SYSAPI_UNIX -#define DAEMON_NAME "synergyc" -#endif - -typedef int (*StartupFunc)(int, char**); -static bool startClient(); -static void parse(int argc, const char* const* argv); - -// -// program arguments -// - -#define ARG CArgs::s_instance - -class CArgs { -public: - CArgs() : - m_pname(NULL), - m_backend(false), - m_restartable(true), - m_daemon(true), - m_yscroll(0), - m_logFilter(NULL), - m_display(NULL), - m_serverAddress(NULL), - m_logFile(NULL) - { s_instance = this; } - ~CArgs() { s_instance = NULL; } - -public: - static CArgs* s_instance; - const char* m_pname; - bool m_backend; - bool m_restartable; - bool m_daemon; - int m_yscroll; - const char* m_logFilter; - const char* m_display; - CString m_name; - CNetworkAddress* m_serverAddress; - const char* m_logFile; - -}; - -CArgs* CArgs::s_instance = NULL; - - -// -// platform dependent factories -// - -static -CScreen* -createScreen() -{ -#if WINAPI_MSWINDOWS - return new CScreen(new CMSWindowsScreen(false)); -#elif WINAPI_XWINDOWS - return new CScreen(new CXWindowsScreen(ARG->m_display, false, ARG->m_yscroll)); -#elif WINAPI_CARBON - return new CScreen(new COSXScreen(false)); -#endif -} - -static -CClientTaskBarReceiver* -createTaskBarReceiver(const CBufferedLogOutputter* logBuffer) -{ -#if WINAPI_MSWINDOWS - return new CMSWindowsClientTaskBarReceiver( - CMSWindowsScreen::getInstance(), logBuffer); -#elif WINAPI_XWINDOWS - return new CXWindowsClientTaskBarReceiver(logBuffer); -#elif WINAPI_CARBON - return new COSXClientTaskBarReceiver(logBuffer); -#endif -} - - -// -// platform independent main -// - -static CClient* s_client = NULL; -static CScreen* s_clientScreen = NULL; -static CClientTaskBarReceiver* s_taskBarReceiver = NULL; -//static double s_retryTime = 0.0; -static bool s_suspened = false; - -#define RETRY_TIME 1.0 - -static -void -updateStatus() -{ - s_taskBarReceiver->updateStatus(s_client, ""); -} - -static -void -updateStatus(const CString& msg) -{ - s_taskBarReceiver->updateStatus(s_client, msg); -} - -static -void -resetRestartTimeout() -{ - // retry time can nolonger be changed - //s_retryTime = 0.0; -} - -static -double -nextRestartTimeout() -{ - // retry at a constant rate (Issue 52) - return RETRY_TIME; - - /* - // choose next restart timeout. we start with rapid retries - // then slow down. - if (s_retryTime < 1.0) { - s_retryTime = 1.0; - } - else if (s_retryTime < 3.0) { - s_retryTime = 3.0; - } - else { - s_retryTime = 5.0; - } - return s_retryTime; - */ -} - -static -void -handleScreenError(const CEvent&, void*) -{ - LOG((CLOG_CRIT "error on screen")); - EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); -} - -static -CScreen* -openClientScreen() -{ - CScreen* screen = createScreen(); - EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(), - screen->getEventTarget(), - new CFunctionEventJob( - &handleScreenError)); - return screen; -} - -static -void -closeClientScreen(CScreen* screen) -{ - if (screen != NULL) { - EVENTQUEUE->removeHandler(IScreen::getErrorEvent(), - screen->getEventTarget()); - delete screen; - } -} - -static -void -handleClientRestart(const CEvent&, void* vtimer) -{ - // discard old timer - CEventQueueTimer* timer = reinterpret_cast(vtimer); - EVENTQUEUE->deleteTimer(timer); - EVENTQUEUE->removeHandler(CEvent::kTimer, timer); - - // reconnect - startClient(); -} - -static -void -scheduleClientRestart(double retryTime) -{ - // install a timer and handler to retry later - LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime)); - CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL); - EVENTQUEUE->adoptHandler(CEvent::kTimer, timer, - new CFunctionEventJob(&handleClientRestart, timer)); -} - -static -void -handleClientConnected(const CEvent&, void*) -{ - LOG((CLOG_NOTE "connected to server")); - resetRestartTimeout(); - updateStatus(); -} - -static -void -handleClientFailed(const CEvent& e, void*) -{ - CClient::CFailInfo* info = - reinterpret_cast(e.getData()); - - updateStatus(CString("Failed to connect to server: ") + info->m_what); - if (!ARG->m_restartable || !info->m_retry) { - LOG((CLOG_ERR "failed to connect to server: %s", info->m_what)); - EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); - } - else { - LOG((CLOG_WARN "failed to connect to server: %s", info->m_what)); - if (!s_suspened) { - scheduleClientRestart(nextRestartTimeout()); - } - } -} - -static -void -handleClientDisconnected(const CEvent&, void*) -{ - LOG((CLOG_NOTE "disconnected from server")); - if (!ARG->m_restartable) { - EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); - } - else if (!s_suspened) { - s_client->connect(); - } - updateStatus(); -} - -static -CClient* -openClient(const CString& name, const CNetworkAddress& address, CScreen* screen) -{ - CClient* client = new CClient(name, address, - new CTCPSocketFactory, NULL, screen); - EVENTQUEUE->adoptHandler(CClient::getConnectedEvent(), - client->getEventTarget(), - new CFunctionEventJob(handleClientConnected)); - EVENTQUEUE->adoptHandler(CClient::getConnectionFailedEvent(), - client->getEventTarget(), - new CFunctionEventJob(handleClientFailed)); - EVENTQUEUE->adoptHandler(CClient::getDisconnectedEvent(), - client->getEventTarget(), - new CFunctionEventJob(handleClientDisconnected)); - return client; -} - -static -void -closeClient(CClient* client) -{ - if (client == NULL) { - return; - } - - EVENTQUEUE->removeHandler(CClient::getConnectedEvent(), client); - EVENTQUEUE->removeHandler(CClient::getConnectionFailedEvent(), client); - EVENTQUEUE->removeHandler(CClient::getDisconnectedEvent(), client); - delete client; -} - -static -bool -startClient() -{ - double retryTime; - CScreen* clientScreen = NULL; - try { - if (s_clientScreen == NULL) { - clientScreen = openClientScreen(); - s_client = openClient(ARG->m_name, - *ARG->m_serverAddress, clientScreen); - s_clientScreen = clientScreen; - LOG((CLOG_NOTE "started client")); - } - s_client->connect(); - updateStatus(); - return true; - } - catch (XScreenUnavailable& e) { - LOG((CLOG_WARN "cannot open secondary screen: %s", e.what())); - closeClientScreen(clientScreen); - updateStatus(CString("Cannot open secondary screen: ") + e.what()); - retryTime = e.getRetryTime(); - } - catch (XScreenOpenFailure& e) { - LOG((CLOG_CRIT "cannot open secondary screen: %s", e.what())); - closeClientScreen(clientScreen); - return false; - } - catch (XBase& e) { - LOG((CLOG_CRIT "failed to start client: %s", e.what())); - closeClientScreen(clientScreen); - return false; - } - - if (ARG->m_restartable) { - scheduleClientRestart(retryTime); - return true; - } - else { - // don't try again - return false; - } -} - -static -void -stopClient() -{ - closeClient(s_client); - closeClientScreen(s_clientScreen); - s_client = NULL; - s_clientScreen = NULL; -} - -static -int -mainLoop() -{ - // logging to files - CFileLogOutputter* fileLog = NULL; - - if (ARG->m_logFile != NULL) { - fileLog = new CFileLogOutputter(ARG->m_logFile); - - CLOG->insert(fileLog); - - LOG((CLOG_DEBUG1 "Logging to file (%s) enabled", ARG->m_logFile)); - } - - // create socket multiplexer. this must happen after daemonization - // on unix because threads evaporate across a fork(). - CSocketMultiplexer multiplexer; - - // create the event queue - CEventQueue eventQueue; - - // start the client. if this return false then we've failed and - // we shouldn't retry. - LOG((CLOG_DEBUG1 "starting client")); - if (!startClient()) { - return kExitFailed; - } - - // run event loop. if startClient() failed we're supposed to retry - // later. the timer installed by startClient() will take care of - // that. - CEvent event; - DAEMON_RUNNING(true); - EVENTQUEUE->getEvent(event); - while (event.getType() != CEvent::kQuit) { - EVENTQUEUE->dispatchEvent(event); - CEvent::deleteData(event); - EVENTQUEUE->getEvent(event); - } - DAEMON_RUNNING(false); - - // close down - LOG((CLOG_DEBUG1 "stopping client")); - stopClient(); - updateStatus(); - LOG((CLOG_NOTE "stopped client")); - - if (fileLog) { - CLOG->remove(fileLog); - delete fileLog; - } - - return kExitSuccess; -} - -static -int -daemonMainLoop(int, const char**) -{ -#if SYSAPI_WIN32 - CSystemLogger sysLogger(DAEMON_NAME, false); #else - CSystemLogger sysLogger(DAEMON_NAME, true); +#error Platform not supported. #endif - return mainLoop(); -} - -static -int -standardStartup(int argc, char** argv) -{ - if (!ARG->m_daemon) { - ARCH->showConsole(false); - } - - // parse command line - parse(argc, argv); - - // daemonize if requested - if (ARG->m_daemon) { - return ARCH->daemonize(DAEMON_NAME, &daemonMainLoop); - } - else { - return mainLoop(); - } -} - -static -int -run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup) -{ - // general initialization - 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; -} - - -// -// command line parsing -// - -#define BYE "\nTry `%s --help' for more information." - -static void (*bye)(int) = &exit; - -static -void -version() -{ - LOG((CLOG_PRINT "%s %s, protocol version %d.%d\n%s", - ARG->m_pname, - kVersion, - kProtocolMajorVersion, - kProtocolMinorVersion, - kCopyright)); -} - -static -void -help() -{ -#if WINAPI_XWINDOWS -# define USAGE_DISPLAY_ARG \ -" [--display ]" -# define USAGE_DISPLAY_INFO \ -" --display connect to the X server at \n" -#else -# define USAGE_DISPLAY_ARG -# define USAGE_DISPLAY_INFO -#endif - - LOG((CLOG_PRINT -"Usage: %s" -" [--daemon|--no-daemon]" -" [--debug ]" -USAGE_DISPLAY_ARG -" [--name ]" -" [--yscroll ]" -" [--restart|--no-restart]" -" " -"\n\n" -"Start the synergy mouse/keyboard sharing server.\n" -"\n" -" -d, --debug filter out log messages with priorty below level.\n" -" level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" -" DEBUG, DEBUG1, DEBUG2.\n" -USAGE_DISPLAY_INFO -" -f, --no-daemon run the client in the foreground.\n" -"* --daemon run the client as a daemon.\n" -" -n, --name use screen-name instead the hostname to identify\n" -" ourself to the server.\n" -" --yscroll defines the vertical scrolling delta, which is\n" -" 120 by default.\n" -" -1, --no-restart do not try to restart the client if it fails for\n" -" some reason.\n" -"* --restart restart the client automatically if it fails.\n" -" -l --log write log messages to file.\n" -" -h, --help display this help and exit.\n" -" --version display version information and exit.\n" -"\n" -"* marks defaults.\n" -"\n" -"The server address is of the form: [][:]. The hostname\n" -"must be the address or hostname of the server. The port overrides the\n" -"default port, %d.\n" -"\n" -"Where log messages go depends on the platform and whether or not the\n" -"client is running as a daemon.", - ARG->m_pname, kDefaultPort)); - -} - -static -bool -isArg(int argi, int argc, const char* const* argv, - const char* name1, const char* name2, - int minRequiredParameters = 0) -{ - if ((name1 != NULL && strcmp(argv[argi], name1) == 0) || - (name2 != NULL && strcmp(argv[argi], name2) == 0)) { - // match. check args left. - if (argi + minRequiredParameters >= argc) { - LOG((CLOG_PRINT "%s: missing arguments for `%s'" BYE, - ARG->m_pname, argv[argi], ARG->m_pname)); - bye(kExitArgs); - } - return true; - } - - // no match - return false; -} - -static -void -parse(int argc, const char* const* argv) -{ - assert(ARG->m_pname != NULL); - assert(argv != NULL); - assert(argc >= 1); - - if(ARG->m_pname == NULL - || argv == NULL - || argc < 1) { - return; - } - - // set defaults - ARG->m_name = ARCH->getHostName(); - - // parse options - int i; - for (i = 1; i < argc; ++i) { - if (isArg(i, argc, argv, "-d", "--debug", 1)) { - // change logging level - ARG->m_logFilter = argv[++i]; - } - - else if (isArg(i, argc, argv, "-n", "--name", 1)) { - // save screen name - ARG->m_name = argv[++i]; - } - - else if (isArg(i, argc, argv, NULL, "--camp")) { - // ignore -- included for backwards compatibility - } - - else if (isArg(i, argc, argv, NULL, "--no-camp")) { - // ignore -- included for backwards compatibility - } - - else if (isArg(i, argc, argv, "-f", "--no-daemon")) { - // not a daemon - ARG->m_daemon = false; - } - - else if (isArg(i, argc, argv, NULL, "--daemon")) { - // daemonize - ARG->m_daemon = true; - } - -#if WINAPI_XWINDOWS - else if (isArg(i, argc, argv, "-display", "--display", 1)) { - // use alternative display - ARG->m_display = argv[++i]; - } -#endif - - else if (isArg(i, argc, argv, NULL, "--yscroll", 1)) { - // define scroll - ARG->m_yscroll = atoi(argv[++i]); - } - - else if (isArg(i, argc, argv, "-l", "--log", 1)) { - ARG->m_logFile = argv[++i]; - } - - else if (isArg(i, argc, argv, "-1", "--no-restart")) { - // don't try to restart - ARG->m_restartable = false; - } - - else if (isArg(i, argc, argv, NULL, "--restart")) { - // try to restart - ARG->m_restartable = true; - } - - else if (isArg(i, argc, argv, "-z", NULL)) { - ARG->m_backend = true; - } - - else if (isArg(i, argc, argv, "-h", "--help")) { - help(); - bye(kExitSuccess); - } - - else if (isArg(i, argc, argv, NULL, "--version")) { - version(); - bye(kExitSuccess); - } - - else if (isArg(i, argc, argv, "--", NULL)) { - // remaining arguments are not options - ++i; - break; - } - - else if (argv[i][0] == '-') { - LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, - ARG->m_pname, argv[i], ARG->m_pname)); - bye(kExitArgs); - } - - else { - // this and remaining arguments are not options - break; - } - } - - // exactly one non-option argument (server-address) - if (i == argc) { - LOG((CLOG_PRINT "%s: a server address or name is required" BYE, - ARG->m_pname, ARG->m_pname)); - bye(kExitArgs); - } - if (i + 1 != argc) { - LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, - ARG->m_pname, argv[i], ARG->m_pname)); - bye(kExitArgs); - } - - // save server address - try { - *ARG->m_serverAddress = CNetworkAddress(argv[i], kDefaultPort); - ARG->m_serverAddress->resolve(); - } - catch (XSocketAddress& e) { - // allow an address that we can't look up if we're restartable. - // we'll try to resolve the address each time we connect to the - // server. a bad port will never get better. patch by Brent - // Priddy. - if (!ARG->m_restartable || e.getError() == XSocketAddress::kBadPort) { - LOG((CLOG_PRINT "%s: %s" BYE, - ARG->m_pname, e.what(), ARG->m_pname)); - bye(kExitFailed); - } - } - - // increase default filter level for daemon. the user must - // explicitly request another level for a daemon. - if (ARG->m_daemon && ARG->m_logFilter == NULL) { -#if SYSAPI_WIN32 - if (CArchMiscWindows::isWindows95Family()) { - // windows 95 has no place for logging so avoid showing - // the log console window. - ARG->m_logFilter = "FATAL"; - } - else -#endif - { - ARG->m_logFilter = "NOTE"; - } - } - - // set log filter - if (!CLOG->setFilter(ARG->m_logFilter)) { - LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE, - ARG->m_pname, ARG->m_logFilter, ARG->m_pname)); - bye(kExitArgs); - } - - // identify system - LOG((CLOG_INFO "%s Client on %s %s", kAppVersion, ARCH->getOSName().c_str(), ARCH->getPlatformName().c_str())); - -#ifdef WIN32 -#ifdef _AMD64_ - LOG((CLOG_WARN "This is an experimental x64 build of %s. Use it at your own risk.", kApplication)); -#endif -#endif -} - - -// -// platform dependent entry points -// - -#if SYSAPI_WIN32 -static bool s_hasImportantLogMessages = false; - -// -// CMessageBoxOutputter -// -// This class writes severe log messages to a message box -// - -class CMessageBoxOutputter : public ILogOutputter { -public: - CMessageBoxOutputter() { } - virtual ~CMessageBoxOutputter() { } - - // ILogOutputter overrides - virtual void open(const char*) { } - virtual void close() { } - virtual void show(bool) { } - virtual bool write(ELevel level, const char* message); - virtual const char* getNewline() const { return ""; } -}; - -bool -CMessageBoxOutputter::write(ELevel level, const char* message) -{ - // note any important messages the user may need to know about - if (level <= CLog::kWARNING) { - s_hasImportantLogMessages = true; - } - - // FATAL and PRINT messages get a dialog box if not running as - // backend. if we're running as a backend the user will have - // a chance to see the messages when we exit. - if (!ARG->m_backend && level <= CLog::kFATAL) { - MessageBox(NULL, message, ARG->m_pname, MB_OK | MB_ICONWARNING); - return false; - } - else { - return true; - } -} - -static -void -byeThrow(int x) -{ - CArchMiscWindows::daemonFailed(x); -} - -static -int -daemonNTMainLoop(int argc, const char** argv) -{ - parse(argc, argv); - ARG->m_backend = false; - return CArchMiscWindows::runDaemon(mainLoop); -} - -static -int -daemonNTStartup(int, char**) -{ - CSystemLogger sysLogger(DAEMON_NAME, false); - bye = &byeThrow; - return ARCH->daemonize(DAEMON_NAME, &daemonNTMainLoop); -} - -static -int -foregroundStartup(int argc, char** argv) -{ - ARCH->showConsole(false); - - // parse command line - parse(argc, argv); - - // never daemonize - return mainLoop(); -} - -static -void -showError(HINSTANCE instance, const char* title, UINT id, const char* arg) -{ - 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) -{ - try { - CArchMiscWindows::setIcons((HICON)LoadImage(instance, - MAKEINTRESOURCE(IDI_SYNERGY), - IMAGE_ICON, - 32, 32, LR_SHARED), - (HICON)LoadImage(instance, - MAKEINTRESOURCE(IDI_SYNERGY), - IMAGE_ICON, - 16, 16, LR_SHARED)); - CArch arch(instance); - CMSWindowsScreen::init(instance); - CLOG; - CThread::getCurrentThread().setPriority(-14); - CArgs args; - - // set title on log window - ARCH->openConsole((CString(kAppVersion) + " " + "Client").c_str()); - - // 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 (!CArchMiscWindows::isWindows95Family()) { - if (__argc <= 1) { - startup = &daemonNTStartup; - } - else { - startup = &foregroundStartup; - } - } - - // 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()); - } - catch (...) { - showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, ""); - //throw; - } - return kExitFailed; -} - -#elif SYSAPI_UNIX int -main(int argc, char** argv) +main(int argc, char** argv) { - CArgs args; - try { - int result; - CArch arch; - CLOG; - CArgs args; - result = run(argc, argv, NULL, &standardStartup); - delete CLOG; - return result; - } - 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; - } + CClientApp app; + return app.run(argc, argv, createTaskBarReceiver); } - -#else - -#error no main() for platform - -#endif diff --git a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp index 50bcc638..97ba1a36 100644 --- a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +++ b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp @@ -21,10 +21,8 @@ #include "CArch.h" #include "CArchTaskBarWindows.h" #include "resource.h" - -extern CEvent::Type getReloadConfigEvent(); -extern CEvent::Type getForceReconnectEvent(); -extern CEvent::Type getResetServerEvent(); +#include "CArchMiscWindows.h" +#include "CMSWindowsScreen.h" // // CMSWindowsServerTaskBarReceiver @@ -378,3 +376,20 @@ CMSWindowsServerTaskBarReceiver::staticDlgProc(HWND hwnd, return (msg == WM_INITDIALOG) ? TRUE : FALSE; } } + +IArchTaskBarReceiver* +createTaskBarReceiver(const CBufferedLogOutputter* logBuffer) +{ + CArchMiscWindows::setIcons( + (HICON)LoadImage(CArchMiscWindows::instanceWin32(), + MAKEINTRESOURCE(IDI_SYNERGY), + IMAGE_ICON, + 32, 32, LR_SHARED), + (HICON)LoadImage(CArchMiscWindows::instanceWin32(), + MAKEINTRESOURCE(IDI_SYNERGY), + IMAGE_ICON, + 16, 16, LR_SHARED)); + + return new CMSWindowsServerTaskBarReceiver( + CMSWindowsScreen::getInstance(), logBuffer); +} diff --git a/cmd/synergys/COSXServerTaskBarReceiver.cpp b/cmd/synergys/COSXServerTaskBarReceiver.cpp index 8195b84f..4770478b 100644 --- a/cmd/synergys/COSXServerTaskBarReceiver.cpp +++ b/cmd/synergys/COSXServerTaskBarReceiver.cpp @@ -54,3 +54,9 @@ COSXServerTaskBarReceiver::getIcon() const { return NULL; } + +IArchTaskBarReceiver* +createTaskBarReceiver(const CBufferedLogOutputter* logBuffer) +{ + return new COSXServerTaskBarReceiver(logBuffer); +} diff --git a/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp b/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp index 861d2f8c..61db20df 100644 --- a/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp +++ b/cmd/synergys/CXWindowsServerTaskBarReceiver.cpp @@ -54,3 +54,9 @@ CXWindowsServerTaskBarReceiver::getIcon() const { return NULL; } + +IArchTaskBarReceiver* +createTaskBarReceiver(const CBufferedLogOutputter* logBuffer) +{ + return new CXWindowsServerTaskBarReceiver(logBuffer); +} diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index dcf2da92..bee31152 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -12,1351 +12,21 @@ * GNU General Public License for more details. */ -#include "CClientListener.h" -#include "CClientProxy.h" -#include "CConfig.h" -#include "CPrimaryClient.h" -#include "CServer.h" -#include "CScreen.h" -#include "ProtocolTypes.h" -#include "Version.h" -#include "XScreen.h" -#include "CSocketMultiplexer.h" -#include "CTCPSocketFactory.h" -#include "XSocket.h" -#include "CThread.h" -#include "CEventQueue.h" -#include "CFunctionEventJob.h" -#include "CLog.h" -#include "CString.h" -#include "CStringUtil.h" -#include "LogOutputters.h" -#include "CArch.h" -#include "XArch.h" -#include "stdfstream.h" -#include +#include "CServerApp.h" -#define DAEMON_RUNNING(running_) #if WINAPI_MSWINDOWS -#include "CArchMiscWindows.h" -#include "CMSWindowsScreen.h" -#include "CMSWindowsUtil.h" #include "CMSWindowsServerTaskBarReceiver.h" -#include "resource.h" -#undef DAEMON_RUNNING -#define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_) #elif WINAPI_XWINDOWS -#include "CXWindowsScreen.h" #include "CXWindowsServerTaskBarReceiver.h" #elif WINAPI_CARBON -#include "COSXScreen.h" #include "COSXServerTaskBarReceiver.h" -#endif - -// platform dependent name of a daemon -#if SYSAPI_WIN32 -#define DAEMON_NAME "Synergy+ Server" -#elif SYSAPI_UNIX -#define DAEMON_NAME "synergys" -#endif - -// configuration file name -#if SYSAPI_WIN32 -#define USR_CONFIG_NAME "synergy.sgc" -#define SYS_CONFIG_NAME "synergy.sgc" -#elif SYSAPI_UNIX -#define USR_CONFIG_NAME ".synergy.conf" -#define SYS_CONFIG_NAME "synergy.conf" -#endif - -typedef int (*StartupFunc)(int, char**); -static void parse(int argc, const char* const* argv); -static bool loadConfig(const CString& pathname); -static void loadConfig(); - -// -// program arguments -// - -#define ARG CArgs::s_instance - -class CArgs { -public: - CArgs() : - m_pname(NULL), - m_backend(false), - m_restartable(true), - m_daemon(true), - m_configFile(), - m_logFilter(NULL), - m_logFile(NULL), - m_display(NULL), - m_synergyAddress(NULL), - m_config(NULL) - { s_instance = this; } - ~CArgs() { s_instance = NULL; } - -public: - static CArgs* s_instance; - const char* m_pname; - bool m_backend; - bool m_restartable; - bool m_daemon; - CString m_configFile; - const char* m_logFilter; - const char* m_logFile; - const char* m_display; - CString m_name; - CNetworkAddress* m_synergyAddress; - CConfig* m_config; -}; - -CArgs* CArgs::s_instance = NULL; - - -// -// platform dependent factories -// - -static -CScreen* -createScreen() -{ -#if WINAPI_MSWINDOWS - return new CScreen(new CMSWindowsScreen(true)); -#elif WINAPI_XWINDOWS - return new CScreen(new CXWindowsScreen(ARG->m_display, true)); -#elif WINAPI_CARBON - return new CScreen(new COSXScreen(true)); -#endif -} - -static -CServerTaskBarReceiver* -createTaskBarReceiver(const CBufferedLogOutputter* logBuffer) -{ -#if WINAPI_MSWINDOWS - return new CMSWindowsServerTaskBarReceiver( - CMSWindowsScreen::getInstance(), logBuffer); -#elif WINAPI_XWINDOWS - return new CXWindowsServerTaskBarReceiver(logBuffer); -#elif WINAPI_CARBON - return new COSXServerTaskBarReceiver(logBuffer); -#endif -} - - -// -// platform independent main -// - -enum EServerState { - kUninitialized, - kInitializing, - kInitializingToStart, - kInitialized, - kStarting, - kStarted -}; - -static EServerState s_serverState = kUninitialized; -static CServer* s_server = NULL; -static CScreen* s_serverScreen = NULL; -static CPrimaryClient* s_primaryClient = NULL; -static CClientListener* s_listener = NULL; -static CServerTaskBarReceiver* s_taskBarReceiver = NULL; -static CEvent::Type s_reloadConfigEvent = CEvent::kUnknown; -static CEvent::Type s_forceReconnectEvent = CEvent::kUnknown; -static CEvent::Type s_resetServerEvent = CEvent::kUnknown; -static bool s_suspended = false; -static CEventQueueTimer* s_timer = NULL; - -CEvent::Type -getReloadConfigEvent() -{ - return CEvent::registerTypeOnce(s_reloadConfigEvent, "reloadConfig"); -} - -CEvent::Type -getForceReconnectEvent() -{ - return CEvent::registerTypeOnce(s_forceReconnectEvent, "forceReconnect"); -} - -CEvent::Type -getResetServerEvent() -{ - return CEvent::registerTypeOnce(s_resetServerEvent, "resetServer"); -} - -static -void -updateStatus() -{ - s_taskBarReceiver->updateStatus(s_server, ""); -} - -static -void -updateStatus(const CString& msg) -{ - s_taskBarReceiver->updateStatus(s_server, msg); -} - -static -void -handleClientConnected(const CEvent&, void* vlistener) -{ - CClientListener* listener = reinterpret_cast(vlistener); - CClientProxy* client = listener->getNextClient(); - if (client != NULL) { - s_server->adoptClient(client); - updateStatus(); - } -} - -static -CClientListener* -openClientListener(const CNetworkAddress& address) -{ - CClientListener* listen = - new CClientListener(address, new CTCPSocketFactory, NULL); - EVENTQUEUE->adoptHandler(CClientListener::getConnectedEvent(), listen, - new CFunctionEventJob( - &handleClientConnected, listen)); - return listen; -} - -static -void -closeClientListener(CClientListener* listen) -{ - if (listen != NULL) { - EVENTQUEUE->removeHandler(CClientListener::getConnectedEvent(), listen); - delete listen; - } -} - -static -void -handleScreenError(const CEvent&, void*) -{ - LOG((CLOG_CRIT "error on screen")); - EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); -} - - -static void handleSuspend(const CEvent& event, void*); -static void handleResume(const CEvent& event, void*); - -static -CScreen* -openServerScreen() -{ - CScreen* screen = createScreen(); - EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(), - screen->getEventTarget(), - new CFunctionEventJob( - &handleScreenError)); - EVENTQUEUE->adoptHandler(IScreen::getSuspendEvent(), - screen->getEventTarget(), - new CFunctionEventJob( - &handleSuspend)); - EVENTQUEUE->adoptHandler(IScreen::getResumeEvent(), - screen->getEventTarget(), - new CFunctionEventJob( - &handleResume)); - return screen; -} - -static -void -closeServerScreen(CScreen* screen) -{ - if (screen != NULL) { - EVENTQUEUE->removeHandler(IScreen::getErrorEvent(), - screen->getEventTarget()); - EVENTQUEUE->removeHandler(IScreen::getSuspendEvent(), - screen->getEventTarget()); - EVENTQUEUE->removeHandler(IScreen::getResumeEvent(), - screen->getEventTarget()); - delete screen; - } -} - -static -CPrimaryClient* -openPrimaryClient(const CString& name, CScreen* screen) -{ - LOG((CLOG_DEBUG1 "creating primary screen")); - return new CPrimaryClient(name, screen); -} - -static -void -closePrimaryClient(CPrimaryClient* primaryClient) -{ - delete primaryClient; -} - -static -void -handleNoClients(const CEvent&, void*) -{ - updateStatus(); -} - -static -void -handleClientsDisconnected(const CEvent&, void*) -{ - EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); -} - -static -CServer* -openServer(const CConfig& config, CPrimaryClient* primaryClient) -{ - CServer* server = new CServer(config, primaryClient); - EVENTQUEUE->adoptHandler(CServer::getDisconnectedEvent(), server, - new CFunctionEventJob(handleNoClients)); - return server; -} - -static -void -closeServer(CServer* server) -{ - if (server == NULL) { - return; - } - - // tell all clients to disconnect - server->disconnect(); - - // wait for clients to disconnect for up to timeout seconds - double timeout = 3.0; - CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(timeout, NULL); - EVENTQUEUE->adoptHandler(CEvent::kTimer, timer, - new CFunctionEventJob(handleClientsDisconnected)); - EVENTQUEUE->adoptHandler(CServer::getDisconnectedEvent(), server, - new CFunctionEventJob(handleClientsDisconnected)); - CEvent event; - EVENTQUEUE->getEvent(event); - while (event.getType() != CEvent::kQuit) { - EVENTQUEUE->dispatchEvent(event); - CEvent::deleteData(event); - EVENTQUEUE->getEvent(event); - } - EVENTQUEUE->removeHandler(CEvent::kTimer, timer); - EVENTQUEUE->deleteTimer(timer); - EVENTQUEUE->removeHandler(CServer::getDisconnectedEvent(), server); - - // done with server - delete server; -} - -static bool initServer(); -static bool startServer(); - -static -void -stopRetryTimer() -{ - if (s_timer != NULL) { - EVENTQUEUE->deleteTimer(s_timer); - EVENTQUEUE->removeHandler(CEvent::kTimer, NULL); - s_timer = NULL; - } -} - -static -void -retryHandler(const CEvent&, void*) -{ - // discard old timer - assert(s_timer != NULL); - stopRetryTimer(); - - // try initializing/starting the server again - switch (s_serverState) { - case kUninitialized: - case kInitialized: - case kStarted: - assert(0 && "bad internal server state"); - break; - - case kInitializing: - LOG((CLOG_DEBUG1 "retry server initialization")); - s_serverState = kUninitialized; - if (!initServer()) { - EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); - } - break; - - case kInitializingToStart: - LOG((CLOG_DEBUG1 "retry server initialization")); - s_serverState = kUninitialized; - if (!initServer()) { - EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); - } - else if (s_serverState == kInitialized) { - LOG((CLOG_DEBUG1 "starting server")); - if (!startServer()) { - EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); - } - } - break; - - case kStarting: - LOG((CLOG_DEBUG1 "retry starting server")); - s_serverState = kInitialized; - if (!startServer()) { - EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); - } - break; - } -} - -static -bool -initServer() -{ - // skip if already initialized or initializing - if (s_serverState != kUninitialized) { - return true; - } - - double retryTime; - CScreen* serverScreen = NULL; - CPrimaryClient* primaryClient = NULL; - try { - CString name = ARG->m_config->getCanonicalName(ARG->m_name); - serverScreen = openServerScreen(); - primaryClient = openPrimaryClient(name, serverScreen); - s_serverScreen = serverScreen; - s_primaryClient = primaryClient; - s_serverState = kInitialized; - updateStatus(); - return true; - } - catch (XScreenUnavailable& e) { - LOG((CLOG_WARN "cannot open primary screen: %s", e.what())); - closePrimaryClient(primaryClient); - closeServerScreen(serverScreen); - updateStatus(CString("cannot open primary screen: ") + e.what()); - retryTime = e.getRetryTime(); - } - catch (XScreenOpenFailure& e) { - LOG((CLOG_CRIT "cannot open primary screen: %s", e.what())); - closePrimaryClient(primaryClient); - closeServerScreen(serverScreen); - return false; - } - catch (XBase& e) { - LOG((CLOG_CRIT "failed to start server: %s", e.what())); - closePrimaryClient(primaryClient); - closeServerScreen(serverScreen); - return false; - } - - if (ARG->m_restartable) { - // install a timer and handler to retry later - assert(s_timer == NULL); - LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime)); - s_timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL); - EVENTQUEUE->adoptHandler(CEvent::kTimer, s_timer, - new CFunctionEventJob(&retryHandler, NULL)); - s_serverState = kInitializing; - return true; - } - else { - // don't try again - return false; - } -} - -static -bool -startServer() -{ - // skip if already started or starting - if (s_serverState == kStarting || s_serverState == kStarted) { - return true; - } - - // initialize if necessary - if (s_serverState != kInitialized) { - if (!initServer()) { - // hard initialization failure - return false; - } - if (s_serverState == kInitializing) { - // not ready to start - s_serverState = kInitializingToStart; - return true; - } - assert(s_serverState == kInitialized); - } - - double retryTime; - CClientListener* listener = NULL; - try { - listener = openClientListener(ARG->m_config->getSynergyAddress()); - s_server = openServer(*ARG->m_config, s_primaryClient); - s_listener = listener; - updateStatus(); - LOG((CLOG_NOTE "started server")); - s_serverState = kStarted; - return true; - } - catch (XSocketAddressInUse& e) { - LOG((CLOG_WARN "cannot listen for clients: %s", e.what())); - closeClientListener(listener); - updateStatus(CString("cannot listen for clients: ") + e.what()); - retryTime = 10.0; - } - catch (XBase& e) { - LOG((CLOG_CRIT "failed to start server: %s", e.what())); - closeClientListener(listener); - return false; - } - - if (ARG->m_restartable) { - // install a timer and handler to retry later - assert(s_timer == NULL); - LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime)); - s_timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL); - EVENTQUEUE->adoptHandler(CEvent::kTimer, s_timer, - new CFunctionEventJob(&retryHandler, NULL)); - s_serverState = kStarting; - return true; - } - else { - // don't try again - return false; - } -} - -static -void -stopServer() -{ - if (s_serverState == kStarted) { - closeClientListener(s_listener); - closeServer(s_server); - s_server = NULL; - s_listener = NULL; - s_serverState = kInitialized; - } - else if (s_serverState == kStarting) { - stopRetryTimer(); - s_serverState = kInitialized; - } - assert(s_server == NULL); - assert(s_listener == NULL); -} - -static -void -cleanupServer() -{ - stopServer(); - if (s_serverState == kInitialized) { - closePrimaryClient(s_primaryClient); - closeServerScreen(s_serverScreen); - s_primaryClient = NULL; - s_serverScreen = NULL; - s_serverState = kUninitialized; - } - else if (s_serverState == kInitializing || - s_serverState == kInitializingToStart) { - stopRetryTimer(); - s_serverState = kUninitialized; - } - assert(s_primaryClient == NULL); - assert(s_serverScreen == NULL); - assert(s_serverState == kUninitialized); -} - -static -void -handleSuspend(const CEvent&, void*) -{ - if (!s_suspended) { - LOG((CLOG_INFO "suspend")); - stopServer(); - s_suspended = true; - } -} - -static -void -handleResume(const CEvent&, void*) -{ - if (s_suspended) { - LOG((CLOG_INFO "resume")); - startServer(); - s_suspended = false; - } -} - -static -void -reloadSignalHandler(CArch::ESignal, void*) -{ - EVENTQUEUE->addEvent(CEvent(getReloadConfigEvent(), - IEventQueue::getSystemTarget())); -} - -static -void -reloadConfig(const CEvent&, void*) -{ - LOG((CLOG_DEBUG "reload configuration")); - if (loadConfig(ARG->m_configFile)) { - if (s_server != NULL) { - s_server->setConfig(*ARG->m_config); - } - LOG((CLOG_NOTE "reloaded configuration")); - } -} - -static -void -forceReconnect(const CEvent&, void*) -{ - if (s_server != NULL) { - s_server->disconnect(); - } -} - -// simply stops and starts the server in order to try and -// work around issues like the sticky meta keys problem, etc -static -void -resetServer(const CEvent&, void*) -{ - LOG((CLOG_DEBUG1 "resetting server")); - stopServer(); - cleanupServer(); - startServer(); -} - -static -int -mainLoop() -{ - // create socket multiplexer. this must happen after daemonization - // on unix because threads evaporate across a fork(). - CSocketMultiplexer multiplexer; - - // create the event queue - CEventQueue eventQueue; - - // logging to files - CFileLogOutputter* fileLog = NULL; - - if (ARG->m_logFile != NULL) { - fileLog = new CFileLogOutputter(ARG->m_logFile); - - CLOG->insert(fileLog); - - LOG((CLOG_DEBUG1 "Logging to file (%s) enabled", ARG->m_logFile)); - } - - // 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); - } - - // 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); - } - 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); - if (primaryName.empty()) { - LOG((CLOG_CRIT "unknown screen name `%s'", ARG->m_name.c_str())); - return kExitFailed; - } - - // start the server. if this return false then we've failed and - // we shouldn't retry. - LOG((CLOG_DEBUG1 "starting server")); - if (!startServer()) { - return kExitFailed; - } - - // handle hangup signal by reloading the server's configuration - ARCH->setSignalHandler(CArch::kHANGUP, &reloadSignalHandler, NULL); - EVENTQUEUE->adoptHandler(getReloadConfigEvent(), - IEventQueue::getSystemTarget(), - new CFunctionEventJob(&reloadConfig)); - - // handle force reconnect event by disconnecting clients. they'll - // reconnect automatically. - EVENTQUEUE->adoptHandler(getForceReconnectEvent(), - IEventQueue::getSystemTarget(), - new CFunctionEventJob(&forceReconnect)); - - // to work around the sticky meta keys problem, we'll give users - // the option to reset the state of synergys - EVENTQUEUE->adoptHandler(getResetServerEvent(), - IEventQueue::getSystemTarget(), - new CFunctionEventJob(&resetServer)); - - // run event loop. if startServer() failed we're supposed to retry - // later. the timer installed by startServer() will take care of - // that. - CEvent event; - DAEMON_RUNNING(true); - EVENTQUEUE->getEvent(event); - while (event.getType() != CEvent::kQuit) { - EVENTQUEUE->dispatchEvent(event); - CEvent::deleteData(event); - EVENTQUEUE->getEvent(event); - } - DAEMON_RUNNING(false); - - // close down - LOG((CLOG_DEBUG1 "stopping server")); - EVENTQUEUE->removeHandler(getForceReconnectEvent(), - IEventQueue::getSystemTarget()); - EVENTQUEUE->removeHandler(getReloadConfigEvent(), - IEventQueue::getSystemTarget()); - cleanupServer(); - updateStatus(); - LOG((CLOG_NOTE "stopped server")); - - if (fileLog) { - CLOG->remove(fileLog); - delete fileLog; - } - - return kExitSuccess; -} - -static -int -daemonMainLoop(int, const char**) -{ -#if SYSAPI_WIN32 - CSystemLogger sysLogger(DAEMON_NAME, false); #else - CSystemLogger sysLogger(DAEMON_NAME, true); +#error Platform not supported. #endif - return mainLoop(); -} - -static -int -standardStartup(int argc, char** argv) -{ - - // parse command line - parse(argc, argv); - - // load configuration - loadConfig(); - - // daemonize if requested - if (ARG->m_daemon) { - return ARCH->daemonize(DAEMON_NAME, &daemonMainLoop); - } - else { - return mainLoop(); - } -} - -static -int -run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup) -{ - // general initialization - 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 -// - -#define BYE "\nTry `%s --help' for more information." - -static void (*bye)(int) = &exit; - -static -void -version() -{ - LOG((CLOG_PRINT -"%s %s, protocol version %d.%d\n" -"%s", - ARG->m_pname, - kVersion, - kProtocolMajorVersion, - kProtocolMinorVersion, - kCopyright)); -} - -static -void -help() -{ -#if WINAPI_XWINDOWS -# define USAGE_DISPLAY_ARG \ -" [--display ]" -# define USAGE_DISPLAY_INFO \ -" --display connect to the X server at \n" -#else -# define USAGE_DISPLAY_ARG -# define USAGE_DISPLAY_INFO -#endif - -#if SYSAPI_WIN32 - -# define PLATFORM_ARGS \ -" [--daemon|--no-daemon]" -# define PLATFORM_DESC -# define PLATFORM_EXTRA \ -"At least one command line argument is required. If you don't otherwise\n" \ -"need an argument use `--daemon'.\n" \ -"\n" - -#else - -# define PLATFORM_ARGS \ -" [--daemon|--no-daemon]" -# define PLATFORM_DESC -# define PLATFORM_EXTRA - -#endif - - LOG((CLOG_PRINT -"Usage: %s" -" [--address
]" -" [--config ]" -" [--debug ]" -USAGE_DISPLAY_ARG -" [--name ]" -" [--restart|--no-restart]" -PLATFORM_ARGS -"\n\n" -"Start the synergy mouse/keyboard sharing server.\n" -"\n" -" -a, --address
listen for clients on the given address.\n" -" -c, --config use the named configuration file instead.\n" -" -d, --debug filter out log messages with priorty below level.\n" -" level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" -" DEBUG, DEBUG1, DEBUG2.\n" -USAGE_DISPLAY_INFO -" -f, --no-daemon run the server in the foreground.\n" -"* --daemon run the server as a daemon.\n" -" -n, --name use screen-name instead the hostname to identify\n" -" this screen in the configuration.\n" -" -1, --no-restart do not try to restart the server if it fails for\n" -" some reason.\n" -"* --restart restart the server automatically if it fails.\n" -" -l --log write log messages to file.\n" -PLATFORM_DESC -" -h, --help display this help and exit.\n" -" --version display version information and exit.\n" -"\n" -"* marks defaults.\n" -"\n" -PLATFORM_EXTRA -"The argument for --address is of the form: [][:]. The\n" -"hostname must be the address or hostname of an interface on the system.\n" -"The default is to listen on all interfaces. The port overrides the\n" -"default port, %d.\n" -"\n" -"If no configuration file pathname is provided then the first of the\n" -"following to load successfully sets the configuration:\n" -" %s\n" -" %s\n" -"If no configuration file can be loaded then the configuration uses its\n" -"defaults with just the server screen.\n" -"\n" -"Where log messages go depends on the platform and whether or not the\n" -"server is running as a daemon.", - ARG->m_pname, - kDefaultPort, - ARCH->concatPath( - ARCH->getUserDirectory(), - USR_CONFIG_NAME).c_str(), - ARCH->concatPath( - ARCH->getSystemDirectory(), - SYS_CONFIG_NAME).c_str())); -} - -static -bool -isArg(int argi, int argc, const char* const* argv, - const char* name1, const char* name2, - int minRequiredParameters = 0) -{ - if ((name1 != NULL && strcmp(argv[argi], name1) == 0) || - (name2 != NULL && strcmp(argv[argi], name2) == 0)) { - // match. check args left. - if (argi + minRequiredParameters >= argc) { - LOG((CLOG_PRINT "%s: missing arguments for `%s'" BYE, - ARG->m_pname, argv[argi], ARG->m_pname)); - bye(kExitArgs); - } - return true; - } - - // no match - return false; -} - -static -void -parse(int argc, const char* const* argv) -{ - assert(ARG->m_pname != NULL); - assert(argv != NULL); - assert(argc >= 1); - - // set defaults - ARG->m_name = ARCH->getHostName(); - - // parse options - int i = 1; - for (; i < argc; ++i) { - if (isArg(i, argc, argv, "-d", "--debug", 1)) { - // change logging level - ARG->m_logFilter = argv[++i]; - } - - else if (isArg(i, argc, argv, "-a", "--address", 1)) { - // save listen address - try { - *ARG->m_synergyAddress = CNetworkAddress(argv[i + 1], - kDefaultPort); - ARG->m_synergyAddress->resolve(); - } - catch (XSocketAddress& e) { - LOG((CLOG_PRINT "%s: %s" BYE, - ARG->m_pname, e.what(), ARG->m_pname)); - bye(kExitArgs); - } - ++i; - } - - else if (isArg(i, argc, argv, "-n", "--name", 1)) { - // save screen name - ARG->m_name = argv[++i]; - } - - else if (isArg(i, argc, argv, "-c", "--config", 1)) { - // save configuration file path - ARG->m_configFile = argv[++i]; - } - -#if WINAPI_XWINDOWS - else if (isArg(i, argc, argv, "-display", "--display", 1)) { - // use alternative display - ARG->m_display = argv[++i]; - } -#endif - - else if (isArg(i, argc, argv, "-f", "--no-daemon")) { - // not a daemon - ARG->m_daemon = false; - } - - else if (isArg(i, argc, argv, NULL, "--daemon")) { - // daemonize - ARG->m_daemon = true; - } - else if (isArg(i, argc, argv, "-l", "--log", 1)) { - // logging to file - ARG->m_logFile = argv[++i]; - } - - else if (isArg(i, argc, argv, "-1", "--no-restart")) { - // don't try to restart - ARG->m_restartable = false; - } - - else if (isArg(i, argc, argv, NULL, "--restart")) { - // try to restart - ARG->m_restartable = true; - } - - else if (isArg(i, argc, argv, "-z", NULL)) { - ARG->m_backend = true; - } - - else if (isArg(i, argc, argv, "-h", "--help")) { - help(); - bye(kExitSuccess); - } - - else if (isArg(i, argc, argv, NULL, "--version")) { - version(); - bye(kExitSuccess); - } - - else if (isArg(i, argc, argv, "--", NULL)) { - // remaining arguments are not options - ++i; - break; - } - - else if (argv[i][0] == '-') { - LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, - ARG->m_pname, argv[i], ARG->m_pname)); - bye(kExitArgs); - } - - else { - // this and remaining arguments are not options - break; - } - } - - // no non-option arguments are allowed - if (i != argc) { - LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, - ARG->m_pname, argv[i], ARG->m_pname)); - bye(kExitArgs); - } - - // increase default filter level for daemon. the user must - // explicitly request another level for a daemon. - if (ARG->m_daemon && ARG->m_logFilter == NULL) { -#if SYSAPI_WIN32 - if (CArchMiscWindows::isWindows95Family()) { - // windows 95 has no place for logging so avoid showing - // the log console window. - ARG->m_logFilter = "FATAL"; - } - else -#endif - { - ARG->m_logFilter = "NOTE"; - } - } - - // set log filter - if (!CLOG->setFilter(ARG->m_logFilter)) { - LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE, - ARG->m_pname, ARG->m_logFilter, ARG->m_pname)); - bye(kExitArgs); - } - - // identify system - LOG((CLOG_INFO "%s Server on %s %s", kAppVersion, ARCH->getOSName().c_str(), ARCH->getPlatformName().c_str())); - -#ifdef WIN32 -#ifdef _AMD64_ - LOG((CLOG_WARN "This is an experimental x64 build of %s. Use it at your own risk.", kApplication)); -#endif -#endif -} - -static -bool -loadConfig(const CString& pathname) -{ - try { - // load configuration - LOG((CLOG_DEBUG "opening configuration \"%s\"", pathname.c_str())); - std::ifstream configStream(pathname.c_str()); - if (!configStream.is_open()) { - // report failure to open configuration as a debug message - // since we try several paths and we expect some to be - // missing. - LOG((CLOG_DEBUG "cannot open configuration \"%s\"", - pathname.c_str())); - return false; - } - configStream >> *ARG->m_config; - LOG((CLOG_DEBUG "configuration read successfully")); - return true; - } - catch (XConfigRead& e) { - // report error in configuration file - LOG((CLOG_ERR "cannot read configuration \"%s\": %s", - pathname.c_str(), e.what())); - } - return false; -} - -static -void -loadConfig() -{ - bool loaded = false; - - // load the config file, if specified - if (!ARG->m_configFile.empty()) { - loaded = loadConfig(ARG->m_configFile); - } - - // load the default configuration if no explicit file given - else { - // get the user's home directory - CString path = ARCH->getUserDirectory(); - if (!path.empty()) { - // complete path - path = ARCH->concatPath(path, USR_CONFIG_NAME); - - // now try loading the user's configuration - if (loadConfig(path)) { - loaded = true; - ARG->m_configFile = path; - } - } - if (!loaded) { - // try the system-wide config file - path = ARCH->getSystemDirectory(); - if (!path.empty()) { - path = ARCH->concatPath(path, SYS_CONFIG_NAME); - if (loadConfig(path)) { - loaded = true; - ARG->m_configFile = path; - } - } - } - } - - if (!loaded) { - LOG((CLOG_PRINT "%s: no configuration available", ARG->m_pname)); - bye(kExitConfig); - } -} - - -// -// platform dependent entry points -// - -#if SYSAPI_WIN32 - -static bool s_hasImportantLogMessages = false; - -// -// CMessageBoxOutputter -// -// This class writes severe log messages to a message box -// - -class CMessageBoxOutputter : public ILogOutputter { -public: - CMessageBoxOutputter() { } - virtual ~CMessageBoxOutputter() { } - - // ILogOutputter overrides - virtual void open(const char*) { } - virtual void close() { } - virtual void show(bool) { } - virtual bool write(ELevel level, const char* message); - virtual const char* getNewline() const { return ""; } -}; - -bool -CMessageBoxOutputter::write(ELevel level, const char* message) -{ - // note any important messages the user may need to know about - if (level <= CLog::kWARNING) { - s_hasImportantLogMessages = true; - } - - // FATAL and PRINT messages get a dialog box if not running as - // backend. if we're running as a backend the user will have - // a chance to see the messages when we exit. - if (!ARG->m_backend && level <= CLog::kFATAL) { - MessageBox(NULL, message, ARG->m_pname, MB_OK | MB_ICONWARNING); - return false; - } - else { - return true; - } -} - -static -void -byeThrow(int x) -{ - CArchMiscWindows::daemonFailed(x); -} - -static -int -daemonNTMainLoop(int argc, const char** argv) -{ - parse(argc, argv); - ARG->m_backend = false; - loadConfig(); - return CArchMiscWindows::runDaemon(mainLoop); -} - -static -int -daemonNTStartup(int, char**) -{ - CSystemLogger sysLogger(DAEMON_NAME, false); - bye = &byeThrow; - return ARCH->daemonize(DAEMON_NAME, &daemonNTMainLoop); -} - -static -int -foregroundStartup(int argc, char** argv) -{ - - // parse command line - parse(argc, argv); - - // load configuration - loadConfig(); - - // never daemonize - return mainLoop(); -} - -static -void -showError(HINSTANCE instance, const char* title, UINT id, const char* arg) -{ - 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) -{ - try { - CArchMiscWindows::setIcons((HICON)LoadImage(instance, - MAKEINTRESOURCE(IDI_SYNERGY), - IMAGE_ICON, - 32, 32, LR_SHARED), - (HICON)LoadImage(instance, - MAKEINTRESOURCE(IDI_SYNERGY), - IMAGE_ICON, - 16, 16, LR_SHARED)); - CArch arch(instance); - CMSWindowsScreen::init(instance); - CLOG; - CThread::getCurrentThread().setPriority(-14); - CArgs args; - - // set title on log window - ARCH->openConsole((CString(kAppVersion) + " " + "Server").c_str()); - - // 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 (!CArchMiscWindows::isWindows95Family()) { - if (__argc <= 1) { - startup = &daemonNTStartup; - } - else { - startup = &foregroundStartup; - } - } - - // 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()); - } - catch (...) { - showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, ""); - //throw; - } - return kExitFailed; -} - -#elif SYSAPI_UNIX int -main(int argc, char** argv) +main(int argc, char** argv) { - CArgs args; - try { - int result; - CArch arch; - CLOG; - CArgs args; - result = run(argc, argv, NULL, &standardStartup); - delete CLOG; - return result; - } - 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; - } + CServerApp app; + return app.run(argc, argv, createTaskBarReceiver); } - -#else - -#error no main() for platform - -#endif diff --git a/gui/qsynergy.pro b/gui/qsynergy.pro index d0e8dfd0..f67e03af 100644 --- a/gui/qsynergy.pro +++ b/gui/qsynergy.pro @@ -83,3 +83,5 @@ release { RCC_DIR = tmp/release } +Debug:DESTDIR = ../bin/Debug +Release:DESTDIR = ../bin/Release diff --git a/gui/res/win/QSynergy.ico b/gui/res/win/QSynergy.ico index 89f965f4..fc2e4146 100644 Binary files a/gui/res/win/QSynergy.ico and b/gui/res/win/QSynergy.ico differ diff --git a/gui/res/win/QSynergy.rc b/gui/res/win/QSynergy.rc index 696cc6e6..f746d72f 100644 --- a/gui/res/win/QSynergy.rc +++ b/gui/res/win/QSynergy.rc @@ -1 +1 @@ -IDI_ICON1 ICON DISCARDABLE "res\win\QSynergy.ico" +IDI_ICON1 ICON DISCARDABLE "res\\win\\QSynergy.ico" diff --git a/gui/src/AppConfig.cpp b/gui/src/AppConfig.cpp index cfb4345f..e16f3c74 100644 --- a/gui/src/AppConfig.cpp +++ b/gui/src/AppConfig.cpp @@ -6,7 +6,7 @@ #if defined(Q_OS_WIN) const char AppConfig::m_SynergysName[] = "synergys.exe"; const char AppConfig::m_SynergycName[] = "synergyc.exe"; -const char AppConfig::m_SynergyProgramDir[] = "c:/program files/synergy/"; +const char AppConfig::m_SynergyProgramDir[] = "./"; #else const char AppConfig::m_SynergysName[] = "synergys"; const char AppConfig::m_SynergycName[] = "synergyc"; diff --git a/gui/src/MainWindow.cpp b/gui/src/MainWindow.cpp index 7b3b89c3..c676cfc0 100644 --- a/gui/src/MainWindow.cpp +++ b/gui/src/MainWindow.cpp @@ -224,7 +224,7 @@ void MainWindow::startSynergy() setSynergyProcess(new QProcess(this)); if ((synergyType() == synergyClient && !clientArgs(args, app)) - || synergyType() == synergyServer && !serverArgs(args, app)) + || (synergyType() == synergyServer && !serverArgs(args, app))) { stopSynergy(); return; diff --git a/gui/src/main.cpp b/gui/src/main.cpp index 3087fe0b..e3ff1c24 100644 --- a/gui/src/main.cpp +++ b/gui/src/main.cpp @@ -6,16 +6,16 @@ int main(int argc, char* argv[]) { - QCoreApplication::setOrganizationName("Fidra"); - QCoreApplication::setOrganizationDomain("www.fidra.de"); - QCoreApplication::setApplicationName("QSynergy"); + QCoreApplication::setOrganizationName("The Synergy+ Project"); + QCoreApplication::setOrganizationDomain("http://code.google.com/p/synergy-plus/"); + QCoreApplication::setApplicationName("Synergy+"); QSynergyApplication app(argc, argv); #if !defined(Q_OS_MAC) if (!QSystemTrayIcon::isSystemTrayAvailable()) { - QMessageBox::critical(NULL, "QSynergy", QObject::tr("There doesn't seem to be a system tray available. Quitting.")); + QMessageBox::critical(NULL, "Synergy+", QObject::tr("There doesn't seem to be a system tray available. Quitting.")); return -1; } diff --git a/lib/arch/CArch.cpp b/lib/arch/CArch.cpp index 6d0a8c07..7d31bf3e 100644 --- a/lib/arch/CArch.cpp +++ b/lib/arch/CArch.cpp @@ -14,6 +14,7 @@ #include "common.h" #include "CArch.h" +#include "CLog.h" #undef ARCH_CONSOLE #undef ARCH_DAEMON @@ -41,6 +42,7 @@ # include "CArchSystemWindows.h" # include "CArchTaskBarWindows.h" # include "CArchTimeWindows.h" +# include "CArchAppUtilWindows.h" #elif SYSAPI_UNIX # include "CArchConsoleUnix.h" # include "CArchDaemonUnix.h" @@ -55,6 +57,7 @@ # include "CArchSystemUnix.h" # include "CArchTaskBarXWindows.h" # include "CArchTimeUnix.h" +# include "CArchAppUtilUnix.h" #endif #if !defined(ARCH_CONSOLE) @@ -101,13 +104,17 @@ # error unsupported platform for time #endif +#if !defined(ARCH_APPUTIL) +# error unsupported platform for app util +#endif + // // CArch // CArch* CArch::s_instance = NULL; -CArch::CArch(ARCH_ARGS* args) +CArch::CArch() { // only once instance of CArch assert(s_instance == NULL); @@ -122,9 +129,10 @@ CArch::CArch(ARCH_ARGS* args) m_sleep = new ARCH_SLEEP; m_string = new ARCH_STRING; m_time = new ARCH_TIME; - m_console = new ARCH_CONSOLE(args); + m_console = new ARCH_CONSOLE; m_daemon = new ARCH_DAEMON; - m_taskbar = new ARCH_TASKBAR(args); + m_taskbar = new ARCH_TASKBAR; + m_appUtil = new ARCH_APPUTIL; #if SYSAPI_WIN32 CArchMiscWindows::init(); @@ -145,6 +153,7 @@ CArch::~CArch() delete m_file; delete m_system; delete m_mt; + delete m_appUtil; // no instance s_instance = NULL; @@ -182,12 +191,6 @@ CArch::writeConsole(const char* str) m_console->writeConsole(str); } -const char* -CArch::getNewlineForConsole() -{ - return m_console->getNewlineForConsole(); -} - void CArch::installDaemon(const char* name, const char* description, @@ -643,3 +646,27 @@ CArch::time() { return m_time->time(); } + +bool +CArch::parseArg(const int& argc, const char* const* argv, int& i) +{ + return m_appUtil->parseArg(argc, argv, i); +} + +void +CArch::adoptApp(CApp* app) +{ + m_appUtil->adoptApp(app); +} + +CApp& +CArch::app() const +{ + return m_appUtil->app(); +} + +int +CArch::run(int argc, char** argv, CreateTaskBarReceiverFunc createTaskBarReceiver) +{ + return m_appUtil->run(argc, argv, createTaskBarReceiver); +} diff --git a/lib/arch/CArch.h b/lib/arch/CArch.h index d5d5b9d3..a7bcf935 100644 --- a/lib/arch/CArch.h +++ b/lib/arch/CArch.h @@ -26,6 +26,7 @@ #include "IArchSystem.h" #include "IArchTaskBar.h" #include "IArchTime.h" +#include "IArchAppUtil.h" /*! \def ARCH @@ -33,9 +34,7 @@ This macro evaluates to the singleton CArch object. */ #define ARCH (CArch::getInstance()) -#define ARCH_ARGS void - -//! Delegating mplementation of architecture dependent interfaces +//! Delegating implementation of architecture dependent interfaces /*! This class is a centralized interface to all architecture dependent interface implementations (except miscellaneous functions). It @@ -55,9 +54,10 @@ class CArch : public IArchConsole, public IArchString, public IArchSystem, public IArchTaskBar, - public IArchTime { + public IArchTime, + public IArchAppUtil { public: - CArch(ARCH_ARGS* args = NULL); + CArch(); ~CArch(); // @@ -76,7 +76,6 @@ public: virtual void closeConsole(); virtual void showConsole(bool showIfEmpty); virtual void writeConsole(const char*); - virtual const char* getNewlineForConsole(); // IArchDaemon overrides virtual void installDaemon(const char* name, @@ -183,6 +182,12 @@ public: // IArchTime overrides virtual double time(); + + // IArchAppUtil overrides + virtual bool parseArg(const int& argc, const char* const* argv, int& i); + virtual void adoptApp(CApp* app); + virtual CApp& app() const; + virtual int run(int argc, char** argv, CreateTaskBarReceiverFunc createTaskBarReceiver); private: static CArch* s_instance; @@ -198,6 +203,7 @@ private: IArchSystem* m_system; IArchTaskBar* m_taskbar; IArchTime* m_time; + IArchAppUtil* m_appUtil; }; //! Convenience object to lock/unlock an arch mutex diff --git a/lib/arch/CArchAppUtil.cpp b/lib/arch/CArchAppUtil.cpp new file mode 100644 index 00000000..ec8b072a --- /dev/null +++ b/lib/arch/CArchAppUtil.cpp @@ -0,0 +1,55 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 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 "CArchAppUtil.h" +#include "CApp.h" + +CArchAppUtil* CArchAppUtil::s_instance = nullptr; + +CArchAppUtil::CArchAppUtil() : +m_app(nullptr) +{ + s_instance = this; +} + +CArchAppUtil::~CArchAppUtil() +{ +} + +bool +CArchAppUtil::parseArg(const int& argc, const char* const* argv, int& i) +{ + // no common platform args (yet) + return false; +} + +void +CArchAppUtil::adoptApp(CApp* app) +{ + m_app = app; +} + +CApp& +CArchAppUtil::app() const +{ + assert(m_app != nullptr); + return *m_app; +} + +CArchAppUtil& +CArchAppUtil::instance() +{ + assert(s_instance != nullptr); + return *s_instance; +} diff --git a/lib/arch/CArchAppUtil.h b/lib/arch/CArchAppUtil.h new file mode 100644 index 00000000..5b74f648 --- /dev/null +++ b/lib/arch/CArchAppUtil.h @@ -0,0 +1,33 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 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. + */ + +#pragma once + +#include "IArchAppUtil.h" + +class CArchAppUtil : public IArchAppUtil { +public: + CArchAppUtil(); + virtual ~CArchAppUtil(); + + virtual bool parseArg(const int& argc, const char* const* argv, int& i); + virtual void adoptApp(CApp* app); + CApp& app() const; + + static CArchAppUtil& instance(); + +private: + CApp* m_app; + static CArchAppUtil* s_instance; +}; diff --git a/lib/arch/CArchAppUtilUnix.cpp b/lib/arch/CArchAppUtilUnix.cpp new file mode 100644 index 00000000..98527538 --- /dev/null +++ b/lib/arch/CArchAppUtilUnix.cpp @@ -0,0 +1,56 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 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 "CArchAppUtilUnix.h" + +CArchAppUtilUnix::CArchAppUtilUnix() +{ +} + +CArchAppUtilUnix::~CArchAppUtilUnix() +{ +} + +bool +CArchAppUtilUnix::parseArg(const int& argc, const char* const* argv, int& i) +{ +#if WINAPI_XWINDOWS + if (app().isArg(i, argc, argv, "-display", "--display", 1)) { + // use alternative display + app().argsBase().m_display = argv[++i]; + } + + else { + // option not supported here + return false; + } + + return true; +#else + // no options for carbon + return false; +#endif +} + +int +standardStartupStatic(int argc, char** argv) +{ + return CArchAppUtil::instance().app().standardStartup(argc, argv); +} + +int +CArchAppUtilUnix::run(int argc, char** argv, CreateTaskBarReceiverFunc createTaskBarReceiver) +{ + return app().runInner(argc, argv, NULL, &standardStartupStatic, createTaskBarReceiver); +} diff --git a/lib/arch/CArchAppUtilUnix.h b/lib/arch/CArchAppUtilUnix.h new file mode 100644 index 00000000..a9ba730c --- /dev/null +++ b/lib/arch/CArchAppUtilUnix.h @@ -0,0 +1,28 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 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. + */ + +#pragma once + +#include "CArchAppUtil.h" + +#define ARCH_APPUTIL CArchAppUtilUnix + +class CArchAppUtilUnix : public CArchAppUtil { +public: + CArchAppUtilUnix(); + virtual ~CArchAppUtilUnix(); + + bool parseArg(const int& argc, const char* const* argv, int& i); + int run(int argc, char** argv, CreateTaskBarReceiverFunc createTaskBarReceiver); +}; diff --git a/lib/arch/CArchAppUtilWindows.cpp b/lib/arch/CArchAppUtilWindows.cpp new file mode 100644 index 00000000..872ed3c6 --- /dev/null +++ b/lib/arch/CArchAppUtilWindows.cpp @@ -0,0 +1,268 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 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 "CArchAppUtilWindows.h" +#include "Version.h" +#include "CLog.h" +#include "XArchWindows.h" +#include "CArchMiscWindows.h" +#include "CApp.h" +#include "LogOutputters.h" +#include "CMSWindowsScreen.h" + +#include +#include +#include + +CArchAppUtilWindows::CArchAppUtilWindows() +{ +} + +CArchAppUtilWindows::~CArchAppUtilWindows() +{ +} + +bool +CArchAppUtilWindows::parseArg(const int& argc, const char* const* argv, int& i) +{ + if (app().isArg(i, argc, argv, NULL, "--service")) { + + const char* action = argv[++i]; + + if (_stricmp(action, "install") == 0) { + installService(); + } + else if (_stricmp(action, "uninstall") == 0) { + uninstallService(); + } + else if (_stricmp(action, "start") == 0) { + startService(); + } + else if (_stricmp(action, "stop") == 0) { + stopService(); + } + else { + LOG((CLOG_ERR "unknown service action: %s", action)); + app().m_bye(kExitArgs); + } + app().m_bye(kExitSuccess); + } + else { + // option not supported here + return false; + } + + return true; +} + +void +CArchAppUtilWindows::adoptApp(CApp* app) +{ + app->m_bye = &exitPause; + CArchAppUtil::adoptApp(app); +} + +CString +CArchAppUtilWindows::getServiceArgs() const +{ + std::stringstream argBuf; + for (int i = 1; i < __argc; i++) { + const char* arg = __argv[i]; + + // ignore service setup args + if (_stricmp(arg, "--service") == 0) { + // ignore and skip the next arg also (service action) + i++; + } + else { + argBuf << " " << __argv[i]; + } + } + return argBuf.str(); +} + +void +CArchAppUtilWindows::installService() +{ + CString args = getServiceArgs(); + + // get the path of this program + char thisPath[MAX_PATH]; + GetModuleFileName(CArchMiscWindows::instanceWin32(), thisPath, MAX_PATH); + + ARCH->installDaemon( + app().daemonName(), app().daemonInfo(), + thisPath, args.c_str(), NULL, true); + + LOG((CLOG_INFO "service '%s' installed with args: %s", + app().daemonName(), args != "" ? args.c_str() : "none" )); +} + +void +CArchAppUtilWindows::uninstallService() +{ + ARCH->uninstallDaemon(app().daemonName(), true); + LOG((CLOG_INFO "service '%s' uninstalled", app().daemonName())); +} + +void +CArchAppUtilWindows::startService() +{ + // open service manager + SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ); + if (mgr == NULL) { + throw XArchDaemonFailed(new XArchEvalWindows()); + } + + // open the service + SC_HANDLE service = OpenService( + mgr, app().daemonName(), SERVICE_START); + + if (service == NULL) { + CloseServiceHandle(mgr); + throw XArchDaemonFailed(new XArchEvalWindows()); + } + + // start the service + if (StartService(service, 0, NULL)) { + LOG((CLOG_INFO "service '%s' started", app().daemonName())); + } + else { + throw XArchDaemonFailed(new XArchEvalWindows()); + } +} + +void +CArchAppUtilWindows::stopService() +{ + // open service manager + SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ); + if (mgr == NULL) { + throw XArchDaemonFailed(new XArchEvalWindows()); + } + + // open the service + SC_HANDLE service = OpenService( + mgr, app().daemonName(), + SERVICE_STOP | SERVICE_QUERY_STATUS); + + if (service == NULL) { + CloseServiceHandle(mgr); + throw XArchDaemonFailed(new XArchEvalWindows()); + } + + // ask the service to stop, asynchronously + SERVICE_STATUS ss; + if (!ControlService(service, SERVICE_CONTROL_STOP, &ss)) { + DWORD dwErrCode = GetLastError(); + if (dwErrCode != ERROR_SERVICE_NOT_ACTIVE) { + LOG((CLOG_ERR "cannot stop service '%s'", app().daemonName())); + throw XArchDaemonFailed(new XArchEvalWindows()); + } + } + + LOG((CLOG_INFO "service '%s' stopping asyncronously", app().daemonName())); +} + +void +exitPause(int code) +{ + CString name; + CArchMiscWindows::getParentProcessName(name); + + // if the user did not launch from the command prompt (i.e. it was launched + // by double clicking, or through a debugger), allow user to read any error + // messages (instead of the window closing automatically). + if (name != "cmd.exe") { + std::cout << std::endl << "Press any key to exit..." << std::endl; + int c = _getch(); + } + + exit(code); +} + +static +int +mainLoopStatic() +{ + return CArchAppUtil::instance().app().mainLoop(); +} + +int +CArchAppUtilWindows::daemonNTMainLoop(int argc, const char** argv) +{ + app().parseArgs(argc, argv); + app().argsBase().m_backend = false; + app().loadConfig(); + return CArchMiscWindows::runDaemon(mainLoopStatic); +} + +void +CArchAppUtilWindows::byeThrow(int x) +{ + CArchMiscWindows::daemonFailed(x); +} + +int daemonNTMainLoopStatic(int argc, const char** argv) +{ + return CArchAppUtil::instance().app().daemonMainLoop(argc, argv); +} + +int +CArchAppUtilWindows::daemonNTStartup(int, char**) +{ + CSystemLogger sysLogger(app().daemonName(), false); + app().m_bye = &byeThrow; + return ARCH->daemonize(app().daemonName(), daemonNTMainLoopStatic); +} + +static +int +daemonNTStartupStatic(int argc, char** argv) +{ + return CArchAppUtilWindows::instance().daemonNTStartup(argc, argv); +} + +static +int +foregroundStartupStatic(int argc, char** argv) +{ + return CArchAppUtil::instance().app().foregroundStartup(argc, argv); +} + +int +CArchAppUtilWindows::run(int argc, char** argv, CreateTaskBarReceiverFunc createTaskBarReceiver) +{ + // record window instance for tray icon, etc + CArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL)); + + CMSWindowsScreen::init(CArchMiscWindows::instanceWin32()); + CThread::getCurrentThread().setPriority(-14); + + StartupFunc startup; + if (CArchMiscWindows::wasLaunchedAsService()) { + startup = &daemonNTStartupStatic; + } else { + startup = &foregroundStartupStatic; + app().argsBase().m_daemon = false; + } + + return app().runInner(argc, argv, NULL, startup, createTaskBarReceiver); +} + +CArchAppUtilWindows& +CArchAppUtilWindows::instance() +{ + return (CArchAppUtilWindows&)CArchAppUtil::instance(); +} diff --git a/lib/arch/CArchAppUtilWindows.h b/lib/arch/CArchAppUtilWindows.h new file mode 100644 index 00000000..81d1b0d1 --- /dev/null +++ b/lib/arch/CArchAppUtilWindows.h @@ -0,0 +1,63 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 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. + */ + +#pragma once + +#include "CArchAppUtil.h" +#include "CString.h" + +#include "Windows.h" + +#define ARCH_APPUTIL CArchAppUtilWindows + +class CArchAppUtilWindows : public CArchAppUtil { +public: + CArchAppUtilWindows(); + virtual ~CArchAppUtilWindows(); + + // Gets the arguments to be used with a service. + CString getServiceArgs() const; + + // Install application as Windows service. + void installService(); + + // Uninstall a Windows service with matching daemon name. + void uninstallService(); + + // Start a Windows service with matching daemon name. + void startService(); + + // Stop a Windows service with matching daemon name. + void stopService(); + + // Will install, uninstall, start, or stop the service depending on arg. + void handleServiceArg(const char* serviceAction); + + bool parseArg(const int& argc, const char* const* argv, int& i); + + void adoptApp(CApp* app); + + int daemonNTStartup(int, char**); + + int daemonNTMainLoop(int argc, const char** argv); + + int run(int argc, char** argv, CreateTaskBarReceiverFunc createTaskBarReceiver); + + static void byeThrow(int x); + + static CArchAppUtilWindows& instance(); +}; + +// TODO: move to class +void exitPause(int code); diff --git a/lib/arch/CArchConsoleStd.cpp b/lib/arch/CArchConsoleStd.cpp new file mode 100644 index 00000000..e9e06c65 --- /dev/null +++ b/lib/arch/CArchConsoleStd.cpp @@ -0,0 +1,24 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 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 "CArchConsoleStd.h" +#include + +void +CArchConsoleStd::writeConsole(const char* str) +{ + // TODO: we need to use cerr also somehow + std::cout << str << std::endl; + std::cout.flush(); +} \ No newline at end of file diff --git a/lib/arch/CArchConsoleStd.h b/lib/arch/CArchConsoleStd.h new file mode 100644 index 00000000..ac902721 --- /dev/null +++ b/lib/arch/CArchConsoleStd.h @@ -0,0 +1,30 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 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. + */ + +#pragma once + +#include "IArchConsole.h" + +//! Cross platform implementation of IArchConsole +class CArchConsoleStd : public IArchConsole { +public: + CArchConsoleStd() { } + virtual ~CArchConsoleStd() { } + + // IArchConsole overrides + virtual void openConsole(const char* title) { } + virtual void closeConsole() { } + virtual void showConsole(bool) { } + virtual void writeConsole(const char*); +}; diff --git a/lib/arch/CArchConsoleUnix.cpp b/lib/arch/CArchConsoleUnix.cpp index 88337150..59877a19 100644 --- a/lib/arch/CArchConsoleUnix.cpp +++ b/lib/arch/CArchConsoleUnix.cpp @@ -13,49 +13,7 @@ */ #include "CArchConsoleUnix.h" -#include -#include -// -// CArchConsoleUnix -// +CArchConsoleUnix::CArchConsoleUnix() { } -CArchConsoleUnix::CArchConsoleUnix(void*) -{ - // do nothing -} - -CArchConsoleUnix::~CArchConsoleUnix() -{ - // do nothing -} - -void -CArchConsoleUnix::openConsole(const char*) -{ - // do nothing -} - -void -CArchConsoleUnix::closeConsole() -{ - // do nothing -} - -void -CArchConsoleUnix::showConsole(bool) -{ - // do nothing -} - -void -CArchConsoleUnix::writeConsole(const char* str) -{ - fprintf(stderr, "%s", str); -} - -const char* -CArchConsoleUnix::getNewlineForConsole() -{ - return "\n"; -} +CArchConsoleUnix::~CArchConsoleUnix() { } diff --git a/lib/arch/CArchConsoleUnix.h b/lib/arch/CArchConsoleUnix.h index f93630bd..c8a737f3 100644 --- a/lib/arch/CArchConsoleUnix.h +++ b/lib/arch/CArchConsoleUnix.h @@ -12,25 +12,14 @@ * GNU General Public License for more details. */ -#ifndef CARCHCONSOLEUNIX_H -#define CARCHCONSOLEUNIX_H +#pragma once -#include "IArchConsole.h" +#include "CArchConsoleStd.h" #define ARCH_CONSOLE CArchConsoleUnix -//! Unix implementation of IArchConsole -class CArchConsoleUnix : public IArchConsole { +class CArchConsoleUnix : public CArchConsoleStd { public: - CArchConsoleUnix(void*); + CArchConsoleUnix(); virtual ~CArchConsoleUnix(); - - // IArchConsole overrides - virtual void openConsole(const char* title); - virtual void closeConsole(); - virtual void showConsole(bool); - virtual void writeConsole(const char*); - virtual const char* getNewlineForConsole(); }; - -#endif diff --git a/lib/arch/CArchConsoleWindows.cpp b/lib/arch/CArchConsoleWindows.cpp index 3f470e5e..1f721c5b 100644 --- a/lib/arch/CArchConsoleWindows.cpp +++ b/lib/arch/CArchConsoleWindows.cpp @@ -1,439 +1,19 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 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 "CArchConsoleWindows.h" -#include "IArchMultithread.h" -#include "CArch.h" -#include "CArchMiscWindows.h" -#include - -#define SYNERGY_MSG_CONSOLE_OPEN WM_APP + 0x0021 -#define SYNERGY_MSG_CONSOLE_CLOSE WM_APP + 0x0022 -#define SYNERGY_MSG_CONSOLE_SHOW WM_APP + 0x0023 -#define SYNERGY_MSG_CONSOLE_WRITE WM_APP + 0x0024 -#define SYNERGY_MSG_CONSOLE_CLEAR WM_APP + 0x0025 -#define TWIPS_PER_POINT 20 - -// -// CArchConsoleWindows -// - -CArchConsoleWindows* CArchConsoleWindows::s_instance = NULL; -HINSTANCE CArchConsoleWindows::s_appInstance = NULL; - -CArchConsoleWindows::CArchConsoleWindows(void* appInstance) : - m_show(false), - m_maxLines(1000), - m_numCharacters(0), - m_maxCharacters(65536) -{ - // save the singleton instance - s_instance = this; - - // save app instance - s_appInstance = reinterpret_cast(appInstance); - - // we need a mutex - m_mutex = ARCH->newMutex(); - - // and a condition variable which uses the above mutex - m_ready = false; - m_condVar = ARCH->newCondVar(); - - // 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(&CArchConsoleWindows::threadEntry, this); - - // wait for child thread - while (!m_ready) { - ARCH->waitCondVar(m_condVar, m_mutex, -1.0); - } - - // ready - ARCH->unlockMutex(m_mutex); - -} - -CArchConsoleWindows::~CArchConsoleWindows() -{ - if (m_thread != NULL) { - PostMessage(m_hwnd, WM_QUIT, 0, 0); - ARCH->wait(m_thread, -1.0); - ARCH->closeThread(m_thread); - } - ARCH->closeCondVar(m_condVar); - ARCH->closeMutex(m_mutex); - s_instance = NULL; -} - -void -CArchConsoleWindows::openConsole(const char* title) -{ - SetWindowText(m_frame, title); - SendMessage(m_frame, SYNERGY_MSG_CONSOLE_OPEN, 0, 0); -} - -void -CArchConsoleWindows::closeConsole() -{ - SendMessage(m_frame, SYNERGY_MSG_CONSOLE_CLOSE, 0, 0); - SendMessage(m_frame, SYNERGY_MSG_CONSOLE_CLEAR, 0, 0); -} - -void -CArchConsoleWindows::showConsole(bool showIfEmpty) -{ - SendMessage(m_frame, SYNERGY_MSG_CONSOLE_SHOW, showIfEmpty ? 1 : 0, 0); -} - -void -CArchConsoleWindows::writeConsole(const char* str) -{ - SendMessage(m_frame, SYNERGY_MSG_CONSOLE_WRITE, - reinterpret_cast(str), 0); -} - -const char* -CArchConsoleWindows::getNewlineForConsole() -{ - return "\r\n"; -} - -void -CArchConsoleWindows::clearBuffer() -{ - m_buffer.clear(); - m_numCharacters = 0; - SetWindowText(m_hwnd, ""); -} - -void -CArchConsoleWindows::appendBuffer(const char* msg) -{ - bool wasEmpty = m_buffer.empty(); - - // get current selection - CHARRANGE selection; - SendMessage(m_hwnd, EM_EXGETSEL, 0, reinterpret_cast(&selection)); - - // remove tail of buffer - size_t removedCharacters = 0; - while (m_buffer.size() >= m_maxLines) { - removedCharacters += m_buffer.front().size(); - m_buffer.pop_front(); - } - - // remove lines from top of control - if (removedCharacters > 0) { - CHARRANGE range; - range.cpMin = 0; - range.cpMax = static_cast(removedCharacters); - SendMessage(m_hwnd, EM_EXSETSEL, 0, reinterpret_cast(&range)); - SendMessage(m_hwnd, EM_REPLACESEL, FALSE, reinterpret_cast("")); - - // adjust selection - if (selection.cpMin < static_cast(removedCharacters) || - selection.cpMax < static_cast(removedCharacters)) { - selection.cpMin = 0; - selection.cpMax = 0; - } - else { - selection.cpMin -= static_cast(removedCharacters); - selection.cpMax -= static_cast(removedCharacters); - } - - m_numCharacters -= removedCharacters; - } - - // append message - m_buffer.push_back(msg); - size_t newNumCharacters = m_numCharacters + m_buffer.back().size(); - - // add line to bottom of control - if (newNumCharacters > m_maxCharacters) { - m_maxCharacters = newNumCharacters; - SendMessage(m_hwnd, EM_EXLIMITTEXT, 0, m_maxCharacters); - } - CHARRANGE range; - range.cpMin = (LONG)m_numCharacters; - range.cpMax = (LONG)m_numCharacters; - SendMessage(m_hwnd, EM_EXSETSEL, 0, reinterpret_cast(&range)); - SendMessage(m_hwnd, EM_REPLACESEL, FALSE, - reinterpret_cast(m_buffer.back().c_str())); - - // adjust selection - bool atEnd = false; - if (selection.cpMax == static_cast(m_numCharacters)) { - selection.cpMin = static_cast(newNumCharacters); - selection.cpMax = static_cast(newNumCharacters); - atEnd = true; - } - - // restore the selection - SendMessage(m_hwnd, EM_EXSETSEL, 0, reinterpret_cast(&selection)); - if (atEnd) { - SendMessage(m_hwnd, EM_SCROLLCARET, 0, 0); - } - - if (wasEmpty && m_show) { - ShowWindow(m_frame, TRUE); - } - - m_numCharacters = newNumCharacters; -} - -void -CArchConsoleWindows::setSize(int width, int height) -{ - DWORD style = GetWindowLong(m_frame, GWL_STYLE); - DWORD exStyle = GetWindowLong(m_frame, GWL_EXSTYLE); - RECT rect; - rect.left = 100; - rect.top = 100; - rect.right = rect.left + width * m_wChar; - rect.bottom = rect.top + height * m_hChar; - AdjustWindowRectEx(&rect, style, FALSE, exStyle); - SetWindowPos(m_frame, NULL, 0, 0, rect.right - rect.left, - rect.bottom - rect.top, - SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER); -} - -LRESULT -CArchConsoleWindows::wndProc(HWND hwnd, - UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch (msg) { - case WM_CLOSE: - ShowWindow(m_frame, FALSE); - m_show = false; - return 0; - - case SYNERGY_MSG_CONSOLE_OPEN: - return 0; - - case SYNERGY_MSG_CONSOLE_CLOSE: - SendMessage(m_frame, WM_CLOSE, 0, 0); - m_show = false; - return 0; - - case SYNERGY_MSG_CONSOLE_SHOW: - m_show = true; - if (wParam != 0 || !m_buffer.empty()) { - ShowWindow(m_frame, TRUE); - } - return 0; - - case SYNERGY_MSG_CONSOLE_WRITE: - appendBuffer(reinterpret_cast(wParam)); - return 0; - - case SYNERGY_MSG_CONSOLE_CLEAR: - clearBuffer(); - return 0; - - case WM_SIZE: - if (hwnd == m_frame) { - MoveWindow(m_hwnd, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE); - } - break; - - case WM_SIZING: - if (hwnd == m_frame) { - // get window vs client area info - int wBase = 40 * m_wChar; - int hBase = 40 * m_hChar; - DWORD style = GetWindowLong(m_frame, GWL_STYLE); - DWORD exStyle = GetWindowLong(m_frame, GWL_EXSTYLE); - RECT rect; - rect.left = 100; - rect.top = 100; - rect.right = rect.left + wBase; - rect.bottom = rect.top + hBase; - AdjustWindowRectEx(&rect, style, FALSE, exStyle); - wBase = rect.right - rect.left - wBase; - hBase = rect.bottom - rect.top - hBase; - - // get closest size that's a multiple of the character size - RECT* newRect = (RECT*)lParam; - int width = (newRect->right - newRect->left - wBase) / m_wChar; - int height = (newRect->bottom - newRect->top - hBase) / m_hChar; - width = width * m_wChar + wBase; - height = height * m_hChar + hBase; - - // adjust sizing rect - switch (wParam) { - case WMSZ_LEFT: - case WMSZ_TOPLEFT: - case WMSZ_BOTTOMLEFT: - newRect->left = newRect->right - width; - break; - - case WMSZ_RIGHT: - case WMSZ_TOPRIGHT: - case WMSZ_BOTTOMRIGHT: - newRect->right = newRect->left + width; - break; - } - switch (wParam) { - case WMSZ_TOP: - case WMSZ_TOPLEFT: - case WMSZ_TOPRIGHT: - newRect->top = newRect->bottom - height; - break; - - case WMSZ_BOTTOM: - case WMSZ_BOTTOMLEFT: - case WMSZ_BOTTOMRIGHT: - newRect->bottom = newRect->top + height; - break; - } - return TRUE; - } - break; - - default: - break; - } - - return DefWindowProc(hwnd, msg, wParam, lParam); -} - -LRESULT CALLBACK -CArchConsoleWindows::staticWndProc(HWND hwnd, UINT msg, - WPARAM wParam, LPARAM lParam) -{ - // forward the message - if (s_instance != NULL) { - return s_instance->wndProc(hwnd, msg, wParam, lParam); - } - else { - return DefWindowProc(hwnd, msg, wParam, lParam); - } -} - -void -CArchConsoleWindows::threadMainLoop() -{ - LoadLibrary("RICHED32.DLL"); - - // get the app icons - HICON largeIcon, smallIcon; - CArchMiscWindows::getIcons(largeIcon, smallIcon); - - // register a window class - WNDCLASSEX classInfo; - classInfo.cbSize = sizeof(classInfo); - classInfo.style = 0; - classInfo.lpfnWndProc = &CArchConsoleWindows::staticWndProc; - classInfo.cbClsExtra = 0; - classInfo.cbWndExtra = sizeof(CArchConsoleWindows*); - classInfo.hInstance = s_appInstance; - classInfo.hIcon = largeIcon; - classInfo.hCursor = NULL; - classInfo.hbrBackground = NULL; - classInfo.lpszMenuName = NULL; - classInfo.lpszClassName = TEXT("SynergyConsole"); - classInfo.hIconSm = smallIcon; - ATOM windowClass = RegisterClassEx(&classInfo); - - // create frame window - m_frame = CreateWindowEx(0, - reinterpret_cast(windowClass), - TEXT("Synergy Log"), - WS_OVERLAPPEDWINDOW, - CW_USEDEFAULT, CW_USEDEFAULT, 100, 100, - NULL, - NULL, - s_appInstance, - NULL); - - // create log window - m_hwnd = CreateWindowEx(0, - "RichEdit", - TEXT(""), - WS_CHILD | WS_VISIBLE | WS_VSCROLL | - ES_MULTILINE | ES_READONLY, - 0, 0, 1, 1, - m_frame, - (HMENU)1, - s_appInstance, - NULL); - - // select font and get info - HDC hdc = GetDC(m_hwnd); - HGDIOBJ oldFont = SelectObject(hdc, GetStockObject(ANSI_FIXED_FONT)); - TEXTMETRIC metrics; - GetTextMetrics(hdc, &metrics); - CHARFORMAT format; - format.cbSize = sizeof(format); - format.dwMask = CFM_CHARSET | CFM_COLOR | CFM_FACE | - CFM_OFFSET | CFM_SIZE | CFM_PROTECTED | - CFM_BOLD | CFM_ITALIC | - CFM_STRIKEOUT | CFM_UNDERLINE; - format.dwEffects = 0; - format.yHeight = metrics.tmHeight * TWIPS_PER_POINT; // this is in 1/1440 in (twips) - format.yOffset = 0; - format.crTextColor = RGB(0, 0, 0); - format.bCharSet = DEFAULT_CHARSET; - format.bPitchAndFamily = FIXED_PITCH | FF_MODERN; - GetTextFace(hdc, sizeof(format.szFaceName), format.szFaceName); - SelectObject(hdc, oldFont); - ReleaseDC(m_hwnd, hdc); - - // prep window - SendMessage(m_hwnd, EM_EXLIMITTEXT, 0, m_maxCharacters); - SendMessage(m_hwnd, EM_SETCHARFORMAT, 0, reinterpret_cast(&format)); - SendMessage(m_hwnd, EM_SETBKGNDCOLOR, 0, RGB(255, 255, 255)); - m_wChar = metrics.tmAveCharWidth; - m_hChar = metrics.tmHeight + metrics.tmExternalLeading; - setSize(80, 25); - - // signal ready - ARCH->lockMutex(m_mutex); - m_ready = true; - ARCH->broadcastCondVar(m_condVar); - ARCH->unlockMutex(m_mutex); - - // handle failure - if (m_hwnd == NULL) { - UnregisterClass(reinterpret_cast(windowClass), s_appInstance); - return; - } - - // main loop - MSG msg; - while (GetMessage(&msg, NULL, 0, 0)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - // clean up - DestroyWindow(m_hwnd); - UnregisterClass(reinterpret_cast(windowClass), s_appInstance); -} - -void* -CArchConsoleWindows::threadEntry(void* self) -{ - reinterpret_cast(self)->threadMainLoop(); - return NULL; -} +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 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 "CArchConsoleWindows.h" + +CArchConsoleWindows::CArchConsoleWindows() { } + +CArchConsoleWindows::~CArchConsoleWindows() { } diff --git a/lib/arch/CArchConsoleWindows.h b/lib/arch/CArchConsoleWindows.h index 0d59e6ef..4a2a6ac4 100644 --- a/lib/arch/CArchConsoleWindows.h +++ b/lib/arch/CArchConsoleWindows.h @@ -12,66 +12,14 @@ * GNU General Public License for more details. */ -#ifndef CARCHCONSOLEWINDOWS_H -#define CARCHCONSOLEWINDOWS_H +#pragma once -#define WIN32_LEAN_AND_MEAN - -#include "IArchConsole.h" -#include "IArchMultithread.h" -#include "stddeque.h" -#include +#include "CArchConsoleStd.h" #define ARCH_CONSOLE CArchConsoleWindows -//! Win32 implementation of IArchConsole -class CArchConsoleWindows : public IArchConsole { +class CArchConsoleWindows : public CArchConsoleStd { public: - CArchConsoleWindows(void*); + CArchConsoleWindows(); virtual ~CArchConsoleWindows(); - - // IArchConsole overrides - virtual void openConsole(const char* title); - virtual void closeConsole(); - virtual void showConsole(bool showIfEmpty); - virtual void writeConsole(const char*); - virtual const char* getNewlineForConsole(); - -private: - void clearBuffer(); - void appendBuffer(const char*); - void setSize(int width, int height); - - LRESULT wndProc(HWND, UINT, WPARAM, LPARAM); - static LRESULT CALLBACK - staticWndProc(HWND, UINT, WPARAM, LPARAM); - void threadMainLoop(); - static void* threadEntry(void*); - -private: - typedef std::deque MessageBuffer; - - static CArchConsoleWindows* s_instance; - static HINSTANCE s_appInstance; - - // multithread data - CArchMutex m_mutex; - CArchCond m_condVar; - bool m_ready; - CArchThread m_thread; - - // child thread data - HWND m_frame; - HWND m_hwnd; - LONG m_wChar; - LONG m_hChar; - bool m_show; - - // messages - size_t m_maxLines; - size_t m_maxCharacters; - size_t m_numCharacters; - MessageBuffer m_buffer; }; - -#endif diff --git a/lib/arch/CArchDaemonWindows.cpp b/lib/arch/CArchDaemonWindows.cpp index c308dcb3..65aa3909 100644 --- a/lib/arch/CArchDaemonWindows.cpp +++ b/lib/arch/CArchDaemonWindows.cpp @@ -142,9 +142,12 @@ CArchDaemonWindows::installDaemon(const char* name, throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); } } + else { + // done with service (but only try to close if not null) + CloseServiceHandle(service); + } - // done with service and manager - CloseServiceHandle(service); + // done with manager CloseServiceHandle(mgr); // open the registry key for this service diff --git a/lib/arch/CArchMiscWindows.cpp b/lib/arch/CArchMiscWindows.cpp index b680bb18..49b576c0 100644 --- a/lib/arch/CArchMiscWindows.cpp +++ b/lib/arch/CArchMiscWindows.cpp @@ -1,439 +1,551 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 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 "CArchMiscWindows.h" -#include "CArchDaemonWindows.h" - -#ifndef ES_SYSTEM_REQUIRED -#define ES_SYSTEM_REQUIRED ((DWORD)0x00000001) -#endif -#ifndef ES_DISPLAY_REQUIRED -#define ES_DISPLAY_REQUIRED ((DWORD)0x00000002) -#endif -#ifndef ES_CONTINUOUS -#define ES_CONTINUOUS ((DWORD)0x80000000) -#endif -typedef DWORD EXECUTION_STATE; - -// -// CArchMiscWindows -// - -CArchMiscWindows::CDialogs* CArchMiscWindows::s_dialogs = NULL; -DWORD CArchMiscWindows::s_busyState = 0; -CArchMiscWindows::STES_t CArchMiscWindows::s_stes = NULL; -HICON CArchMiscWindows::s_largeIcon = NULL; -HICON CArchMiscWindows::s_smallIcon = NULL; - -void -CArchMiscWindows::init() -{ - s_dialogs = new CDialogs; - isWindows95Family(); -} - -bool -CArchMiscWindows::isWindows95Family() -{ - static bool init = false; - static bool result = false; - - if (!init) { - OSVERSIONINFO version; - version.dwOSVersionInfoSize = sizeof(version); - if (GetVersionEx(&version) == 0) { - // cannot determine OS; assume windows 95 family - result = true; - } - else { - result = (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); - } - init = true; - } - return result; -} - -bool -CArchMiscWindows::isWindowsModern() -{ - static bool init = false; - static bool result = false; - - if (!init) { - OSVERSIONINFO version; - version.dwOSVersionInfoSize = sizeof(version); - if (GetVersionEx(&version) == 0) { - // cannot determine OS; assume not modern - result = false; - } - else { - result = ((version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS && - version.dwMajorVersion == 4 && - version.dwMinorVersion > 0) || - (version.dwPlatformId == VER_PLATFORM_WIN32_NT && - version.dwMajorVersion > 4)); - } - init = true; - } - return result; -} - -void -CArchMiscWindows::setIcons(HICON largeIcon, HICON smallIcon) -{ - s_largeIcon = largeIcon; - s_smallIcon = smallIcon; -} - -void -CArchMiscWindows::getIcons(HICON& largeIcon, HICON& smallIcon) -{ - largeIcon = s_largeIcon; - smallIcon = s_smallIcon; -} - -int -CArchMiscWindows::runDaemon(RunFunc runFunc) -{ - return CArchDaemonWindows::runDaemon(runFunc); -} - -void -CArchMiscWindows::daemonRunning(bool running) -{ - CArchDaemonWindows::daemonRunning(running); -} - -void -CArchMiscWindows::daemonFailed(int result) -{ - CArchDaemonWindows::daemonFailed(result); -} - -UINT -CArchMiscWindows::getDaemonQuitMessage() -{ - return CArchDaemonWindows::getDaemonQuitMessage(); -} - -HKEY -CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName) -{ - return openKey(key, keyName, false); -} - -HKEY -CArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames) -{ - return openKey(key, keyNames, false); -} - -HKEY -CArchMiscWindows::addKey(HKEY key, const TCHAR* keyName) -{ - return openKey(key, keyName, true); -} - -HKEY -CArchMiscWindows::addKey(HKEY key, const TCHAR* const* keyNames) -{ - return openKey(key, keyNames, true); -} - -HKEY -CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName, bool create) -{ - // ignore if parent is NULL - if (key == NULL) { - return NULL; - } - - // open next key - HKEY newKey; - LONG result = RegOpenKeyEx(key, keyName, 0, - KEY_WRITE | KEY_QUERY_VALUE, &newKey); - if (result != ERROR_SUCCESS && create) { - DWORD disp; - result = RegCreateKeyEx(key, keyName, 0, TEXT(""), - 0, KEY_WRITE | KEY_QUERY_VALUE, - NULL, &newKey, &disp); - } - if (result != ERROR_SUCCESS) { - RegCloseKey(key); - return NULL; - } - - // switch to new key - RegCloseKey(key); - return newKey; -} - -HKEY -CArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames, bool create) -{ - for (size_t i = 0; key != NULL && keyNames[i] != NULL; ++i) { - // open next key - key = openKey(key, keyNames[i], create); - } - return key; -} - -void -CArchMiscWindows::closeKey(HKEY key) -{ - assert(key != NULL); - RegCloseKey(key); -} - -void -CArchMiscWindows::deleteKey(HKEY key, const TCHAR* name) -{ - assert(key != NULL); - assert(name != NULL); - RegDeleteKey(key, name); -} - -void -CArchMiscWindows::deleteValue(HKEY key, const TCHAR* name) -{ - assert(key != NULL); - assert(name != NULL); - RegDeleteValue(key, name); -} - -bool -CArchMiscWindows::hasValue(HKEY key, const TCHAR* name) -{ - DWORD type; - LONG result = RegQueryValueEx(key, name, 0, &type, NULL, NULL); - return (result == ERROR_SUCCESS && - (type == REG_DWORD || type == REG_SZ)); -} - -CArchMiscWindows::EValueType -CArchMiscWindows::typeOfValue(HKEY key, const TCHAR* name) -{ - DWORD type; - LONG result = RegQueryValueEx(key, name, 0, &type, NULL, NULL); - if (result != ERROR_SUCCESS) { - return kNO_VALUE; - } - switch (type) { - case REG_DWORD: - return kUINT; - - case REG_SZ: - return kSTRING; - - case REG_BINARY: - return kBINARY; - - default: - return kUNKNOWN; - } -} - -void -CArchMiscWindows::setValue(HKEY key, - const TCHAR* name, const std::string& value) -{ - assert(key != NULL); - assert(name != NULL); - RegSetValueEx(key, name, 0, REG_SZ, - reinterpret_cast(value.c_str()), - (DWORD)value.size() + 1); -} - -void -CArchMiscWindows::setValue(HKEY key, const TCHAR* name, DWORD value) -{ - assert(key != NULL); - assert(name != NULL); - RegSetValueEx(key, name, 0, REG_DWORD, - reinterpret_cast(&value), - sizeof(DWORD)); -} - -void -CArchMiscWindows::setValueBinary(HKEY key, - const TCHAR* name, const std::string& value) -{ - assert(key != NULL); - assert(name != NULL); - RegSetValueEx(key, name, 0, REG_BINARY, - reinterpret_cast(value.data()), - (DWORD)value.size()); -} - -std::string -CArchMiscWindows::readBinaryOrString(HKEY key, const TCHAR* name, DWORD type) -{ - // get the size of the string - DWORD actualType; - DWORD size = 0; - LONG result = RegQueryValueEx(key, name, 0, &actualType, NULL, &size); - if (result != ERROR_SUCCESS || actualType != type) { - return std::string(); - } - - // if zero size then return empty string - if (size == 0) { - return std::string(); - } - - // allocate space - char* buffer = new char[size]; - - // read it - result = RegQueryValueEx(key, name, 0, &actualType, - reinterpret_cast(buffer), &size); - if (result != ERROR_SUCCESS || actualType != type) { - delete[] buffer; - return std::string(); - } - - // clean up and return value - if (type == REG_SZ && buffer[size - 1] == '\0') { - // don't include terminating nul; std::string will add one. - --size; - } - std::string value(buffer, size); - delete[] buffer; - return value; -} - -std::string -CArchMiscWindows::readValueString(HKEY key, const TCHAR* name) -{ - return readBinaryOrString(key, name, REG_SZ); -} - -std::string -CArchMiscWindows::readValueBinary(HKEY key, const TCHAR* name) -{ - return readBinaryOrString(key, name, REG_BINARY); -} - -DWORD -CArchMiscWindows::readValueInt(HKEY key, const TCHAR* name) -{ - DWORD type; - DWORD value; - DWORD size = sizeof(value); - LONG result = RegQueryValueEx(key, name, 0, &type, - reinterpret_cast(&value), &size); - if (result != ERROR_SUCCESS || type != REG_DWORD) { - return 0; - } - 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; -} - -void -CArchMiscWindows::addBusyState(DWORD busyModes) -{ - s_busyState |= busyModes; - setThreadExecutionState(s_busyState); -} - -void -CArchMiscWindows::removeBusyState(DWORD busyModes) -{ - s_busyState &= ~busyModes; - setThreadExecutionState(s_busyState); -} - -void -CArchMiscWindows::setThreadExecutionState(DWORD busyModes) -{ - // look up function dynamically so we work on older systems - if (s_stes == NULL) { - HINSTANCE kernel = LoadLibrary("kernel32.dll"); - if (kernel != NULL) { - s_stes = reinterpret_cast(GetProcAddress(kernel, - "SetThreadExecutionState")); - } - if (s_stes == NULL) { - s_stes = &CArchMiscWindows::dummySetThreadExecutionState; - } - } - - // convert to STES form - EXECUTION_STATE state = 0; - if ((busyModes & kSYSTEM) != 0) { - state |= ES_SYSTEM_REQUIRED; - } - if ((busyModes & kDISPLAY) != 0) { - state |= ES_DISPLAY_REQUIRED; - } - if (state != 0) { - state |= ES_CONTINUOUS; - } - - // do it - s_stes(state); -} - -DWORD -CArchMiscWindows::dummySetThreadExecutionState(DWORD) -{ - // do nothing - return 0; -} - -void -CArchMiscWindows::wakeupDisplay() -{ - // We can't use ::setThreadExecutionState here because it sets - // ES_CONTINUOUS, which we don't want. - - if (s_stes == NULL) { - HINSTANCE kernel = LoadLibrary("kernel32.dll"); - if (kernel != NULL) { - s_stes = reinterpret_cast(GetProcAddress(kernel, - "SetThreadExecutionState")); - } - if (s_stes == NULL) { - s_stes = &CArchMiscWindows::dummySetThreadExecutionState; - } - } - - s_stes(ES_DISPLAY_REQUIRED); - - // restore the original execution states - setThreadExecutionState(s_busyState); +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 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 "CArchMiscWindows.h" +#include "CArchDaemonWindows.h" +#include "CLog.h" + +#include +#pragma warning(disable: 4099) +#include +#pragma warning(default: 4099) +#include "Version.h" + +// parent process name for services in Vista +#define SERVICE_LAUNCHER "services.exe" + +#ifndef ES_SYSTEM_REQUIRED +#define ES_SYSTEM_REQUIRED ((DWORD)0x00000001) +#endif +#ifndef ES_DISPLAY_REQUIRED +#define ES_DISPLAY_REQUIRED ((DWORD)0x00000002) +#endif +#ifndef ES_CONTINUOUS +#define ES_CONTINUOUS ((DWORD)0x80000000) +#endif +typedef DWORD EXECUTION_STATE; + +// +// CArchMiscWindows +// + +CArchMiscWindows::CDialogs* CArchMiscWindows::s_dialogs = NULL; +DWORD CArchMiscWindows::s_busyState = 0; +CArchMiscWindows::STES_t CArchMiscWindows::s_stes = NULL; +HICON CArchMiscWindows::s_largeIcon = NULL; +HICON CArchMiscWindows::s_smallIcon = NULL; +HINSTANCE CArchMiscWindows::s_instanceWin32 = NULL; + +void +CArchMiscWindows::init() +{ + s_dialogs = new CDialogs; + isWindows95Family(); +} + +bool +CArchMiscWindows::isWindows95Family() +{ + static bool init = false; + static bool result = false; + + if (!init) { + OSVERSIONINFO version; + version.dwOSVersionInfoSize = sizeof(version); + if (GetVersionEx(&version) == 0) { + // cannot determine OS; assume windows 95 family + result = true; + } + else { + result = (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); + } + init = true; + } + return result; +} + +bool +CArchMiscWindows::isWindowsModern() +{ + static bool init = false; + static bool result = false; + + if (!init) { + OSVERSIONINFO version; + version.dwOSVersionInfoSize = sizeof(version); + if (GetVersionEx(&version) == 0) { + // cannot determine OS; assume not modern + result = false; + } + else { + result = ((version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS && + version.dwMajorVersion == 4 && + version.dwMinorVersion > 0) || + (version.dwPlatformId == VER_PLATFORM_WIN32_NT && + version.dwMajorVersion > 4)); + } + init = true; + } + return result; +} + +void +CArchMiscWindows::setIcons(HICON largeIcon, HICON smallIcon) +{ + s_largeIcon = largeIcon; + s_smallIcon = smallIcon; +} + +void +CArchMiscWindows::getIcons(HICON& largeIcon, HICON& smallIcon) +{ + largeIcon = s_largeIcon; + smallIcon = s_smallIcon; +} + +int +CArchMiscWindows::runDaemon(RunFunc runFunc) +{ + return CArchDaemonWindows::runDaemon(runFunc); +} + +void +CArchMiscWindows::daemonRunning(bool running) +{ + CArchDaemonWindows::daemonRunning(running); +} + +void +CArchMiscWindows::daemonFailed(int result) +{ + CArchDaemonWindows::daemonFailed(result); +} + +UINT +CArchMiscWindows::getDaemonQuitMessage() +{ + return CArchDaemonWindows::getDaemonQuitMessage(); +} + +HKEY +CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName) +{ + return openKey(key, keyName, false); +} + +HKEY +CArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames) +{ + return openKey(key, keyNames, false); +} + +HKEY +CArchMiscWindows::addKey(HKEY key, const TCHAR* keyName) +{ + return openKey(key, keyName, true); +} + +HKEY +CArchMiscWindows::addKey(HKEY key, const TCHAR* const* keyNames) +{ + return openKey(key, keyNames, true); +} + +HKEY +CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName, bool create) +{ + // ignore if parent is NULL + if (key == NULL) { + return NULL; + } + + // open next key + HKEY newKey; + LONG result = RegOpenKeyEx(key, keyName, 0, + KEY_WRITE | KEY_QUERY_VALUE, &newKey); + if (result != ERROR_SUCCESS && create) { + DWORD disp; + result = RegCreateKeyEx(key, keyName, 0, TEXT(""), + 0, KEY_WRITE | KEY_QUERY_VALUE, + NULL, &newKey, &disp); + } + if (result != ERROR_SUCCESS) { + RegCloseKey(key); + return NULL; + } + + // switch to new key + RegCloseKey(key); + return newKey; +} + +HKEY +CArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames, bool create) +{ + for (size_t i = 0; key != NULL && keyNames[i] != NULL; ++i) { + // open next key + key = openKey(key, keyNames[i], create); + } + return key; +} + +void +CArchMiscWindows::closeKey(HKEY key) +{ + assert(key != NULL); + if (key==NULL) return; + RegCloseKey(key); +} + +void +CArchMiscWindows::deleteKey(HKEY key, const TCHAR* name) +{ + assert(key != NULL); + assert(name != NULL); + if (key==NULL || name==NULL) return; + RegDeleteKey(key, name); +} + +void +CArchMiscWindows::deleteValue(HKEY key, const TCHAR* name) +{ + assert(key != NULL); + assert(name != NULL); + if (key==NULL || name==NULL) return; + RegDeleteValue(key, name); +} + +bool +CArchMiscWindows::hasValue(HKEY key, const TCHAR* name) +{ + DWORD type; + LONG result = RegQueryValueEx(key, name, 0, &type, NULL, NULL); + return (result == ERROR_SUCCESS && + (type == REG_DWORD || type == REG_SZ)); +} + +CArchMiscWindows::EValueType +CArchMiscWindows::typeOfValue(HKEY key, const TCHAR* name) +{ + DWORD type; + LONG result = RegQueryValueEx(key, name, 0, &type, NULL, NULL); + if (result != ERROR_SUCCESS) { + return kNO_VALUE; + } + switch (type) { + case REG_DWORD: + return kUINT; + + case REG_SZ: + return kSTRING; + + case REG_BINARY: + return kBINARY; + + default: + return kUNKNOWN; + } +} + +void +CArchMiscWindows::setValue(HKEY key, + const TCHAR* name, const std::string& value) +{ + assert(key != NULL); + assert(name != NULL); + if(key ==NULL || name==NULL) return; // TODO: throw exception + RegSetValueEx(key, name, 0, REG_SZ, + reinterpret_cast(value.c_str()), + (DWORD)value.size() + 1); +} + +void +CArchMiscWindows::setValue(HKEY key, const TCHAR* name, DWORD value) +{ + assert(key != NULL); + assert(name != NULL); + if(key ==NULL || name==NULL) return; // TODO: throw exception + RegSetValueEx(key, name, 0, REG_DWORD, + reinterpret_cast(&value), + sizeof(DWORD)); +} + +void +CArchMiscWindows::setValueBinary(HKEY key, + const TCHAR* name, const std::string& value) +{ + assert(key != NULL); + assert(name != NULL); + if(key ==NULL || name==NULL) return; // TODO: throw exception + RegSetValueEx(key, name, 0, REG_BINARY, + reinterpret_cast(value.data()), + (DWORD)value.size()); +} + +std::string +CArchMiscWindows::readBinaryOrString(HKEY key, const TCHAR* name, DWORD type) +{ + // get the size of the string + DWORD actualType; + DWORD size = 0; + LONG result = RegQueryValueEx(key, name, 0, &actualType, NULL, &size); + if (result != ERROR_SUCCESS || actualType != type) { + return std::string(); + } + + // if zero size then return empty string + if (size == 0) { + return std::string(); + } + + // allocate space + char* buffer = new char[size]; + + // read it + result = RegQueryValueEx(key, name, 0, &actualType, + reinterpret_cast(buffer), &size); + if (result != ERROR_SUCCESS || actualType != type) { + delete[] buffer; + return std::string(); + } + + // clean up and return value + if (type == REG_SZ && buffer[size - 1] == '\0') { + // don't include terminating nul; std::string will add one. + --size; + } + std::string value(buffer, size); + delete[] buffer; + return value; +} + +std::string +CArchMiscWindows::readValueString(HKEY key, const TCHAR* name) +{ + return readBinaryOrString(key, name, REG_SZ); +} + +std::string +CArchMiscWindows::readValueBinary(HKEY key, const TCHAR* name) +{ + return readBinaryOrString(key, name, REG_BINARY); +} + +DWORD +CArchMiscWindows::readValueInt(HKEY key, const TCHAR* name) +{ + DWORD type; + DWORD value; + DWORD size = sizeof(value); + LONG result = RegQueryValueEx(key, name, 0, &type, + reinterpret_cast(&value), &size); + if (result != ERROR_SUCCESS || type != REG_DWORD) { + return 0; + } + 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; +} + +void +CArchMiscWindows::addBusyState(DWORD busyModes) +{ + s_busyState |= busyModes; + setThreadExecutionState(s_busyState); +} + +void +CArchMiscWindows::removeBusyState(DWORD busyModes) +{ + s_busyState &= ~busyModes; + setThreadExecutionState(s_busyState); +} + +void +CArchMiscWindows::setThreadExecutionState(DWORD busyModes) +{ + // look up function dynamically so we work on older systems + if (s_stes == NULL) { + HINSTANCE kernel = LoadLibrary("kernel32.dll"); + if (kernel != NULL) { + s_stes = reinterpret_cast(GetProcAddress(kernel, + "SetThreadExecutionState")); + } + if (s_stes == NULL) { + s_stes = &CArchMiscWindows::dummySetThreadExecutionState; + } + } + + // convert to STES form + EXECUTION_STATE state = 0; + if ((busyModes & kSYSTEM) != 0) { + state |= ES_SYSTEM_REQUIRED; + } + if ((busyModes & kDISPLAY) != 0) { + state |= ES_DISPLAY_REQUIRED; + } + if (state != 0) { + state |= ES_CONTINUOUS; + } + + // do it + s_stes(state); +} + +DWORD +CArchMiscWindows::dummySetThreadExecutionState(DWORD) +{ + // do nothing + return 0; +} + +void +CArchMiscWindows::wakeupDisplay() +{ + // We can't use ::setThreadExecutionState here because it sets + // ES_CONTINUOUS, which we don't want. + + if (s_stes == NULL) { + HINSTANCE kernel = LoadLibrary("kernel32.dll"); + if (kernel != NULL) { + s_stes = reinterpret_cast(GetProcAddress(kernel, + "SetThreadExecutionState")); + } + if (s_stes == NULL) { + s_stes = &CArchMiscWindows::dummySetThreadExecutionState; + } + } + + s_stes(ES_DISPLAY_REQUIRED); + + // restore the original execution states + setThreadExecutionState(s_busyState); +} + +bool +CArchMiscWindows::wasLaunchedAsService() +{ + CString name; + if (!getParentProcessName(name)) { + LOG((CLOG_ERR "cannot determine if process was launched as service")); + return false; + } + + return (name == SERVICE_LAUNCHER); +} + +bool +CArchMiscWindows::getParentProcessName(CString &name) +{ + PROCESSENTRY32 parentEntry; + if (!getParentProcessEntry(parentEntry)){ + LOG((CLOG_ERR "could not get entry for parent process")); + return false; + } + + name = parentEntry.szExeFile; + return true; +} + +BOOL WINAPI +CArchMiscWindows::getSelfProcessEntry(PROCESSENTRY32& entry) +{ + // get entry from current PID + return getProcessEntry(entry, GetCurrentProcessId()); +} + +BOOL WINAPI +CArchMiscWindows::getParentProcessEntry(PROCESSENTRY32& entry) +{ + // get the current process, so we can get parent PID + PROCESSENTRY32 selfEntry; + if (!getSelfProcessEntry(selfEntry)) { + return FALSE; + } + + // get entry from parent PID + return getProcessEntry(entry, selfEntry.th32ParentProcessID); +} + +BOOL WINAPI +CArchMiscWindows::getProcessEntry(PROCESSENTRY32& entry, DWORD processID) +{ + // first we need to take a snapshot of the running processes + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (snapshot == INVALID_HANDLE_VALUE) { + LOG((CLOG_ERR "could not get process snapshot (error: %i)", + GetLastError())); + return FALSE; + } + + entry.dwSize = sizeof(PROCESSENTRY32); + + // get the first process, and if we can't do that then it's + // unlikely we can go any further + BOOL gotEntry = Process32First(snapshot, &entry); + if (!gotEntry) { + LOG((CLOG_ERR "could not get first process entry (error: %i)", + GetLastError())); + return FALSE; + } + + while(gotEntry) { + + if (entry.th32ProcessID == processID) { + // found current process + return TRUE; + } + + // now move on to the next entry (when we reach end, loop will stop) + gotEntry = Process32Next(snapshot, &entry); + } + + return FALSE; +} + +HINSTANCE +CArchMiscWindows::instanceWin32() +{ + assert(s_instanceWin32 != NULL); + return s_instanceWin32; +} + +void +CArchMiscWindows::setInstanceWin32(HINSTANCE instance) +{ + assert(instance != NULL); + s_instanceWin32 = instance; } \ No newline at end of file diff --git a/lib/arch/CArchMiscWindows.h b/lib/arch/CArchMiscWindows.h index 5260eaeb..dc1eca02 100644 --- a/lib/arch/CArchMiscWindows.h +++ b/lib/arch/CArchMiscWindows.h @@ -20,7 +20,9 @@ #include "common.h" #include "stdstring.h" #include "stdset.h" -#include +#include +#include +#include "CString.h" //! Miscellaneous win32 functions. class CArchMiscWindows { @@ -164,6 +166,16 @@ public: //! Briefly interrupt power saving static void wakeupDisplay(); + //! Returns true if this process was launched via NT service host. + static bool wasLaunchedAsService(); + + //! Returns true if we got the parent process name. + static bool getParentProcessName(CString &name); + + static HINSTANCE instanceWin32(); + + static void setInstanceWin32(HINSTANCE instance); + private: //! Open and return a registry key, closing the parent key static HKEY openKey(HKEY parent, const TCHAR* child, bool create); @@ -180,6 +192,10 @@ private: static DWORD WINAPI dummySetThreadExecutionState(DWORD); + static BOOL WINAPI getProcessEntry(PROCESSENTRY32& entry, DWORD processID); + static BOOL WINAPI getSelfProcessEntry(PROCESSENTRY32& entry); + static BOOL WINAPI getParentProcessEntry(PROCESSENTRY32& entry); + private: typedef std::set CDialogs; typedef DWORD (WINAPI *STES_t)(DWORD); @@ -189,6 +205,7 @@ private: static STES_t s_stes; static HICON s_largeIcon; static HICON s_smallIcon; + static HINSTANCE s_instanceWin32; }; #endif diff --git a/lib/arch/CArchTaskBarWindows.cpp b/lib/arch/CArchTaskBarWindows.cpp index 7ff2a2c1..8b0f6e8f 100644 --- a/lib/arch/CArchTaskBarWindows.cpp +++ b/lib/arch/CArchTaskBarWindows.cpp @@ -19,6 +19,7 @@ #include "XArch.h" #include #include +#include "CArchAppUtilWindows.h" static const UINT kAddReceiver = WM_USER + 10; static const UINT kRemoveReceiver = WM_USER + 11; @@ -31,17 +32,13 @@ static const UINT kFirstReceiverID = WM_USER + 14; // CArchTaskBarWindows* CArchTaskBarWindows::s_instance = NULL; -HINSTANCE CArchTaskBarWindows::s_appInstance = NULL; -CArchTaskBarWindows::CArchTaskBarWindows(void* appInstance) : +CArchTaskBarWindows::CArchTaskBarWindows() : m_nextID(kFirstReceiverID) { // save the singleton instance s_instance = this; - // save app instance - s_appInstance = reinterpret_cast(appInstance); - // we need a mutex m_mutex = ARCH->newMutex(); @@ -437,7 +434,7 @@ CArchTaskBarWindows::threadMainLoop() classInfo.lpfnWndProc = &CArchTaskBarWindows::staticWndProc; classInfo.cbClsExtra = 0; classInfo.cbWndExtra = sizeof(CArchTaskBarWindows*); - classInfo.hInstance = s_appInstance; + classInfo.hInstance = instanceWin32(); classInfo.hIcon = NULL; classInfo.hCursor = NULL; classInfo.hbrBackground = NULL; @@ -454,7 +451,7 @@ CArchTaskBarWindows::threadMainLoop() 0, 0, 1, 1, NULL, NULL, - s_appInstance, + instanceWin32(), reinterpret_cast(this)); // signal ready @@ -465,7 +462,7 @@ CArchTaskBarWindows::threadMainLoop() // handle failure if (m_hwnd == NULL) { - UnregisterClass(reinterpret_cast(windowClass), s_appInstance); + UnregisterClass(reinterpret_cast(windowClass), instanceWin32()); return; } @@ -481,7 +478,7 @@ CArchTaskBarWindows::threadMainLoop() // clean up removeAllIcons(); DestroyWindow(m_hwnd); - UnregisterClass(reinterpret_cast(windowClass), s_appInstance); + UnregisterClass(reinterpret_cast(windowClass), instanceWin32()); } void* @@ -490,3 +487,8 @@ CArchTaskBarWindows::threadEntry(void* self) reinterpret_cast(self)->threadMainLoop(); return NULL; } + +HINSTANCE CArchTaskBarWindows::instanceWin32() +{ + return CArchMiscWindows::instanceWin32(); +} \ No newline at end of file diff --git a/lib/arch/CArchTaskBarWindows.h b/lib/arch/CArchTaskBarWindows.h index 67e9af17..82aefcb2 100644 --- a/lib/arch/CArchTaskBarWindows.h +++ b/lib/arch/CArchTaskBarWindows.h @@ -28,7 +28,7 @@ //! Win32 implementation of IArchTaskBar class CArchTaskBarWindows : public IArchTaskBar { public: - CArchTaskBarWindows(void*); + CArchTaskBarWindows(); virtual ~CArchTaskBarWindows(); //! Add a dialog window @@ -81,9 +81,10 @@ private: void threadMainLoop(); static void* threadEntry(void*); + HINSTANCE instanceWin32(); + private: static CArchTaskBarWindows* s_instance; - static HINSTANCE s_appInstance; // multithread data CArchMutex m_mutex; diff --git a/lib/arch/CArchTaskBarXWindows.cpp b/lib/arch/CArchTaskBarXWindows.cpp index 6934f271..075c7cba 100644 --- a/lib/arch/CArchTaskBarXWindows.cpp +++ b/lib/arch/CArchTaskBarXWindows.cpp @@ -18,7 +18,7 @@ // CArchTaskBarXWindows // -CArchTaskBarXWindows::CArchTaskBarXWindows(void*) +CArchTaskBarXWindows::CArchTaskBarXWindows() { // do nothing } diff --git a/lib/arch/CArchTaskBarXWindows.h b/lib/arch/CArchTaskBarXWindows.h index abf28012..0b74a817 100644 --- a/lib/arch/CArchTaskBarXWindows.h +++ b/lib/arch/CArchTaskBarXWindows.h @@ -22,7 +22,7 @@ //! X11 implementation of IArchTaskBar class CArchTaskBarXWindows : public IArchTaskBar { public: - CArchTaskBarXWindows(void*); + CArchTaskBarXWindows(); virtual ~CArchTaskBarXWindows(); // IArchTaskBar overrides diff --git a/lib/arch/IArchAppUtil.h b/lib/arch/IArchAppUtil.h new file mode 100644 index 00000000..8b79c86f --- /dev/null +++ b/lib/arch/IArchAppUtil.h @@ -0,0 +1,29 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 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. + */ + +#pragma once + +#include "IInterface.h" + +// TODO: replace with forward declaration if possible +// we need to decouple these classes! +#include "CApp.h" + +class IArchAppUtil : public IInterface { +public: + virtual bool parseArg(const int& argc, const char* const* argv, int& i) = 0; + virtual void adoptApp(CApp* app) = 0; + virtual CApp& app() const = 0; + virtual int run(int argc, char** argv, CreateTaskBarReceiverFunc createTaskBarReceiver) = 0; +}; diff --git a/lib/arch/IArchConsole.h b/lib/arch/IArchConsole.h index 2befb196..76e78853 100644 --- a/lib/arch/IArchConsole.h +++ b/lib/arch/IArchConsole.h @@ -58,13 +58,6 @@ public: */ virtual void writeConsole(const char*) = 0; - //! Returns the newline sequence for the console - /*! - Different consoles use different character sequences for newlines. - This method returns the appropriate newline sequence for the console. - */ - virtual const char* getNewlineForConsole() = 0; - //@} }; diff --git a/lib/arch/IArchTaskBarReceiver.h b/lib/arch/IArchTaskBarReceiver.h index 917f2fbf..e0dd9bff 100644 --- a/lib/arch/IArchTaskBarReceiver.h +++ b/lib/arch/IArchTaskBarReceiver.h @@ -18,6 +18,9 @@ #include "IInterface.h" #include "stdstring.h" +class IScreen; +class INode; + //! Interface for architecture dependent task bar event handling /*! This interface defines the task bar icon event handlers required @@ -84,6 +87,8 @@ public: */ virtual std::string getToolTip() const = 0; + virtual void updateStatus(INode*, const CString& errorMsg) = 0; + //@} }; diff --git a/lib/base/CLog.cpp b/lib/base/CLog.cpp index 783710fb..616545d9 100644 --- a/lib/base/CLog.cpp +++ b/lib/base/CLog.cpp @@ -1,319 +1,300 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2002 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 "CLog.h" -#include "CString.h" -#include "CStringUtil.h" -#include "LogOutputters.h" -#include "CArch.h" -#include "Version.h" -#include -#include -#include -#include - -// names of priorities -static const char* g_priority[] = { - "FATAL", - "ERROR", - "WARNING", - "NOTE", - "INFO", - "DEBUG", - "DEBUG1", - "DEBUG2" - }; - -// number of priorities -static const int g_numPriority = (int)(sizeof(g_priority) / - sizeof(g_priority[0])); - -// the default priority -#if defined(NDEBUG) -static const int g_defaultMaxPriority = 4; -#else -static const int g_defaultMaxPriority = 5; -#endif - -// length of longest string in g_priority -static const int g_maxPriorityLength = 7; - -// length of suffix string (": ") -static const int g_prioritySuffixLength = 2; - -// amount of padded required to fill in the priority prefix -static const int g_priorityPad = g_maxPriorityLength + - g_prioritySuffixLength; - - -// -// CLog -// - -CLog* CLog::s_log = NULL; - -CLog::CLog() -{ - assert(s_log == NULL); - - // create mutex for multithread safe operation - m_mutex = ARCH->newMutex(); - - // other initalization - m_maxPriority = g_defaultMaxPriority; - m_maxNewlineLength = 0; - insert(new CConsoleLogOutputter); -} - -CLog::~CLog() -{ - // clean up - for (COutputterList::iterator index = m_outputters.begin(); - index != m_outputters.end(); ++index) { - delete *index; - } - for (COutputterList::iterator index = m_alwaysOutputters.begin(); - index != m_alwaysOutputters.end(); ++index) { - delete *index; - } - ARCH->closeMutex(m_mutex); - s_log = NULL; -} - -CLog* -CLog::getInstance() -{ - // note -- not thread safe; client must initialize log safely - if (s_log == NULL) { - s_log = new CLog; - } - return s_log; -} - -void -CLog::print(const char* file, int line, const char* fmt, ...) const -{ - // check if fmt begins with a priority argument - int priority = 4; - if (fmt[0] == '%' && fmt[1] == 'z') { - priority = fmt[2] - '\060'; - fmt += 3; - } - - // done if below priority threshold - if (priority > getFilter()) { - return; - } - - // compute prefix padding length - char stack[1024]; - - // compute suffix padding length - int sPad = m_maxNewlineLength; - - // print to buffer, leaving space for a newline at the end and prefix - // at the beginning. - char* buffer = stack; - int len = (int)(sizeof(stack) / sizeof(stack[0])); - while (true) { - // try printing into the buffer - va_list args; - va_start(args, fmt); - int n = ARCH->vsnprintf(buffer, len - sPad, fmt, args); - va_end(args); - - // if the buffer wasn't big enough then make it bigger and try again - if (n < 0 || n > (int)len) { - if (buffer != stack) { - delete[] buffer; - } - len *= 2; - buffer = new char[len]; - } - - // if the buffer was big enough then continue - else { - break; - } - } - - // print the prefix to the buffer. leave space for priority label. - if (file != NULL) { - char message[2048]; - struct tm *tm; - char tmp[220]; - time_t t; - time(&t); - tm = localtime(&t); - sprintf(tmp, "%04i-%02i-%02iT%02i:%02i:%02i", tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); - //strcpy(msg, tmp); - - - sprintf(message, "%s %s: %s\n\t%s,%d", tmp, g_priority[priority], buffer, file, line); -// buffer[pPad - 1] = ' '; - - // discard file and line if priority < 0 - /*if (priority < 0) { - message += pPad - g_priorityPad; - } - */ - // output buffer - output(priority, message); - } else { - output(priority, buffer); - } - - // clean up - if (buffer != stack) { - delete[] buffer; - } -} - -void -CLog::insert(ILogOutputter* outputter, bool alwaysAtHead) -{ - assert(outputter != NULL); - assert(outputter->getNewline() != NULL); - - CArchMutexLock lock(m_mutex); - if (alwaysAtHead) { - m_alwaysOutputters.push_front(outputter); - } - else { - m_outputters.push_front(outputter); - } - int newlineLength = (int)strlen(outputter->getNewline()); - if (newlineLength > m_maxNewlineLength) { - m_maxNewlineLength = newlineLength; - } - outputter->open(kAppVersion); - - // Issue 41 - // don't show log unless user requests it, as some users find this - // feature irritating (i.e. when they lose network connectivity). - // in windows the log window can be displayed by selecting "show log" - // from the synergy system tray icon. - // if this causes problems for other architectures, then a different - // work around should be attempted. - //outputter->show(false); -} - -void -CLog::remove(ILogOutputter* outputter) -{ - CArchMutexLock lock(m_mutex); - m_outputters.remove(outputter); - m_alwaysOutputters.remove(outputter); -} - -void -CLog::pop_front(bool alwaysAtHead) -{ - CArchMutexLock lock(m_mutex); - COutputterList* list = alwaysAtHead ? &m_alwaysOutputters : &m_outputters; - if (!list->empty()) { - delete list->front(); - list->pop_front(); - } -} - -bool -CLog::setFilter(const char* maxPriority) -{ - if (maxPriority != NULL) { - for (int i = 0; i < g_numPriority; ++i) { - if (strcmp(maxPriority, g_priority[i]) == 0) { - setFilter(i); - return true; - } - } - return false; - } - return true; -} - -void -CLog::setFilter(int maxPriority) -{ - CArchMutexLock lock(m_mutex); - m_maxPriority = maxPriority; -} - -int -CLog::getFilter() const -{ - CArchMutexLock lock(m_mutex); - return m_maxPriority; -} - -void -CLog::output(int priority, char* msg) const -{ - assert(priority >= -1 && priority < g_numPriority); - assert(msg != NULL); - - // insert priority label - //int n = -g_prioritySuffixLength; - /* - if (priority >= 0) { - - - n = strlen(g_priority[priority]); - strcpy(msg + g_maxPriorityLength - n, g_priority[priority]); - msg[g_maxPriorityLength + 0] = ':'; - msg[g_maxPriorityLength + 1] = ' '; - msg[g_maxPriorityLength + 1] = ' '; - - - } -*/ - // find end of message - //char* end = msg + g_priorityPad + strlen(msg + g_priorityPad); - int len = (int)strlen(msg); - char* tmp = new char[len+m_maxNewlineLength+1]; - char* end = tmp + len; - strcpy(tmp, msg); - - // write to each outputter - CArchMutexLock lock(m_mutex); - for (COutputterList::const_iterator index = m_alwaysOutputters.begin(); - index != m_alwaysOutputters.end(); - ++index) { - // get outputter - ILogOutputter* outputter = *index; - - // put an appropriate newline at the end - strcpy(end, outputter->getNewline()); - - // write message - outputter->write(static_cast(priority), - tmp /*+ g_maxPriorityLength - n*/); - } - for (COutputterList::const_iterator index = m_outputters.begin(); - index != m_outputters.end(); ++index) { - // get outputter - ILogOutputter* outputter = *index; - - // put an appropriate newline at the end - strcpy(end, outputter->getNewline()); - - // write message and break out of loop if it returns false - if (!outputter->write(static_cast(priority), - tmp /*+ g_maxPriorityLength - n*/)) { - break; - } - } - - delete[] tmp; -} +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 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 "CLog.h" +#include "CString.h" +#include "CStringUtil.h" +#include "LogOutputters.h" +#include "CArch.h" +#include "Version.h" +#include +#include +#include +#include + +// names of priorities +static const char* g_priority[] = { + "FATAL", + "ERROR", + "WARNING", + "NOTE", + "INFO", + "DEBUG", + "DEBUG1", + "DEBUG2", + "DEBUG3", + "DEBUG4", + "DEBUG5" + }; + +// number of priorities +static const int g_numPriority = (int)(sizeof(g_priority) / sizeof(g_priority[0])); + +// the default priority +#if defined(NDEBUG) +static const int g_defaultMaxPriority = 4; +#else +static const int g_defaultMaxPriority = 5; +#endif + +// length of longest string in g_priority +static const int g_maxPriorityLength = 7; + +// length of suffix string (": ") +static const int g_prioritySuffixLength = 2; + +// amount of padded required to fill in the priority prefix +static const int g_priorityPad = g_maxPriorityLength + + g_prioritySuffixLength; + + +// +// CLog +// + +CLog* CLog::s_log = NULL; + +CLog::CLog() +{ + assert(s_log == NULL); + + // create mutex for multithread safe operation + m_mutex = ARCH->newMutex(); + + // other initalization + m_maxPriority = g_defaultMaxPriority; + m_maxNewlineLength = 0; + insert(new CConsoleLogOutputter); +} + +CLog::~CLog() +{ + // clean up + for (COutputterList::iterator index = m_outputters.begin(); + index != m_outputters.end(); ++index) { + delete *index; + } + for (COutputterList::iterator index = m_alwaysOutputters.begin(); + index != m_alwaysOutputters.end(); ++index) { + delete *index; + } + ARCH->closeMutex(m_mutex); + s_log = NULL; +} + +CLog* +CLog::getInstance() +{ + // note -- not thread safe; client must initialize log safely + if (s_log == NULL) { + s_log = new CLog; + } + return s_log; +} + +const char* +CLog::getFilterName() const +{ + return getFilterName(getFilter()); +} + +const char* +CLog::getFilterName(int level) const +{ + return g_priority[level]; +} + +void +CLog::print(const char* file, int line, const char* fmt, ...) +{ + // check if fmt begins with a priority argument + ELevel priority = kINFO; + if (fmt[0] == '%' && fmt[1] == 'z') { + + // 060 in octal is 0 (48 in decimal), so subtracting this converts ascii + // number it a true number. we could use atoi instead, but this is how + // it was done originally. + priority = (ELevel)(fmt[2] - '\060'); // TODO: fix this shit + + // move the pointer on past the debug priority char + fmt += 3; + } + + // done if below priority threshold + if (priority > getFilter()) { + return; + } + + // compute prefix padding length + char stack[1024]; + + // compute suffix padding length + int sPad = m_maxNewlineLength; + + // print to buffer, leaving space for a newline at the end and prefix + // at the beginning. + char* buffer = stack; + int len = (int)(sizeof(stack) / sizeof(stack[0])); + while (true) { + // try printing into the buffer + va_list args; + va_start(args, fmt); + int n = ARCH->vsnprintf(buffer, len - sPad, fmt, args); + va_end(args); + + // if the buffer wasn't big enough then make it bigger and try again + if (n < 0 || n > (int)len) { + if (buffer != stack) { + delete[] buffer; + } + len *= 2; + buffer = new char[len]; + } + + // if the buffer was big enough then continue + else { + break; + } + } + + // print the prefix to the buffer. leave space for priority label. + if (file != NULL) { + char message[2048]; + struct tm *tm; + char tmp[220]; + time_t t; + time(&t); + tm = localtime(&t); + sprintf(tmp, "%04i-%02i-%02iT%02i:%02i:%02i", tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + //strcpy(msg, tmp); + + + sprintf(message, "%s %s: %s\n\t%s,%d", tmp, g_priority[priority], buffer, file, line); +// buffer[pPad - 1] = ' '; + + // discard file and line if priority < 0 + /*if (priority < 0) { + message += pPad - g_priorityPad; + } + */ + // output buffer + output(priority, message); + } else { + output(priority, buffer); + } + + // clean up + if (buffer != stack) { + delete[] buffer; + } +} + +void +CLog::insert(ILogOutputter* outputter, bool alwaysAtHead) +{ + assert(outputter != NULL); + + CArchMutexLock lock(m_mutex); + if (alwaysAtHead) { + m_alwaysOutputters.push_front(outputter); + } + else { + m_outputters.push_front(outputter); + } + + outputter->open(kAppVersion); + + // Issue 41 + // don't show log unless user requests it, as some users find this + // feature irritating (i.e. when they lose network connectivity). + // in windows the log window can be displayed by selecting "show log" + // from the synergy system tray icon. + // if this causes problems for other architectures, then a different + // work around should be attempted. + //outputter->show(false); +} + +void +CLog::remove(ILogOutputter* outputter) +{ + CArchMutexLock lock(m_mutex); + m_outputters.remove(outputter); + m_alwaysOutputters.remove(outputter); +} + +void +CLog::pop_front(bool alwaysAtHead) +{ + CArchMutexLock lock(m_mutex); + COutputterList* list = alwaysAtHead ? &m_alwaysOutputters : &m_outputters; + if (!list->empty()) { + delete list->front(); + list->pop_front(); + } +} + +bool +CLog::setFilter(const char* maxPriority) +{ + if (maxPriority != NULL) { + for (int i = 0; i < g_numPriority; ++i) { + if (strcmp(maxPriority, g_priority[i]) == 0) { + setFilter(i); + return true; + } + } + return false; + } + return true; +} + +void +CLog::setFilter(int maxPriority) +{ + CArchMutexLock lock(m_mutex); + m_maxPriority = maxPriority; +} + +int +CLog::getFilter() const +{ + CArchMutexLock lock(m_mutex); + return m_maxPriority; +} + +void +CLog::output(ELevel priority, char* msg) +{ + assert(priority >= -1 && priority < g_numPriority); + assert(msg != NULL); + if (!msg) return; + + CArchMutexLock lock(m_mutex); + + COutputterList::const_iterator i; + + for (i = m_alwaysOutputters.begin(); i != m_alwaysOutputters.end(); ++i) { + + // write to outputter + (*i)->write(priority, msg); + } + + for (i = m_outputters.begin(); i != m_outputters.end(); ++i) { + + // write to outputter and break out of loop if it returns false + if (!(*i)->write(priority, msg)) { + break; + } + } +} \ No newline at end of file diff --git a/lib/base/CLog.h b/lib/base/CLog.h index 391480e2..06cff03a 100644 --- a/lib/base/CLog.h +++ b/lib/base/CLog.h @@ -19,10 +19,12 @@ #include "IArchMultithread.h" #include "stdlist.h" #include +#include "CArch.h" #define CLOG (CLog::getInstance()) class ILogOutputter; +class CThread; //! Logging facility /*! @@ -44,8 +46,11 @@ public: kNOTE, //!< For messages about notable events kINFO, //!< For informational messages kDEBUG, //!< For important debugging messages - kDEBUG1, //!< For more detailed debugging messages - kDEBUG2 //!< For even more detailed debugging messages + kDEBUG1, //!< For verbosity +1 debugging messages + kDEBUG2, //!< For verbosity +2 debugging messages + kDEBUG3, //!< For verbosity +3 debugging messages + kDEBUG4, //!< For verbosity +4 debugging messages + kDEBUG5 //!< For verbosity +5 debugging messages }; ~CLog(); @@ -109,20 +114,28 @@ public: neither the file nor the line are printed. */ void print(const char* file, int line, - const char* format, ...) const; + const char* format, ...); //! Get the minimum priority level. int getFilter() const; + //! Get the filter name of the current filter level. + const char* getFilterName() const; + + //! Get the filter name of a specified filter level. + const char* getFilterName(int level) const; + //! Get the singleton instance of the log static CLog* getInstance(); + //! Get the console filter level (messages above this are not sent to console). + int getConsoleMaxLevel() const { return kDEBUG1; } //@} private: CLog(); - void output(int priority, char* msg) const; + void output(ELevel priority, char* msg); private: typedef std::list COutputterList; @@ -189,8 +202,13 @@ otherwise it expands to a call that doesn't. #define CLOG_TRACE __FILE__, __LINE__, #endif -#define CLOG_PRINT CLOG_TRACE "%z\057" -#define CLOG_CRIT CLOG_TRACE "%z\060" +// the CLOG_* defines are line and file plus %z and an octal number (060=0, +// 071=9), but the limitation is that once we run out of numbers at either +// end, then we resort to using non-numerical chars. this still works (since +// to deduce the number we subtract octal \060, so '/' is -1, and ':' is 10 + +#define CLOG_PRINT CLOG_TRACE "%z\057" // char is '/' +#define CLOG_CRIT CLOG_TRACE "%z\060" // char is '0' #define CLOG_ERR CLOG_TRACE "%z\061" #define CLOG_WARN CLOG_TRACE "%z\062" #define CLOG_NOTE CLOG_TRACE "%z\063" @@ -198,5 +216,8 @@ otherwise it expands to a call that doesn't. #define CLOG_DEBUG CLOG_TRACE "%z\065" #define CLOG_DEBUG1 CLOG_TRACE "%z\066" #define CLOG_DEBUG2 CLOG_TRACE "%z\067" +#define CLOG_DEBUG3 CLOG_TRACE "%z\070" +#define CLOG_DEBUG4 CLOG_TRACE "%z\071" // char is '9' +#define CLOG_DEBUG5 CLOG_TRACE "%z\072" // char is ':' #endif diff --git a/lib/base/ILogOutputter.h b/lib/base/ILogOutputter.h index 2be4dcc9..c75c643c 100644 --- a/lib/base/ILogOutputter.h +++ b/lib/base/ILogOutputter.h @@ -63,18 +63,6 @@ public: */ virtual bool write(ELevel level, const char* message) = 0; - //@} - //! @name accessors - //@{ - - //! Returns the newline sequence for the outputter - /*! - Different outputters use different character sequences for newlines. - This method returns the appropriate newline sequence for this - outputter. - */ - virtual const char* getNewline() const = 0; - //@} }; diff --git a/lib/base/LogOutputters.cpp b/lib/base/LogOutputters.cpp index 84bb8d85..9aaaae84 100644 --- a/lib/base/LogOutputters.cpp +++ b/lib/base/LogOutputters.cpp @@ -14,6 +14,7 @@ #include "LogOutputters.h" #include "CArch.h" +#include "TMethodJob.h" #include // @@ -54,12 +55,6 @@ CStopLogOutputter::write(ELevel, const char*) return false; } -const char* -CStopLogOutputter::getNewline() const -{ - return ""; -} - // // CConsoleLogOutputter @@ -67,12 +62,10 @@ CStopLogOutputter::getNewline() const CConsoleLogOutputter::CConsoleLogOutputter() { - // do nothing } CConsoleLogOutputter::~CConsoleLogOutputter() { - // do nothing } void @@ -94,16 +87,16 @@ CConsoleLogOutputter::show(bool showIfEmpty) } bool -CConsoleLogOutputter::write(ELevel, const char* msg) +CConsoleLogOutputter::write(ELevel level, const char* msg) { ARCH->writeConsole(msg); - return true; + return true; // wtf? } -const char* -CConsoleLogOutputter::getNewline() const +void +CConsoleLogOutputter::flush() { - return ARCH->getNewlineForConsole(); + } @@ -170,13 +163,6 @@ CSystemLogOutputter::write(ELevel level, const char* msg) return true; } -const char* -CSystemLogOutputter::getNewline() const -{ - return ""; -} - - // // CSystemLogger // @@ -261,12 +247,6 @@ CBufferedLogOutputter::write(ELevel, const char* message) return true; } -const char* -CBufferedLogOutputter::getNewline() const -{ - return ""; -} - // // CFileLogOutputter @@ -287,17 +267,11 @@ CFileLogOutputter::~CFileLogOutputter() m_handle.close(); } -const char* -CFileLogOutputter::getNewline() const -{ - return "\n"; -} - bool CFileLogOutputter::write(ILogOutputter::ELevel level, const char *message) { if (m_handle.is_open() && m_handle.fail() != true) { - m_handle << message; + m_handle << message << std::endl; // write buffer to file m_handle.flush(); diff --git a/lib/base/LogOutputters.h b/lib/base/LogOutputters.h index 9d7629f4..f08b5108 100644 --- a/lib/base/LogOutputters.h +++ b/lib/base/LogOutputters.h @@ -19,8 +19,11 @@ #include "ILogOutputter.h" #include "CString.h" #include "stddeque.h" +#include "CThread.h" +#include #include + //! Stop traversing log chain outputter /*! This outputter performs no output and returns false from \c write(), @@ -37,7 +40,6 @@ public: virtual void close(); virtual void show(bool showIfEmpty); virtual bool write(ELevel level, const char* message); - virtual const char* getNewline() const; }; //! Write log to console @@ -55,7 +57,7 @@ public: virtual void close(); virtual void show(bool showIfEmpty); virtual bool write(ELevel level, const char* message); - virtual const char* getNewline() const; + virtual void flush(); }; //! Write log to file @@ -74,7 +76,6 @@ public: virtual void close(); virtual void show(bool showIfEmpty); virtual bool write(ELevel level, const char* message); - virtual const char* getNewline() const; private: std::ofstream m_handle; }; @@ -93,7 +94,6 @@ public: virtual void close(); virtual void show(bool showIfEmpty); virtual bool write(ELevel level, const char* message); - virtual const char* getNewline() const; }; //! Write log to system log only @@ -144,8 +144,6 @@ public: virtual void close(); virtual void show(bool showIfEmpty); virtual bool write(ELevel level, const char* message); - virtual const char* getNewline() const; - private: UInt32 m_maxBufferSize; CBuffer m_buffer; diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index b19781e2..a118cb6a 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -390,10 +390,10 @@ CClient::sendEvent(CEvent::Type type, void* data) void CClient::sendConnectionFailedEvent(const char* msg) { - CFailInfo* info = (CFailInfo*)malloc(sizeof(CFailInfo) + strlen(msg)); - info->m_retry = true; - strcpy(info->m_what, msg); - sendEvent(getConnectionFailedEvent(), info); + CFailInfo* info = new CFailInfo(msg); + info->m_retry = true; + CEvent event(getConnectionFailedEvent(), getEventTarget(), info, CEvent::kDontFreeData); + EVENTQUEUE->addEvent(event); } void @@ -549,7 +549,8 @@ CClient::handleConnectionFailed(const CEvent& event, void*) delete m_stream; m_stream = NULL; LOG((CLOG_DEBUG1 "connection failed")); - sendConnectionFailedEvent(info->m_what); + sendConnectionFailedEvent(info->m_what.c_str()); + delete info; } void diff --git a/lib/client/CClient.h b/lib/client/CClient.h index 4bd11c66..066fcc61 100644 --- a/lib/client/CClient.h +++ b/lib/client/CClient.h @@ -18,6 +18,7 @@ #include "IClient.h" #include "IClipboard.h" #include "CNetworkAddress.h" +#include "INode.h" class CEventQueueTimer; class CScreen; @@ -31,12 +32,13 @@ class IStreamFilterFactory; /*! This class implements the top-level client algorithms for synergy. */ -class CClient : public IClient { +class CClient : public IClient, public INode { public: class CFailInfo { public: + CFailInfo(const char* what) : m_retry(false), m_what(what) { } bool m_retry; - char m_what[1]; + CString m_what; }; /*! diff --git a/lib/common/common.h b/lib/common/common.h index 0d7818ca..a470bbb5 100644 --- a/lib/common/common.h +++ b/lib/common/common.h @@ -98,6 +98,10 @@ // this one's a little too aggressive # pragma warning(disable: 4127) // conditional expression is constant + // Code Analysis +# pragma warning(disable: 6011) + + // emitted incorrectly under release build in some circumstances # if defined(NDEBUG) # pragma warning(disable: 4702) // unreachable code @@ -129,6 +133,15 @@ // define NULL #include +// we don't want to use NULL since it's old and nasty, so replace any +// usages with nullptr (warning: this could break many things). +// if not c++0x yet, future proof code by allowing use of nullptr +#ifdef nullptr +#define NULL nullptr +#else +#define nullptr NULL +#endif + // make assert available since we use it a lot #include #include diff --git a/lib/net/CTCPListenSocket.cpp b/lib/net/CTCPListenSocket.cpp index 938bf95a..62c43a83 100644 --- a/lib/net/CTCPListenSocket.cpp +++ b/lib/net/CTCPListenSocket.cpp @@ -101,9 +101,9 @@ CTCPListenSocket::getEventTarget() const IDataSocket* CTCPListenSocket::accept() { + IDataSocket* socket = NULL; try { - IDataSocket* socket = - new CTCPSocket(ARCH->acceptSocket(m_socket, NULL)); + socket = new CTCPSocket(ARCH->acceptSocket(m_socket, NULL)); if (socket != NULL) { CSocketMultiplexer::getInstance()->addSocket(this, new TSocketMultiplexerMethodJob( @@ -113,8 +113,17 @@ CTCPListenSocket::accept() return socket; } catch (XArchNetwork&) { + if (socket != NULL) { + delete socket; + } return NULL; } + catch (std::exception &ex) { + if (socket != NULL) { + delete socket; + } + throw ex; + } } ISocketMultiplexerJob* diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index f83e24ea..2b88f7a2 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -346,11 +346,9 @@ CTCPSocket::newJob() void CTCPSocket::sendConnectionFailedEvent(const char* msg) { - CConnectionFailedInfo* info = (CConnectionFailedInfo*)malloc( - sizeof(CConnectionFailedInfo) + strlen(msg)); - strcpy(info->m_what, msg); + CConnectionFailedInfo* info = new CConnectionFailedInfo(msg); EVENTQUEUE->addEvent(CEvent(getConnectionFailedEvent(), - getEventTarget(), info)); + getEventTarget(), info, CEvent::kDontFreeData)); } void diff --git a/lib/net/IDataSocket.h b/lib/net/IDataSocket.h index d760d4ab..21bef9b1 100644 --- a/lib/net/IDataSocket.h +++ b/lib/net/IDataSocket.h @@ -17,6 +17,7 @@ #include "ISocket.h" #include "IStream.h" +#include "CString.h" //! Data stream socket interface /*! @@ -27,8 +28,8 @@ class IDataSocket : public ISocket, public IStream { public: class CConnectionFailedInfo { public: - // pointer to a string describing the failure - char m_what[1]; + CConnectionFailedInfo(const char* what) : m_what(what) { } + CString m_what; }; //! @name manipulators diff --git a/lib/platform/CMSWindowsDesks.cpp b/lib/platform/CMSWindowsDesks.cpp index 1905f122..994692c4 100644 --- a/lib/platform/CMSWindowsDesks.cpp +++ b/lib/platform/CMSWindowsDesks.cpp @@ -86,7 +86,7 @@ // CMSWindowsDesks::CMSWindowsDesks( - bool isPrimary, HINSTANCE hookLibrary, + bool isPrimary, bool noHooks, HINSTANCE hookLibrary, const IScreenSaver* screensaver, IJob* updateKeys) : m_isPrimary(isPrimary), m_is95Family(CArchMiscWindows::isWindows95Family()), @@ -361,7 +361,7 @@ void CMSWindowsDesks::queryHookLibrary(HINSTANCE hookLibrary) { // look up functions - if (m_isPrimary) { + if (m_isPrimary && !m_noHooks) { m_install = (InstallFunc)GetProcAddress(hookLibrary, "install"); m_uninstall = (UninstallFunc)GetProcAddress(hookLibrary, "uninstall"); m_installScreensaver = @@ -736,7 +736,7 @@ CMSWindowsDesks::deskThread(void* vdesk) continue; case SYNERGY_MSG_SWITCH: - if (m_isPrimary) { + if (m_isPrimary && !m_noHooks) { m_uninstall(); if (m_screensaverNotify) { m_uninstallScreensaver(); @@ -816,11 +816,13 @@ CMSWindowsDesks::deskThread(void* vdesk) break; case SYNERGY_MSG_SCREENSAVER: - if (msg.wParam != 0) { - m_installScreensaver(); - } - else { - m_uninstallScreensaver(); + if (!m_noHooks) { + if (msg.wParam != 0) { + m_installScreensaver(); + } + else { + m_uninstallScreensaver(); + } } break; diff --git a/lib/platform/CMSWindowsDesks.h b/lib/platform/CMSWindowsDesks.h index f0388f98..1771d576 100644 --- a/lib/platform/CMSWindowsDesks.h +++ b/lib/platform/CMSWindowsDesks.h @@ -59,7 +59,7 @@ public: updated in a thread attached to the current desk. \p hookLibrary must be a handle to the hook library. */ - CMSWindowsDesks(bool isPrimary, HINSTANCE hookLibrary, + CMSWindowsDesks(bool isPrimary, bool noHooks, HINSTANCE hookLibrary, const IScreenSaver* screensaver, IJob* updateKeys); ~CMSWindowsDesks(); @@ -241,6 +241,9 @@ private: // true if screen is being used as a primary screen, false otherwise bool m_isPrimary; + // true if hooks are not to be installed (useful for debugging) + bool m_noHooks; + // true if windows 95/98/me bool m_is95Family; diff --git a/lib/platform/CMSWindowsScreen.cpp b/lib/platform/CMSWindowsScreen.cpp index ada627d0..87162feb 100644 --- a/lib/platform/CMSWindowsScreen.cpp +++ b/lib/platform/CMSWindowsScreen.cpp @@ -77,8 +77,9 @@ HINSTANCE CMSWindowsScreen::s_instance = NULL; CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL; -CMSWindowsScreen::CMSWindowsScreen(bool isPrimary) : +CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, bool noHooks) : m_isPrimary(isPrimary), + m_noHooks(noHooks), m_is95Family(CArchMiscWindows::isWindows95Family()), m_isOnScreen(m_isPrimary), m_class(0), @@ -118,7 +119,8 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary) : m_hookLibrary = openHookLibrary("synrgyhk"); } m_screensaver = new CMSWindowsScreenSaver(); - m_desks = new CMSWindowsDesks(m_isPrimary, + m_desks = new CMSWindowsDesks( + m_isPrimary, m_noHooks, m_hookLibrary, m_screensaver, new TMethodJob(this, &CMSWindowsScreen::updateKeysCB)); @@ -491,7 +493,7 @@ void CMSWindowsScreen::saveMousePosition(SInt32 x, SInt32 y) { m_xCursor = x; m_yCursor = y; - LOG((CLOG_DEBUG2 "saved mouse position for next delta: %+d,%+d", x,y)); + LOG((CLOG_DEBUG5 "saved mouse position for next delta: %+d,%+d", x,y)); } UInt32 @@ -763,6 +765,7 @@ 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)); @@ -916,7 +919,7 @@ bool CMSWindowsScreen::onPreDispatchPrimary(HWND, UINT message, WPARAM wParam, LPARAM lParam) { - LOG((CLOG_DEBUG2 "handling pre-dispatch primary")); + LOG((CLOG_DEBUG5 "handling pre-dispatch primary")); // handle event switch (message) { @@ -1298,8 +1301,8 @@ CMSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) SInt32 x = mx - m_xCursor; SInt32 y = my - m_yCursor; - LOG((CLOG_DEBUG2 - "handling mouse move; delta motion calc: %+d=(%+d - %+d),%+d=(%+d - %+d)", + LOG((CLOG_DEBUG3 + "mouse move - motion delta: %+d=(%+d - %+d),%+d=(%+d - %+d)", x, mx, m_xCursor, y, my, m_yCursor)); // ignore if the mouse didn't move or if message posted prior @@ -1324,7 +1327,7 @@ CMSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) // center on the server screen. if we don't do this, then the mouse // will always try to return to the original entry point on the // secondary screen. - LOG((CLOG_DEBUG2 "warping server cursor to center: %+d,%+d", m_xCenter, m_yCenter)); + LOG((CLOG_DEBUG5 "warping server cursor to center: %+d,%+d", m_xCenter, m_yCenter)); warpCursorNoFlush(m_xCenter, m_yCenter); // examine the motion. if it's about the distance diff --git a/lib/platform/CMSWindowsScreen.h b/lib/platform/CMSWindowsScreen.h index 629b6c0a..6d29bf96 100644 --- a/lib/platform/CMSWindowsScreen.h +++ b/lib/platform/CMSWindowsScreen.h @@ -32,7 +32,7 @@ class CThread; //! Implementation of IPlatformScreen for Microsoft Windows class CMSWindowsScreen : public CPlatformScreen { public: - CMSWindowsScreen(bool isPrimary); + CMSWindowsScreen(bool isPrimary, bool noHooks); virtual ~CMSWindowsScreen(); //! @name manipulators @@ -216,6 +216,9 @@ private: // true if screen is being used as a primary screen, false otherwise bool m_isPrimary; + // true if hooks are not to be installed (useful for debugging) + bool m_noHooks; + // true if windows 95/98/me bool m_is95Family; diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index b9baf99b..7907b662 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -293,7 +293,7 @@ CServer::adoptClient(CBaseClientProxy* client) // send notification CServer::CScreenConnectedInfo* info = - CServer::CScreenConnectedInfo::alloc(getName(client)); + new CServer::CScreenConnectedInfo(getName(client)); EVENTQUEUE->addEvent(CEvent(CServer::getConnectedEvent(), m_primaryClient->getEventTarget(), info)); } @@ -1638,7 +1638,7 @@ CServer::onMouseUp(ButtonID id) bool CServer::onMouseMovePrimary(SInt32 x, SInt32 y) { - LOG((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y)); + LOG((CLOG_DEBUG4 "onMouseMovePrimary %d,%d", x, y)); // mouse move on primary (server's) screen if (m_active != m_primaryClient) { @@ -2133,22 +2133,6 @@ CServer::CSwitchInDirectionInfo::alloc(EDirection direction) return info; } - -// -// CServer::CScreenConnectedInfo -// - -CServer::CScreenConnectedInfo* -CServer::CScreenConnectedInfo::alloc(const CString& screen) -{ - CScreenConnectedInfo* info = - (CScreenConnectedInfo*)malloc(sizeof(CScreenConnectedInfo) + - screen.size()); - strcpy(info->m_screen, screen.c_str()); - return info; -} - - // // CServer::CKeyboardBroadcastInfo // diff --git a/lib/server/CServer.h b/lib/server/CServer.h index 36cf5e83..03171bf5 100644 --- a/lib/server/CServer.h +++ b/lib/server/CServer.h @@ -25,6 +25,7 @@ #include "stdmap.h" #include "stdset.h" #include "stdvector.h" +#include "INode.h" class CBaseClientProxy; class CEventQueueTimer; @@ -35,7 +36,7 @@ class CInputFilter; /*! This class implements the top-level server algorithms for synergy. */ -class CServer { +class CServer : public INode { public: //! Lock cursor to screen data class CLockCursorToScreenInfo { @@ -70,11 +71,10 @@ public: //! Screen connected data class CScreenConnectedInfo { public: - static CScreenConnectedInfo* alloc(const CString& screen); + CScreenConnectedInfo(CString screen) : m_screen(screen) { } public: - // this is a C-string; this type is a variable size structure - char m_screen[1]; + CString m_screen; // was char[1] }; //! Keyboard broadcast data diff --git a/lib/synergy/CApp.cpp b/lib/synergy/CApp.cpp new file mode 100644 index 00000000..cd69aea5 --- /dev/null +++ b/lib/synergy/CApp.cpp @@ -0,0 +1,273 @@ +/* +* synergy -- mouse and keyboard sharing utility +* Copyright (C) 2002 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 "CApp.h" +#include "CLog.h" +#include "Version.h" +#include "ProtocolTypes.h" +#include "CArch.h" +#include "XBase.h" +#include "XArch.h" +#include "CArchMiscWindows.h" +#include "LogOutputters.h" + +#include +#include + +CApp* CApp::s_instance = nullptr; + +CApp::CApp(CArgsBase* args) : +m_args(args), +m_bye(&exit), +s_taskBarReceiver(NULL), +s_suspended(false) +{ + assert(s_instance == nullptr); + s_instance = this; +} + +CApp::~CApp() +{ + delete m_args; +} + +CApp::CArgsBase::CArgsBase() : +m_daemon(true), +m_backend(false), +m_restartable(true), +m_noHooks(false), +m_pname(NULL), +m_logFilter(NULL), +m_logFile(NULL), +m_display(NULL) +{ +} + +CApp::CArgsBase::~CArgsBase() +{ +} + +bool +CApp::isArg( + int argi, int argc, const char* const* argv, + const char* name1, const char* name2, + int minRequiredParameters) +{ + if ((name1 != NULL && strcmp(argv[argi], name1) == 0) || + (name2 != NULL && strcmp(argv[argi], name2) == 0)) { + // match. check args left. + if (argi + minRequiredParameters >= argc) { + LOG((CLOG_PRINT "%s: missing arguments for `%s'" BYE, + argsBase().m_pname, argv[argi], argsBase().m_pname)); + m_bye(kExitArgs); + } + return true; + } + + // no match + return false; +} + +bool +CApp::parseArg(const int& argc, const char* const* argv, int& i) +{ + if (ARCH->parseArg(argc, argv, i)) { + // handled by platform util + return true; + } + + else if (isArg(i, argc, argv, "-d", "--debug", 1)) { + // change logging level + argsBase().m_logFilter = argv[++i]; + } + + else if (isArg(i, argc, argv, "-l", "--log", 1)) { + argsBase().m_logFile = argv[++i]; + } + + else if (isArg(i, argc, argv, "-f", "--no-daemon")) { + // not a daemon + argsBase().m_daemon = false; + } + + else if (isArg(i, argc, argv, NULL, "--daemon")) { + // daemonize + argsBase().m_daemon = true; + } + + else if (isArg(i, argc, argv, "-n", "--name", 1)) { + // save screen name + argsBase().m_name = argv[++i]; + } + + else if (isArg(i, argc, argv, "-1", "--no-restart")) { + // don't try to restart + argsBase().m_restartable = false; + } + + else if (isArg(i, argc, argv, NULL, "--restart")) { + // try to restart + argsBase().m_restartable = true; + } + + else if (isArg(i, argc, argv, "-z", NULL)) { + argsBase().m_backend = true; + } + + else if (isArg(i, argc, argv, NULL, "--no-hooks")) { + argsBase().m_noHooks = true; + } + + else if (isArg(i, argc, argv, "-h", "--help")) { + help(); + m_bye(kExitSuccess); + } + + else if (isArg(i, argc, argv, NULL, "--version")) { + version(); + m_bye(kExitSuccess); + } + + else { + // option not supported here + return false; + } + + return true; +} + +void +CApp::parseArgs(int argc, const char* const* argv, int& i) +{ + // about these use of assert() here: + // previously an /analyze warning was displayed if we only used assert and + // did not return on failure. however, this warning does not appear to show + // any more (could be because new compiler args have been added). + // the asserts are programmer benefit only; the os should never pass 0 args, + // because the first is always the binary name. the only way assert would + // evaluate to true, is if this parse function were implemented incorrectly, + // which is unlikely because it's old code and has a specific use. + // we should avoid using anything other than assert here, because it will + // look like important code, which it's not really. + assert(argsBase().m_pname != NULL); + assert(argv != NULL); + assert(argc >= 1); + + // set defaults + argsBase().m_name = ARCH->getHostName(); + + // parse options + for (i = 1; i < argc; ++i) { + + if (parseArg(argc, argv, i)) { + continue; + } + + else if (isArg(i, argc, argv, "--", NULL)) { + // remaining arguments are not options + ++i; + break; + } + + else if (argv[i][0] == '-') { + std::cerr << "Unrecognized option: " << argv[i] << std::endl; + m_bye(kExitArgs); + } + + else { + // this and remaining arguments are not options + break; + } + } + + // increase default filter level for daemon. the user must + // explicitly request another level for a daemon. + if (argsBase().m_daemon && argsBase().m_logFilter == NULL) { + argsBase().m_logFilter = "NOTE"; + } +} + +void +CApp::version() +{ + char buffer[500]; + sprintf( + buffer, + "%s %s, protocol version %d.%d\n%s", + argsBase().m_pname, + kVersion, + kProtocolMajorVersion, + kProtocolMinorVersion, + kCopyright + ); + + std::cout << buffer << std::endl; +} + +int +CApp::run(int argc, char** argv, CreateTaskBarReceiverFunc createTaskBarReceiver) +{ +#if SYSAPI_WIN32 + // record window instance for tray icon, etc + CArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL)); +#endif + + CArch arch; + + // install application in to arch + ARCH->adoptApp(this); + + // create an instance of log + CLOG; + + int result; + try { + result = ARCH->run(argc, argv, createTaskBarReceiver); + } + catch (XBase& e) { + LOG((CLOG_CRIT "Uncaught exception: %s\n", e.what())); + result = kExitFailed; + } + catch (XArch& e) { + LOG((CLOG_CRIT "Initialization failed: %s" BYE, e.what().c_str(), argsBase().m_pname)); + result = kExitFailed; + } + catch (std::exception& e) { + LOG((CLOG_CRIT "Uncaught exception: %s\n", e.what())); + result = kExitFailed; + } + catch (...) { + LOG((CLOG_CRIT "Uncaught exception: \n")); + result = kExitFailed; + } + + delete CLOG; + + // not sure i like what's going on here; m_bye could call exit, but it also does + // some other stuff - if we don't return then we get compiler warning (and it's + // not good practice anyway), but the return will never get hit. + m_bye(result); + return result; +} + +int +CApp::daemonMainLoop(int, const char**) +{ +#if SYSAPI_WIN32 + CSystemLogger sysLogger(daemonName(), false); +#else + CSystemLogger sysLogger(daemonName(), true); +#endif + return mainLoop(); +} diff --git a/lib/synergy/CApp.h b/lib/synergy/CApp.h new file mode 100644 index 00000000..c13d0ec3 --- /dev/null +++ b/lib/synergy/CApp.h @@ -0,0 +1,105 @@ +/* +* synergy -- mouse and keyboard sharing utility +* Copyright (C) 2002 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. +*/ + +#pragma once + +#include "common.h" +#include "CString.h" + +class IArchTaskBarReceiver; +class CBufferedLogOutputter; +class ILogOutputter; + +typedef IArchTaskBarReceiver* (*CreateTaskBarReceiverFunc)(const CBufferedLogOutputter*); +typedef int (*StartupFunc)(int, char**); + +class CApp { +public: + class CArgsBase { + public: + CArgsBase(); + virtual ~CArgsBase(); + bool m_daemon; + bool m_backend; + bool m_restartable; + bool m_noHooks; + const char* m_pname; + const char* m_logFilter; + const char* m_logFile; + const char* m_display; + CString m_name; + }; + + CApp(CArgsBase* args); + virtual ~CApp(); + + // Returns args that are common between server and client. + CArgsBase& argsBase() const { return *m_args; } + + // Prints the current compiled version. + virtual void version(); + + // Prints help specific to client or server. + virtual void help() = 0; + + // Parse command line arguments. + virtual void parseArgs(int argc, const char* const* argv) = 0; + + int run(int argc, char** argv, CreateTaskBarReceiverFunc createTaskBarReceiver); + + int daemonMainLoop(int, const char**); + + virtual void loadConfig() = 0; + virtual bool loadConfig(const CString& pathname) = 0; + virtual int mainLoop() = 0; + virtual int foregroundStartup(int argc, char** argv) = 0; + virtual int standardStartup(int argc, char** argv) = 0; + virtual int runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup, CreateTaskBarReceiverFunc createTaskBarReceiver) = 0; + + // Name of the daemon (used for Unix and Windows). + virtual const char* daemonName() const = 0; + + // A description of the daemon (used only on Windows). + virtual const char* daemonInfo() const = 0; + + // Function pointer for function to exit immediately. + // TODO: this is old C code - use inheritance to normalize + void (*m_bye)(int); + + // Returns true if argv[argi] is equal to name1 or name2. + bool isArg(int argi, int argc, const char* const* argv, + const char* name1, const char* name2, + int minRequiredParameters = 0); + + static CApp& instance() { assert(s_instance != nullptr); return *s_instance; } + + bool s_suspended; + IArchTaskBarReceiver* s_taskBarReceiver; + +protected: + virtual void parseArgs(int argc, const char* const* argv, int &i); + virtual bool parseArg(const int& argc, const char* const* argv, int& i); + +private: + CArgsBase* m_args; + static CApp* s_instance; +}; + +#define BYE "\nTry `%s --help' for more information." + +#if WINAPI_MSWINDOWS +#define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_) +#else +#define DAEMON_RUNNING(running_) +#endif diff --git a/lib/synergy/CClientApp.cpp b/lib/synergy/CClientApp.cpp new file mode 100644 index 00000000..fb92dbc2 --- /dev/null +++ b/lib/synergy/CClientApp.cpp @@ -0,0 +1,616 @@ +/* +* synergy -- mouse and keyboard sharing utility +* Copyright (C) 2002 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 "CClientApp.h" +#include "CLog.h" +#include "CArch.h" +#include "XSocket.h" +#include "Version.h" +#include "ProtocolTypes.h" +#include "CString.h" +#include "CScreen.h" +#include "CEvent.h" +#include "CClient.h" +#include "CNetworkAddress.h" +#include "IArchTaskBarReceiver.h" +#include "IEventQueue.h" +#include "TMethodEventJob.h" +#include "CTCPSocketFactory.h" +#include "XScreen.h" +#include "LogOutputters.h" +#include "CSocketMultiplexer.h" +#include "CEventQueue.h" + +#if SYSAPI_WIN32 +#include "CArchMiscWindows.h" +#endif + +#if WINAPI_MSWINDOWS +#include "CMSWindowsScreen.h" +#elif WINAPI_XWINDOWS +#include "CXWindowsScreen.h" +#elif WINAPI_CARBON +#include "COSXScreen.h" +#endif + +#include +#include + +#define RETRY_TIME 1.0 + +CClientApp::CClientApp() : +CApp(new CArgs()), +s_client(NULL), +s_clientScreen(NULL) +{ +} + +CClientApp::~CClientApp() +{ +} + +CClientApp::CArgs::CArgs() : +m_yscroll(0), +m_serverAddress(NULL) +{ +} + +CClientApp::CArgs::~CArgs() +{ +} + +bool +CClientApp::parseArg(const int& argc, const char* const* argv, int& i) +{ + if (CApp::parseArg(argc, argv, i)) { + // found common arg + return true; + } + + else if (isArg(i, argc, argv, NULL, "--camp")) { + // ignore -- included for backwards compatibility + } + + else if (isArg(i, argc, argv, NULL, "--no-camp")) { + // ignore -- included for backwards compatibility + } + + else if (isArg(i, argc, argv, NULL, "--yscroll", 1)) { + // define scroll + args().m_yscroll = atoi(argv[++i]); + } + + else { + // option not supported here + return false; + } + + // argument was valid + return true; +} + +void +CClientApp::parseArgs(int argc, const char* const* argv) +{ + // asserts values, sets defaults, and parses args + int i; + CApp::parseArgs(argc, argv, i); + + // exactly one non-option argument (server-address) + if (i == argc) { + LOG((CLOG_PRINT "%s: a server address or name is required" BYE, + args().m_pname, args().m_pname)); + m_bye(kExitArgs); + } + if (i + 1 != argc) { + LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, + args().m_pname, argv[i], args().m_pname)); + m_bye(kExitArgs); + } + + // save server address + try { + *args().m_serverAddress = CNetworkAddress(argv[i], kDefaultPort); + args().m_serverAddress->resolve(); + } + catch (XSocketAddress& e) { + // allow an address that we can't look up if we're restartable. + // we'll try to resolve the address each time we connect to the + // server. a bad port will never get better. patch by Brent + // Priddy. + if (!args().m_restartable || e.getError() == XSocketAddress::kBadPort) { + LOG((CLOG_PRINT "%s: %s" BYE, + args().m_pname, e.what(), args().m_pname)); + m_bye(kExitFailed); + } + } + + // set log filter + if (!CLOG->setFilter(args().m_logFilter)) { + LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE, + args().m_pname, args().m_logFilter, args().m_pname)); + m_bye(kExitArgs); + } + + // identify system + LOG((CLOG_INFO "%s Client on %s %s", kAppVersion, ARCH->getOSName().c_str(), ARCH->getPlatformName().c_str())); + +#ifdef WIN32 +#ifdef _AMD64_ + LOG((CLOG_WARN "This is an experimental x64 build of %s. Use it at your own risk.", kApplication)); +#endif +#endif + + if (CLOG->getFilter() > CLOG->getConsoleMaxLevel()) { + if (args().m_logFile == NULL) { + LOG((CLOG_WARN "log messages above %s are NOT sent to console (use file logging)", + CLOG->getFilterName(CLOG->getConsoleMaxLevel()))); + } + } +} + +void +CClientApp::help() +{ +#if WINAPI_XWINDOWS +# define USAGE_DISPLAY_ARG \ + " [--display ]" +# define USAGE_DISPLAY_INFO \ + " --display connect to the X server at \n" +#else +# define USAGE_DISPLAY_ARG +# define USAGE_DISPLAY_INFO +#endif + + char buffer[2000]; + sprintf( + buffer, + "Usage: %s" + " [--daemon|--no-daemon]" + " [--debug ]" + USAGE_DISPLAY_ARG + " [--name ]" + " [--yscroll ]" + " [--restart|--no-restart]" + " " + "\n\n" + "Start the synergy mouse/keyboard sharing server.\n" + "\n" + " -d, --debug filter out log messages with priorty below level.\n" + " level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" + " DEBUG, DEBUG1, DEBUG2.\n" + USAGE_DISPLAY_INFO + " -f, --no-daemon run the client in the foreground.\n" + "* --daemon run the client as a daemon.\n" + " -n, --name use screen-name instead the hostname to identify\n" + " ourself to the server.\n" + " --yscroll defines the vertical scrolling delta, which is\n" + " 120 by default.\n" + " -1, --no-restart do not try to restart the client if it fails for\n" + " some reason.\n" + "* --restart restart the client automatically if it fails.\n" + " -l --log write log messages to file.\n" + " -h, --help display this help and exit.\n" + " --version display version information and exit.\n" + "\n" + "* marks defaults.\n" + "\n" + "The server address is of the form: [][:]. The hostname\n" + "must be the address or hostname of the server. The port overrides the\n" + "default port, %d.\n" + "\n" + "Where log messages go depends on the platform and whether or not the\n" + "client is running as a daemon.", + args().m_pname, kDefaultPort + ); + + std::cout << buffer << std::endl; +} + +const char* +CClientApp::daemonName() const +{ +#if SYSAPI_WIN32 + return "Synergy+ Client"; +#elif SYSAPI_UNIX + return "synergyc"; +#endif +} + +const char* +CClientApp::daemonInfo() const +{ +#if SYSAPI_WIN32 + return "Allows another computer to share it's keyboard and mouse with this computer."; +#elif SYSAPI_UNIX + return ""; +#endif +} + +CScreen* +CClientApp::createScreen() +{ +#if WINAPI_MSWINDOWS + return new CScreen(new CMSWindowsScreen(false, args().m_noHooks)); +#elif WINAPI_XWINDOWS + return new CScreen(new CXWindowsScreen(args().m_display, false, args().m_yscroll)); +#elif WINAPI_CARBON + return new CScreen(new COSXScreen(false)); +#endif +} + +void +CClientApp::updateStatus() +{ + s_taskBarReceiver->updateStatus(s_client, ""); +} + + +void +CClientApp::updateStatus(const CString& msg) +{ + s_taskBarReceiver->updateStatus(s_client, msg); +} + + +void +CClientApp::resetRestartTimeout() +{ + // retry time can nolonger be changed + //s_retryTime = 0.0; +} + + +double +CClientApp::nextRestartTimeout() +{ + // retry at a constant rate (Issue 52) + return RETRY_TIME; + + /* + // choose next restart timeout. we start with rapid retries + // then slow down. + if (s_retryTime < 1.0) { + s_retryTime = 1.0; + } + else if (s_retryTime < 3.0) { + s_retryTime = 3.0; + } + else { + s_retryTime = 5.0; + } + return s_retryTime; + */ +} + + +void +CClientApp::handleScreenError(const CEvent&, void*) +{ + LOG((CLOG_CRIT "error on screen")); + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); +} + + +CScreen* +CClientApp::openClientScreen() +{ + CScreen* screen = createScreen(); + EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(), + screen->getEventTarget(), + new TMethodEventJob( + this, &CClientApp::handleScreenError)); + return screen; +} + + +void +CClientApp::closeClientScreen(CScreen* screen) +{ + if (screen != NULL) { + EVENTQUEUE->removeHandler(IScreen::getErrorEvent(), + screen->getEventTarget()); + delete screen; + } +} + + +void +CClientApp::handleClientRestart(const CEvent&, void* vtimer) +{ + // discard old timer + CEventQueueTimer* timer = reinterpret_cast(vtimer); + EVENTQUEUE->deleteTimer(timer); + EVENTQUEUE->removeHandler(CEvent::kTimer, timer); + + // reconnect + startClient(); +} + + +void +CClientApp::scheduleClientRestart(double retryTime) +{ + // install a timer and handler to retry later + LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime)); + CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, timer, + new TMethodEventJob(this, &CClientApp::handleClientRestart, timer)); +} + + +void +CClientApp::handleClientConnected(const CEvent&, void*) +{ + LOG((CLOG_NOTE "connected to server")); + resetRestartTimeout(); + updateStatus(); +} + + +void +CClientApp::handleClientFailed(const CEvent& e, void*) +{ + CClient::CFailInfo* info = + reinterpret_cast(e.getData()); + + updateStatus(CString("Failed to connect to server: ") + info->m_what); + if (!args().m_restartable || !info->m_retry) { + LOG((CLOG_ERR "failed to connect to server: %s", info->m_what.c_str())); + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); + } + else { + LOG((CLOG_WARN "failed to connect to server: %s", info->m_what.c_str())); + if (!s_suspended) { + scheduleClientRestart(nextRestartTimeout()); + } + } + delete info; +} + + +void +CClientApp::handleClientDisconnected(const CEvent&, void*) +{ + LOG((CLOG_NOTE "disconnected from server")); + if (!args().m_restartable) { + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); + } + else if (!s_suspended) { + s_client->connect(); + } + updateStatus(); +} + + +CClient* +CClientApp::openClient(const CString& name, const CNetworkAddress& address, CScreen* screen) +{ + CClient* client = new CClient( + name, address, new CTCPSocketFactory, NULL, screen); + + try { + EVENTQUEUE->adoptHandler( + CClient::getConnectedEvent(), + client->getEventTarget(), + new TMethodEventJob(this, &CClientApp::handleClientConnected)); + + EVENTQUEUE->adoptHandler( + CClient::getConnectionFailedEvent(), + client->getEventTarget(), + new TMethodEventJob(this, &CClientApp::handleClientFailed)); + + EVENTQUEUE->adoptHandler( + CClient::getDisconnectedEvent(), + client->getEventTarget(), + new TMethodEventJob(this, &CClientApp::handleClientDisconnected)); + + } catch (std::bad_alloc &ba) { + delete client; + throw ba; + } + + return client; +} + + +void +CClientApp::closeClient(CClient* client) +{ + if (client == NULL) { + return; + } + + EVENTQUEUE->removeHandler(CClient::getConnectedEvent(), client); + EVENTQUEUE->removeHandler(CClient::getConnectionFailedEvent(), client); + EVENTQUEUE->removeHandler(CClient::getDisconnectedEvent(), client); + delete client; +} + +int +CClientApp::foregroundStartup(int argc, char** argv) +{ + // parse command line + parseArgs(argc, argv); + + // never daemonize + return mainLoop(); +} + +bool +CClientApp::startClient() +{ + double retryTime; + CScreen* clientScreen = NULL; + try { + if (s_clientScreen == NULL) { + clientScreen = openClientScreen(); + s_client = openClient(args().m_name, + *args().m_serverAddress, clientScreen); + s_clientScreen = clientScreen; + LOG((CLOG_NOTE "started client")); + } + s_client->connect(); + updateStatus(); + return true; + } + catch (XScreenUnavailable& e) { + LOG((CLOG_WARN "cannot open secondary screen: %s", e.what())); + closeClientScreen(clientScreen); + updateStatus(CString("Cannot open secondary screen: ") + e.what()); + retryTime = e.getRetryTime(); + } + catch (XScreenOpenFailure& e) { + LOG((CLOG_CRIT "cannot open secondary screen: %s", e.what())); + closeClientScreen(clientScreen); + return false; + } + catch (XBase& e) { + LOG((CLOG_CRIT "failed to start client: %s", e.what())); + closeClientScreen(clientScreen); + return false; + } + + if (args().m_restartable) { + scheduleClientRestart(retryTime); + return true; + } + else { + // don't try again + return false; + } +} + + +void +CClientApp::stopClient() +{ + closeClient(s_client); + closeClientScreen(s_clientScreen); + s_client = NULL; + s_clientScreen = NULL; +} + + +int +CClientApp::mainLoop() +{ + // logging to files + CFileLogOutputter* fileLog = NULL; + + if (args().m_logFile != NULL) { + fileLog = new CFileLogOutputter(args().m_logFile); + + CLOG->insert(fileLog); + + LOG((CLOG_DEBUG1 "Logging to file (%s) enabled", args().m_logFile)); + } + + // create socket multiplexer. this must happen after daemonization + // on unix because threads evaporate across a fork(). + CSocketMultiplexer multiplexer; + + // create the event queue + CEventQueue eventQueue; + + // start the client. if this return false then we've failed and + // we shouldn't retry. + LOG((CLOG_DEBUG1 "starting client")); + if (!startClient()) { + return kExitFailed; + } + + // run event loop. if startClient() failed we're supposed to retry + // later. the timer installed by startClient() will take care of + // that. + CEvent event; + DAEMON_RUNNING(true); + EVENTQUEUE->getEvent(event); + while (event.getType() != CEvent::kQuit) { + EVENTQUEUE->dispatchEvent(event); + CEvent::deleteData(event); + EVENTQUEUE->getEvent(event); + } + DAEMON_RUNNING(false); + + // close down + LOG((CLOG_DEBUG1 "stopping client")); + stopClient(); + updateStatus(); + LOG((CLOG_NOTE "stopped client")); + + if (fileLog) { + CLOG->remove(fileLog); + delete fileLog; + } + + return kExitSuccess; +} + +static +int +daemonMainLoopStatic(int argc, const char** argv) +{ + return CClientApp::instance().daemonMainLoop(argc, argv); +} + +int +CClientApp::standardStartup(int argc, char** argv) +{ + if (!args().m_daemon) { + ARCH->showConsole(false); + } + + // parse command line + parseArgs(argc, argv); + + // daemonize if requested + if (args().m_daemon) { + return ARCH->daemonize(daemonName(), &daemonMainLoopStatic); + } + else { + return mainLoop(); + } +} + +int +CClientApp::runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup, CreateTaskBarReceiverFunc createTaskBarReceiver) +{ + // general initialization + args().m_serverAddress = new CNetworkAddress; + args().m_pname = ARCH->getBasename(argv[0]); + + // install caller's output filter + if (outputter != NULL) { + CLOG->insert(outputter); + } + + // save log messages + // use heap memory because CLog deletes outputters on destruction + CBufferedLogOutputter* logBuffer = new CBufferedLogOutputter(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; + + delete args().m_serverAddress; + return result; +} diff --git a/lib/synergy/CClientApp.h b/lib/synergy/CClientApp.h new file mode 100644 index 00000000..39c67bce --- /dev/null +++ b/lib/synergy/CClientApp.h @@ -0,0 +1,84 @@ +/* +* synergy -- mouse and keyboard sharing utility +* Copyright (C) 2002 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. +*/ + +#pragma once + +#include "CApp.h" + +class CScreen; +class CEvent; +class CClient; +class CNetworkAddress; + +class CClientApp : public CApp { +public: + class CArgs : public CApp::CArgsBase { + public: + CArgs(); + ~CArgs(); + + public: + int m_yscroll; + CNetworkAddress* m_serverAddress; + }; + + CClientApp(); + virtual ~CClientApp(); + + // Parse client specific command line arguments. + void parseArgs(int argc, const char* const* argv); + + // Prints help specific to client. + void help(); + + // Returns arguments that are common and for client. + CArgs& args() const { return (CArgs&)argsBase(); } + + const char* daemonName() const; + const char* daemonInfo() const; + + // TODO: move to server only (not supported on client) + void loadConfig() { } + bool loadConfig(const CString& pathname) { return false; } + + int foregroundStartup(int argc, char** argv); + int standardStartup(int argc, char** argv); + int runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup, CreateTaskBarReceiverFunc createTaskBarReceiver); + CScreen* createScreen(); + void updateStatus(); + void updateStatus(const CString& msg); + void resetRestartTimeout(); + double nextRestartTimeout(); + void handleScreenError(const CEvent&, void*); + CScreen* openClientScreen(); + void closeClientScreen(CScreen* screen); + void handleClientRestart(const CEvent&, void* vtimer); + void scheduleClientRestart(double retryTime); + void handleClientConnected(const CEvent&, void*); + void handleClientFailed(const CEvent& e, void*); + void handleClientDisconnected(const CEvent&, void*); + CClient* openClient(const CString& name, const CNetworkAddress& address, CScreen* screen); + void closeClient(CClient* client); + bool startClient(); + void stopClient(); + int mainLoop(); + + static CClientApp& instance() { return (CClientApp&)CApp::instance(); } + +private: + CClient* s_client; + CScreen* s_clientScreen; + + virtual bool parseArg(const int& argc, const char* const* argv, int& i); +}; diff --git a/cmd/synergyc/CClientTaskBarReceiver.cpp b/lib/synergy/CClientTaskBarReceiver.cpp similarity index 100% rename from cmd/synergyc/CClientTaskBarReceiver.cpp rename to lib/synergy/CClientTaskBarReceiver.cpp diff --git a/cmd/synergyc/CClientTaskBarReceiver.h b/lib/synergy/CClientTaskBarReceiver.h similarity index 89% rename from cmd/synergyc/CClientTaskBarReceiver.h rename to lib/synergy/CClientTaskBarReceiver.h index 0e3440e1..92f4b352 100644 --- a/cmd/synergyc/CClientTaskBarReceiver.h +++ b/lib/synergy/CClientTaskBarReceiver.h @@ -17,8 +17,8 @@ #include "CString.h" #include "IArchTaskBarReceiver.h" - -class CClient; +#include "LogOutputters.h" +#include "CClient.h" //! Implementation of IArchTaskBarReceiver for the synergy server class CClientTaskBarReceiver : public IArchTaskBarReceiver { @@ -35,6 +35,8 @@ public: */ void updateStatus(CClient*, const CString& errorMsg); + void updateStatus(INode* n, const CString& errorMsg) { updateStatus((CClient*)n, errorMsg); } + //@} // IArchTaskBarReceiver overrides @@ -80,4 +82,6 @@ private: CString m_server; }; +IArchTaskBarReceiver* createTaskBarReceiver(const CBufferedLogOutputter* logBuffer); + #endif diff --git a/lib/synergy/CKeyMap.cpp b/lib/synergy/CKeyMap.cpp index 87e64d7f..38effdaf 100644 --- a/lib/synergy/CKeyMap.cpp +++ b/lib/synergy/CKeyMap.cpp @@ -98,7 +98,7 @@ CKeyMap::addKeyEntry(const KeyItem& item) // add item list entries.push_back(items); - LOG((CLOG_DEBUG1 "add key: %04x %d %03x %04x (%04x %04x %04x)%s", newItem.m_id, newItem.m_group, newItem.m_button, newItem.m_client, newItem.m_required, newItem.m_sensitive, newItem.m_generates, newItem.m_dead ? " dead" : "")); + LOG((CLOG_DEBUG3 "add key: %04x %d %03x %04x (%04x %04x %04x)%s", newItem.m_id, newItem.m_group, newItem.m_button, newItem.m_client, newItem.m_required, newItem.m_sensitive, newItem.m_generates, newItem.m_dead ? " dead" : "")); } void diff --git a/lib/synergy/CServerApp.cpp b/lib/synergy/CServerApp.cpp new file mode 100644 index 00000000..2db1af73 --- /dev/null +++ b/lib/synergy/CServerApp.cpp @@ -0,0 +1,969 @@ +/* +* synergy -- mouse and keyboard sharing utility +* Copyright (C) 2002 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 "CServerApp.h" +#include "CLog.h" +#include "CArch.h" +#include "XSocket.h" +#include "Version.h" +#include "IEventQueue.h" +#include "CServer.h" +#include "CClientListener.h" +#include "CClientProxy.h" +#include "TMethodEventJob.h" +#include "CServerTaskBarReceiver.h" +#include "CPrimaryClient.h" +#include "CScreen.h" +#include "CSocketMultiplexer.h" +#include "CEventQueue.h" +#include "LogOutputters.h" +#include "CFunctionEventJob.h" + +#if SYSAPI_WIN32 +#include "CArchMiscWindows.h" +#endif + +#if WINAPI_MSWINDOWS +#include "CMSWindowsScreen.h" +#elif WINAPI_XWINDOWS +#include "CXWindowsScreen.h" +#elif WINAPI_CARBON +#include "COSXScreen.h" +#endif + +#include +#include +#include +#include "XScreen.h" +#include "CTCPSocketFactory.h" + +CEvent::Type CServerApp::s_reloadConfigEvent = CEvent::kUnknown; + +CServerApp::CServerApp() : +CApp(new CArgs()), +s_server(NULL), +s_forceReconnectEvent(CEvent::kUnknown), +s_resetServerEvent(CEvent::kUnknown), +s_serverState(kUninitialized), +s_serverScreen(NULL), +s_primaryClient(NULL), +s_listener(NULL), +s_timer(NULL) +{ +} + +CServerApp::~CServerApp() +{ +} + +CServerApp::CArgs::CArgs() : +m_synergyAddress(NULL), +m_config(NULL) +{ +} + +CServerApp::CArgs::~CArgs() +{ +} + +bool +CServerApp::parseArg(const int& argc, const char* const* argv, int& i) +{ + if (CApp::parseArg(argc, argv, i)) { + // found common arg + return true; + } + + else if (isArg(i, argc, argv, "-a", "--address", 1)) { + // save listen address + try { + *args().m_synergyAddress = CNetworkAddress(argv[i + 1], + kDefaultPort); + args().m_synergyAddress->resolve(); + } + catch (XSocketAddress& e) { + LOG((CLOG_PRINT "%s: %s" BYE, + args().m_pname, e.what(), args().m_pname)); + m_bye(kExitArgs); + } + ++i; + } + + else if (isArg(i, argc, argv, "-c", "--config", 1)) { + // save configuration file path + args().m_configFile = argv[++i]; + } + + else { + // option not supported here + return false; + } + + // argument was valid + return true; +} + +void +CServerApp::parseArgs(int argc, const char* const* argv) +{ + // asserts values, sets defaults, and parses args + int i; + CApp::parseArgs(argc, argv, i); + + // no non-option arguments are allowed + if (i != argc) { + LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, + args().m_pname, argv[i], args().m_pname)); + m_bye(kExitArgs); + } + +#if SYSAPI_WIN32 + // if user wants to run as daemon, but process not launched from service launcher... + if (args().m_daemon && !CArchMiscWindows::wasLaunchedAsService()) { + LOG((CLOG_ERR "cannot launch as daemon if process not started through " + "service host (use '--service start' argument instead)")); + m_bye(kExitArgs); + } +#endif + + // set log filter + if (!CLOG->setFilter(args().m_logFilter)) { + LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE, + args().m_pname, args().m_logFilter, args().m_pname)); + m_bye(kExitArgs); + } + + // identify system + LOG((CLOG_INFO "%s Server on %s %s", kAppVersion, ARCH->getOSName().c_str(), ARCH->getPlatformName().c_str())); + +#ifdef WIN32 +#ifdef _AMD64_ + LOG((CLOG_WARN "This is an experimental x64 build of %s. Use it at your own risk.", kApplication)); +#endif +#endif + + if (CLOG->getFilter() > CLOG->getConsoleMaxLevel()) { + if (args().m_logFile == NULL) { + LOG((CLOG_WARN "log messages above %s are NOT sent to console (use file logging)", + CLOG->getFilterName(CLOG->getConsoleMaxLevel()))); + } + } +} + +void +CServerApp::help() +{ +#if WINAPI_XWINDOWS +# define USAGE_DISPLAY_ARG \ + " [--display ]" +# define USAGE_DISPLAY_INFO \ + " --display connect to the X server at \n" +#else +# define USAGE_DISPLAY_ARG +# define USAGE_DISPLAY_INFO +#endif + +#if SYSAPI_WIN32 + +# define PLATFORM_ARGS \ + " [--daemon|--no-daemon]" +# define PLATFORM_DESC +# define PLATFORM_EXTRA \ + "At least one command line argument is required. If you don't otherwise\n" \ + "need an argument use `--daemon'.\n" \ + "\n" + +#else + +# define PLATFORM_ARGS \ + " [--daemon|--no-daemon]" +# define PLATFORM_DESC +# define PLATFORM_EXTRA + +#endif + + char buffer[2000]; + sprintf( + buffer, + "Usage: %s" + " [--address
]" + " [--config ]" + " [--debug ]" + USAGE_DISPLAY_ARG + " [--name ]" + " [--restart|--no-restart]" + PLATFORM_ARGS + "\n\n" + "Start the synergy mouse/keyboard sharing server.\n" + "\n" + " -a, --address
listen for clients on the given address.\n" + " -c, --config use the named configuration file instead.\n" + " -d, --debug filter out log messages with priorty below level.\n" + " level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" + " DEBUG, DEBUG1, DEBUG2.\n" + USAGE_DISPLAY_INFO + " -f, --no-daemon run the server in the foreground.\n" + "* --daemon run the server as a daemon.\n" + " -n, --name use screen-name instead the hostname to identify\n" + " this screen in the configuration.\n" + " -1, --no-restart do not try to restart the server if it fails for\n" + " some reason.\n" + "* --restart restart the server automatically if it fails.\n" + " -l --log write log messages to file.\n" + PLATFORM_DESC + " -h, --help display this help and exit.\n" + " --version display version information and exit.\n" + "\n" + "* marks defaults.\n" + "\n" + PLATFORM_EXTRA + "The argument for --address is of the form: [][:]. The\n" + "hostname must be the address or hostname of an interface on the system.\n" + "The default is to listen on all interfaces. The port overrides the\n" + "default port, %d.\n" + "\n" + "If no configuration file pathname is provided then the first of the\n" + "following to load successfully sets the configuration:\n" + " %s\n" + " %s\n" + "If no configuration file can be loaded then the configuration uses its\n" + "defaults with just the server screen.\n" + "\n" + "Where log messages go depends on the platform and whether or not the\n" + "server is running as a daemon.", + args().m_pname, kDefaultPort, + ARCH->concatPath(ARCH->getUserDirectory(), USR_CONFIG_NAME).c_str(), + ARCH->concatPath(ARCH->getSystemDirectory(), SYS_CONFIG_NAME).c_str() + ); + + std::cout << buffer << std::endl; +} + +void +CServerApp::reloadSignalHandler(CArch::ESignal, void*) +{ + EVENTQUEUE->addEvent(CEvent(getReloadConfigEvent(), + IEventQueue::getSystemTarget())); +} + +void +CServerApp::reloadConfig(const CEvent&, void*) +{ + LOG((CLOG_DEBUG "reload configuration")); + if (loadConfig(args().m_configFile)) { + if (s_server != NULL) { + s_server->setConfig(*args().m_config); + } + LOG((CLOG_NOTE "reloaded configuration")); + } +} + +void +CServerApp::loadConfig() +{ + bool loaded = false; + + // load the config file, if specified + if (!args().m_configFile.empty()) { + loaded = loadConfig(args().m_configFile); + } + + // load the default configuration if no explicit file given + else { + // get the user's home directory + CString path = ARCH->getUserDirectory(); + if (!path.empty()) { + // complete path + path = ARCH->concatPath(path, USR_CONFIG_NAME); + + // now try loading the user's configuration + if (loadConfig(path)) { + loaded = true; + args().m_configFile = path; + } + } + if (!loaded) { + // try the system-wide config file + path = ARCH->getSystemDirectory(); + if (!path.empty()) { + path = ARCH->concatPath(path, SYS_CONFIG_NAME); + if (loadConfig(path)) { + loaded = true; + args().m_configFile = path; + } + } + } + } + + if (!loaded) { + LOG((CLOG_PRINT "%s: no configuration available", args().m_pname)); + m_bye(kExitConfig); + } +} + +bool +CServerApp::loadConfig(const CString& pathname) +{ + try { + // load configuration + LOG((CLOG_DEBUG "opening configuration \"%s\"", pathname.c_str())); + std::ifstream configStream(pathname.c_str()); + if (!configStream.is_open()) { + // report failure to open configuration as a debug message + // since we try several paths and we expect some to be + // missing. + LOG((CLOG_DEBUG "cannot open configuration \"%s\"", + pathname.c_str())); + return false; + } + configStream >> *args().m_config; + LOG((CLOG_DEBUG "configuration read successfully")); + return true; + } + catch (XConfigRead& e) { + // report error in configuration file + LOG((CLOG_ERR "cannot read configuration \"%s\": %s", + pathname.c_str(), e.what())); + } + return false; +} + +CEvent::Type +CServerApp::getReloadConfigEvent() +{ + return CEvent::registerTypeOnce(s_reloadConfigEvent, "reloadConfig"); +} + +void +CServerApp::forceReconnect(const CEvent&, void*) +{ + if (s_server != NULL) { + s_server->disconnect(); + } +} + +CEvent::Type +CServerApp::getForceReconnectEvent() +{ + return CEvent::registerTypeOnce(s_forceReconnectEvent, "forceReconnect"); +} + +CEvent::Type +CServerApp::getResetServerEvent() +{ + return CEvent::registerTypeOnce(s_resetServerEvent, "resetServer"); +} + +void +CServerApp::handleClientConnected(const CEvent&, void* vlistener) +{ + CClientListener* listener = reinterpret_cast(vlistener); + CClientProxy* client = listener->getNextClient(); + if (client != NULL) { + s_server->adoptClient(client); + updateStatus(); + } +} + +void +CServerApp::handleClientsDisconnected(const CEvent&, void*) +{ + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); +} + +void +CServerApp::closeServer(CServer* server) +{ + if (server == NULL) { + return; + } + + // tell all clients to disconnect + server->disconnect(); + + // wait for clients to disconnect for up to timeout seconds + double timeout = 3.0; + CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(timeout, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, timer, + new TMethodEventJob(this, &CServerApp::handleClientsDisconnected)); + EVENTQUEUE->adoptHandler(CServer::getDisconnectedEvent(), server, + new TMethodEventJob(this, &CServerApp::handleClientsDisconnected)); + CEvent event; + EVENTQUEUE->getEvent(event); + while (event.getType() != CEvent::kQuit) { + EVENTQUEUE->dispatchEvent(event); + CEvent::deleteData(event); + EVENTQUEUE->getEvent(event); + } + EVENTQUEUE->removeHandler(CEvent::kTimer, timer); + EVENTQUEUE->deleteTimer(timer); + EVENTQUEUE->removeHandler(CServer::getDisconnectedEvent(), server); + + // done with server + delete server; +} + +void +CServerApp::stopRetryTimer() +{ + if (s_timer != NULL) { + EVENTQUEUE->deleteTimer(s_timer); + EVENTQUEUE->removeHandler(CEvent::kTimer, NULL); + s_timer = NULL; + } +} + +void +CServerApp::updateStatus() +{ + s_taskBarReceiver->updateStatus(s_server, ""); +} + +void CServerApp::updateStatus( const CString& msg ) +{ + s_taskBarReceiver->updateStatus(s_server, msg); +} + +void +CServerApp::closeClientListener(CClientListener* listen) +{ + if (listen != NULL) { + EVENTQUEUE->removeHandler(CClientListener::getConnectedEvent(), listen); + delete listen; + } +} + +void +CServerApp::stopServer() +{ + if (s_serverState == kStarted) { + closeClientListener(s_listener); + closeServer(s_server); + s_server = NULL; + s_listener = NULL; + s_serverState = kInitialized; + } + else if (s_serverState == kStarting) { + stopRetryTimer(); + s_serverState = kInitialized; + } + assert(s_server == NULL); + assert(s_listener == NULL); +} + +void +CServerApp::closePrimaryClient(CPrimaryClient* primaryClient) +{ + delete primaryClient; +} + +void +CServerApp::closeServerScreen(CScreen* screen) +{ + if (screen != NULL) { + EVENTQUEUE->removeHandler(IScreen::getErrorEvent(), + screen->getEventTarget()); + EVENTQUEUE->removeHandler(IScreen::getSuspendEvent(), + screen->getEventTarget()); + EVENTQUEUE->removeHandler(IScreen::getResumeEvent(), + screen->getEventTarget()); + delete screen; + } +} + +void CServerApp::cleanupServer() +{ + stopServer(); + if (s_serverState == kInitialized) { + closePrimaryClient(s_primaryClient); + closeServerScreen(s_serverScreen); + s_primaryClient = NULL; + s_serverScreen = NULL; + s_serverState = kUninitialized; + } + else if (s_serverState == kInitializing || + s_serverState == kInitializingToStart) { + stopRetryTimer(); + s_serverState = kUninitialized; + } + assert(s_primaryClient == NULL); + assert(s_serverScreen == NULL); + assert(s_serverState == kUninitialized); +} + +void +CServerApp::retryHandler(const CEvent&, void*) +{ + // discard old timer + assert(s_timer != NULL); + stopRetryTimer(); + + // try initializing/starting the server again + switch (s_serverState) { + case kUninitialized: + case kInitialized: + case kStarted: + assert(0 && "bad internal server state"); + break; + + case kInitializing: + LOG((CLOG_DEBUG1 "retry server initialization")); + s_serverState = kUninitialized; + if (!initServer()) { + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); + } + break; + + case kInitializingToStart: + LOG((CLOG_DEBUG1 "retry server initialization")); + s_serverState = kUninitialized; + if (!initServer()) { + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); + } + else if (s_serverState == kInitialized) { + LOG((CLOG_DEBUG1 "starting server")); + if (!startServer()) { + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); + } + } + break; + + case kStarting: + LOG((CLOG_DEBUG1 "retry starting server")); + s_serverState = kInitialized; + if (!startServer()) { + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); + } + break; + } +} + +bool CServerApp::initServer() +{ + // skip if already initialized or initializing + if (s_serverState != kUninitialized) { + return true; + } + + double retryTime; + CScreen* serverScreen = NULL; + CPrimaryClient* primaryClient = NULL; + try { + CString name = args().m_config->getCanonicalName(args().m_name); + serverScreen = openServerScreen(); + primaryClient = openPrimaryClient(name, serverScreen); + s_serverScreen = serverScreen; + s_primaryClient = primaryClient; + s_serverState = kInitialized; + updateStatus(); + return true; + } + catch (XScreenUnavailable& e) { + LOG((CLOG_WARN "cannot open primary screen: %s", e.what())); + closePrimaryClient(primaryClient); + closeServerScreen(serverScreen); + updateStatus(CString("cannot open primary screen: ") + e.what()); + retryTime = e.getRetryTime(); + } + catch (XScreenOpenFailure& e) { + LOG((CLOG_CRIT "cannot open primary screen: %s", e.what())); + closePrimaryClient(primaryClient); + closeServerScreen(serverScreen); + return false; + } + catch (XBase& e) { + LOG((CLOG_CRIT "failed to start server: %s", e.what())); + closePrimaryClient(primaryClient); + closeServerScreen(serverScreen); + return false; + } + + if (args().m_restartable) { + // install a timer and handler to retry later + assert(s_timer == NULL); + LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime)); + s_timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, s_timer, + new TMethodEventJob(this, &CServerApp::retryHandler)); + s_serverState = kInitializing; + return true; + } + else { + // don't try again + return false; + } +} + +CScreen* CServerApp::openServerScreen() +{ + CScreen* screen = createScreen(); + EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(), + screen->getEventTarget(), + new TMethodEventJob( + this, &CServerApp::handleScreenError)); + EVENTQUEUE->adoptHandler(IScreen::getSuspendEvent(), + screen->getEventTarget(), + new TMethodEventJob( + this, &CServerApp::handleSuspend)); + EVENTQUEUE->adoptHandler(IScreen::getResumeEvent(), + screen->getEventTarget(), + new TMethodEventJob( + this, &CServerApp::handleResume)); + return screen; +} + +bool +CServerApp::startServer() +{ + // skip if already started or starting + if (s_serverState == kStarting || s_serverState == kStarted) { + return true; + } + + // initialize if necessary + if (s_serverState != kInitialized) { + if (!initServer()) { + // hard initialization failure + return false; + } + if (s_serverState == kInitializing) { + // not ready to start + s_serverState = kInitializingToStart; + return true; + } + assert(s_serverState == kInitialized); + } + + double retryTime; + CClientListener* listener = NULL; + try { + listener = openClientListener(args().m_config->getSynergyAddress()); + s_server = openServer(*args().m_config, s_primaryClient); + s_listener = listener; + updateStatus(); + LOG((CLOG_NOTE "started server")); + s_serverState = kStarted; + return true; + } + catch (XSocketAddressInUse& e) { + LOG((CLOG_WARN "cannot listen for clients: %s", e.what())); + closeClientListener(listener); + updateStatus(CString("cannot listen for clients: ") + e.what()); + retryTime = 10.0; + } + catch (XBase& e) { + LOG((CLOG_CRIT "failed to start server: %s", e.what())); + closeClientListener(listener); + return false; + } + + if (args().m_restartable) { + // install a timer and handler to retry later + assert(s_timer == NULL); + LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime)); + s_timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, s_timer, + new TMethodEventJob(this, &CServerApp::retryHandler)); + s_serverState = kStarting; + return true; + } + else { + // don't try again + return false; + } +} + +CScreen* +CServerApp::createScreen() +{ +#if WINAPI_MSWINDOWS + return new CScreen(new CMSWindowsScreen(true, args().m_noHooks)); +#elif WINAPI_XWINDOWS + return new CScreen(new CXWindowsScreen(args().m_display, true)); +#elif WINAPI_CARBON + return new CScreen(new COSXScreen(true)); +#endif +} + +CPrimaryClient* +CServerApp::openPrimaryClient(const CString& name, CScreen* screen) +{ + LOG((CLOG_DEBUG1 "creating primary screen")); + return new CPrimaryClient(name, screen); + +} + +void +CServerApp::handleScreenError(const CEvent&, void*) +{ + LOG((CLOG_CRIT "error on screen")); + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); +} + +void +CServerApp::handleSuspend(const CEvent&, void*) +{ + if (!s_suspended) { + LOG((CLOG_INFO "suspend")); + stopServer(); + s_suspended = true; + } +} + +void +CServerApp::handleResume(const CEvent&, void*) +{ + if (s_suspended) { + LOG((CLOG_INFO "resume")); + startServer(); + s_suspended = false; + } +} + +CClientListener* +CServerApp::openClientListener(const CNetworkAddress& address) +{ + CClientListener* listen = + new CClientListener(address, new CTCPSocketFactory, NULL); + EVENTQUEUE->adoptHandler(CClientListener::getConnectedEvent(), listen, + new TMethodEventJob( + this, &CServerApp::handleClientConnected, listen)); + return listen; +} + +CServer* +CServerApp::openServer(const CConfig& config, CPrimaryClient* primaryClient) +{ + CServer* server = new CServer(config, primaryClient); + + try { + EVENTQUEUE->adoptHandler( + CServer::getDisconnectedEvent(), server, + new TMethodEventJob(this, &CServerApp::handleNoClients)); + + } catch (std::bad_alloc &ba) { + delete server; + throw ba; + } + + return server; +} + +void CServerApp::handleNoClients( const CEvent&, void* ) +{ + updateStatus(); +} + +int CServerApp::mainLoop() +{ + // create socket multiplexer. this must happen after daemonization + // on unix because threads evaporate across a fork(). + CSocketMultiplexer multiplexer; + + // create the event queue + CEventQueue eventQueue; + + // logging to files + CFileLogOutputter* fileLog = NULL; + + if (args().m_logFile != NULL) { + fileLog = new CFileLogOutputter(args().m_logFile); + + CLOG->insert(fileLog); + + LOG((CLOG_DEBUG1 "Logging to file (%s) enabled", args().m_logFile)); + } + + // if configuration has no screens then add this system + // as the default + if (args().m_config->begin() == args().m_config->end()) { + args().m_config->addScreen(args().m_name); + } + + // set the contact address, if provided, in the config. + // otherwise, if the config doesn't have an address, use + // the default. + if (args().m_synergyAddress->isValid()) { + args().m_config->setSynergyAddress(*args().m_synergyAddress); + } + else if (!args().m_config->getSynergyAddress().isValid()) { + args().m_config->setSynergyAddress(CNetworkAddress(kDefaultPort)); + } + + // canonicalize the primary screen name + CString primaryName = args().m_config->getCanonicalName(args().m_name); + if (primaryName.empty()) { + LOG((CLOG_CRIT "unknown screen name `%s'", args().m_name.c_str())); + return kExitFailed; + } + + // start the server. if this return false then we've failed and + // we shouldn't retry. + LOG((CLOG_DEBUG1 "starting server")); + if (!startServer()) { + return kExitFailed; + } + + // handle hangup signal by reloading the server's configuration + ARCH->setSignalHandler(CArch::kHANGUP, &reloadSignalHandler, NULL); + EVENTQUEUE->adoptHandler(getReloadConfigEvent(), + IEventQueue::getSystemTarget(), + new TMethodEventJob(this, &CServerApp::reloadConfig)); + + // handle force reconnect event by disconnecting clients. they'll + // reconnect automatically. + EVENTQUEUE->adoptHandler(getForceReconnectEvent(), + IEventQueue::getSystemTarget(), + new TMethodEventJob(this, &CServerApp::forceReconnect)); + + // to work around the sticky meta keys problem, we'll give users + // the option to reset the state of synergys + EVENTQUEUE->adoptHandler(getResetServerEvent(), + IEventQueue::getSystemTarget(), + new TMethodEventJob(this, &CServerApp::resetServer)); + + // run event loop. if startServer() failed we're supposed to retry + // later. the timer installed by startServer() will take care of + // that. + CEvent event; + DAEMON_RUNNING(true); + EVENTQUEUE->getEvent(event); + while (event.getType() != CEvent::kQuit) { + EVENTQUEUE->dispatchEvent(event); + CEvent::deleteData(event); + EVENTQUEUE->getEvent(event); + } + DAEMON_RUNNING(false); + + // close down + LOG((CLOG_DEBUG1 "stopping server")); + EVENTQUEUE->removeHandler(getForceReconnectEvent(), + IEventQueue::getSystemTarget()); + EVENTQUEUE->removeHandler(getReloadConfigEvent(), + IEventQueue::getSystemTarget()); + cleanupServer(); + updateStatus(); + LOG((CLOG_NOTE "stopped server")); + + if (fileLog) { + CLOG->remove(fileLog); + delete fileLog; + } + + return kExitSuccess; +} + +void CServerApp::resetServer(const CEvent&, void*) +{ + LOG((CLOG_DEBUG1 "resetting server")); + stopServer(); + cleanupServer(); + startServer(); +} + +int +CServerApp::runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup, CreateTaskBarReceiverFunc createTaskBarReceiver) +{ + // general initialization + args().m_synergyAddress = new CNetworkAddress; + args().m_config = new CConfig; + args().m_pname = ARCH->getBasename(argv[0]); + + // install caller's output filter + if (outputter != NULL) { + CLOG->insert(outputter); + } + + // save log messages + // use heap memory because CLog deletes outputters on destruction + CBufferedLogOutputter* logBuffer = new CBufferedLogOutputter(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; + + delete args().m_config; + delete args().m_synergyAddress; + return result; +} + +int daemonMainLoopStatic(int argc, const char** argv) { + return CServerApp::instance().daemonMainLoop(argc, argv); +} + +int +CServerApp::standardStartup(int argc, char** argv) +{ + // parse command line + parseArgs(argc, argv); + + // load configuration + loadConfig(); + + // daemonize if requested + if (args().m_daemon) { + return ARCH->daemonize(daemonName(), daemonMainLoopStatic); + } + else { + return mainLoop(); + } +} + +int +CServerApp::foregroundStartup(int argc, char** argv) +{ + // parse command line + parseArgs(argc, argv); + + // load configuration + loadConfig(); + + // never daemonize + return mainLoop(); +} + +static +int +mainLoopStatic() +{ + return CServerApp::instance().mainLoop(); +} + +const char* +CServerApp::daemonName() const +{ +#if SYSAPI_WIN32 + return "Synergy+ Server"; +#elif SYSAPI_UNIX + return "synergys"; +#endif +} + +const char* +CServerApp::daemonInfo() const +{ +#if SYSAPI_WIN32 + return "Shares this computers mouse and keyboard with other computers."; +#elif SYSAPI_UNIX + return ""; +#endif +} + diff --git a/lib/synergy/CServerApp.h b/lib/synergy/CServerApp.h new file mode 100644 index 00000000..2dda6ff7 --- /dev/null +++ b/lib/synergy/CServerApp.h @@ -0,0 +1,130 @@ +/* +* synergy -- mouse and keyboard sharing utility +* Copyright (C) 2002 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. +*/ + +#pragma once + +#include "CApp.h" +#include "CString.h" +#include "CConfig.h" +#include "CNetworkAddress.h" +#include "CArch.h" +#include "IArchMultithread.h" + +enum EServerState { + kUninitialized, + kInitializing, + kInitializingToStart, + kInitialized, + kStarting, + kStarted +}; + +class CServer; +class CScreen; +class CClientListener; +class CEventQueueTimer; +class ILogOutputter; + +class CServerApp : public CApp { +public: + class CArgs : public CApp::CArgsBase { + public: + CArgs(); + ~CArgs(); + + public: + CString m_configFile; + CNetworkAddress* m_synergyAddress; + CConfig* m_config; + }; + + CServerApp(); + virtual ~CServerApp(); + + // Parse server specific command line arguments. + void parseArgs(int argc, const char* const* argv); + + // Prints help specific to server. + void help(); + + // Returns arguments that are common and for server. + CArgs& args() const { return (CArgs&)argsBase(); } + + const char* daemonName() const; + const char* daemonInfo() const; + + // TODO: Document these functions. + static void reloadSignalHandler(CArch::ESignal, void*); + static CEvent::Type getReloadConfigEvent(); + + void reloadConfig(const CEvent&, void*); + void loadConfig(); + bool loadConfig(const CString& pathname); + void forceReconnect(const CEvent&, void*); + CEvent::Type getForceReconnectEvent(); + void resetServer(const CEvent&, void*); + CEvent::Type getResetServerEvent(); + void handleClientConnected(const CEvent&, void* vlistener); + void handleClientsDisconnected(const CEvent&, void*); + void closeServer(CServer* server); + void stopRetryTimer(); + void updateStatus(); + void updateStatus(const CString& msg); + void closeClientListener(CClientListener* listen); + void stopServer(); + void closePrimaryClient(CPrimaryClient* primaryClient); + void closeServerScreen(CScreen* screen); + void cleanupServer(); + bool initServer(); + void retryHandler(const CEvent&, void*); + CScreen* openServerScreen(); + CScreen* createScreen(); + CPrimaryClient* openPrimaryClient(const CString& name, CScreen* screen); + void handleScreenError(const CEvent&, void*); + void handleSuspend(const CEvent&, void*); + void handleResume(const CEvent&, void*); + CClientListener* openClientListener(const CNetworkAddress& address); + CServer* openServer(const CConfig& config, CPrimaryClient* primaryClient); + void handleNoClients(const CEvent&, void*); + bool startServer(); + int mainLoop(); + int runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup, CreateTaskBarReceiverFunc createTaskBarReceiver); + int standardStartup(int argc, char** argv); + int foregroundStartup(int argc, char** argv); + + static CServerApp& instance() { return (CServerApp&)CApp::instance(); } + + // TODO: change s_ to m_ + CServer* s_server; + static CEvent::Type s_reloadConfigEvent; + CEvent::Type s_forceReconnectEvent; + CEvent::Type s_resetServerEvent; + EServerState s_serverState; + CScreen* s_serverScreen; + CPrimaryClient* s_primaryClient; + CClientListener* s_listener; + CEventQueueTimer* s_timer; + +private: + virtual bool parseArg(const int& argc, const char* const* argv, int& i); +}; + +// configuration file name +#if SYSAPI_WIN32 +#define USR_CONFIG_NAME "synergy.sgc" +#define SYS_CONFIG_NAME "synergy.sgc" +#elif SYSAPI_UNIX +#define USR_CONFIG_NAME ".synergy.conf" +#define SYS_CONFIG_NAME "synergy.conf" +#endif diff --git a/cmd/synergys/CServerTaskBarReceiver.cpp b/lib/synergy/CServerTaskBarReceiver.cpp similarity index 87% rename from cmd/synergys/CServerTaskBarReceiver.cpp rename to lib/synergy/CServerTaskBarReceiver.cpp index 6555b214..632c9a8d 100644 --- a/cmd/synergys/CServerTaskBarReceiver.cpp +++ b/lib/synergy/CServerTaskBarReceiver.cpp @@ -131,3 +131,21 @@ CServerTaskBarReceiver::getToolTip() const return ""; } } + +CEvent::Type +CServerTaskBarReceiver::getReloadConfigEvent() +{ + return CServerApp::instance().getReloadConfigEvent(); +} + +CEvent::Type +CServerTaskBarReceiver::getForceReconnectEvent() +{ + return CServerApp::instance().getForceReconnectEvent(); +} + +CEvent::Type +CServerTaskBarReceiver::getResetServerEvent() +{ + return CServerApp::instance().getResetServerEvent(); +} diff --git a/cmd/synergys/CServerTaskBarReceiver.h b/lib/synergy/CServerTaskBarReceiver.h similarity index 84% rename from cmd/synergys/CServerTaskBarReceiver.h rename to lib/synergy/CServerTaskBarReceiver.h index d6ec8571..66b15534 100644 --- a/cmd/synergys/CServerTaskBarReceiver.h +++ b/lib/synergy/CServerTaskBarReceiver.h @@ -18,8 +18,9 @@ #include "CString.h" #include "IArchTaskBarReceiver.h" #include "stdvector.h" - -class CServer; +#include "CEvent.h" +#include "CServerApp.h" +#include "CServer.h" //! Implementation of IArchTaskBarReceiver for the synergy server class CServerTaskBarReceiver : public IArchTaskBarReceiver { @@ -36,6 +37,8 @@ public: */ void updateStatus(CServer*, const CString& errorMsg); + void updateStatus(INode* n, const CString& errorMsg) { updateStatus((CServer*)n, errorMsg); } + //@} // IArchTaskBarReceiver overrides @@ -79,10 +82,17 @@ protected: */ virtual void onStatusChanged(CServer* server); +protected: + CEvent::Type getReloadConfigEvent(); + CEvent::Type getForceReconnectEvent(); + CEvent::Type getResetServerEvent(); + private: EState m_state; CString m_errorMessage; CClients m_clients; }; +IArchTaskBarReceiver* createTaskBarReceiver(const CBufferedLogOutputter* logBuffer); + #endif diff --git a/lib/synergy/INode.h b/lib/synergy/INode.h new file mode 100644 index 00000000..de4ecbd9 --- /dev/null +++ b/lib/synergy/INode.h @@ -0,0 +1,19 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 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 "IInterface.h" + +class INode : IInterface { + +};