Some work toward Issue 27 and Issue 319
This commit is contained in:
parent
fea12827d4
commit
f974d8d680
|
@ -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")
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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.";
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -54,3 +54,10 @@ COSXClientTaskBarReceiver::getIcon() const
|
|||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
IArchTaskBarReceiver*
|
||||
createTaskBarReceiver(const CBufferedLogOutputter* logBuffer)
|
||||
{
|
||||
return new COSXClientTaskBarReceiver(logBuffer);
|
||||
}
|
||||
|
||||
|
|
|
@ -54,3 +54,9 @@ CXWindowsClientTaskBarReceiver::getIcon() const
|
|||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
IArchTaskBarReceiver*
|
||||
createTaskBarReceiver(const CBufferedLogOutputter* logBuffer)
|
||||
{
|
||||
return new CXWindowsClientTaskBarReceiver(logBuffer);
|
||||
}
|
||||
|
|
|
@ -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 <cstring>
|
||||
#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<CEventQueueTimer*>(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<CClient::CFailInfo*>(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 <display>]"
|
||||
# define USAGE_DISPLAY_INFO \
|
||||
" --display <display> connect to the X server at <display>\n"
|
||||
#else
|
||||
# define USAGE_DISPLAY_ARG
|
||||
# define USAGE_DISPLAY_INFO
|
||||
#endif
|
||||
|
||||
LOG((CLOG_PRINT
|
||||
"Usage: %s"
|
||||
" [--daemon|--no-daemon]"
|
||||
" [--debug <level>]"
|
||||
USAGE_DISPLAY_ARG
|
||||
" [--name <screen-name>]"
|
||||
" [--yscroll <delta>]"
|
||||
" [--restart|--no-restart]"
|
||||
" <server-address>"
|
||||
"\n\n"
|
||||
"Start the synergy mouse/keyboard sharing server.\n"
|
||||
"\n"
|
||||
" -d, --debug <level> 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 <screen-name> use screen-name instead the hostname to identify\n"
|
||||
" ourself to the server.\n"
|
||||
" --yscroll <delta> 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 <file> 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: [<hostname>][:<port>]. 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, "<unknown>");
|
||||
//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: <unknown exception>\n"));
|
||||
throw;
|
||||
}
|
||||
CClientApp app;
|
||||
return app.run(argc, argv, createTaskBarReceiver);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#error no main() for platform
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -54,3 +54,9 @@ COSXServerTaskBarReceiver::getIcon() const
|
|||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
IArchTaskBarReceiver*
|
||||
createTaskBarReceiver(const CBufferedLogOutputter* logBuffer)
|
||||
{
|
||||
return new COSXServerTaskBarReceiver(logBuffer);
|
||||
}
|
||||
|
|
|
@ -54,3 +54,9 @@ CXWindowsServerTaskBarReceiver::getIcon() const
|
|||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
IArchTaskBarReceiver*
|
||||
createTaskBarReceiver(const CBufferedLogOutputter* logBuffer)
|
||||
{
|
||||
return new CXWindowsServerTaskBarReceiver(logBuffer);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -83,3 +83,5 @@ release {
|
|||
RCC_DIR = tmp/release
|
||||
}
|
||||
|
||||
Debug:DESTDIR = ../bin/Debug
|
||||
Release:DESTDIR = ../bin/Release
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 281 KiB |
|
@ -1 +1 @@
|
|||
IDI_ICON1 ICON DISCARDABLE "res\win\QSynergy.ico"
|
||||
IDI_ICON1 ICON DISCARDABLE "res\\win\\QSynergy.ico"
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
};
|
|
@ -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 <sstream>
|
||||
#include <iostream>
|
||||
#include <conio.h>
|
||||
|
||||
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();
|
||||
}
|
|
@ -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);
|
|
@ -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 <iostream>
|
||||
|
||||
void
|
||||
CArchConsoleStd::writeConsole(const char* str)
|
||||
{
|
||||
// TODO: we need to use cerr also somehow
|
||||
std::cout << str << std::endl;
|
||||
std::cout.flush();
|
||||
}
|
|
@ -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*);
|
||||
};
|
|
@ -13,49 +13,7 @@
|
|||
*/
|
||||
|
||||
#include "CArchConsoleUnix.h"
|
||||
#include <cstdio>
|
||||
#include <stdio.h>
|
||||
|
||||
//
|
||||
// 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() { }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <richedit.h>
|
||||
|
||||
#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<HINSTANCE>(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<WPARAM>(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<LPARAM>(&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<LONG>(removedCharacters);
|
||||
SendMessage(m_hwnd, EM_EXSETSEL, 0, reinterpret_cast<LPARAM>(&range));
|
||||
SendMessage(m_hwnd, EM_REPLACESEL, FALSE, reinterpret_cast<LPARAM>(""));
|
||||
|
||||
// adjust selection
|
||||
if (selection.cpMin < static_cast<LONG>(removedCharacters) ||
|
||||
selection.cpMax < static_cast<LONG>(removedCharacters)) {
|
||||
selection.cpMin = 0;
|
||||
selection.cpMax = 0;
|
||||
}
|
||||
else {
|
||||
selection.cpMin -= static_cast<LONG>(removedCharacters);
|
||||
selection.cpMax -= static_cast<LONG>(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<LPARAM>(&range));
|
||||
SendMessage(m_hwnd, EM_REPLACESEL, FALSE,
|
||||
reinterpret_cast<LPARAM>(m_buffer.back().c_str()));
|
||||
|
||||
// adjust selection
|
||||
bool atEnd = false;
|
||||
if (selection.cpMax == static_cast<LONG>(m_numCharacters)) {
|
||||
selection.cpMin = static_cast<LONG>(newNumCharacters);
|
||||
selection.cpMax = static_cast<LONG>(newNumCharacters);
|
||||
atEnd = true;
|
||||
}
|
||||
|
||||
// restore the selection
|
||||
SendMessage(m_hwnd, EM_EXSETSEL, 0, reinterpret_cast<LPARAM>(&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<const char*>(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<LPCTSTR>(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<LPARAM>(&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<LPCTSTR>(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<LPCTSTR>(windowClass), s_appInstance);
|
||||
}
|
||||
|
||||
void*
|
||||
CArchConsoleWindows::threadEntry(void* self)
|
||||
{
|
||||
reinterpret_cast<CArchConsoleWindows*>(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() { }
|
||||
|
|
|
@ -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 <windows.h>
|
||||
#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<std::string> 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<const BYTE*>(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<CONST BYTE*>(&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<const BYTE*>(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<BYTE*>(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<BYTE*>(&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<STES_t>(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<STES_t>(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 <Wtsapi32.h>
|
||||
#pragma warning(disable: 4099)
|
||||
#include <Userenv.h>
|
||||
#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<const BYTE*>(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<CONST BYTE*>(&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<const BYTE*>(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<BYTE*>(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<BYTE*>(&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<STES_t>(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<STES_t>(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;
|
||||
}
|
|
@ -20,7 +20,9 @@
|
|||
#include "common.h"
|
||||
#include "stdstring.h"
|
||||
#include "stdset.h"
|
||||
#include <windows.h>
|
||||
#include <windows.h>
|
||||
#include <Tlhelp32.h>
|
||||
#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<HWND> 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
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "XArch.h"
|
||||
#include <string.h>
|
||||
#include <shellapi.h>
|
||||
#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<HINSTANCE>(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<void*>(this));
|
||||
|
||||
// signal ready
|
||||
|
@ -465,7 +462,7 @@ CArchTaskBarWindows::threadMainLoop()
|
|||
|
||||
// handle failure
|
||||
if (m_hwnd == NULL) {
|
||||
UnregisterClass(reinterpret_cast<LPCTSTR>(windowClass), s_appInstance);
|
||||
UnregisterClass(reinterpret_cast<LPCTSTR>(windowClass), instanceWin32());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -481,7 +478,7 @@ CArchTaskBarWindows::threadMainLoop()
|
|||
// clean up
|
||||
removeAllIcons();
|
||||
DestroyWindow(m_hwnd);
|
||||
UnregisterClass(reinterpret_cast<LPCTSTR>(windowClass), s_appInstance);
|
||||
UnregisterClass(reinterpret_cast<LPCTSTR>(windowClass), instanceWin32());
|
||||
}
|
||||
|
||||
void*
|
||||
|
@ -490,3 +487,8 @@ CArchTaskBarWindows::threadEntry(void* self)
|
|||
reinterpret_cast<CArchTaskBarWindows*>(self)->threadMainLoop();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HINSTANCE CArchTaskBarWindows::instanceWin32()
|
||||
{
|
||||
return CArchMiscWindows::instanceWin32();
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
// CArchTaskBarXWindows
|
||||
//
|
||||
|
||||
CArchTaskBarXWindows::CArchTaskBarXWindows(void*)
|
||||
CArchTaskBarXWindows::CArchTaskBarXWindows()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
//! X11 implementation of IArchTaskBar
|
||||
class CArchTaskBarXWindows : public IArchTaskBar {
|
||||
public:
|
||||
CArchTaskBarXWindows(void*);
|
||||
CArchTaskBarXWindows();
|
||||
virtual ~CArchTaskBarXWindows();
|
||||
|
||||
// IArchTaskBar overrides
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
|
||||
//@}
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
//@}
|
||||
};
|
||||
|
||||
|
|
|
@ -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 <cstdio>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <ctime>
|
||||
|
||||
// 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<ILogOutputter::ELevel>(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<ILogOutputter::ELevel>(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 <cstdio>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <ctime>
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,10 +19,12 @@
|
|||
#include "IArchMultithread.h"
|
||||
#include "stdlist.h"
|
||||
#include <stdarg.h>
|
||||
#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<ILogOutputter*> 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
|
||||
|
|
|
@ -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;
|
||||
|
||||
//@}
|
||||
};
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "LogOutputters.h"
|
||||
#include "CArch.h"
|
||||
#include "TMethodJob.h"
|
||||
|
||||
#include <fstream>
|
||||
//
|
||||
|
@ -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();
|
||||
|
|
|
@ -19,8 +19,11 @@
|
|||
#include "ILogOutputter.h"
|
||||
#include "CString.h"
|
||||
#include "stddeque.h"
|
||||
#include "CThread.h"
|
||||
|
||||
#include <list>
|
||||
#include <fstream>
|
||||
|
||||
//! 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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
|
|
@ -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 <stddef.h>
|
||||
|
||||
// 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 <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
@ -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<CTCPListenSocket>(
|
||||
|
@ -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*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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<CMSWindowsScreen>(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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
//
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <iostream>
|
||||
#include <stdio.h>
|
||||
|
||||
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: <unknown 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();
|
||||
}
|
|
@ -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
|
|
@ -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 <iostream>
|
||||
#include <stdio.h>
|
||||
|
||||
#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 <display>]"
|
||||
# define USAGE_DISPLAY_INFO \
|
||||
" --display <display> connect to the X server at <display>\n"
|
||||
#else
|
||||
# define USAGE_DISPLAY_ARG
|
||||
# define USAGE_DISPLAY_INFO
|
||||
#endif
|
||||
|
||||
char buffer[2000];
|
||||
sprintf(
|
||||
buffer,
|
||||
"Usage: %s"
|
||||
" [--daemon|--no-daemon]"
|
||||
" [--debug <level>]"
|
||||
USAGE_DISPLAY_ARG
|
||||
" [--name <screen-name>]"
|
||||
" [--yscroll <delta>]"
|
||||
" [--restart|--no-restart]"
|
||||
" <server-address>"
|
||||
"\n\n"
|
||||
"Start the synergy mouse/keyboard sharing server.\n"
|
||||
"\n"
|
||||
" -d, --debug <level> 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 <screen-name> use screen-name instead the hostname to identify\n"
|
||||
" ourself to the server.\n"
|
||||
" --yscroll <delta> 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 <file> 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: [<hostname>][:<port>]. 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<CClientApp>(
|
||||
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<CEventQueueTimer*>(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<CClientApp>(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<CClient::CFailInfo*>(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<CClientApp>(this, &CClientApp::handleClientConnected));
|
||||
|
||||
EVENTQUEUE->adoptHandler(
|
||||
CClient::getConnectionFailedEvent(),
|
||||
client->getEventTarget(),
|
||||
new TMethodEventJob<CClientApp>(this, &CClientApp::handleClientFailed));
|
||||
|
||||
EVENTQUEUE->adoptHandler(
|
||||
CClient::getDisconnectedEvent(),
|
||||
client->getEventTarget(),
|
||||
new TMethodEventJob<CClientApp>(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;
|
||||
}
|
|
@ -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);
|
||||
};
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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 <iostream>
|
||||
#include <stdio.h>
|
||||
#include <fstream>
|
||||
#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 <display>]"
|
||||
# define USAGE_DISPLAY_INFO \
|
||||
" --display <display> connect to the X server at <display>\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 <address>]"
|
||||
" [--config <pathname>]"
|
||||
" [--debug <level>]"
|
||||
USAGE_DISPLAY_ARG
|
||||
" [--name <screen-name>]"
|
||||
" [--restart|--no-restart]"
|
||||
PLATFORM_ARGS
|
||||
"\n\n"
|
||||
"Start the synergy mouse/keyboard sharing server.\n"
|
||||
"\n"
|
||||
" -a, --address <address> listen for clients on the given address.\n"
|
||||
" -c, --config <pathname> use the named configuration file instead.\n"
|
||||
" -d, --debug <level> 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 <screen-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 <file> 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: [<hostname>][:<port>]. 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<CClientListener*>(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<CServerApp>(this, &CServerApp::handleClientsDisconnected));
|
||||
EVENTQUEUE->adoptHandler(CServer::getDisconnectedEvent(), server,
|
||||
new TMethodEventJob<CServerApp>(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<CServerApp>(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<CServerApp>(
|
||||
this, &CServerApp::handleScreenError));
|
||||
EVENTQUEUE->adoptHandler(IScreen::getSuspendEvent(),
|
||||
screen->getEventTarget(),
|
||||
new TMethodEventJob<CServerApp>(
|
||||
this, &CServerApp::handleSuspend));
|
||||
EVENTQUEUE->adoptHandler(IScreen::getResumeEvent(),
|
||||
screen->getEventTarget(),
|
||||
new TMethodEventJob<CServerApp>(
|
||||
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<CServerApp>(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<CServerApp>(
|
||||
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<CServerApp>(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<CServerApp>(this, &CServerApp::reloadConfig));
|
||||
|
||||
// handle force reconnect event by disconnecting clients. they'll
|
||||
// reconnect automatically.
|
||||
EVENTQUEUE->adoptHandler(getForceReconnectEvent(),
|
||||
IEventQueue::getSystemTarget(),
|
||||
new TMethodEventJob<CServerApp>(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<CServerApp>(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
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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 {
|
||||
|
||||
};
|
Loading…
Reference in New Issue