Merged Win32 updates. Added full warnings on g++. Fixed bug in

client when handling server rejection.
This commit is contained in:
crs 2004-02-28 12:19:49 +00:00
parent 612a2054e6
commit 54acf38d82
74 changed files with 2333 additions and 2185 deletions

View File

@ -513,3 +513,33 @@ else
$2 $2
fi fi
])dnl ACX_PTHREAD ])dnl ACX_PTHREAD
dnl enable maximum compiler warnings
dnl we only know how to do this for g++
AC_DEFUN([ACX_CXX_WARNINGS], [
AC_MSG_CHECKING([for C++ compiler warning flags])
if test "$GXX" = "yes"; then
acx_cxx_warnings="-Wall"
fi
if test -n "$acx_cxx_warnings"; then
CXXFLAGS="$CXXFLAGS $acx_cxx_warnings"
else
acx_cxx_warnings="unknown"
fi
AC_MSG_RESULT($acx_cxx_warnings)
])dnl ACX_CXX_WARNINGS
dnl enable compiler warnings are errors
dnl we only know how to do this for g++
AC_DEFUN([ACX_CXX_WARNINGS_ARE_ERRORS], [
AC_MSG_CHECKING([for C++ compiler warning are errors flags])
if test "$GXX" = "yes"; then
acx_cxx_warnings_are_errors="-Werror"
fi
if test -n "$acx_cxx_warnings_are_errors"; then
CXXFLAGS="$CXXFLAGS $acx_cxx_warnings_are_errors"
else
acx_cxx_warnings_are_errors="unknown"
fi
AC_MSG_RESULT($acx_cxx_warnings_are_errors)
])dnl ACX_CXX_WARNINGS_ARE_ERRORS

View File

@ -14,6 +14,7 @@
#include "CConfig.h" #include "CConfig.h"
#include "LaunchUtil.h" #include "LaunchUtil.h"
#include "CMSWindowsUtil.h"
#include "CArch.h" #include "CArch.h"
#include "resource.h" #include "resource.h"
#include "stdfstream.h" #include "stdfstream.h"
@ -23,32 +24,13 @@
CString CString
getString(DWORD id) getString(DWORD id)
{ {
char buffer[1024]; return CMSWindowsUtil::getString(s_instance, id);
buffer[0] = '\0';
LoadString(s_instance, id, buffer, sizeof(buffer) / sizeof(buffer[0]));
return buffer;
} }
CString CString
getErrorString(DWORD error) getErrorString(DWORD error)
{ {
char* buffer; return CMSWindowsUtil::getErrorString(s_instance, error, IDS_ERROR);
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_SYSTEM,
0,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&buffer,
0,
NULL) == 0) {
return getString(IDS_ERROR);
}
else {
CString result(buffer);
LocalFree(buffer);
return result;
}
} }
void void

View File

@ -332,8 +332,8 @@ BEGIN
"Synergy is not configured to start automatically." "Synergy is not configured to start automatically."
IDS_INSTALL_LABEL "Install" IDS_INSTALL_LABEL "Install"
IDS_UNINSTALL_LABEL "Uninstall" IDS_UNINSTALL_LABEL "Uninstall"
IDS_INSTALL_GENERIC_ERROR "Install failed: %{1}." IDS_INSTALL_GENERIC_ERROR "Install failed: %{1}"
IDS_UNINSTALL_GENERIC_ERROR "Uninstall failed: %{1}." IDS_UNINSTALL_GENERIC_ERROR "Uninstall failed: %{1}"
IDS_INSTALL_TITLE "Installed Auto-Start" IDS_INSTALL_TITLE "Installed Auto-Start"
IDS_INSTALLED_SYSTEM "Installed auto-start. Synergy will now automatically start each time you start your computer." IDS_INSTALLED_SYSTEM "Installed auto-start. Synergy will now automatically start each time you start your computer."
IDS_INSTALLED_USER "Installed auto-start. Synergy will now automatically start each time you log in." IDS_INSTALLED_USER "Installed auto-start. Synergy will now automatically start each time you log in."
@ -348,6 +348,7 @@ BEGIN
IDS_SERVER_IS_CLIENT "Please enter the computer name of the synergy server, not\nthe name of this computer, in the Server Host Name field." IDS_SERVER_IS_CLIENT "Please enter the computer name of the synergy server, not\nthe name of this computer, in the Server Host Name field."
IDS_ADD_SCREEN "Add Screen" IDS_ADD_SCREEN "Add Screen"
IDS_EDIT_SCREEN "Edit Screen %{1}" IDS_EDIT_SCREEN "Edit Screen %{1}"
IDS_ERROR_CODE "Error code: %{1}"
END END
#endif // English (U.S.) resources #endif // English (U.S.) resources

View File

@ -41,6 +41,7 @@
#define IDS_ADD_SCREEN 37 #define IDS_ADD_SCREEN 37
#define IDS_EDIT_SCREEN 38 #define IDS_EDIT_SCREEN 38
#define IDS_INVALID_TIME 39 #define IDS_INVALID_TIME 39
#define IDS_ERROR_CODE 39
#define IDD_MAIN 101 #define IDD_MAIN 101
#define IDD_ADD 102 #define IDD_ADD 102
#define IDD_WAIT 103 #define IDD_WAIT 103

View File

