Some work toward Issue 27 and Issue 319

This commit is contained in:
Nick Bolton 2010-05-31 21:30:29 +00:00
parent fea12827d4
commit f974d8d680
74 changed files with 4057 additions and 3756 deletions

View File

@ -1,7 +1,7 @@
# Version number for Synergy+ # Version number for Synergy+
SET(VERSION_MAJOR 1) SET(VERSION_MAJOR 1)
SET(VERSION_MINOR 3) SET(VERSION_MINOR 4)
SET(VERSION_REV 5) SET(VERSION_REV 0)
SET(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") SET(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}")
@ -44,11 +44,28 @@ INCLUDE(${cmake_dir}/CMakeLists_cpack.txt)
IF(WIN32) IF(WIN32)
# add /analyze in order to unconver potential bugs in the source code # add /analyze in order to unconver potential bugs in the source code
# Details: http://msdn.microsoft.com/en-us/library/fwkeyyhe.aspx # Details: http://msdn.microsoft.com/en-us/library/fwkeyyhe.aspx
SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /analyze") # add /FR to generate browse information (ncb files) usefull for using IDE
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /analyze")
#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}")
ENDIF(WIN32)
# this line removes "/D NDEBUG" from release, we want them in order to find bugs even on release builds. # 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") SET(CMAKE_CXX_FLAGS_RELEASE "/MD /O2 /Ob2")
ENDIF(WIN32)

View File

@ -17,6 +17,18 @@ INSTALL(
TARGETS ${cpack_targets} TARGETS ${cpack_targets}
RUNTIME DESTINATION bin) 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 # The default CPack behaviour is not to append the system processor
# type, which is undesirable in our case, since we want to support # type, which is undesirable in our case, since we want to support
# both 32-bit and 64-bit processors. # both 32-bit and 64-bit processors.
@ -62,7 +74,7 @@ IF(WIN32)
SET(CPACK_NSIS_MUI_UNIICON ${WIN32_ICON}) SET(CPACK_NSIS_MUI_UNIICON ${WIN32_ICON})
SET(CPACK_NSIS_INSTALLED_ICON_NAME launcher) SET(CPACK_NSIS_INSTALLED_ICON_NAME launcher)
SET(CPACK_PACKAGE_INSTALL_DIRECTORY "Synergy+") SET(CPACK_PACKAGE_INSTALL_DIRECTORY "Synergy+")
SET(CPACK_PACKAGE_EXECUTABLES launcher;Synergy+) SET(CPACK_PACKAGE_EXECUTABLES qsynergy;Synergy+)
ENDIF(WIN32) ENDIF(WIN32)
# For source package, leave out temp and Mercurial stuff. # For source package, leave out temp and Mercurial stuff.

View File

@ -1,12 +1,15 @@
SET(root_lib ${root_dir}/lib) SET(root_lib ${root_dir}/lib)
SET(src_lib_arch SET(src_lib_arch
${root_lib}/arch/CArchAppUtil.cpp
${root_lib}/arch/CArch.cpp ${root_lib}/arch/CArch.cpp
${root_lib}/arch/CArchDaemonNone.cpp ${root_lib}/arch/CArchDaemonNone.cpp
${root_lib}/arch/XArch.cpp ${root_lib}/arch/XArch.cpp
${root_lib}/arch/CArchConsoleStd.cpp
) )
SET(src_lib_arch_unix SET(src_lib_arch_unix
${root_lib}/arch/CArchAppUtilUnix.cpp
${root_lib}/arch/CArchConsoleUnix.cpp ${root_lib}/arch/CArchConsoleUnix.cpp
${root_lib}/arch/CArchDaemonUnix.cpp ${root_lib}/arch/CArchDaemonUnix.cpp
${root_lib}/arch/CArchFileUnix.cpp ${root_lib}/arch/CArchFileUnix.cpp
@ -22,6 +25,7 @@ SET(src_lib_arch_unix
) )
SET(src_lib_arch_windows SET(src_lib_arch_windows
${root_lib}/arch/CArchAppUtilWindows.cpp
${root_lib}/arch/CArchConsoleWindows.cpp ${root_lib}/arch/CArchConsoleWindows.cpp
${root_lib}/arch/CArchDaemonWindows.cpp ${root_lib}/arch/CArchDaemonWindows.cpp
${root_lib}/arch/CArchFileWindows.cpp ${root_lib}/arch/CArchFileWindows.cpp
@ -38,6 +42,8 @@ SET(src_lib_arch_windows
) )
SET(inc_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/CArchConsoleWindows.h
${root_lib}/arch/CArchDaemonWindows.h ${root_lib}/arch/CArchDaemonWindows.h
${root_lib}/arch/CArchFileWindows.h ${root_lib}/arch/CArchFileWindows.h
@ -50,6 +56,8 @@ SET(inc_lib_arch_windows
${root_lib}/arch/CArchSystemWindows.h ${root_lib}/arch/CArchSystemWindows.h
${root_lib}/arch/CArchTaskBarWindows.h ${root_lib}/arch/CArchTaskBarWindows.h
${root_lib}/arch/CArchTimeWindows.h ${root_lib}/arch/CArchTimeWindows.h
${root_lib}/arch/CArchConsoleStd.h
${root_lib}/arch/IArchAppUtil.h
${root_lib}/arch/XArchWindows.h ${root_lib}/arch/XArchWindows.h
) )
@ -264,6 +272,11 @@ SET(inc_lib_server
) )
SET(src_lib_synergy 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/CClipboard.cpp
${root_lib}/synergy/CKeyMap.cpp ${root_lib}/synergy/CKeyMap.cpp
${root_lib}/synergy/CKeyState.cpp ${root_lib}/synergy/CKeyState.cpp
@ -282,6 +295,11 @@ SET(src_lib_synergy
) )
SET(inc_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/CClipboard.h
${root_lib}/synergy/CKeyMap.h ${root_lib}/synergy/CKeyMap.h
${root_lib}/synergy/CKeyState.h ${root_lib}/synergy/CKeyState.h
@ -324,9 +342,17 @@ IF(UNIX)
LIST(APPEND src_lib ${src_lib_arch_unix}) LIST(APPEND src_lib ${src_lib_arch_unix})
IF(APPLE) 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) 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(APPLE)
ENDIF(UNIX) ENDIF(UNIX)
@ -345,6 +371,8 @@ IF(WIN32)
${src_lib_arch_windows} ${src_lib_arch_windows}
${inc_lib_platform_mswindows} ${inc_lib_platform_mswindows}
${src_lib_platform_mswindows} ${src_lib_platform_mswindows}
${inc_lib_synergy_mswindows}
${src_lib_synergy_mswindows}
) )
ENDIF(WIN32) ENDIF(WIN32)

View File

@ -1,7 +1,6 @@
SET(root_cmd_synergyc ${root_dir}/cmd/synergyc) SET(root_cmd_synergyc ${root_dir}/cmd/synergyc)
SET(src_cmd_synergyc_common SET(src_cmd_synergyc_common
${root_cmd_synergyc}/CClientTaskBarReceiver.cpp
${root_cmd_synergyc}/synergyc.cpp ${root_cmd_synergyc}/synergyc.cpp
) )
@ -66,5 +65,5 @@ SET(inc_dirs_cmd_synergyc
) )
INCLUDE_DIRECTORIES(${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}) TARGET_LINK_LIBRARIES(synergyc synergy ${libs})

View File