@ -21,18 +21,19 @@
#include "CArchTaskBarWindows.h" #include "CArchTaskBarWindows.h"
#include "resource.h" #include "resource.h"
static const UINT g_stateToIconID[CMSWindowsClientTaskBarReceiver::kMaxState] = //
// CMSWindowsClientTaskBarReceiver
//
const UINT CMSWindowsClientTaskBarReceiver::s_stateToIconID[kMaxState] =
{ {
IDI_TASKBAR_NOT_RUNNING, IDI_TASKBAR_NOT_RUNNING,
IDI_TASKBAR_NOT_WORKING, IDI_TASKBAR_NOT_WORKING,
IDI_TASKBAR_NOT_CONNECTED, IDI_TASKBAR_NOT_CONNECTED,
IDI_TASKBAR_NOT_CONNECTED,
IDI_TASKBAR_CONNECTED IDI_TASKBAR_CONNECTED
}; };
//
// CMSWindowsClientTaskBarReceiver
//
CMSWindowsClientTaskBarReceiver::CMSWindowsClientTaskBarReceiver( CMSWindowsClientTaskBarReceiver::CMSWindowsClientTaskBarReceiver(
HINSTANCE appInstance, const CBufferedLogOutputter* logBuffer) : HINSTANCE appInstance, const CBufferedLogOutputter* logBuffer) :
CClientTaskBarReceiver(), CClientTaskBarReceiver(),
@ -41,7 +42,7 @@ CMSWindowsClientTaskBarReceiver::CMSWindowsClientTaskBarReceiver(
m_logBuffer(logBuffer) m_logBuffer(logBuffer)
{ {
for (UInt32 i = 0; i < kMaxState; ++i) { for (UInt32 i = 0; i < kMaxState; ++i) {
m_icon[i] = loadIcon(g_stateToIconID[i]); m_icon[i] = loadIcon(s_stateToIconID[i]);
} }
m_menu = LoadMenu(m_appInstance, MAKEINTRESOURCE(IDR_TASKBAR)); m_menu = LoadMenu(m_appInstance, MAKEINTRESOURCE(IDR_TASKBAR));
@ -171,7 +172,7 @@ CMSWindowsClientTaskBarReceiver::primaryAction()
const IArchTaskBarReceiver::Icon const IArchTaskBarReceiver::Icon
CMSWindowsClientTaskBarReceiver::getIcon() const CMSWindowsClientTaskBarReceiver::getIcon() const
{ {
return reinterpret_cast<Icon>(m_icon[getState()]); return reinterpret_cast<Icon>(m_icon[getStatus()]);
} }
void void

View File

@ -58,6 +58,7 @@ private:
HMENU m_menu; HMENU m_menu;
HICON m_icon[kMaxState]; HICON m_icon[kMaxState];
const CBufferedLogOutputter* m_logBuffer; const CBufferedLogOutputter* m_logBuffer;
static const UINT s_stateToIconID[];
}; };
#endif #endif

View File

@ -19,7 +19,8 @@
// CXWindowsClientTaskBarReceiver // CXWindowsClientTaskBarReceiver
// //
CXWindowsClientTaskBarReceiver::CXWindowsClientTaskBarReceiver() CXWindowsClientTaskBarReceiver::CXWindowsClientTaskBarReceiver(
const CBufferedLogOutputter*)
{ {
// add ourself to the task bar // add ourself to the task bar
ARCH->addReceiver(this); ARCH->addReceiver(this);

View File

@ -17,10 +17,12 @@
#include "CClientTaskBarReceiver.h" #include "CClientTaskBarReceiver.h"
class CBufferedLogOutputter;
//! Implementation of CClientTaskBarReceiver for X Windows //! Implementation of CClientTaskBarReceiver for X Windows
class CXWindowsClientTaskBarReceiver : public CClientTaskBarReceiver { class CXWindowsClientTaskBarReceiver : public CClientTaskBarReceiver {
public: public:
CXWindowsClientTaskBarReceiver(); CXWindowsClientTaskBarReceiver(const CBufferedLogOutputter*);
virtual ~CXWindowsClientTaskBarReceiver(); virtual ~CXWindowsClientTaskBarReceiver();
// IArchTaskBarReceiver overrides // IArchTaskBarReceiver overrides

View File

@ -48,6 +48,7 @@ synergyc_LDADD = \
$(DEPTH)/lib/io/libio.a \ $(DEPTH)/lib/io/libio.a \
$(DEPTH)/lib/mt/libmt.a \ $(DEPTH)/lib/mt/libmt.a \
$(DEPTH)/lib/base/libbase.a \ $(DEPTH)/lib/base/libbase.a \
$(DEPTH)/lib/common/libcommon.a \
$(DEPTH)/lib/arch/libarch.a \ $(DEPTH)/lib/arch/libarch.a \
$(X_LIBS) \ $(X_LIBS) \
$(X_PRE_LIBS) \ $(X_PRE_LIBS) \

View File

@ -3,6 +3,8 @@
// Used by synergyc.rc // Used by synergyc.rc
// //
#define IDS_FAILED 1 #define IDS_FAILED 1
#define IDS_INIT_FAILED 2
#define IDS_UNCAUGHT_EXCEPTION 3
#define IDI_SYNERGY 101 #define IDI_SYNERGY 101
#define IDI_TASKBAR_NOT_RUNNING 102 #define IDI_TASKBAR_NOT_RUNNING 102
#define IDI_TASKBAR_NOT_WORKING 103 #define IDI_TASKBAR_NOT_WORKING 103

View File

@ -26,6 +26,7 @@
#include "CFunctionEventJob.h" #include "CFunctionEventJob.h"
#include "CLog.h" #include "CLog.h"
#include "CString.h" #include "CString.h"
#include "CStringUtil.h"
#include "LogOutputters.h" #include "LogOutputters.h"
#include "CArch.h" #include "CArch.h"
#include "XArch.h" #include "XArch.h"
@ -33,8 +34,9 @@
#define DAEMON_RUNNING(running_) #define DAEMON_RUNNING(running_)
#if WINDOWS_LIKE #if WINDOWS_LIKE
#include "CMSWindowsScreen.h"
#include "CArchMiscWindows.h" #include "CArchMiscWindows.h"
#include "CMSWindowsScreen.h"
#include "CMSWindowsUtil.h"
#include "CMSWindowsClientTaskBarReceiver.h" #include "CMSWindowsClientTaskBarReceiver.h"
#include "resource.h" #include "resource.h"
#undef DAEMON_RUNNING #undef DAEMON_RUNNING
@ -51,6 +53,9 @@
#define DAEMON_NAME "synergyc" #define DAEMON_NAME "synergyc"
#endif #endif
typedef int (*StartupFunc)(int, char**);
static void parse(int argc, const char* const* argv);
// //
// program arguments // program arguments
// //
@ -64,7 +69,8 @@ public:
m_backend(false), m_backend(false),
m_restartable(true), m_restartable(true),
m_daemon(true), m_daemon(true),
m_logFilter(NULL) m_logFilter(NULL),
m_serverAddress(NULL)
{ s_instance = this; } { s_instance = this; }
~CArgs() { s_instance = NULL; } ~CArgs() { s_instance = NULL; }
@ -76,7 +82,7 @@ public:
bool m_daemon; bool m_daemon;
const char* m_logFilter; const char* m_logFilter;
CString m_name; CString m_name;
CNetworkAddress m_serverAddress; CNetworkAddress* m_serverAddress;
}; };
CArgs* CArgs::s_instance = NULL; CArgs* CArgs::s_instance = NULL;
@ -97,6 +103,18 @@ createScreen()
#endif #endif
} }
static
CClientTaskBarReceiver*
createTaskBarReceiver(const CBufferedLogOutputter* logBuffer)
{
#if WINDOWS_LIKE
return new CMSWindowsClientTaskBarReceiver(
CMSWindowsScreen::getInstance(), logBuffer);
#elif UNIX_LIKE
return new CXWindowsClientTaskBarReceiver(logBuffer);
#endif
}
// //
// platform independent main // platform independent main
@ -292,7 +310,7 @@ startClient()
try { try {
clientScreen = openClientScreen(); clientScreen = openClientScreen();
s_client = openClient(ARG->m_name, s_client = openClient(ARG->m_name,
ARG->m_serverAddress, clientScreen); *ARG->m_serverAddress, clientScreen);
s_clientScreen = clientScreen; s_clientScreen = clientScreen;
LOG((CLOG_NOTE "started client")); LOG((CLOG_NOTE "started client"));
s_client->connect(); s_client->connect();
@ -338,7 +356,7 @@ stopClient()
static static
int int
realMain() mainLoop()
{ {
// start the client. if this return false then we've failed and // start the client. if this return false then we've failed and
// we shouldn't retry. // we shouldn't retry.
@ -350,8 +368,8 @@ realMain()
// run event loop. if startClient() failed we're supposed to retry // run event loop. if startClient() failed we're supposed to retry
// later. the timer installed by startClient() will take care of // later. the timer installed by startClient() will take care of
// that. // that.
DAEMON_RUNNING(true);
CEvent event; CEvent event;
DAEMON_RUNNING(true);
EVENTQUEUE->getEvent(event); EVENTQUEUE->getEvent(event);
while (event.getType() != CEvent::kQuit) { while (event.getType() != CEvent::kQuit) {
EVENTQUEUE->dispatchEvent(event); EVENTQUEUE->dispatchEvent(event);
@ -369,45 +387,65 @@ realMain()
return kExitSuccess; return kExitSuccess;
} }
/*
static static
void int
realMainEntry(void* vresult) daemonMainLoop(int, const char**)
{ {
*reinterpret_cast<int*>(vresult) = realMain(); CSystemLogger sysLogger(DAEMON_NAME);
return mainLoop();
} }
static static
int int
runMainInThread(void) standardStartup(int argc, char** argv)
{ {
int result = 0; // parse command line
CThread appThread(new CFunctionJob(&realMainEntry, &result)); parse(argc, argv);
try {
#if WINDOWS_LIKE // daemonize if requested
MSG msg; if (ARG->m_daemon) {
while (appThread.waitForEvent(-1.0) == CThread::kEvent) { return ARCH->daemonize(DAEMON_NAME, &daemonMainLoop);
// check for a quit event
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
CThread::getCurrentThread().cancel();
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
#else
appThread.wait(-1.0);
#endif
return result;
} }
catch (XThread&) { else {
appThread.cancel(); return mainLoop();
appThread.wait(-1.0);
throw;
} }
} }
*/
static
int
run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup)
{
// general initialization
CSocketMultiplexer multiplexer;
CEventQueue eventQueue;
ARG->m_serverAddress = new CNetworkAddress;
ARG->m_pname = ARCH->getBasename(argv[0]);
// install caller's output filter
if (outputter != NULL) {
CLOG->insert(outputter);
}
// save log messages
CBufferedLogOutputter logBuffer(1000);
CLOG->insert(&logBuffer, true);
// make the task bar receiver. the user can control this app
// through the task bar.
s_taskBarReceiver = createTaskBarReceiver(&logBuffer);
// run
int result = startup(argc, argv);
// done with task bar receiver
delete s_taskBarReceiver;
// done with log buffer
CLOG->remove(&logBuffer);
delete ARG->m_serverAddress;
return result;
}
// //
@ -588,7 +626,7 @@ parse(int argc, const char* const* argv)
// save server address // save server address
try { try {
ARG->m_serverAddress = CNetworkAddress(argv[i], kDefaultPort); *ARG->m_serverAddress = CNetworkAddress(argv[i], kDefaultPort);
} }
catch (XSocketAddress& e) { catch (XSocketAddress& e) {
LOG((CLOG_PRINT "%s: %s" BYE, LOG((CLOG_PRINT "%s: %s" BYE,
@ -676,192 +714,106 @@ byeThrow(int x)
static static
int int
daemonStartup(int argc, const char** argv) daemonNTMainLoop(int argc, const char** argv)
{ {
CSystemLogger sysLogger(DAEMON_NAME);
// have to cancel this thread to quit
s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread()));
// catch errors that would normally exit
bye = &byeThrow;
// parse command line
parse(argc, argv); parse(argc, argv);
// cannot run as backend if running as a service
ARG->m_backend = false; ARG->m_backend = false;
return CArchMiscWindows::runDaemon(mainLoop);
// run as a service
return CArchMiscWindows::runDaemon(realMain);
} }
static static
int int
daemonStartup95(int, const char**) daemonNTStartup(int, char**)
{ {
CSystemLogger sysLogger(DAEMON_NAME); CSystemLogger sysLogger(DAEMON_NAME);
return runMainInThread(); bye = &byeThrow;
return ARCH->daemonize(DAEMON_NAME, &daemonNTMainLoop);
} }
static static
int void
run(int argc, char** argv) showError(HINSTANCE instance, const char* title, UINT id, const char* arg)
{ {
// windows NT family starts services using no command line options. CString fmt = CMSWindowsUtil::getString(instance, id);
// since i'm not sure how to tell the difference between that and CString msg = CStringUtil::format(fmt.c_str(), arg);
// a user providing no options we'll assume that if there are no MessageBox(NULL, msg.c_str(), title, MB_OK | MB_ICONWARNING);
// arguments and we're on NT then we're being invoked as a service.
// users on NT can use `--daemon' or `--no-daemon' to force us out
// of the service code path.
if (argc <= 1 && !CArchMiscWindows::isWindows95Family()) {
try {
return ARCH->daemonize(DAEMON_NAME, &daemonStartup);
}
catch (XArchDaemon& e) {
LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname));
}
return kExitFailed;
}
// parse command line
parse(argc, argv);
// daemonize if requested
if (ARG->m_daemon) {
// start as a daemon
if (CArchMiscWindows::isWindows95Family()) {
try {
return ARCH->daemonize(DAEMON_NAME, &daemonStartup95);
}
catch (XArchDaemon& e) {
LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname));
}
return kExitFailed;
}
else {
// cannot start a service from the command line so just
// run normally (except with log messages redirected).
CSystemLogger sysLogger(DAEMON_NAME);
return runMainInThread();
}
}
else {
// run
return runMainInThread();
}
} }
int WINAPI int WINAPI
WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
{ {
CArch arch(instance);
CLOG;
CArgs args;
// save instance
CMSWindowsScreen::init(instance);
// get program name
ARG->m_pname = ARCH->getBasename(__argv[0]);
// send PRINT and FATAL output to a message box
CLOG->insert(new CMessageBoxOutputter);
// save log messages
CBufferedLogOutputter logBuffer(1000);
CLOG->insert(&logBuffer, true);
// make the task bar receiver. the user can control this app
// through the task bar.
s_taskBarReceiver = new CMSWindowsClientTaskBarReceiver(instance,
&logBuffer);
s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread()));
int result;
try { try {
// run in foreground or as a daemon CArch arch(instance);
result = run(__argc, __argv); CMSWindowsScreen::init(instance);
CLOG;
// FIXME
// CThread::getCurrentThread().setPriority(-14);
CArgs args;
// windows NT family starts services using no command line options.
// since i'm not sure how to tell the difference between that and
// a user providing no options we'll assume that if there are no
// arguments and we're on NT then we're being invoked as a service.
// users on NT can use `--daemon' or `--no-daemon' to force us out
// of the service code path.
StartupFunc startup = &standardStartup;
if (__argc <= 1 && !CArchMiscWindows::isWindows95Family()) {
startup = &daemonNTStartup;
}
// send PRINT and FATAL output to a message box
int result = run(__argc, __argv, new CMessageBoxOutputter, startup);
// let user examine any messages if we're running as a backend
// by putting up a dialog box before exiting.
if (args.m_backend && s_hasImportantLogMessages) {
showError(instance, args.m_pname, IDS_FAILED, "");
}
delete CLOG;
return result;
}
catch (XBase& e) {
showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, e.what());
throw;
}
catch (XArch& e) {
showError(instance, __argv[0], IDS_INIT_FAILED, e.what().c_str());
return kExitFailed;
} }
catch (...) { catch (...) {
// note that we don't rethrow thread cancellation. we'll showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, "<unknown>");
// be exiting soon so it doesn't matter. what we'd like throw;
// is for everything after this try/catch to be in a
// finally block.
result = kExitFailed;
} }
// done with task bar receiver
delete s_taskBarReceiver;
// done with log buffer
CLOG->remove(&logBuffer);
// let user examine any messages if we're running as a backend
// by putting up a dialog box before exiting.
if (ARG->m_backend && s_hasImportantLogMessages) {
char msg[1024];
msg[0] = '\0';
LoadString(instance, IDS_FAILED, msg, sizeof(msg) / sizeof(msg[0]));
MessageBox(NULL, msg, ARG->m_pname, MB_OK | MB_ICONWARNING);
}
delete CLOG;
return result;
} }
#elif UNIX_LIKE #elif UNIX_LIKE
static
int
daemonStartup(int, const char**)
{
CSystemLogger sysLogger(DAEMON_NAME);
return realMain();
}
int int
main(int argc, char** argv) main(int argc, char** argv)
{ {
CArch arch;
CLOG;
// go really fast
CThread::getCurrentThread().setPriority(-14);
CSocketMultiplexer multiplexer;
CEventQueue eventQueue;
// get program name
CArgs args; CArgs args;
ARG->m_pname = ARCH->getBasename(argv[0]); try {
int result;
// make the task bar receiver. the user can control this app CArch arch;
// through the task bar. CLOG;
s_taskBarReceiver = new CXWindowsClientTaskBarReceiver; CArgs args;
result = run(argc, argv, NULL, &standardStartup);
// parse command line delete CLOG;
parse(argc, argv); return result;
// daemonize if requested
int result;
if (ARG->m_daemon) {
try {
result = ARCH->daemonize(DAEMON_NAME, &daemonStartup);
}
catch (XArchDaemon&) {
LOG((CLOG_CRIT "failed to daemonize"));
result = kExitFailed;
}
} }
else { catch (XBase& e) {
result = realMain(); 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;
} }
// done with task bar receiver
delete s_taskBarReceiver;
return result;
} }
#else #else

View File

@ -99,6 +99,8 @@ END
STRINGTABLE DISCARDABLE STRINGTABLE DISCARDABLE
BEGIN BEGIN
IDS_FAILED "Synergy is about to quit with errors or warnings. Please check the log then click OK." IDS_FAILED "Synergy is about to quit with errors or warnings. Please check the log then click OK."
IDS_INIT_FAILED "Synergy failed to initialize: %{1}"
IDS_UNCAUGHT_EXCEPTION "Uncaught exception: %{1}"
END END
#endif // English (U.S.) resources #endif // English (U.S.) resources

View File

@ -21,7 +21,11 @@
#include "CArchTaskBarWindows.h" #include "CArchTaskBarWindows.h"
#include "resource.h" #include "resource.h"
static const UINT g_stateToIconID[CMSWindowsServerTaskBarReceiver::kMaxState] = //
// CMSWindowsServerTaskBarReceiver
//
const UINT CMSWindowsServerTaskBarReceiver::s_stateToIconID[kMaxState] =
{ {
IDI_TASKBAR_NOT_RUNNING, IDI_TASKBAR_NOT_RUNNING,
IDI_TASKBAR_NOT_WORKING, IDI_TASKBAR_NOT_WORKING,
@ -29,10 +33,6 @@ static const UINT g_stateToIconID[CMSWindowsServerTaskBarReceiver::kMaxState] =
IDI_TASKBAR_CONNECTED IDI_TASKBAR_CONNECTED
}; };
//
// CMSWindowsServerTaskBarReceiver
//
CMSWindowsServerTaskBarReceiver::CMSWindowsServerTaskBarReceiver( CMSWindowsServerTaskBarReceiver::CMSWindowsServerTaskBarReceiver(
HINSTANCE appInstance, const CBufferedLogOutputter* logBuffer) : HINSTANCE appInstance, const CBufferedLogOutputter* logBuffer) :
CServerTaskBarReceiver(), CServerTaskBarReceiver(),
@ -41,7 +41,7 @@ CMSWindowsServerTaskBarReceiver::CMSWindowsServerTaskBarReceiver(
m_logBuffer(logBuffer) m_logBuffer(logBuffer)
{ {
for (UInt32 i = 0; i < kMaxState; ++i) { for (UInt32 i = 0; i < kMaxState; ++i) {
m_icon[i] = loadIcon(g_stateToIconID[i]); m_icon[i] = loadIcon(s_stateToIconID[i]);
} }
m_menu = LoadMenu(m_appInstance, MAKEINTRESOURCE(IDR_TASKBAR)); m_menu = LoadMenu(m_appInstance, MAKEINTRESOURCE(IDR_TASKBAR));
@ -77,12 +77,7 @@ CMSWindowsServerTaskBarReceiver::showStatus()
std::string status = getToolTip(); std::string status = getToolTip();
// get the connect clients, if any // get the connect clients, if any
typedef std::vector<CString> CClientList; const CClients& clients = getClients();
CClientList clients;
CServer* server = getServer();
if (server != NULL) {
server->getClients(clients);
}
// done getting status // done getting status
unlock(); unlock();
@ -92,7 +87,7 @@ CMSWindowsServerTaskBarReceiver::showStatus()
SendMessage(child, WM_SETTEXT, 0, (LPARAM)status.c_str()); SendMessage(child, WM_SETTEXT, 0, (LPARAM)status.c_str());
child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_CLIENTS); child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_CLIENTS);
SendMessage(child, LB_RESETCONTENT, 0, 0); SendMessage(child, LB_RESETCONTENT, 0, 0);
for (CClientList::const_iterator index = clients.begin(); for (CClients::const_iterator index = clients.begin();
index != clients.end(); ) { index != clients.end(); ) {
const char* client = index->c_str(); const char* client = index->c_str();
if (++index == clients.end()) { if (++index == clients.end()) {
@ -191,7 +186,7 @@ CMSWindowsServerTaskBarReceiver::primaryAction()
const IArchTaskBarReceiver::Icon const IArchTaskBarReceiver::Icon
CMSWindowsServerTaskBarReceiver::getIcon() const CMSWindowsServerTaskBarReceiver::getIcon() const
{ {
return reinterpret_cast<Icon>(m_icon[getState()]); return reinterpret_cast<Icon>(m_icon[getStatus()]);
} }
void void

View File

@ -58,6 +58,7 @@ private:
HMENU m_menu; HMENU m_menu;
HICON m_icon[kMaxState]; HICON m_icon[kMaxState];
const CBufferedLogOutputter* m_logBuffer; const CBufferedLogOutputter* m_logBuffer;
static const UINT s_stateToIconID[];
}; };
#endif #endif

View File

@ -19,7 +19,8 @@
// CXWindowsServerTaskBarReceiver // CXWindowsServerTaskBarReceiver
// //
CXWindowsServerTaskBarReceiver::CXWindowsServerTaskBarReceiver() CXWindowsServerTaskBarReceiver::CXWindowsServerTaskBarReceiver(
const CBufferedLogOutputter*)
{ {
// add ourself to the task bar // add ourself to the task bar
ARCH->addReceiver(this); ARCH->addReceiver(this);

View File

@ -17,10 +17,12 @@
#include "CServerTaskBarReceiver.h" #include "CServerTaskBarReceiver.h"
class CBufferedLogOutputter;
//! Implementation of CServerTaskBarReceiver for X Windows //! Implementation of CServerTaskBarReceiver for X Windows
class CXWindowsServerTaskBarReceiver : public CServerTaskBarReceiver { class CXWindowsServerTaskBarReceiver : public CServerTaskBarReceiver {
public: public:
CXWindowsServerTaskBarReceiver(); CXWindowsServerTaskBarReceiver(const CBufferedLogOutputter*);
virtual ~CXWindowsServerTaskBarReceiver(); virtual ~CXWindowsServerTaskBarReceiver();
// IArchTaskBarReceiver overrides // IArchTaskBarReceiver overrides

View File

@ -48,6 +48,7 @@ synergys_LDADD = \
$(DEPTH)/lib/io/libio.a \ $(DEPTH)/lib/io/libio.a \
$(DEPTH)/lib/mt/libmt.a \ $(DEPTH)/lib/mt/libmt.a \
$(DEPTH)/lib/base/libbase.a \ $(DEPTH)/lib/base/libbase.a \
$(DEPTH)/lib/common/libcommon.a \
$(DEPTH)/lib/arch/libarch.a \ $(DEPTH)/lib/arch/libarch.a \
$(X_LIBS) \ $(X_LIBS) \
$(X_PRE_LIBS) \ $(X_PRE_LIBS) \

View File

@ -3,6 +3,8 @@
// Used by synergys.rc // Used by synergys.rc
// //
#define IDS_FAILED 1 #define IDS_FAILED 1
#define IDS_INIT_FAILED 2
#define IDS_UNCAUGHT_EXCEPTION 3
#define IDI_SYNERGY 101 #define IDI_SYNERGY 101
#define IDI_TASKBAR_NOT_RUNNING 102 #define IDI_TASKBAR_NOT_RUNNING 102
#define IDI_TASKBAR_NOT_WORKING 103 #define IDI_TASKBAR_NOT_WORKING 103

View File

@ -28,6 +28,8 @@
#include "CEventQueue.h" #include "CEventQueue.h"
#include "CFunctionEventJob.h" #include "CFunctionEventJob.h"
#include "CLog.h" #include "CLog.h"
#include "CString.h"
#include "CStringUtil.h"
#include "LogOutputters.h" #include "LogOutputters.h"
#include "CArch.h" #include "CArch.h"
#include "XArch.h" #include "XArch.h"
@ -36,8 +38,9 @@
#define DAEMON_RUNNING(running_) #define DAEMON_RUNNING(running_)
#if WINDOWS_LIKE #if WINDOWS_LIKE
#include "CMSWindowsScreen.h"
#include "CArchMiscWindows.h" #include "CArchMiscWindows.h"
#include "CMSWindowsScreen.h"
#include "CMSWindowsUtil.h"
#include "CMSWindowsServerTaskBarReceiver.h" #include "CMSWindowsServerTaskBarReceiver.h"
#include "resource.h" #include "resource.h"
#undef DAEMON_RUNNING #undef DAEMON_RUNNING
@ -63,6 +66,10 @@
#define SYS_CONFIG_NAME "synergy.conf" #define SYS_CONFIG_NAME "synergy.conf"
#endif #endif
typedef int (*StartupFunc)(int, char**);
static void parse(int argc, const char* const* argv);
static void loadConfig();
// //
// program arguments // program arguments
// //
@ -90,8 +97,8 @@ public:
const char* m_configFile; const char* m_configFile;
const char* m_logFilter; const char* m_logFilter;
CString m_name; CString m_name;
CNetworkAddress m_synergyAddress; CNetworkAddress* m_synergyAddress;
CConfig m_config; CConfig* m_config;
}; };
CArgs* CArgs::s_instance = NULL; CArgs* CArgs::s_instance = NULL;
@ -112,6 +119,18 @@ createScreen()
#endif #endif
} }
static
CServerTaskBarReceiver*
createTaskBarReceiver(const CBufferedLogOutputter* logBuffer)
{
#if WINDOWS_LIKE
return new CMSWindowsServerTaskBarReceiver(
CMSWindowsScreen::getInstance(), logBuffer);
#elif UNIX_LIKE
return new CXWindowsServerTaskBarReceiver(logBuffer);
#endif
}
// //
// platform independent main // platform independent main
@ -299,11 +318,11 @@ startServer()
CPrimaryClient* primaryClient = NULL; CPrimaryClient* primaryClient = NULL;
CClientListener* listener = NULL; CClientListener* listener = NULL;
try { try {
CString name = ARG->m_config.getCanonicalName(ARG->m_name); CString name = ARG->m_config->getCanonicalName(ARG->m_name);
serverScreen = openServerScreen(); serverScreen = openServerScreen();
primaryClient = openPrimaryClient(name, serverScreen); primaryClient = openPrimaryClient(name, serverScreen);
listener = openClientListener(ARG->m_config.getSynergyAddress()); listener = openClientListener(ARG->m_config->getSynergyAddress());
s_server = openServer(ARG->m_config, primaryClient); s_server = openServer(*ARG->m_config, primaryClient);
s_serverScreen = serverScreen; s_serverScreen = serverScreen;
s_primaryClient = primaryClient; s_primaryClient = primaryClient;
s_listener = listener; s_listener = listener;
@ -372,26 +391,26 @@ stopServer()
static static
int int
realMain() mainLoop()
{ {
// if configuration has no screens then add this system // if configuration has no screens then add this system
// as the default // as the default
if (ARG->m_config.begin() == ARG->m_config.end()) { if (ARG->m_config->begin() == ARG->m_config->end()) {
ARG->m_config.addScreen(ARG->m_name); ARG->m_config->addScreen(ARG->m_name);
} }
// set the contact address, if provided, in the config. // set the contact address, if provided, in the config.
// otherwise, if the config doesn't have an address, use // otherwise, if the config doesn't have an address, use
// the default. // the default.
if (ARG->m_synergyAddress.isValid()) { if (ARG->m_synergyAddress->isValid()) {
ARG->m_config.setSynergyAddress(ARG->m_synergyAddress); ARG->m_config->setSynergyAddress(*ARG->m_synergyAddress);
} }
else if (!ARG->m_config.getSynergyAddress().isValid()) { else if (!ARG->m_config->getSynergyAddress().isValid()) {
ARG->m_config.setSynergyAddress(CNetworkAddress(kDefaultPort)); ARG->m_config->setSynergyAddress(CNetworkAddress(kDefaultPort));
} }
// canonicalize the primary screen name // canonicalize the primary screen name
CString primaryName = ARG->m_config.getCanonicalName(ARG->m_name); CString primaryName = ARG->m_config->getCanonicalName(ARG->m_name);
if (primaryName.empty()) { if (primaryName.empty()) {
LOG((CLOG_CRIT "unknown screen name `%s'", ARG->m_name.c_str())); LOG((CLOG_CRIT "unknown screen name `%s'", ARG->m_name.c_str()));
return kExitFailed; return kExitFailed;
@ -407,8 +426,8 @@ realMain()
// run event loop. if startServer() failed we're supposed to retry // run event loop. if startServer() failed we're supposed to retry
// later. the timer installed by startServer() will take care of // later. the timer installed by startServer() will take care of
// that. // that.
DAEMON_RUNNING(true);
CEvent event; CEvent event;
DAEMON_RUNNING(true);
EVENTQUEUE->getEvent(event); EVENTQUEUE->getEvent(event);
while (event.getType() != CEvent::kQuit) { while (event.getType() != CEvent::kQuit) {
EVENTQUEUE->dispatchEvent(event); EVENTQUEUE->dispatchEvent(event);
@ -426,43 +445,71 @@ realMain()
return kExitSuccess; return kExitSuccess;
} }
/* XXX
static static
void int
realMainEntry(void* vresult) daemonMainLoop(int, const char**)
{ {
*reinterpret_cast<int*>(vresult) = realMain(); CSystemLogger sysLogger(DAEMON_NAME);
return mainLoop();
} }
static static
int int
runMainInThread(void) standardStartup(int argc, char** argv)
{ {
int result = 0; // parse command line
CThread appThread(new CFunctionJob(&realMainEntry, &result)); parse(argc, argv);
try {
#if WINDOWS_LIKE // load configuration
MSG msg; loadConfig();
while (appThread.waitForEvent(-1.0) == CThread::kEvent) {
// check for a quit event // daemonize if requested
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (ARG->m_daemon) {
if (msg.message == WM_QUIT) { return ARCH->daemonize(DAEMON_NAME, &daemonMainLoop);
CThread::getCurrentThread().cancel();
}
}
}
#else
appThread.wait(-1.0);
#endif
return result;
} }
catch (XThread&) { else {
appThread.cancel(); return mainLoop();
appThread.wait(-1.0);
throw;
} }
} }
*/
static
int
run(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup)
{
// general initialization
CSocketMultiplexer multiplexer;
CEventQueue eventQueue;
ARG->m_synergyAddress = new CNetworkAddress;
ARG->m_config = new CConfig;
ARG->m_pname = ARCH->getBasename(argv[0]);
// install caller's output filter
if (outputter != NULL) {
CLOG->insert(outputter);
}
// save log messages
CBufferedLogOutputter logBuffer(1000);
CLOG->insert(&logBuffer, true);
// make the task bar receiver. the user can control this app
// through the task bar.
s_taskBarReceiver = createTaskBarReceiver(&logBuffer);
// run
int result = startup(argc, argv);
// done with task bar receiver
delete s_taskBarReceiver;
// done with log buffer
CLOG->remove(&logBuffer);
delete ARG->m_config;
delete ARG->m_synergyAddress;
return result;
}
// //
// command line parsing // command line parsing
@ -606,7 +653,7 @@ parse(int argc, const char* const* argv)
else if (isArg(i, argc, argv, "-a", "--address", 1)) { else if (isArg(i, argc, argv, "-a", "--address", 1)) {
// save listen address // save listen address
try { try {
ARG->m_synergyAddress = CNetworkAddress(argv[i + 1], *ARG->m_synergyAddress = CNetworkAddress(argv[i + 1],
kDefaultPort); kDefaultPort);
} }
catch (XSocketAddress& e) { catch (XSocketAddress& e) {
@ -723,7 +770,7 @@ loadConfig(const char* pathname)
if (!configStream) { if (!configStream) {
throw XConfigRead("cannot open file"); throw XConfigRead("cannot open file");
} }
configStream >> ARG->m_config; configStream >> *ARG->m_config;
LOG((CLOG_DEBUG "configuration read successfully")); LOG((CLOG_DEBUG "configuration read successfully"));
return true; return true;
} }
@ -828,197 +875,107 @@ byeThrow(int x)
static static
int int
daemonStartup(int argc, const char** argv) daemonNTMainLoop(int argc, const char** argv)
{ {
CSystemLogger sysLogger(DAEMON_NAME);
// catch errors that would normally exit
bye = &byeThrow;
// parse command line
parse(argc, argv); parse(argc, argv);
// cannot run as backend if running as a service
ARG->m_backend = false; ARG->m_backend = false;
// load configuration
loadConfig(); loadConfig();
return CArchMiscWindows::runDaemon(mainLoop);
// run as a service
return CArchMiscWindows::runDaemon(realMain);
} }
static static
int int
daemonStartup95(int, const char**) daemonNTStartup(int, char**)
{ {
CSystemLogger sysLogger(DAEMON_NAME); CSystemLogger sysLogger(DAEMON_NAME);
return runMainInThread(); bye = &byeThrow;
return ARCH->daemonize(DAEMON_NAME, &daemonNTMainLoop);
} }
static static
int void
run(int argc, char** argv) showError(HINSTANCE instance, const char* title, UINT id, const char* arg)
{ {
// windows NT family starts services using no command line options. CString fmt = CMSWindowsUtil::getString(instance, id);
// since i'm not sure how to tell the difference between that and CString msg = CStringUtil::format(fmt.c_str(), arg);
// a user providing no options we'll assume that if there are no MessageBox(NULL, msg.c_str(), title, MB_OK | MB_ICONWARNING);
// arguments and we're on NT then we're being invoked as a service.
// users on NT can use `--daemon' or `--no-daemon' to force us out
// of the service code path.
if (argc <= 1 && !CArchMiscWindows::isWindows95Family()) {
try {
return ARCH->daemonize(DAEMON_NAME, &daemonStartup);
}
catch (XArchDaemon& e) {
LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname));
}
return kExitFailed;
}
// parse command line
parse(argc, argv);
// load configuration
loadConfig();
// daemonize if requested
if (ARG->m_daemon) {
// start as a daemon
if (CArchMiscWindows::isWindows95Family()) {
try {
return ARCH->daemonize(DAEMON_NAME, &daemonStartup95);
}
catch (XArchDaemon& e) {
LOG((CLOG_CRIT "failed to start as a service: %s" BYE, e.what().c_str(), ARG->m_pname));
}
return kExitFailed;
}
else {
// cannot start a service from the command line so just
// run normally (except with log messages redirected).
CSystemLogger sysLogger(DAEMON_NAME);
return runMainInThread();
}
}
else {
// run
return runMainInThread();
}
} }
int WINAPI int WINAPI
WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
{ {
CArch arch(instance);
CLOG;
CArgs args;
// save instance
CMSWindowsScreen::init(instance);
// get program name
ARG->m_pname = ARCH->getBasename(__argv[0]);
// send PRINT and FATAL output to a message box
CLOG->insert(new CMessageBoxOutputter);
// save log messages
CBufferedLogOutputter logBuffer(1000);
CLOG->insert(&logBuffer, true);
// make the task bar receiver. the user can control this app
// through the task bar.
s_taskBarReceiver = new CMSWindowsServerTaskBarReceiver(instance,
&logBuffer);
int result;
try { try {
// run in foreground or as a daemon CArch arch(instance);
result = run(__argc, __argv); CMSWindowsScreen::init(instance);
CLOG;
// FIXME
// CThread::getCurrentThread().setPriority(-14);
CArgs args;
// windows NT family starts services using no command line options.
// since i'm not sure how to tell the difference between that and
// a user providing no options we'll assume that if there are no
// arguments and we're on NT then we're being invoked as a service.
// users on NT can use `--daemon' or `--no-daemon' to force us out
// of the service code path.
StartupFunc startup = &standardStartup;
if (__argc <= 1 && !CArchMiscWindows::isWindows95Family()) {
startup = &daemonNTStartup;
}
// send PRINT and FATAL output to a message box
int result = run(__argc, __argv, new CMessageBoxOutputter, startup);
// let user examine any messages if we're running as a backend
// by putting up a dialog box before exiting.
if (args.m_backend && s_hasImportantLogMessages) {
showError(instance, args.m_pname, IDS_FAILED, "");
}
delete CLOG;
return result;
}
catch (XBase& e) {
showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, e.what());
throw;
}
catch (XArch& e) {
showError(instance, __argv[0], IDS_INIT_FAILED, e.what().c_str());
return kExitFailed;
} }
catch (...) { catch (...) {
// note that we don't rethrow thread cancellation. we'll showError(instance, __argv[0], IDS_UNCAUGHT_EXCEPTION, "<unknown>");
// be exiting soon so it doesn't matter. what we'd like throw;
// is for everything after this try/catch to be in a
// finally block.
result = kExitFailed;
} }
// done with task bar receiver
delete s_taskBarReceiver;
// done with log buffer
CLOG->remove(&logBuffer);
// let user examine any messages if we're running as a backend
// by putting up a dialog box before exiting.
if (ARG->m_backend && s_hasImportantLogMessages) {
char msg[1024];
msg[0] = '\0';
LoadString(instance, IDS_FAILED, msg, sizeof(msg) / sizeof(msg[0]));
MessageBox(NULL, msg, ARG->m_pname, MB_OK | MB_ICONWARNING);
}
delete CLOG;
return result;
} }
#elif UNIX_LIKE #elif UNIX_LIKE
static
int
daemonStartup(int, const char**)
{
CSystemLogger sysLogger(DAEMON_NAME);
return realMain();
}
int int
main(int argc, char** argv) main(int argc, char** argv)
{ {
CArch arch;
CLOG;
// go really fast
CThread::getCurrentThread().setPriority(-14);
CSocketMultiplexer multiplexer;
CEventQueue eventQueue;
// get program name
CArgs args; CArgs args;
ARG->m_pname = ARCH->getBasename(argv[0]); try {
int result;
// make the task bar receiver. the user can control this app CArch arch;
// through the task bar. CLOG;
s_taskBarReceiver = new CXWindowsServerTaskBarReceiver; CArgs args;
result = run(argc, argv, NULL, &standardStartup);
// parse command line delete CLOG;
parse(argc, argv); return result;
// load configuration
loadConfig();
// daemonize if requested
int result;
if (ARG->m_daemon) {
try {
result = ARCH->daemonize(DAEMON_NAME, &daemonStartup);
}
catch (XArchDaemon&) {
LOG((CLOG_CRIT "failed to daemonize"));
result = kExitFailed;
}
} }
else { catch (XBase& e) {
result = realMain(); 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;
} }
// done with task bar receiver
delete s_taskBarReceiver;
return result;
} }
#else #else

View File

@ -102,6 +102,8 @@ END
STRINGTABLE DISCARDABLE STRINGTABLE DISCARDABLE
BEGIN BEGIN
IDS_FAILED "Synergy is about to quit with errors or warnings. Please check the log then click OK." IDS_FAILED "Synergy is about to quit with errors or warnings. Please check the log then click OK."
IDS_INIT_FAILED "Synergy failed to initialize: %{1}"
IDS_UNCAUGHT_EXCEPTION "Uncaught exception: %{1}"
END END
#endif // English (U.S.) resources #endif // English (U.S.) resources

View File

@ -94,6 +94,9 @@ dnl use AC_REPLACE_FUNCS() for stuff in string.h
dnl checks for system services dnl checks for system services
dnl enable maximum compiler warnings and warnings are errors.
ACX_CXX_WARNINGS
ACX_CXX_WARNINGS_ARE_ERRORS
dnl adjust variables for X11 and pthreads dnl adjust variables for X11 and pthreads
CXXFLAGS="$CXXFLAGS $SYNERGY_CXXFLAGS $X_CFLAGS $PTHREAD_CFLAGS -D_BSD_SOURCE -D_XOPEN_SOURCE=500" CXXFLAGS="$CXXFLAGS $SYNERGY_CXXFLAGS $X_CFLAGS $PTHREAD_CFLAGS -D_BSD_SOURCE -D_XOPEN_SOURCE=500"

View File

@ -351,18 +351,6 @@ CArch::wait(CArchThread thread, double timeout)
return m_mt->wait(thread, timeout); return m_mt->wait(thread, timeout);
} }
IArchMultithread::EWaitResult
CArch::waitForEvent(CArchThread thread, double timeout)
{
return m_mt->waitForEvent(thread, timeout);
}
void
CArch::unblockThread(CArchThread thread)
{
m_mt->unblockThread(thread);
}
bool bool
CArch::isSameThread(CArchThread thread1, CArchThread thread2) CArch::isSameThread(CArchThread thread1, CArchThread thread2)
{ {
@ -390,13 +378,13 @@ CArch::getIDOfThread(CArchThread thread)
void void
CArch::setInterruptHandler(InterruptFunc func, void* userData) CArch::setInterruptHandler(InterruptFunc func, void* userData)
{ {
return m_mt->setInterruptHandler(func, userData); m_mt->setInterruptHandler(func, userData);
} }
void void
CArch::interrupt() CArch::interrupt()
{ {
return m_mt->interrupt(); m_mt->interrupt();
} }
CArchSocket CArchSocket
@ -459,6 +447,12 @@ CArch::pollSocket(CPollEntry pe[], int num, double timeout)
return m_net->pollSocket(pe, num, timeout); return m_net->pollSocket(pe, num, timeout);
} }
void
CArch::unblockPollSocket(CArchThread thread)
{
m_net->unblockPollSocket(thread);
}
size_t size_t
CArch::readSocket(CArchSocket s, void* buf, size_t len) CArch::readSocket(CArchSocket s, void* buf, size_t len)
{ {

View File

@ -116,8 +116,6 @@ public:
virtual void setPriorityOfThread(CArchThread, int n); virtual void setPriorityOfThread(CArchThread, int n);
virtual void testCancelThread(); virtual void testCancelThread();
virtual bool wait(CArchThread, double timeout); virtual bool wait(CArchThread, double timeout);
virtual EWaitResult waitForEvent(CArchThread, double timeout);
virtual void unblockThread(CArchThread thread);
virtual bool isSameThread(CArchThread, CArchThread); virtual bool isSameThread(CArchThread, CArchThread);
virtual bool isExitedThread(CArchThread); virtual bool isExitedThread(CArchThread);
virtual void* getResultOfThread(CArchThread); virtual void* getResultOfThread(CArchThread);
@ -136,6 +134,7 @@ public:
virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr);
virtual void connectSocket(CArchSocket s, CArchNetAddress name); virtual void connectSocket(CArchSocket s, CArchNetAddress name);
virtual int pollSocket(CPollEntry[], int num, double timeout); virtual int pollSocket(CPollEntry[], int num, double timeout);
virtual void unblockPollSocket(CArchThread thread);
virtual size_t readSocket(CArchSocket s, void* buf, size_t len); virtual size_t readSocket(CArchSocket s, void* buf, size_t len);
virtual size_t writeSocket(CArchSocket s, virtual size_t writeSocket(CArchSocket s,
const void* buf, size_t len); const void* buf, size_t len);

View File

@ -110,7 +110,7 @@ CArchConsoleWindows::getNewlineForConsole()
BOOL WINAPI BOOL WINAPI
CArchConsoleWindows::signalHandler(DWORD) CArchConsoleWindows::signalHandler(DWORD)
{ {
// terminate thread and skip remaining handlers // terminate app and skip remaining handlers
ARCH->cancelThread(s_thread); ARCH->interrupt();
return TRUE; return TRUE;
} }

View File

@ -24,10 +24,9 @@
CArchDaemonWindows* CArchDaemonWindows::s_daemon = NULL; CArchDaemonWindows* CArchDaemonWindows::s_daemon = NULL;
CArchDaemonWindows::CArchDaemonWindows() : CArchDaemonWindows::CArchDaemonWindows()
m_daemonThread(NULL)
{ {
// do nothing m_quitMessage = RegisterWindowMessage("SynergyDaemonExit");
} }
CArchDaemonWindows::~CArchDaemonWindows() CArchDaemonWindows::~CArchDaemonWindows()
@ -55,6 +54,17 @@ CArchDaemonWindows::daemonRunning(bool running)
} }
} }
UINT
CArchDaemonWindows::getDaemonQuitMessage()
{
if (s_daemon != NULL) {
return s_daemon->doGetDaemonQuitMessage();
}
else {
return 0;
}
}
void void
CArchDaemonWindows::daemonFailed(int result) CArchDaemonWindows::daemonFailed(int result)
{ {
@ -437,122 +447,89 @@ CArchDaemonWindows::openUserStartupKey()
return CArchMiscWindows::openKey(HKEY_CURRENT_USER, s_keyNames); return CArchMiscWindows::openKey(HKEY_CURRENT_USER, s_keyNames);
} }
bool
CArchDaemonWindows::isRunState(DWORD state)
{
switch (state) {
case SERVICE_START_PENDING:
case SERVICE_CONTINUE_PENDING:
case SERVICE_RUNNING:
return true;
default:
return false;
}
}
int int
CArchDaemonWindows::doRunDaemon(RunFunc run) CArchDaemonWindows::doRunDaemon(RunFunc run)
{ {
// should only be called from DaemonFunc // should only be called from DaemonFunc
assert(m_serviceMutex != NULL); assert(m_serviceMutex != NULL);
assert(run != NULL); assert(run != NULL);
// create message queue for this thread
MSG dummy;
PeekMessage(&dummy, NULL, 0, 0, PM_NOREMOVE);
int result = 0;
ARCH->lockMutex(m_serviceMutex); ARCH->lockMutex(m_serviceMutex);
try { m_daemonThreadID = GetCurrentThreadId();
int result; while (m_serviceState != SERVICE_STOPPED) {
m_serviceHandlerWaiting = false; // wait until we're told to start
m_serviceRunning = false; while (!isRunState(m_serviceState) &&
for (;;) { m_serviceState != SERVICE_STOP_PENDING) {
// mark server as running
setStatus(SERVICE_RUNNING);
// run callback in another thread
m_serviceRunning = true;
m_daemonThread = ARCH->newThread(
&CArchDaemonWindows::runDaemonThreadEntry, run);
ARCH->wait(m_daemonThread, -1.0);
result = reinterpret_cast<int>(
ARCH->getResultOfThread(m_daemonThread));
m_serviceRunning = false;
// notify handler that the server stopped. if handler
// isn't waiting then we stopped unexpectedly and we
// quit.
if (m_serviceHandlerWaiting) {
m_serviceHandlerWaiting = false;
ARCH->broadcastCondVar(m_serviceCondVar);
}
else {
break;
}
// wait until we're told what to do next
while (m_serviceState != SERVICE_RUNNING &&
m_serviceState != SERVICE_STOPPED) {
ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
}
// exit loop if we've been told to stop
if (m_serviceState == SERVICE_STOPPED) {
break;
}
// done with callback thread
ARCH->closeThread(m_daemonThread);
m_daemonThread = NULL;
}
// prevent daemonHandler from changing state
m_serviceState = SERVICE_STOPPED;
// tell service control that the service is stopped.
// FIXME -- hopefully this will ensure that our handler won't
// be called again but i can't find documentation that
// verifies that. if it does it'll crash on the mutex that
// we're about to destroy.
setStatus(m_serviceState);
// clean up
if (m_daemonThread != NULL) {
ARCH->closeThread(m_daemonThread);
m_daemonThread = NULL;
}
ARCH->unlockMutex(m_serviceMutex);
return result;
}
catch (...) {
// FIXME -- report error
// prevent serviceHandler from changing state
m_serviceState = SERVICE_STOPPED;
// set status
setStatusError(0);
// wake up serviceHandler if it's waiting then wait for it
if (m_serviceHandlerWaiting) {
m_serviceHandlerWaiting = false;
ARCH->broadcastCondVar(m_serviceCondVar);
ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
// serviceHandler has exited by now
} }
ARCH->unlockMutex(m_serviceMutex); // run unless told to stop
throw; if (m_serviceState != SERVICE_STOP_PENDING) {
ARCH->unlockMutex(m_serviceMutex);
try {
result = run();
}
catch (...) {
ARCH->lockMutex(m_serviceMutex);
setStatusError(0);
m_serviceState = SERVICE_STOPPED;
setStatus(m_serviceState);
ARCH->broadcastCondVar(m_serviceCondVar);
ARCH->unlockMutex(m_serviceMutex);
throw;
}
ARCH->lockMutex(m_serviceMutex);
}
// notify of new state
if (m_serviceState == SERVICE_PAUSE_PENDING) {
m_serviceState = SERVICE_PAUSED;
}
else {
m_serviceState = SERVICE_STOPPED;
}
setStatus(m_serviceState);
ARCH->broadcastCondVar(m_serviceCondVar);
} }
ARCH->unlockMutex(m_serviceMutex);
return result;
} }
void void
CArchDaemonWindows::doDaemonRunning(bool running) CArchDaemonWindows::doDaemonRunning(bool running)
{ {
ARCH->lockMutex(m_serviceMutex);
if (running) { if (running) {
ARCH->unlockMutex(m_serviceMutex); m_serviceState = SERVICE_RUNNING;
} setStatus(m_serviceState);
else { ARCH->broadcastCondVar(m_serviceCondVar);
ARCH->lockMutex(m_serviceMutex);
} }
ARCH->unlockMutex(m_serviceMutex);
} }
void* UINT
CArchDaemonWindows::runDaemonThread(RunFunc run) CArchDaemonWindows::doGetDaemonQuitMessage()
{ {
return reinterpret_cast<void*>(run()); return m_quitMessage;
}
void*
CArchDaemonWindows::runDaemonThreadEntry(void* vrun)
{
assert(s_daemon != NULL);
return s_daemon->runDaemonThread(reinterpret_cast<RunFunc>(vrun));
} }
void void
@ -583,6 +560,8 @@ CArchDaemonWindows::setStatus(DWORD state, DWORD step, DWORD waitHint)
void void
CArchDaemonWindows::setStatusError(DWORD error) CArchDaemonWindows::setStatusError(DWORD error)
{ {
assert(s_daemon != NULL);
SERVICE_STATUS status; SERVICE_STATUS status;
status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | status.dwServiceType = SERVICE_WIN32_OWN_PROCESS |
SERVICE_INTERACTIVE_PROCESS; SERVICE_INTERACTIVE_PROCESS;
@ -605,11 +584,10 @@ CArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn)
const char** argv = const_cast<const char**>(argvIn); const char** argv = const_cast<const char**>(argvIn);
// create synchronization objects // create synchronization objects
m_serviceMutex = ARCH->newMutex(); m_serviceMutex = ARCH->newMutex();
m_serviceCondVar = ARCH->newCondVar(); m_serviceCondVar = ARCH->newCondVar();
m_serviceState = SERVICE_RUNNING;
// register our service handler functiom // register our service handler function
m_statusHandle = RegisterServiceCtrlHandler(argv[0], m_statusHandle = RegisterServiceCtrlHandler(argv[0],
&CArchDaemonWindows::serviceHandlerEntry); &CArchDaemonWindows::serviceHandlerEntry);
if (m_statusHandle == NULL) { if (m_statusHandle == NULL) {
@ -621,7 +599,8 @@ CArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn)
} }
// tell service control manager that we're starting // tell service control manager that we're starting
setStatus(SERVICE_START_PENDING, 0, 10000); m_serviceState = SERVICE_START_PENDING;
setStatus(m_serviceState, 0, 10000);
// if no arguments supplied then try getting them from the registry. // if no arguments supplied then try getting them from the registry.
// the first argument doesn't count because it's the service name. // the first argument doesn't count because it's the service name.
@ -726,58 +705,40 @@ CArchDaemonWindows::serviceHandler(DWORD ctrl)
ARCH->lockMutex(m_serviceMutex); ARCH->lockMutex(m_serviceMutex);
// ignore request if service is already stopped // ignore request if service is already stopped
if (m_serviceState == SERVICE_STOPPED) { if (s_daemon == NULL || m_serviceState == SERVICE_STOPPED) {
setStatus(m_serviceState); if (s_daemon != NULL) {
setStatus(m_serviceState);
}
ARCH->unlockMutex(m_serviceMutex); ARCH->unlockMutex(m_serviceMutex);
return; return;
} }
switch (ctrl) { switch (ctrl) {
case SERVICE_CONTROL_PAUSE: case SERVICE_CONTROL_PAUSE:
// update state
m_serviceState = SERVICE_PAUSE_PENDING; m_serviceState = SERVICE_PAUSE_PENDING;
setStatus(m_serviceState, 0, 5000); setStatus(m_serviceState, 0, 5000);
PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0);
// stop run callback if running and wait for it to finish while (isRunState(m_serviceState)) {
if (m_serviceRunning) {
m_serviceHandlerWaiting = true;
ARCH->cancelThread(m_daemonThread);
ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
} }
// update state if service hasn't stopped while we were waiting
if (m_serviceState != SERVICE_STOPPED) {
m_serviceState = SERVICE_PAUSED;
}
ARCH->broadcastCondVar(m_serviceCondVar);
break; break;
case SERVICE_CONTROL_CONTINUE: case SERVICE_CONTROL_CONTINUE:
// required status update // FIXME -- maybe should flush quit messages from queue
setStatus(m_serviceState); m_serviceState = SERVICE_CONTINUE_PENDING;
setStatus(m_serviceState, 0, 5000);
// update state but let main loop send RUNNING notification
m_serviceState = SERVICE_RUNNING;
ARCH->broadcastCondVar(m_serviceCondVar); ARCH->broadcastCondVar(m_serviceCondVar);
ARCH->unlockMutex(m_serviceMutex); break;
return;
case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN: case SERVICE_CONTROL_SHUTDOWN:
// update state
m_serviceState = SERVICE_STOP_PENDING; m_serviceState = SERVICE_STOP_PENDING;
setStatus(m_serviceState, 0, 5000); setStatus(m_serviceState, 0, 5000);
PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0);
// stop run callback if running and wait for it to finish ARCH->broadcastCondVar(m_serviceCondVar);
if (m_serviceRunning) { while (isRunState(m_serviceState)) {
m_serviceHandlerWaiting = true;
ARCH->cancelThread(m_daemonThread);
ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0); ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
} }
// update state
m_serviceState = SERVICE_STOPPED;
ARCH->broadcastCondVar(m_serviceCondVar);
break; break;
default: default:
@ -785,12 +746,10 @@ CArchDaemonWindows::serviceHandler(DWORD ctrl)
// fall through // fall through
case SERVICE_CONTROL_INTERROGATE: case SERVICE_CONTROL_INTERROGATE:
setStatus(m_serviceState);
break; break;
} }
// send update
setStatus(m_serviceState);
ARCH->unlockMutex(m_serviceMutex); ARCH->unlockMutex(m_serviceMutex);
} }

View File

@ -42,7 +42,7 @@ public:
(i.e. after initialization) and \c daemonRunning(false) when it leaves (i.e. after initialization) and \c daemonRunning(false) when it leaves
the main loop. The \c runFunc is called in a new thread and when the the main loop. The \c runFunc is called in a new thread and when the
daemon must exit the main loop due to some external control the daemon must exit the main loop due to some external control the
thread is cancelled on behalf of the client. This function returns getDaemonQuitMessage() is posted to the thread. This function returns
what \c runFunc returns. \c runFunc should call \c daemonFailed() if what \c runFunc returns. \c runFunc should call \c daemonFailed() if
the daemon fails. the daemon fails.
*/ */
@ -63,6 +63,14 @@ public:
*/ */
static void daemonFailed(int result); static void daemonFailed(int result);
//! Get daemon quit message
/*!
The windows NT daemon tells daemon thread to exit by posting this
message to it. The thread must, of course, have a message queue
for this to work.
*/
static UINT getDaemonQuitMessage();
// IArchDaemon overrides // IArchDaemon overrides
virtual void installDaemon(const char* name, virtual void installDaemon(const char* name,
const char* description, const char* description,
@ -81,13 +89,13 @@ private:
int doRunDaemon(RunFunc runFunc); int doRunDaemon(RunFunc runFunc);
void doDaemonRunning(bool running); void doDaemonRunning(bool running);
UINT doGetDaemonQuitMessage();
static void setStatus(DWORD state); static void setStatus(DWORD state);
static void setStatus(DWORD state, DWORD step, DWORD waitHint); static void setStatus(DWORD state, DWORD step, DWORD waitHint);
static void setStatusError(DWORD error); static void setStatusError(DWORD error);
void* runDaemonThread(RunFunc); static bool isRunState(DWORD state);
static void* runDaemonThreadEntry(void*);
void serviceMain(DWORD, LPTSTR*); void serviceMain(DWORD, LPTSTR*);
static void WINAPI serviceMainEntry(DWORD, LPTSTR*); static void WINAPI serviceMainEntry(DWORD, LPTSTR*);
@ -113,11 +121,13 @@ private:
bool m_serviceHandlerWaiting; bool m_serviceHandlerWaiting;
bool m_serviceRunning; bool m_serviceRunning;
CArchThread m_daemonThread; DWORD m_daemonThreadID;
DaemonFunc m_daemonFunc; DaemonFunc m_daemonFunc;
int m_daemonResult; int m_daemonResult;
SERVICE_STATUS_HANDLE m_statusHandle; SERVICE_STATUS_HANDLE m_statusHandle;
UINT m_quitMessage;
}; };
#endif #endif

View File

@ -19,9 +19,12 @@
// CArchMiscWindows // CArchMiscWindows
// //
CArchMiscWindows::CDialogs* CArchMiscWindows::s_dialogs = NULL;
void void
CArchMiscWindows::init() CArchMiscWindows::init()
{ {
s_dialogs = new CDialogs;
isWindows95Family(); isWindows95Family();
} }
@ -64,6 +67,12 @@ CArchMiscWindows::daemonFailed(int result)
CArchDaemonWindows::daemonFailed(result); CArchDaemonWindows::daemonFailed(result);
} }
UINT
CArchMiscWindows::getDaemonQuitMessage()
{
return CArchDaemonWindows::getDaemonQuitMessage();
}
HKEY HKEY
CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName) CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName)
{ {
@ -196,3 +205,27 @@ CArchMiscWindows::readValueInt(HKEY key, const TCHAR* name)
} }
return value; return value;
} }
void
CArchMiscWindows::addDialog(HWND hwnd)
{
s_dialogs->insert(hwnd);
}
void
CArchMiscWindows::removeDialog(HWND hwnd)
{
s_dialogs->erase(hwnd);
}
bool
CArchMiscWindows::processDialog(MSG* msg)
{
for (CDialogs::const_iterator index = s_dialogs->begin();
index != s_dialogs->end(); ++index) {
if (IsDialogMessage(*index, msg)) {
return true;
}
}
return false;
}

View File

@ -19,6 +19,7 @@
#include "common.h" #include "common.h"
#include "stdstring.h" #include "stdstring.h"
#include "stdset.h"
#include <windows.h> #include <windows.h>
//! Miscellaneous win32 functions. //! Miscellaneous win32 functions.
@ -53,6 +54,12 @@ public:
*/ */
static void daemonFailed(int result); static void daemonFailed(int result);
//! Get daemon quit message
/*!
Delegates to CArchDaemonWindows.
*/
static UINT getDaemonQuitMessage();
//! Open and return a registry key, closing the parent key //! Open and return a registry key, closing the parent key
static HKEY openKey(HKEY parent, const TCHAR* child); static HKEY openKey(HKEY parent, const TCHAR* child);
@ -83,6 +90,24 @@ public:
//! Read a DWORD value from the registry //! Read a DWORD value from the registry
static DWORD readValueInt(HKEY, const TCHAR* name); static DWORD readValueInt(HKEY, const TCHAR* name);
//! Add a dialog
static void addDialog(HWND);
//! Remove a dialog
static void removeDialog(HWND);
//! Process dialog message
/*!
Checks if the message is destined for a dialog. If so the message
is passed to the dialog and returns true, otherwise returns false.
*/
static bool processDialog(MSG*);
private:
typedef std::set<HWND> CDialogs;
static CDialogs* s_dialogs;
}; };
#endif #endif

View File