@ -1,7 +1,6 @@
SET(root_cmd_synergys ${root_dir}/cmd/synergys) SET(root_cmd_synergys ${root_dir}/cmd/synergys)
SET(src_cmd_synergys_common SET(src_cmd_synergys_common
${root_cmd_synergys}/CServerTaskBarReceiver.cpp
${root_cmd_synergys}/synergys.cpp ${root_cmd_synergys}/synergys.cpp
) )
@ -14,7 +13,6 @@ SET(src_cmd_synergys_mswindows
) )
SET(inc_cmd_synergys_mswindows SET(inc_cmd_synergys_mswindows
${root_cmd_synergys}/CServerTaskBarReceiver.h
${root_cmd_synergys}/CXWindowsServerTaskBarReceiver.h ${root_cmd_synergys}/CXWindowsServerTaskBarReceiver.h
${root_cmd_synergys}/resource.h ${root_cmd_synergys}/resource.h
${root_cmd_synergys}/COSXServerTaskBarReceiver.h ${root_cmd_synergys}/COSXServerTaskBarReceiver.h
@ -69,5 +67,5 @@ SET(inc_dirs_cmd_synergys
) )
INCLUDE_DIRECTORIES(${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}) TARGET_LINK_LIBRARIES(synergys synergy ${libs})

View File

@ -21,8 +21,8 @@
#include "LaunchUtil.h" #include "LaunchUtil.h"
#include "resource.h" #include "resource.h"
static const char* CLIENT_DAEMON_NAME = "Synergy Client"; static const char* CLIENT_DAEMON_NAME = "Synergy+ Client";
static const char* SERVER_DAEMON_NAME = "Synergy Server"; static const char* SERVER_DAEMON_NAME = "Synergy+ Server";
static const char* CLIENT_DAEMON_INFO = "Uses a shared mouse and keyboard."; 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."; static const char* SERVER_DAEMON_INFO = "Shares this system's mouse and keyboard with others.";

View File

@ -675,7 +675,9 @@ mainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
int WINAPI int WINAPI
WinMain(HINSTANCE instance, HINSTANCE, LPSTR cmdLine, int nCmdShow) WinMain(HINSTANCE instance, HINSTANCE, LPSTR cmdLine, int nCmdShow)
{ {
CArch arch(instance); CArchMiscWindows::setInstanceWin32(instance);
CArch arch;
CLOG; CLOG;
CArgs args; CArgs args;

View File

@ -19,7 +19,9 @@
#include "BasicTypes.h" #include "BasicTypes.h"
#include "CArch.h" #include "CArch.h"
#include "CArchTaskBarWindows.h" #include "CArchTaskBarWindows.h"
#include "CArchMiscWindows.h"
#include "resource.h" #include "resource.h"
#include "CMSWindowsScreen.h"
// //
// CMSWindowsClientTaskBarReceiver // CMSWindowsClientTaskBarReceiver
@ -343,3 +345,20 @@ CMSWindowsClientTaskBarReceiver::staticDlgProc(HWND hwnd,
return (msg == WM_INITDIALOG) ? TRUE : FALSE; 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);
}

View File

@ -54,3 +54,10 @@ COSXClientTaskBarReceiver::getIcon() const
{ {
return NULL; return NULL;
} }
IArchTaskBarReceiver*
createTaskBarReceiver(const CBufferedLogOutputter* logBuffer)
{
return new COSXClientTaskBarReceiver(logBuffer);
}

View File

@ -54,3 +54,9 @@ CXWindowsClientTaskBarReceiver::getIcon() const
{ {
return NULL; return NULL;
} }
IArchTaskBarReceiver*
createTaskBarReceiver(const CBufferedLogOutputter* logBuffer)
{
return new CXWindowsClientTaskBarReceiver(logBuffer);
}

View File

@ -12,943 +12,21 @@
* GNU General Public License for more details. * GNU General Public License for more details.
*/ */
#include "CClient.h" #include "CClientApp.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>
#define DAEMON_RUNNING(running_)
#if WINAPI_MSWINDOWS #if WINAPI_MSWINDOWS
#include "CArchMiscWindows.h"
#include "CMSWindowsScreen.h"
#include "CMSWindowsUtil.h"
#include "CMSWindowsClientTaskBarReceiver.h" #include "CMSWindowsClientTaskBarReceiver.h"
#include "resource.h"
#undef DAEMON_RUNNING
#define DAEMON_RUNNING(running_) CArchMiscWindows::daemonRunning(running_)
#elif WINAPI_XWINDOWS #elif WINAPI_XWINDOWS
#include "CXWindowsScreen.h"
#include "CXWindowsClientTaskBarReceiver.h" #include "CXWindowsClientTaskBarReceiver.h"
#elif WINAPI_CARBON #elif WINAPI_CARBON
#include "COSXScreen.h"
#include "COSXClientTaskBarReceiver.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 #else
CSystemLogger sysLogger(DAEMON_NAME, true); #error Platform not supported.
#endif #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 int
main(int argc, char** argv) main(int argc, char** argv)
{ {
CArgs args; CClientApp app;
try { return app.run(argc, argv, createTaskBarReceiver);
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;
}
}
#else
#error no main() for platform
#endif

View File

@ -21,10 +21,8 @@
#include "CArch.h" #include "CArch.h"
#include "CArchTaskBarWindows.h" #include "CArchTaskBarWindows.h"
#include "resource.h" #include "resource.h"
#include "CArchMiscWindows.h"
extern CEvent::Type getReloadConfigEvent(); #include "CMSWindowsScreen.h"
extern CEvent::Type getForceReconnectEvent();
extern CEvent::Type getResetServerEvent();
// //
// CMSWindowsServerTaskBarReceiver // CMSWindowsServerTaskBarReceiver
@ -378,3 +376,20 @@ CMSWindowsServerTaskBarReceiver::staticDlgProc(HWND hwnd,
return (msg == WM_INITDIALOG) ? TRUE : FALSE; 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);
}

View File

@ -54,3 +54,9 @@ COSXServerTaskBarReceiver::getIcon() const
{ {
return NULL; return NULL;
} }
IArchTaskBarReceiver*
createTaskBarReceiver(const CBufferedLogOutputter* logBuffer)
{
return new COSXServerTaskBarReceiver(logBuffer);
}

View File

@ -54,3 +54,9 @@ CXWindowsServerTaskBarReceiver::getIcon() const
{ {
return NULL; return NULL;
} }
IArchTaskBarReceiver*
createTaskBarReceiver(const CBufferedLogOutputter* logBuffer)
{
return new CXWindowsServerTaskBarReceiver(logBuffer);
}

File diff suppressed because it is too large Load Diff

View File

@ -83,3 +83,5 @@ release {
RCC_DIR = tmp/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

View File

@ -1 +1 @@
IDI_ICON1 ICON DISCARDABLE "res\win\QSynergy.ico" IDI_ICON1 ICON DISCARDABLE "res\\win\\QSynergy.ico"

View File

@ -6,7 +6,7 @@
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
const char AppConfig::m_SynergysName[] = "synergys.exe"; const char AppConfig::m_SynergysName[] = "synergys.exe";
const char AppConfig::m_SynergycName[] = "synergyc.exe"; const char AppConfig::m_SynergycName[] = "synergyc.exe";
const char AppConfig::m_SynergyProgramDir[] = "c:/program files/synergy/"; const char AppConfig::m_SynergyProgramDir[] = "./";
#else #else
const char AppConfig::m_SynergysName[] = "synergys"; const char AppConfig::m_SynergysName[] = "synergys";
const char AppConfig::m_SynergycName[] = "synergyc"; const char AppConfig::m_SynergycName[] = "synergyc";

View File

@ -224,7 +224,7 @@ void MainWindow::startSynergy()
setSynergyProcess(new QProcess(this)); setSynergyProcess(new QProcess(this));
if ((synergyType() == synergyClient && !clientArgs(args, app)) if ((synergyType() == synergyClient && !clientArgs(args, app))
|| synergyType() == synergyServer && !serverArgs(args, app)) || (synergyType() == synergyServer && !serverArgs(args, app)))
{ {
stopSynergy(); stopSynergy();
return; return;

View File

@ -6,16 +6,16 @@
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
QCoreApplication::setOrganizationName("Fidra"); QCoreApplication::setOrganizationName("The Synergy+ Project");
QCoreApplication::setOrganizationDomain("www.fidra.de"); QCoreApplication::setOrganizationDomain("http://code.google.com/p/synergy-plus/");
QCoreApplication::setApplicationName("QSynergy"); QCoreApplication::setApplicationName("Synergy+");
QSynergyApplication app(argc, argv); QSynergyApplication app(argc, argv);
#if !defined(Q_OS_MAC) #if !defined(Q_OS_MAC)
if (!QSystemTrayIcon::isSystemTrayAvailable()) 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; return -1;
} }

View File

@ -14,6 +14,7 @@
#include "common.h" #include "common.h"
#include "CArch.h" #include "CArch.h"
#include "CLog.h"
#undef ARCH_CONSOLE #undef ARCH_CONSOLE
#undef ARCH_DAEMON #undef ARCH_DAEMON
@ -41,6 +42,7 @@
# include "CArchSystemWindows.h" # include "CArchSystemWindows.h"
# include "CArchTaskBarWindows.h" # include "CArchTaskBarWindows.h"
# include "CArchTimeWindows.h" # include "CArchTimeWindows.h"
# include "CArchAppUtilWindows.h"
#elif SYSAPI_UNIX #elif SYSAPI_UNIX
# include "CArchConsoleUnix.h" # include "CArchConsoleUnix.h"
# include "CArchDaemonUnix.h" # include "CArchDaemonUnix.h"
@ -55,6 +57,7 @@
# include "CArchSystemUnix.h" # include "CArchSystemUnix.h"
# include "CArchTaskBarXWindows.h" # include "CArchTaskBarXWindows.h"
# include "CArchTimeUnix.h" # include "CArchTimeUnix.h"
# include "CArchAppUtilUnix.h"
#endif #endif
#if !defined(ARCH_CONSOLE) #if !defined(ARCH_CONSOLE)
@ -101,13 +104,17 @@
# error unsupported platform for time # error unsupported platform for time
#endif #endif
#if !defined(ARCH_APPUTIL)
# error unsupported platform for app util
#endif
// //
// CArch // CArch
// //
CArch* CArch::s_instance = NULL; CArch* CArch::s_instance = NULL;
CArch::CArch(ARCH_ARGS* args) CArch::CArch()
{ {
// only once instance of CArch // only once instance of CArch
assert(s_instance == NULL); assert(s_instance == NULL);
@ -122,9 +129,10 @@ CArch::CArch(ARCH_ARGS* args)
m_sleep = new ARCH_SLEEP; m_sleep = new ARCH_SLEEP;
m_string = new ARCH_STRING; m_string = new ARCH_STRING;
m_time = new ARCH_TIME; m_time = new ARCH_TIME;
m_console = new ARCH_CONSOLE(args); m_console = new ARCH_CONSOLE;
m_daemon = new ARCH_DAEMON; m_daemon = new ARCH_DAEMON;
m_taskbar = new ARCH_TASKBAR(args); m_taskbar = new ARCH_TASKBAR;
m_appUtil = new ARCH_APPUTIL;
#if SYSAPI_WIN32 #if SYSAPI_WIN32
CArchMiscWindows::init(); CArchMiscWindows::init();
@ -145,6 +153,7 @@ CArch::~CArch()
delete m_file; delete m_file;
delete m_system; delete m_system;
delete m_mt; delete m_mt;
delete m_appUtil;
// no instance // no instance
s_instance = NULL; s_instance = NULL;
@ -182,12 +191,6 @@ CArch::writeConsole(const char* str)
m_console->writeConsole(str); m_console->writeConsole(str);
} }
const char*
CArch::getNewlineForConsole()
{
return m_console->getNewlineForConsole();
}
void void
CArch::installDaemon(const char* name, CArch::installDaemon(const char* name,
const char* description, const char* description,
@ -643,3 +646,27 @@ CArch::time()
{ {
return m_time->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);
}

View File

@ -26,6 +26,7 @@
#include "IArchSystem.h" #include "IArchSystem.h"
#include "IArchTaskBar.h" #include "IArchTaskBar.h"
#include "IArchTime.h" #include "IArchTime.h"
#include "IArchAppUtil.h"
/*! /*!
\def ARCH \def ARCH
@ -33,9 +34,7 @@ This macro evaluates to the singleton CArch object.
*/ */
#define ARCH (CArch::getInstance()) #define ARCH (CArch::getInstance())
#define ARCH_ARGS void //! Delegating implementation of architecture dependent interfaces
//! Delegating mplementation of architecture dependent interfaces
/*! /*!
This class is a centralized interface to all architecture dependent This class is a centralized interface to all architecture dependent
interface implementations (except miscellaneous functions). It interface implementations (except miscellaneous functions). It
@ -55,9 +54,10 @@ class CArch : public IArchConsole,
public IArchString, public IArchString,
public IArchSystem, public IArchSystem,
public IArchTaskBar, public IArchTaskBar,
public IArchTime { public IArchTime,
public IArchAppUtil {
public: public:
CArch(ARCH_ARGS* args = NULL); CArch();
~CArch(); ~CArch();
// //
@ -76,7 +76,6 @@ public:
virtual void closeConsole(); virtual void closeConsole();
virtual void showConsole(bool showIfEmpty); virtual void showConsole(bool showIfEmpty);
virtual void writeConsole(const char*); virtual void writeConsole(const char*);
virtual const char* getNewlineForConsole();
// IArchDaemon overrides // IArchDaemon overrides
virtual void installDaemon(const char* name, virtual void installDaemon(const char* name,
@ -184,6 +183,12 @@ public:
// IArchTime overrides // IArchTime overrides
virtual double time(); 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: private:
static CArch* s_instance; static CArch* s_instance;
@ -198,6 +203,7 @@ private:
IArchSystem* m_system; IArchSystem* m_system;
IArchTaskBar* m_taskbar; IArchTaskBar* m_taskbar;
IArchTime* m_time; IArchTime* m_time;
IArchAppUtil* m_appUtil;
}; };
//! Convenience object to lock/unlock an arch mutex //! Convenience object to lock/unlock an arch mutex

55
lib/arch/CArchAppUtil.cpp Normal file
View File

@ -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;
}

33
lib/arch/CArchAppUtil.h Normal file
View File

@ -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;
};

View File

@ -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);
}

View File

@ -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);
};

View File

@ -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();
}

View File

@ -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);

View File

@ -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();
}

View File

@ -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*);
};

View File

@ -13,49 +13,7 @@
*/ */
#include "CArchConsoleUnix.h" #include "CArchConsoleUnix.h"
#include <cstdio>
#include <stdio.h>
// CArchConsoleUnix::CArchConsoleUnix() { }
// CArchConsoleUnix
//
CArchConsoleUnix::CArchConsoleUnix(void*) CArchConsoleUnix::~CArchConsoleUnix() { }
{
// 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";
}

View File

@ -12,25 +12,14 @@
* GNU General Public License for more details. * GNU General Public License for more details.
*/ */
#ifndef CARCHCONSOLEUNIX_H #pragma once
#define CARCHCONSOLEUNIX_H
#include "IArchConsole.h" #include "CArchConsoleStd.h"
#define ARCH_CONSOLE CArchConsoleUnix #define ARCH_CONSOLE CArchConsoleUnix
//! Unix implementation of IArchConsole class CArchConsoleUnix : public CArchConsoleStd {
class CArchConsoleUnix : public IArchConsole {
public: public:
CArchConsoleUnix(void*); CArchConsoleUnix();
virtual ~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

View File

@ -13,427 +13,7 @@
*/ */
#include "CArchConsoleWindows.h" #include "CArchConsoleWindows.h"
#include "IArchMultithread.h"
#include "CArch.h"
#include "CArchMiscWindows.h"
#include <richedit.h>
#define SYNERGY_MSG_CONSOLE_OPEN WM_APP + 0x0021 CArchConsoleWindows::CArchConsoleWindows() { }
#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
//
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;
}

View File

@ -12,66 +12,14 @@
* GNU General Public License for more details. * GNU General Public License for more details.
*/ */
#ifndef CARCHCONSOLEWINDOWS_H #pragma once
#define CARCHCONSOLEWINDOWS_H
#define WIN32_LEAN_AND_MEAN #include "CArchConsoleStd.h"
#include "IArchConsole.h"
#include "IArchMultithread.h"
#include "stddeque.h"
#include <windows.h>
#define ARCH_CONSOLE CArchConsoleWindows #define ARCH_CONSOLE CArchConsoleWindows
//! Win32 implementation of IArchConsole class CArchConsoleWindows : public CArchConsoleStd {
class CArchConsoleWindows : public IArchConsole {
public: public:
CArchConsoleWindows(void*); CArchConsoleWindows();
virtual ~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

View File

@ -142,9 +142,12 @@ CArchDaemonWindows::installDaemon(const char* name,
throw XArchDaemonInstallFailed(new XArchEvalWindows(err)); throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
} }
} }
else {
// done with service and manager // done with service (but only try to close if not null)
CloseServiceHandle(service); CloseServiceHandle(service);
}
// done with manager
CloseServiceHandle(mgr); CloseServiceHandle(mgr);
// open the registry key for this service // open the registry key for this service

View File

@ -14,6 +14,16 @@
#include "CArchMiscWindows.h" #include "CArchMiscWindows.h"
#include "CArchDaemonWindows.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 #ifndef ES_SYSTEM_REQUIRED
#define ES_SYSTEM_REQUIRED ((DWORD)0x00000001) #define ES_SYSTEM_REQUIRED ((DWORD)0x00000001)
@ -35,6 +45,7 @@ DWORD CArchMiscWindows::s_busyState = 0;
CArchMiscWindows::STES_t CArchMiscWindows::s_stes = NULL; CArchMiscWindows::STES_t CArchMiscWindows::s_stes = NULL;
HICON CArchMiscWindows::s_largeIcon = NULL; HICON CArchMiscWindows::s_largeIcon = NULL;
HICON CArchMiscWindows::s_smallIcon = NULL; HICON CArchMiscWindows::s_smallIcon = NULL;
HINSTANCE CArchMiscWindows::s_instanceWin32 = NULL;
void void
CArchMiscWindows::init() CArchMiscWindows::init()
@ -193,6 +204,7 @@ void
CArchMiscWindows::closeKey(HKEY key) CArchMiscWindows::closeKey(HKEY key)
{ {
assert(key != NULL); assert(key != NULL);
if (key==NULL) return;
RegCloseKey(key); RegCloseKey(key);
} }
@ -201,6 +213,7 @@ CArchMiscWindows::deleteKey(HKEY key, const TCHAR* name)
{ {
assert(key != NULL); assert(key != NULL);
assert(name != NULL); assert(name != NULL);
if (key==NULL || name==NULL) return;
RegDeleteKey(key, name); RegDeleteKey(key, name);
} }
@ -209,6 +222,7 @@ CArchMiscWindows::deleteValue(HKEY key, const TCHAR* name)
{ {
assert(key != NULL); assert(key != NULL);
assert(name != NULL); assert(name != NULL);
if (key==NULL || name==NULL) return;
RegDeleteValue(key, name); RegDeleteValue(key, name);
} }
@ -250,6 +264,7 @@ CArchMiscWindows::setValue(HKEY key,
{ {
assert(key != NULL); assert(key != NULL);
assert(name != NULL); assert(name != NULL);
if(key ==NULL || name==NULL) return; // TODO: throw exception
RegSetValueEx(key, name, 0, REG_SZ, RegSetValueEx(key, name, 0, REG_SZ,
reinterpret_cast<const BYTE*>(value.c_str()), reinterpret_cast<const BYTE*>(value.c_str()),
(DWORD)value.size() + 1); (DWORD)value.size() + 1);
@ -260,6 +275,7 @@ CArchMiscWindows::setValue(HKEY key, const TCHAR* name, DWORD value)
{ {
assert(key != NULL); assert(key != NULL);
assert(name != NULL); assert(name != NULL);
if(key ==NULL || name==NULL) return; // TODO: throw exception
RegSetValueEx(key, name, 0, REG_DWORD, RegSetValueEx(key, name, 0, REG_DWORD,
reinterpret_cast<CONST BYTE*>(&value), reinterpret_cast<CONST BYTE*>(&value),
sizeof(DWORD)); sizeof(DWORD));
@ -271,6 +287,7 @@ CArchMiscWindows::setValueBinary(HKEY key,
{ {
assert(key != NULL); assert(key != NULL);
assert(name != NULL); assert(name != NULL);
if(key ==NULL || name==NULL) return; // TODO: throw exception
RegSetValueEx(key, name, 0, REG_BINARY, RegSetValueEx(key, name, 0, REG_BINARY,
reinterpret_cast<const BYTE*>(value.data()), reinterpret_cast<const BYTE*>(value.data()),
(DWORD)value.size()); (DWORD)value.size());
@ -437,3 +454,98 @@ CArchMiscWindows::wakeupDisplay()
// restore the original execution states // restore the original execution states
setThreadExecutionState(s_busyState); 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;
}

View File

@ -21,6 +21,8 @@
#include "stdstring.h" #include "stdstring.h"
#include "stdset.h" #include "stdset.h"
#include <windows.h> #include <windows.h>
#include <Tlhelp32.h>
#include "CString.h"
//! Miscellaneous win32 functions. //! Miscellaneous win32 functions.
class CArchMiscWindows { class CArchMiscWindows {
@ -164,6 +166,16 @@ public:
//! Briefly interrupt power saving //! Briefly interrupt power saving
static void wakeupDisplay(); 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: private:
//! Open and return a registry key, closing the parent key //! Open and return a registry key, closing the parent key
static HKEY openKey(HKEY parent, const TCHAR* child, bool create); static HKEY openKey(HKEY parent, const TCHAR* child, bool create);
@ -180,6 +192,10 @@ private:
static DWORD WINAPI dummySetThreadExecutionState(DWORD); 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: private:
typedef std::set<HWND> CDialogs; typedef std::set<HWND> CDialogs;
typedef DWORD (WINAPI *STES_t)(DWORD); typedef DWORD (WINAPI *STES_t)(DWORD);
@ -189,6 +205,7 @@ private:
static STES_t s_stes; static STES_t s_stes;
static HICON s_largeIcon; static HICON s_largeIcon;
static HICON s_smallIcon; static HICON s_smallIcon;
static HINSTANCE s_instanceWin32;
}; };
#endif #endif

View File

@ -19,6 +19,7 @@
#include "XArch.h" #include "XArch.h"
#include <string.h> #include <string.h>
#include <shellapi.h> #include <shellapi.h>
#include "CArchAppUtilWindows.h"
static const UINT kAddReceiver = WM_USER + 10; static const UINT kAddReceiver = WM_USER + 10;
static const UINT kRemoveReceiver = WM_USER + 11; static const UINT kRemoveReceiver = WM_USER + 11;
@ -31,17 +32,13 @@ static const UINT kFirstReceiverID = WM_USER + 14;
// //
CArchTaskBarWindows* CArchTaskBarWindows::s_instance = NULL; CArchTaskBarWindows* CArchTaskBarWindows::s_instance = NULL;
HINSTANCE CArchTaskBarWindows::s_appInstance = NULL;
CArchTaskBarWindows::CArchTaskBarWindows(void* appInstance) : CArchTaskBarWindows::CArchTaskBarWindows() :
m_nextID(kFirstReceiverID) m_nextID(kFirstReceiverID)
{ {
// save the singleton instance // save the singleton instance
s_instance = this; s_instance = this;
// save app instance
s_appInstance = reinterpret_cast<HINSTANCE>(appInstance);
// we need a mutex // we need a mutex
m_mutex = ARCH->newMutex(); m_mutex = ARCH->newMutex();
@ -437,7 +434,7 @@ CArchTaskBarWindows::threadMainLoop()
classInfo.lpfnWndProc = &CArchTaskBarWindows::staticWndProc; classInfo.lpfnWndProc = &CArchTaskBarWindows::staticWndProc;
classInfo.cbClsExtra = 0; classInfo.cbClsExtra = 0;
classInfo.cbWndExtra = sizeof(CArchTaskBarWindows*); classInfo.cbWndExtra = sizeof(CArchTaskBarWindows*);
classInfo.hInstance = s_appInstance; classInfo.hInstance = instanceWin32();
classInfo.hIcon = NULL; classInfo.hIcon = NULL;
classInfo.hCursor = NULL; classInfo.hCursor = NULL;
classInfo.hbrBackground = NULL; classInfo.hbrBackground = NULL;
@ -454,7 +451,7 @@ CArchTaskBarWindows::threadMainLoop()
0, 0, 1, 1, 0, 0, 1, 1,
NULL, NULL,
NULL, NULL,
s_appInstance, instanceWin32(),
reinterpret_cast<void*>(this)); reinterpret_cast<void*>(this));
// signal ready // signal ready
@ -465,7 +462,7 @@ CArchTaskBarWindows::threadMainLoop()
// handle failure // handle failure
if (m_hwnd == NULL) { if (m_hwnd == NULL) {
UnregisterClass(reinterpret_cast<LPCTSTR>(windowClass), s_appInstance); UnregisterClass(reinterpret_cast<LPCTSTR>(windowClass), instanceWin32());
return; return;
} }
@ -481,7 +478,7 @@ CArchTaskBarWindows::threadMainLoop()
// clean up // clean up
removeAllIcons(); removeAllIcons();
DestroyWindow(m_hwnd); DestroyWindow(m_hwnd);
UnregisterClass(reinterpret_cast<LPCTSTR>(windowClass), s_appInstance); UnregisterClass(reinterpret_cast<LPCTSTR>(windowClass), instanceWin32());
} }
void* void*
@ -490,3 +487,8 @@ CArchTaskBarWindows::threadEntry(void* self)
reinterpret_cast<CArchTaskBarWindows*>(self)->threadMainLoop(); reinterpret_cast<CArchTaskBarWindows*>(self)->threadMainLoop();
return NULL; return NULL;
} }
HINSTANCE CArchTaskBarWindows::instanceWin32()
{
return CArchMiscWindows::instanceWin32();
}

View File

@ -28,7 +28,7 @@
//! Win32 implementation of IArchTaskBar //! Win32 implementation of IArchTaskBar
class CArchTaskBarWindows : public IArchTaskBar { class CArchTaskBarWindows : public IArchTaskBar {
public: public:
CArchTaskBarWindows(void*); CArchTaskBarWindows();
virtual ~CArchTaskBarWindows(); virtual ~CArchTaskBarWindows();
//! Add a dialog window //! Add a dialog window
@ -81,9 +81,10 @@ private:
void threadMainLoop(); void threadMainLoop();
static void* threadEntry(void*); static void* threadEntry(void*);
HINSTANCE instanceWin32();
private: private:
static CArchTaskBarWindows* s_instance; static CArchTaskBarWindows* s_instance;
static HINSTANCE s_appInstance;
// multithread data // multithread data
CArchMutex m_mutex; CArchMutex m_mutex;

View File

@ -18,7 +18,7 @@
// CArchTaskBarXWindows // CArchTaskBarXWindows
// //
CArchTaskBarXWindows::CArchTaskBarXWindows(void*) CArchTaskBarXWindows::CArchTaskBarXWindows()
{ {
// do nothing // do nothing
} }

View File

@ -22,7 +22,7 @@
//! X11 implementation of IArchTaskBar //! X11 implementation of IArchTaskBar
class CArchTaskBarXWindows : public IArchTaskBar { class CArchTaskBarXWindows : public IArchTaskBar {
public: public:
CArchTaskBarXWindows(void*); CArchTaskBarXWindows();
virtual ~CArchTaskBarXWindows(); virtual ~CArchTaskBarXWindows();
// IArchTaskBar overrides // IArchTaskBar overrides

29
lib/arch/IArchAppUtil.h Normal file
View File

@ -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;
};

View File

@ -58,13 +58,6 @@ public:
*/ */
virtual void writeConsole(const char*) = 0; 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;
//@} //@}
}; };

View File

@ -18,6 +18,9 @@
#include "IInterface.h" #include "IInterface.h"
#include "stdstring.h" #include "stdstring.h"
class IScreen;
class INode;
//! Interface for architecture dependent task bar event handling //! Interface for architecture dependent task bar event handling
/*! /*!
This interface defines the task bar icon event handlers required This interface defines the task bar icon event handlers required
@ -84,6 +87,8 @@ public:
*/ */
virtual std::string getToolTip() const = 0; virtual std::string getToolTip() const = 0;
virtual void updateStatus(INode*, const CString& errorMsg) = 0;
//@} //@}
}; };

View File

@ -32,12 +32,14 @@ static const char* g_priority[] = {
"INFO", "INFO",
"DEBUG", "DEBUG",
"DEBUG1", "DEBUG1",
"DEBUG2" "DEBUG2",
"DEBUG3",
"DEBUG4",
"DEBUG5"
}; };
// number of priorities // number of priorities
static const int g_numPriority = (int)(sizeof(g_priority) / static const int g_numPriority = (int)(sizeof(g_priority) / sizeof(g_priority[0]));
sizeof(g_priority[0]));
// the default priority // the default priority
#if defined(NDEBUG) #if defined(NDEBUG)
@ -101,13 +103,31 @@ CLog::getInstance()
return s_log; return s_log;
} }
const char*
CLog::getFilterName() const
{
return getFilterName(getFilter());
}
const char*
CLog::getFilterName(int level) const
{
return g_priority[level];
}
void void
CLog::print(const char* file, int line, const char* fmt, ...) const CLog::print(const char* file, int line, const char* fmt, ...)
{ {
// check if fmt begins with a priority argument // check if fmt begins with a priority argument
int priority = 4; ELevel priority = kINFO;
if (fmt[0] == '%' && fmt[1] == 'z') { if (fmt[0] == '%' && fmt[1] == 'z') {
priority = fmt[2] - '\060';
// 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; fmt += 3;
} }
@ -184,7 +204,6 @@ void
CLog::insert(ILogOutputter* outputter, bool alwaysAtHead) CLog::insert(ILogOutputter* outputter, bool alwaysAtHead)
{ {
assert(outputter != NULL); assert(outputter != NULL);
assert(outputter->getNewline() != NULL);
CArchMutexLock lock(m_mutex); CArchMutexLock lock(m_mutex);
if (alwaysAtHead) { if (alwaysAtHead) {
@ -193,10 +212,7 @@ CLog::insert(ILogOutputter* outputter, bool alwaysAtHead)
else { else {
m_outputters.push_front(outputter); m_outputters.push_front(outputter);
} }
int newlineLength = (int)strlen(outputter->getNewline());
if (newlineLength > m_maxNewlineLength) {
m_maxNewlineLength = newlineLength;
}
outputter->open(kAppVersion); outputter->open(kAppVersion);
// Issue 41 // Issue 41
@ -258,62 +274,27 @@ CLog::getFilter() const
} }
void void
CLog::output(int priority, char* msg) const CLog::output(ELevel priority, char* msg)
{ {
assert(priority >= -1 && priority < g_numPriority); assert(priority >= -1 && priority < g_numPriority);
assert(msg != NULL); assert(msg != NULL);
if (!msg) return;
// 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); 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 COutputterList::const_iterator i;
strcpy(end, outputter->getNewline());
// write message for (i = m_alwaysOutputters.begin(); i != m_alwaysOutputters.end(); ++i) {
outputter->write(static_cast<ILogOutputter::ELevel>(priority),
tmp /*+ g_maxPriorityLength - n*/); // write to outputter
(*i)->write(priority, msg);
} }
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 for (i = m_outputters.begin(); i != m_outputters.end(); ++i) {
strcpy(end, outputter->getNewline());
// write message and break out of loop if it returns false // write to outputter and break out of loop if it returns false
if (!outputter->write(static_cast<ILogOutputter::ELevel>(priority), if (!(*i)->write(priority, msg)) {
tmp /*+ g_maxPriorityLength - n*/)) {
break; break;
} }
} }
delete[] tmp;
} }

View File

@ -19,10 +19,12 @@
#include "IArchMultithread.h" #include "IArchMultithread.h"
#include "stdlist.h" #include "stdlist.h"
#include <stdarg.h> #include <stdarg.h>
#include "CArch.h"
#define CLOG (CLog::getInstance()) #define CLOG (CLog::getInstance())
class ILogOutputter; class ILogOutputter;
class CThread;
//! Logging facility //! Logging facility
/*! /*!
@ -44,8 +46,11 @@ public:
kNOTE, //!< For messages about notable events kNOTE, //!< For messages about notable events
kINFO, //!< For informational messages kINFO, //!< For informational messages
kDEBUG, //!< For important debugging messages kDEBUG, //!< For important debugging messages
kDEBUG1, //!< For more detailed debugging messages kDEBUG1, //!< For verbosity +1 debugging messages
kDEBUG2 //!< For even more detailed 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(); ~CLog();
@ -109,20 +114,28 @@ public:
neither the file nor the line are printed. neither the file nor the line are printed.
*/ */
void print(const char* file, int line, void print(const char* file, int line,
const char* format, ...) const; const char* format, ...);
//! Get the minimum priority level. //! Get the minimum priority level.
int getFilter() const; 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 //! Get the singleton instance of the log
static CLog* getInstance(); static CLog* getInstance();
//! Get the console filter level (messages above this are not sent to console).
int getConsoleMaxLevel() const { return kDEBUG1; }
//@} //@}
private: private:
CLog(); CLog();
void output(int priority, char* msg) const; void output(ELevel priority, char* msg);
private: private:
typedef std::list<ILogOutputter*> COutputterList; typedef std::list<ILogOutputter*> COutputterList;
@ -189,8 +202,13 @@ otherwise it expands to a call that doesn't.
#define CLOG_TRACE __FILE__, __LINE__, #define CLOG_TRACE __FILE__, __LINE__,
#endif #endif
#define CLOG_PRINT CLOG_TRACE "%z\057" // the CLOG_* defines are line and file plus %z and an octal number (060=0,
#define CLOG_CRIT CLOG_TRACE "%z\060" // 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_ERR CLOG_TRACE "%z\061"
#define CLOG_WARN CLOG_TRACE "%z\062" #define CLOG_WARN CLOG_TRACE "%z\062"
#define CLOG_NOTE CLOG_TRACE "%z\063" #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_DEBUG CLOG_TRACE "%z\065"
#define CLOG_DEBUG1 CLOG_TRACE "%z\066" #define CLOG_DEBUG1 CLOG_TRACE "%z\066"
#define CLOG_DEBUG2 CLOG_TRACE "%z\067" #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 #endif

View File

@ -63,18 +63,6 @@ public:
*/ */
virtual bool write(ELevel level, const char* message) = 0; 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;
//@} //@}
}; };

View File

@ -14,6 +14,7 @@
#include "LogOutputters.h" #include "LogOutputters.h"
#include "CArch.h" #include "CArch.h"
#include "TMethodJob.h"
#include <fstream> #include <fstream>
// //
@ -54,12 +55,6 @@ CStopLogOutputter::write(ELevel, const char*)
return false; return false;
} }
const char*
CStopLogOutputter::getNewline() const
{
return "";
}
// //
// CConsoleLogOutputter // CConsoleLogOutputter
@ -67,12 +62,10 @@ CStopLogOutputter::getNewline() const
CConsoleLogOutputter::CConsoleLogOutputter() CConsoleLogOutputter::CConsoleLogOutputter()
{ {
// do nothing
} }
CConsoleLogOutputter::~CConsoleLogOutputter() CConsoleLogOutputter::~CConsoleLogOutputter()
{ {
// do nothing
} }
void void
@ -94,16 +87,16 @@ CConsoleLogOutputter::show(bool showIfEmpty)
} }
bool bool
CConsoleLogOutputter::write(ELevel, const char* msg) CConsoleLogOutputter::write(ELevel level, const char* msg)
{ {
ARCH->writeConsole(msg); ARCH->writeConsole(msg);
return true; return true; // wtf?
} }
const char* void
CConsoleLogOutputter::getNewline() const CConsoleLogOutputter::flush()
{ {
return ARCH->getNewlineForConsole();
} }
@ -170,13 +163,6 @@ CSystemLogOutputter::write(ELevel level, const char* msg)
return true; return true;
} }
const char*
CSystemLogOutputter::getNewline() const
{
return "";
}
// //
// CSystemLogger // CSystemLogger
// //
@ -261,12 +247,6 @@ CBufferedLogOutputter::write(ELevel, const char* message)
return true; return true;
} }
const char*
CBufferedLogOutputter::getNewline() const
{
return "";
}
// //
// CFileLogOutputter // CFileLogOutputter
@ -287,17 +267,11 @@ CFileLogOutputter::~CFileLogOutputter()
m_handle.close(); m_handle.close();
} }
const char*
CFileLogOutputter::getNewline() const
{
return "\n";
}
bool bool
CFileLogOutputter::write(ILogOutputter::ELevel level, const char *message) CFileLogOutputter::write(ILogOutputter::ELevel level, const char *message)
{ {
if (m_handle.is_open() && m_handle.fail() != true) { if (m_handle.is_open() && m_handle.fail() != true) {
m_handle << message; m_handle << message << std::endl;
// write buffer to file // write buffer to file
m_handle.flush(); m_handle.flush();

View File

@ -19,8 +19,11 @@
#include "ILogOutputter.h" #include "ILogOutputter.h"
#include "CString.h" #include "CString.h"
#include "stddeque.h" #include "stddeque.h"
#include "CThread.h"
#include <list>
#include <fstream> #include <fstream>
//! Stop traversing log chain outputter //! Stop traversing log chain outputter
/*! /*!
This outputter performs no output and returns false from \c write(), This outputter performs no output and returns false from \c write(),
@ -37,7 +40,6 @@ public:
virtual void close(); virtual void close();
virtual void show(bool showIfEmpty); virtual void show(bool showIfEmpty);
virtual bool write(ELevel level, const char* message); virtual bool write(ELevel level, const char* message);
virtual const char* getNewline() const;
}; };
//! Write log to console //! Write log to console
@ -55,7 +57,7 @@ public:
virtual void close(); virtual void close();
virtual void show(bool showIfEmpty); virtual void show(bool showIfEmpty);
virtual bool write(ELevel level, const char* message); virtual bool write(ELevel level, const char* message);
virtual const char* getNewline() const; virtual void flush();
}; };
//! Write log to file //! Write log to file
@ -74,7 +76,6 @@ public:
virtual void close(); virtual void close();
virtual void show(bool showIfEmpty); virtual void show(bool showIfEmpty);
virtual bool write(ELevel level, const char* message); virtual bool write(ELevel level, const char* message);
virtual const char* getNewline() const;
private: private:
std::ofstream m_handle; std::ofstream m_handle;
}; };
@ -93,7 +94,6 @@ public:
virtual void close(); virtual void close();
virtual void show(bool showIfEmpty); virtual void show(bool showIfEmpty);
virtual bool write(ELevel level, const char* message); virtual bool write(ELevel level, const char* message);
virtual const char* getNewline() const;
}; };
//! Write log to system log only //! Write log to system log only
@ -144,8 +144,6 @@ public:
virtual void close(); virtual void close();
virtual void show(bool showIfEmpty); virtual void show(bool showIfEmpty);
virtual bool write(ELevel level, const char* message); virtual bool write(ELevel level, const char* message);
virtual const char* getNewline() const;
private: private:
UInt32 m_maxBufferSize; UInt32 m_maxBufferSize;
CBuffer m_buffer; CBuffer m_buffer;

View File

@ -390,10 +390,10 @@ CClient::sendEvent(CEvent::Type type, void* data)
void void
CClient::sendConnectionFailedEvent(const char* msg) CClient::sendConnectionFailedEvent(const char* msg)
{ {
CFailInfo* info = (CFailInfo*)malloc(sizeof(CFailInfo) + strlen(msg)); CFailInfo* info = new CFailInfo(msg);
info->m_retry = true; info->m_retry = true;
strcpy(info->m_what, msg); CEvent event(getConnectionFailedEvent(), getEventTarget(), info, CEvent::kDontFreeData);
sendEvent(getConnectionFailedEvent(), info); EVENTQUEUE->addEvent(event);
} }
void void
@ -549,7 +549,8 @@ CClient::handleConnectionFailed(const CEvent& event, void*)
delete m_stream; delete m_stream;
m_stream = NULL; m_stream = NULL;
LOG((CLOG_DEBUG1 "connection failed")); LOG((CLOG_DEBUG1 "connection failed"));
sendConnectionFailedEvent(info->m_what); sendConnectionFailedEvent(info->m_what.c_str());
delete info;
} }
void void

View File

@ -18,6 +18,7 @@
#include "IClient.h" #include "IClient.h"
#include "IClipboard.h" #include "IClipboard.h"
#include "CNetworkAddress.h" #include "CNetworkAddress.h"
#include "INode.h"
class CEventQueueTimer; class CEventQueueTimer;
class CScreen; class CScreen;
@ -31,12 +32,13 @@ class IStreamFilterFactory;
/*! /*!
This class implements the top-level client algorithms for synergy. This class implements the top-level client algorithms for synergy.
*/ */
class CClient : public IClient { class CClient : public IClient, public INode {
public: public:
class CFailInfo { class CFailInfo {
public: public:
CFailInfo(const char* what) : m_retry(false), m_what(what) { }
bool m_retry; bool m_retry;
char m_what[1]; CString m_what;
}; };
/*! /*!

View File

@ -98,6 +98,10 @@
// this one's a little too aggressive // this one's a little too aggressive
# pragma warning(disable: 4127) // conditional expression is constant # pragma warning(disable: 4127) // conditional expression is constant
// Code Analysis
# pragma warning(disable: 6011)
// emitted incorrectly under release build in some circumstances // emitted incorrectly under release build in some circumstances
# if defined(NDEBUG) # if defined(NDEBUG)
# pragma warning(disable: 4702) // unreachable code # pragma warning(disable: 4702) // unreachable code
@ -129,6 +133,15 @@
// define NULL // define NULL
#include <stddef.h> #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 // make assert available since we use it a lot
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>

View File

@ -101,9 +101,9 @@ CTCPListenSocket::getEventTarget() const
IDataSocket* IDataSocket*
CTCPListenSocket::accept() CTCPListenSocket::accept()
{ {
IDataSocket* socket = NULL;
try { try {
IDataSocket* socket = socket = new CTCPSocket(ARCH->acceptSocket(m_socket, NULL));
new CTCPSocket(ARCH->acceptSocket(m_socket, NULL));
if (socket != NULL) { if (socket != NULL) {
CSocketMultiplexer::getInstance()->addSocket(this, CSocketMultiplexer::getInstance()->addSocket(this,
new TSocketMultiplexerMethodJob<CTCPListenSocket>( new TSocketMultiplexerMethodJob<CTCPListenSocket>(
@ -113,8 +113,17 @@ CTCPListenSocket::accept()
return socket; return socket;
} }
catch (XArchNetwork&) { catch (XArchNetwork&) {
if (socket != NULL) {
delete socket;
}
return NULL; return NULL;
} }
catch (std::exception &ex) {
if (socket != NULL) {
delete socket;
}
throw ex;
}
} }
ISocketMultiplexerJob* ISocketMultiplexerJob*

View File

@ -346,11 +346,9 @@ CTCPSocket::newJob()
void void
CTCPSocket::sendConnectionFailedEvent(const char* msg) CTCPSocket::sendConnectionFailedEvent(const char* msg)
{ {
CConnectionFailedInfo* info = (CConnectionFailedInfo*)malloc( CConnectionFailedInfo* info = new CConnectionFailedInfo(msg);
sizeof(CConnectionFailedInfo) + strlen(msg));
strcpy(info->m_what, msg);
EVENTQUEUE->addEvent(CEvent(getConnectionFailedEvent(), EVENTQUEUE->addEvent(CEvent(getConnectionFailedEvent(),
getEventTarget(), info)); getEventTarget(), info, CEvent::kDontFreeData));
} }
void void

View File

@ -17,6 +17,7 @@
#include "ISocket.h" #include "ISocket.h"
#include "IStream.h" #include "IStream.h"
#include "CString.h"
//! Data stream socket interface //! Data stream socket interface
/*! /*!
@ -27,8 +28,8 @@ class IDataSocket : public ISocket, public IStream {
public: public:
class CConnectionFailedInfo { class CConnectionFailedInfo {
public: public:
// pointer to a string describing the failure CConnectionFailedInfo(const char* what) : m_what(what) { }
char m_what[1]; CString m_what;
}; };
//! @name manipulators //! @name manipulators

View File

@ -86,7 +86,7 @@
// //
CMSWindowsDesks::CMSWindowsDesks( CMSWindowsDesks::CMSWindowsDesks(
bool isPrimary, HINSTANCE hookLibrary, bool isPrimary, bool noHooks, HINSTANCE hookLibrary,
const IScreenSaver* screensaver, IJob* updateKeys) : const IScreenSaver* screensaver, IJob* updateKeys) :
m_isPrimary(isPrimary), m_isPrimary(isPrimary),
m_is95Family(CArchMiscWindows::isWindows95Family()), m_is95Family(CArchMiscWindows::isWindows95Family()),
@ -361,7 +361,7 @@ void
CMSWindowsDesks::queryHookLibrary(HINSTANCE hookLibrary) CMSWindowsDesks::queryHookLibrary(HINSTANCE hookLibrary)
{ {
// look up functions // look up functions
if (m_isPrimary) { if (m_isPrimary && !m_noHooks) {
m_install = (InstallFunc)GetProcAddress(hookLibrary, "install"); m_install = (InstallFunc)GetProcAddress(hookLibrary, "install");
m_uninstall = (UninstallFunc)GetProcAddress(hookLibrary, "uninstall"); m_uninstall = (UninstallFunc)GetProcAddress(hookLibrary, "uninstall");
m_installScreensaver = m_installScreensaver =
@ -736,7 +736,7 @@ CMSWindowsDesks::deskThread(void* vdesk)
continue; continue;
case SYNERGY_MSG_SWITCH: case SYNERGY_MSG_SWITCH:
if (m_isPrimary) { if (m_isPrimary && !m_noHooks) {
m_uninstall(); m_uninstall();
if (m_screensaverNotify) { if (m_screensaverNotify) {
m_uninstallScreensaver(); m_uninstallScreensaver();
@ -816,12 +816,14 @@ CMSWindowsDesks::deskThread(void* vdesk)
break; break;
case SYNERGY_MSG_SCREENSAVER: case SYNERGY_MSG_SCREENSAVER:
if (!m_noHooks) {
if (msg.wParam != 0) { if (msg.wParam != 0) {
m_installScreensaver(); m_installScreensaver();
} }
else { else {
m_uninstallScreensaver(); m_uninstallScreensaver();
} }
}
break; break;
case SYNERGY_MSG_FAKE_INPUT: case SYNERGY_MSG_FAKE_INPUT:

View File

@ -59,7 +59,7 @@ public:
updated in a thread attached to the current desk. updated in a thread attached to the current desk.
\p hookLibrary must be a handle to the hook library. \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); const IScreenSaver* screensaver, IJob* updateKeys);
~CMSWindowsDesks(); ~CMSWindowsDesks();
@ -241,6 +241,9 @@ private:
// true if screen is being used as a primary screen, false otherwise // true if screen is being used as a primary screen, false otherwise
bool m_isPrimary; bool m_isPrimary;
// true if hooks are not to be installed (useful for debugging)
bool m_noHooks;
// true if windows 95/98/me // true if windows 95/98/me
bool m_is95Family; bool m_is95Family;

View File

@ -77,8 +77,9 @@
HINSTANCE CMSWindowsScreen::s_instance = NULL; HINSTANCE CMSWindowsScreen::s_instance = NULL;
CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL; CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL;
CMSWindowsScreen::CMSWindowsScreen(bool isPrimary) : CMSWindowsScreen::CMSWindowsScreen(bool isPrimary, bool noHooks) :
m_isPrimary(isPrimary), m_isPrimary(isPrimary),
m_noHooks(noHooks),
m_is95Family(CArchMiscWindows::isWindows95Family()), m_is95Family(CArchMiscWindows::isWindows95Family()),
m_isOnScreen(m_isPrimary), m_isOnScreen(m_isPrimary),
m_class(0), m_class(0),
@ -118,7 +119,8 @@ CMSWindowsScreen::CMSWindowsScreen(bool isPrimary) :
m_hookLibrary = openHookLibrary("synrgyhk"); m_hookLibrary = openHookLibrary("synrgyhk");
} }
m_screensaver = new CMSWindowsScreenSaver(); m_screensaver = new CMSWindowsScreenSaver();
m_desks = new CMSWindowsDesks(m_isPrimary, m_desks = new CMSWindowsDesks(
m_isPrimary, m_noHooks,
m_hookLibrary, m_screensaver, m_hookLibrary, m_screensaver,
new TMethodJob<CMSWindowsScreen>(this, new TMethodJob<CMSWindowsScreen>(this,
&CMSWindowsScreen::updateKeysCB)); &CMSWindowsScreen::updateKeysCB));
@ -491,7 +493,7 @@ void CMSWindowsScreen::saveMousePosition(SInt32 x, SInt32 y) {
m_xCursor = x; m_xCursor = x;
m_yCursor = y; 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 UInt32
@ -763,6 +765,7 @@ CMSWindowsScreen::createBlankCursor() const
// create a transparent cursor // create a transparent cursor
int cw = GetSystemMetrics(SM_CXCURSOR); int cw = GetSystemMetrics(SM_CXCURSOR);
int ch = GetSystemMetrics(SM_CYCURSOR); int ch = GetSystemMetrics(SM_CYCURSOR);
UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)]; UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)];
UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)]; UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)];
memset(cursorAND, 0xff, ch * ((cw + 31) >> 2)); memset(cursorAND, 0xff, ch * ((cw + 31) >> 2));
@ -916,7 +919,7 @@ bool
CMSWindowsScreen::onPreDispatchPrimary(HWND, CMSWindowsScreen::onPreDispatchPrimary(HWND,
UINT message, WPARAM wParam, LPARAM lParam) UINT message, WPARAM wParam, LPARAM lParam)
{ {
LOG((CLOG_DEBUG2 "handling pre-dispatch primary")); LOG((CLOG_DEBUG5 "handling pre-dispatch primary"));
// handle event // handle event
switch (message) { switch (message) {
@ -1298,8 +1301,8 @@ CMSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my)
SInt32 x = mx - m_xCursor; SInt32 x = mx - m_xCursor;
SInt32 y = my - m_yCursor; SInt32 y = my - m_yCursor;
LOG((CLOG_DEBUG2 LOG((CLOG_DEBUG3
"handling mouse move; delta motion calc: %+d=(%+d - %+d),%+d=(%+d - %+d)", "mouse move - motion delta: %+d=(%+d - %+d),%+d=(%+d - %+d)",
x, mx, m_xCursor, y, my, m_yCursor)); x, mx, m_xCursor, y, my, m_yCursor));
// ignore if the mouse didn't move or if message posted prior // 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 // 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 // will always try to return to the original entry point on the
// secondary screen. // 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); warpCursorNoFlush(m_xCenter, m_yCenter);
// examine the motion. if it's about the distance // examine the motion. if it's about the distance

View File

@ -32,7 +32,7 @@ class CThread;
//! Implementation of IPlatformScreen for Microsoft Windows //! Implementation of IPlatformScreen for Microsoft Windows
class CMSWindowsScreen : public CPlatformScreen { class CMSWindowsScreen : public CPlatformScreen {
public: public:
CMSWindowsScreen(bool isPrimary); CMSWindowsScreen(bool isPrimary, bool noHooks);
virtual ~CMSWindowsScreen(); virtual ~CMSWindowsScreen();
//! @name manipulators //! @name manipulators
@ -216,6 +216,9 @@ private:
// true if screen is being used as a primary screen, false otherwise // true if screen is being used as a primary screen, false otherwise
bool m_isPrimary; bool m_isPrimary;
// true if hooks are not to be installed (useful for debugging)
bool m_noHooks;
// true if windows 95/98/me // true if windows 95/98/me
bool m_is95Family; bool m_is95Family;

View File

@ -293,7 +293,7 @@ CServer::adoptClient(CBaseClientProxy* client)
// send notification // send notification
CServer::CScreenConnectedInfo* info = CServer::CScreenConnectedInfo* info =
CServer::CScreenConnectedInfo::alloc(getName(client)); new CServer::CScreenConnectedInfo(getName(client));
EVENTQUEUE->addEvent(CEvent(CServer::getConnectedEvent(), EVENTQUEUE->addEvent(CEvent(CServer::getConnectedEvent(),
m_primaryClient->getEventTarget(), info)); m_primaryClient->getEventTarget(), info));
} }
@ -1638,7 +1638,7 @@ CServer::onMouseUp(ButtonID id)
bool bool
CServer::onMouseMovePrimary(SInt32 x, SInt32 y) 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 // mouse move on primary (server's) screen
if (m_active != m_primaryClient) { if (m_active != m_primaryClient) {
@ -2133,22 +2133,6 @@ CServer::CSwitchInDirectionInfo::alloc(EDirection direction)
return info; 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 // CServer::CKeyboardBroadcastInfo
// //

View File

@ -25,6 +25,7 @@
#include "stdmap.h" #include "stdmap.h"
#include "stdset.h" #include "stdset.h"
#include "stdvector.h" #include "stdvector.h"
#include "INode.h"
class CBaseClientProxy; class CBaseClientProxy;
class CEventQueueTimer; class CEventQueueTimer;
@ -35,7 +36,7 @@ class CInputFilter;
/*! /*!
This class implements the top-level server algorithms for synergy. This class implements the top-level server algorithms for synergy.
*/ */
class CServer { class CServer : public INode {
public: public:
//! Lock cursor to screen data //! Lock cursor to screen data
class CLockCursorToScreenInfo { class CLockCursorToScreenInfo {
@ -70,11 +71,10 @@ public:
//! Screen connected data //! Screen connected data
class CScreenConnectedInfo { class CScreenConnectedInfo {
public: public:
static CScreenConnectedInfo* alloc(const CString& screen); CScreenConnectedInfo(CString screen) : m_screen(screen) { }
public: public:
// this is a C-string; this type is a variable size structure CString m_screen; // was char[1]
char m_screen[1];
}; };
//! Keyboard broadcast data //! Keyboard broadcast data

273
lib/synergy/CApp.cpp Normal file
View File

@ -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();
}

105
lib/synergy/CApp.h Normal file
View File

@ -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

616
lib/synergy/CClientApp.cpp Normal file
View File

@ -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;
}

84
lib/synergy/CClientApp.h Normal file
View File

@ -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);
};

View File

@ -17,8 +17,8 @@
#include "CString.h" #include "CString.h"
#include "IArchTaskBarReceiver.h" #include "IArchTaskBarReceiver.h"
#include "LogOutputters.h"
class CClient; #include "CClient.h"
//! Implementation of IArchTaskBarReceiver for the synergy server //! Implementation of IArchTaskBarReceiver for the synergy server
class CClientTaskBarReceiver : public IArchTaskBarReceiver { class CClientTaskBarReceiver : public IArchTaskBarReceiver {
@ -35,6 +35,8 @@ public:
*/ */
void updateStatus(CClient*, const CString& errorMsg); void updateStatus(CClient*, const CString& errorMsg);
void updateStatus(INode* n, const CString& errorMsg) { updateStatus((CClient*)n, errorMsg); }
//@} //@}
// IArchTaskBarReceiver overrides // IArchTaskBarReceiver overrides
@ -80,4 +82,6 @@ private:
CString m_server; CString m_server;
}; };
IArchTaskBarReceiver* createTaskBarReceiver(const CBufferedLogOutputter* logBuffer);
#endif #endif

View File

@ -98,7 +98,7 @@ CKeyMap::addKeyEntry(const KeyItem& item)
// add item list // add item list
entries.push_back(items); 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 void

969
lib/synergy/CServerApp.cpp Normal file
View File

@ -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
}

130
lib/synergy/CServerApp.h Normal file
View File

@ -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

View File

@ -131,3 +131,21 @@ CServerTaskBarReceiver::getToolTip() const
return ""; 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();
}

View File

@ -18,8 +18,9 @@
#include "CString.h" #include "CString.h"
#include "IArchTaskBarReceiver.h" #include "IArchTaskBarReceiver.h"
#include "stdvector.h" #include "stdvector.h"
#include "CEvent.h"
class CServer; #include "CServerApp.h"
#include "CServer.h"
//! Implementation of IArchTaskBarReceiver for the synergy server //! Implementation of IArchTaskBarReceiver for the synergy server
class CServerTaskBarReceiver : public IArchTaskBarReceiver { class CServerTaskBarReceiver : public IArchTaskBarReceiver {
@ -36,6 +37,8 @@ public:
*/ */
void updateStatus(CServer*, const CString& errorMsg); void updateStatus(CServer*, const CString& errorMsg);
void updateStatus(INode* n, const CString& errorMsg) { updateStatus((CServer*)n, errorMsg); }
//@} //@}
// IArchTaskBarReceiver overrides // IArchTaskBarReceiver overrides
@ -79,10 +82,17 @@ protected:
*/ */
virtual void onStatusChanged(CServer* server); virtual void onStatusChanged(CServer* server);
protected:
CEvent::Type getReloadConfigEvent();
CEvent::Type getForceReconnectEvent();
CEvent::Type getResetServerEvent();
private: private:
EState m_state; EState m_state;
CString m_errorMessage; CString m_errorMessage;
CClients m_clients; CClients m_clients;
}; };
IArchTaskBarReceiver* createTaskBarReceiver(const CBufferedLogOutputter* logBuffer);
#endif #endif

19
lib/synergy/INode.h Normal file
View File

@ -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 {
};