@ -134,6 +134,18 @@ CArchMultithreadPosix::~CArchMultithreadPosix()
s_instance = NULL; s_instance = NULL;
} }
void
CArchMultithreadPosix::unblockThread(CArchThread thread)
{
pthread_kill(thread->m_thread, SIGWAKEUP);
}
CArchMultithreadPosix*
CArchMultithreadPosix::getInstance()
{
return s_instance;
}
CArchCond CArchCond
CArchMultithreadPosix::newCondVar() CArchMultithreadPosix::newCondVar()
{ {
@ -517,19 +529,6 @@ CArchMultithreadPosix::wait(CArchThread target, double timeout)
} }
} }
IArchMultithread::EWaitResult
CArchMultithreadPosix::waitForEvent(CArchThread, double /*timeout*/)
{
// not implemented
return kTimeout;
}
void
CArchMultithreadPosix::unblockThread(CArchThread thread)
{
pthread_kill(thread->m_thread, SIGWAKEUP);
}
bool bool
CArchMultithreadPosix::isSameThread(CArchThread thread1, CArchThread thread2) CArchMultithreadPosix::isSameThread(CArchThread thread1, CArchThread thread2)
{ {
@ -575,7 +574,7 @@ CArchMultithreadPosix::interrupt()
lockMutex(m_threadMutex); lockMutex(m_threadMutex);
if (m_signalFunc != NULL) { if (m_signalFunc != NULL) {
m_signalFunc(m_signalUserData); m_signalFunc(m_signalUserData);
pthread_kill(m_mainThread->m_thread, SIGWAKEUP); unblockThread(m_mainThread);
} }
else { else {
ARCH->cancelThread(m_mainThread); ARCH->cancelThread(m_mainThread);

View File

@ -37,6 +37,19 @@ public:
CArchMultithreadPosix(); CArchMultithreadPosix();
virtual ~CArchMultithreadPosix(); virtual ~CArchMultithreadPosix();
//! @name manipulators
//@{
void unblockThread(CArchThread thread);
//@}
//! @name accessors
//@{
static CArchMultithreadPosix* getInstance();
//@}
// IArchMultithread overrides // IArchMultithread overrides
virtual CArchCond newCondVar(); virtual CArchCond newCondVar();
virtual void closeCondVar(CArchCond); virtual void closeCondVar(CArchCond);
@ -55,8 +68,6 @@ public:
virtual void setPriorityOfThread(CArchThread, int n); virtual void setPriorityOfThread(CArchThread, int n);
virtual void testCancelThread(); virtual void testCancelThread();
virtual bool wait(CArchThread, double timeout); virtual bool wait(CArchThread, double timeout);
virtual EWaitResult waitForEvent(CArchThread, double timeout);
virtual void unblockThread(CArchThread thread);
virtual bool isSameThread(CArchThread, CArchThread); virtual bool isSameThread(CArchThread, CArchThread);
virtual bool isExitedThread(CArchThread); virtual bool isExitedThread(CArchThread);
virtual void* getResultOfThread(CArchThread); virtual void* getResultOfThread(CArchThread);

View File

@ -50,6 +50,7 @@ public:
bool m_cancelling; bool m_cancelling;
HANDLE m_exit; HANDLE m_exit;
void* m_result; void* m_result;
void* m_networkData;
}; };
CArchThreadImpl::CArchThreadImpl() : CArchThreadImpl::CArchThreadImpl() :
@ -59,7 +60,8 @@ CArchThreadImpl::CArchThreadImpl() :
m_func(NULL), m_func(NULL),
m_userData(NULL), m_userData(NULL),
m_cancelling(false), m_cancelling(false),
m_result(NULL) m_result(NULL),
m_networkData(NULL)
{ {
m_exit = CreateEvent(NULL, TRUE, FALSE, NULL); m_exit = CreateEvent(NULL, TRUE, FALSE, NULL);
m_cancel = CreateEvent(NULL, TRUE, FALSE, NULL); m_cancel = CreateEvent(NULL, TRUE, FALSE, NULL);
@ -78,7 +80,9 @@ CArchThreadImpl::~CArchThreadImpl()
CArchMultithreadWindows* CArchMultithreadWindows::s_instance = NULL; CArchMultithreadWindows* CArchMultithreadWindows::s_instance = NULL;
CArchMultithreadWindows::CArchMultithreadWindows() CArchMultithreadWindows::CArchMultithreadWindows() :
m_signalFunc(NULL),
m_signalUserData(NULL)
{ {
assert(s_instance == NULL); assert(s_instance == NULL);
s_instance = this; s_instance = this;
@ -88,10 +92,10 @@ CArchMultithreadWindows::CArchMultithreadWindows()
// create thread for calling (main) thread and add it to our // create thread for calling (main) thread and add it to our
// list. no need to lock the mutex since we're the only thread. // list. no need to lock the mutex since we're the only thread.
CArchThreadImpl* mainThread = new CArchThreadImpl; m_mainThread = new CArchThreadImpl;
mainThread->m_thread = NULL; m_mainThread->m_thread = NULL;
mainThread->m_id = GetCurrentThreadId(); m_mainThread->m_id = GetCurrentThreadId();
insert(mainThread); insert(m_mainThread);
} }
CArchMultithreadWindows::~CArchMultithreadWindows() CArchMultithreadWindows::~CArchMultithreadWindows()
@ -108,6 +112,24 @@ CArchMultithreadWindows::~CArchMultithreadWindows()
delete m_threadMutex; delete m_threadMutex;
} }
void
CArchMultithreadWindows::setNetworkDataForCurrentThread(void* data)
{
lockMutex(m_threadMutex);
CArchThreadImpl* thread = findNoRef(GetCurrentThreadId());
thread->m_networkData = data;
unlockMutex(m_threadMutex);
}
void*
CArchMultithreadWindows::getNetworkDataForThread(CArchThread thread)
{
lockMutex(m_threadMutex);
void* data = thread->m_networkData;
unlockMutex(m_threadMutex);
return data;
}
HANDLE HANDLE
CArchMultithreadWindows::getCancelEventForCurrentThread() CArchMultithreadWindows::getCancelEventForCurrentThread()
{ {
@ -183,7 +205,7 @@ CArchMultithreadWindows::waitCondVar(CArchCond cond,
// make a list of the condition variable events and the cancel event // make a list of the condition variable events and the cancel event
// for the current thread. // for the current thread.
HANDLE handles[3]; HANDLE handles[4];
handles[0] = cond->m_events[CArchCondImpl::kSignal]; handles[0] = cond->m_events[CArchCondImpl::kSignal];
handles[1] = cond->m_events[CArchCondImpl::kBroadcast]; handles[1] = cond->m_events[CArchCondImpl::kBroadcast];
handles[2] = getCancelEventForCurrentThread(); handles[2] = getCancelEventForCurrentThread();
@ -446,8 +468,8 @@ CArchMultithreadWindows::wait(CArchThread target, double timeout)
t = (DWORD)(1000.0 * timeout); t = (DWORD)(1000.0 * timeout);
} }
// wait for this thread to be cancelled or for the target thread to // wait for this thread to be cancelled or woken up or for the
// terminate. // target thread to terminate.
HANDLE handles[2]; HANDLE handles[2];
handles[0] = target->m_exit; handles[0] = target->m_exit;
handles[1] = self->m_cancel; handles[1] = self->m_cancel;
@ -478,137 +500,6 @@ CArchMultithreadWindows::wait(CArchThread target, double timeout)
} }
} }
IArchMultithread::EWaitResult
CArchMultithreadWindows::waitForEvent(CArchThread target, double timeout)
{
// find current thread. ref the target so it can't go away while
// we're watching it.
lockMutex(m_threadMutex);
CArchThreadImpl* self = findNoRef(GetCurrentThreadId());
assert(self != NULL);
if (target != NULL) {
refThread(target);
}
unlockMutex(m_threadMutex);
// see if we've been cancelled before checking if any events
// are pending.
DWORD result = WaitForSingleObject(self->m_cancel, 0);
if (result == WAIT_OBJECT_0) {
if (target != NULL) {
closeThread(target);
}
testCancelThreadImpl(self);
}
// check if messages are available first. if we don't do this then
// MsgWaitForMultipleObjects() will block even if the queue isn't
// empty if the messages in the queue were there before the last
// call to GetMessage()/PeekMessage().
if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) {
return kEvent;
}
// convert timeout
DWORD t;
if (timeout < 0.0) {
t = INFINITE;
}
else {
t = (DWORD)(1000.0 * timeout);
}
// wait for this thread to be cancelled or for the target thread to
// terminate.
DWORD n = (target == NULL || target == self) ? 1 : 2;
HANDLE handles[2];
handles[0] = self->m_cancel;
handles[1] = (n == 2) ? target->m_exit : NULL;
result = MsgWaitForMultipleObjects(n, handles, FALSE, t, QS_ALLINPUT);
// cancel takes priority
if (result != WAIT_OBJECT_0 + 0 &&
WaitForSingleObject(handles[0], 0) == WAIT_OBJECT_0) {
result = WAIT_OBJECT_0 + 0;
}
// release target
if (target != NULL) {
closeThread(target);
}
// handle result
switch (result) {
case WAIT_OBJECT_0 + 0:
// this thread was cancelled. does not return.
testCancelThreadImpl(self);
case WAIT_OBJECT_0 + 1:
// target thread terminated
if (n == 2) {
return kExit;
}
// fall through
case WAIT_OBJECT_0 + 2:
// message is available
return kEvent;
default:
// timeout or error
return kTimeout;
}
}
/*
bool
CArchMultithreadWindows::waitForEvent(double timeout)
{
// check if messages are available first. if we don't do this then
// MsgWaitForMultipleObjects() will block even if the queue isn't
// empty if the messages in the queue were there before the last
// call to GetMessage()/PeekMessage().
if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) {
return true;
}
// find current thread
lockMutex(m_threadMutex);
CArchThreadImpl* self = findNoRef(GetCurrentThreadId());
unlockMutex(m_threadMutex);
assert(self != NULL);
// convert timeout
DWORD t;
if (timeout < 0.0) {
t = INFINITE;
}
else {
t = (DWORD)(1000.0 * timeout);
}
// wait for this thread to be cancelled or for a message
HANDLE handles[1];
handles[0] = self->m_cancel;
DWORD result = MsgWaitForMultipleObjects(1, handles, FALSE, t, QS_ALLINPUT);
// handle result
switch (result) {
case WAIT_OBJECT_0 + 1:
// message is available
return true;
case WAIT_OBJECT_0 + 0:
// this thread was cancelled. does not return.
testCancelThreadImpl(self);
default:
// timeout or error
return false;
}
}
*/
bool bool
CArchMultithreadWindows::isSameThread(CArchThread thread1, CArchThread thread2) CArchMultithreadWindows::isSameThread(CArchThread thread1, CArchThread thread2)
{ {
@ -637,6 +528,29 @@ CArchMultithreadWindows::getIDOfThread(CArchThread thread)
return static_cast<ThreadID>(thread->m_id); return static_cast<ThreadID>(thread->m_id);
} }
void
CArchMultithreadWindows::setInterruptHandler(InterruptFunc func, void* userData)
{
lockMutex(m_threadMutex);
m_signalFunc = func;
m_signalUserData = userData;
unlockMutex(m_threadMutex);
}
void
CArchMultithreadWindows::interrupt()
{
lockMutex(m_threadMutex);
if (m_signalFunc != NULL) {
m_signalFunc(m_signalUserData);
ARCH->unblockPollSocket(m_mainThread);
}
else {
ARCH->cancelThread(m_mainThread);
}
unlockMutex(m_threadMutex);
}
CArchThreadImpl* CArchThreadImpl*
CArchMultithreadWindows::find(DWORD id) CArchMultithreadWindows::find(DWORD id)
{ {

View File

@ -43,12 +43,20 @@ public:
CArchMultithreadWindows(); CArchMultithreadWindows();
virtual ~CArchMultithreadWindows(); virtual ~CArchMultithreadWindows();
//
// manipulators
//
void setNetworkDataForCurrentThread(void*);
// //
// accessors // accessors
// //
HANDLE getCancelEventForCurrentThread(); HANDLE getCancelEventForCurrentThread();
void* getNetworkDataForThread(CArchThread);
static CArchMultithreadWindows* getInstance(); static CArchMultithreadWindows* getInstance();
// IArchMultithread overrides // IArchMultithread overrides
@ -69,11 +77,12 @@ public:
virtual void setPriorityOfThread(CArchThread, int n); virtual void setPriorityOfThread(CArchThread, int n);
virtual void testCancelThread(); virtual void testCancelThread();
virtual bool wait(CArchThread, double timeout); virtual bool wait(CArchThread, double timeout);
virtual EWaitResult waitForEvent(CArchThread, double timeout);
virtual bool isSameThread(CArchThread, CArchThread); virtual bool isSameThread(CArchThread, CArchThread);
virtual bool isExitedThread(CArchThread); virtual bool isExitedThread(CArchThread);
virtual void* getResultOfThread(CArchThread); virtual void* getResultOfThread(CArchThread);
virtual ThreadID getIDOfThread(CArchThread); virtual ThreadID getIDOfThread(CArchThread);
virtual void setInterruptHandler(InterruptFunc, void*);
virtual void interrupt();
private: private:
CArchThreadImpl* find(DWORD id); CArchThreadImpl* find(DWORD id);
@ -96,6 +105,10 @@ private:
CArchMutex m_threadMutex; CArchMutex m_threadMutex;
CThreadList m_threadList; CThreadList m_threadList;
CArchThread m_mainThread;
InterruptFunc m_signalFunc;
void* m_signalUserData;
}; };
#endif #endif

View File

@ -424,6 +424,12 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout)
#endif #endif
void
CArchNetworkBSD::unblockPollSocket(CArchThread thread)
{
CArchMultithreadPosix::getInstance()->unblockThread(thread);
}
size_t size_t
CArchNetworkBSD::readSocket(CArchSocket s, void* buf, size_t len) CArchNetworkBSD::readSocket(CArchSocket s, void* buf, size_t len)
{ {
@ -435,8 +441,8 @@ CArchNetworkBSD::readSocket(CArchSocket s, void* buf, size_t len)
if (n == -1) { if (n == -1) {
if (errno == EINTR) { if (errno == EINTR) {
// interrupted system call // interrupted system call
ARCH->testCancelThread(); n = 0;
continue; break;
} }
else if (errno == EAGAIN) { else if (errno == EAGAIN) {
n = 0; n = 0;
@ -460,8 +466,8 @@ CArchNetworkBSD::writeSocket(CArchSocket s, const void* buf, size_t len)
if (n == -1) { if (n == -1) {
if (errno == EINTR) { if (errno == EINTR) {
// interrupted system call // interrupted system call
ARCH->testCancelThread(); n = 0;
continue; break;
} }
else if (errno == EAGAIN) { else if (errno == EAGAIN) {
// no buffer space // no buffer space

View File

@ -60,6 +60,7 @@ public:
virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr);
virtual void connectSocket(CArchSocket s, CArchNetAddress name); virtual void connectSocket(CArchSocket s, CArchNetAddress name);
virtual int pollSocket(CPollEntry[], int num, double timeout); virtual int pollSocket(CPollEntry[], int num, double timeout);
virtual void unblockPollSocket(CArchThread thread);
virtual size_t readSocket(CArchSocket s, void* buf, size_t len); virtual size_t readSocket(CArchSocket s, void* buf, size_t len);
virtual size_t writeSocket(CArchSocket s, virtual size_t writeSocket(CArchSocket s,
const void* buf, size_t len); const void* buf, size_t len);

View File

@ -15,7 +15,9 @@
#include "CArchNetworkWinsock.h" #include "CArchNetworkWinsock.h"
#include "CArch.h" #include "CArch.h"
#include "IArchMultithread.h"
#include "XArchWindows.h" #include "XArchWindows.h"
#include <malloc.h>
static const int s_family[] = { static const int s_family[] = {
PF_UNSPEC, PF_UNSPEC,
@ -49,6 +51,13 @@ static struct hostent FAR * (PASCAL FAR *gethostbyaddr_winsock)(const char FAR *
static struct hostent FAR * (PASCAL FAR *gethostbyname_winsock)(const char FAR * name); static struct hostent FAR * (PASCAL FAR *gethostbyname_winsock)(const char FAR * name);
static int (PASCAL FAR *WSACleanup_winsock)(void); static int (PASCAL FAR *WSACleanup_winsock)(void);
static int (PASCAL FAR *WSAFDIsSet_winsock)(SOCKET, fd_set FAR * fdset); static int (PASCAL FAR *WSAFDIsSet_winsock)(SOCKET, fd_set FAR * fdset);
static WSAEVENT (PASCAL FAR *WSACreateEvent_winsock)(void);
static BOOL (PASCAL FAR *WSACloseEvent_winsock)(WSAEVENT);
static BOOL (PASCAL FAR *WSASetEvent_winsock)(WSAEVENT);
static BOOL (PASCAL FAR *WSAResetEvent_winsock)(WSAEVENT);
static int (PASCAL FAR *WSAEventSelect_winsock)(SOCKET, WSAEVENT, long);
static DWORD (PASCAL FAR *WSAWaitForMultipleEvents_winsock)(DWORD, const WSAEVENT FAR*, BOOL, DWORD, BOOL);
static int (PASCAL FAR *WSAEnumNetworkEvents_winsock)(SOCKET, WSAEVENT, LPWSANETWORKEVENTS);
#undef FD_ISSET #undef FD_ISSET
#define FD_ISSET(fd, set) WSAFDIsSet_winsock((SOCKET)(fd), (fd_set FAR *)(set)) #define FD_ISSET(fd, set) WSAFDIsSet_winsock((SOCKET)(fd), (fd_set FAR *)(set))
@ -68,13 +77,23 @@ netGetProcAddress(HMODULE module, LPCSTR name)
return func; return func;
} }
CArchNetAddressImpl*
CArchNetAddressImpl::alloc(size_t size)
{
size_t totalSize = size + ADDR_HDR_SIZE;
CArchNetAddressImpl* addr = (CArchNetAddressImpl*)malloc(totalSize);
addr->m_len = size;
return addr;
}
// //
// CArchNetworkWinsock // CArchNetworkWinsock
// //
CArchNetworkWinsock::CArchNetworkWinsock() CArchNetworkWinsock::CArchNetworkWinsock()
{ {
static const char* s_library[] = { "ws2_32.dll", "wsock32.dll" }; static const char* s_library[] = { "ws2_32.dll" };
assert(WSACleanup_winsock == NULL); assert(WSACleanup_winsock == NULL);
assert(s_networkModule == NULL); assert(s_networkModule == NULL);
@ -110,14 +129,16 @@ CArchNetworkWinsock::~CArchNetworkWinsock()
void void
CArchNetworkWinsock::init(HMODULE module) CArchNetworkWinsock::init(HMODULE module)
{ {
assert(module != NULL); if (module == NULL) {
throw XArchNetworkSupport("");
}
// get startup function address // get startup function address
int (PASCAL FAR *startup)(WORD, LPWSADATA); int (PASCAL FAR *startup)(WORD, LPWSADATA);
setfunc(startup, WSAStartup, int(PASCAL FAR*)(WORD, LPWSADATA)); setfunc(startup, WSAStartup, int(PASCAL FAR*)(WORD, LPWSADATA));
// startup network library // startup network library
WORD version = MAKEWORD(1 /*major*/, 1 /*minor*/); WORD version = MAKEWORD(2 /*major*/, 0 /*minor*/);
WSADATA data; WSADATA data;
int err = startup(version, &data); int err = startup(version, &data);
if (data.wVersion != version) { if (data.wVersion != version) {
@ -152,6 +173,13 @@ CArchNetworkWinsock::init(HMODULE module)
setfunc(gethostbyname_winsock, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name)); setfunc(gethostbyname_winsock, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name));
setfunc(WSACleanup_winsock, WSACleanup, int (PASCAL FAR *)(void)); setfunc(WSACleanup_winsock, WSACleanup, int (PASCAL FAR *)(void));
setfunc(WSAFDIsSet_winsock, __WSAFDIsSet, int (PASCAL FAR *)(SOCKET, fd_set FAR *)); setfunc(WSAFDIsSet_winsock, __WSAFDIsSet, int (PASCAL FAR *)(SOCKET, fd_set FAR *));
setfunc(WSACreateEvent_winsock, WSACreateEvent, WSAEVENT (PASCAL FAR *)(void));
setfunc(WSACloseEvent_winsock, WSACloseEvent, BOOL (PASCAL FAR *)(WSAEVENT));
setfunc(WSASetEvent_winsock, WSASetEvent, BOOL (PASCAL FAR *)(WSAEVENT));
setfunc(WSAResetEvent_winsock, WSAResetEvent, BOOL (PASCAL FAR *)(WSAEVENT));
setfunc(WSAEventSelect_winsock, WSAEventSelect, int (PASCAL FAR *)(SOCKET, WSAEVENT, long));
setfunc(WSAWaitForMultipleEvents_winsock, WSAWaitForMultipleEvents, DWORD (PASCAL FAR *)(DWORD, const WSAEVENT FAR*, BOOL, DWORD, BOOL));
setfunc(WSAEnumNetworkEvents_winsock, WSAEnumNetworkEvents, int (PASCAL FAR *)(SOCKET, WSAEVENT, LPWSANETWORKEVENTS));
s_networkModule = module; s_networkModule = module;
} }
@ -171,6 +199,8 @@ CArchNetworkWinsock::newSocket(EAddressFamily family, ESocketType type)
socket->m_socket = fd; socket->m_socket = fd;
socket->m_connected = false; socket->m_connected = false;
socket->m_refCount = 1; socket->m_refCount = 1;
socket->m_event = WSACreateEvent_winsock();
socket->m_pollWrite = false;
return socket; return socket;
} }
@ -202,7 +232,7 @@ CArchNetworkWinsock::closeSocket(CArchSocket s)
if (close_winsock(s->m_socket) == SOCKET_ERROR) { if (close_winsock(s->m_socket) == SOCKET_ERROR) {
// close failed // close failed
int err = getsockerror_winsock(); int err = getsockerror_winsock();
if (err == EINTR) { if (err == WSAEINTR) {
// interrupted system call // interrupted system call
ARCH->testCancelThread(); ARCH->testCancelThread();
continue; continue;
@ -215,6 +245,7 @@ CArchNetworkWinsock::closeSocket(CArchSocket s)
throwError(err); throwError(err);
} }
} while (false); } while (false);
WSACloseEvent_winsock(s->m_event);
delete s; delete s;
} }
} }
@ -270,35 +301,24 @@ CArchNetworkWinsock::acceptSocket(CArchSocket s, CArchNetAddress* addr)
{ {
assert(s != NULL); assert(s != NULL);
// if user passed NULL in addr then use scratch space // create new socket and temporary address
CArchNetAddress dummy;
if (addr == NULL) {
addr = &dummy;
}
// create new socket and address
CArchSocketImpl* socket = new CArchSocketImpl; CArchSocketImpl* socket = new CArchSocketImpl;
*addr = new CArchNetAddressImpl; CArchNetAddress tmp = CArchNetAddressImpl::alloc(sizeof(struct sockaddr));
// accept on socket // accept on socket
SOCKET fd; SOCKET fd;
do { do {
fd = accept_winsock(s->m_socket, &(*addr)->m_addr, &(*addr)->m_len); fd = accept_winsock(s->m_socket, &tmp->m_addr, &tmp->m_len);
if (fd == INVALID_SOCKET) { if (fd == INVALID_SOCKET) {
int err = getsockerror_winsock(); int err = getsockerror_winsock();
if (err == EINTR) { delete socket;
free(tmp);
*addr = NULL;
if (err == WSAEINTR) {
// interrupted system call // interrupted system call
ARCH->testCancelThread(); ARCH->testCancelThread();
continue; return NULL;
} }
if (err == WSAECONNABORTED) {
// connection was aborted; try again
ARCH->testCancelThread();
continue;
}
delete socket;
delete *addr;
*addr = NULL;
throwError(err); throwError(err);
} }
} while (false); } while (false);
@ -307,12 +327,15 @@ CArchNetworkWinsock::acceptSocket(CArchSocket s, CArchNetAddress* addr)
socket->m_socket = fd; socket->m_socket = fd;
socket->m_connected = true; socket->m_connected = true;
socket->m_refCount = 1; socket->m_refCount = 1;
socket->m_event = WSACreateEvent_winsock();
socket->m_pollWrite = true;
// discard address if not requested // copy address if requested
if (addr == &dummy) { if (addr != NULL) {
ARCH->closeAddr(dummy); *addr = ARCH->copyAddr(tmp);
} }
free(tmp);
return socket; return socket;
} }
@ -325,7 +348,7 @@ CArchNetworkWinsock::connectSocket(CArchSocket s, CArchNetAddress addr)
do { do {
if (connect_winsock(s->m_socket, &addr->m_addr, if (connect_winsock(s->m_socket, &addr->m_addr,
addr->m_len) == SOCKET_ERROR) { addr->m_len) == SOCKET_ERROR) {
if (getsockerror_winsock() == EINTR) { if (getsockerror_winsock() == WSAEINTR) {
// interrupted system call // interrupted system call
ARCH->testCancelThread(); ARCH->testCancelThread();
continue; continue;
@ -354,95 +377,162 @@ CArchNetworkWinsock::connectSocket(CArchSocket s, CArchNetAddress addr)
int int
CArchNetworkWinsock::pollSocket(CPollEntry pe[], int num, double timeout) CArchNetworkWinsock::pollSocket(CPollEntry pe[], int num, double timeout)
{ {
int i, n; int i;
DWORD n;
do { // prepare sockets and wait list
// prepare sets for select bool canWrite = false;
n = 0; WSAEVENT* events = (WSAEVENT*)alloca((num + 1) * sizeof(WSAEVENT));
fd_set readSet, writeSet, errSet; for (i = 0, n = 0; i < num; ++i) {
fd_set* readSetP = NULL; // reset return flags
fd_set* writeSetP = NULL; pe[i].m_revents = 0;
fd_set* errSetP = NULL;
FD_ZERO(&readSet);
FD_ZERO(&writeSet);
FD_ZERO(&errSet);
for (i = 0; i < num; ++i) {
// reset return flags
pe[i].m_revents = 0;
// set invalid flag if socket is bogus then go to next socket // set invalid flag if socket is bogus then go to next socket
if (pe[i].m_socket == NULL) { if (pe[i].m_socket == NULL) {
pe[i].m_revents |= kPOLLNVAL; pe[i].m_revents |= kPOLLNVAL;
continue; continue;
} }
if (pe[i].m_events & kPOLLIN) { // select desired events
FD_SET(pe[i].m_socket->m_socket, &readSet); long socketEvents = 0;
readSetP = &readSet; if ((pe[i].m_events & kPOLLIN) != 0) {
n = 1; socketEvents |= FD_READ | FD_ACCEPT | FD_CLOSE;
} }
if (pe[i].m_events & kPOLLOUT) { if ((pe[i].m_events & kPOLLOUT) != 0) {
FD_SET(pe[i].m_socket->m_socket, &writeSet); socketEvents |= FD_WRITE | FD_CONNECT | FD_CLOSE;
writeSetP = &writeSet;
n = 1; // if m_pollWrite is false then we assume the socket is
} // writable. winsock doesn't signal writability except
if (true) { // when the state changes from unwritable.
FD_SET(pe[i].m_socket->m_socket, &errSet); if (!pe[i].m_socket->m_pollWrite) {
errSetP = &errSet; canWrite = true;
n = 1; pe[i].m_revents |= kPOLLOUT;
} }
} }
// if there are no sockets then don't block forever // if no events then ignore socket
if (n == 0 && timeout < 0.0) { if (socketEvents == 0) {
timeout = 0.0; continue;
} }
// prepare timeout for select // select socket for desired events
struct timeval timeout2; WSAEventSelect_winsock(pe[i].m_socket->m_socket,
struct timeval* timeout2P; pe[i].m_socket->m_event, socketEvents);
if (timeout < 0) {
timeout2P = NULL; // add socket event to wait list
events[n++] = pe[i].m_socket->m_event;
}
// if no sockets then return immediately
if (n == 0) {
return 0;
}
// add the unblock event
CArchMultithreadWindows* mt = CArchMultithreadWindows::getInstance();
CArchThread thread = mt->newCurrentThread();
WSAEVENT* unblockEvent = (WSAEVENT*)mt->getNetworkDataForThread(thread);
if (unblockEvent == NULL) {
unblockEvent = new WSAEVENT;
*unblockEvent = WSACreateEvent_winsock();
mt->setNetworkDataForCurrentThread(unblockEvent);
}
events[n++] = *unblockEvent;
// prepare timeout
DWORD t = (timeout < 0.0) ? INFINITE : (DWORD)(1000.0 * timeout);
if (canWrite) {
// if we know we can write then don't block
t = 0;
}
// wait
DWORD result = WSAWaitForMultipleEvents_winsock(n, events, FALSE, t, FALSE);
// reset the unblock event
WSAResetEvent_winsock(*unblockEvent);
// handle results
if (result == WSA_WAIT_FAILED) {
if (getsockerror_winsock() == WSAEINTR) {
// interrupted system call
ARCH->testCancelThread();
return 0;
} }
else { throwError(getsockerror_winsock());
timeout2P = &timeout2; }
timeout2.tv_sec = static_cast<int>(timeout); if (result == WSA_WAIT_TIMEOUT && !canWrite) {
timeout2.tv_usec = static_cast<int>(1.0e+6 * return 0;
(timeout - timeout2.tv_sec)); }
if (result == WSA_WAIT_EVENT_0 + n - 1) {
// the unblock event was signalled
return 0;
}
for (i = 0, n = 0; i < num; ++i) {
// skip events we didn't check
if (pe[i].m_socket == NULL ||
(pe[i].m_events & (kPOLLIN | kPOLLOUT)) == 0) {
continue;
} }
// do the select // get events
n = select_winsock(0, readSetP, writeSetP, errSetP, timeout2P); WSANETWORKEVENTS info;
if (WSAEnumNetworkEvents_winsock(pe[i].m_socket->m_socket,
pe[i].m_socket->m_event, &info) == SOCKET_ERROR) {
continue;
}
if ((info.lNetworkEvents & FD_READ) != 0) {
pe[i].m_revents |= kPOLLIN;
}
if ((info.lNetworkEvents & FD_ACCEPT) != 0) {
pe[i].m_revents |= kPOLLIN;
}
if ((info.lNetworkEvents & FD_WRITE) != 0) {
pe[i].m_revents |= kPOLLOUT;
// handle results // socket is now writable so don't bothing polling for
if (n == SOCKET_ERROR) { // writable until it becomes unwritable.
if (getsockerror_winsock() == EINTR) { pe[i].m_socket->m_pollWrite = false;
// interrupted system call }
ARCH->testCancelThread(); if ((info.lNetworkEvents & FD_CONNECT) != 0) {
continue; if (info.iErrorCode[FD_CONNECT_BIT] != 0) {
pe[i].m_revents |= kPOLLERR;
}
else {
pe[i].m_revents |= kPOLLOUT;
pe[i].m_socket->m_pollWrite = false;
} }
throwError(getsockerror_winsock());
} }
n = 0; if ((info.lNetworkEvents & FD_CLOSE) != 0) {
for (i = 0; i < num; ++i) { if (info.iErrorCode[FD_CLOSE_BIT] != 0) {
if (pe[i].m_socket != NULL) { pe[i].m_revents |= kPOLLERR;
if (FD_ISSET(pe[i].m_socket->m_socket, &readSet)) { }
else {
if ((pe[i].m_events & kPOLLIN) != 0) {
pe[i].m_revents |= kPOLLIN; pe[i].m_revents |= kPOLLIN;
} }
if (FD_ISSET(pe[i].m_socket->m_socket, &writeSet)) { if ((pe[i].m_events & kPOLLOUT) != 0) {
pe[i].m_revents |= kPOLLOUT; pe[i].m_revents |= kPOLLOUT;
} }
if (FD_ISSET(pe[i].m_socket->m_socket, &errSet)) {
pe[i].m_revents |= kPOLLERR;
}
}
if (pe[i].m_revents != 0) {
++n;
} }
} }
} while (false); if (pe[i].m_revents != 0) {
++n;
}
}
return n; return (int)n;
}
void
CArchNetworkWinsock::unblockPollSocket(CArchThread thread)
{
// set the unblock event
CArchMultithreadWindows* mt = CArchMultithreadWindows::getInstance();
WSAEVENT* unblockEvent = (WSAEVENT*)mt->getNetworkDataForThread(thread);
if (unblockEvent != NULL) {
WSASetEvent_winsock(*unblockEvent);
}
} }
size_t size_t
@ -454,10 +544,14 @@ CArchNetworkWinsock::readSocket(CArchSocket s, void* buf, size_t len)
do { do {
n = recv_winsock(s->m_socket, buf, len, 0); n = recv_winsock(s->m_socket, buf, len, 0);
if (n == SOCKET_ERROR) { if (n == SOCKET_ERROR) {
if (getsockerror_winsock() == EINTR) { if (getsockerror_winsock() == WSAEINTR) {
// interrupted system call // interrupted system call
ARCH->testCancelThread(); n = 0;
continue; break;
}
else if (getsockerror_winsock() == WSAEWOULDBLOCK) {
n = 0;
break;
} }
throwError(getsockerror_winsock()); throwError(getsockerror_winsock());
} }
@ -475,10 +569,15 @@ CArchNetworkWinsock::writeSocket(CArchSocket s, const void* buf, size_t len)
do { do {
n = send_winsock(s->m_socket, buf, len, 0); n = send_winsock(s->m_socket, buf, len, 0);
if (n == SOCKET_ERROR) { if (n == SOCKET_ERROR) {
if (getsockerror_winsock() == EINTR) { if (getsockerror_winsock() == WSAEINTR) {
// interrupted system call // interrupted system call
ARCH->testCancelThread(); n = 0;
continue; break;
}
else if (getsockerror_winsock() == WSAEWOULDBLOCK) {
s->m_pollWrite = true;
n = 0;
break;
} }
throwError(getsockerror_winsock()); throwError(getsockerror_winsock());
} }
@ -559,26 +658,20 @@ CArchNetworkWinsock::getHostName()
CArchNetAddress CArchNetAddress
CArchNetworkWinsock::newAnyAddr(EAddressFamily family) CArchNetworkWinsock::newAnyAddr(EAddressFamily family)
{ {
// allocate address CArchNetAddressImpl* addr = NULL;
CArchNetAddressImpl* addr = new CArchNetAddressImpl;
// fill it in
switch (family) { switch (family) {
case kINET: { case kINET: {
struct sockaddr_in* ipAddr = addr = CArchNetAddressImpl::alloc(sizeof(struct sockaddr_in));
reinterpret_cast<struct sockaddr_in*>(&addr->m_addr); struct sockaddr_in* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
ipAddr->sin_family = AF_INET; ipAddr->sin_family = AF_INET;
ipAddr->sin_port = 0; ipAddr->sin_port = 0;
ipAddr->sin_addr.s_addr = INADDR_ANY; ipAddr->sin_addr.s_addr = INADDR_ANY;
addr->m_len = sizeof(struct sockaddr_in);
break; break;
} }
default: default:
delete addr;
assert(0 && "invalid family"); assert(0 && "invalid family");
} }
return addr; return addr;
} }
@ -587,26 +680,27 @@ CArchNetworkWinsock::copyAddr(CArchNetAddress addr)
{ {
assert(addr != NULL); assert(addr != NULL);
// allocate and copy address CArchNetAddressImpl* copy = CArchNetAddressImpl::alloc(addr->m_len);
return new CArchNetAddressImpl(*addr); memcpy(TYPED_ADDR(void, copy), TYPED_ADDR(void, addr), addr->m_len);
return copy;
} }
CArchNetAddress CArchNetAddress
CArchNetworkWinsock::nameToAddr(const std::string& name) CArchNetworkWinsock::nameToAddr(const std::string& name)
{ {
// allocate address // allocate address
CArchNetAddressImpl* addr = new CArchNetAddressImpl; CArchNetAddressImpl* addr = NULL;
// try to convert assuming an IPv4 dot notation address // try to convert assuming an IPv4 dot notation address
struct sockaddr_in inaddr; struct sockaddr_in inaddr;
memset(&inaddr, 0, sizeof(inaddr)); memset(&inaddr, 0, sizeof(inaddr));
inaddr.sin_family = AF_INET;
inaddr.sin_port = 0;
inaddr.sin_addr.s_addr = inet_addr_winsock(name.c_str()); inaddr.sin_addr.s_addr = inet_addr_winsock(name.c_str());
if (inaddr.sin_addr.s_addr != INADDR_NONE) { if (inaddr.sin_addr.s_addr != INADDR_NONE) {
// it's a dot notation address // it's a dot notation address
addr->m_len = sizeof(struct sockaddr_in); addr = CArchNetAddressImpl::alloc(sizeof(struct sockaddr_in));
inaddr.sin_family = AF_INET; memcpy(TYPED_ADDR(void, addr), &inaddr, addr->m_len);
inaddr.sin_port = 0;
memcpy(&addr->m_addr, &inaddr, addr->m_len);
} }
else { else {
@ -616,16 +710,8 @@ CArchNetworkWinsock::nameToAddr(const std::string& name)
delete addr; delete addr;
throwNameError(getsockerror_winsock()); throwNameError(getsockerror_winsock());
} }
addr = CArchNetAddressImpl::alloc(info->h_length);
// copy over address (only IPv4 currently supported) memcpy(TYPED_ADDR(void, addr), info->h_addr_list[0], info->h_length);
if (info->h_addrtype == AF_INET) {
addr->m_len = sizeof(struct sockaddr_in);
inaddr.sin_family = info->h_addrtype;
inaddr.sin_port = 0;
memcpy(&inaddr.sin_addr, info->h_addr_list[0],
sizeof(inaddr.sin_addr));
memcpy(&addr->m_addr, &inaddr, addr->m_len);
}
} }
return addr; return addr;
@ -636,7 +722,7 @@ CArchNetworkWinsock::closeAddr(CArchNetAddress addr)
{ {
assert(addr != NULL); assert(addr != NULL);
delete addr; free(addr);
} }
std::string std::string
@ -734,8 +820,8 @@ CArchNetworkWinsock::isAnyAddr(CArchNetAddress addr)
case kINET: { case kINET: {
struct sockaddr_in* ipAddr = struct sockaddr_in* ipAddr =
reinterpret_cast<struct sockaddr_in*>(&addr->m_addr); reinterpret_cast<struct sockaddr_in*>(&addr->m_addr);
return (ipAddr->sin_addr.s_addr == INADDR_ANY && return (addr->m_len == sizeof(struct sockaddr_in) &&
addr->m_len == sizeof(struct sockaddr_in)); ipAddr->sin_addr.s_addr == INADDR_ANY);
} }
default: default:
@ -744,6 +830,13 @@ CArchNetworkWinsock::isAnyAddr(CArchNetAddress addr)
} }
} }
bool
CArchNetworkWinsock::isEqualAddr(CArchNetAddress a, CArchNetAddress b)
{
return (a == b || (a->m_len == b->m_len &&
memcmp(&a->m_addr, &b->m_addr, a->m_len) == 0));
}
void void
CArchNetworkWinsock::throwError(int err) CArchNetworkWinsock::throwError(int err)
{ {
@ -786,8 +879,10 @@ CArchNetworkWinsock::throwError(int err)
case WSAENOTCONN: case WSAENOTCONN:
throw XArchNetworkNotConnected(new XArchEvalWinsock(err)); throw XArchNetworkNotConnected(new XArchEvalWinsock(err));
case WSAENETRESET:
case WSAEDISCON: case WSAEDISCON:
throw XArchNetworkShutdown(new XArchEvalWinsock(err));
case WSAENETRESET:
case WSAECONNABORTED: case WSAECONNABORTED:
case WSAECONNRESET: case WSAECONNRESET:
throw XArchNetworkDisconnected(new XArchEvalWinsock(err)); throw XArchNetworkDisconnected(new XArchEvalWinsock(err));

View File

@ -33,16 +33,20 @@ public:
SOCKET m_socket; SOCKET m_socket;
bool m_connected; bool m_connected;
int m_refCount; int m_refCount;
WSAEVENT m_event;
bool m_pollWrite;
}; };
class CArchNetAddressImpl { class CArchNetAddressImpl {
public: public:
CArchNetAddressImpl() : m_len(sizeof(m_addr)) { } static CArchNetAddressImpl* alloc(size_t);
public: public:
struct sockaddr m_addr;
int m_len; int m_len;
struct sockaddr m_addr;
}; };
#define ADDR_HDR_SIZE offsetof(CArchNetAddressImpl, m_addr)
#define TYPED_ADDR(type_, addr_) (reinterpret_cast<type_*>(&addr_->m_addr))
//! Win32 implementation of IArchNetwork //! Win32 implementation of IArchNetwork
class CArchNetworkWinsock : public IArchNetwork { class CArchNetworkWinsock : public IArchNetwork {
@ -61,6 +65,7 @@ public:
virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr); virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr);
virtual void connectSocket(CArchSocket s, CArchNetAddress name); virtual void connectSocket(CArchSocket s, CArchNetAddress name);
virtual int pollSocket(CPollEntry[], int num, double timeout); virtual int pollSocket(CPollEntry[], int num, double timeout);
virtual void unblockPollSocket(CArchThread thread);
virtual size_t readSocket(CArchSocket s, void* buf, size_t len); virtual size_t readSocket(CArchSocket s, void* buf, size_t len);
virtual size_t writeSocket(CArchSocket s, virtual size_t writeSocket(CArchSocket s,
const void* buf, size_t len); const void* buf, size_t len);
@ -78,6 +83,7 @@ public:
virtual void setAddrPort(CArchNetAddress, int port); virtual void setAddrPort(CArchNetAddress, int port);
virtual int getAddrPort(CArchNetAddress); virtual int getAddrPort(CArchNetAddress);
virtual bool isAnyAddr(CArchNetAddress); virtual bool isAnyAddr(CArchNetAddress);
virtual bool isEqualAddr(CArchNetAddress, CArchNetAddress);
private: private:
void init(HMODULE); void init(HMODULE);

View File

@ -41,71 +41,67 @@ CArchTaskBarWindows::CArchTaskBarWindows(void* appInstance) :
// save app instance // save app instance
s_appInstance = reinterpret_cast<HINSTANCE>(appInstance); s_appInstance = reinterpret_cast<HINSTANCE>(appInstance);
// we need a mutex // register the task bar restart message
m_mutex = ARCH->newMutex(); m_taskBarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
// and a condition variable which uses the above mutex // register a window class
m_ready = false; WNDCLASSEX classInfo;
m_condVar = ARCH->newCondVar(); classInfo.cbSize = sizeof(classInfo);
classInfo.style = CS_NOCLOSE;
classInfo.lpfnWndProc = &CArchTaskBarWindows::staticWndProc;
classInfo.cbClsExtra = 0;
classInfo.cbWndExtra = sizeof(CArchTaskBarWindows*);
classInfo.hInstance = s_appInstance;
classInfo.hIcon = NULL;
classInfo.hCursor = NULL;
classInfo.hbrBackground = NULL;
classInfo.lpszMenuName = NULL;
classInfo.lpszClassName = TEXT("SynergyTaskBar");
classInfo.hIconSm = NULL;
m_windowClass = RegisterClassEx(&classInfo);
// we're going to want to get a result from the thread we're // create window
// about to create to know if it initialized successfully. m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW,
// so we lock the condition variable. reinterpret_cast<LPCTSTR>(m_windowClass),
ARCH->lockMutex(m_mutex); TEXT("Synergy Task Bar"),
WS_POPUP,
// open a window and run an event loop in a separate thread. 0, 0, 1, 1,
// this has to happen in a separate thread because if we NULL,
// create a window on the current desktop with the current NULL,
// thread then the current thread won't be able to switch s_appInstance,
// desktops if it needs to. reinterpret_cast<void*>(this));
m_thread = ARCH->newThread(&CArchTaskBarWindows::threadEntry, this);
// wait for child thread
while (!m_ready) {
ARCH->waitCondVar(m_condVar, m_mutex, -1.0);
}
// ready
ARCH->unlockMutex(m_mutex);
} }
CArchTaskBarWindows::~CArchTaskBarWindows() CArchTaskBarWindows::~CArchTaskBarWindows()
{ {
if (m_thread != NULL) { if (m_hwnd != NULL) {
ARCH->cancelThread(m_thread); removeAllIcons();
ARCH->wait(m_thread, -1.0); DestroyWindow(m_hwnd);
ARCH->closeThread(m_thread);
} }
ARCH->closeCondVar(m_condVar); UnregisterClass((LPCTSTR)m_windowClass, s_appInstance);
ARCH->closeMutex(m_mutex);
s_instance = NULL; s_instance = NULL;
} }
void void
CArchTaskBarWindows::addDialog(HWND hwnd) CArchTaskBarWindows::addDialog(HWND hwnd)
{ {
// add dialog to added dialogs list CArchMiscWindows::addDialog(hwnd);
ARCH->lockMutex(s_instance->m_mutex);
s_instance->m_addedDialogs.insert(std::make_pair(hwnd, true));
ARCH->unlockMutex(s_instance->m_mutex);
} }
void void
CArchTaskBarWindows::removeDialog(HWND hwnd) CArchTaskBarWindows::removeDialog(HWND hwnd)
{ {
// mark dialog as removed CArchMiscWindows::removeDialog(hwnd);
ARCH->lockMutex(s_instance->m_mutex);
CDialogs::iterator index = s_instance->m_dialogs.find(hwnd);
if (index != s_instance->m_dialogs.end()) {
index->second = false;
}
s_instance->m_addedDialogs.erase(hwnd);
ARCH->unlockMutex(s_instance->m_mutex);
} }
void void
CArchTaskBarWindows::addReceiver(IArchTaskBarReceiver* receiver) CArchTaskBarWindows::addReceiver(IArchTaskBarReceiver* receiver)
{ {
if (m_hwnd == NULL) {
return;
}
// ignore bogus receiver // ignore bogus receiver
if (receiver == NULL) { if (receiver == NULL) {
return; return;
@ -180,53 +176,43 @@ CArchTaskBarWindows::recycleID(UINT id)
void void
CArchTaskBarWindows::addIcon(UINT id) CArchTaskBarWindows::addIcon(UINT id)
{ {
ARCH->lockMutex(m_mutex);
CIDToReceiverMap::const_iterator index = m_idTable.find(id); CIDToReceiverMap::const_iterator index = m_idTable.find(id);
if (index != m_idTable.end()) { if (index != m_idTable.end()) {
modifyIconNoLock(index->second, NIM_ADD); modifyIconNoLock(index->second, NIM_ADD);
} }
ARCH->unlockMutex(m_mutex);
} }
void void
CArchTaskBarWindows::removeIcon(UINT id) CArchTaskBarWindows::removeIcon(UINT id)
{ {
ARCH->lockMutex(m_mutex);
removeIconNoLock(id); removeIconNoLock(id);
ARCH->unlockMutex(m_mutex);
} }
void void
CArchTaskBarWindows::updateIcon(UINT id) CArchTaskBarWindows::updateIcon(UINT id)
{ {
ARCH->lockMutex(m_mutex);
CIDToReceiverMap::const_iterator index = m_idTable.find(id); CIDToReceiverMap::const_iterator index = m_idTable.find(id);
if (index != m_idTable.end()) { if (index != m_idTable.end()) {
modifyIconNoLock(index->second, NIM_MODIFY); modifyIconNoLock(index->second, NIM_MODIFY);
} }
ARCH->unlockMutex(m_mutex);
} }
void void
CArchTaskBarWindows::addAllIcons() CArchTaskBarWindows::addAllIcons()
{ {
ARCH->lockMutex(m_mutex);
for (CReceiverToInfoMap::const_iterator index = m_receivers.begin(); for (CReceiverToInfoMap::const_iterator index = m_receivers.begin();
index != m_receivers.end(); ++index) { index != m_receivers.end(); ++index) {
modifyIconNoLock(index, NIM_ADD); modifyIconNoLock(index, NIM_ADD);
} }
ARCH->unlockMutex(m_mutex);
} }
void void
CArchTaskBarWindows::removeAllIcons() CArchTaskBarWindows::removeAllIcons()
{ {
ARCH->lockMutex(m_mutex);
for (CReceiverToInfoMap::const_iterator index = m_receivers.begin(); for (CReceiverToInfoMap::const_iterator index = m_receivers.begin();
index != m_receivers.end(); ++index) { index != m_receivers.end(); ++index) {
removeIconNoLock(index->second.m_id); removeIconNoLock(index->second.m_id);
} }
ARCH->unlockMutex(m_mutex);
} }
void void
@ -319,49 +305,6 @@ CArchTaskBarWindows::handleIconMessage(
} }
} }
bool
CArchTaskBarWindows::processDialogs(MSG* msg)
{
// only one thread can be in this method on any particular object
// at any given time. that's not a problem since only our event
// loop calls this method and there's just one of those.
ARCH->lockMutex(m_mutex);
// remove removed dialogs
m_dialogs.erase(false);
// merge added dialogs into the dialog list
for (CDialogs::const_iterator index = m_addedDialogs.begin();
index != m_addedDialogs.end(); ++index) {
m_dialogs.insert(std::make_pair(index->first, index->second));
}
m_addedDialogs.clear();
ARCH->unlockMutex(m_mutex);
// check message against all dialogs until one handles it.
// note that we don't hold a lock while checking because
// the message is processed and may make calls to this
// object. that's okay because addDialog() and
// removeDialog() don't change the map itself (just the
// values of some elements).
ARCH->lockMutex(m_mutex);
for (CDialogs::const_iterator index = m_dialogs.begin();
index != m_dialogs.end(); ++index) {
if (index->second) {
ARCH->unlockMutex(m_mutex);
if (IsDialogMessage(index->first, msg)) {
return true;
}
ARCH->lockMutex(m_mutex);
}
}
ARCH->unlockMutex(m_mutex);
return false;
}
LRESULT LRESULT
CArchTaskBarWindows::wndProc(HWND hwnd, CArchTaskBarWindows::wndProc(HWND hwnd,
UINT msg, WPARAM wParam, LPARAM lParam) UINT msg, WPARAM wParam, LPARAM lParam)
@ -432,87 +375,3 @@ CArchTaskBarWindows::staticWndProc(HWND hwnd, UINT msg,
return DefWindowProc(hwnd, msg, wParam, lParam); return DefWindowProc(hwnd, msg, wParam, lParam);
} }
} }
void
CArchTaskBarWindows::threadMainLoop()
{
// register the task bar restart message
m_taskBarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
// register a window class
WNDCLASSEX classInfo;
classInfo.cbSize = sizeof(classInfo);
classInfo.style = CS_NOCLOSE;
classInfo.lpfnWndProc = &CArchTaskBarWindows::staticWndProc;
classInfo.cbClsExtra = 0;
classInfo.cbWndExtra = sizeof(CArchTaskBarWindows*);
classInfo.hInstance = s_appInstance;
classInfo.hIcon = NULL;
classInfo.hCursor = NULL;
classInfo.hbrBackground = NULL;
classInfo.lpszMenuName = NULL;
classInfo.lpszClassName = TEXT("SynergyTaskBar");
classInfo.hIconSm = NULL;
ATOM windowClass = RegisterClassEx(&classInfo);
// create window
m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW,
reinterpret_cast<LPCTSTR>(windowClass),
TEXT("Synergy Task Bar"),
WS_POPUP,
0, 0, 1, 1,
NULL,
NULL,
s_appInstance,
reinterpret_cast<void*>(this));
// signal ready
ARCH->lockMutex(m_mutex);
m_ready = true;
ARCH->broadcastCondVar(m_condVar);
ARCH->unlockMutex(m_mutex);
// handle failure
if (m_hwnd == NULL) {
UnregisterClass((LPCTSTR)windowClass, s_appInstance);
return;
}
try {
// main loop
MSG msg;
for (;;) {
// wait for message
if (ARCH->waitForEvent(NULL, -1.0) != IArchMultithread::kEvent) {
continue;
}
// peek for message and remove it. we don't GetMessage()
// because we should never block here, only in waitForEvent().
if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
continue;
}
// check message against dialogs
if (!processDialogs(&msg)) {
// process message
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
catch (XThread&) {
// clean up
removeAllIcons();
DestroyWindow(m_hwnd);
UnregisterClass((LPCTSTR)windowClass, s_appInstance);
throw;
}
}
void*
CArchTaskBarWindows::threadEntry(void* self)
{
reinterpret_cast<CArchTaskBarWindows*>(self)->threadMainLoop();
return NULL;
}

View File

@ -18,7 +18,6 @@
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include "IArchTaskBar.h" #include "IArchTaskBar.h"
#include "IArchMultithread.h"
#include "stdmap.h" #include "stdmap.h"
#include "stdvector.h" #include "stdvector.h"
#include <windows.h> #include <windows.h>
@ -78,21 +77,13 @@ private:
LRESULT wndProc(HWND, UINT, WPARAM, LPARAM); LRESULT wndProc(HWND, UINT, WPARAM, LPARAM);
static LRESULT CALLBACK static LRESULT CALLBACK
staticWndProc(HWND, UINT, WPARAM, LPARAM); staticWndProc(HWND, UINT, WPARAM, LPARAM);
void threadMainLoop();
static void* threadEntry(void*);
private: private:
static CArchTaskBarWindows* s_instance; static CArchTaskBarWindows* s_instance;
static HINSTANCE s_appInstance; static HINSTANCE s_appInstance;
// multithread data
CArchMutex m_mutex;
CArchCond m_condVar;
bool m_ready;
int m_result;
CArchThread m_thread;
// child thread data // child thread data
ATOM m_windowClass;
HWND m_hwnd; HWND m_hwnd;
UINT m_taskBarRestart; UINT m_taskBarRestart;

View File

@ -67,13 +67,6 @@ synergy. Each architecture must implement this interface.
*/ */
class IArchMultithread : public IInterface { class IArchMultithread : public IInterface {
public: public:
//! Result of waitForEvent()
enum EWaitResult {
kEvent, //!< An event is pending
kExit, //!< Thread exited
kTimeout //!< Wait timed out
};
//! Type of thread entry point //! Type of thread entry point
typedef void* (*ThreadFunc)(void*); typedef void* (*ThreadFunc)(void*);
//! Type of thread identifier //! Type of thread identifier
@ -213,30 +206,6 @@ public:
*/ */
virtual bool wait(CArchThread thread, double timeout) = 0; virtual bool wait(CArchThread thread, double timeout) = 0;
//! Wait for a user event
/*!
Waits for up to \c timeout seconds for a pending user event or
\c thread to exit (normally or by cancellation). Waits forever
if \c timeout < 0. Returns kEvent if an event occurred, kExit
if \c thread exited, or kTimeout if the timeout expired. If
\c thread is NULL then it doesn't wait for any thread to exit
and it will not return kExit.
This method is not required by all platforms.
(Cancellation point)
*/
virtual EWaitResult waitForEvent(CArchThread thread, double timeout) = 0;
//! Unblock thread in system call
/*!
Cause a thread that's in a blocking system call to return. This
call may return before the thread is unblocked. If the thread is
not in a blocking system call, this call has no effect. This does
not cause a lockMutex() or waitCondVar() to return prematurely.
*/
virtual void unblockThread(CArchThread thread) = 0;
//! Compare threads //! Compare threads
/*! /*!
Returns true iff two thread objects refer to the same thread. Returns true iff two thread objects refer to the same thread.

View File

@ -18,6 +18,9 @@
#include "IInterface.h" #include "IInterface.h"
#include "stdstring.h" #include "stdstring.h"
class CArchThreadImpl;
typedef CArchThreadImpl* CArchThread;
/*! /*!
\class CArchSocketImpl \class CArchSocketImpl
\brief Internal socket data. \brief Internal socket data.
@ -179,12 +182,21 @@ public:
the \c m_revents members of the entries. \c kPOLLERR and \c kPOLLNVAL the \c m_revents members of the entries. \c kPOLLERR and \c kPOLLNVAL
are set in \c m_revents as appropriate. If a socket indicates are set in \c m_revents as appropriate. If a socket indicates
\c kPOLLERR then \c throwErrorOnSocket() can be used to determine \c kPOLLERR then \c throwErrorOnSocket() can be used to determine
the type of error. the type of error. Returns 0 immediately regardless of the \c timeout
if no valid sockets are selected for testing.
(Cancellation point) (Cancellation point)
*/ */
virtual int pollSocket(CPollEntry[], int num, double timeout) = 0; virtual int pollSocket(CPollEntry[], int num, double timeout) = 0;
//! Unblock thread in pollSocket()
/*!
Cause a thread that's in a pollSocket() call to return. This
call may return before the thread is unblocked. If the thread is
not in a pollSocket() call this call has no effect.
*/
virtual void unblockPollSocket(CArchThread thread) = 0;
//! Read data from socket //! Read data from socket
/*! /*!
Read up to \c len bytes from socket \c s in \c buf and return the Read up to \c len bytes from socket \c s in \c buf and return the

View File

@ -119,10 +119,6 @@ SOURCE=.\CArchFileWindows.h
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=.\CArchImpl.h
# End Source File
# Begin Source File
SOURCE=.\CArchLogWindows.h SOURCE=.\CArchLogWindows.h
# End Source File # End Source File
# Begin Source File # Begin Source File

View File

@ -169,6 +169,10 @@ retry:
event = removeEvent(dataID); event = removeEvent(dataID);
return true; return true;
} }
default:
assert(0 && "invalid event type");
return false;
} }
} }
@ -498,7 +502,7 @@ CEventQueue::CTimer::reset()
m_time = m_timeout; m_time = m_timeout;
} }
CEventQueue::CTimer::CTimer& CEventQueue::CTimer&
CEventQueue::CTimer::operator-=(double dt) CEventQueue::CTimer::operator-=(double dt)
{ {
m_time -= dt; m_time -= dt;

View File

@ -26,7 +26,11 @@ it sorts by std::greater, it has a forward iterator through the elements
(which can appear in any order), and its contents can be swapped. (which can appear in any order), and its contents can be swapped.
*/ */
template <class T, class Container = std::vector<T>, template <class T, class Container = std::vector<T>,
#if WINDOWS_LIKE
class Compare = std::greater<Container::value_type> >
#else
class Compare = std::greater<typename Container::value_type> > class Compare = std::greater<typename Container::value_type> >
#endif
class CPriorityQueue { class CPriorityQueue {
public: public:
typedef typename Container::value_type value_type; typedef typename Container::value_type value_type;

View File

@ -53,7 +53,7 @@ CSimpleEventQueueBuffer::waitForEvent(double timeout)
} }
IEventQueueBuffer::Type IEventQueueBuffer::Type
CSimpleEventQueueBuffer::getEvent(CEvent& event, UInt32& dataID) CSimpleEventQueueBuffer::getEvent(CEvent&, UInt32& dataID)
{ {
CArchMutexLock lock(m_queueMutex); CArchMutexLock lock(m_queueMutex);
if (!m_queueReady) { if (!m_queueReady) {

View File

@ -39,7 +39,8 @@ CServerProxy::CServerProxy(CClient* client, IStream* stream) :
m_seqNum(0), m_seqNum(0),
m_compressMouse(false), m_compressMouse(false),
m_ignoreMouse(false), m_ignoreMouse(false),
m_heartRate(0.0) m_heartRate(0.0),
m_parser(&CServerProxy::parseHandshakeMessage)
{ {
assert(m_client != NULL); assert(m_client != NULL);
assert(m_stream != NULL); assert(m_stream != NULL);
@ -52,7 +53,7 @@ CServerProxy::CServerProxy(CClient* client, IStream* stream) :
EVENTQUEUE->adoptHandler(IStream::getInputReadyEvent(), EVENTQUEUE->adoptHandler(IStream::getInputReadyEvent(),
m_stream->getEventTarget(), m_stream->getEventTarget(),
new TMethodEventJob<CServerProxy>(this, new TMethodEventJob<CServerProxy>(this,
&CServerProxy::handleMessage)); &CServerProxy::handleData));
// send heartbeat // send heartbeat
installHeartBeat(kHeartRate); installHeartBeat(kHeartRate);
@ -61,6 +62,8 @@ CServerProxy::CServerProxy(CClient* client, IStream* stream) :
CServerProxy::~CServerProxy() CServerProxy::~CServerProxy()
{ {
installHeartBeat(-1.0); installHeartBeat(-1.0);
EVENTQUEUE->removeHandler(IStream::getInputReadyEvent(),
m_stream->getEventTarget());
} }
CEvent::Type CEvent::Type
@ -87,136 +90,193 @@ CServerProxy::installHeartBeat(double heartRate)
} }
void void
CServerProxy::handleMessage(const CEvent&, void*) CServerProxy::handleData(const CEvent&, void*)
{ {
while (m_stream->isReady()) { // handle messages until there are no more. first read message code.
// read next code UInt8 code[4];
UInt8 code[4]; UInt32 n = m_stream->read(code, 4);
UInt32 n = m_stream->read(code, sizeof(code)); while (n != 0) {
if (n == 0) { // verify we got an entire code
break;
}
if (n != 4) { if (n != 4) {
// client sent an incomplete message LOG((CLOG_ERR "incomplete message from server: %d bytes", n));
LOG((CLOG_ERR "incomplete message from server"));
m_client->disconnect("incomplete message from server"); m_client->disconnect("incomplete message from server");
return; return;
} }
// parse message // parse message
LOG((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3])); LOG((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3]));
if (memcmp(code, kMsgDMouseMove, 4) == 0) { switch ((this->*m_parser)(code)) {
mouseMove(); case kOkay:
}
else if (memcmp(code, kMsgDMouseWheel, 4) == 0) {
mouseWheel();
}
else if (memcmp(code, kMsgDKeyDown, 4) == 0) {
keyDown();
}
else if (memcmp(code, kMsgDKeyUp, 4) == 0) {
keyUp();
}
else if (memcmp(code, kMsgDMouseDown, 4) == 0) {
mouseDown();
}
else if (memcmp(code, kMsgDMouseUp, 4) == 0) {
mouseUp();
}
else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) {
keyRepeat();
}
else if (memcmp(code, kMsgCNoop, 4) == 0) {
// accept and discard no-op
}
else if (memcmp(code, kMsgCEnter, 4) == 0) {
enter();
}
else if (memcmp(code, kMsgCLeave, 4) == 0) {
leave();
}
else if (memcmp(code, kMsgCClipboard, 4) == 0) {
grabClipboard();
}
else if (memcmp(code, kMsgCScreenSaver, 4) == 0) {
screensaver();
}
else if (memcmp(code, kMsgQInfo, 4) == 0) {
queryInfo();
}
else if (memcmp(code, kMsgCInfoAck, 4) == 0) {
infoAcknowledgment();
}
else if (memcmp(code, kMsgDClipboard, 4) == 0) {
setClipboard();
}
else if (memcmp(code, kMsgCResetOptions, 4) == 0) {
resetOptions();
}
else if (memcmp(code, kMsgDSetOptions, 4) == 0) {
setOptions();
}
else if (memcmp(code, kMsgCClose, 4) == 0) {
// server wants us to hangup
LOG((CLOG_DEBUG1 "recv close"));
m_client->disconnect(NULL);
break; break;
}
else if (memcmp(code, kMsgEIncompatible, 4) == 0) { case kUnknown:
SInt32 major, minor; LOG((CLOG_ERR "invalid message from server"));
CProtocolUtil::readf(m_stream, m_client->disconnect("invalid message from server");
kMsgEIncompatible + 4, &major, &minor); return;
LOG((CLOG_ERR "server has incompatible version %d.%d", major, minor));
m_client->disconnect("server has incompatible version"); case kDisconnect:
return; return;
} }
else if (memcmp(code, kMsgEBusy, 4) == 0) { // next message
LOG((CLOG_ERR "server already has a connected client with name \"%s\"", m_client->getName().c_str())); n = m_stream->read(code, 4);
m_client->disconnect("server already has a connected client with our name");
return;
}
else if (memcmp(code, kMsgEUnknown, 4) == 0) {
LOG((CLOG_ERR "server refused client with name \"%s\"", m_client->getName().c_str()));
m_client->disconnect("server refused client with our name");
return;
}
else if (memcmp(code, kMsgEBad, 4) == 0) {
LOG((CLOG_ERR "server disconnected due to a protocol error"));
m_client->disconnect("server reported a protocol error");
return;
}
else {
// unknown message
LOG((CLOG_ERR "unknown message: %d %d %d %d [%c%c%c%c]", code[0], code[1], code[2], code[3], code[0], code[1], code[2], code[3]));
m_client->disconnect("unknown message from server");
return;
}
} }
flushCompressedMouse(); flushCompressedMouse();
} }
CServerProxy::EResult
CServerProxy::parseHandshakeMessage(const UInt8* code)
{
if (memcmp(code, kMsgQInfo, 4) == 0) {
queryInfo();
}
else if (memcmp(code, kMsgCInfoAck, 4) == 0) {
infoAcknowledgment();
}
else if (memcmp(code, kMsgCResetOptions, 4) == 0) {
resetOptions();
// handshake is complete
m_parser = &CServerProxy::parseMessage;
EVENTQUEUE->addEvent(CEvent(getHandshakeCompleteEvent(), this));
}
else if (memcmp(code, kMsgCNoop, 4) == 0) {
// accept and discard no-op
}
else if (memcmp(code, kMsgCClose, 4) == 0) {
// server wants us to hangup
LOG((CLOG_DEBUG1 "recv close"));
m_client->disconnect(NULL);
return kDisconnect;
}
else if (memcmp(code, kMsgEIncompatible, 4) == 0) {
SInt32 major, minor;
CProtocolUtil::readf(m_stream,
kMsgEIncompatible + 4, &major, &minor);
LOG((CLOG_ERR "server has incompatible version %d.%d", major, minor));
m_client->disconnect("server has incompatible version");
return kDisconnect;
}
else if (memcmp(code, kMsgEBusy, 4) == 0) {
LOG((CLOG_ERR "server already has a connected client with name \"%s\"", m_client->getName().c_str()));
m_client->disconnect("server already has a connected client with our name");
return kDisconnect;
}
else if (memcmp(code, kMsgEUnknown, 4) == 0) {
LOG((CLOG_ERR "server refused client with name \"%s\"", m_client->getName().c_str()));
m_client->disconnect("server refused client with our name");
return kDisconnect;
}
else if (memcmp(code, kMsgEBad, 4) == 0) {
LOG((CLOG_ERR "server disconnected due to a protocol error"));
m_client->disconnect("server reported a protocol error");
return kDisconnect;
}
else {
return kUnknown;
}
return kOkay;
}
CServerProxy::EResult
CServerProxy::parseMessage(const UInt8* code)
{
if (memcmp(code, kMsgDMouseMove, 4) == 0) {
mouseMove();
}
else if (memcmp(code, kMsgDMouseWheel, 4) == 0) {
mouseWheel();
}
else if (memcmp(code, kMsgDKeyDown, 4) == 0) {
keyDown();
}
else if (memcmp(code, kMsgDKeyUp, 4) == 0) {
keyUp();
}
else if (memcmp(code, kMsgDMouseDown, 4) == 0) {
mouseDown();
}
else if (memcmp(code, kMsgDMouseUp, 4) == 0) {
mouseUp();
}
else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) {
keyRepeat();
}
else if (memcmp(code, kMsgCNoop, 4) == 0) {
// accept and discard no-op
}
else if (memcmp(code, kMsgCEnter, 4) == 0) {
enter();
}
else if (memcmp(code, kMsgCLeave, 4) == 0) {
leave();
}
else if (memcmp(code, kMsgCClipboard, 4) == 0) {
grabClipboard();
}
else if (memcmp(code, kMsgCScreenSaver, 4) == 0) {
screensaver();
}
else if (memcmp(code, kMsgQInfo, 4) == 0) {
queryInfo();
}
else if (memcmp(code, kMsgCInfoAck, 4) == 0) {
infoAcknowledgment();
}
else if (memcmp(code, kMsgDClipboard, 4) == 0) {
setClipboard();
}
else if (memcmp(code, kMsgCResetOptions, 4) == 0) {
resetOptions();
}
else if (memcmp(code, kMsgDSetOptions, 4) == 0) {
setOptions();
}
else if (memcmp(code, kMsgCClose, 4) == 0) {
// server wants us to hangup
LOG((CLOG_DEBUG1 "recv close"));
m_client->disconnect(NULL);
return kDisconnect;
}
else if (memcmp(code, kMsgEBad, 4) == 0) {
LOG((CLOG_ERR "server disconnected due to a protocol error"));
m_client->disconnect("server reported a protocol error");
return kDisconnect;
}
else {
return kUnknown;
}
return kOkay;
}
void void
CServerProxy::handleHeartBeat(const CEvent&, void*) CServerProxy::handleHeartBeat(const CEvent&, void*)
{ {

View File

@ -42,9 +42,9 @@ public:
//! @name manipulators //! @name manipulators
//@{ //@{
virtual void onInfoChanged(); void onInfoChanged();
virtual bool onGrabClipboard(ClipboardID); bool onGrabClipboard(ClipboardID);
virtual void onClipboardChanged(ClipboardID, const IClipboard*); void onClipboardChanged(ClipboardID, const IClipboard*);
//@} //@}
//! @name accessors //! @name accessors
@ -59,6 +59,11 @@ public:
//@} //@}
protected:
enum EResult { kOkay, kUnknown, kDisconnect };
EResult parseHandshakeMessage(const UInt8* code);
EResult parseMessage(const UInt8* code);
private: private:
// if compressing mouse motion then send the last motion now // if compressing mouse motion then send the last motion now
void flushCompressedMouse(); void flushCompressedMouse();
@ -72,7 +77,7 @@ private:
KeyModifierMask translateModifierMask(KeyModifierMask) const; KeyModifierMask translateModifierMask(KeyModifierMask) const;
// event handlers // event handlers
void handleMessage(const CEvent&, void*); void handleData(const CEvent&, void*);
void handleHeartBeat(const CEvent&, void*); void handleHeartBeat(const CEvent&, void*);
// message handlers // message handlers
@ -94,6 +99,8 @@ private:
void infoAcknowledgment(); void infoAcknowledgment();
private: private:
typedef EResult (CServerProxy::*MessageParser)(const UInt8*);
CClient* m_client; CClient* m_client;
IStream* m_stream; IStream* m_stream;
CEventQueueTimer* m_timer; CEventQueueTimer* m_timer;
@ -108,6 +115,8 @@ private:
KeyModifierID m_modifierTranslationTable[kKeyModifierIDLast]; KeyModifierID m_modifierTranslationTable[kKeyModifierIDLast];
double m_heartRate; double m_heartRate;
MessageParser m_parser;
static CEvent::Type s_handshakeCompleteEvent; static CEvent::Type s_handshakeCompleteEvent;
}; };

View File

@ -19,7 +19,6 @@ EXTRA_DIST = \
common.dsp \ common.dsp \
BasicTypes.h \ BasicTypes.h \
IInterface.h \ IInterface.h \
Version.h \
common.h \ common.h \
stdbitset.h \ stdbitset.h \
stddeque.h \ stddeque.h \
@ -40,5 +39,11 @@ MAINTAINERCLEANFILES = \
Makefile.in \ Makefile.in \
$(NULL) $(NULL)
noinst_LIBRARIES = libcommon.a
libcommon_a_SOURCES = \
Version.cpp \
Version.h \
$(NULL)
INCLUDES = \ INCLUDES = \
$(NULL) $(NULL)

View File

@ -23,17 +23,17 @@
#endif #endif
// important strings // important strings
static const char* kApplication = "synergy"; extern const char* kApplication;
static const char* kCopyright = "Copyright (C) 2002 Chris Schoeneman"; extern const char* kCopyright;
static const char* kContact = "Chris Schoeneman, crs23@bigfoot.com"; extern const char* kContact;
static const char* kWebsite = "http://synergy2.sourceforge.net/"; extern const char* kWebsite;
// build version. follows linux kernel style: an even minor number implies // build version. follows linux kernel style: an even minor number implies
// a release version, odd implies development version. // a release version, odd implies development version.
static const char* kVersion = VERSION; extern const char* kVersion;
// application version // application version
static const char* kAppVersion = "synergy " VERSION; extern const char* kAppVersion;
// exit codes // exit codes
static const int kExitSuccess = 0; // successful completion static const int kExitSuccess = 0; // successful completion

View File

@ -85,6 +85,10 @@ LIB32=link.exe -lib
# Begin Group "Source Files" # Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
SOURCE=.\Version.cpp
# End Source File
# End Group # End Group
# Begin Group "Header Files" # Begin Group "Header Files"

View File

@ -79,6 +79,12 @@ CThread::setPriority(int n)
ARCH->setPriorityOfThread(m_thread, n); ARCH->setPriorityOfThread(m_thread, n);
} }
void
CThread::unblockPollSocket()
{
ARCH->unblockPollSocket(m_thread);
}
CThread CThread
CThread::getCurrentThread() CThread::getCurrentThread()
{ {
@ -97,24 +103,6 @@ CThread::wait(double timeout) const
return ARCH->wait(m_thread, timeout); return ARCH->wait(m_thread, timeout);
} }
CThread::EWaitResult
CThread::waitForEvent(double timeout) const
{
// IArchMultithread EWaitResults map directly to our EWaitResults
static const EWaitResult s_map[] = {
kEvent,
kExit,
kTimeout
};
return s_map[ARCH->waitForEvent(m_thread, timeout)];
}
void
CThread::unblock() const
{
ARCH->unblockThread(m_thread);
}
void* void*
CThread::getResult() const CThread::getResult() const
{ {

View File

@ -39,13 +39,6 @@ documentation.
// note -- do not derive from this class // note -- do not derive from this class
class CThread { class CThread {
public: public:
//! Result of waitForEvent()
enum EWaitResult {
kEvent, //!< An event is pending
kExit, //!< Thread exited
kTimeout //!< Wait timed out
};
//! Run \c adoptedJob in a new thread //! Run \c adoptedJob in a new thread
/*! /*!
Create and start a new thread executing the \c adoptedJob. The Create and start a new thread executing the \c adoptedJob. The
@ -130,6 +123,13 @@ public:
*/ */
void setPriority(int n); void setPriority(int n);
//! Force pollSocket() to return
/*!
Forces a currently blocked pollSocket() in the thread to return
immediately.
*/
void unblockPollSocket();
//@} //@}
//! @name accessors //! @name accessors
//@{ //@{
@ -164,31 +164,6 @@ public:
*/ */
bool wait(double timeout = -1.0) const; bool wait(double timeout = -1.0) const;
//! Wait for an event (win32)
/*!
Wait for the message queue to contain a message or for the thread
to exit for up to \c timeout seconds. This returns immediately if
any message is available (including messages that were already in
the queue during the last call to \c GetMessage() or
\c PeekMessage() or waitForEvent(). Returns kEvent if a message
is available, kExit if the thread exited, and kTimeout otherwise.
This will wait forever if \c timeout < 0.0.
This method is available under win32 only.
(cancellation point)
*/
EWaitResult waitForEvent(double timeout = -1.0) const;
//! Unblock thread in system call
/*!
Cause a thread that's in a blocking system call to return. This
call may return before the thread is unblocked. If the thread is
not in a blocking system call, this call has no effect. This does
not cause CMutex::lock() or CCondVar::wait() to return prematurely.
*/
void unblock() const;
//! Get the exit result //! Get the exit result
/*! /*!
Returns the exit result. This does an implicit wait(). It returns Returns the exit result. This does an implicit wait(). It returns

View File

@ -54,6 +54,7 @@ CSocketMultiplexer::CSocketMultiplexer() :
CSocketMultiplexer::~CSocketMultiplexer() CSocketMultiplexer::~CSocketMultiplexer()
{ {
m_thread->cancel(); m_thread->cancel();
m_thread->unblockPollSocket();
m_thread->wait(); m_thread->wait();
delete m_thread; delete m_thread;
delete m_polling; delete m_polling;
@ -87,7 +88,7 @@ CSocketMultiplexer::addSocket(ISocket* socket, ISocketMultiplexerJob* job)
*m_pollable = false; *m_pollable = false;
// break thread out of poll // break thread out of poll
m_thread->unblock(); m_thread->unblockPollSocket();
// wait for poll to finish // wait for poll to finish
while (*m_polling) { while (*m_polling) {
@ -129,7 +130,7 @@ CSocketMultiplexer::removeSocket(ISocket* socket)
*m_pollable = false; *m_pollable = false;
// break thread out of poll // break thread out of poll
m_thread->unblock(); m_thread->unblockPollSocket();
// wait until thread finishes poll // wait until thread finishes poll
while (*m_polling) { while (*m_polling) {
@ -160,11 +161,12 @@ CSocketMultiplexer::serviceThread(void*)
// service the connections // service the connections
for (;;) { for (;;) {
CThread::testCancel();
{ {
CLock lock(m_mutex); CLock lock(m_mutex);
// wait until pollable // wait until pollable
while (!*m_pollable) { while (!(bool)*m_pollable) {
m_pollable->wait(); m_pollable->wait();
} }
@ -281,7 +283,6 @@ CSocketMultiplexer::CJobCursor
CSocketMultiplexer::nextCursor(CJobCursor cursor) CSocketMultiplexer::nextCursor(CJobCursor cursor)
{ {
CLock lock(m_mutex); CLock lock(m_mutex);
ISocketMultiplexerJob* job = NULL;
CJobCursor j = m_socketJobs.end(); CJobCursor j = m_socketJobs.end();
CJobCursor i = cursor; CJobCursor i = cursor;
while (++i != m_socketJobs.end()) { while (++i != m_socketJobs.end()) {

View File

@ -34,3 +34,18 @@ IDataSocket::getConnectionFailedEvent()
return CEvent::registerTypeOnce(s_failedEvent, return CEvent::registerTypeOnce(s_failedEvent,
"IDataSocket::failed"); "IDataSocket::failed");
} }
void
IDataSocket::close()
{
// this is here to work around a VC++6 bug. see the header file.
assert(0 && "bad call");
}
void*
IDataSocket::getEventTarget() const
{
// this is here to work around a VC++6 bug. see the header file.
assert(0 && "bad call");
return NULL;
}

View File

@ -65,9 +65,13 @@ public:
//@} //@}
// ISocket overrides // ISocket overrides
// close() and getEventTarget() aren't pure to work around a bug
// in VC++6. it claims the methods are unused locals and warns
// that it's removing them. it's presumably tickled by inheriting
// methods with identical signatures from both superclasses.
virtual void bind(const CNetworkAddress&) = 0; virtual void bind(const CNetworkAddress&) = 0;
virtual void close() = 0; virtual void close();
virtual void* getEventTarget() const = 0; virtual void* getEventTarget() const;
// IStream overrides // IStream overrides
virtual UInt32 read(void* buffer, UInt32 n) = 0; virtual UInt32 read(void* buffer, UInt32 n) = 0;

View File

@ -21,6 +21,12 @@
//! Encapsulate Microsoft Windows desktop //! Encapsulate Microsoft Windows desktop
class CMSWindowsDesktop { class CMSWindowsDesktop {
public: public:
//! Open the desktop
/*!
Opens the desktop named \p name. The caller must close the desktop.
*/
static HDESK openDesktop(const CString& name);
//! Open the input desktop //! Open the input desktop
/*! /*!
Opens the input desktop. The caller must close the desktop. Opens the input desktop. The caller must close the desktop.

View File

@ -0,0 +1,129 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CMSWindowsEventQueueBuffer.h"
#include "CThread.h"
#include "IEventQueue.h"
#include "CArchMiscWindows.h"
//
// CEventQueueTimer
//
class CEventQueueTimer { };
//
// CMSWindowsEventQueueBuffer
//
CMSWindowsEventQueueBuffer::CMSWindowsEventQueueBuffer()
{
// remember thread. we'll be posting messages to it.
m_thread = GetCurrentThreadId();
// create a message type for custom events
m_userEvent = RegisterWindowMessage("SYNERGY_USER_EVENT");
// get message type for daemon quit
m_daemonQuit = CArchMiscWindows::getDaemonQuitMessage();
// make sure this thread has a message queue
MSG dummy;
PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE);
}
CMSWindowsEventQueueBuffer::~CMSWindowsEventQueueBuffer()
{
// do nothing
}
void
CMSWindowsEventQueueBuffer::waitForEvent(double timeout)
{
// check if messages are available first. if we don't do this then
// MsgWaitForMultipleObjects() will block even if the queue isn't
// empty if the messages in the queue were there before the last
// call to GetMessage()/PeekMessage().
if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) {
return;
}
// convert timeout
DWORD t;
if (timeout < 0.0) {
t = INFINITE;
}
else {
t = (DWORD)(1000.0 * timeout);
}
// wait for a message. we cannot be interrupted by thread
// cancellation but that's okay because we're run in the main
// thread and we never cancel that thread.
HANDLE dummy[1];
MsgWaitForMultipleObjects(0, dummy, FALSE, t, QS_ALLINPUT);
}
IEventQueueBuffer::Type
CMSWindowsEventQueueBuffer::getEvent(CEvent& event, UInt32& dataID)
{
// BOOL. yeah, right.
BOOL result = GetMessage(&m_event, NULL, 0, 0);
if (result == -1) {
return kNone;
}
else if (result == 0) {
event = CEvent(CEvent::kQuit);
return kSystem;
}
else if (m_event.message == m_daemonQuit) {
event = CEvent(CEvent::kQuit);
return kSystem;
}
else if (m_event.message == m_userEvent) {
dataID = static_cast<UInt32>(m_event.wParam);
return kUser;
}
else {
event = CEvent(CEvent::kSystem,
IEventQueue::getSystemTarget(), &m_event);
return kSystem;
}
}
bool
CMSWindowsEventQueueBuffer::addEvent(UInt32 dataID)
{
return (PostThreadMessage(m_thread, m_userEvent,
static_cast<WPARAM>(dataID), 0) != 0);
}
bool
CMSWindowsEventQueueBuffer::isEmpty() const
{
return (HIWORD(GetQueueStatus(QS_ALLINPUT)) == 0);
}
CEventQueueTimer*
CMSWindowsEventQueueBuffer::newTimer(double, bool) const
{
return new CEventQueueTimer;
}
void
CMSWindowsEventQueueBuffer::deleteTimer(CEventQueueTimer* timer) const
{
delete timer;
}

View File

@ -0,0 +1,44 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CMSWINDOWSEVENTQUEUEBUFFER_H
#define CMSWINDOWSEVENTQUEUEBUFFER_H
#include "IEventQueueBuffer.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
//! Event queue buffer for Win32
class CMSWindowsEventQueueBuffer : public IEventQueueBuffer {
public:
CMSWindowsEventQueueBuffer();
virtual ~CMSWindowsEventQueueBuffer();
// IEventQueueBuffer overrides
virtual void waitForEvent(double timeout);
virtual Type getEvent(CEvent& event, UInt32& dataID);
virtual bool addEvent(UInt32 dataID);
virtual bool isEmpty() const;
virtual CEventQueueTimer*
newTimer(double duration, bool oneShot) const;
virtual void deleteTimer(CEventQueueTimer*) const;
private:
DWORD m_thread;
UINT m_userEvent;
MSG m_event;
UINT m_daemonQuit;
};
#endif

View File

@ -741,7 +741,7 @@ CMSWindowsKeyMapper::update(IKeyState* keyState)
for (size_t j = 0; j < CModifierKeys::s_maxKeys; ++j) { for (size_t j = 0; j < CModifierKeys::s_maxKeys; ++j) {
if (s_modifiers[i].m_keys[j] != 0) { if (s_modifiers[i].m_keys[j] != 0) {
SHORT s = GetKeyState(s_modifiers[i].m_keys[j]); SHORT s = GetKeyState(s_modifiers[i].m_keys[j]);
m_keys[s_modifiers[i].m_keys[j]] = static_cast<BYTE>(s); m_keys[s_modifiers[i].m_keys[j] & 0xffu] = static_cast<BYTE>(s);
if (keyState != NULL) { if (keyState != NULL) {
if ((s & 0x01) != 0) { if ((s & 0x01) != 0) {
keyState->setToggled(s_modifiers[i].m_mask); keyState->setToggled(s_modifiers[i].m_mask);

File diff suppressed because it is too large Load Diff

View File

@ -18,19 +18,20 @@
#include "IPlatformScreen.h" #include "IPlatformScreen.h"
#include "CMSWindowsKeyMapper.h" #include "CMSWindowsKeyMapper.h"
#include "CSynergyHook.h" #include "CSynergyHook.h"
#include "CCondVar.h"
#include "CMutex.h" #include "CMutex.h"
#include "CString.h" #include "CString.h"
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
class CEventQueueTimer;
class CMSWindowsScreenSaver; class CMSWindowsScreenSaver;
class IScreenReceiver; class CThread;
class IPrimaryScreenReceiver;
//! Implementation of IPlatformScreen for Microsoft Windows //! Implementation of IPlatformScreen for Microsoft Windows
class CMSWindowsScreen : public IPlatformScreen { class CMSWindowsScreen : public IPlatformScreen {
public: public:
CMSWindowsScreen(IScreenReceiver*, IPrimaryScreenReceiver*); CMSWindowsScreen(bool isPrimary);
virtual ~CMSWindowsScreen(); virtual ~CMSWindowsScreen();
//! @name manipulators //! @name manipulators
@ -56,12 +57,9 @@ public:
//@} //@}
// IPlatformScreen overrides // IPlatformScreen overrides
virtual void open(IKeyState*); virtual void setKeyState(IKeyState*);
virtual void close();
virtual void enable(); virtual void enable();
virtual void disable(); virtual void disable();
virtual void mainLoop();
virtual void exitMainLoop();
virtual void enter(); virtual void enter();
virtual bool leave(); virtual bool leave();
virtual bool setClipboard(ClipboardID, const IClipboard*); virtual bool setClipboard(ClipboardID, const IClipboard*);
@ -72,17 +70,22 @@ public:
virtual void resetOptions(); virtual void resetOptions();
virtual void setOptions(const COptionsList& options); virtual void setOptions(const COptionsList& options);
virtual void updateKeys(); virtual void updateKeys();
virtual void setSequenceNumber(UInt32);
virtual bool isPrimary() const; virtual bool isPrimary() const;
virtual bool getClipboard(ClipboardID, IClipboard*) const;
virtual void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; // IScreen overrides
virtual void getCursorPos(SInt32&, SInt32&) const; virtual void* getEventTarget() const;
virtual bool getClipboard(ClipboardID id, IClipboard*) const;
virtual void getShape(SInt32& x, SInt32& y,
SInt32& width, SInt32& height) const;
virtual void getCursorPos(SInt32& x, SInt32& y) const;
// IPrimaryScreen overrides // IPrimaryScreen overrides
virtual void reconfigure(UInt32 activeSides); virtual void reconfigure(UInt32 activeSides);
virtual void warpCursor(SInt32 x, SInt32 y); virtual void warpCursor(SInt32 x, SInt32 y);
virtual UInt32 addOneShotTimer(double timeout);
virtual SInt32 getJumpZoneSize() const; virtual SInt32 getJumpZoneSize() const;
virtual bool isAnyMouseButtonDown() const; virtual bool isAnyMouseButtonDown() const;
virtual void getCursorCenter(SInt32& x, SInt32& y) const;
virtual const char* getKeyName(KeyButton) const; virtual const char* getKeyName(KeyButton) const;
// ISecondaryScreen overrides // ISecondaryScreen overrides
@ -97,17 +100,31 @@ public:
bool isAutoRepeat) const; bool isAutoRepeat) const;
private: private:
// update screen size cache class CDesk {
void updateScreenShape(); public:
CString m_name;
CThread* m_thread;
DWORD m_threadID;
DWORD m_targetID;
HDESK m_desk;
HWND m_window;
bool m_lowLevel;
};
typedef std::map<CString, CDesk*> CDesks;
// switch to the given desktop. this destroys the window and unhooks // FIXME -- comment
// all hooks, switches the desktop, then creates the window and rehooks HINSTANCE openHookLibrary(const char* name);
// all hooks (because you can't switch the thread's desktop if it has void closeHookLibrary(HINSTANCE hookLibrary) const;
// any windows or hooks). HCURSOR createBlankCursor() const;
bool switchDesktop(HDESK desk); void destroyCursor(HCURSOR cursor) const;
ATOM createWindowClass() const;
// make sure we're on the expected desktop ATOM createDeskWindowClass(bool isPrimary) const;
void syncDesktop() const; void destroyClass(ATOM windowClass) const;
HWND createWindow(ATOM windowClass, const char* name) const;
void destroyWindow(HWND) const;
void sendEvent(CEvent::Type type, void* = NULL);
void sendClipboardEvent(CEvent::Type type, ClipboardID id);
void handleSystemEvent(const CEvent& event, void*);
// handle message before it gets dispatched. returns true iff // handle message before it gets dispatched. returns true iff
// the message should not be dispatched. // the message should not be dispatched.
@ -128,10 +145,8 @@ private:
bool onMouseMove(SInt32 x, SInt32 y); bool onMouseMove(SInt32 x, SInt32 y);
bool onMouseWheel(SInt32 delta); bool onMouseWheel(SInt32 delta);
bool onScreensaver(bool activated); bool onScreensaver(bool activated);
bool onTimer(UINT timerID);
bool onDisplayChange(); bool onDisplayChange();
bool onClipboardChange(); bool onClipboardChange();
bool onActivate(bool activated);
// XXX // XXX
// warp cursor without discarding queued events // warp cursor without discarding queued events
@ -144,11 +159,8 @@ private:
bool ignore() const; bool ignore() const;
// XXX // XXX
// create the transparent cursor // update screen size cache
HCURSOR createBlankCursor() const; void updateScreenShape();
// show/hide the cursor
void showCursor(bool) const;
// enable/disable special key combinations so we can catch/pass them // enable/disable special key combinations so we can catch/pass them
void enableSpecialKeys(bool) const; void enableSpecialKeys(bool) const;
@ -169,6 +181,22 @@ private:
// our window proc // our window proc
static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM); static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM);
// our desk window procs
static LRESULT CALLBACK primaryDeskProc(HWND, UINT, WPARAM, LPARAM);
static LRESULT CALLBACK secondaryDeskProc(HWND, UINT, WPARAM, LPARAM);
void deskMouseMove(SInt32 x, SInt32 y) const;
void deskEnter(CDesk* desk, DWORD& cursorThreadID);
void deskLeave(CDesk* desk, DWORD& cursorThreadID);
void deskThread(void* vdesk);
CDesk* addDesk(const CString& name, HDESK hdesk);
void removeDesks();
void checkDesk();
bool isDeskAccessible(const CDesk* desk) const;
void sendDeskMessage(UINT, WPARAM, LPARAM) const;
void waitForDesk() const;
void handleCheckDesk(const CEvent& event, void*);
private: private:
static HINSTANCE s_instance; static HINSTANCE s_instance;
@ -178,17 +206,13 @@ private:
// true if windows 95/98/me // true if windows 95/98/me
bool m_is95Family; bool m_is95Family;
// receivers
IScreenReceiver* m_receiver;
IPrimaryScreenReceiver* m_primaryReceiver;
// true if mouse has entered the screen // true if mouse has entered the screen
bool m_isOnScreen; bool m_isOnScreen;
// our resources // our resources
ATOM m_class; ATOM m_class;
ATOM m_deskClass;
HCURSOR m_cursor; HCURSOR m_cursor;
HWND m_window;
// screen shape stuff // screen shape stuff
SInt32 m_x, m_y; SInt32 m_x, m_y;
@ -201,6 +225,8 @@ private:
// last mouse position // last mouse position
SInt32 m_xCursor, m_yCursor; SInt32 m_xCursor, m_yCursor;
UInt32 m_sequenceNumber;
// used to discard queued messages that are no longer needed // used to discard queued messages that are no longer needed
UInt32 m_mark; UInt32 m_mark;
UInt32 m_markReceived; UInt32 m_markReceived;
@ -208,33 +234,27 @@ private:
// the main loop's thread id // the main loop's thread id
DWORD m_threadID; DWORD m_threadID;
// the thread id of the last attached thread
DWORD m_lastThreadID;
// the timer used to check for desktop switching // the timer used to check for desktop switching
UINT m_timer; CEventQueueTimer* m_timer;
// the one shot timer
UINT m_oneShotTimer;
// screen saver stuff // screen saver stuff
CMSWindowsScreenSaver* m_screensaver; CMSWindowsScreenSaver* m_screensaver;
bool m_screensaverNotify; bool m_screensaverNotify;
// clipboard stuff // clipboard stuff. our window is used mainly as a clipboard
// owner and as a link in the clipboard viewer chain.
HWND m_window;
HWND m_nextClipboardWindow; HWND m_nextClipboardWindow;
bool m_ownClipboard; bool m_ownClipboard;
// the current desk and it's name // the current desk and it's name
HDESK m_desk; CDesk* m_activeDesk;
CString m_deskName; CString m_activeDeskName;
// true when the current desktop is inaccessible. while // one desk per desktop and a cond var to communicate with it
// the desktop is inaccessible we won't receive user input CMutex m_mutex;
// and we'll lose track of the keyboard state. when the CCondVar<bool> m_deskReady;
// desktop becomes accessible again we'll notify the event CDesks m_desks;
// handler of that.
bool m_inaccessibleDesktop;
// hook library stuff // hook library stuff
HINSTANCE m_hookLibrary; HINSTANCE m_hookLibrary;
@ -247,7 +267,6 @@ private:
SetModeFunc m_setMode; SetModeFunc m_setMode;
InstallScreenSaverFunc m_installScreensaver; InstallScreenSaverFunc m_installScreensaver;
UninstallScreenSaverFunc m_uninstallScreensaver; UninstallScreenSaverFunc m_uninstallScreensaver;
bool m_lowLevel;
// keyboard stuff // keyboard stuff
IKeyState* m_keyState; IKeyState* m_keyState;
@ -256,9 +275,6 @@ private:
// map of button state // map of button state
BYTE m_buttons[1 + kButtonExtra0 + 1]; BYTE m_buttons[1 + kButtonExtra0 + 1];
// stuff for hiding the cursor
DWORD m_cursorThread;
static CMSWindowsScreen* s_screen; static CMSWindowsScreen* s_screen;
}; };

View File

@ -76,13 +76,10 @@ static DWORD g_threadID = 0;
static HHOOK g_keyboard = NULL; static HHOOK g_keyboard = NULL;
static HHOOK g_mouse = NULL; static HHOOK g_mouse = NULL;
static HHOOK g_getMessage = NULL; static HHOOK g_getMessage = NULL;
static HANDLE g_hookThreadLL = NULL;
static DWORD g_hookThreadIDLL = 0;
static HANDLE g_hookEventLL = NULL;
static HHOOK g_keyboardLL = NULL; static HHOOK g_keyboardLL = NULL;
static HHOOK g_mouseLL = NULL; static HHOOK g_mouseLL = NULL;
static bool g_screenSaver = false; static bool g_screenSaver = false;
static EHookMode g_mode = kHOOK_WATCH_JUMP_ZONE; static EHookMode g_mode = kHOOK_DISABLE;
static UInt32 g_zoneSides = 0; static UInt32 g_zoneSides = 0;
static SInt32 g_zoneSize = 0; static SInt32 g_zoneSize = 0;
static SInt32 g_xScreen = 0; static SInt32 g_xScreen = 0;
@ -141,6 +138,7 @@ restoreCursor()
g_cursorThread = 0; g_cursorThread = 0;
} }
#if !NO_GRAB_KEYBOARD
static static
bool bool
keyboardHookHandler(WPARAM wParam, LPARAM lParam) keyboardHookHandler(WPARAM wParam, LPARAM lParam)
@ -170,6 +168,7 @@ keyboardHookHandler(WPARAM wParam, LPARAM lParam)
return false; return false;
} }
#endif
static static
bool bool
@ -231,7 +230,7 @@ mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data)
PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y); PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y);
return true; return true;
} }
else { else if (g_mode == kHOOK_WATCH_JUMP_ZONE) {
// check for mouse inside jump zone // check for mouse inside jump zone
bool inside = false; bool inside = false;
if (!inside && (g_zoneSides & kLeftMask) != 0) { if (!inside && (g_zoneSides & kLeftMask) != 0) {
@ -259,6 +258,7 @@ mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data)
return false; return false;
} }
#if !NO_GRAB_KEYBOARD
static static
LRESULT CALLBACK LRESULT CALLBACK
keyboardHook(int code, WPARAM wParam, LPARAM lParam) keyboardHook(int code, WPARAM wParam, LPARAM lParam)
@ -272,6 +272,7 @@ keyboardHook(int code, WPARAM wParam, LPARAM lParam)
return CallNextHookEx(g_keyboard, code, wParam, lParam); return CallNextHookEx(g_keyboard, code, wParam, lParam);
} }
#endif
static static
LRESULT CALLBACK LRESULT CALLBACK
@ -354,6 +355,7 @@ getMessageHook(int code, WPARAM wParam, LPARAM lParam)
// side, key repeats are not reported to us. // side, key repeats are not reported to us.
// //
#if !NO_GRAB_KEYBOARD
static static
LRESULT CALLBACK LRESULT CALLBACK
keyboardLLHook(int code, WPARAM wParam, LPARAM lParam) keyboardLLHook(int code, WPARAM wParam, LPARAM lParam)
@ -385,6 +387,7 @@ keyboardLLHook(int code, WPARAM wParam, LPARAM lParam)
return CallNextHookEx(g_keyboardLL, code, wParam, lParam); return CallNextHookEx(g_keyboardLL, code, wParam, lParam);
} }
#endif
// //
// low-level mouse hook -- this allows us to capture and handle mouse // low-level mouse hook -- this allows us to capture and handle mouse
@ -411,92 +414,6 @@ mouseLLHook(int code, WPARAM wParam, LPARAM lParam)
return CallNextHookEx(g_mouseLL, code, wParam, lParam); return CallNextHookEx(g_mouseLL, code, wParam, lParam);
} }
static
DWORD WINAPI
getLowLevelProc(void*)
{
// thread proc for low-level keyboard/mouse hooks. this does
// nothing but install the hook, process events, and uninstall
// the hook.
// force this thread to have a message queue
MSG msg;
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
#if !NO_GRAB_KEYBOARD
// install low-level keyboard hook
g_keyboardLL = SetWindowsHookEx(WH_KEYBOARD_LL,
&keyboardLLHook,
g_hinstance,
0);
if (g_keyboardLL == NULL) {
// indicate failure and exit
g_hookThreadIDLL = 0;
SetEvent(g_hookEventLL);
return 1;
}
#else
// keep compiler quiet
&keyboardLLHook;
#endif
// install low-level mouse hook
g_mouseLL = SetWindowsHookEx(WH_MOUSE_LL,
&mouseLLHook,
g_hinstance,
0);
if (g_mouseLL == NULL) {
// indicate failure and exit
if (g_keyboardLL != NULL) {
UnhookWindowsHookEx(g_keyboardLL);
g_keyboardLL = NULL;
}
g_hookThreadIDLL = 0;
SetEvent(g_hookEventLL);
return 1;
}
// ready
SetEvent(g_hookEventLL);
// message loop
bool done = false;
while (!done) {
switch (GetMessage(&msg, NULL, 0, 0)) {
case -1:
break;
case 0:
done = true;
break;
default:
TranslateMessage(&msg);
DispatchMessage(&msg);
break;
}
}
// uninstall hook
UnhookWindowsHookEx(g_mouseLL);
UnhookWindowsHookEx(g_keyboardLL);
g_mouseLL = NULL;
g_keyboardLL = NULL;
return 0;
}
#else // (_WIN32_WINNT < 0x0400)
static
DWORD WINAPI
getLowLevelProc(void*)
{
g_hookThreadIDLL = 0;
SetEvent(g_hookEventLL);
return 1;
}
#endif #endif
static static
@ -559,12 +476,8 @@ DllMain(HINSTANCE instance, DWORD reason, LPVOID)
} }
else if (reason == DLL_PROCESS_DETACH) { else if (reason == DLL_PROCESS_DETACH) {
if (g_processID == GetCurrentProcessId()) { if (g_processID == GetCurrentProcessId()) {
if (g_keyboard != NULL || uninstall();
g_mouse != NULL || uninstallScreenSaver();
g_getMessage != NULL) {
uninstall();
uninstallScreenSaver();
}
g_processID = 0; g_processID = 0;
g_hinstance = NULL; g_hinstance = NULL;
} }
@ -601,9 +514,6 @@ init(DWORD threadID)
g_keyboard = NULL; g_keyboard = NULL;
g_mouse = NULL; g_mouse = NULL;
g_getMessage = NULL; g_getMessage = NULL;
g_hookThreadLL = NULL;
g_hookThreadIDLL = 0;
g_hookEventLL = NULL;
g_keyboardLL = NULL; g_keyboardLL = NULL;
g_mouseLL = NULL; g_mouseLL = NULL;
g_screenSaver = false; g_screenSaver = false;
@ -614,7 +524,7 @@ init(DWORD threadID)
g_threadID = threadID; g_threadID = threadID;
// set defaults // set defaults
g_mode = kHOOK_WATCH_JUMP_ZONE; g_mode = kHOOK_DISABLE;
g_zoneSides = 0; g_zoneSides = 0;
g_zoneSize = 0; g_zoneSize = 0;
g_xScreen = 0; g_xScreen = 0;
@ -663,75 +573,51 @@ install()
0); 0);
} }
// install low-level keyboard/mouse hooks, if possible. since these // install keyboard hook
// hooks are called in the context of the installing thread and that
// thread must have a message loop but we don't want the caller's
// message loop to do the work, we'll fire up a separate thread
// just for the hooks. note that low-level hooks are only available
// on windows NT SP3 and above.
g_hookEventLL = CreateEvent(NULL, TRUE, FALSE, NULL);
if (g_hookEventLL != NULL) {
g_hookThreadLL = CreateThread(NULL, 0, &getLowLevelProc, 0,
CREATE_SUSPENDED, &g_hookThreadIDLL);
if (g_hookThreadLL != NULL) {
// start the thread and wait for it to initialize
ResumeThread(g_hookThreadLL);
WaitForSingleObject(g_hookEventLL, INFINITE);
ResetEvent(g_hookEventLL);
// the thread clears g_hookThreadIDLL if it failed
if (g_hookThreadIDLL == 0) {
CloseHandle(g_hookThreadLL);
g_hookThreadLL = NULL;
}
}
if (g_hookThreadLL == NULL) {
CloseHandle(g_hookEventLL);
g_hookEventLL = NULL;
}
}
// install non-low-level hooks if the low-level hooks are not installed
if (g_hookThreadLL == NULL) {
#if !NO_GRAB_KEYBOARD #if !NO_GRAB_KEYBOARD
#if (_WIN32_WINNT >= 0x0400)
g_keyboardLL = SetWindowsHookEx(WH_KEYBOARD_LL,
&keyboardLLHook,
g_hinstance,
0);
#endif
if (g_keyboardLL == NULL) {
g_keyboard = SetWindowsHookEx(WH_KEYBOARD, g_keyboard = SetWindowsHookEx(WH_KEYBOARD,
&keyboardHook, &keyboardHook,
g_hinstance, g_hinstance,
0); 0);
#else }
// keep compiler quiet
&keyboardHook;
#endif #endif
// install mouse hook
#if (_WIN32_WINNT >= 0x0400)
g_mouseLL = SetWindowsHookEx(WH_MOUSE_LL,
&mouseLLHook,
g_hinstance,
0);
#endif
if (g_mouseLL == NULL) {
g_mouse = SetWindowsHookEx(WH_MOUSE, g_mouse = SetWindowsHookEx(WH_MOUSE,
&mouseHook, &mouseHook,
g_hinstance, g_hinstance,
0); 0);
} }
// check for any failures. uninstall all hooks on failure. // check that we got all the hooks we wanted
if (g_hookThreadLL == NULL && if ((g_getMessage == NULL && g_wheelSupport == kWheelOld) ||
#if !NO_GRAB_KEYBOARD #if !NO_GRAB_KEYBOARD
(g_keyboard == NULL || g_mouse == NULL)) { (g_keyboardLL == NULL && g_keyboard == NULL) ||
#else
(g_mouse == NULL)) {
#endif #endif
if (g_keyboard != NULL) { (g_mouseLL == NULL && g_mouse == NULL)) {
UnhookWindowsHookEx(g_keyboard); uninstall();
g_keyboard = NULL;
}
if (g_mouse != NULL) {
UnhookWindowsHookEx(g_mouse);
g_mouse = NULL;
}
if (g_getMessage != NULL && !g_screenSaver) {
UnhookWindowsHookEx(g_getMessage);
g_getMessage = NULL;
}
g_threadID = NULL;
return kHOOK_FAILED; return kHOOK_FAILED;
} }
return (g_hookThreadLL == NULL) ? kHOOK_OKAY : kHOOK_OKAY_LL; if (g_keyboardLL != NULL || g_mouseLL != NULL) {
return kHOOK_OKAY_LL;
}
return kHOOK_OKAY;
} }
int int
@ -740,14 +626,13 @@ uninstall(void)
assert(g_hinstance != NULL); assert(g_hinstance != NULL);
// uninstall hooks // uninstall hooks
if (g_hookThreadLL != NULL) { if (g_keyboardLL != NULL) {
PostThreadMessage(g_hookThreadIDLL, WM_QUIT, 0, 0); UnhookWindowsHookEx(g_keyboardLL);
WaitForSingleObject(g_hookThreadLL, INFINITE); g_keyboardLL = NULL;
CloseHandle(g_hookEventLL); }
CloseHandle(g_hookThreadLL); if (g_mouseLL != NULL) {
g_hookEventLL = NULL; UnhookWindowsHookEx(g_mouseLL);
g_hookThreadLL = NULL; g_mouseLL = NULL;
g_hookThreadIDLL = 0;
} }
if (g_keyboard != NULL) { if (g_keyboard != NULL) {
UnhookWindowsHookEx(g_keyboard); UnhookWindowsHookEx(g_keyboard);

View File

@ -40,6 +40,7 @@
#define SYNERGY_MSG_SCREEN_SAVER WM_APP + 0x0018 // activated; <unused> #define SYNERGY_MSG_SCREEN_SAVER WM_APP + 0x0018 // activated; <unused>
#define SYNERGY_MSG_INPUT_FIRST SYNERGY_MSG_KEY #define SYNERGY_MSG_INPUT_FIRST SYNERGY_MSG_KEY
#define SYNERGY_MSG_INPUT_LAST SYNERGY_MSG_PRE_WARP #define SYNERGY_MSG_INPUT_LAST SYNERGY_MSG_PRE_WARP
#define SYNERGY_HOOK_LAST_MSG SYNERGY_MSG_SCREEN_SAVER
extern "C" { extern "C" {
@ -50,6 +51,7 @@ enum EHookResult {
}; };
enum EHookMode { enum EHookMode {
kHOOK_DISABLE,
kHOOK_WATCH_JUMP_ZONE, kHOOK_WATCH_JUMP_ZONE,
kHOOK_RELAY_EVENTS kHOOK_RELAY_EVENTS
}; };

View File

@ -740,7 +740,7 @@ CXWindowsClipboard::motifFillCache()
// save it // save it
motifFormats.insert(std::make_pair(motifFormat->m_type, data)); motifFormats.insert(std::make_pair(motifFormat->m_type, data));
} }
const UInt32 numMotifFormats = motifFormats.size(); //const UInt32 numMotifFormats = motifFormats.size();
// try each converter in order (because they're in order of // try each converter in order (because they're in order of
// preference). // preference).

View File

@ -143,6 +143,7 @@ CXWindowsEventQueueBuffer::addEvent(UInt32 dataID)
// force waitForEvent() to return // force waitForEvent() to return
XFlush(m_display); XFlush(m_display);
return true;
} }
bool bool
@ -154,7 +155,7 @@ CXWindowsEventQueueBuffer::isEmpty() const
CEventQueueTimer* CEventQueueTimer*
CXWindowsEventQueueBuffer::newTimer(double, bool) const CXWindowsEventQueueBuffer::newTimer(double, bool) const
{ {
return new CEventQueueTimer(); return new CEventQueueTimer;
} }
void void

View File

@ -308,8 +308,8 @@ CXWindowsKeyMapper::updateKeysymMap(Display* display, IKeyState* keyState)
} }
// get modifier map from server // get modifier map from server
XModifierKeymap* modifiers = XGetModifierMapping(display); XModifierKeymap* modifiers = XGetModifierMapping(display);
int keysPerModifier = modifiers->max_keypermod; unsigned int keysPerModifier = modifiers->max_keypermod;
// clear state // clear state
m_keysymMap.clear(); m_keysymMap.clear();
@ -330,7 +330,6 @@ CXWindowsKeyMapper::updateKeysymMap(Display* display, IKeyState* keyState)
// the keysym map. also collect all keycodes for each modifier. // the keysym map. also collect all keycodes for each modifier.
for (unsigned int i = 0; i < 8; ++i) { for (unsigned int i = 0; i < 8; ++i) {
// no keycodes for this modifier yet // no keycodes for this modifier yet
bool hasKeycode = false;
KeyModifierMask mask = 0; KeyModifierMask mask = 0;
IKeyState::KeyButtons modifierKeys; IKeyState::KeyButtons modifierKeys;

View File

@ -128,8 +128,8 @@ CXWindowsScreen::CXWindowsScreen(bool isPrimary) :
m_isOnScreen(m_isPrimary), m_isOnScreen(m_isPrimary),
m_x(0), m_y(0), m_x(0), m_y(0),
m_w(0), m_h(0), m_w(0), m_h(0),
m_xCursor(0), m_yCursor(0),
m_xCenter(0), m_yCenter(0), m_xCenter(0), m_yCenter(0),
m_xCursor(0), m_yCursor(0),
m_keyState(NULL), m_keyState(NULL),
m_keyMapper(), m_keyMapper(),
m_im(NULL), m_im(NULL),

View File

@ -28,6 +28,7 @@ EXTRA_DIST = \
CMSWindowsKeyMapper.cpp \ CMSWindowsKeyMapper.cpp \
CMSWindowsScreen.cpp \ CMSWindowsScreen.cpp \
CMSWindowsScreenSaver.cpp \ CMSWindowsScreenSaver.cpp \
CMSWindowsUtil.cpp \
CSynergyHook.cpp \ CSynergyHook.cpp \
CMSWindowsClipboard.h \ CMSWindowsClipboard.h \
CMSWindowsClipboardAnyTextConverter.h \ CMSWindowsClipboardAnyTextConverter.h \
@ -38,6 +39,7 @@ EXTRA_DIST = \
CMSWindowsKeyMapper.h \ CMSWindowsKeyMapper.h \
CMSWindowsScreen.h \ CMSWindowsScreen.h \
CMSWindowsScreenSaver.h \ CMSWindowsScreenSaver.h \
CMSWindowsUtil.h \
CSynergyHook.h \ CSynergyHook.h \
$(NULL) $(NULL)

View File

@ -121,6 +121,10 @@ SOURCE=.\CMSWindowsScreen.cpp
SOURCE=.\CMSWindowsScreenSaver.cpp SOURCE=.\CMSWindowsScreenSaver.cpp
# End Source File # End Source File
# Begin Source File
SOURCE=.\CMSWindowsUtil.cpp
# End Source File
# End Group # End Group
# Begin Group "Header Files" # Begin Group "Header Files"
@ -161,6 +165,10 @@ SOURCE=.\CMSWindowsScreen.h
SOURCE=.\CMSWindowsScreenSaver.h SOURCE=.\CMSWindowsScreenSaver.h
# End Source File # End Source File
# Begin Source File
SOURCE=.\CMSWindowsUtil.h
# End Source File
# End Group # End Group
# End Target # End Target
# End Project # End Project

View File

@ -47,13 +47,13 @@ CClientListener::CClientListener(const CNetworkAddress& address,
LOG((CLOG_DEBUG1 "binding listen socket")); LOG((CLOG_DEBUG1 "binding listen socket"));
m_listen->bind(address); m_listen->bind(address);
} }
catch (XSocketAddressInUse& e) { catch (XSocketAddressInUse&) {
delete m_listen; delete m_listen;
delete m_socketFactory; delete m_socketFactory;
delete m_streamFilterFactory; delete m_streamFilterFactory;
throw; throw;
} }
catch (XBase& e) { catch (XBase&) {
delete m_listen; delete m_listen;
delete m_socketFactory; delete m_socketFactory;
delete m_streamFilterFactory; delete m_streamFilterFactory;

View File

@ -37,8 +37,8 @@ CEvent::Type CServer::s_errorEvent = CEvent::kUnknown;
CEvent::Type CServer::s_disconnectedEvent = CEvent::kUnknown; CEvent::Type CServer::s_disconnectedEvent = CEvent::kUnknown;
CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) : CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) :
m_active(primaryClient),
m_primaryClient(primaryClient), m_primaryClient(primaryClient),
m_active(primaryClient),
m_seqNum(0), m_seqNum(0),
m_config(config), m_config(config),
m_activeSaver(NULL), m_activeSaver(NULL),
@ -117,9 +117,6 @@ CServer::CServer(const CConfig& config, CPrimaryClient* primaryClient) :
// add connection // add connection
addClient(m_primaryClient); addClient(m_primaryClient);
// tell it about the active sides
m_primaryClient->reconfigure(getActivePrimarySides());
// tell primary client about its options // tell primary client about its options
sendOptions(m_primaryClient); sendOptions(m_primaryClient);
@ -171,8 +168,7 @@ bool
CServer::setConfig(const CConfig& config) CServer::setConfig(const CConfig& config)
{ {
// refuse configuration if it doesn't include the primary screen // refuse configuration if it doesn't include the primary screen
if (m_primaryClient != NULL && if (!config.isScreen(m_primaryClient->getName())) {
!config.isScreen(m_primaryClient->getName())) {
return false; return false;
} }
@ -185,9 +181,7 @@ CServer::setConfig(const CConfig& config)
processOptions(); processOptions();
// tell primary screen about reconfiguration // tell primary screen about reconfiguration
if (m_primaryClient != NULL) { m_primaryClient->reconfigure(getActivePrimarySides());
m_primaryClient->reconfigure(getActivePrimarySides());
}
// tell all (connected) clients about current options // tell all (connected) clients about current options
for (CClientList::const_iterator index = m_clients.begin(); for (CClientList::const_iterator index = m_clients.begin();
@ -296,18 +290,17 @@ CServer::getName(const IClient* client) const
UInt32 UInt32
CServer::getActivePrimarySides() const CServer::getActivePrimarySides() const
{ {
CString primaryName = getName(m_primaryClient);
UInt32 sides = 0; UInt32 sides = 0;
if (!m_config.getNeighbor(primaryName, kLeft).empty()) { if (getNeighbor(m_primaryClient, kLeft) != NULL) {
sides |= kLeftMask; sides |= kLeftMask;
} }
if (!m_config.getNeighbor(primaryName, kRight).empty()) { if (getNeighbor(m_primaryClient, kRight) != NULL) {
sides |= kRightMask; sides |= kRightMask;
} }
if (!m_config.getNeighbor(primaryName, kTop).empty()) { if (getNeighbor(m_primaryClient, kTop) != NULL) {
sides |= kTopMask; sides |= kTopMask;
} }
if (!m_config.getNeighbor(primaryName, kBottom).empty()) { if (getNeighbor(m_primaryClient, kBottom) != NULL) {
sides |= kBottomMask; sides |= kBottomMask;
} }
return sides; return sides;
@ -546,6 +539,10 @@ CServer::getNeighbor(IClient* src,
assert(lastGoodScreen != NULL); assert(lastGoodScreen != NULL);
y += dy; y += dy;
break; break;
case kNoDirection:
assert(0 && "bad direction");
return NULL;
} }
// save destination screen // save destination screen
@ -582,6 +579,10 @@ CServer::getNeighbor(IClient* src,
y < dy + getJumpZoneSize(dst)) y < dy + getJumpZoneSize(dst))
y = dy + getJumpZoneSize(dst); y = dy + getJumpZoneSize(dst);
break; break;
case kNoDirection:
assert(0 && "bad direction");
return NULL;
} }
} }
@ -623,6 +624,10 @@ CServer::getNeighbor(IClient* src,
} }
x += dx; x += dx;
break; break;
case kNoDirection:
assert(0 && "bad direction");
return NULL;
} }
return dst; return dst;
@ -999,13 +1004,13 @@ CServer::handleWheelEvent(const CEvent& event, void*)
} }
void void
CServer::handleScreensaverActivatedEvent(const CEvent& event, void*) CServer::handleScreensaverActivatedEvent(const CEvent&, void*)
{ {
onScreensaver(true); onScreensaver(true);
} }
void void
CServer::handleScreensaverDeactivatedEvent(const CEvent& event, void*) CServer::handleScreensaverDeactivatedEvent(const CEvent&, void*)
{ {
onScreensaver(false); onScreensaver(false);
} }
@ -1212,7 +1217,6 @@ CServer::onMouseMovePrimary(SInt32 x, SInt32 y)
LOG((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y)); LOG((CLOG_DEBUG2 "onMouseMovePrimary %d,%d", x, y));
// mouse move on primary (server's) screen // mouse move on primary (server's) screen
assert(m_primaryClient != NULL);
assert(m_active == m_primaryClient); assert(m_active == m_primaryClient);
// save position // save position
@ -1429,6 +1433,10 @@ CServer::addClient(IClient* client)
// add to list // add to list
m_clientSet.insert(client); m_clientSet.insert(client);
m_clients.insert(std::make_pair(name, client)); m_clients.insert(std::make_pair(name, client));
// tell primary client about the active sides
m_primaryClient->reconfigure(getActivePrimarySides());
return true; return true;
} }
@ -1452,6 +1460,7 @@ CServer::removeClient(IClient* client)
// remove from list // remove from list
m_clients.erase(i); m_clients.erase(i);
m_clientSet.erase(client); m_clientSet.erase(client);
return true; return true;
} }
@ -1477,7 +1486,7 @@ CServer::closeClient(IClient* client, const char* msg)
// install timer. wait timeout seconds for client to close. // install timer. wait timeout seconds for client to close.
double timeout = 5.0; double timeout = 5.0;
CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(5.0, NULL); CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(timeout, NULL);
EVENTQUEUE->adoptHandler(CEvent::kTimer, timer, EVENTQUEUE->adoptHandler(CEvent::kTimer, timer,
new TMethodEventJob<CServer>(this, new TMethodEventJob<CServer>(this,
&CServer::handleClientCloseTimeout, client)); &CServer::handleClientCloseTimeout, client));
@ -1577,6 +1586,9 @@ CServer::forceLeaveClient(IClient* client)
if (m_activeSaver == client) { if (m_activeSaver == client) {
m_activeSaver = NULL; m_activeSaver = NULL;
} }
// tell primary client about the active sides
m_primaryClient->reconfigure(getActivePrimarySides());
} }

View File

@ -89,7 +89,7 @@ CProtocolUtil::vwritef(IStream* stream,
} }
} }
bool void
CProtocolUtil::vreadf(IStream* stream, const char* fmt, va_list args) CProtocolUtil::vreadf(IStream* stream, const char* fmt, va_list args)
{ {
assert(stream != NULL); assert(stream != NULL);

View File

@ -71,7 +71,7 @@ public:
private: private:
static void vwritef(IStream*, static void vwritef(IStream*,
const char* fmt, UInt32 size, va_list); const char* fmt, UInt32 size, va_list);
static bool vreadf(IStream*, static void vreadf(IStream*,
const char* fmt, va_list); const char* fmt, va_list);
static UInt32 getLength(const char* fmt, va_list); static UInt32 getLength(const char* fmt, va_list);

View File

@ -27,8 +27,8 @@ CScreen::CScreen(IPlatformScreen* platformScreen) :
m_isPrimary(platformScreen->isPrimary()), m_isPrimary(platformScreen->isPrimary()),
m_enabled(false), m_enabled(false),
m_entered(m_isPrimary), m_entered(m_isPrimary),
m_toggleKeys(0), m_screenSaverSync(true),
m_screenSaverSync(true) m_toggleKeys(0)
{ {
assert(m_screen != NULL); assert(m_screen != NULL);
@ -376,7 +376,7 @@ CScreen::setOptions(const COptionsList& options)
void void
CScreen::setSequenceNumber(UInt32 seqNum) CScreen::setSequenceNumber(UInt32 seqNum)
{ {
return m_screen->setSequenceNumber(seqNum); m_screen->setSequenceNumber(seqNum);
} }
bool bool

View File

@ -73,8 +73,7 @@ public:
//! Get shape changed event type //! Get shape changed event type
/*! /*!
Returns the shape changed event type. This is sent whenever the Returns the shape changed event type. This is sent whenever the
screen's shape changes, the cursor center moves, or the jump zone screen's shape changes.
size changes.
*/ */
static CEvent::Type getShapeChangedEvent(); static CEvent::Type getShapeChangedEvent();