moving 1.4 to trunk

This commit is contained in:
Nick Bolton
2012-06-10 16:50:54 +00:00
parent cdeb3a7824
commit 488241850c
1291 changed files with 425650 additions and 12 deletions

25
src/lib/CMakeLists.txt Normal file
View File

@@ -0,0 +1,25 @@
# synergy -- mouse and keyboard sharing utility
# Copyright (C) 2009 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
add_subdirectory(arch)
add_subdirectory(base)
add_subdirectory(client)
add_subdirectory(common)
add_subdirectory(io)
add_subdirectory(mt)
add_subdirectory(net)
add_subdirectory(platform)
add_subdirectory(server)
add_subdirectory(synergy)

54
src/lib/arch/CArch.cpp Normal file
View File

@@ -0,0 +1,54 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArch.h"
//
// CArch
//
CArch* CArch::s_instance = NULL;
CArch::CArch()
{
}
CArch::~CArch()
{
}
void
CArch::init()
{
// initialization that requires ARCH is done here.
ARCH_NETWORK::init();
#if SYSAPI_WIN32
ARCH_TASKBAR::init();
CArchMiscWindows::init();
#endif
}
CArch*
CArch::getInstance()
{
if (s_instance == NULL) {
s_instance = new CArch();
s_instance->init();
}
return s_instance;
}

143
src/lib/arch/CArch.h Normal file
View File

@@ -0,0 +1,143 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// TODO: consider whether or not to use either encapsulation (as below)
// or inheritance (as it is now) for the ARCH stuff.
//
// case for encapsulation:
// pros:
// - compiler errors for missing pv implementations are not absolutely bonkers.
// - function names don't have to be so verbose.
// - easier to understand and debug.
// - ctors in IArch implementations can call other implementations.
// cons:
// - slightly more code for calls to ARCH.
// - you'll have to modify each ARCH call.
//
// also, we may want to consider making each encapsulated
// class lazy-loaded so that apps like the daemon don't load
// stuff when they don't need it.
#ifndef CARCH_H
#define CARCH_H
#include "common.h"
#if SYSAPI_WIN32
# include "CArchConsoleWindows.h"
# include "CArchDaemonWindows.h"
# include "CArchFileWindows.h"
# include "CArchLogWindows.h"
# include "CArchIpcLogWindows.h"
# include "CArchMiscWindows.h"
# include "CArchMultithreadWindows.h"
# include "CArchNetworkWinsock.h"
# include "CArchSleepWindows.h"
# include "CArchStringWindows.h"
# include "CArchSystemWindows.h"
# include "CArchTaskBarWindows.h"
# include "CArchTimeWindows.h"
# include "CArchPluginWindows.h"
#elif SYSAPI_UNIX
# include "CArchConsoleUnix.h"
# include "CArchDaemonUnix.h"
# include "CArchFileUnix.h"
# include "CArchLogUnix.h"
# include "CArchIpcLogUnix.h"
# if HAVE_PTHREAD
# include "CArchMultithreadPosix.h"
# endif
# include "CArchNetworkBSD.h"
# include "CArchSleepUnix.h"
# include "CArchStringUnix.h"
# include "CArchSystemUnix.h"
# include "CArchTaskBarXWindows.h"
# include "CArchTimeUnix.h"
# include "CArchPluginUnix.h"
#endif
/*!
\def ARCH
This macro evaluates to the singleton CArch object.
*/
#define ARCH (CArch::getInstance())
//! Delegating implementation of architecture dependent interfaces
/*!
This class is a centralized interface to all architecture dependent
interface implementations (except miscellaneous functions). It
instantiates an implementation of each interface and delegates calls
to each method to those implementations. Clients should use the
\c ARCH macro to access this object. Clients must also instantiate
exactly one of these objects before attempting to call any method,
typically at the beginning of \c main().
*/
class CArch : public ARCH_CONSOLE,
public ARCH_DAEMON,
public ARCH_FILE,
public ARCH_LOG,
public ARCH_MULTITHREAD,
public ARCH_NETWORK,
public ARCH_SLEEP,
public ARCH_STRING,
public ARCH_SYSTEM,
public ARCH_TASKBAR,
public ARCH_TIME {
public:
~CArch();
//
// accessors
//
//! Return the singleton instance
/*!
The client must have instantiated exactly once CArch object before
calling this function.
*/
static CArch* getInstance();
ARCH_IPC_LOG& ipcLog() const { return (ARCH_IPC_LOG&)m_ipcLog; }
ARCH_PLUGIN& plugin() const { return (ARCH_PLUGIN&)m_plugin; }
private:
CArch();
void init();
private:
static CArch* s_instance;
ARCH_IPC_LOG m_ipcLog;
ARCH_PLUGIN m_plugin;
};
//! Convenience object to lock/unlock an arch mutex
class CArchMutexLock {
public:
CArchMutexLock(CArchMutex mutex) : m_mutex(mutex)
{
ARCH->lockMutex(m_mutex);
}
~CArchMutexLock()
{
ARCH->unlockMutex(m_mutex);
}
private:
CArchMutex m_mutex;
};
#endif

View File

@@ -0,0 +1,31 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchConsoleStd.h"
#include "CLog.h"
#include <iostream>
void
CArchConsoleStd::writeConsole(ELevel level, const char* str)
{
if ((level >= kFATAL) && (level <= kWARNING))
std::cerr << str << std::endl;
else
std::cout << str << std::endl;
std::cout.flush();
}

View File

@@ -0,0 +1,33 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "IArchConsole.h"
//! Cross platform implementation of IArchConsole
class CArchConsoleStd : public IArchConsole {
public:
CArchConsoleStd() { }
virtual ~CArchConsoleStd() { }
// IArchConsole overrides
virtual void openConsole(const char* title) { }
virtual void closeConsole() { }
virtual void showConsole(bool) { }
virtual void writeConsole(ELevel level, const char*);
};

View File

@@ -0,0 +1,22 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchConsoleUnix.h"
CArchConsoleUnix::CArchConsoleUnix() { }
CArchConsoleUnix::~CArchConsoleUnix() { }

View File

@@ -0,0 +1,28 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "CArchConsoleStd.h"
#define ARCH_CONSOLE CArchConsoleUnix
class CArchConsoleUnix : public CArchConsoleStd {
public:
CArchConsoleUnix();
virtual ~CArchConsoleUnix();
};

View File

@@ -0,0 +1,22 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchConsoleWindows.h"
CArchConsoleWindows::CArchConsoleWindows() { }
CArchConsoleWindows::~CArchConsoleWindows() { }

View File

@@ -0,0 +1,28 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "CArchConsoleStd.h"
#define ARCH_CONSOLE CArchConsoleWindows
class CArchConsoleWindows : public CArchConsoleStd {
public:
CArchConsoleWindows();
virtual ~CArchConsoleWindows();
};

View File

@@ -0,0 +1,85 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchDaemonNone.h"
//
// CArchDaemonNone
//
CArchDaemonNone::CArchDaemonNone()
{
// do nothing
}
CArchDaemonNone::~CArchDaemonNone()
{
// do nothing
}
void
CArchDaemonNone::installDaemon(const char*,
const char*,
const char*,
const char*,
const char*,
bool)
{
// do nothing
}
void
CArchDaemonNone::uninstallDaemon(const char*, bool)
{
// do nothing
}
int
CArchDaemonNone::daemonize(const char* name, DaemonFunc func)
{
// simply forward the call to func. obviously, this doesn't
// do any daemonizing.
return func(1, &name);
}
bool
CArchDaemonNone::canInstallDaemon(const char*, bool)
{
return false;
}
bool
CArchDaemonNone::isDaemonInstalled(const char*, bool)
{
return false;
}
void
CArchDaemonNone::installDaemon()
{
}
void
CArchDaemonNone::uninstallDaemon()
{
}
std::string
CArchDaemonNone::commandLine() const
{
return "";
}

View File

@@ -0,0 +1,53 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHDAEMONNONE_H
#define CARCHDAEMONNONE_H
#include "IArchDaemon.h"
#define ARCH_DAEMON CArchDaemonNone
//! Dummy implementation of IArchDaemon
/*!
This class implements IArchDaemon for a platform that does not have
daemons. The install and uninstall functions do nothing, the query
functions return false, and \c daemonize() simply calls the passed
function and returns its result.
*/
class CArchDaemonNone : public IArchDaemon {
public:
CArchDaemonNone();
virtual ~CArchDaemonNone();
// IArchDaemon overrides
virtual void installDaemon(const char* name,
const char* description,
const char* pathname,
const char* commandLine,
const char* dependencies,
bool allUsers);
virtual void uninstallDaemon(const char* name, bool allUsers);
virtual int daemonize(const char* name, DaemonFunc func);
virtual bool canInstallDaemon(const char* name, bool allUsers);
virtual bool isDaemonInstalled(const char* name, bool allUsers);
virtual void installDaemon();
virtual void uninstallDaemon();
virtual std::string commandLine() const;
};
#endif

View File

@@ -0,0 +1,127 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchDaemonUnix.h"
#include "XArchUnix.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <cstdlib>
#include "CLog.h"
//
// CArchDaemonUnix
//
CArchDaemonUnix::CArchDaemonUnix()
{
// do nothing
}
CArchDaemonUnix::~CArchDaemonUnix()
{
// do nothing
}
#ifdef __APPLE__
// In Mac OS X, fork()'d child processes can't use most APIs (the frameworks
// that Synergy uses in fact prevent it and make the process just up and die),
// so need to exec a copy of the program that doesn't fork so isn't limited.
int
execSelfNonDaemonized()
{
extern char** NXArgv;
char** selfArgv = NXArgv;
setenv("_SYNERGY_DAEMONIZED", "", 1);
execvp(selfArgv[0], selfArgv);
return 0;
}
bool alreadyDaemonized() {
return getenv("_SYNERGY_DAEMONIZED") != NULL;
}
#endif
int
CArchDaemonUnix::daemonize(const char* name, DaemonFunc func)
{
#ifdef __APPLE__
if (alreadyDaemonized())
return func(1, &name);
#endif
// fork so shell thinks we're done and so we're not a process
// group leader
switch (fork()) {
case -1:
// failed
throw XArchDaemonFailed(new XArchEvalUnix(errno));
case 0:
// child
break;
default:
// parent exits
exit(0);
}
// become leader of a new session
setsid();
#ifndef __APPLE__
// NB: don't run chdir on apple; causes strange behaviour.
// chdir to root so we don't keep mounted filesystems points busy
// TODO: this is a bit of a hack - can we find a better solution?
int chdirErr = chdir("/");
if (chdirErr)
// NB: file logging actually isn't working at this point!
LOG((CLOG_ERR "chdir error: %i", chdirErr));
#endif
// mask off permissions for any but owner
umask(077);
// close open files. we only expect stdin, stdout, stderr to be open.
close(0);
close(1);
close(2);
// attach file descriptors 0, 1, 2 to /dev/null so inadvertent use
// of standard I/O safely goes in the bit bucket.
open("/dev/null", O_RDONLY);
open("/dev/null", O_RDWR);
int dupErr = dup(1);
if (dupErr)
// NB: file logging actually isn't working at this point!
LOG((CLOG_ERR "dup error: %i", dupErr));
#ifdef __APPLE__
return execSelfNonDaemonized();
#endif
// invoke function
return func(1, &name);
}

View File

@@ -0,0 +1,38 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHDAEMONUNIX_H
#define CARCHDAEMONUNIX_H
#include "CArchDaemonNone.h"
#undef ARCH_DAEMON
#define ARCH_DAEMON CArchDaemonUnix
//! Unix implementation of IArchDaemon
class CArchDaemonUnix : public CArchDaemonNone {
public:
CArchDaemonUnix();
virtual ~CArchDaemonUnix();
// IArchDaemon overrides
virtual int daemonize(const char* name, DaemonFunc func);
};
#define CONFIG_FILE "/etc/synergy/synergyd.conf"
#endif

View File

@@ -0,0 +1,841 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchDaemonWindows.h"
#include "CArch.h"
#include "CArchMiscWindows.h"
#include "XArchWindows.h"
#include "stdvector.h"
//
// CArchDaemonWindows
//
CArchDaemonWindows* CArchDaemonWindows::s_daemon = NULL;
CArchDaemonWindows::CArchDaemonWindows() :
m_daemonThreadID(0)
{
m_quitMessage = RegisterWindowMessage("SynergyDaemonExit");
}
CArchDaemonWindows::~CArchDaemonWindows()
{
// do nothing
}
int
CArchDaemonWindows::runDaemon(RunFunc runFunc)
{
assert(s_daemon != NULL);
return s_daemon->doRunDaemon(runFunc);
}
void
CArchDaemonWindows::daemonRunning(bool running)
{
// if s_daemon is NULL we assume we're running on the windows
// 95 family and we just ignore this call so the caller doesn't
// have to go through the trouble of not calling it on the
// windows 95 family.
if (s_daemon != NULL) {
s_daemon->doDaemonRunning(running);
}
}
UINT
CArchDaemonWindows::getDaemonQuitMessage()
{
if (s_daemon != NULL) {
return s_daemon->doGetDaemonQuitMessage();
}
else {
return 0;
}
}
void
CArchDaemonWindows::daemonFailed(int result)
{
// if s_daemon is NULL we assume we're running on the windows
// 95 family and we just ignore this call so the caller doesn't
// have to go through the trouble of not calling it on the
// windows 95 family.
if (s_daemon != NULL) {
throw XArchDaemonRunFailed(result);
}
}
void
CArchDaemonWindows::installDaemon(const char* name,
const char* description,
const char* pathname,
const char* commandLine,
const char* dependencies,
bool allUsers)
{
// if not for all users then use the user's autostart registry.
// key. if windows 95 family then use windows 95 services key.
if (!allUsers || CArchMiscWindows::isWindows95Family()) {
// open registry
HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ?
open95ServicesKey() : openUserStartupKey();
if (key == NULL) {
// can't open key
throw XArchDaemonInstallFailed(new XArchEvalWindows);
}
// construct entry
std::string value;
value += "\"";
value += pathname;
value += "\" ";
value += commandLine;
// install entry
CArchMiscWindows::setValue(key, name, value);
// clean up
CArchMiscWindows::closeKey(key);
}
// windows NT family services
else {
// open service manager
SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
if (mgr == NULL) {
// can't open service manager
throw XArchDaemonInstallFailed(new XArchEvalWindows);
}
// create the service
SC_HANDLE service = CreateService(mgr,
name,
name,
0,
SERVICE_WIN32_OWN_PROCESS |
SERVICE_INTERACTIVE_PROCESS,
SERVICE_AUTO_START,
SERVICE_ERROR_NORMAL,
pathname,
NULL,
NULL,
dependencies,
NULL,
NULL);
if (service == NULL) {
// can't create service
DWORD err = GetLastError();
if (err != ERROR_SERVICE_EXISTS) {
CloseServiceHandle(mgr);
throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
}
}
else {
// done with service (but only try to close if not null)
CloseServiceHandle(service);
}
// done with manager
CloseServiceHandle(mgr);
// open the registry key for this service
HKEY key = openNTServicesKey();
key = CArchMiscWindows::addKey(key, name);
if (key == NULL) {
// can't open key
DWORD err = GetLastError();
try {
uninstallDaemon(name, allUsers);
}
catch (...) {
// ignore
}
throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
}
// set the description
CArchMiscWindows::setValue(key, _T("Description"), description);
// set command line
key = CArchMiscWindows::addKey(key, _T("Parameters"));
if (key == NULL) {
// can't open key
DWORD err = GetLastError();
CArchMiscWindows::closeKey(key);
try {
uninstallDaemon(name, allUsers);
}
catch (...) {
// ignore
}
throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
}
CArchMiscWindows::setValue(key, _T("CommandLine"), commandLine);
// done with registry
CArchMiscWindows::closeKey(key);
}
}
void
CArchDaemonWindows::uninstallDaemon(const char* name, bool allUsers)
{
// if not for all users then use the user's autostart registry.
// key. if windows 95 family then use windows 95 services key.
if (!allUsers || CArchMiscWindows::isWindows95Family()) {
// open registry
HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ?
open95ServicesKey() : openUserStartupKey();
if (key == NULL) {
// can't open key. daemon is probably not installed.
throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows);
}
// remove entry
CArchMiscWindows::deleteValue(key, name);
// clean up
CArchMiscWindows::closeKey(key);
}
// windows NT family services
else {
// remove parameters for this service. ignore failures.
HKEY key = openNTServicesKey();
key = CArchMiscWindows::openKey(key, name);
if (key != NULL) {
CArchMiscWindows::deleteKey(key, _T("Parameters"));
CArchMiscWindows::closeKey(key);
}
// open service manager
SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
if (mgr == NULL) {
// can't open service manager
throw XArchDaemonUninstallFailed(new XArchEvalWindows);
}
// open the service. oddly, you must open a service to delete it.
SC_HANDLE service = OpenService(mgr, name, DELETE | SERVICE_STOP);
if (service == NULL) {
DWORD err = GetLastError();
CloseServiceHandle(mgr);
if (err != ERROR_SERVICE_DOES_NOT_EXIST) {
throw XArchDaemonUninstallFailed(new XArchEvalWindows(err));
}
throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err));
}
// stop the service. we don't care if we fail.
SERVICE_STATUS status;
ControlService(service, SERVICE_CONTROL_STOP, &status);
// delete the service
const bool okay = (DeleteService(service) == 0);
const DWORD err = GetLastError();
// clean up
CloseServiceHandle(service);
CloseServiceHandle(mgr);
// give windows a chance to remove the service before
// we check if it still exists.
ARCH->sleep(1);
// handle failure. ignore error if service isn't installed anymore.
if (!okay && isDaemonInstalled(name, allUsers)) {
if (err == ERROR_SUCCESS) {
// this seems to occur even though the uninstall was successful.
// it could be a timing issue, i.e., isDaemonInstalled is
// called too soon. i've added a sleep to try and stop this.
return;
}
if (err == ERROR_IO_PENDING) {
// this seems to be a spurious error
return;
}
if (err != ERROR_SERVICE_MARKED_FOR_DELETE) {
throw XArchDaemonUninstallFailed(new XArchEvalWindows(err));
}
throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err));
}
}
}
int
CArchDaemonWindows::daemonize(const char* name, DaemonFunc func)
{
assert(name != NULL);
assert(func != NULL);
// windows 95 family services
if (CArchMiscWindows::isWindows95Family()) {
typedef DWORD (WINAPI *RegisterServiceProcessT)(DWORD, DWORD);
// mark this process as a service so it's not killed when the
// user logs off.
HINSTANCE kernel = LoadLibrary("kernel32.dll");
if (kernel == NULL) {
throw XArchDaemonFailed(new XArchEvalWindows);
}
RegisterServiceProcessT RegisterServiceProcess =
reinterpret_cast<RegisterServiceProcessT>(
GetProcAddress(kernel,
"RegisterServiceProcess"));
if (RegisterServiceProcess == NULL) {
// missing RegisterServiceProcess function
DWORD err = GetLastError();
FreeLibrary(kernel);
throw XArchDaemonFailed(new XArchEvalWindows(err));
}
if (RegisterServiceProcess(0, 1) == 0) {
// RegisterServiceProcess failed
DWORD err = GetLastError();
FreeLibrary(kernel);
throw XArchDaemonFailed(new XArchEvalWindows(err));
}
FreeLibrary(kernel);
// now simply call the daemon function
return func(1, &name);
}
// windows NT family services
else {
// save daemon function
m_daemonFunc = func;
// construct the service entry
SERVICE_TABLE_ENTRY entry[2];
entry[0].lpServiceName = const_cast<char*>(name);
entry[0].lpServiceProc = &CArchDaemonWindows::serviceMainEntry;
entry[1].lpServiceName = NULL;
entry[1].lpServiceProc = NULL;
// hook us up to the service control manager. this won't return
// (if successful) until the processes have terminated.
s_daemon = this;
if (StartServiceCtrlDispatcher(entry) == 0) {
// StartServiceCtrlDispatcher failed
s_daemon = NULL;
throw XArchDaemonFailed(new XArchEvalWindows);
}
s_daemon = NULL;
return m_daemonResult;
}
}
bool
CArchDaemonWindows::canInstallDaemon(const char* /*name*/, bool allUsers)
{
// if not for all users then use the user's autostart registry.
// key. if windows 95 family then use windows 95 services key.
if (!allUsers || CArchMiscWindows::isWindows95Family()) {
// check if we can open the registry key
HKEY key = (allUsers && CArchMiscWindows::isWindows95Family()) ?
open95ServicesKey() : openUserStartupKey();
CArchMiscWindows::closeKey(key);
return (key != NULL);
}
// windows NT family services
else {
// check if we can open service manager for write
SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
if (mgr == NULL) {
return false;
}
CloseServiceHandle(mgr);
// check if we can open the registry key
HKEY key = openNTServicesKey();
// key = CArchMiscWindows::addKey(key, name);
// key = CArchMiscWindows::addKey(key, _T("Parameters"));
CArchMiscWindows::closeKey(key);
return (key != NULL);
}
}
bool
CArchDaemonWindows::isDaemonInstalled(const char* name, bool allUsers)
{
// open service manager
SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ);
if (mgr == NULL) {
return false;
}
// open the service
SC_HANDLE service = OpenService(mgr, name, GENERIC_READ);
// clean up
if (service != NULL) {
CloseServiceHandle(service);
}
CloseServiceHandle(mgr);
return (service != NULL);
}
HKEY
CArchDaemonWindows::openNTServicesKey()
{
static const char* s_keyNames[] = {
_T("SYSTEM"),
_T("CurrentControlSet"),
_T("Services"),
NULL
};
return CArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames);
}
HKEY
CArchDaemonWindows::open95ServicesKey()
{
static const char* s_keyNames[] = {
_T("Software"),
_T("Microsoft"),
_T("Windows"),
_T("CurrentVersion"),
_T("RunServices"),
NULL
};
return CArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames);
}
HKEY
CArchDaemonWindows::openUserStartupKey()
{
static const char* s_keyNames[] = {
_T("Software"),
_T("Microsoft"),
_T("Windows"),
_T("CurrentVersion"),
_T("Run"),
NULL
};
return CArchMiscWindows::addKey(HKEY_CURRENT_USER, s_keyNames);
}
bool
CArchDaemonWindows::isRunState(DWORD state)
{
switch (state) {
case SERVICE_START_PENDING:
case SERVICE_CONTINUE_PENDING:
case SERVICE_RUNNING:
return true;
default:
return false;
}
}
int
CArchDaemonWindows::doRunDaemon(RunFunc run)
{
// should only be called from DaemonFunc
assert(m_serviceMutex != NULL);
assert(run != NULL);
// create message queue for this thread
MSG dummy;
PeekMessage(&dummy, NULL, 0, 0, PM_NOREMOVE);
int result = 0;
ARCH->lockMutex(m_serviceMutex);
m_daemonThreadID = GetCurrentThreadId();
while (m_serviceState != SERVICE_STOPPED) {
// wait until we're told to start
while (!isRunState(m_serviceState) &&
m_serviceState != SERVICE_STOP_PENDING) {
ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
}
// run unless told to stop
if (m_serviceState != SERVICE_STOP_PENDING) {
ARCH->unlockMutex(m_serviceMutex);
try {
result = run();
}
catch (...) {
ARCH->lockMutex(m_serviceMutex);
setStatusError(0);
m_serviceState = SERVICE_STOPPED;
setStatus(m_serviceState);
ARCH->broadcastCondVar(m_serviceCondVar);
ARCH->unlockMutex(m_serviceMutex);
throw;
}
ARCH->lockMutex(m_serviceMutex);
}
// notify of new state
if (m_serviceState == SERVICE_PAUSE_PENDING) {
m_serviceState = SERVICE_PAUSED;
}
else {
m_serviceState = SERVICE_STOPPED;
}
setStatus(m_serviceState);
ARCH->broadcastCondVar(m_serviceCondVar);
}
ARCH->unlockMutex(m_serviceMutex);
return result;
}
void
CArchDaemonWindows::doDaemonRunning(bool running)
{
ARCH->lockMutex(m_serviceMutex);
if (running) {
m_serviceState = SERVICE_RUNNING;
setStatus(m_serviceState);
ARCH->broadcastCondVar(m_serviceCondVar);
}
ARCH->unlockMutex(m_serviceMutex);
}
UINT
CArchDaemonWindows::doGetDaemonQuitMessage()
{
return m_quitMessage;
}
void
CArchDaemonWindows::setStatus(DWORD state)
{
setStatus(state, 0, 0);
}
void
CArchDaemonWindows::setStatus(DWORD state, DWORD step, DWORD waitHint)
{
assert(s_daemon != NULL);
SERVICE_STATUS status;
status.dwServiceType = SERVICE_WIN32_OWN_PROCESS |
SERVICE_INTERACTIVE_PROCESS;
status.dwCurrentState = state;
status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_PAUSE_CONTINUE |
SERVICE_ACCEPT_SHUTDOWN;
status.dwWin32ExitCode = NO_ERROR;
status.dwServiceSpecificExitCode = 0;
status.dwCheckPoint = step;
status.dwWaitHint = waitHint;
SetServiceStatus(s_daemon->m_statusHandle, &status);
}
void
CArchDaemonWindows::setStatusError(DWORD error)
{
assert(s_daemon != NULL);
SERVICE_STATUS status;
status.dwServiceType = SERVICE_WIN32_OWN_PROCESS |
SERVICE_INTERACTIVE_PROCESS;
status.dwCurrentState = SERVICE_STOPPED;
status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
SERVICE_ACCEPT_PAUSE_CONTINUE |
SERVICE_ACCEPT_SHUTDOWN;
status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
status.dwServiceSpecificExitCode = error;
status.dwCheckPoint = 0;
status.dwWaitHint = 0;
SetServiceStatus(s_daemon->m_statusHandle, &status);
}
void
CArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn)
{
typedef std::vector<LPCTSTR> ArgList;
typedef std::vector<std::string> Arguments;
const char** argv = const_cast<const char**>(argvIn);
// create synchronization objects
m_serviceMutex = ARCH->newMutex();
m_serviceCondVar = ARCH->newCondVar();
// register our service handler function
m_statusHandle = RegisterServiceCtrlHandler(argv[0],
&CArchDaemonWindows::serviceHandlerEntry);
if (m_statusHandle == 0) {
// cannot start as service
m_daemonResult = -1;
ARCH->closeCondVar(m_serviceCondVar);
ARCH->closeMutex(m_serviceMutex);
return;
}
// tell service control manager that we're starting
m_serviceState = SERVICE_START_PENDING;
setStatus(m_serviceState, 0, 10000);
std::string commandLine;
// if no arguments supplied then try getting them from the registry.
// the first argument doesn't count because it's the service name.
Arguments args;
ArgList myArgv;
if (argc <= 1) {
// read command line
HKEY key = openNTServicesKey();
key = CArchMiscWindows::openKey(key, argvIn[0]);
key = CArchMiscWindows::openKey(key, _T("Parameters"));
if (key != NULL) {
commandLine = CArchMiscWindows::readValueString(key,
_T("CommandLine"));
}
// if the command line isn't empty then parse and use it
if (!commandLine.empty()) {
// parse, honoring double quoted substrings
std::string::size_type i = commandLine.find_first_not_of(" \t");
while (i != std::string::npos && i != commandLine.size()) {
// find end of string
std::string::size_type e;
if (commandLine[i] == '\"') {
// quoted. find closing quote.
++i;
e = commandLine.find("\"", i);
// whitespace must follow closing quote
if (e == std::string::npos ||
(e + 1 != commandLine.size() &&
commandLine[e + 1] != ' ' &&
commandLine[e + 1] != '\t')) {
args.clear();
break;
}
// extract
args.push_back(commandLine.substr(i, e - i));
i = e + 1;
}
else {
// unquoted. find next whitespace.
e = commandLine.find_first_of(" \t", i);
if (e == std::string::npos) {
e = commandLine.size();
}
// extract
args.push_back(commandLine.substr(i, e - i));
i = e + 1;
}
// next argument
i = commandLine.find_first_not_of(" \t", i);
}
// service name goes first
myArgv.push_back(argv[0]);
// get pointers
for (size_t j = 0; j < args.size(); ++j) {
myArgv.push_back(args[j].c_str());
}
// adjust argc/argv
argc = (DWORD)myArgv.size();
argv = &myArgv[0];
}
}
m_commandLine = commandLine;
try {
// invoke daemon function
m_daemonResult = m_daemonFunc(static_cast<int>(argc), argv);
}
catch (XArchDaemonRunFailed& e) {
setStatusError(e.m_result);
m_daemonResult = -1;
}
catch (...) {
setStatusError(1);
m_daemonResult = -1;
}
// clean up
ARCH->closeCondVar(m_serviceCondVar);
ARCH->closeMutex(m_serviceMutex);
// we're going to exit now, so set status to stopped
m_serviceState = SERVICE_STOPPED;
setStatus(m_serviceState, 0, 10000);
}
void WINAPI
CArchDaemonWindows::serviceMainEntry(DWORD argc, LPTSTR* argv)
{
s_daemon->serviceMain(argc, argv);
}
void
CArchDaemonWindows::serviceHandler(DWORD ctrl)
{
assert(m_serviceMutex != NULL);
assert(m_serviceCondVar != NULL);
ARCH->lockMutex(m_serviceMutex);
// ignore request if service is already stopped
if (s_daemon == NULL || m_serviceState == SERVICE_STOPPED) {
if (s_daemon != NULL) {
setStatus(m_serviceState);
}
ARCH->unlockMutex(m_serviceMutex);
return;
}
switch (ctrl) {
case SERVICE_CONTROL_PAUSE:
m_serviceState = SERVICE_PAUSE_PENDING;
setStatus(m_serviceState, 0, 5000);
PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0);
while (isRunState(m_serviceState)) {
ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
}
break;
case SERVICE_CONTROL_CONTINUE:
// FIXME -- maybe should flush quit messages from queue
m_serviceState = SERVICE_CONTINUE_PENDING;
setStatus(m_serviceState, 0, 5000);
ARCH->broadcastCondVar(m_serviceCondVar);
break;
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
m_serviceState = SERVICE_STOP_PENDING;
setStatus(m_serviceState, 0, 5000);
PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0);
ARCH->broadcastCondVar(m_serviceCondVar);
while (isRunState(m_serviceState)) {
ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
}
break;
default:
// unknown service command
// fall through
case SERVICE_CONTROL_INTERROGATE:
setStatus(m_serviceState);
break;
}
ARCH->unlockMutex(m_serviceMutex);
}
void WINAPI
CArchDaemonWindows::serviceHandlerEntry(DWORD ctrl)
{
s_daemon->serviceHandler(ctrl);
}
void
CArchDaemonWindows::start(const char* name)
{
// open service manager
SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ);
if (mgr == NULL) {
throw XArchDaemonFailed(new XArchEvalWindows());
}
// open the service
SC_HANDLE service = OpenService(
mgr, name, SERVICE_START);
if (service == NULL) {
CloseServiceHandle(mgr);
throw XArchDaemonFailed(new XArchEvalWindows());
}
// start the service
if (!StartService(service, 0, NULL)) {
throw XArchDaemonFailed(new XArchEvalWindows());
}
}
void
CArchDaemonWindows::stop(const char* name)
{
// open service manager
SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ);
if (mgr == NULL) {
throw XArchDaemonFailed(new XArchEvalWindows());
}
// open the service
SC_HANDLE service = OpenService(
mgr, name,
SERVICE_STOP | SERVICE_QUERY_STATUS);
if (service == NULL) {
CloseServiceHandle(mgr);
throw XArchDaemonFailed(new XArchEvalWindows());
}
// ask the service to stop, asynchronously
SERVICE_STATUS ss;
if (!ControlService(service, SERVICE_CONTROL_STOP, &ss)) {
DWORD dwErrCode = GetLastError();
if (dwErrCode != ERROR_SERVICE_NOT_ACTIVE) {
throw XArchDaemonFailed(new XArchEvalWindows());
}
}
}
void
CArchDaemonWindows::installDaemon()
{
// install default daemon if not already installed.
if (!isDaemonInstalled(DEFAULT_DAEMON_NAME, true)) {
char path[MAX_PATH];
GetModuleFileName(CArchMiscWindows::instanceWin32(), path, MAX_PATH);
installDaemon(DEFAULT_DAEMON_NAME, DEFAULT_DAEMON_INFO, path, "", "", true);
}
start(DEFAULT_DAEMON_NAME);
}
void
CArchDaemonWindows::uninstallDaemon()
{
// remove legacy services if installed.
if (isDaemonInstalled(LEGACY_SERVER_DAEMON_NAME, true)) {
uninstallDaemon(LEGACY_SERVER_DAEMON_NAME, true);
}
if (isDaemonInstalled(LEGACY_CLIENT_DAEMON_NAME, true)) {
uninstallDaemon(LEGACY_CLIENT_DAEMON_NAME, true);
}
// remove new service if installed.
if (isDaemonInstalled(DEFAULT_DAEMON_NAME, true)) {
uninstallDaemon(DEFAULT_DAEMON_NAME, true);
}
}

View File

@@ -0,0 +1,159 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHDAEMONWINDOWS_H
#define CARCHDAEMONWINDOWS_H
#define WIN32_LEAN_AND_MEAN
#include "IArchDaemon.h"
#include "IArchMultithread.h"
#include "stdstring.h"
#include <windows.h>
#include <tchar.h>
#define ARCH_DAEMON CArchDaemonWindows
//! Win32 implementation of IArchDaemon
class CArchDaemonWindows : public IArchDaemon {
public:
typedef int (*RunFunc)(void);
CArchDaemonWindows();
virtual ~CArchDaemonWindows();
//! Run the daemon
/*!
When the client calls \c daemonize(), the \c DaemonFunc should call this
function after initialization and argument parsing to perform the
daemon processing. The \c runFunc should perform the daemon's
main loop, calling \c daemonRunning(true) when it enters the main loop
(i.e. after initialization) and \c daemonRunning(false) when it leaves
the main loop. The \c runFunc is called in a new thread and when the
daemon must exit the main loop due to some external control the
getDaemonQuitMessage() is posted to the thread. This function returns
what \c runFunc returns. \c runFunc should call \c daemonFailed() if
the daemon fails.
*/
static int runDaemon(RunFunc runFunc);
//! Indicate daemon is in main loop
/*!
The \c runFunc passed to \c runDaemon() should call this function
to indicate when it has entered (\c running is \c true) or exited
(\c running is \c false) the main loop.
*/
static void daemonRunning(bool running);
//! Indicate failure of running daemon
/*!
The \c runFunc passed to \c runDaemon() should call this function
to indicate failure. \c result is returned by \c daemonize().
*/
static void daemonFailed(int result);
//! Get daemon quit message
/*!
The windows NT daemon tells daemon thread to exit by posting this
message to it. The thread must, of course, have a message queue
for this to work.
*/
static UINT getDaemonQuitMessage();
// IArchDaemon overrides
virtual void installDaemon(const char* name,
const char* description,
const char* pathname,
const char* commandLine,
const char* dependencies,
bool allUsers);
virtual void uninstallDaemon(const char* name, bool allUsers);
virtual void installDaemon();
virtual void uninstallDaemon();
virtual int daemonize(const char* name, DaemonFunc func);
virtual bool canInstallDaemon(const char* name, bool allUsers);
virtual bool isDaemonInstalled(const char* name, bool allUsers);
std::string commandLine() const { return m_commandLine; }
private:
static HKEY openNTServicesKey();
static HKEY open95ServicesKey();
static HKEY openUserStartupKey();
int doRunDaemon(RunFunc runFunc);
void doDaemonRunning(bool running);
UINT doGetDaemonQuitMessage();
static void setStatus(DWORD state);
static void setStatus(DWORD state, DWORD step, DWORD waitHint);
static void setStatusError(DWORD error);
static bool isRunState(DWORD state);
void serviceMain(DWORD, LPTSTR*);
static void WINAPI serviceMainEntry(DWORD, LPTSTR*);
void serviceHandler(DWORD ctrl);
static void WINAPI serviceHandlerEntry(DWORD ctrl);
void start(const char* name);
void stop(const char* name);
private:
class XArchDaemonRunFailed {
public:
XArchDaemonRunFailed(int result) : m_result(result) { }
public:
int m_result;
};
private:
static CArchDaemonWindows* s_daemon;
CArchMutex m_serviceMutex;
CArchCond m_serviceCondVar;
DWORD m_serviceState;
bool m_serviceHandlerWaiting;
bool m_serviceRunning;
DWORD m_daemonThreadID;
DaemonFunc m_daemonFunc;
int m_daemonResult;
SERVICE_STATUS_HANDLE m_statusHandle;
UINT m_quitMessage;
std::string m_commandLine;
};
#define DEFAULT_DAEMON_NAME _T("Synergy")
#define DEFAULT_DAEMON_INFO _T("Manages the Synergy foreground processes.")
#define LEGACY_SERVER_DAEMON_NAME _T("Synergy Server")
#define LEGACY_CLIENT_DAEMON_NAME _T("Synergy Client")
static const TCHAR* const g_daemonKeyPath[] = {
_T("SOFTWARE"),
_T("The Synergy Project"),
_T("Synergy"),
_T("Service"),
NULL
};
#endif

View File

@@ -0,0 +1,101 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchFileUnix.h"
#include <stdio.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/types.h>
#include <cstring>
//
// CArchFileUnix
//
CArchFileUnix::CArchFileUnix()
{
// do nothing
}
CArchFileUnix::~CArchFileUnix()
{
// do nothing
}
const char*
CArchFileUnix::getBasename(const char* pathname)
{
if (pathname == NULL) {
return NULL;
}
const char* basename = strrchr(pathname, '/');
if (basename != NULL) {
return basename + 1;
}
else {
return pathname;
}
}
std::string
CArchFileUnix::getUserDirectory()
{
char* buffer = NULL;
std::string dir;
#if HAVE_GETPWUID_R
struct passwd pwent;
struct passwd* pwentp;
#if defined(_SC_GETPW_R_SIZE_MAX)
long size = sysconf(_SC_GETPW_R_SIZE_MAX);
if (size == -1) {
size = BUFSIZ;
}
#else
long size = BUFSIZ;
#endif
buffer = new char[size];
getpwuid_r(getuid(), &pwent, buffer, size, &pwentp);
#else
struct passwd* pwentp = getpwuid(getuid());
#endif
if (pwentp != NULL && pwentp->pw_dir != NULL) {
dir = pwentp->pw_dir;
}
delete[] buffer;
return dir;
}
std::string
CArchFileUnix::getSystemDirectory()
{
return "/etc";
}
std::string
CArchFileUnix::concatPath(const std::string& prefix,
const std::string& suffix)
{
std::string path;
path.reserve(prefix.size() + 1 + suffix.size());
path += prefix;
if (path.size() == 0 || path[path.size() - 1] != '/') {
path += '/';
}
path += suffix;
return path;
}

View File

@@ -0,0 +1,39 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHFILEUNIX_H
#define CARCHFILEUNIX_H
#include "IArchFile.h"
#define ARCH_FILE CArchFileUnix
//! Unix implementation of IArchFile
class CArchFileUnix : public IArchFile {
public:
CArchFileUnix();
virtual ~CArchFileUnix();
// IArchFile overrides
virtual const char* getBasename(const char* pathname);
virtual std::string getUserDirectory();
virtual std::string getSystemDirectory();
virtual std::string concatPath(const std::string& prefix,
const std::string& suffix);
};
#endif

View File

@@ -0,0 +1,135 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchFileWindows.h"
#include <windows.h>
#include <shlobj.h>
#include <tchar.h>
#include <string.h>
//
// CArchFileWindows
//
CArchFileWindows::CArchFileWindows()
{
// do nothing
}
CArchFileWindows::~CArchFileWindows()
{
// do nothing
}
const char*
CArchFileWindows::getBasename(const char* pathname)
{
if (pathname == NULL) {
return NULL;
}
// check for last slash
const char* basename = strrchr(pathname, '/');
if (basename != NULL) {
++basename;
}
else {
basename = pathname;
}
// check for last backslash
const char* basename2 = strrchr(pathname, '\\');
if (basename2 != NULL && basename2 > basename) {
basename = basename2 + 1;
}
return basename;
}
std::string
CArchFileWindows::getUserDirectory()
{
// try %HOMEPATH%
TCHAR dir[MAX_PATH];
DWORD size = sizeof(dir) / sizeof(TCHAR);
DWORD result = GetEnvironmentVariable(_T("HOMEPATH"), dir, size);
if (result != 0 && result <= size) {
// sanity check -- if dir doesn't appear to start with a
// drive letter and isn't a UNC name then don't use it
// FIXME -- allow UNC names
if (dir[0] != '\0' && (dir[1] == ':' ||
((dir[0] == '\\' || dir[0] == '/') &&
(dir[1] == '\\' || dir[1] == '/')))) {
return dir;
}
}
// get the location of the personal files. that's as close to
// a home directory as we're likely to find.
ITEMIDLIST* idl;
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &idl))) {
TCHAR* path = NULL;
if (SHGetPathFromIDList(idl, dir)) {
DWORD attr = GetFileAttributes(dir);
if (attr != 0xffffffff && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
path = dir;
}
IMalloc* shalloc;
if (SUCCEEDED(SHGetMalloc(&shalloc))) {
shalloc->Free(idl);
shalloc->Release();
}
if (path != NULL) {
return path;
}
}
// use root of C drive as a default
return "C:";
}
std::string
CArchFileWindows::getSystemDirectory()
{
// get windows directory
char dir[MAX_PATH];
if (GetWindowsDirectory(dir, sizeof(dir)) != 0) {
return dir;
}
else {
// can't get it. use C:\ as a default.
return "C:";
}
}
std::string
CArchFileWindows::concatPath(const std::string& prefix,
const std::string& suffix)
{
std::string path;
path.reserve(prefix.size() + 1 + suffix.size());
path += prefix;
if (path.size() == 0 ||
(path[path.size() - 1] != '\\' &&
path[path.size() - 1] != '/')) {
path += '\\';
}
path += suffix;
return path;
}

View File

@@ -0,0 +1,39 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHFILEWINDOWS_H
#define CARCHFILEWINDOWS_H
#include "IArchFile.h"
#define ARCH_FILE CArchFileWindows
//! Win32 implementation of IArchFile
class CArchFileWindows : public IArchFile {
public:
CArchFileWindows();
virtual ~CArchFileWindows();
// IArchFile overrides
virtual const char* getBasename(const char* pathname);
virtual std::string getUserDirectory();
virtual std::string getSystemDirectory();
virtual std::string concatPath(const std::string& prefix,
const std::string& suffix);
};
#endif

View File

@@ -0,0 +1,46 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012 Nick Bolton
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchIpcLogUnix.h"
CArchIpcLogUnix::CArchIpcLogUnix()
{
}
CArchIpcLogUnix::~CArchIpcLogUnix()
{
}
void
CArchIpcLogUnix::openLog(const char* name)
{
}
void
CArchIpcLogUnix::closeLog()
{
}
void
CArchIpcLogUnix::showLog(bool showIfEmpty)
{
}
void
CArchIpcLogUnix::writeLog(ELevel, const char*)
{
}

View File

@@ -0,0 +1,34 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012 Nick Bolton
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "IArchLog.h"
#define ARCH_IPC_LOG CArchIpcLogUnix
class CArchIpcLogUnix : public IArchLog {
public:
CArchIpcLogUnix();
virtual ~CArchIpcLogUnix();
// IArchLog overrides
virtual void openLog(const char* name);
virtual void closeLog();
virtual void showLog(bool showIfEmpty);
virtual void writeLog(ELevel, const char*);
};

View File

@@ -0,0 +1,111 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchIpcLogWindows.h"
#include "CArchMiscWindows.h"
#include "XArch.h"
#include "CThread.h"
#include "TMethodJob.h"
#include "CArch.h"
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
//
// CArchIpcLogWindows
//
CArchIpcLogWindows::CArchIpcLogWindows() :
m_pipe(NULL),
m_listenThread(NULL),
m_connected(false)
{
}
CArchIpcLogWindows::~CArchIpcLogWindows()
{
if (m_listenThread != NULL)
delete m_listenThread;
}
void
CArchIpcLogWindows::openLog(const char* name)
{
// grant access to everyone.
SECURITY_DESCRIPTOR sd;
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, TRUE, static_cast<PACL>(0), FALSE);
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = &sd;
HANDLE pipe = CreateNamedPipe(
TEXT("\\\\.\\pipe\\SynergyLog"),
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
1024, 1024, 0, &sa);
if (pipe == INVALID_HANDLE_VALUE)
XArch("could not create named pipe.");
m_pipe = pipe;
m_listenThread = new CThread(new TMethodJob<CArchIpcLogWindows>(
this, &CArchIpcLogWindows::connectThread, nullptr));
}
void
CArchIpcLogWindows::connectThread(void*)
{
// HACK: this seems like a hacky pile of bollocks. it will continuously call
// ConnectNamedPipe every second. if there is no client, it will block,
// but if there is a client it will return FALSE and GetLastError() will
// be ERROR_PIPE_CONNECTED. in any other case, the client has gone away
// and ConnectNamedPipe will go back to blocking (waiting for the client
// to reconnect).
while (true) {
BOOL result = ConnectNamedPipe(m_pipe, NULL);
if ((result == TRUE) || (GetLastError() == ERROR_PIPE_CONNECTED)) {
m_connected = true;
ARCH->sleep(1);
} else {
DisconnectNamedPipe(m_pipe);
}
}
}
void
CArchIpcLogWindows::closeLog()
{
}
void
CArchIpcLogWindows::showLog(bool)
{
}
void
CArchIpcLogWindows::writeLog(ELevel level, const char* data)
{
if (!m_connected)
return;
DWORD bytesWritten;
WriteFile(m_pipe, data, (DWORD)strlen(data), &bytesWritten, NULL);
}

View File

@@ -0,0 +1,48 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012 Nick Bolton
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define WIN32_LEAN_AND_MEAN
#include "IArchLog.h"
#include <windows.h>
#define ARCH_IPC_LOG CArchIpcLogWindows
class CThread;
//! Win32 implementation of IArchLog (IPC version)
class CArchIpcLogWindows : public IArchLog {
public:
CArchIpcLogWindows();
virtual ~CArchIpcLogWindows();
// IArchLog overrides
virtual void openLog(const char* name);
virtual void closeLog();
virtual void showLog(bool showIfEmpty);
virtual void writeLog(ELevel, const char*);
private:
void connectThread(void*);
private:
HANDLE m_pipe;
CThread* m_listenThread;
bool m_connected;
};

View File

@@ -0,0 +1,82 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchLogUnix.h"
#include <syslog.h>
//
// CArchLogUnix
//
CArchLogUnix::CArchLogUnix()
{
// do nothing
}
CArchLogUnix::~CArchLogUnix()
{
// do nothing
}
void
CArchLogUnix::openLog(const char* name)
{
openlog(name, 0, LOG_DAEMON);
}
void
CArchLogUnix::closeLog()
{
closelog();
}
void
CArchLogUnix::showLog(bool)
{
// do nothing
}
void
CArchLogUnix::writeLog(ELevel level, const char* msg)
{
// convert level
int priority;
switch (level) {
case kERROR:
priority = LOG_ERR;
break;
case kWARNING:
priority = LOG_WARNING;
break;
case kNOTE:
priority = LOG_NOTICE;
break;
case kINFO:
priority = LOG_INFO;
break;
default:
priority = LOG_DEBUG;
break;
}
// log it
syslog(priority, "%s", msg);
}

View File

@@ -0,0 +1,38 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHLOGUNIX_H
#define CARCHLOGUNIX_H
#include "IArchLog.h"
#define ARCH_LOG CArchLogUnix
//! Unix implementation of IArchLog
class CArchLogUnix : public IArchLog {
public:
CArchLogUnix();
virtual ~CArchLogUnix();
// IArchLog overrides
virtual void openLog(const char* name);
virtual void closeLog();
virtual void showLog(bool);
virtual void writeLog(ELevel, const char*);
};
#endif

View File

@@ -0,0 +1,93 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchLogWindows.h"
#include "CArchMiscWindows.h"
#include <string.h>
//
// CArchLogWindows
//
CArchLogWindows::CArchLogWindows() : m_eventLog(NULL)
{
// do nothing
}
CArchLogWindows::~CArchLogWindows()
{
// do nothing
}
void
CArchLogWindows::openLog(const char* name)
{
if (m_eventLog == NULL && !CArchMiscWindows::isWindows95Family()) {
m_eventLog = RegisterEventSource(NULL, name);
}
}
void
CArchLogWindows::closeLog()
{
if (m_eventLog != NULL) {
DeregisterEventSource(m_eventLog);
m_eventLog = NULL;
}
}
void
CArchLogWindows::showLog(bool)
{
// do nothing
}
void
CArchLogWindows::writeLog(ELevel level, const char* msg)
{
if (m_eventLog != NULL) {
// convert priority
WORD type;
switch (level) {
case kERROR:
type = EVENTLOG_ERROR_TYPE;
break;
case kWARNING:
type = EVENTLOG_WARNING_TYPE;
break;
default:
type = EVENTLOG_INFORMATION_TYPE;
break;
}
// log it
// FIXME -- win32 wants to use a message table to look up event
// strings. log messages aren't organized that way so we'll
// just dump our string into the raw data section of the event
// so users can at least see the message. note that we use our
// level as the event category.
ReportEvent(m_eventLog, type, static_cast<WORD>(level),
0, // event ID
NULL,
0,
(DWORD)strlen(msg) + 1, // raw data size
NULL,
const_cast<char*>(msg));// raw data
}
}

View File

@@ -0,0 +1,44 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHLOGWINDOWS_H
#define CARCHLOGWINDOWS_H
#define WIN32_LEAN_AND_MEAN
#include "IArchLog.h"
#include <windows.h>
#define ARCH_LOG CArchLogWindows
//! Win32 implementation of IArchLog
class CArchLogWindows : public IArchLog {
public:
CArchLogWindows();
virtual ~CArchLogWindows();
// IArchLog overrides
virtual void openLog(const char* name);
virtual void closeLog();
virtual void showLog(bool showIfEmpty);
virtual void writeLog(ELevel, const char*);
private:
HANDLE m_eventLog;
};
#endif

View File

@@ -0,0 +1,554 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchMiscWindows.h"
#include "CArchDaemonWindows.h"
#include "CLog.h"
#include <Wtsapi32.h>
#pragma warning(disable: 4099)
#include <Userenv.h>
#pragma warning(default: 4099)
#include "Version.h"
// parent process name for services in Vista
#define SERVICE_LAUNCHER "services.exe"
#ifndef ES_SYSTEM_REQUIRED
#define ES_SYSTEM_REQUIRED ((DWORD)0x00000001)
#endif
#ifndef ES_DISPLAY_REQUIRED
#define ES_DISPLAY_REQUIRED ((DWORD)0x00000002)
#endif
#ifndef ES_CONTINUOUS
#define ES_CONTINUOUS ((DWORD)0x80000000)
#endif
typedef DWORD EXECUTION_STATE;
//
// CArchMiscWindows
//
CArchMiscWindows::CDialogs* CArchMiscWindows::s_dialogs = NULL;
DWORD CArchMiscWindows::s_busyState = 0;
CArchMiscWindows::STES_t CArchMiscWindows::s_stes = NULL;
HICON CArchMiscWindows::s_largeIcon = NULL;
HICON CArchMiscWindows::s_smallIcon = NULL;
HINSTANCE CArchMiscWindows::s_instanceWin32 = NULL;
void
CArchMiscWindows::init()
{
s_dialogs = new CDialogs;
isWindows95Family();
}
bool
CArchMiscWindows::isWindows95Family()
{
static bool init = false;
static bool result = false;
if (!init) {
OSVERSIONINFO version;
version.dwOSVersionInfoSize = sizeof(version);
if (GetVersionEx(&version) == 0) {
// cannot determine OS; assume windows 95 family
result = true;
}
else {
result = (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
}
init = true;
}
return result;
}
bool
CArchMiscWindows::isWindowsModern()
{
static bool init = false;
static bool result = false;
if (!init) {
OSVERSIONINFO version;
version.dwOSVersionInfoSize = sizeof(version);
if (GetVersionEx(&version) == 0) {
// cannot determine OS; assume not modern
result = false;
}
else {
result = ((version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
version.dwMajorVersion == 4 &&
version.dwMinorVersion > 0) ||
(version.dwPlatformId == VER_PLATFORM_WIN32_NT &&
version.dwMajorVersion > 4));
}
init = true;
}
return result;
}
void
CArchMiscWindows::setIcons(HICON largeIcon, HICON smallIcon)
{
s_largeIcon = largeIcon;
s_smallIcon = smallIcon;
}
void
CArchMiscWindows::getIcons(HICON& largeIcon, HICON& smallIcon)
{
largeIcon = s_largeIcon;
smallIcon = s_smallIcon;
}
int
CArchMiscWindows::runDaemon(RunFunc runFunc)
{
return CArchDaemonWindows::runDaemon(runFunc);
}
void
CArchMiscWindows::daemonRunning(bool running)
{
CArchDaemonWindows::daemonRunning(running);
}
void
CArchMiscWindows::daemonFailed(int result)
{
CArchDaemonWindows::daemonFailed(result);
}
UINT
CArchMiscWindows::getDaemonQuitMessage()
{
return CArchDaemonWindows::getDaemonQuitMessage();
}
HKEY
CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName)
{
return openKey(key, keyName, false);
}
HKEY
CArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames)
{
return openKey(key, keyNames, false);
}
HKEY
CArchMiscWindows::addKey(HKEY key, const TCHAR* keyName)
{
return openKey(key, keyName, true);
}
HKEY
CArchMiscWindows::addKey(HKEY key, const TCHAR* const* keyNames)
{
return openKey(key, keyNames, true);
}
HKEY
CArchMiscWindows::openKey(HKEY key, const TCHAR* keyName, bool create)
{
// ignore if parent is NULL
if (key == NULL) {
return NULL;
}
// open next key
HKEY newKey;
LONG result = RegOpenKeyEx(key, keyName, 0,
KEY_WRITE | KEY_QUERY_VALUE, &newKey);
if (result != ERROR_SUCCESS && create) {
DWORD disp;
result = RegCreateKeyEx(key, keyName, 0, TEXT(""),
0, KEY_WRITE | KEY_QUERY_VALUE,
NULL, &newKey, &disp);
}
if (result != ERROR_SUCCESS) {
RegCloseKey(key);
return NULL;
}
// switch to new key
RegCloseKey(key);
return newKey;
}
HKEY
CArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames, bool create)
{
for (size_t i = 0; key != NULL && keyNames[i] != NULL; ++i) {
// open next key
key = openKey(key, keyNames[i], create);
}
return key;
}
void
CArchMiscWindows::closeKey(HKEY key)
{
assert(key != NULL);
if (key==NULL) return;
RegCloseKey(key);
}
void
CArchMiscWindows::deleteKey(HKEY key, const TCHAR* name)
{
assert(key != NULL);
assert(name != NULL);
if (key==NULL || name==NULL) return;
RegDeleteKey(key, name);
}
void
CArchMiscWindows::deleteValue(HKEY key, const TCHAR* name)
{
assert(key != NULL);
assert(name != NULL);
if (key==NULL || name==NULL) return;
RegDeleteValue(key, name);
}
bool
CArchMiscWindows::hasValue(HKEY key, const TCHAR* name)
{
DWORD type;
LONG result = RegQueryValueEx(key, name, 0, &type, NULL, NULL);
return (result == ERROR_SUCCESS &&
(type == REG_DWORD || type == REG_SZ));
}
CArchMiscWindows::EValueType
CArchMiscWindows::typeOfValue(HKEY key, const TCHAR* name)
{
DWORD type;
LONG result = RegQueryValueEx(key, name, 0, &type, NULL, NULL);
if (result != ERROR_SUCCESS) {
return kNO_VALUE;
}
switch (type) {
case REG_DWORD:
return kUINT;
case REG_SZ:
return kSTRING;
case REG_BINARY:
return kBINARY;
default:
return kUNKNOWN;
}
}
void
CArchMiscWindows::setValue(HKEY key,
const TCHAR* name, const std::string& value)
{
assert(key != NULL);
assert(name != NULL);
if(key ==NULL || name==NULL) return; // TODO: throw exception
RegSetValueEx(key, name, 0, REG_SZ,
reinterpret_cast<const BYTE*>(value.c_str()),
(DWORD)value.size() + 1);
}
void
CArchMiscWindows::setValue(HKEY key, const TCHAR* name, DWORD value)
{
assert(key != NULL);
assert(name != NULL);
if(key ==NULL || name==NULL) return; // TODO: throw exception
RegSetValueEx(key, name, 0, REG_DWORD,
reinterpret_cast<CONST BYTE*>(&value),
sizeof(DWORD));
}
void
CArchMiscWindows::setValueBinary(HKEY key,
const TCHAR* name, const std::string& value)
{
assert(key != NULL);
assert(name != NULL);
if(key ==NULL || name==NULL) return; // TODO: throw exception
RegSetValueEx(key, name, 0, REG_BINARY,
reinterpret_cast<const BYTE*>(value.data()),
(DWORD)value.size());
}
std::string
CArchMiscWindows::readBinaryOrString(HKEY key, const TCHAR* name, DWORD type)
{
// get the size of the string
DWORD actualType;
DWORD size = 0;
LONG result = RegQueryValueEx(key, name, 0, &actualType, NULL, &size);
if (result != ERROR_SUCCESS || actualType != type) {
return std::string();
}
// if zero size then return empty string
if (size == 0) {
return std::string();
}
// allocate space
char* buffer = new char[size];
// read it
result = RegQueryValueEx(key, name, 0, &actualType,
reinterpret_cast<BYTE*>(buffer), &size);
if (result != ERROR_SUCCESS || actualType != type) {
delete[] buffer;
return std::string();
}
// clean up and return value
if (type == REG_SZ && buffer[size - 1] == '\0') {
// don't include terminating nul; std::string will add one.
--size;
}
std::string value(buffer, size);
delete[] buffer;
return value;
}
std::string
CArchMiscWindows::readValueString(HKEY key, const TCHAR* name)
{
return readBinaryOrString(key, name, REG_SZ);
}
std::string
CArchMiscWindows::readValueBinary(HKEY key, const TCHAR* name)
{
return readBinaryOrString(key, name, REG_BINARY);
}
DWORD
CArchMiscWindows::readValueInt(HKEY key, const TCHAR* name)
{
DWORD type;
DWORD value;
DWORD size = sizeof(value);
LONG result = RegQueryValueEx(key, name, 0, &type,
reinterpret_cast<BYTE*>(&value), &size);
if (result != ERROR_SUCCESS || type != REG_DWORD) {
return 0;
}
return value;
}
void
CArchMiscWindows::addDialog(HWND hwnd)
{
s_dialogs->insert(hwnd);
}
void
CArchMiscWindows::removeDialog(HWND hwnd)
{
s_dialogs->erase(hwnd);
}
bool
CArchMiscWindows::processDialog(MSG* msg)
{
for (CDialogs::const_iterator index = s_dialogs->begin();
index != s_dialogs->end(); ++index) {
if (IsDialogMessage(*index, msg)) {
return true;
}
}
return false;
}
void
CArchMiscWindows::addBusyState(DWORD busyModes)
{
s_busyState |= busyModes;
setThreadExecutionState(s_busyState);
}
void
CArchMiscWindows::removeBusyState(DWORD busyModes)
{
s_busyState &= ~busyModes;
setThreadExecutionState(s_busyState);
}
void
CArchMiscWindows::setThreadExecutionState(DWORD busyModes)
{
// look up function dynamically so we work on older systems
if (s_stes == NULL) {
HINSTANCE kernel = LoadLibrary("kernel32.dll");
if (kernel != NULL) {
s_stes = reinterpret_cast<STES_t>(GetProcAddress(kernel,
"SetThreadExecutionState"));
}
if (s_stes == NULL) {
s_stes = &CArchMiscWindows::dummySetThreadExecutionState;
}
}
// convert to STES form
EXECUTION_STATE state = 0;
if ((busyModes & kSYSTEM) != 0) {
state |= ES_SYSTEM_REQUIRED;
}
if ((busyModes & kDISPLAY) != 0) {
state |= ES_DISPLAY_REQUIRED;
}
if (state != 0) {
state |= ES_CONTINUOUS;
}
// do it
s_stes(state);
}
DWORD
CArchMiscWindows::dummySetThreadExecutionState(DWORD)
{
// do nothing
return 0;
}
void
CArchMiscWindows::wakeupDisplay()
{
// We can't use ::setThreadExecutionState here because it sets
// ES_CONTINUOUS, which we don't want.
if (s_stes == NULL) {
HINSTANCE kernel = LoadLibrary("kernel32.dll");
if (kernel != NULL) {
s_stes = reinterpret_cast<STES_t>(GetProcAddress(kernel,
"SetThreadExecutionState"));
}
if (s_stes == NULL) {
s_stes = &CArchMiscWindows::dummySetThreadExecutionState;
}
}
s_stes(ES_DISPLAY_REQUIRED);
// restore the original execution states
setThreadExecutionState(s_busyState);
}
bool
CArchMiscWindows::wasLaunchedAsService()
{
CString name;
if (!getParentProcessName(name)) {
LOG((CLOG_ERR "cannot determine if process was launched as service"));
return false;
}
return (name == SERVICE_LAUNCHER);
}
bool
CArchMiscWindows::getParentProcessName(CString &name)
{
PROCESSENTRY32 parentEntry;
if (!getParentProcessEntry(parentEntry)){
LOG((CLOG_ERR "could not get entry for parent process"));
return false;
}
name = parentEntry.szExeFile;
return true;
}
BOOL WINAPI
CArchMiscWindows::getSelfProcessEntry(PROCESSENTRY32& entry)
{
// get entry from current PID
return getProcessEntry(entry, GetCurrentProcessId());
}
BOOL WINAPI
CArchMiscWindows::getParentProcessEntry(PROCESSENTRY32& entry)
{
// get the current process, so we can get parent PID
PROCESSENTRY32 selfEntry;
if (!getSelfProcessEntry(selfEntry)) {
return FALSE;
}
// get entry from parent PID
return getProcessEntry(entry, selfEntry.th32ParentProcessID);
}
BOOL WINAPI
CArchMiscWindows::getProcessEntry(PROCESSENTRY32& entry, DWORD processID)
{
// first we need to take a snapshot of the running processes
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snapshot == INVALID_HANDLE_VALUE) {
LOG((CLOG_ERR "could not get process snapshot (error: %i)",
GetLastError()));
return FALSE;
}
entry.dwSize = sizeof(PROCESSENTRY32);
// get the first process, and if we can't do that then it's
// unlikely we can go any further
BOOL gotEntry = Process32First(snapshot, &entry);
if (!gotEntry) {
LOG((CLOG_ERR "could not get first process entry (error: %i)",
GetLastError()));
return FALSE;
}
while(gotEntry) {
if (entry.th32ProcessID == processID) {
// found current process
return TRUE;
}
// now move on to the next entry (when we reach end, loop will stop)
gotEntry = Process32Next(snapshot, &entry);
}
return FALSE;
}
HINSTANCE
CArchMiscWindows::instanceWin32()
{
assert(s_instanceWin32 != NULL);
return s_instanceWin32;
}
void
CArchMiscWindows::setInstanceWin32(HINSTANCE instance)
{
assert(instance != NULL);
s_instanceWin32 = instance;
}

View File

@@ -0,0 +1,214 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHMISCWINDOWS_H
#define CARCHMISCWINDOWS_H
#define WIN32_LEAN_AND_MEAN
#include "common.h"
#include "stdstring.h"
#include "stdset.h"
#include <windows.h>
#include <Tlhelp32.h>
#include "CString.h"
//! Miscellaneous win32 functions.
class CArchMiscWindows {
public:
enum EValueType {
kUNKNOWN,
kNO_VALUE,
kUINT,
kSTRING,
kBINARY
};
enum EBusyModes {
kIDLE = 0x0000,
kSYSTEM = 0x0001,
kDISPLAY = 0x0002
};
typedef int (*RunFunc)(void);
//! Initialize
static void init();
//! Test if windows 95, et al.
/*!
Returns true iff the platform is win95/98/me.
*/
static bool isWindows95Family();
//! Test if windows 95, et al.
/*!
Returns true iff the platform is win98 or win2k or higher (i.e.
not windows 95 or windows NT).
*/
static bool isWindowsModern();
//! Set the application icons
/*!
Set the application icons.
*/
static void setIcons(HICON largeIcon, HICON smallIcon);
//! Get the application icons
/*!
Get the application icons.
*/
static void getIcons(HICON& largeIcon, HICON& smallIcon);
//! Run the daemon
/*!
Delegates to CArchDaemonWindows.
*/
static int runDaemon(RunFunc runFunc);
//! Indicate daemon is in main loop
/*!
Delegates to CArchDaemonWindows.
*/
static void daemonRunning(bool running);
//! Indicate failure of running daemon
/*!
Delegates to CArchDaemonWindows.
*/
static void daemonFailed(int result);
//! Get daemon quit message
/*!
Delegates to CArchDaemonWindows.
*/
static UINT getDaemonQuitMessage();
//! Open and return a registry key, closing the parent key
static HKEY openKey(HKEY parent, const TCHAR* child);
//! Open and return a registry key, closing the parent key
static HKEY openKey(HKEY parent, const TCHAR* const* keyPath);
//! Open/create and return a registry key, closing the parent key
static HKEY addKey(HKEY parent, const TCHAR* child);
//! Open/create and return a registry key, closing the parent key
static HKEY addKey(HKEY parent, const TCHAR* const* keyPath);
//! Close a key
static void closeKey(HKEY);
//! Delete a key (which should have no subkeys)
static void deleteKey(HKEY parent, const TCHAR* name);
//! Delete a value
static void deleteValue(HKEY parent, const TCHAR* name);
//! Test if a value exists
static bool hasValue(HKEY key, const TCHAR* name);
//! Get type of value
static EValueType typeOfValue(HKEY key, const TCHAR* name);
//! Set a string value in the registry
static void setValue(HKEY key, const TCHAR* name,
const std::string& value);
//! Set a DWORD value in the registry
static void setValue(HKEY key, const TCHAR* name, DWORD value);
//! Set a BINARY value in the registry
/*!
Sets the \p name value of \p key to \p value.data().
*/
static void setValueBinary(HKEY key, const TCHAR* name,
const std::string& value);
//! Read a string value from the registry
static std::string readValueString(HKEY, const TCHAR* name);
//! Read a DWORD value from the registry
static DWORD readValueInt(HKEY, const TCHAR* name);
//! Read a BINARY value from the registry
static std::string readValueBinary(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*);
//! Disable power saving
static void addBusyState(DWORD busyModes);
//! Enable power saving
static void removeBusyState(DWORD busyModes);
//! Briefly interrupt power saving
static void wakeupDisplay();
//! Returns true if this process was launched via NT service host.
static bool wasLaunchedAsService();
//! Returns true if we got the parent process name.
static bool getParentProcessName(CString &name);
static HINSTANCE instanceWin32();
static void setInstanceWin32(HINSTANCE instance);
static BOOL WINAPI getProcessEntry(PROCESSENTRY32& entry, DWORD processID);
static BOOL WINAPI getSelfProcessEntry(PROCESSENTRY32& entry);
static BOOL WINAPI getParentProcessEntry(PROCESSENTRY32& entry);
private:
//! Open and return a registry key, closing the parent key
static HKEY openKey(HKEY parent, const TCHAR* child, bool create);
//! Open and return a registry key, closing the parent key
static HKEY openKey(HKEY parent, const TCHAR* const* keyPath,
bool create);
//! Read a string value from the registry
static std::string readBinaryOrString(HKEY, const TCHAR* name, DWORD type);
//! Set thread busy state
static void setThreadExecutionState(DWORD);
static DWORD WINAPI dummySetThreadExecutionState(DWORD);
private:
typedef std::set<HWND> CDialogs;
typedef DWORD (WINAPI *STES_t)(DWORD);
static CDialogs* s_dialogs;
static DWORD s_busyState;
static STES_t s_stes;
static HICON s_largeIcon;
static HICON s_smallIcon;
static HINSTANCE s_instanceWin32;
};
#endif

View File

@@ -0,0 +1,809 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchMultithreadPosix.h"
#include "CArch.h"
#include "XArch.h"
#include <signal.h>
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#include <cerrno>
#define SIGWAKEUP SIGUSR1
#if !HAVE_PTHREAD_SIGNAL
// boy, is this platform broken. forget about pthread signal
// handling and let signals through to every process. synergy
// will not terminate cleanly when it gets SIGTERM or SIGINT.
# define pthread_sigmask sigprocmask
# define pthread_kill(tid_, sig_) kill(0, (sig_))
# define sigwait(set_, sig_)
# undef HAVE_POSIX_SIGWAIT
# define HAVE_POSIX_SIGWAIT 1
#endif
static
void
setSignalSet(sigset_t* sigset)
{
sigemptyset(sigset);
sigaddset(sigset, SIGHUP);
sigaddset(sigset, SIGINT);
sigaddset(sigset, SIGTERM);
sigaddset(sigset, SIGUSR2);
}
//
// CArchThreadImpl
//
class CArchThreadImpl {
public:
CArchThreadImpl();
public:
int m_refCount;
IArchMultithread::ThreadID m_id;
pthread_t m_thread;
IArchMultithread::ThreadFunc m_func;
void* m_userData;
bool m_cancel;
bool m_cancelling;
bool m_exited;
void* m_result;
void* m_networkData;
};
CArchThreadImpl::CArchThreadImpl() :
m_refCount(1),
m_id(0),
m_func(NULL),
m_userData(NULL),
m_cancel(false),
m_cancelling(false),
m_exited(false),
m_result(NULL),
m_networkData(NULL)
{
// do nothing
}
//
// CArchMultithreadPosix
//
CArchMultithreadPosix* CArchMultithreadPosix::s_instance = NULL;
CArchMultithreadPosix::CArchMultithreadPosix() :
m_newThreadCalled(false),
m_nextID(0)
{
assert(s_instance == NULL);
s_instance = this;
// no signal handlers
for (size_t i = 0; i < kNUM_SIGNALS; ++i) {
m_signalFunc[i] = NULL;
m_signalUserData[i] = NULL;
}
// create mutex for thread list
m_threadMutex = newMutex();
// create thread for calling (main) thread and add it to our
// list. no need to lock the mutex since we're the only thread.
m_mainThread = new CArchThreadImpl;
m_mainThread->m_thread = pthread_self();
insert(m_mainThread);
// install SIGWAKEUP handler. this causes SIGWAKEUP to interrupt
// system calls. we use that when cancelling a thread to force it
// to wake up immediately if it's blocked in a system call. we
// won't need this until another thread is created but it's fine
// to install it now.
struct sigaction act;
sigemptyset(&act.sa_mask);
# if defined(SA_INTERRUPT)
act.sa_flags = SA_INTERRUPT;
# else
act.sa_flags = 0;
# endif
act.sa_handler = &threadCancel;
sigaction(SIGWAKEUP, &act, NULL);
// set desired signal dispositions. let SIGWAKEUP through but
// ignore SIGPIPE (we'll handle EPIPE).
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGWAKEUP);
pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
sigemptyset(&sigset);
sigaddset(&sigset, SIGPIPE);
pthread_sigmask(SIG_BLOCK, &sigset, NULL);
}
CArchMultithreadPosix::~CArchMultithreadPosix()
{
assert(s_instance != NULL);
closeMutex(m_threadMutex);
s_instance = NULL;
}
void
CArchMultithreadPosix::setNetworkDataForCurrentThread(void* data)
{
lockMutex(m_threadMutex);
CArchThreadImpl* thread = find(pthread_self());
thread->m_networkData = data;
unlockMutex(m_threadMutex);
}
void*
CArchMultithreadPosix::getNetworkDataForThread(CArchThread thread)
{
lockMutex(m_threadMutex);
void* data = thread->m_networkData;
unlockMutex(m_threadMutex);
return data;
}
CArchMultithreadPosix*
CArchMultithreadPosix::getInstance()
{
return s_instance;
}
CArchCond
CArchMultithreadPosix::newCondVar()
{
CArchCondImpl* cond = new CArchCondImpl;
int status = pthread_cond_init(&cond->m_cond, NULL);
(void)status;
assert(status == 0);
return cond;
}
void
CArchMultithreadPosix::closeCondVar(CArchCond cond)
{
int status = pthread_cond_destroy(&cond->m_cond);
(void)status;
assert(status == 0);
delete cond;
}
void
CArchMultithreadPosix::signalCondVar(CArchCond cond)
{
int status = pthread_cond_signal(&cond->m_cond);
(void)status;
assert(status == 0);
}
void
CArchMultithreadPosix::broadcastCondVar(CArchCond cond)
{
int status = pthread_cond_broadcast(&cond->m_cond);
(void)status;
assert(status == 0);
}
bool
CArchMultithreadPosix::waitCondVar(CArchCond cond,
CArchMutex mutex, double timeout)
{
// we can't wait on a condition variable and also wake it up for
// cancellation since we don't use posix cancellation. so we
// must wake up periodically to check for cancellation. we
// can't simply go back to waiting after the check since the
// condition may have changed and we'll have lost the signal.
// so we have to return to the caller. since the caller will
// always check for spurious wakeups the only drawback here is
// performance: we're waking up a lot more than desired.
static const double maxCancellationLatency = 0.1;
if (timeout < 0.0 || timeout > maxCancellationLatency) {
timeout = maxCancellationLatency;
}
// see if we should cancel this thread
testCancelThread();
// get final time
struct timeval now;
gettimeofday(&now, NULL);
struct timespec finalTime;
finalTime.tv_sec = now.tv_sec;
finalTime.tv_nsec = now.tv_usec * 1000;
long timeout_sec = (long)timeout;
long timeout_nsec = (long)(1.0e+9 * (timeout - timeout_sec));
finalTime.tv_sec += timeout_sec;
finalTime.tv_nsec += timeout_nsec;
if (finalTime.tv_nsec >= 1000000000) {
finalTime.tv_nsec -= 1000000000;
finalTime.tv_sec += 1;
}
// wait
int status = pthread_cond_timedwait(&cond->m_cond,
&mutex->m_mutex, &finalTime);
// check for cancel again
testCancelThread();
switch (status) {
case 0:
// success
return true;
case ETIMEDOUT:
return false;
default:
assert(0 && "condition variable wait error");
return false;
}
}
CArchMutex
CArchMultithreadPosix::newMutex()
{
pthread_mutexattr_t attr;
int status = pthread_mutexattr_init(&attr);
assert(status == 0);
CArchMutexImpl* mutex = new CArchMutexImpl;
status = pthread_mutex_init(&mutex->m_mutex, &attr);
assert(status == 0);
return mutex;
}
void
CArchMultithreadPosix::closeMutex(CArchMutex mutex)
{
int status = pthread_mutex_destroy(&mutex->m_mutex);
(void)status;
assert(status == 0);
delete mutex;
}
void
CArchMultithreadPosix::lockMutex(CArchMutex mutex)
{
int status = pthread_mutex_lock(&mutex->m_mutex);
switch (status) {
case 0:
// success
return;
case EDEADLK:
assert(0 && "lock already owned");
break;
case EAGAIN:
assert(0 && "too many recursive locks");
break;
default:
assert(0 && "unexpected error");
break;
}
}
void
CArchMultithreadPosix::unlockMutex(CArchMutex mutex)
{
int status = pthread_mutex_unlock(&mutex->m_mutex);
switch (status) {
case 0:
// success
return;
case EPERM:
assert(0 && "thread doesn't own a lock");
break;
default:
assert(0 && "unexpected error");
break;
}
}
CArchThread
CArchMultithreadPosix::newThread(ThreadFunc func, void* data)
{
assert(func != NULL);
// initialize signal handler. we do this here instead of the
// constructor so we can avoid daemonizing (using fork())
// when there are multiple threads. clients can safely
// use condition variables and mutexes before creating a
// new thread and they can safely use the only thread
// they have access to, the main thread, so they really
// can't tell the difference.
if (!m_newThreadCalled) {
m_newThreadCalled = true;
#if HAVE_PTHREAD_SIGNAL
startSignalHandler();
#endif
}
lockMutex(m_threadMutex);
// create thread impl for new thread
CArchThreadImpl* thread = new CArchThreadImpl;
thread->m_func = func;
thread->m_userData = data;
// create the thread. pthread_create() on RedHat 7.2 smp fails
// if passed a NULL attr so use a default attr.
pthread_attr_t attr;
int status = pthread_attr_init(&attr);
if (status == 0) {
status = pthread_create(&thread->m_thread, &attr,
&CArchMultithreadPosix::threadFunc, thread);
pthread_attr_destroy(&attr);
}
// check if thread was started
if (status != 0) {
// failed to start thread so clean up
delete thread;
thread = NULL;
}
else {
// add thread to list
insert(thread);
// increment ref count to account for the thread itself
refThread(thread);
}
// note that the child thread will wait until we release this mutex
unlockMutex(m_threadMutex);
return thread;
}
CArchThread
CArchMultithreadPosix::newCurrentThread()
{
lockMutex(m_threadMutex);
CArchThreadImpl* thread = find(pthread_self());
unlockMutex(m_threadMutex);
assert(thread != NULL);
return thread;
}
void
CArchMultithreadPosix::closeThread(CArchThread thread)
{
assert(thread != NULL);
// decrement ref count and clean up thread if no more references
if (--thread->m_refCount == 0) {
// detach from thread (unless it's the main thread)
if (thread->m_func != NULL) {
pthread_detach(thread->m_thread);
}
// remove thread from list
lockMutex(m_threadMutex);
assert(findNoRef(thread->m_thread) == thread);
erase(thread);
unlockMutex(m_threadMutex);
// done with thread
delete thread;
}
}
CArchThread
CArchMultithreadPosix::copyThread(CArchThread thread)
{
refThread(thread);
return thread;
}
void
CArchMultithreadPosix::cancelThread(CArchThread thread)
{
assert(thread != NULL);
// set cancel and wakeup flags if thread can be cancelled
bool wakeup = false;
lockMutex(m_threadMutex);
if (!thread->m_exited && !thread->m_cancelling) {
thread->m_cancel = true;
wakeup = true;
}
unlockMutex(m_threadMutex);
// force thread to exit system calls if wakeup is true
if (wakeup) {
pthread_kill(thread->m_thread, SIGWAKEUP);
}
}
void
CArchMultithreadPosix::setPriorityOfThread(CArchThread thread, int /*n*/)
{
assert(thread != NULL);
// FIXME
}
void
CArchMultithreadPosix::testCancelThread()
{
// find current thread
lockMutex(m_threadMutex);
CArchThreadImpl* thread = findNoRef(pthread_self());
unlockMutex(m_threadMutex);
// test cancel on thread
testCancelThreadImpl(thread);
}
bool
CArchMultithreadPosix::wait(CArchThread target, double timeout)
{
assert(target != NULL);
lockMutex(m_threadMutex);
// find current thread
CArchThreadImpl* self = findNoRef(pthread_self());
// ignore wait if trying to wait on ourself
if (target == self) {
unlockMutex(m_threadMutex);
return false;
}
// ref the target so it can't go away while we're watching it
refThread(target);
unlockMutex(m_threadMutex);
try {
// do first test regardless of timeout
testCancelThreadImpl(self);
if (isExitedThread(target)) {
closeThread(target);
return true;
}
// wait and repeat test if there's a timeout
if (timeout != 0.0) {
const double start = ARCH->time();
do {
// wait a little
ARCH->sleep(0.05);
// repeat test
testCancelThreadImpl(self);
if (isExitedThread(target)) {
closeThread(target);
return true;
}
// repeat wait and test until timed out
} while (timeout < 0.0 || (ARCH->time() - start) <= timeout);
}
closeThread(target);
return false;
}
catch (...) {
closeThread(target);
throw;
}
}
bool
CArchMultithreadPosix::isSameThread(CArchThread thread1, CArchThread thread2)
{
return (thread1 == thread2);
}
bool
CArchMultithreadPosix::isExitedThread(CArchThread thread)
{
lockMutex(m_threadMutex);
bool exited = thread->m_exited;
unlockMutex(m_threadMutex);
return exited;
}
void*
CArchMultithreadPosix::getResultOfThread(CArchThread thread)
{
lockMutex(m_threadMutex);
void* result = thread->m_result;
unlockMutex(m_threadMutex);
return result;
}
IArchMultithread::ThreadID
CArchMultithreadPosix::getIDOfThread(CArchThread thread)
{
return thread->m_id;
}
void
CArchMultithreadPosix::setSignalHandler(
ESignal signal, SignalFunc func, void* userData)
{
lockMutex(m_threadMutex);
m_signalFunc[signal] = func;
m_signalUserData[signal] = userData;
unlockMutex(m_threadMutex);
}
void
CArchMultithreadPosix::raiseSignal(ESignal signal)
{
lockMutex(m_threadMutex);
if (m_signalFunc[signal] != NULL) {
m_signalFunc[signal](signal, m_signalUserData[signal]);
pthread_kill(m_mainThread->m_thread, SIGWAKEUP);
}
else if (signal == kINTERRUPT || signal == kTERMINATE) {
ARCH->cancelThread(m_mainThread);
}
unlockMutex(m_threadMutex);
}
void
CArchMultithreadPosix::startSignalHandler()
{
// set signal mask. the main thread blocks these signals and
// the signal handler thread will listen for them.
sigset_t sigset, oldsigset;
setSignalSet(&sigset);
pthread_sigmask(SIG_BLOCK, &sigset, &oldsigset);
// fire up the INT and TERM signal handler thread. we could
// instead arrange to catch and handle these signals but
// we'd be unable to cancel the main thread since no pthread
// calls are allowed in a signal handler.
pthread_attr_t attr;
int status = pthread_attr_init(&attr);
if (status == 0) {
status = pthread_create(&m_signalThread, &attr,
&CArchMultithreadPosix::threadSignalHandler,
NULL);
pthread_attr_destroy(&attr);
}
if (status != 0) {
// can't create thread to wait for signal so don't block
// the signals.
pthread_sigmask(SIG_UNBLOCK, &oldsigset, NULL);
}
}
CArchThreadImpl*
CArchMultithreadPosix::find(pthread_t thread)
{
CArchThreadImpl* impl = findNoRef(thread);
if (impl != NULL) {
refThread(impl);
}
return impl;
}
CArchThreadImpl*
CArchMultithreadPosix::findNoRef(pthread_t thread)
{
// linear search
for (CThreadList::const_iterator index = m_threadList.begin();
index != m_threadList.end(); ++index) {
if ((*index)->m_thread == thread) {
return *index;
}
}
return NULL;
}
void
CArchMultithreadPosix::insert(CArchThreadImpl* thread)
{
assert(thread != NULL);
// thread shouldn't already be on the list
assert(findNoRef(thread->m_thread) == NULL);
// set thread id. note that we don't worry about m_nextID
// wrapping back to 0 and duplicating thread ID's since the
// likelihood of synergy running that long is vanishingly
// small.
thread->m_id = ++m_nextID;
// append to list
m_threadList.push_back(thread);
}
void
CArchMultithreadPosix::erase(CArchThreadImpl* thread)
{
for (CThreadList::iterator index = m_threadList.begin();
index != m_threadList.end(); ++index) {
if (*index == thread) {
m_threadList.erase(index);
break;
}
}
}
void
CArchMultithreadPosix::refThread(CArchThreadImpl* thread)
{
assert(thread != NULL);
assert(findNoRef(thread->m_thread) != NULL);
++thread->m_refCount;
}
void
CArchMultithreadPosix::testCancelThreadImpl(CArchThreadImpl* thread)
{
assert(thread != NULL);
// update cancel state
lockMutex(m_threadMutex);
bool cancel = false;
if (thread->m_cancel && !thread->m_cancelling) {
thread->m_cancelling = true;
thread->m_cancel = false;
cancel = true;
}
unlockMutex(m_threadMutex);
// unwind thread's stack if cancelling
if (cancel) {
throw XThreadCancel();
}
}
void*
CArchMultithreadPosix::threadFunc(void* vrep)
{
// get the thread
CArchThreadImpl* thread = reinterpret_cast<CArchThreadImpl*>(vrep);
// setup pthreads
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
// run thread
s_instance->doThreadFunc(thread);
// terminate the thread
return NULL;
}
void
CArchMultithreadPosix::doThreadFunc(CArchThread thread)
{
// default priority is slightly below normal
setPriorityOfThread(thread, 1);
// wait for parent to initialize this object
lockMutex(m_threadMutex);
unlockMutex(m_threadMutex);
void* result = NULL;
try {
// go
result = (*thread->m_func)(thread->m_userData);
}
catch (XThreadCancel&) {
// client called cancel()
}
catch (...) {
// note -- don't catch (...) to avoid masking bugs
lockMutex(m_threadMutex);
thread->m_exited = true;
unlockMutex(m_threadMutex);
closeThread(thread);
throw;
}
// thread has exited
lockMutex(m_threadMutex);
thread->m_result = result;
thread->m_exited = true;
unlockMutex(m_threadMutex);
// done with thread
closeThread(thread);
}
void
CArchMultithreadPosix::threadCancel(int)
{
// do nothing
}
void*
CArchMultithreadPosix::threadSignalHandler(void*)
{
// detach
pthread_detach(pthread_self());
// add signal to mask
sigset_t sigset;
setSignalSet(&sigset);
// also wait on SIGABRT. on linux (others?) this thread (process)
// will persist after all the other threads evaporate due to an
// assert unless we wait on SIGABRT. that means our resources (like
// the socket we're listening on) are not released and never will be
// until the lingering thread is killed. i don't know why sigwait()
// should protect the thread from being killed. note that sigwait()
// doesn't actually return if we receive SIGABRT and, for some
// reason, we don't have to block SIGABRT.
sigaddset(&sigset, SIGABRT);
// we exit the loop via thread cancellation in sigwait()
for (;;) {
// wait
#if HAVE_POSIX_SIGWAIT
int signal = 0;
sigwait(&sigset, &signal);
#else
sigwait(&sigset);
#endif
// if we get here then the signal was raised
switch (signal) {
case SIGINT:
ARCH->raiseSignal(kINTERRUPT);
break;
case SIGTERM:
ARCH->raiseSignal(kTERMINATE);
break;
case SIGHUP:
ARCH->raiseSignal(kHANGUP);
break;
case SIGUSR2:
ARCH->raiseSignal(kUSER);
break;
default:
// ignore
break;
}
}
return NULL;
}

View File

@@ -0,0 +1,116 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHMULTITHREADPOSIX_H
#define CARCHMULTITHREADPOSIX_H
#include "IArchMultithread.h"
#include "stdlist.h"
#include <pthread.h>
#define ARCH_MULTITHREAD CArchMultithreadPosix
class CArchCondImpl {
public:
pthread_cond_t m_cond;
};
class CArchMutexImpl {
public:
pthread_mutex_t m_mutex;
};
//! Posix implementation of IArchMultithread
class CArchMultithreadPosix : public IArchMultithread {
public:
CArchMultithreadPosix();
virtual ~CArchMultithreadPosix();
//! @name manipulators
//@{
void setNetworkDataForCurrentThread(void*);
//@}
//! @name accessors
//@{
void* getNetworkDataForThread(CArchThread);
static CArchMultithreadPosix* getInstance();
//@}
// IArchMultithread overrides
virtual CArchCond newCondVar();
virtual void closeCondVar(CArchCond);
virtual void signalCondVar(CArchCond);
virtual void broadcastCondVar(CArchCond);
virtual bool waitCondVar(CArchCond, CArchMutex, double timeout);
virtual CArchMutex newMutex();
virtual void closeMutex(CArchMutex);
virtual void lockMutex(CArchMutex);
virtual void unlockMutex(CArchMutex);
virtual CArchThread newThread(ThreadFunc, void*);
virtual CArchThread newCurrentThread();
virtual CArchThread copyThread(CArchThread);
virtual void closeThread(CArchThread);
virtual void cancelThread(CArchThread);
virtual void setPriorityOfThread(CArchThread, int n);
virtual void testCancelThread();
virtual bool wait(CArchThread, double timeout);
virtual bool isSameThread(CArchThread, CArchThread);
virtual bool isExitedThread(CArchThread);
virtual void* getResultOfThread(CArchThread);
virtual ThreadID getIDOfThread(CArchThread);
virtual void setSignalHandler(ESignal, SignalFunc, void*);
virtual void raiseSignal(ESignal);
private:
void startSignalHandler();
CArchThreadImpl* find(pthread_t thread);
CArchThreadImpl* findNoRef(pthread_t thread);
void insert(CArchThreadImpl* thread);
void erase(CArchThreadImpl* thread);
void refThread(CArchThreadImpl* rep);
void testCancelThreadImpl(CArchThreadImpl* rep);
void doThreadFunc(CArchThread thread);
static void* threadFunc(void* vrep);
static void threadCancel(int);
static void* threadSignalHandler(void* vrep);
private:
typedef std::list<CArchThread> CThreadList;
static CArchMultithreadPosix* s_instance;
bool m_newThreadCalled;
CArchMutex m_threadMutex;
CArchThread m_mainThread;
CThreadList m_threadList;
ThreadID m_nextID;
pthread_t m_signalThread;
SignalFunc m_signalFunc[kNUM_SIGNALS];
void* m_signalUserData[kNUM_SIGNALS];
};
#endif

View File

@@ -0,0 +1,702 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(_MSC_VER) && !defined(_MT)
# error multithreading compile option is required
#endif
#include "CArchMultithreadWindows.h"
#include "CArch.h"
#include "XArch.h"
#include <process.h>
//
// note -- implementation of condition variable taken from:
// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
// titled "Strategies for Implementing POSIX Condition Variables
// on Win32." it also provides an implementation that doesn't
// suffer from the incorrectness problem described in our
// corresponding header but it is slower, still unfair, and
// can cause busy waiting.
//
//
// CArchThreadImpl
//
class CArchThreadImpl {
public:
CArchThreadImpl();
~CArchThreadImpl();
public:
int m_refCount;
HANDLE m_thread;
DWORD m_id;
IArchMultithread::ThreadFunc m_func;
void* m_userData;
HANDLE m_cancel;
bool m_cancelling;
HANDLE m_exit;
void* m_result;
void* m_networkData;
};
CArchThreadImpl::CArchThreadImpl() :
m_refCount(1),
m_thread(NULL),
m_id(0),
m_func(NULL),
m_userData(NULL),
m_cancelling(false),
m_result(NULL),
m_networkData(NULL)
{
m_exit = CreateEvent(NULL, TRUE, FALSE, NULL);
m_cancel = CreateEvent(NULL, TRUE, FALSE, NULL);
}
CArchThreadImpl::~CArchThreadImpl()
{
CloseHandle(m_exit);
CloseHandle(m_cancel);
}
//
// CArchMultithreadWindows
//
CArchMultithreadWindows* CArchMultithreadWindows::s_instance = NULL;
CArchMultithreadWindows::CArchMultithreadWindows()
{
assert(s_instance == NULL);
s_instance = this;
// no signal handlers
for (size_t i = 0; i < kNUM_SIGNALS; ++i) {
m_signalFunc[i] = NULL;
m_signalUserData[i] = NULL;
}
// create mutex for thread list
m_threadMutex = newMutex();
// create thread for calling (main) thread and add it to our
// list. no need to lock the mutex since we're the only thread.
m_mainThread = new CArchThreadImpl;
m_mainThread->m_thread = NULL;
m_mainThread->m_id = GetCurrentThreadId();
insert(m_mainThread);
}
CArchMultithreadWindows::~CArchMultithreadWindows()
{
s_instance = NULL;
// clean up thread list
for (CThreadList::iterator index = m_threadList.begin();
index != m_threadList.end(); ++index) {
delete *index;
}
// done with mutex
delete m_threadMutex;
}
void
CArchMultithreadWindows::setNetworkDataForCurrentThread(void* data)
{
lockMutex(m_threadMutex);
CArchThreadImpl* thread = findNoRef(GetCurrentThreadId());
thread->m_networkData = data;
unlockMutex(m_threadMutex);
}
void*
CArchMultithreadWindows::getNetworkDataForThread(CArchThread thread)
{
lockMutex(m_threadMutex);
void* data = thread->m_networkData;
unlockMutex(m_threadMutex);
return data;
}
HANDLE
CArchMultithreadWindows::getCancelEventForCurrentThread()
{
lockMutex(m_threadMutex);
CArchThreadImpl* thread = findNoRef(GetCurrentThreadId());
unlockMutex(m_threadMutex);
return thread->m_cancel;
}
CArchMultithreadWindows*
CArchMultithreadWindows::getInstance()
{
return s_instance;
}
CArchCond
CArchMultithreadWindows::newCondVar()
{
CArchCondImpl* cond = new CArchCondImpl;
cond->m_events[CArchCondImpl::kSignal] = CreateEvent(NULL,
FALSE, FALSE, NULL);
cond->m_events[CArchCondImpl::kBroadcast] = CreateEvent(NULL,
TRUE, FALSE, NULL);
cond->m_waitCountMutex = newMutex();
cond->m_waitCount = 0;
return cond;
}
void
CArchMultithreadWindows::closeCondVar(CArchCond cond)
{
CloseHandle(cond->m_events[CArchCondImpl::kSignal]);
CloseHandle(cond->m_events[CArchCondImpl::kBroadcast]);
closeMutex(cond->m_waitCountMutex);
delete cond;
}
void
CArchMultithreadWindows::signalCondVar(CArchCond cond)
{
// is anybody waiting?
lockMutex(cond->m_waitCountMutex);
const bool hasWaiter = (cond->m_waitCount > 0);
unlockMutex(cond->m_waitCountMutex);
// wake one thread if anybody is waiting
if (hasWaiter) {
SetEvent(cond->m_events[CArchCondImpl::kSignal]);
}
}
void
CArchMultithreadWindows::broadcastCondVar(CArchCond cond)
{
// is anybody waiting?
lockMutex(cond->m_waitCountMutex);
const bool hasWaiter = (cond->m_waitCount > 0);
unlockMutex(cond->m_waitCountMutex);
// wake all threads if anybody is waiting
if (hasWaiter) {
SetEvent(cond->m_events[CArchCondImpl::kBroadcast]);
}
}
bool
CArchMultithreadWindows::waitCondVar(CArchCond cond,
CArchMutex mutex, double timeout)
{
// prepare to wait
const DWORD winTimeout = (timeout < 0.0) ? INFINITE :
static_cast<DWORD>(1000.0 * timeout);
// make a list of the condition variable events and the cancel event
// for the current thread.
HANDLE handles[4];
handles[0] = cond->m_events[CArchCondImpl::kSignal];
handles[1] = cond->m_events[CArchCondImpl::kBroadcast];
handles[2] = getCancelEventForCurrentThread();
// update waiter count
lockMutex(cond->m_waitCountMutex);
++cond->m_waitCount;
unlockMutex(cond->m_waitCountMutex);
// release mutex. this should be atomic with the wait so that it's
// impossible for another thread to signal us between the unlock and
// the wait, which would lead to a lost signal on broadcasts.
// however, we're using a manual reset event for broadcasts which
// stays set until we reset it, so we don't lose the broadcast.
unlockMutex(mutex);
// wait for a signal or broadcast
DWORD result = WaitForMultipleObjects(3, handles, FALSE, winTimeout);
// cancel takes priority
if (result != WAIT_OBJECT_0 + 2 &&
WaitForSingleObject(handles[2], 0) == WAIT_OBJECT_0) {
result = WAIT_OBJECT_0 + 2;
}
// update the waiter count and check if we're the last waiter
lockMutex(cond->m_waitCountMutex);
--cond->m_waitCount;
const bool last = (result == WAIT_OBJECT_0 + 1 && cond->m_waitCount == 0);
unlockMutex(cond->m_waitCountMutex);
// reset the broadcast event if we're the last waiter
if (last) {
ResetEvent(cond->m_events[CArchCondImpl::kBroadcast]);
}
// reacquire the mutex
lockMutex(mutex);
// cancel thread if necessary
if (result == WAIT_OBJECT_0 + 2) {
ARCH->testCancelThread();
}
// return success or failure
return (result == WAIT_OBJECT_0 + 0 ||
result == WAIT_OBJECT_0 + 1);
}
CArchMutex
CArchMultithreadWindows::newMutex()
{
CArchMutexImpl* mutex = new CArchMutexImpl;
InitializeCriticalSection(&mutex->m_mutex);
return mutex;
}
void
CArchMultithreadWindows::closeMutex(CArchMutex mutex)
{
DeleteCriticalSection(&mutex->m_mutex);
delete mutex;
}
void
CArchMultithreadWindows::lockMutex(CArchMutex mutex)
{
EnterCriticalSection(&mutex->m_mutex);
}
void
CArchMultithreadWindows::unlockMutex(CArchMutex mutex)
{
LeaveCriticalSection(&mutex->m_mutex);
}
CArchThread
CArchMultithreadWindows::newThread(ThreadFunc func, void* data)
{
lockMutex(m_threadMutex);
// create thread impl for new thread
CArchThreadImpl* thread = new CArchThreadImpl;
thread->m_func = func;
thread->m_userData = data;
// create thread
unsigned int id = 0;
thread->m_thread = reinterpret_cast<HANDLE>(_beginthreadex(NULL, 0,
threadFunc, (void*)thread, 0, &id));
thread->m_id = static_cast<DWORD>(id);
// check if thread was started
if (thread->m_thread == 0) {
// failed to start thread so clean up
delete thread;
thread = NULL;
}
else {
// add thread to list
insert(thread);
// increment ref count to account for the thread itself
refThread(thread);
}
// note that the child thread will wait until we release this mutex
unlockMutex(m_threadMutex);
return thread;
}
CArchThread
CArchMultithreadWindows::newCurrentThread()
{
lockMutex(m_threadMutex);
CArchThreadImpl* thread = find(GetCurrentThreadId());
unlockMutex(m_threadMutex);
assert(thread != NULL);
return thread;
}
void
CArchMultithreadWindows::closeThread(CArchThread thread)
{
assert(thread != NULL);
// decrement ref count and clean up thread if no more references
if (--thread->m_refCount == 0) {
// close the handle (main thread has a NULL handle)
if (thread->m_thread != NULL) {
CloseHandle(thread->m_thread);
}
// remove thread from list
lockMutex(m_threadMutex);
assert(findNoRefOrCreate(thread->m_id) == thread);
erase(thread);
unlockMutex(m_threadMutex);
// done with thread
delete thread;
}
}
CArchThread
CArchMultithreadWindows::copyThread(CArchThread thread)
{
refThread(thread);
return thread;
}
void
CArchMultithreadWindows::cancelThread(CArchThread thread)
{
assert(thread != NULL);
// set cancel flag
SetEvent(thread->m_cancel);
}
void
CArchMultithreadWindows::setPriorityOfThread(CArchThread thread, int n)
{
struct CPriorityInfo {
public:
DWORD m_class;
int m_level;
};
static const CPriorityInfo s_pClass[] = {
{ IDLE_PRIORITY_CLASS, THREAD_PRIORITY_IDLE },
{ IDLE_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST },
{ IDLE_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL },
{ IDLE_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL },
{ IDLE_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL },
{ IDLE_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST },
{ NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST },
{ NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL },
{ NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL },
{ NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL },
{ NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST },
{ HIGH_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST },
{ HIGH_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL },
{ HIGH_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL },
{ HIGH_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL },
{ HIGH_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST },
{ REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_IDLE },
{ REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST },
{ REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL },
{ REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL },
{ REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL },
{ REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST },
{ REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_TIME_CRITICAL}
};
#if defined(_DEBUG)
// don't use really high priorities when debugging
static const size_t s_pMax = 13;
#else
static const size_t s_pMax = sizeof(s_pClass) / sizeof(s_pClass[0]) - 1;
#endif
static const size_t s_pBase = 8; // index of normal priority
assert(thread != NULL);
size_t index;
if (n > 0 && s_pBase < (size_t)n) {
// lowest priority
index = 0;
}
else {
index = (size_t)((int)s_pBase - n);
if (index > s_pMax) {
// highest priority
index = s_pMax;
}
}
SetPriorityClass(GetCurrentProcess(), s_pClass[index].m_class);
SetThreadPriority(thread->m_thread, s_pClass[index].m_level);
}
void
CArchMultithreadWindows::testCancelThread()
{
// find current thread
lockMutex(m_threadMutex);
CArchThreadImpl* thread = findNoRef(GetCurrentThreadId());
unlockMutex(m_threadMutex);
// test cancel on thread
testCancelThreadImpl(thread);
}
bool
CArchMultithreadWindows::wait(CArchThread target, double timeout)
{
assert(target != NULL);
lockMutex(m_threadMutex);
// find current thread
CArchThreadImpl* self = findNoRef(GetCurrentThreadId());
// ignore wait if trying to wait on ourself
if (target == self) {
unlockMutex(m_threadMutex);
return false;
}
// ref the target so it can't go away while we're watching it
refThread(target);
unlockMutex(m_threadMutex);
// convert timeout
DWORD t;
if (timeout < 0.0) {
t = INFINITE;
}
else {
t = (DWORD)(1000.0 * timeout);
}
// wait for this thread to be cancelled or woken up or for the
// target thread to terminate.
HANDLE handles[2];
handles[0] = target->m_exit;
handles[1] = self->m_cancel;
DWORD result = WaitForMultipleObjects(2, handles, FALSE, t);
// cancel takes priority
if (result != WAIT_OBJECT_0 + 1 &&
WaitForSingleObject(handles[1], 0) == WAIT_OBJECT_0) {
result = WAIT_OBJECT_0 + 1;
}
// release target
closeThread(target);
// handle result
switch (result) {
case WAIT_OBJECT_0 + 0:
// target thread terminated
return true;
case WAIT_OBJECT_0 + 1:
// this thread was cancelled. does not return.
testCancelThreadImpl(self);
default:
// timeout or error
return false;
}
}
bool
CArchMultithreadWindows::isSameThread(CArchThread thread1, CArchThread thread2)
{
return (thread1 == thread2);
}
bool
CArchMultithreadWindows::isExitedThread(CArchThread thread)
{
// poll exit event
return (WaitForSingleObject(thread->m_exit, 0) == WAIT_OBJECT_0);
}
void*
CArchMultithreadWindows::getResultOfThread(CArchThread thread)
{
lockMutex(m_threadMutex);
void* result = thread->m_result;
unlockMutex(m_threadMutex);
return result;
}
IArchMultithread::ThreadID
CArchMultithreadWindows::getIDOfThread(CArchThread thread)
{
return static_cast<ThreadID>(thread->m_id);
}
void
CArchMultithreadWindows::setSignalHandler(
ESignal signal, SignalFunc func, void* userData)
{
lockMutex(m_threadMutex);
m_signalFunc[signal] = func;
m_signalUserData[signal] = userData;
unlockMutex(m_threadMutex);
}
void
CArchMultithreadWindows::raiseSignal(ESignal signal)
{
lockMutex(m_threadMutex);
if (m_signalFunc[signal] != NULL) {
m_signalFunc[signal](signal, m_signalUserData[signal]);
ARCH->unblockPollSocket(m_mainThread);
}
else if (signal == kINTERRUPT || signal == kTERMINATE) {
ARCH->cancelThread(m_mainThread);
}
unlockMutex(m_threadMutex);
}
CArchThreadImpl*
CArchMultithreadWindows::find(DWORD id)
{
CArchThreadImpl* impl = findNoRef(id);
if (impl != NULL) {
refThread(impl);
}
return impl;
}
CArchThreadImpl*
CArchMultithreadWindows::findNoRef(DWORD id)
{
CArchThreadImpl* impl = findNoRefOrCreate(id);
if (impl == NULL) {
// create thread for calling thread which isn't in our list and
// add it to the list. this won't normally happen but it can if
// the system calls us under a new thread, like it does when we
// run as a service.
impl = new CArchThreadImpl;
impl->m_thread = NULL;
impl->m_id = GetCurrentThreadId();
insert(impl);
}
return impl;
}
CArchThreadImpl*
CArchMultithreadWindows::findNoRefOrCreate(DWORD id)
{
// linear search
for (CThreadList::const_iterator index = m_threadList.begin();
index != m_threadList.end(); ++index) {
if ((*index)->m_id == id) {
return *index;
}
}
return NULL;
}
void
CArchMultithreadWindows::insert(CArchThreadImpl* thread)
{
assert(thread != NULL);
// thread shouldn't already be on the list
assert(findNoRefOrCreate(thread->m_id) == NULL);
// append to list
m_threadList.push_back(thread);
}
void
CArchMultithreadWindows::erase(CArchThreadImpl* thread)
{
for (CThreadList::iterator index = m_threadList.begin();
index != m_threadList.end(); ++index) {
if (*index == thread) {
m_threadList.erase(index);
break;
}
}
}
void
CArchMultithreadWindows::refThread(CArchThreadImpl* thread)
{
assert(thread != NULL);
assert(findNoRefOrCreate(thread->m_id) != NULL);
++thread->m_refCount;
}
void
CArchMultithreadWindows::testCancelThreadImpl(CArchThreadImpl* thread)
{
assert(thread != NULL);
// poll cancel event. return if not set.
const DWORD result = WaitForSingleObject(thread->m_cancel, 0);
if (result != WAIT_OBJECT_0) {
return;
}
// update cancel state
lockMutex(m_threadMutex);
bool cancel = !thread->m_cancelling;
thread->m_cancelling = true;
ResetEvent(thread->m_cancel);
unlockMutex(m_threadMutex);
// unwind thread's stack if cancelling
if (cancel) {
throw XThreadCancel();
}
}
unsigned int __stdcall
CArchMultithreadWindows::threadFunc(void* vrep)
{
// get the thread
CArchThreadImpl* thread = reinterpret_cast<CArchThreadImpl*>(vrep);
// run thread
s_instance->doThreadFunc(thread);
// terminate the thread
return 0;
}
void
CArchMultithreadWindows::doThreadFunc(CArchThread thread)
{
// wait for parent to initialize this object
lockMutex(m_threadMutex);
unlockMutex(m_threadMutex);
void* result = NULL;
try {
// go
result = (*thread->m_func)(thread->m_userData);
}
catch (XThreadCancel&) {
// client called cancel()
}
catch (...) {
// note -- don't catch (...) to avoid masking bugs
SetEvent(thread->m_exit);
closeThread(thread);
throw;
}
// thread has exited
lockMutex(m_threadMutex);
thread->m_result = result;
unlockMutex(m_threadMutex);
SetEvent(thread->m_exit);
// done with thread
closeThread(thread);
}

View File

@@ -0,0 +1,118 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHMULTITHREADWINDOWS_H
#define CARCHMULTITHREADWINDOWS_H
#define WIN32_LEAN_AND_MEAN
#include "IArchMultithread.h"
#include "stdlist.h"
#include <windows.h>
#define ARCH_MULTITHREAD CArchMultithreadWindows
class CArchCondImpl {
public:
enum { kSignal = 0, kBroadcast };
HANDLE m_events[2];
mutable int m_waitCount;
CArchMutex m_waitCountMutex;
};
class CArchMutexImpl {
public:
CRITICAL_SECTION m_mutex;
};
//! Win32 implementation of IArchMultithread
class CArchMultithreadWindows : public IArchMultithread {
public:
CArchMultithreadWindows();
virtual ~CArchMultithreadWindows();
//! @name manipulators
//@{
void setNetworkDataForCurrentThread(void*);
//@}
//! @name accessors
//@{
HANDLE getCancelEventForCurrentThread();
void* getNetworkDataForThread(CArchThread);
static CArchMultithreadWindows* getInstance();
//@}
// IArchMultithread overrides
virtual CArchCond newCondVar();
virtual void closeCondVar(CArchCond);
virtual void signalCondVar(CArchCond);
virtual void broadcastCondVar(CArchCond);
virtual bool waitCondVar(CArchCond, CArchMutex, double timeout);
virtual CArchMutex newMutex();
virtual void closeMutex(CArchMutex);
virtual void lockMutex(CArchMutex);
virtual void unlockMutex(CArchMutex);
virtual CArchThread newThread(ThreadFunc, void*);
virtual CArchThread newCurrentThread();
virtual CArchThread copyThread(CArchThread);
virtual void closeThread(CArchThread);
virtual void cancelThread(CArchThread);
virtual void setPriorityOfThread(CArchThread, int n);
virtual void testCancelThread();
virtual bool wait(CArchThread, double timeout);
virtual bool isSameThread(CArchThread, CArchThread);
virtual bool isExitedThread(CArchThread);
virtual void* getResultOfThread(CArchThread);
virtual ThreadID getIDOfThread(CArchThread);
virtual void setSignalHandler(ESignal, SignalFunc, void*);
virtual void raiseSignal(ESignal);
private:
CArchThreadImpl* find(DWORD id);
CArchThreadImpl* findNoRef(DWORD id);
CArchThreadImpl* findNoRefOrCreate(DWORD id);
void insert(CArchThreadImpl* thread);
void erase(CArchThreadImpl* thread);
void refThread(CArchThreadImpl* rep);
void testCancelThreadImpl(CArchThreadImpl* rep);
void doThreadFunc(CArchThread thread);
static unsigned int __stdcall threadFunc(void* vrep);
private:
typedef std::list<CArchThread> CThreadList;
static CArchMultithreadWindows* s_instance;
CArchMutex m_threadMutex;
CThreadList m_threadList;
CArchThread m_mainThread;
SignalFunc m_signalFunc[kNUM_SIGNALS];
void* m_signalUserData[kNUM_SIGNALS];
};
#endif

View File

@@ -0,0 +1,986 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchNetworkBSD.h"
#include "CArch.h"
#include "CArchMultithreadPosix.h"
#include "XArchUnix.h"
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <netinet/in.h>
#include <netdb.h>
#if !defined(TCP_NODELAY)
# include <netinet/tcp.h>
#endif
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#if HAVE_POLL
# include <poll.h>
#else
# if HAVE_SYS_SELECT_H
# include <sys/select.h>
# endif
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# endif
#endif
#if !HAVE_INET_ATON
# include <stdio.h>
#endif
static const int s_family[] = {
PF_UNSPEC,
PF_INET
};
static const int s_type[] = {
SOCK_DGRAM,
SOCK_STREAM
};
#if !HAVE_INET_ATON
// parse dotted quad addresses. we don't bother with the weird BSD'ism
// of handling octal and hex and partial forms.
static
in_addr_t
inet_aton(const char* cp, struct in_addr* inp)
{
unsigned int a, b, c, d;
if (sscanf(cp, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) {
return 0;
}
if (a >= 256 || b >= 256 || c >= 256 || d >= 256) {
return 0;
}
unsigned char* incp = (unsigned char*)inp;
incp[0] = (unsigned char)(a & 0xffu);
incp[1] = (unsigned char)(b & 0xffu);
incp[2] = (unsigned char)(c & 0xffu);
incp[3] = (unsigned char)(d & 0xffu);
return inp->s_addr;
}
#endif
//
// CArchNetworkBSD
//
CArchNetworkBSD::CArchNetworkBSD()
{
}
CArchNetworkBSD::~CArchNetworkBSD()
{
ARCH->closeMutex(m_mutex);
}
void
CArchNetworkBSD::init()
{
// create mutex to make some calls thread safe
m_mutex = ARCH->newMutex();
}
CArchSocket
CArchNetworkBSD::newSocket(EAddressFamily family, ESocketType type)
{
// create socket
int fd = socket(s_family[family], s_type[type], 0);
if (fd == -1) {
throwError(errno);
}
try {
setBlockingOnSocket(fd, false);
}
catch (...) {
close(fd);
throw;
}
// allocate socket object
CArchSocketImpl* newSocket = new CArchSocketImpl;
newSocket->m_fd = fd;
newSocket->m_refCount = 1;
return newSocket;
}
CArchSocket
CArchNetworkBSD::copySocket(CArchSocket s)
{
assert(s != NULL);
// ref the socket and return it
ARCH->lockMutex(m_mutex);
++s->m_refCount;
ARCH->unlockMutex(m_mutex);
return s;
}
void
CArchNetworkBSD::closeSocket(CArchSocket s)
{
assert(s != NULL);
// unref the socket and note if it should be released
ARCH->lockMutex(m_mutex);
const bool doClose = (--s->m_refCount == 0);
ARCH->unlockMutex(m_mutex);
// close the socket if necessary
if (doClose) {
if (close(s->m_fd) == -1) {
// close failed. restore the last ref and throw.
int err = errno;
ARCH->lockMutex(m_mutex);
++s->m_refCount;
ARCH->unlockMutex(m_mutex);
throwError(err);
}
delete s;
}
}
void
CArchNetworkBSD::closeSocketForRead(CArchSocket s)
{
assert(s != NULL);
if (shutdown(s->m_fd, 0) == -1) {
if (errno != ENOTCONN) {
throwError(errno);
}
}
}
void
CArchNetworkBSD::closeSocketForWrite(CArchSocket s)
{
assert(s != NULL);
if (shutdown(s->m_fd, 1) == -1) {
if (errno != ENOTCONN) {
throwError(errno);
}
}
}
void
CArchNetworkBSD::bindSocket(CArchSocket s, CArchNetAddress addr)
{
assert(s != NULL);
assert(addr != NULL);
if (bind(s->m_fd, &addr->m_addr, addr->m_len) == -1) {
throwError(errno);
}
}
void
CArchNetworkBSD::listenOnSocket(CArchSocket s)
{
assert(s != NULL);
// hardcoding backlog
if (listen(s->m_fd, 3) == -1) {
throwError(errno);
}
}
CArchSocket
CArchNetworkBSD::acceptSocket(CArchSocket s, CArchNetAddress* addr)
{
assert(s != NULL);
// if user passed NULL in addr then use scratch space
CArchNetAddress dummy;
if (addr == NULL) {
addr = &dummy;
}
// create new socket and address
CArchSocketImpl* newSocket = new CArchSocketImpl;
*addr = new CArchNetAddressImpl;
// accept on socket
ACCEPT_TYPE_ARG3 len = (ACCEPT_TYPE_ARG3)((*addr)->m_len);
int fd = accept(s->m_fd, &(*addr)->m_addr, &len);
(*addr)->m_len = (socklen_t)len;
if (fd == -1) {
int err = errno;
delete newSocket;
delete *addr;
*addr = NULL;
if (err == EAGAIN) {
return NULL;
}
throwError(err);
}
try {
setBlockingOnSocket(fd, false);
}
catch (...) {
close(fd);
delete newSocket;
delete *addr;
*addr = NULL;
throw;
}
// initialize socket
newSocket->m_fd = fd;
newSocket->m_refCount = 1;
// discard address if not requested
if (addr == &dummy) {
ARCH->closeAddr(dummy);
}
return newSocket;
}
bool
CArchNetworkBSD::connectSocket(CArchSocket s, CArchNetAddress addr)
{
assert(s != NULL);
assert(addr != NULL);
if (connect(s->m_fd, &addr->m_addr, addr->m_len) == -1) {
if (errno == EISCONN) {
return true;
}
if (errno == EINPROGRESS) {
return false;
}
throwError(errno);
}
return true;
}
#if HAVE_POLL
int
CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout)
{
assert(pe != NULL || num == 0);
// return if nothing to do
if (num == 0) {
if (timeout > 0.0) {
ARCH->sleep(timeout);
}
return 0;
}
// allocate space for translated query
struct pollfd* pfd = new struct pollfd[1 + num];
// translate query
for (int i = 0; i < num; ++i) {
pfd[i].fd = (pe[i].m_socket == NULL) ? -1 : pe[i].m_socket->m_fd;
pfd[i].events = 0;
if ((pe[i].m_events & kPOLLIN) != 0) {
pfd[i].events |= POLLIN;
}
if ((pe[i].m_events & kPOLLOUT) != 0) {
pfd[i].events |= POLLOUT;
}
}
int n = num;
// add the unblock pipe
const int* unblockPipe = getUnblockPipe();
if (unblockPipe != NULL) {
pfd[n].fd = unblockPipe[0];
pfd[n].events = POLLIN;
++n;
}
// prepare timeout
int t = (timeout < 0.0) ? -1 : static_cast<int>(1000.0 * timeout);
// do the poll
n = poll(pfd, n, t);
// reset the unblock pipe
if (n > 0 && unblockPipe != NULL && (pfd[num].revents & POLLIN) != 0) {
// the unblock event was signalled. flush the pipe.
char dummy[100];
int ignore;
do {
ignore = read(unblockPipe[0], dummy, sizeof(dummy));
} while (errno != EAGAIN);
// don't count this unblock pipe in return value
--n;
}
// handle results
if (n == -1) {
if (errno == EINTR) {
// interrupted system call
ARCH->testCancelThread();
delete[] pfd;
return 0;
}
delete[] pfd;
throwError(errno);
}
// translate back
for (int i = 0; i < num; ++i) {
pe[i].m_revents = 0;
if ((pfd[i].revents & POLLIN) != 0) {
pe[i].m_revents |= kPOLLIN;
}
if ((pfd[i].revents & POLLOUT) != 0) {
pe[i].m_revents |= kPOLLOUT;
}
if ((pfd[i].revents & POLLERR) != 0) {
pe[i].m_revents |= kPOLLERR;
}
if ((pfd[i].revents & POLLNVAL) != 0) {
pe[i].m_revents |= kPOLLNVAL;
}
}
delete[] pfd;
return n;
}
#else
int
CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout)
{
int i, n;
// prepare sets for select
n = 0;
fd_set readSet, writeSet, errSet;
fd_set* readSetP = NULL;
fd_set* writeSetP = NULL;
fd_set* errSetP = NULL;
FD_ZERO(&readSet);
FD_ZERO(&writeSet);
FD_ZERO(&errSet);
for (i = 0; i < num; ++i) {
// reset return flags
pe[i].m_revents = 0;
// set invalid flag if socket is bogus then go to next socket
if (pe[i].m_socket == NULL) {
pe[i].m_revents |= kPOLLNVAL;
continue;
}
int fdi = pe[i].m_socket->m_fd;
if (pe[i].m_events & kPOLLIN) {
FD_SET(pe[i].m_socket->m_fd, &readSet);
readSetP = &readSet;
if (fdi > n) {
n = fdi;
}
}
if (pe[i].m_events & kPOLLOUT) {
FD_SET(pe[i].m_socket->m_fd, &writeSet);
writeSetP = &writeSet;
if (fdi > n) {
n = fdi;
}
}
if (true) {
FD_SET(pe[i].m_socket->m_fd, &errSet);
errSetP = &errSet;
if (fdi > n) {
n = fdi;
}
}
}
// add the unblock pipe
const int* unblockPipe = getUnblockPipe();
if (unblockPipe != NULL) {
FD_SET(unblockPipe[0], &readSet);
readSetP = &readSet;
if (unblockPipe[0] > n) {
n = unblockPipe[0];
}
}
// if there are no sockets then don't block forever
if (n == 0 && timeout < 0.0) {
timeout = 0.0;
}
// prepare timeout for select
struct timeval timeout2;
struct timeval* timeout2P;
if (timeout < 0.0) {
timeout2P = NULL;
}
else {
timeout2P = &timeout2;
timeout2.tv_sec = static_cast<int>(timeout);
timeout2.tv_usec = static_cast<int>(1.0e+6 *
(timeout - timeout2.tv_sec));
}
// do the select
n = select((SELECT_TYPE_ARG1) n + 1,
SELECT_TYPE_ARG234 readSetP,
SELECT_TYPE_ARG234 writeSetP,
SELECT_TYPE_ARG234 errSetP,
SELECT_TYPE_ARG5 timeout2P);
// reset the unblock pipe
if (n > 0 && unblockPipe != NULL && FD_ISSET(unblockPipe[0], &readSet)) {
// the unblock event was signalled. flush the pipe.
char dummy[100];
do {
read(unblockPipe[0], dummy, sizeof(dummy));
} while (errno != EAGAIN);
}
// handle results
if (n == -1) {
if (errno == EINTR) {
// interrupted system call
ARCH->testCancelThread();
return 0;
}
throwError(errno);
}
n = 0;
for (i = 0; i < num; ++i) {
if (pe[i].m_socket != NULL) {
if (FD_ISSET(pe[i].m_socket->m_fd, &readSet)) {
pe[i].m_revents |= kPOLLIN;
}
if (FD_ISSET(pe[i].m_socket->m_fd, &writeSet)) {
pe[i].m_revents |= kPOLLOUT;
}
if (FD_ISSET(pe[i].m_socket->m_fd, &errSet)) {
pe[i].m_revents |= kPOLLERR;
}
}
if (pe[i].m_revents != 0) {
++n;
}
}
return n;
}
#endif
void
CArchNetworkBSD::unblockPollSocket(CArchThread thread)
{
const int* unblockPipe = getUnblockPipeForThread(thread);
if (unblockPipe != NULL) {
char dummy = 0;
int ignore;
ignore = write(unblockPipe[1], &dummy, 1);
}
}
size_t
CArchNetworkBSD::readSocket(CArchSocket s, void* buf, size_t len)
{
assert(s != NULL);
ssize_t n = read(s->m_fd, buf, len);
if (n == -1) {
if (errno == EINTR || errno == EAGAIN) {
return 0;
}
throwError(errno);
}
return n;
}
size_t
CArchNetworkBSD::writeSocket(CArchSocket s, const void* buf, size_t len)
{
assert(s != NULL);
ssize_t n = write(s->m_fd, buf, len);
if (n == -1) {
if (errno == EINTR || errno == EAGAIN) {
return 0;
}
throwError(errno);
}
return n;
}
void
CArchNetworkBSD::throwErrorOnSocket(CArchSocket s)
{
assert(s != NULL);
// get the error from the socket layer
int err = 0;
socklen_t size = (socklen_t)sizeof(err);
if (getsockopt(s->m_fd, SOL_SOCKET, SO_ERROR,
(optval_t*)&err, &size) == -1) {
err = errno;
}
// throw if there's an error
if (err != 0) {
throwError(err);
}
}
void
CArchNetworkBSD::setBlockingOnSocket(int fd, bool blocking)
{
assert(fd != -1);
int mode = fcntl(fd, F_GETFL, 0);
if (mode == -1) {
throwError(errno);
}
if (blocking) {
mode &= ~O_NONBLOCK;
}
else {
mode |= O_NONBLOCK;
}
if (fcntl(fd, F_SETFL, mode) == -1) {
throwError(errno);
}
}
bool
CArchNetworkBSD::setNoDelayOnSocket(CArchSocket s, bool noDelay)
{
assert(s != NULL);
// get old state
int oflag;
socklen_t size = (socklen_t)sizeof(oflag);
if (getsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY,
(optval_t*)&oflag, &size) == -1) {
throwError(errno);
}
int flag = noDelay ? 1 : 0;
size = (socklen_t)sizeof(flag);
if (setsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY,
(optval_t*)&flag, size) == -1) {
throwError(errno);
}
return (oflag != 0);
}
bool
CArchNetworkBSD::setReuseAddrOnSocket(CArchSocket s, bool reuse)
{
assert(s != NULL);
// get old state
int oflag;
socklen_t size = (socklen_t)sizeof(oflag);
if (getsockopt(s->m_fd, SOL_SOCKET, SO_REUSEADDR,
(optval_t*)&oflag, &size) == -1) {
throwError(errno);
}
int flag = reuse ? 1 : 0;
size = (socklen_t)sizeof(flag);
if (setsockopt(s->m_fd, SOL_SOCKET, SO_REUSEADDR,
(optval_t*)&flag, size) == -1) {
throwError(errno);
}
return (oflag != 0);
}
std::string
CArchNetworkBSD::getHostName()
{
char name[256];
if (gethostname(name, sizeof(name)) == -1) {
name[0] = '\0';
}
else {
name[sizeof(name) - 1] = '\0';
}
return name;
}
CArchNetAddress
CArchNetworkBSD::newAnyAddr(EAddressFamily family)
{
// allocate address
CArchNetAddressImpl* addr = new CArchNetAddressImpl;
// fill it in
switch (family) {
case kINET: {
struct sockaddr_in* ipAddr =
reinterpret_cast<struct sockaddr_in*>(&addr->m_addr);
ipAddr->sin_family = AF_INET;
ipAddr->sin_port = 0;
ipAddr->sin_addr.s_addr = INADDR_ANY;
addr->m_len = (socklen_t)sizeof(struct sockaddr_in);
break;
}
default:
delete addr;
assert(0 && "invalid family");
}
return addr;
}
CArchNetAddress
CArchNetworkBSD::copyAddr(CArchNetAddress addr)
{
assert(addr != NULL);
// allocate and copy address
return new CArchNetAddressImpl(*addr);
}
CArchNetAddress
CArchNetworkBSD::nameToAddr(const std::string& name)
{
// allocate address
CArchNetAddressImpl* addr = new CArchNetAddressImpl;
// try to convert assuming an IPv4 dot notation address
struct sockaddr_in inaddr;
memset(&inaddr, 0, sizeof(inaddr));
if (inet_aton(name.c_str(), &inaddr.sin_addr) != 0) {
// it's a dot notation address
addr->m_len = (socklen_t)sizeof(struct sockaddr_in);
inaddr.sin_family = AF_INET;
inaddr.sin_port = 0;
memcpy(&addr->m_addr, &inaddr, addr->m_len);
}
else {
// mutexed address lookup (ugh)
ARCH->lockMutex(m_mutex);
struct hostent* info = gethostbyname(name.c_str());
if (info == NULL) {
ARCH->unlockMutex(m_mutex);
delete addr;
throwNameError(h_errno);
}
// copy over address (only IPv4 currently supported)
if (info->h_addrtype == AF_INET) {
addr->m_len = (socklen_t)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);
}
else {
ARCH->unlockMutex(m_mutex);
delete addr;
throw XArchNetworkNameUnsupported(
"The requested name is valid but "
"does not have a supported address family");
}
// done with static buffer
ARCH->unlockMutex(m_mutex);
}
return addr;
}
void
CArchNetworkBSD::closeAddr(CArchNetAddress addr)
{
assert(addr != NULL);
delete addr;
}
std::string
CArchNetworkBSD::addrToName(CArchNetAddress addr)
{
assert(addr != NULL);
// mutexed name lookup (ugh)
ARCH->lockMutex(m_mutex);
struct hostent* info = gethostbyaddr(
reinterpret_cast<const char*>(&addr->m_addr),
addr->m_len, addr->m_addr.sa_family);
if (info == NULL) {
ARCH->unlockMutex(m_mutex);
throwNameError(h_errno);
}
// save (primary) name
std::string name = info->h_name;
// done with static buffer
ARCH->unlockMutex(m_mutex);
return name;
}
std::string
CArchNetworkBSD::addrToString(CArchNetAddress addr)
{
assert(addr != NULL);
switch (getAddrFamily(addr)) {
case kINET: {
struct sockaddr_in* ipAddr =
reinterpret_cast<struct sockaddr_in*>(&addr->m_addr);
ARCH->lockMutex(m_mutex);
std::string s = inet_ntoa(ipAddr->sin_addr);
ARCH->unlockMutex(m_mutex);
return s;
}
default:
assert(0 && "unknown address family");
return "";
}
}
IArchNetwork::EAddressFamily
CArchNetworkBSD::getAddrFamily(CArchNetAddress addr)
{
assert(addr != NULL);
switch (addr->m_addr.sa_family) {
case AF_INET:
return kINET;
default:
return kUNKNOWN;
}
}
void
CArchNetworkBSD::setAddrPort(CArchNetAddress addr, int port)
{
assert(addr != NULL);
switch (getAddrFamily(addr)) {
case kINET: {
struct sockaddr_in* ipAddr =
reinterpret_cast<struct sockaddr_in*>(&addr->m_addr);
ipAddr->sin_port = htons(port);
break;
}
default:
assert(0 && "unknown address family");
break;
}
}
int
CArchNetworkBSD::getAddrPort(CArchNetAddress addr)
{
assert(addr != NULL);
switch (getAddrFamily(addr)) {
case kINET: {
struct sockaddr_in* ipAddr =
reinterpret_cast<struct sockaddr_in*>(&addr->m_addr);
return ntohs(ipAddr->sin_port);
}
default:
assert(0 && "unknown address family");
return 0;
}
}
bool
CArchNetworkBSD::isAnyAddr(CArchNetAddress addr)
{
assert(addr != NULL);
switch (getAddrFamily(addr)) {
case kINET: {
struct sockaddr_in* ipAddr =
reinterpret_cast<struct sockaddr_in*>(&addr->m_addr);
return (ipAddr->sin_addr.s_addr == INADDR_ANY &&
addr->m_len == (socklen_t)sizeof(struct sockaddr_in));
}
default:
assert(0 && "unknown address family");
return true;
}
}
bool
CArchNetworkBSD::isEqualAddr(CArchNetAddress a, CArchNetAddress b)
{
return (a->m_len == b->m_len &&
memcmp(&a->m_addr, &b->m_addr, a->m_len) == 0);
}
const int*
CArchNetworkBSD::getUnblockPipe()
{
CArchMultithreadPosix* mt = CArchMultithreadPosix::getInstance();
CArchThread thread = mt->newCurrentThread();
const int* p = getUnblockPipeForThread(thread);
ARCH->closeThread(thread);
return p;
}
const int*
CArchNetworkBSD::getUnblockPipeForThread(CArchThread thread)
{
CArchMultithreadPosix* mt = CArchMultithreadPosix::getInstance();
int* unblockPipe = (int*)mt->getNetworkDataForThread(thread);
if (unblockPipe == NULL) {
unblockPipe = new int[2];
if (pipe(unblockPipe) != -1) {
try {
setBlockingOnSocket(unblockPipe[0], false);
mt->setNetworkDataForCurrentThread(unblockPipe);
}
catch (...) {
delete[] unblockPipe;
unblockPipe = NULL;
}
}
else {
delete[] unblockPipe;
unblockPipe = NULL;
}
}
return unblockPipe;
}
void
CArchNetworkBSD::throwError(int err)
{
switch (err) {
case EINTR:
ARCH->testCancelThread();
throw XArchNetworkInterrupted(new XArchEvalUnix(err));
case EACCES:
case EPERM:
throw XArchNetworkAccess(new XArchEvalUnix(err));
case ENFILE:
case EMFILE:
case ENODEV:
case ENOBUFS:
case ENOMEM:
case ENETDOWN:
#if defined(ENOSR)
case ENOSR:
#endif
throw XArchNetworkResource(new XArchEvalUnix(err));
case EPROTOTYPE:
case EPROTONOSUPPORT:
case EAFNOSUPPORT:
case EPFNOSUPPORT:
case ESOCKTNOSUPPORT:
case EINVAL:
case ENOPROTOOPT:
case EOPNOTSUPP:
case ESHUTDOWN:
#if defined(ENOPKG)
case ENOPKG:
#endif
throw XArchNetworkSupport(new XArchEvalUnix(err));
case EIO:
throw XArchNetworkIO(new XArchEvalUnix(err));
case EADDRNOTAVAIL:
throw XArchNetworkNoAddress(new XArchEvalUnix(err));
case EADDRINUSE:
throw XArchNetworkAddressInUse(new XArchEvalUnix(err));
case EHOSTUNREACH:
case ENETUNREACH:
throw XArchNetworkNoRoute(new XArchEvalUnix(err));
case ENOTCONN:
throw XArchNetworkNotConnected(new XArchEvalUnix(err));
case EPIPE:
throw XArchNetworkShutdown(new XArchEvalUnix(err));
case ECONNABORTED:
case ECONNRESET:
throw XArchNetworkDisconnected(new XArchEvalUnix(err));
case ECONNREFUSED:
throw XArchNetworkConnectionRefused(new XArchEvalUnix(err));
case EHOSTDOWN:
case ETIMEDOUT:
throw XArchNetworkTimedOut(new XArchEvalUnix(err));
default:
throw XArchNetwork(new XArchEvalUnix(err));
}
}
void
CArchNetworkBSD::throwNameError(int err)
{
static const char* s_msg[] = {
"The specified host is unknown",
"The requested name is valid but does not have an IP address",
"A non-recoverable name server error occurred",
"A temporary error occurred on an authoritative name server",
"An unknown name server error occurred"
};
switch (err) {
case HOST_NOT_FOUND:
throw XArchNetworkNameUnknown(s_msg[0]);
case NO_DATA:
throw XArchNetworkNameNoAddress(s_msg[1]);
case NO_RECOVERY:
throw XArchNetworkNameFailure(s_msg[2]);
case TRY_AGAIN:
throw XArchNetworkNameUnavailable(s_msg[3]);
default:
throw XArchNetworkName(s_msg[4]);
}
}

View File

@@ -0,0 +1,105 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHNETWORKBSD_H
#define CARCHNETWORKBSD_H
#include "IArchNetwork.h"
#include "IArchMultithread.h"
#if HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#if HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#if !HAVE_SOCKLEN_T
typedef int socklen_t;
#endif
// old systems may use char* for [gs]etsockopt()'s optval argument.
// this should be void on modern systems but char is forwards
// compatible so we always use it.
typedef char optval_t;
#define ARCH_NETWORK CArchNetworkBSD
class CArchSocketImpl {
public:
int m_fd;
int m_refCount;
};
class CArchNetAddressImpl {
public:
CArchNetAddressImpl() : m_len(sizeof(m_addr)) { }
public:
struct sockaddr m_addr;
socklen_t m_len;
};
//! Berkeley (BSD) sockets implementation of IArchNetwork
class CArchNetworkBSD : public IArchNetwork {
public:
CArchNetworkBSD();
virtual ~CArchNetworkBSD();
virtual void init();
// IArchNetwork overrides
virtual CArchSocket newSocket(EAddressFamily, ESocketType);
virtual CArchSocket copySocket(CArchSocket s); virtual void closeSocket(CArchSocket s);
virtual void closeSocketForRead(CArchSocket s);
virtual void closeSocketForWrite(CArchSocket s);
virtual void bindSocket(CArchSocket s, CArchNetAddress addr);
virtual void listenOnSocket(CArchSocket s);
virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr);
virtual bool connectSocket(CArchSocket s, CArchNetAddress name);
virtual int pollSocket(CPollEntry[], int num, double timeout);
virtual void unblockPollSocket(CArchThread thread);
virtual size_t readSocket(CArchSocket s, void* buf, size_t len);
virtual size_t writeSocket(CArchSocket s,
const void* buf, size_t len);
virtual void throwErrorOnSocket(CArchSocket);
virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay);
virtual bool setReuseAddrOnSocket(CArchSocket, bool reuse);
virtual std::string getHostName();
virtual CArchNetAddress newAnyAddr(EAddressFamily);
virtual CArchNetAddress copyAddr(CArchNetAddress);
virtual CArchNetAddress nameToAddr(const std::string&);
virtual void closeAddr(CArchNetAddress);
virtual std::string addrToName(CArchNetAddress);
virtual std::string addrToString(CArchNetAddress);
virtual EAddressFamily getAddrFamily(CArchNetAddress);
virtual void setAddrPort(CArchNetAddress, int port);
virtual int getAddrPort(CArchNetAddress);
virtual bool isAnyAddr(CArchNetAddress);
virtual bool isEqualAddr(CArchNetAddress, CArchNetAddress);
private:
const int* getUnblockPipe();
const int* getUnblockPipeForThread(CArchThread);
void setBlockingOnSocket(int fd, bool blocking);
void throwError(int);
void throwNameError(int);
private:
CArchMutex m_mutex;
};
#endif

View File

@@ -0,0 +1,942 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchNetworkWinsock.h"
#include "CArch.h"
#include "CArchMultithreadWindows.h"
#include "IArchMultithread.h"
#include "XArchWindows.h"
#include <malloc.h>
static const int s_family[] = {
PF_UNSPEC,
PF_INET
};
static const int s_type[] = {
SOCK_DGRAM,
SOCK_STREAM
};
static SOCKET (PASCAL FAR *accept_winsock)(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen);
static int (PASCAL FAR *bind_winsock)(SOCKET s, const struct sockaddr FAR *addr, int namelen);
static int (PASCAL FAR *close_winsock)(SOCKET s);
static int (PASCAL FAR *connect_winsock)(SOCKET s, const struct sockaddr FAR *name, int namelen);
static int (PASCAL FAR *gethostname_winsock)(char FAR * name, int namelen);
static int (PASCAL FAR *getsockerror_winsock)(void);
static int (PASCAL FAR *getsockopt_winsock)(SOCKET s, int level, int optname, void FAR * optval, int FAR *optlen);
static u_short (PASCAL FAR *htons_winsock)(u_short v);
static char FAR * (PASCAL FAR *inet_ntoa_winsock)(struct in_addr in);
static unsigned long (PASCAL FAR *inet_addr_winsock)(const char FAR * cp);
static int (PASCAL FAR *ioctl_winsock)(SOCKET s, int cmd, void FAR * data);
static int (PASCAL FAR *listen_winsock)(SOCKET s, int backlog);
static u_short (PASCAL FAR *ntohs_winsock)(u_short v);
static int (PASCAL FAR *recv_winsock)(SOCKET s, void FAR * buf, int len, int flags);
static int (PASCAL FAR *select_winsock)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout);
static int (PASCAL FAR *send_winsock)(SOCKET s, const void FAR * buf, int len, int flags);
static int (PASCAL FAR *setsockopt_winsock)(SOCKET s, int level, int optname, const void FAR * optval, int optlen);
static int (PASCAL FAR *shutdown_winsock)(SOCKET s, int how);
static SOCKET (PASCAL FAR *socket_winsock)(int af, int type, int protocol);
static struct hostent FAR * (PASCAL FAR *gethostbyaddr_winsock)(const char FAR * addr, int len, int type);
static struct hostent FAR * (PASCAL FAR *gethostbyname_winsock)(const char FAR * name);
static int (PASCAL FAR *WSACleanup_winsock)(void);
static int (PASCAL FAR *WSAFDIsSet_winsock)(SOCKET, fd_set FAR * fdset);
static WSAEVENT (PASCAL FAR *WSACreateEvent_winsock)(void);
static BOOL (PASCAL FAR *WSACloseEvent_winsock)(WSAEVENT);
static BOOL (PASCAL FAR *WSASetEvent_winsock)(WSAEVENT);
static BOOL (PASCAL FAR *WSAResetEvent_winsock)(WSAEVENT);
static int (PASCAL FAR *WSAEventSelect_winsock)(SOCKET, WSAEVENT, long);
static DWORD (PASCAL FAR *WSAWaitForMultipleEvents_winsock)(DWORD, const WSAEVENT FAR*, BOOL, DWORD, BOOL);
static int (PASCAL FAR *WSAEnumNetworkEvents_winsock)(SOCKET, WSAEVENT, LPWSANETWORKEVENTS);
#undef FD_ISSET
#define FD_ISSET(fd, set) WSAFDIsSet_winsock((SOCKET)(fd), (fd_set FAR *)(set))
#define setfunc(var, name, type) var = (type)netGetProcAddress(module, #name)
static HMODULE s_networkModule = NULL;
static
FARPROC
netGetProcAddress(HMODULE module, LPCSTR name)
{
FARPROC func = ::GetProcAddress(module, name);
if (!func) {
throw XArchNetworkSupport("");
}
return func;
}
CArchNetAddressImpl*
CArchNetAddressImpl::alloc(size_t size)
{
size_t totalSize = size + ADDR_HDR_SIZE;
CArchNetAddressImpl* addr = (CArchNetAddressImpl*)malloc(totalSize);
addr->m_len = (int)size;
return addr;
}
//
// CArchNetworkWinsock
//
CArchNetworkWinsock::CArchNetworkWinsock()
{
}
CArchNetworkWinsock::~CArchNetworkWinsock()
{
if (s_networkModule != NULL) {
WSACleanup_winsock();
::FreeLibrary(s_networkModule);
WSACleanup_winsock = NULL;
s_networkModule = NULL;
}
ARCH->closeMutex(m_mutex);
}
void
CArchNetworkWinsock::init()
{
static const char* s_library[] = { "ws2_32.dll" };
assert(WSACleanup_winsock == NULL);
assert(s_networkModule == NULL);
// try each winsock library
for (size_t i = 0; i < sizeof(s_library) / sizeof(s_library[0]); ++i) {
try {
initModule((HMODULE)::LoadLibrary(s_library[i]));
m_mutex = ARCH->newMutex();
return;
}
catch (XArchNetwork&) {
// ignore
}
}
// can't initialize any library
throw XArchNetworkSupport("Cannot load winsock library");
}
void
CArchNetworkWinsock::initModule(HMODULE module)
{
if (module == NULL) {
throw XArchNetworkSupport("");
}
// get startup function address
int (PASCAL FAR *startup)(WORD, LPWSADATA);
setfunc(startup, WSAStartup, int(PASCAL FAR*)(WORD, LPWSADATA));
// startup network library
WORD version = MAKEWORD(2 /*major*/, 0 /*minor*/);
WSADATA data;
int err = startup(version, &data);
if (data.wVersion != version) {
throw XArchNetworkSupport(new XArchEvalWinsock(err));
}
if (err != 0) {
// some other initialization error
throwError(err);
}
// get function addresses
setfunc(accept_winsock, accept, SOCKET (PASCAL FAR *)(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen));
setfunc(bind_winsock, bind, int (PASCAL FAR *)(SOCKET s, const struct sockaddr FAR *addr, int namelen));
setfunc(close_winsock, closesocket, int (PASCAL FAR *)(SOCKET s));
setfunc(connect_winsock, connect, int (PASCAL FAR *)(SOCKET s, const struct sockaddr FAR *name, int namelen));
setfunc(gethostname_winsock, gethostname, int (PASCAL FAR *)(char FAR * name, int namelen));
setfunc(getsockerror_winsock, WSAGetLastError, int (PASCAL FAR *)(void));
setfunc(getsockopt_winsock, getsockopt, int (PASCAL FAR *)(SOCKET s, int level, int optname, void FAR * optval, int FAR *optlen));
setfunc(htons_winsock, htons, u_short (PASCAL FAR *)(u_short v));
setfunc(inet_ntoa_winsock, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in));
setfunc(inet_addr_winsock, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp));
setfunc(ioctl_winsock, ioctlsocket, int (PASCAL FAR *)(SOCKET s, int cmd, void FAR *));
setfunc(listen_winsock, listen, int (PASCAL FAR *)(SOCKET s, int backlog));
setfunc(ntohs_winsock, ntohs, u_short (PASCAL FAR *)(u_short v));
setfunc(recv_winsock, recv, int (PASCAL FAR *)(SOCKET s, void FAR * buf, int len, int flags));
setfunc(select_winsock, select, int (PASCAL FAR *)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout));
setfunc(send_winsock, send, int (PASCAL FAR *)(SOCKET s, const void FAR * buf, int len, int flags));
setfunc(setsockopt_winsock, setsockopt, int (PASCAL FAR *)(SOCKET s, int level, int optname, const void FAR * optval, int optlen));
setfunc(shutdown_winsock, shutdown, int (PASCAL FAR *)(SOCKET s, int how));
setfunc(socket_winsock, socket, SOCKET (PASCAL FAR *)(int af, int type, int protocol));
setfunc(gethostbyaddr_winsock, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type));
setfunc(gethostbyname_winsock, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name));
setfunc(WSACleanup_winsock, WSACleanup, int (PASCAL FAR *)(void));
setfunc(WSAFDIsSet_winsock, __WSAFDIsSet, int (PASCAL FAR *)(SOCKET, fd_set FAR *));
setfunc(WSACreateEvent_winsock, WSACreateEvent, WSAEVENT (PASCAL FAR *)(void));
setfunc(WSACloseEvent_winsock, WSACloseEvent, BOOL (PASCAL FAR *)(WSAEVENT));
setfunc(WSASetEvent_winsock, WSASetEvent, BOOL (PASCAL FAR *)(WSAEVENT));
setfunc(WSAResetEvent_winsock, WSAResetEvent, BOOL (PASCAL FAR *)(WSAEVENT));
setfunc(WSAEventSelect_winsock, WSAEventSelect, int (PASCAL FAR *)(SOCKET, WSAEVENT, long));
setfunc(WSAWaitForMultipleEvents_winsock, WSAWaitForMultipleEvents, DWORD (PASCAL FAR *)(DWORD, const WSAEVENT FAR*, BOOL, DWORD, BOOL));
setfunc(WSAEnumNetworkEvents_winsock, WSAEnumNetworkEvents, int (PASCAL FAR *)(SOCKET, WSAEVENT, LPWSANETWORKEVENTS));
s_networkModule = module;
}
CArchSocket
CArchNetworkWinsock::newSocket(EAddressFamily family, ESocketType type)
{
// create socket
SOCKET fd = socket_winsock(s_family[family], s_type[type], 0);
if (fd == INVALID_SOCKET) {
throwError(getsockerror_winsock());
}
try {
setBlockingOnSocket(fd, false);
}
catch (...) {
close_winsock(fd);
throw;
}
// allocate socket object
CArchSocketImpl* socket = new CArchSocketImpl;
socket->m_socket = fd;
socket->m_refCount = 1;
socket->m_event = WSACreateEvent_winsock();
socket->m_pollWrite = true;
return socket;
}
CArchSocket
CArchNetworkWinsock::copySocket(CArchSocket s)
{
assert(s != NULL);
// ref the socket and return it
ARCH->lockMutex(m_mutex);
++s->m_refCount;
ARCH->unlockMutex(m_mutex);
return s;
}
void
CArchNetworkWinsock::closeSocket(CArchSocket s)
{
assert(s != NULL);
// unref the socket and note if it should be released
ARCH->lockMutex(m_mutex);
const bool doClose = (--s->m_refCount == 0);
ARCH->unlockMutex(m_mutex);
// close the socket if necessary
if (doClose) {
if (close_winsock(s->m_socket) == SOCKET_ERROR) {
// close failed. restore the last ref and throw.
int err = getsockerror_winsock();
ARCH->lockMutex(m_mutex);
++s->m_refCount;
ARCH->unlockMutex(m_mutex);
throwError(err);
}
WSACloseEvent_winsock(s->m_event);
delete s;
}
}
void
CArchNetworkWinsock::closeSocketForRead(CArchSocket s)
{
assert(s != NULL);
if (shutdown_winsock(s->m_socket, SD_RECEIVE) == SOCKET_ERROR) {
if (getsockerror_winsock() != WSAENOTCONN) {
throwError(getsockerror_winsock());
}
}
}
void
CArchNetworkWinsock::closeSocketForWrite(CArchSocket s)
{
assert(s != NULL);
if (shutdown_winsock(s->m_socket, SD_SEND) == SOCKET_ERROR) {
if (getsockerror_winsock() != WSAENOTCONN) {
throwError(getsockerror_winsock());
}
}
}
void
CArchNetworkWinsock::bindSocket(CArchSocket s, CArchNetAddress addr)
{
assert(s != NULL);
assert(addr != NULL);
if (bind_winsock(s->m_socket, &addr->m_addr, addr->m_len) == SOCKET_ERROR) {
throwError(getsockerror_winsock());
}
}
void
CArchNetworkWinsock::listenOnSocket(CArchSocket s)
{
assert(s != NULL);
// hardcoding backlog
if (listen_winsock(s->m_socket, 3) == SOCKET_ERROR) {
throwError(getsockerror_winsock());
}
}
CArchSocket
CArchNetworkWinsock::acceptSocket(CArchSocket s, CArchNetAddress* addr)
{
assert(s != NULL);
// create new socket and temporary address
CArchSocketImpl* socket = new CArchSocketImpl;
CArchNetAddress tmp = CArchNetAddressImpl::alloc(sizeof(struct sockaddr));
// accept on socket
SOCKET fd = accept_winsock(s->m_socket, &tmp->m_addr, &tmp->m_len);
if (fd == INVALID_SOCKET) {
int err = getsockerror_winsock();
delete socket;
free(tmp);
*addr = NULL;
if (err == WSAEWOULDBLOCK) {
return NULL;
}
throwError(err);
}
try {
setBlockingOnSocket(fd, false);
}
catch (...) {
close_winsock(fd);
delete socket;
free(tmp);
*addr = NULL;
throw;
}
// initialize socket
socket->m_socket = fd;
socket->m_refCount = 1;
socket->m_event = WSACreateEvent_winsock();
socket->m_pollWrite = true;
// copy address if requested
if (addr != NULL) {
*addr = ARCH->copyAddr(tmp);
}
free(tmp);
return socket;
}
bool
CArchNetworkWinsock::connectSocket(CArchSocket s, CArchNetAddress addr)
{
assert(s != NULL);
assert(addr != NULL);
if (connect_winsock(s->m_socket, &addr->m_addr,
addr->m_len) == SOCKET_ERROR) {
if (getsockerror_winsock() == WSAEISCONN) {
return true;
}
if (getsockerror_winsock() == WSAEWOULDBLOCK) {
return false;
}
throwError(getsockerror_winsock());
}
return true;
}
int
CArchNetworkWinsock::pollSocket(CPollEntry pe[], int num, double timeout)
{
int i;
DWORD n;
// prepare sockets and wait list
bool canWrite = false;
WSAEVENT* events = (WSAEVENT*)alloca((num + 1) * sizeof(WSAEVENT));
for (i = 0, n = 0; i < num; ++i) {
// reset return flags
pe[i].m_revents = 0;
// set invalid flag if socket is bogus then go to next socket
if (pe[i].m_socket == NULL) {
pe[i].m_revents |= kPOLLNVAL;
continue;
}
// select desired events
long socketEvents = 0;
if ((pe[i].m_events & kPOLLIN) != 0) {
socketEvents |= FD_READ | FD_ACCEPT | FD_CLOSE;
}
if ((pe[i].m_events & kPOLLOUT) != 0) {
socketEvents |= FD_WRITE | FD_CONNECT | FD_CLOSE;
// if m_pollWrite is false then we assume the socket is
// writable. winsock doesn't signal writability except
// when the state changes from unwritable.
if (!pe[i].m_socket->m_pollWrite) {
canWrite = true;
pe[i].m_revents |= kPOLLOUT;
}
}
// if no events then ignore socket
if (socketEvents == 0) {
continue;
}
// select socket for desired events
WSAEventSelect_winsock(pe[i].m_socket->m_socket,
pe[i].m_socket->m_event, socketEvents);
// add socket event to wait list
events[n++] = pe[i].m_socket->m_event;
}
// if no sockets then return immediately
if (n == 0) {
return 0;
}
// add the unblock event
CArchMultithreadWindows* mt = CArchMultithreadWindows::getInstance();
CArchThread thread = mt->newCurrentThread();
WSAEVENT* unblockEvent = (WSAEVENT*)mt->getNetworkDataForThread(thread);
ARCH->closeThread(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;
}
throwError(getsockerror_winsock());
}
if (result == WSA_WAIT_TIMEOUT && !canWrite) {
return 0;
}
if (result == WSA_WAIT_EVENT_0 + n - 1) {
// the unblock event was signalled
return 0;
}
for (i = 0, n = 0; i < num; ++i) {
// skip events we didn't check
if (pe[i].m_socket == NULL ||
(pe[i].m_events & (kPOLLIN | kPOLLOUT)) == 0) {
continue;
}
// get events
WSANETWORKEVENTS info;
if (WSAEnumNetworkEvents_winsock(pe[i].m_socket->m_socket,
pe[i].m_socket->m_event, &info) == SOCKET_ERROR) {
continue;
}
if ((info.lNetworkEvents & FD_READ) != 0) {
pe[i].m_revents |= kPOLLIN;
}
if ((info.lNetworkEvents & FD_ACCEPT) != 0) {
pe[i].m_revents |= kPOLLIN;
}
if ((info.lNetworkEvents & FD_WRITE) != 0) {
pe[i].m_revents |= kPOLLOUT;
// socket is now writable so don't bothing polling for
// writable until it becomes unwritable.
pe[i].m_socket->m_pollWrite = false;
}
if ((info.lNetworkEvents & FD_CONNECT) != 0) {
if (info.iErrorCode[FD_CONNECT_BIT] != 0) {
pe[i].m_revents |= kPOLLERR;
}
else {
pe[i].m_revents |= kPOLLOUT;
pe[i].m_socket->m_pollWrite = false;
}
}
if ((info.lNetworkEvents & FD_CLOSE) != 0) {
if (info.iErrorCode[FD_CLOSE_BIT] != 0) {
pe[i].m_revents |= kPOLLERR;
}
else {
if ((pe[i].m_events & kPOLLIN) != 0) {
pe[i].m_revents |= kPOLLIN;
}
if ((pe[i].m_events & kPOLLOUT) != 0) {
pe[i].m_revents |= kPOLLOUT;
}
}
}
if (pe[i].m_revents != 0) {
++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
CArchNetworkWinsock::readSocket(CArchSocket s, void* buf, size_t len)
{
assert(s != NULL);
int n = recv_winsock(s->m_socket, buf, (int)len, 0);
if (n == SOCKET_ERROR) {
int err = getsockerror_winsock();
if (err == WSAEINTR || err == WSAEWOULDBLOCK) {
return 0;
}
throwError(err);
}
return static_cast<size_t>(n);
}
size_t
CArchNetworkWinsock::writeSocket(CArchSocket s, const void* buf, size_t len)
{
assert(s != NULL);
int n = send_winsock(s->m_socket, buf, (int)len, 0);
if (n == SOCKET_ERROR) {
int err = getsockerror_winsock();
if (err == WSAEINTR) {
return 0;
}
if (err == WSAEWOULDBLOCK) {
s->m_pollWrite = true;
return 0;
}
throwError(err);
}
return static_cast<size_t>(n);
}
void
CArchNetworkWinsock::throwErrorOnSocket(CArchSocket s)
{
assert(s != NULL);
// get the error from the socket layer
int err = 0;
int size = sizeof(err);
if (getsockopt_winsock(s->m_socket, SOL_SOCKET,
SO_ERROR, &err, &size) == SOCKET_ERROR) {
err = getsockerror_winsock();
}
// throw if there's an error
if (err != 0) {
throwError(err);
}
}
void
CArchNetworkWinsock::setBlockingOnSocket(SOCKET s, bool blocking)
{
assert(s != 0);
int flag = blocking ? 0 : 1;
if (ioctl_winsock(s, FIONBIO, &flag) == SOCKET_ERROR) {
throwError(getsockerror_winsock());
}
}
bool
CArchNetworkWinsock::setNoDelayOnSocket(CArchSocket s, bool noDelay)
{
assert(s != NULL);
// get old state
BOOL oflag;
int size = sizeof(oflag);
if (getsockopt_winsock(s->m_socket, IPPROTO_TCP,
TCP_NODELAY, &oflag, &size) == SOCKET_ERROR) {
throwError(getsockerror_winsock());
}
// set new state
BOOL flag = noDelay ? 1 : 0;
size = sizeof(flag);
if (setsockopt_winsock(s->m_socket, IPPROTO_TCP,
TCP_NODELAY, &flag, size) == SOCKET_ERROR) {
throwError(getsockerror_winsock());
}
return (oflag != 0);
}
bool
CArchNetworkWinsock::setReuseAddrOnSocket(CArchSocket s, bool reuse)
{
assert(s != NULL);
// get old state
BOOL oflag;
int size = sizeof(oflag);
if (getsockopt_winsock(s->m_socket, SOL_SOCKET,
SO_REUSEADDR, &oflag, &size) == SOCKET_ERROR) {
throwError(getsockerror_winsock());
}
// set new state
BOOL flag = reuse ? 1 : 0;
size = sizeof(flag);
if (setsockopt_winsock(s->m_socket, SOL_SOCKET,
SO_REUSEADDR, &flag, size) == SOCKET_ERROR) {
throwError(getsockerror_winsock());
}
return (oflag != 0);
}
std::string
CArchNetworkWinsock::getHostName()
{
char name[256];
if (gethostname_winsock(name, sizeof(name)) == -1) {
name[0] = '\0';
}
else {
name[sizeof(name) - 1] = '\0';
}
return name;
}
CArchNetAddress
CArchNetworkWinsock::newAnyAddr(EAddressFamily family)
{
CArchNetAddressImpl* addr = NULL;
switch (family) {
case kINET: {
addr = CArchNetAddressImpl::alloc(sizeof(struct sockaddr_in));
struct sockaddr_in* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
ipAddr->sin_family = AF_INET;
ipAddr->sin_port = 0;
ipAddr->sin_addr.s_addr = INADDR_ANY;
break;
}
default:
assert(0 && "invalid family");
}
return addr;
}
CArchNetAddress
CArchNetworkWinsock::copyAddr(CArchNetAddress addr)
{
assert(addr != NULL);
CArchNetAddressImpl* copy = CArchNetAddressImpl::alloc(addr->m_len);
memcpy(TYPED_ADDR(void, copy), TYPED_ADDR(void, addr), addr->m_len);
return copy;
}
CArchNetAddress
CArchNetworkWinsock::nameToAddr(const std::string& name)
{
// allocate address
CArchNetAddressImpl* addr = NULL;
// try to convert assuming an IPv4 dot notation address
struct sockaddr_in inaddr;
memset(&inaddr, 0, sizeof(inaddr));
inaddr.sin_family = AF_INET;
inaddr.sin_port = 0;
inaddr.sin_addr.s_addr = inet_addr_winsock(name.c_str());
if (inaddr.sin_addr.s_addr != INADDR_NONE) {
// it's a dot notation address
addr = CArchNetAddressImpl::alloc(sizeof(struct sockaddr_in));
memcpy(TYPED_ADDR(void, addr), &inaddr, addr->m_len);
}
else {
// address lookup
struct hostent* info = gethostbyname_winsock(name.c_str());
if (info == NULL) {
throwNameError(getsockerror_winsock());
}
// copy over address (only IPv4 currently supported)
if (info->h_addrtype == AF_INET) {
addr = CArchNetAddressImpl::alloc(sizeof(struct sockaddr_in));
memcpy(&inaddr.sin_addr, info->h_addr_list[0],
sizeof(inaddr.sin_addr));
memcpy(TYPED_ADDR(void, addr), &inaddr, addr->m_len);
}
else {
throw XArchNetworkNameUnsupported(
"The requested name is valid but "
"does not have a supported address family");
}
}
return addr;
}
void
CArchNetworkWinsock::closeAddr(CArchNetAddress addr)
{
assert(addr != NULL);
free(addr);
}
std::string
CArchNetworkWinsock::addrToName(CArchNetAddress addr)
{
assert(addr != NULL);
// name lookup
struct hostent* info = gethostbyaddr_winsock(
reinterpret_cast<const char FAR*>(&addr->m_addr),
addr->m_len, addr->m_addr.sa_family);
if (info == NULL) {
throwNameError(getsockerror_winsock());
}
// return (primary) name
return info->h_name;
}
std::string
CArchNetworkWinsock::addrToString(CArchNetAddress addr)
{
assert(addr != NULL);
switch (getAddrFamily(addr)) {
case kINET: {
struct sockaddr_in* ipAddr =
reinterpret_cast<struct sockaddr_in*>(&addr->m_addr);
return inet_ntoa_winsock(ipAddr->sin_addr);
}
default:
assert(0 && "unknown address family");
return "";
}
}
IArchNetwork::EAddressFamily
CArchNetworkWinsock::getAddrFamily(CArchNetAddress addr)
{
assert(addr != NULL);
switch (addr->m_addr.sa_family) {
case AF_INET:
return kINET;
default:
return kUNKNOWN;
}
}
void
CArchNetworkWinsock::setAddrPort(CArchNetAddress addr, int port)
{
assert(addr != NULL);
switch (getAddrFamily(addr)) {
case kINET: {
struct sockaddr_in* ipAddr =
reinterpret_cast<struct sockaddr_in*>(&addr->m_addr);
ipAddr->sin_port = htons_winsock(static_cast<u_short>(port));
break;
}
default:
assert(0 && "unknown address family");
break;
}
}
int
CArchNetworkWinsock::getAddrPort(CArchNetAddress addr)
{
assert(addr != NULL);
switch (getAddrFamily(addr)) {
case kINET: {
struct sockaddr_in* ipAddr =
reinterpret_cast<struct sockaddr_in*>(&addr->m_addr);
return ntohs_winsock(ipAddr->sin_port);
}
default:
assert(0 && "unknown address family");
return 0;
}
}
bool
CArchNetworkWinsock::isAnyAddr(CArchNetAddress addr)
{
assert(addr != NULL);
switch (getAddrFamily(addr)) {
case kINET: {
struct sockaddr_in* ipAddr =
reinterpret_cast<struct sockaddr_in*>(&addr->m_addr);
return (addr->m_len == sizeof(struct sockaddr_in) &&
ipAddr->sin_addr.s_addr == INADDR_ANY);
}
default:
assert(0 && "unknown address family");
return true;
}
}
bool
CArchNetworkWinsock::isEqualAddr(CArchNetAddress a, CArchNetAddress b)
{
return (a == b || (a->m_len == b->m_len &&
memcmp(&a->m_addr, &b->m_addr, a->m_len) == 0));
}
void
CArchNetworkWinsock::throwError(int err)
{
switch (err) {
case WSAEACCES:
throw XArchNetworkAccess(new XArchEvalWinsock(err));
case WSAEMFILE:
case WSAENOBUFS:
case WSAENETDOWN:
throw XArchNetworkResource(new XArchEvalWinsock(err));
case WSAEPROTOTYPE:
case WSAEPROTONOSUPPORT:
case WSAEAFNOSUPPORT:
case WSAEPFNOSUPPORT:
case WSAESOCKTNOSUPPORT:
case WSAEINVAL:
case WSAENOPROTOOPT:
case WSAEOPNOTSUPP:
case WSAESHUTDOWN:
case WSANOTINITIALISED:
case WSAVERNOTSUPPORTED:
case WSASYSNOTREADY:
throw XArchNetworkSupport(new XArchEvalWinsock(err));
case WSAEADDRNOTAVAIL:
throw XArchNetworkNoAddress(new XArchEvalWinsock(err));
case WSAEADDRINUSE:
throw XArchNetworkAddressInUse(new XArchEvalWinsock(err));
case WSAEHOSTUNREACH:
case WSAENETUNREACH:
throw XArchNetworkNoRoute(new XArchEvalWinsock(err));
case WSAENOTCONN:
throw XArchNetworkNotConnected(new XArchEvalWinsock(err));
case WSAEDISCON:
throw XArchNetworkShutdown(new XArchEvalWinsock(err));
case WSAENETRESET:
case WSAECONNABORTED:
case WSAECONNRESET:
throw XArchNetworkDisconnected(new XArchEvalWinsock(err));
case WSAECONNREFUSED:
throw XArchNetworkConnectionRefused(new XArchEvalWinsock(err));
case WSAEHOSTDOWN:
case WSAETIMEDOUT:
throw XArchNetworkTimedOut(new XArchEvalWinsock(err));
case WSAHOST_NOT_FOUND:
throw XArchNetworkNameUnknown(new XArchEvalWinsock(err));
case WSANO_DATA:
throw XArchNetworkNameNoAddress(new XArchEvalWinsock(err));
case WSANO_RECOVERY:
throw XArchNetworkNameFailure(new XArchEvalWinsock(err));
case WSATRY_AGAIN:
throw XArchNetworkNameUnavailable(new XArchEvalWinsock(err));
default:
throw XArchNetwork(new XArchEvalWinsock(err));
}
}
void
CArchNetworkWinsock::throwNameError(int err)
{
switch (err) {
case WSAHOST_NOT_FOUND:
throw XArchNetworkNameUnknown(new XArchEvalWinsock(err));
case WSANO_DATA:
throw XArchNetworkNameNoAddress(new XArchEvalWinsock(err));
case WSANO_RECOVERY:
throw XArchNetworkNameFailure(new XArchEvalWinsock(err));
case WSATRY_AGAIN:
throw XArchNetworkNameUnavailable(new XArchEvalWinsock(err));
default:
throw XArchNetworkName(new XArchEvalWinsock(err));
}
}

View File

@@ -0,0 +1,104 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHNETWORKWINSOCK_H
#define CARCHNETWORKWINSOCK_H
#define WIN32_LEAN_AND_MEAN
// declare no functions in winsock2
#define INCL_WINSOCK_API_PROTOTYPES 0
#define INCL_WINSOCK_API_TYPEDEFS 0
#include "IArchNetwork.h"
#include "IArchMultithread.h"
#include <windows.h>
#include <winsock2.h>
#define ARCH_NETWORK CArchNetworkWinsock
class CArchSocketImpl {
public:
SOCKET m_socket;
int m_refCount;
WSAEVENT m_event;
bool m_pollWrite;
};
class CArchNetAddressImpl {
public:
static CArchNetAddressImpl* alloc(size_t);
public:
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
class CArchNetworkWinsock : public IArchNetwork {
public:
CArchNetworkWinsock();
virtual ~CArchNetworkWinsock();
virtual void init();
// IArchNetwork overrides
virtual CArchSocket newSocket(EAddressFamily, ESocketType);
virtual CArchSocket copySocket(CArchSocket s);
virtual void closeSocket(CArchSocket s);
virtual void closeSocketForRead(CArchSocket s);
virtual void closeSocketForWrite(CArchSocket s);
virtual void bindSocket(CArchSocket s, CArchNetAddress addr);
virtual void listenOnSocket(CArchSocket s);
virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr);
virtual bool connectSocket(CArchSocket s, CArchNetAddress name);
virtual int pollSocket(CPollEntry[], int num, double timeout);
virtual void unblockPollSocket(CArchThread thread);
virtual size_t readSocket(CArchSocket s, void* buf, size_t len);
virtual size_t writeSocket(CArchSocket s,
const void* buf, size_t len);
virtual void throwErrorOnSocket(CArchSocket);
virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay);
virtual bool setReuseAddrOnSocket(CArchSocket, bool reuse);
virtual std::string getHostName();
virtual CArchNetAddress newAnyAddr(EAddressFamily);
virtual CArchNetAddress copyAddr(CArchNetAddress);
virtual CArchNetAddress nameToAddr(const std::string&);
virtual void closeAddr(CArchNetAddress);
virtual std::string addrToName(CArchNetAddress);
virtual std::string addrToString(CArchNetAddress);
virtual EAddressFamily getAddrFamily(CArchNetAddress);
virtual void setAddrPort(CArchNetAddress, int port);
virtual int getAddrPort(CArchNetAddress);
virtual bool isAnyAddr(CArchNetAddress);
virtual bool isEqualAddr(CArchNetAddress, CArchNetAddress);
private:
void initModule(HMODULE);
void setBlockingOnSocket(SOCKET, bool blocking);
void throwError(int);
void throwNameError(int);
private:
CArchMutex m_mutex;
};
#endif

View File

@@ -0,0 +1,31 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012 Nick Bolton
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchPluginUnix.h"
CArchPluginUnix::CArchPluginUnix()
{
}
CArchPluginUnix::~CArchPluginUnix()
{
}
void
CArchPluginUnix::init(void* eventTarget)
{
}

View File

@@ -0,0 +1,32 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012 Nick Bolton
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "IArchPlugin.h"
#define ARCH_PLUGIN CArchPluginUnix
//! Unix implementation of IArchPlugin
class CArchPluginUnix : public IArchPlugin {
public:
CArchPluginUnix();
virtual ~CArchPluginUnix();
// IArchPlugin overrides
void init(void* eventTarget);
};

View File

@@ -0,0 +1,126 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012 Nick Bolton
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchPluginWindows.h"
#include "XArchWindows.h"
#include "CLog.h"
#include "IEventQueue.h"
#include "CEvent.h"
#include "CScreen.h"
#include "IPlatformScreen.h" // temp
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <iostream>
typedef int (*initFunc)(void (*sendEvent)(const char*, void*), void (*log)(const char*));
void* CArchPluginWindows::m_eventTarget = NULL;
CArchPluginWindows::CArchPluginWindows()
{
}
CArchPluginWindows::~CArchPluginWindows()
{
}
void
CArchPluginWindows::init(void* eventTarget)
{
m_eventTarget = eventTarget;
CString dir = getPluginsDir();
LOG((CLOG_DEBUG "plugins dir: %s", dir.c_str()));
CString pattern = CString(dir).append("\\*.dll");
std::vector<CString> plugins;
getFilenames(pattern, plugins);
std::vector<CString>::iterator it;
for (it = plugins.begin(); it != plugins.end(); ++it)
load(*it);
}
void
CArchPluginWindows::load(const CString& dllFilename)
{
LOG((CLOG_DEBUG "loading plugin: %s", dllFilename.c_str()));
CString path = CString(getPluginsDir()).append("\\").append(dllFilename);
HINSTANCE library = LoadLibrary(path.c_str());
if (library == NULL)
throw XArch(new XArchEvalWindows);
initFunc initPlugin = (initFunc)GetProcAddress(library, "init");
initPlugin(&sendEvent, &log);
}
CString
CArchPluginWindows::getModuleDir()
{
TCHAR c_modulePath[MAX_PATH];
GetModuleFileName(NULL, c_modulePath, MAX_PATH);
if (GetLastError() != ERROR_SUCCESS) {
throw XArch(new XArchEvalWindows);
}
CString modulePath(c_modulePath);
size_t lastSlash = modulePath.find_last_of("\\");
if (lastSlash != CString::npos) {
return modulePath.substr(0, lastSlash);
}
throw XArch("could not get module path.");
}
void
CArchPluginWindows::getFilenames(const CString& pattern, std::vector<CString>& filenames)
{
WIN32_FIND_DATA data;
HANDLE find = FindFirstFile(pattern.c_str(), &data);
if (find == INVALID_HANDLE_VALUE) {
FindClose(find);
LOG((CLOG_DEBUG "plugins dir is empty: %s", pattern.c_str()));
return;
}
do {
filenames.push_back(data.cFileName);
} while (FindNextFile(find, &data));
FindClose(find);
}
CString CArchPluginWindows::getPluginsDir()
{
return getModuleDir().append("\\").append(PLUGINS_DIR);
}
void
sendEvent(const char* eventName, void* data)
{
LOG((CLOG_DEBUG5 "plugin sending event"));
CEvent::Type type = EVENTQUEUE->getRegisteredType(eventName);
EVENTQUEUE->addEvent(CEvent(type, CArchPluginWindows::m_eventTarget, data));
}
void
log(const char* text)
{
LOG((CLOG_DEBUG "plugin: %s", text));
}

View File

@@ -0,0 +1,47 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012 Nick Bolton
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "IArchPlugin.h"
#include "CString.h"
#include <vector>
#define ARCH_PLUGIN CArchPluginWindows
class CScreen;
//! Windows implementation of IArchPlugin
class CArchPluginWindows : public IArchPlugin {
public:
CArchPluginWindows();
virtual ~CArchPluginWindows();
// IArchPlugin overrides
void init(void* eventTarget);
static void* m_eventTarget;
private:
CString getModuleDir();
void getFilenames(const CString& pattern, std::vector<CString>& filenames);
void load(const CString& dllPath);
CString getPluginsDir();
};
void sendEvent(const char* text, void* data);
void log(const char* text);

View File

@@ -0,0 +1,91 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchSleepUnix.h"
#include "CArch.h"
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#if !HAVE_NANOSLEEP
# if HAVE_SYS_SELECT_H
# include <sys/select.h>
# endif
# if HAVE_SYS_TYPES_H
# include <sys/types.h>
# endif
# if HAVE_UNISTD_H
# include <unistd.h>
# endif
#endif
//
// CArchSleepUnix
//
CArchSleepUnix::CArchSleepUnix()
{
// do nothing
}
CArchSleepUnix::~CArchSleepUnix()
{
// do nothing
}
void
CArchSleepUnix::sleep(double timeout)
{
ARCH->testCancelThread();
if (timeout < 0.0) {
return;
}
#if HAVE_NANOSLEEP
// prep timeout
struct timespec t;
t.tv_sec = (long)timeout;
t.tv_nsec = (long)(1.0e+9 * (timeout - (double)t.tv_sec));
// wait
while (nanosleep(&t, &t) < 0)
ARCH->testCancelThread();
#else
/* emulate nanosleep() with select() */
double startTime = ARCH->time();
double timeLeft = timeout;
while (timeLeft > 0.0) {
struct timeval timeout2;
timeout2.tv_sec = static_cast<int>(timeLeft);
timeout2.tv_usec = static_cast<int>(1.0e+6 * (timeLeft -
timeout2.tv_sec));
select((SELECT_TYPE_ARG1) 0,
SELECT_TYPE_ARG234 NULL,
SELECT_TYPE_ARG234 NULL,
SELECT_TYPE_ARG234 NULL,
SELECT_TYPE_ARG5 &timeout2);
ARCH->testCancelThread();
timeLeft = timeout - (ARCH->time() - startTime);
}
#endif
}

View File

@@ -0,0 +1,35 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHSLEEPUNIX_H
#define CARCHSLEEPUNIX_H
#include "IArchSleep.h"
#define ARCH_SLEEP CArchSleepUnix
//! Unix implementation of IArchSleep
class CArchSleepUnix : public IArchSleep {
public:
CArchSleepUnix();
virtual ~CArchSleepUnix();
// IArchSleep overrides
virtual void sleep(double timeout);
};
#endif

View File

@@ -0,0 +1,60 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchSleepWindows.h"
#include "CArch.h"
#include "CArchMultithreadWindows.h"
//
// CArchSleepWindows
//
CArchSleepWindows::CArchSleepWindows()
{
// do nothing
}
CArchSleepWindows::~CArchSleepWindows()
{
// do nothing
}
void
CArchSleepWindows::sleep(double timeout)
{
ARCH->testCancelThread();
if (timeout < 0.0) {
return;
}
// get the cancel event from the current thread. this only
// works if we're using the windows multithread object but
// this is windows so that's pretty certain; we'll get a
// link error if we're not, though.
CArchMultithreadWindows* mt = CArchMultithreadWindows::getInstance();
if (mt != NULL) {
HANDLE cancelEvent = mt->getCancelEventForCurrentThread();
WaitForSingleObject(cancelEvent, (DWORD)(1000.0 * timeout));
if (timeout == 0.0) {
Sleep(0);
}
}
else {
Sleep((DWORD)(1000.0 * timeout));
}
ARCH->testCancelThread();
}

View File

@@ -0,0 +1,35 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHSLEEPWINDOWS_H
#define CARCHSLEEPWINDOWS_H
#include "IArchSleep.h"
#define ARCH_SLEEP CArchSleepWindows
//! Win32 implementation of IArchSleep
class CArchSleepWindows : public IArchSleep {
public:
CArchSleepWindows();
virtual ~CArchSleepWindows();
// IArchSleep overrides
virtual void sleep(double timeout);
};
#endif

View File

@@ -0,0 +1,40 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchStringUnix.h"
#include <stdio.h>
//
// CArchStringUnix
//
#include "CMultibyte.h"
#include "vsnprintf.h"
CArchStringUnix::CArchStringUnix()
{
}
CArchStringUnix::~CArchStringUnix()
{
}
IArchString::EWideCharEncoding
CArchStringUnix::getWideCharEncoding()
{
return kUCS4;
}

View File

@@ -0,0 +1,36 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHSTRINGUNIX_H
#define CARCHSTRINGUNIX_H
#include "IArchString.h"
#define ARCH_STRING CArchStringUnix
//! Unix implementation of IArchString
class CArchStringUnix : public IArchString {
public:
CArchStringUnix();
virtual ~CArchStringUnix();
// IArchString overrides
virtual EWideCharEncoding
getWideCharEncoding();
};
#endif

View File

@@ -0,0 +1,45 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define WIN32_LEAN_AND_MEAN
#include "CArchStringWindows.h"
#include <windows.h>
#include <stdio.h>
//
// CArchStringWindows
//
#include "CMultibyte.h"
#define HAVE_VSNPRINTF 1
#define ARCH_VSNPRINTF _vsnprintf
#include "vsnprintf.h"
CArchStringWindows::CArchStringWindows()
{
}
CArchStringWindows::~CArchStringWindows()
{
}
IArchString::EWideCharEncoding
CArchStringWindows::getWideCharEncoding()
{
return kUTF16;
}

View File

@@ -0,0 +1,36 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHSTRINGWINDOWS_H
#define CARCHSTRINGWINDOWS_H
#include "IArchString.h"
#define ARCH_STRING CArchStringWindows
//! Win32 implementation of IArchString
class CArchStringWindows : public IArchString {
public:
CArchStringWindows();
virtual ~CArchStringWindows();
// IArchString overrides
virtual EWideCharEncoding
getWideCharEncoding();
};
#endif

View File

@@ -0,0 +1,74 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchSystemUnix.h"
#include <sys/utsname.h>
//
// CArchSystemUnix
//
CArchSystemUnix::CArchSystemUnix()
{
// do nothing
}
CArchSystemUnix::~CArchSystemUnix()
{
// do nothing
}
std::string
CArchSystemUnix::getOSName() const
{
#if defined(HAVE_SYS_UTSNAME_H)
struct utsname info;
if (uname(&info) == 0) {
std::string msg;
msg += info.sysname;
msg += " ";
msg += info.release;
msg += " ";
msg += info.version;
return msg;
}
#endif
return "Unix";
}
std::string
CArchSystemUnix::getPlatformName() const
{
#if defined(HAVE_SYS_UTSNAME_H)
struct utsname info;
if (uname(&info) == 0) {
return std::string(info.machine);
}
#endif
return "unknown";
}
std::string
CArchSystemUnix::setting(const std::string&) const
{
return "";
}
void
CArchSystemUnix::setting(const std::string&, const std::string&) const
{
}

View File

@@ -0,0 +1,38 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHSYSTEMUNIX_H
#define CARCHSYSTEMUNIX_H
#include "IArchSystem.h"
#define ARCH_SYSTEM CArchSystemUnix
//! Unix implementation of IArchString
class CArchSystemUnix : public IArchSystem {
public:
CArchSystemUnix();
virtual ~CArchSystemUnix();
// IArchSystem overrides
virtual std::string getOSName() const;
virtual std::string getPlatformName() const;
virtual std::string setting(const std::string&) const;
virtual void setting(const std::string&, const std::string&) const;
};
#endif

View File

@@ -0,0 +1,178 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchSystemWindows.h"
#include "CArchMiscWindows.h"
#include "XArchWindows.h"
#include "tchar.h"
#include <string>
static const char* s_settingsKeyNames[] = {
_T("SOFTWARE"),
_T("Synergy"),
NULL
};
//
// CArchSystemWindows
//
CArchSystemWindows::CArchSystemWindows()
{
// do nothing
}
CArchSystemWindows::~CArchSystemWindows()
{
// do nothing
}
std::string
CArchSystemWindows::getOSName() const
{
#if WINVER >= _WIN32_WINNT_WIN2K
OSVERSIONINFOEX info;
#else
OSVERSIONINFO info;
#endif
info.dwOSVersionInfoSize = sizeof(info);
if (GetVersionEx((OSVERSIONINFO*) &info)) {
switch (info.dwPlatformId) {
case VER_PLATFORM_WIN32_NT:
#if WINVER >= _WIN32_WINNT_WIN2K
if (info.dwMajorVersion == 6) {
if(info.dwMinorVersion == 0) {
if (info.wProductType == VER_NT_WORKSTATION) {
return "Microsoft Windows Vista";
} else {
return "Microsoft Windows Server 2008";
}
} else if(info.dwMinorVersion == 1) {
if (info.wProductType == VER_NT_WORKSTATION) {
return "Microsoft Windows 7";
} else {
return "Microsoft Windows Server 2008 R2";
}
}
}
#endif
if (info.dwMajorVersion == 5 && info.dwMinorVersion == 2) {
return "Microsoft Windows Server 2003";
}
if (info.dwMajorVersion == 5 && info.dwMinorVersion == 1) {
return "Microsoft Windows XP";
}
if (info.dwMajorVersion == 5 && info.dwMinorVersion == 0) {
return "Microsoft Windows Server 2000";
}
if (info.dwMajorVersion <= 4) {
return "Microsoft Windows NT";
}
char buffer[100];
sprintf(buffer, "Microsoft Windows %d.%d",
info.dwMajorVersion, info.dwMinorVersion);
return buffer;
case VER_PLATFORM_WIN32_WINDOWS:
if (info.dwMajorVersion == 4 && info.dwMinorVersion == 0) {
if (info.szCSDVersion[1] == 'C' ||
info.szCSDVersion[1] == 'B') {
return "Microsoft Windows 95 OSR2";
}
return "Microsoft Windows 95";
}
if (info.dwMajorVersion == 4 && info.dwMinorVersion == 10) {
if (info.szCSDVersion[1] == 'A') {
return "Microsoft Windows 98 SE";
}
return "Microsoft Windows 98";
}
if (info.dwMajorVersion == 4 && info.dwMinorVersion == 90) {
return "Microsoft Windows ME";
}
if (info.dwMajorVersion == 4) {
return "Microsoft Windows unknown 95 family";
}
break;
default:
break;
}
}
return "Microsoft Windows <unknown>";
}
std::string
CArchSystemWindows::getPlatformName() const
{
#ifdef _X86_
if(isWOW64())
return "x86 (WOW64)";
else
return "x86";
#else
#ifdef _AMD64_
return "x64";
#else
return "Unknown";
#endif
#endif
}
std::string
CArchSystemWindows::setting(const std::string& valueName) const
{
HKEY key = CArchMiscWindows::openKey(HKEY_LOCAL_MACHINE, s_settingsKeyNames);
if (key == NULL)
return "";
return CArchMiscWindows::readValueString(key, valueName.c_str());
}
void
CArchSystemWindows::setting(const std::string& valueName, const std::string& valueString) const
{
HKEY key = CArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_settingsKeyNames);
if (key == NULL)
throw XArch(std::string("could not access registry key: ") + valueName);
CArchMiscWindows::setValue(key, valueName.c_str(), valueString.c_str());
}
bool
CArchSystemWindows::isWOW64() const
{
#if WINVER >= _WIN32_WINNT_WINXP
typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
HMODULE hModule = GetModuleHandle(TEXT("kernel32"));
if (!hModule) return FALSE;
LPFN_ISWOW64PROCESS fnIsWow64Process =
(LPFN_ISWOW64PROCESS) GetProcAddress(hModule, "IsWow64Process");
BOOL bIsWow64 = FALSE;
if(NULL != fnIsWow64Process &&
fnIsWow64Process(GetCurrentProcess(), &bIsWow64) &&
bIsWow64)
{
return true;
}
#endif
return false;
}

View File

@@ -0,0 +1,40 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHSYSTEMWINDOWS_H
#define CARCHSYSTEMWINDOWS_H
#include "IArchSystem.h"
#define ARCH_SYSTEM CArchSystemWindows
//! Win32 implementation of IArchString
class CArchSystemWindows : public IArchSystem {
public:
CArchSystemWindows();
virtual ~CArchSystemWindows();
// IArchSystem overrides
virtual std::string getOSName() const;
virtual std::string getPlatformName() const;
virtual std::string setting(const std::string& valueName) const;
virtual void setting(const std::string& valueName, const std::string& valueString) const;
bool isWOW64() const;
};
#endif

View File

@@ -0,0 +1,501 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchTaskBarWindows.h"
#include "CArchMiscWindows.h"
#include "IArchTaskBarReceiver.h"
#include "CArch.h"
#include "XArch.h"
#include <string.h>
#include <shellapi.h>
#include "CAppUtilWindows.h"
static const UINT kAddReceiver = WM_USER + 10;
static const UINT kRemoveReceiver = WM_USER + 11;
static const UINT kUpdateReceiver = WM_USER + 12;
static const UINT kNotifyReceiver = WM_USER + 13;
static const UINT kFirstReceiverID = WM_USER + 14;
//
// CArchTaskBarWindows
//
CArchTaskBarWindows* CArchTaskBarWindows::s_instance = NULL;
CArchTaskBarWindows::CArchTaskBarWindows() :
m_nextID(kFirstReceiverID)
{
// save the singleton instance
s_instance = this;
}
CArchTaskBarWindows::~CArchTaskBarWindows()
{
if (m_thread != NULL) {
PostMessage(m_hwnd, WM_QUIT, 0, 0);
ARCH->wait(m_thread, -1.0);
ARCH->closeThread(m_thread);
}
ARCH->closeCondVar(m_condVar);
ARCH->closeMutex(m_mutex);
s_instance = NULL;
}
void
CArchTaskBarWindows::init()
{
// we need a mutex
m_mutex = ARCH->newMutex();
// and a condition variable which uses the above mutex
m_ready = false;
m_condVar = ARCH->newCondVar();
// we're going to want to get a result from the thread we're
// about to create to know if it initialized successfully.
// so we lock the condition variable.
ARCH->lockMutex(m_mutex);
// open a window and run an event loop in a separate thread.
// this has to happen in a separate thread because if we
// create a window on the current desktop with the current
// thread then the current thread won't be able to switch
// desktops if it needs to.
m_thread = ARCH->newThread(&CArchTaskBarWindows::threadEntry, this);
// wait for child thread
while (!m_ready) {
ARCH->waitCondVar(m_condVar, m_mutex, -1.0);
}
// ready
ARCH->unlockMutex(m_mutex);
}
void
CArchTaskBarWindows::addDialog(HWND hwnd)
{
CArchMiscWindows::addDialog(hwnd);
}
void
CArchTaskBarWindows::removeDialog(HWND hwnd)
{
CArchMiscWindows::removeDialog(hwnd);
}
void
CArchTaskBarWindows::addReceiver(IArchTaskBarReceiver* receiver)
{
// ignore bogus receiver
if (receiver == NULL) {
return;
}
// add receiver if necessary
CReceiverToInfoMap::iterator index = m_receivers.find(receiver);
if (index == m_receivers.end()) {
// add it, creating a new message ID for it
CReceiverInfo info;
info.m_id = getNextID();
index = m_receivers.insert(std::make_pair(receiver, info)).first;
// add ID to receiver mapping
m_idTable.insert(std::make_pair(info.m_id, index));
}
// add receiver
PostMessage(m_hwnd, kAddReceiver, index->second.m_id, 0);
}
void
CArchTaskBarWindows::removeReceiver(IArchTaskBarReceiver* receiver)
{
// find receiver
CReceiverToInfoMap::iterator index = m_receivers.find(receiver);
if (index == m_receivers.end()) {
return;
}
// remove icon. wait for this to finish before returning.
SendMessage(m_hwnd, kRemoveReceiver, index->second.m_id, 0);
// recycle the ID
recycleID(index->second.m_id);
// discard
m_idTable.erase(index->second.m_id);
m_receivers.erase(index);
}
void
CArchTaskBarWindows::updateReceiver(IArchTaskBarReceiver* receiver)
{
// find receiver
CReceiverToInfoMap::const_iterator index = m_receivers.find(receiver);
if (index == m_receivers.end()) {
return;
}
// update icon and tool tip
PostMessage(m_hwnd, kUpdateReceiver, index->second.m_id, 0);
}
UINT
CArchTaskBarWindows::getNextID()
{
if (m_oldIDs.empty()) {
return m_nextID++;
}
UINT id = m_oldIDs.back();
m_oldIDs.pop_back();
return id;
}
void
CArchTaskBarWindows::recycleID(UINT id)
{
m_oldIDs.push_back(id);
}
void
CArchTaskBarWindows::addIcon(UINT id)
{
ARCH->lockMutex(m_mutex);
CIDToReceiverMap::const_iterator index = m_idTable.find(id);
if (index != m_idTable.end()) {
modifyIconNoLock(index->second, NIM_ADD);
}
ARCH->unlockMutex(m_mutex);
}
void
CArchTaskBarWindows::removeIcon(UINT id)
{
ARCH->lockMutex(m_mutex);
removeIconNoLock(id);
ARCH->unlockMutex(m_mutex);
}
void
CArchTaskBarWindows::updateIcon(UINT id)
{
ARCH->lockMutex(m_mutex);
CIDToReceiverMap::const_iterator index = m_idTable.find(id);
if (index != m_idTable.end()) {
modifyIconNoLock(index->second, NIM_MODIFY);
}
ARCH->unlockMutex(m_mutex);
}
void
CArchTaskBarWindows::addAllIcons()
{
ARCH->lockMutex(m_mutex);
for (CReceiverToInfoMap::const_iterator index = m_receivers.begin();
index != m_receivers.end(); ++index) {
modifyIconNoLock(index, NIM_ADD);
}
ARCH->unlockMutex(m_mutex);
}
void
CArchTaskBarWindows::removeAllIcons()
{
ARCH->lockMutex(m_mutex);
for (CReceiverToInfoMap::const_iterator index = m_receivers.begin();
index != m_receivers.end(); ++index) {
removeIconNoLock(index->second.m_id);
}
ARCH->unlockMutex(m_mutex);
}
void
CArchTaskBarWindows::modifyIconNoLock(
CReceiverToInfoMap::const_iterator index, DWORD taskBarMessage)
{
// get receiver
UINT id = index->second.m_id;
IArchTaskBarReceiver* receiver = index->first;
// lock receiver so icon and tool tip are guaranteed to be consistent
receiver->lock();
// get icon data
HICON icon = reinterpret_cast<HICON>(
const_cast<IArchTaskBarReceiver::Icon>(receiver->getIcon()));
// get tool tip
std::string toolTip = receiver->getToolTip();
// done querying
receiver->unlock();
// prepare to add icon
NOTIFYICONDATA data;
data.cbSize = sizeof(NOTIFYICONDATA);
data.hWnd = m_hwnd;
data.uID = id;
data.uFlags = NIF_MESSAGE;
data.uCallbackMessage = kNotifyReceiver;
data.hIcon = icon;
if (icon != NULL) {
data.uFlags |= NIF_ICON;
}
if (!toolTip.empty()) {
strncpy(data.szTip, toolTip.c_str(), sizeof(data.szTip));
data.szTip[sizeof(data.szTip) - 1] = '\0';
data.uFlags |= NIF_TIP;
}
else {
data.szTip[0] = '\0';
}
// add icon
if (Shell_NotifyIcon(taskBarMessage, &data) == 0) {
// failed
}
}
void
CArchTaskBarWindows::removeIconNoLock(UINT id)
{
NOTIFYICONDATA data;
data.cbSize = sizeof(NOTIFYICONDATA);
data.hWnd = m_hwnd;
data.uID = id;
if (Shell_NotifyIcon(NIM_DELETE, &data) == 0) {
// failed
}
}
void
CArchTaskBarWindows::handleIconMessage(
IArchTaskBarReceiver* receiver, LPARAM lParam)
{
// process message
switch (lParam) {
case WM_LBUTTONDOWN:
receiver->showStatus();
break;
case WM_LBUTTONDBLCLK:
receiver->primaryAction();
break;
case WM_RBUTTONUP: {
POINT p;
GetCursorPos(&p);
receiver->runMenu(p.x, p.y);
break;
}
case WM_MOUSEMOVE:
// currently unused
break;
default:
// unused
break;
}
}
bool
CArchTaskBarWindows::processDialogs(MSG* msg)
{
// only one thread can be in this method on any particular object
// at any given time. that's not a problem since only our event
// loop calls this method and there's just one of those.
ARCH->lockMutex(m_mutex);
// remove removed dialogs
m_dialogs.erase(false);
// merge added dialogs into the dialog list
for (CDialogs::const_iterator index = m_addedDialogs.begin();
index != m_addedDialogs.end(); ++index) {
m_dialogs.insert(std::make_pair(index->first, index->second));
}
m_addedDialogs.clear();
ARCH->unlockMutex(m_mutex);
// check message against all dialogs until one handles it.
// note that we don't hold a lock while checking because
// the message is processed and may make calls to this
// object. that's okay because addDialog() and
// removeDialog() don't change the map itself (just the
// values of some elements).
ARCH->lockMutex(m_mutex);
for (CDialogs::const_iterator index = m_dialogs.begin();
index != m_dialogs.end(); ++index) {
if (index->second) {
ARCH->unlockMutex(m_mutex);
if (IsDialogMessage(index->first, msg)) {
return true;
}
ARCH->lockMutex(m_mutex);
}
}
ARCH->unlockMutex(m_mutex);
return false;
}
LRESULT
CArchTaskBarWindows::wndProc(HWND hwnd,
UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case kNotifyReceiver: {
// lookup receiver
CIDToReceiverMap::const_iterator index = m_idTable.find((UINT)wParam);
if (index != m_idTable.end()) {
IArchTaskBarReceiver* receiver = index->second->first;
handleIconMessage(receiver, lParam);
return 0;
}
break;
}
case kAddReceiver:
addIcon((UINT)wParam);
break;
case kRemoveReceiver:
removeIcon((UINT)wParam);
break;
case kUpdateReceiver:
updateIcon((UINT)wParam);
break;
default:
if (msg == m_taskBarRestart) {
// task bar was recreated so re-add our icons
addAllIcons();
}
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
LRESULT CALLBACK
CArchTaskBarWindows::staticWndProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
// if msg is WM_NCCREATE, extract the CArchTaskBarWindows* and put
// it in the extra window data then forward the call.
CArchTaskBarWindows* self = NULL;
if (msg == WM_NCCREATE) {
CREATESTRUCT* createInfo;
createInfo = reinterpret_cast<CREATESTRUCT*>(lParam);
self = reinterpret_cast<CArchTaskBarWindows*>(
createInfo->lpCreateParams);
SetWindowLong(hwnd, 0, reinterpret_cast<LONG>(self));
}
else {
// get the extra window data and forward the call
LONG data = GetWindowLong(hwnd, 0);
if (data != 0) {
self = reinterpret_cast<CArchTaskBarWindows*>(
reinterpret_cast<void*>(data));
}
}
// forward the message
if (self != NULL) {
return self->wndProc(hwnd, msg, wParam, lParam);
}
else {
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 = instanceWin32();
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,
instanceWin32(),
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(reinterpret_cast<LPCTSTR>(windowClass), instanceWin32());
return;
}
// main loop
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
if (!processDialogs(&msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// clean up
removeAllIcons();
DestroyWindow(m_hwnd);
UnregisterClass(reinterpret_cast<LPCTSTR>(windowClass), instanceWin32());
}
void*
CArchTaskBarWindows::threadEntry(void* self)
{
reinterpret_cast<CArchTaskBarWindows*>(self)->threadMainLoop();
return NULL;
}
HINSTANCE CArchTaskBarWindows::instanceWin32()
{
return CArchMiscWindows::instanceWin32();
}

View File

@@ -0,0 +1,116 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHTASKBARWINDOWS_H
#define CARCHTASKBARWINDOWS_H
#define WIN32_LEAN_AND_MEAN
#include "IArchTaskBar.h"
#include "IArchMultithread.h"
#include "stdmap.h"
#include "stdvector.h"
#include <windows.h>
#define ARCH_TASKBAR CArchTaskBarWindows
//! Win32 implementation of IArchTaskBar
class CArchTaskBarWindows : public IArchTaskBar {
public:
CArchTaskBarWindows();
virtual ~CArchTaskBarWindows();
virtual void init();
//! Add a dialog window
/*!
Tell the task bar event loop about a dialog. Win32 annoyingly
requires messages destined for modeless dialog boxes to be
dispatched differently than other messages.
*/
static void addDialog(HWND);
//! Remove a dialog window
/*!
Remove a dialog window added via \c addDialog().
*/
static void removeDialog(HWND);
// IArchTaskBar overrides
virtual void addReceiver(IArchTaskBarReceiver*);
virtual void removeReceiver(IArchTaskBarReceiver*);
virtual void updateReceiver(IArchTaskBarReceiver*);
private:
class CReceiverInfo {
public:
UINT m_id;
};
typedef std::map<IArchTaskBarReceiver*, CReceiverInfo> CReceiverToInfoMap;
typedef std::map<UINT, CReceiverToInfoMap::iterator> CIDToReceiverMap;
typedef std::vector<UINT> CIDStack;
typedef std::map<HWND, bool> CDialogs;
UINT getNextID();
void recycleID(UINT);
void addIcon(UINT);
void removeIcon(UINT);
void updateIcon(UINT);
void addAllIcons();
void removeAllIcons();
void modifyIconNoLock(CReceiverToInfoMap::const_iterator,
DWORD taskBarMessage);
void removeIconNoLock(UINT id);
void handleIconMessage(IArchTaskBarReceiver*, LPARAM);
bool processDialogs(MSG*);
LRESULT wndProc(HWND, UINT, WPARAM, LPARAM);
static LRESULT CALLBACK
staticWndProc(HWND, UINT, WPARAM, LPARAM);
void threadMainLoop();
static void* threadEntry(void*);
HINSTANCE instanceWin32();
private:
static CArchTaskBarWindows* s_instance;
// multithread data
CArchMutex m_mutex;
CArchCond m_condVar;
bool m_ready;
int m_result;
CArchThread m_thread;
// child thread data
HWND m_hwnd;
UINT m_taskBarRestart;
// shared data
CReceiverToInfoMap m_receivers;
CIDToReceiverMap m_idTable;
CIDStack m_oldIDs;
UINT m_nextID;
// dialogs
CDialogs m_dialogs;
CDialogs m_addedDialogs;
};
#endif

View File

@@ -0,0 +1,50 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchTaskBarXWindows.h"
//
// CArchTaskBarXWindows
//
CArchTaskBarXWindows::CArchTaskBarXWindows()
{
// do nothing
}
CArchTaskBarXWindows::~CArchTaskBarXWindows()
{
// do nothing
}
void
CArchTaskBarXWindows::addReceiver(IArchTaskBarReceiver* /*receiver*/)
{
// do nothing
}
void
CArchTaskBarXWindows::removeReceiver(IArchTaskBarReceiver* /*receiver*/)
{
// do nothing
}
void
CArchTaskBarXWindows::updateReceiver(IArchTaskBarReceiver* /*receiver*/)
{
// do nothing
}

View File

@@ -0,0 +1,37 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHTASKBARXWINDOWS_H
#define CARCHTASKBARXWINDOWS_H
#include "IArchTaskBar.h"
#define ARCH_TASKBAR CArchTaskBarXWindows
//! X11 implementation of IArchTaskBar
class CArchTaskBarXWindows : public IArchTaskBar {
public:
CArchTaskBarXWindows();
virtual ~CArchTaskBarXWindows();
// IArchTaskBar overrides
virtual void addReceiver(IArchTaskBarReceiver*);
virtual void removeReceiver(IArchTaskBarReceiver*);
virtual void updateReceiver(IArchTaskBarReceiver*);
};
#endif

View File

@@ -0,0 +1,50 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CArchTimeUnix.h"
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
//
// CArchTimeUnix
//
CArchTimeUnix::CArchTimeUnix()
{
// do nothing
}
CArchTimeUnix::~CArchTimeUnix()
{
// do nothing
}
double
CArchTimeUnix::time()
{
struct timeval t;
gettimeofday(&t, NULL);
return (double)t.tv_sec + 1.0e-6 * (double)t.tv_usec;
}

View File

@@ -0,0 +1,35 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHTIMEUNIX_H
#define CARCHTIMEUNIX_H
#include "IArchTime.h"
#define ARCH_TIME CArchTimeUnix
//! Generic Unix implementation of IArchTime
class CArchTimeUnix : public IArchTime {
public:
CArchTimeUnix();
virtual ~CArchTimeUnix();
// IArchTime overrides
virtual double time();
};
#endif

View File

@@ -0,0 +1,89 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// avoid getting a lot a crap from mmsystem.h that we don't need
#define MMNODRV // Installable driver support
#define MMNOSOUND // Sound support
#define MMNOWAVE // Waveform support
#define MMNOMIDI // MIDI support
#define MMNOAUX // Auxiliary audio support
#define MMNOMIXER // Mixer support
#define MMNOJOY // Joystick support
#define MMNOMCI // MCI support
#define MMNOMMIO // Multimedia file I/O support
#define MMNOMMSYSTEM // General MMSYSTEM functions
#define WIN32_LEAN_AND_MEAN
#include "CArchTimeWindows.h"
#include <windows.h>
#include <mmsystem.h>
typedef WINMMAPI DWORD (WINAPI *PTimeGetTime)(void);
static double s_freq = 0.0;
static HINSTANCE s_mmInstance = NULL;
static PTimeGetTime s_tgt = NULL;
//
// CArchTimeWindows
//
CArchTimeWindows::CArchTimeWindows()
{
assert(s_freq == 0.0 || s_mmInstance == NULL);
LARGE_INTEGER freq;
if (QueryPerformanceFrequency(&freq) && freq.QuadPart != 0) {
s_freq = 1.0 / static_cast<double>(freq.QuadPart);
}
else {
// load winmm.dll and get timeGetTime
s_mmInstance = LoadLibrary("winmm");
if (s_mmInstance != NULL) {
s_tgt = (PTimeGetTime)GetProcAddress(s_mmInstance, "timeGetTime");
}
}
}
CArchTimeWindows::~CArchTimeWindows()
{
s_freq = 0.0;
if (s_mmInstance == NULL) {
FreeLibrary(reinterpret_cast<HMODULE>(s_mmInstance));
s_tgt = NULL;
s_mmInstance = NULL;
}
}
double
CArchTimeWindows::time()
{
// get time. we try three ways, in order of descending precision
if (s_freq != 0.0) {
LARGE_INTEGER c;
QueryPerformanceCounter(&c);
return s_freq * static_cast<double>(c.QuadPart);
}
else if (s_tgt != NULL) {
return 0.001 * static_cast<double>(s_tgt());
}
else {
return 0.001 * static_cast<double>(GetTickCount());
}
}

View File

@@ -0,0 +1,35 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CARCHTIMEWINDOWS_H
#define CARCHTIMEWINDOWS_H
#include "IArchTime.h"
#define ARCH_TIME CArchTimeWindows
//! Win32 implementation of IArchTime
class CArchTimeWindows : public IArchTime {
public:
CArchTimeWindows();
virtual ~CArchTimeWindows();
// IArchTime overrides
virtual double time();
};
#endif

111
src/lib/arch/CMakeLists.txt Normal file
View File

@@ -0,0 +1,111 @@
# synergy -- mouse and keyboard sharing utility
# Copyright (C) 2009 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
set(src
CArch.cpp
IArchString.cpp
XArch.cpp
CArchConsoleStd.cpp
)
if (WIN32)
set(inc
CArchConsoleWindows.h
CArchDaemonWindows.h
CArchFileWindows.h
CArchLogWindows.h
CArchIpcLogWindows.h
CArchMiscWindows.h
CArchMultithreadWindows.h
CArchNetworkWinsock.h
CArchSleepWindows.h
CArchStringWindows.h
CArchSystemWindows.h
CArchTaskBarWindows.h
CArchTimeWindows.h
CArchConsoleStd.h
XArchWindows.h
CMultibyte.h
vsnprintf.h
XArch.h
IArchPlugin.h
CArchPluginWindows.h
)
list(APPEND src
${inc}
CArchConsoleWindows.cpp
CArchDaemonWindows.cpp
CArchFileWindows.cpp
CArchLogWindows.cpp
CArchIpcLogWindows.cpp
CArchMiscWindows.cpp
CArchMultithreadWindows.cpp
CArchNetworkWinsock.cpp
CArchSleepWindows.cpp
CArchStringWindows.cpp
CArchSystemWindows.cpp
CArchTaskBarWindows.cpp
CArchTimeWindows.cpp
XArchWindows.cpp
CArchPluginWindows.cpp
)
elseif (UNIX)
list(APPEND src
CArchConsoleUnix.cpp
CArchDaemonUnix.cpp
CArchFileUnix.cpp
CArchLogUnix.cpp
CArchIpcLogUnix.cpp
CArchMultithreadPosix.cpp
CArchNetworkBSD.cpp
CArchSleepUnix.cpp
CArchStringUnix.cpp
CArchSystemUnix.cpp
CArchTaskBarXWindows.cpp
CArchTimeUnix.cpp
XArchUnix.cpp
CArchDaemonNone.cpp
CArchPluginUnix.cpp
)
endif()
set(inc
../base
../common
../mt
../platform
../synergy
)
if (UNIX)
list(APPEND inc
../../..
../arch
)
endif()
include_directories(${inc})
add_library(arch STATIC ${src})
if (WIN32)
if (GAME_DEVICE_SUPPORT)
target_link_libraries(arch synxinhk)
endif()
endif()

57
src/lib/arch/CMultibyte.h Normal file
View File

@@ -0,0 +1,57 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CMULTIBYTE_H
#define CMULTIBYTE_H
#include "common.h"
#include "CArch.h"
#include <climits>
#include <cstring>
#include <cstdlib>
#if HAVE_LOCALE_H
# include <locale.h>
#endif
#if HAVE_WCHAR_H || defined(_MSC_VER)
# include <wchar.h>
#elif __APPLE__
// wtf? Darwin puts mbtowc() et al. in stdlib
# include <cstdlib>
#else
// platform apparently has no wchar_t support. provide dummy
// implementations. hopefully at least the C++ compiler has
// a built-in wchar_t type.
static inline
int
mbtowc(wchar_t* dst, const char* src, int n)
{
*dst = static_cast<wchar_t>(*src);
return 1;
}
static inline
int
wctomb(char* dst, wchar_t src)
{
*dst = static_cast<char>(src);
return 1;
}
#endif
#endif

View File

@@ -0,0 +1,68 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef IARCHCONSOLE_H
#define IARCHCONSOLE_H
#include "IInterface.h"
#include "ELevel.h"
//! Interface for architecture dependent console output
/*!
This interface defines the console operations required by
synergy. Each architecture must implement this interface.
*/
class IArchConsole : public IInterface {
public:
//! @name manipulators
//@{
//! Open the console
/*!
Opens the console for writing. The console is opened automatically
on the first write so calling this method is optional. Uses \c title
for the console's title if appropriate for the architecture. Calling
this method on an already open console must have no effect.
*/
virtual void openConsole(const char* title) = 0;
//! Close the console
/*!
Close the console. Calling this method on an already closed console
must have no effect.
*/
virtual void closeConsole() = 0;
//! Show the console
/*!
Causes the console to become visible. This generally only makes sense
for a console in a graphical user interface. Other implementations
will do nothing. Iff \p showIfEmpty is \c false then the implementation
may optionally only show the console if it's not empty.
*/
virtual void showConsole(bool showIfEmpty) = 0;
//! Write to the console
/*!
Writes the given string to the console, opening it if necessary.
*/
virtual void writeConsole(ELevel, const char*) = 0;
//@}
};
#endif

131
src/lib/arch/IArchDaemon.h Normal file
View File

@@ -0,0 +1,131 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef IARCHDAEMON_H
#define IARCHDAEMON_H
#include "IInterface.h"
#include "CString.h"
//! Interface for architecture dependent daemonizing
/*!
This interface defines the operations required by synergy for installing
uninstalling daeamons and daemonizing a process. Each architecture must
implement this interface.
*/
class IArchDaemon : public IInterface {
public:
typedef int (*DaemonFunc)(int argc, const char** argv);
//! @name manipulators
//@{
//! Install daemon
/*!
Install a daemon. \c name is the name of the daemon passed to the
system and \c description is a short human readable description of
the daemon. \c pathname is the path to the daemon executable.
\c commandLine should \b not include the name of program as the
first argument. If \c allUsers is true then the daemon will be
installed to start at boot time, otherwise it will be installed to
start when the current user logs in. If \p dependencies is not NULL
then it's a concatenation of NUL terminated other daemon names
followed by a NUL; the daemon will be configured to startup after
the listed daemons. Throws an \c XArchDaemon exception on failure.
*/
virtual void installDaemon(const char* name,
const char* description,
const char* pathname,
const char* commandLine,
const char* dependencies,
bool allUsers) = 0;
//! Uninstall daemon
/*!
Uninstall a daemon. Throws an \c XArchDaemon on failure.
*/
virtual void uninstallDaemon(const char* name, bool allUsers) = 0;
//! Install daemon
/*!
Installs the default daemon.
*/
virtual void installDaemon() = 0;
//! Uninstall daemon
/*!
Uninstalls the default daemon.
*/
virtual void uninstallDaemon() = 0;
//! Daemonize the process
/*!
Daemonize. Throw XArchDaemonFailed on error. \c name is the name
of the daemon. Once daemonized, \c func is invoked and daemonize
returns when and what it does.
Exactly what happens when daemonizing depends on the platform.
<ul>
<li>unix:
Detaches from terminal. \c func gets passed one argument, the
name passed to daemonize().
<li>win32:
Becomes a service. Argument 0 is the name of the service
and the rest are the arguments passed to StartService().
\c func is only called when the service is actually started.
\c func must call \c CArchMiscWindows::runDaemon() to finally
becoming a service. The \c runFunc function passed to \c runDaemon()
must call \c CArchMiscWindows::daemonRunning(true) when it
enters the main loop (i.e. after initialization) and
\c CArchMiscWindows::daemonRunning(false) when it leaves
the main loop. The \c stopFunc function passed to \c runDaemon()
is called when the daemon must exit the main loop and it must cause
\c runFunc to return. \c func should return what \c runDaemon()
returns. \c func or \c runFunc can call
\c CArchMiscWindows::daemonFailed() to indicate startup failure.
</ul>
*/
virtual int daemonize(const char* name, DaemonFunc func) = 0;
//! Check if user has permission to install the daemon
/*!
Returns true iff the caller has permission to install or
uninstall the daemon. Note that even if this method returns
true it's possible that installing/uninstalling the service
may still fail. This method ignores whether or not the
service is already installed.
*/
virtual bool canInstallDaemon(const char* name, bool allUsers) = 0;
//! Check if the daemon is installed
/*!
Returns true iff the daemon is installed.
*/
virtual bool isDaemonInstalled(const char* name, bool allUsers) = 0;
//@}
//! Get the command line
/*!
Gets the command line with which the application was started.
*/
virtual std::string commandLine() const = 0;
//@}
};
#endif

67
src/lib/arch/IArchFile.h Normal file
View File

@@ -0,0 +1,67 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef IARCHFILE_H
#define IARCHFILE_H
#include "IInterface.h"
#include "stdstring.h"
//! Interface for architecture dependent file system operations
/*!
This interface defines the file system operations required by
synergy. Each architecture must implement this interface.
*/
class IArchFile : public IInterface {
public:
//! @name manipulators
//@{
//! Extract base name
/*!
Find the base name in the given \c pathname.
*/
virtual const char* getBasename(const char* pathname) = 0;
//! Get user's home directory
/*!
Returns the user's home directory. Returns the empty string if
this cannot be determined.
*/
virtual std::string getUserDirectory() = 0;
//! Get system directory
/*!
Returns the ussystem configuration file directory.
*/
virtual std::string getSystemDirectory() = 0;
//! Concatenate path components
/*!
Concatenate pathname components with a directory separator
between them. This should not check if the resulting path
is longer than allowed by the system; we'll rely on the
system calls to tell us that.
*/
virtual std::string concatPath(
const std::string& prefix,
const std::string& suffix) = 0;
//@}
};
#endif

65
src/lib/arch/IArchLog.h Normal file
View File

@@ -0,0 +1,65 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef IARCHLOG_H
#define IARCHLOG_H
#include "IInterface.h"
#include "ELevel.h"
//! Interface for architecture dependent logging
/*!
This interface defines the logging operations required by
synergy. Each architecture must implement this interface.
*/
class IArchLog : public IInterface {
public:
//! @name manipulators
//@{
//! Open the log
/*!
Opens the log for writing. The log must be opened before being
written to.
*/
virtual void openLog(const char* name) = 0;
//! Close the log
/*!
Close the log.
*/
virtual void closeLog() = 0;
//! Show the log
/*!
Causes the log to become visible. This generally only makes sense
for a log in a graphical user interface. Other implementations
will do nothing. Iff \p showIfEmpty is \c false then the implementation
may optionally only show the log if it's not empty.
*/
virtual void showLog(bool showIfEmpty) = 0;
//! Write to the log
/*!
Writes the given string to the log with the given level.
*/
virtual void writeLog(ELevel, const char*) = 0;
//@}
};
#endif

View File

@@ -0,0 +1,275 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef IARCHMULTITHREAD_H
#define IARCHMULTITHREAD_H
#include "IInterface.h"
/*!
\class CArchCondImpl
\brief Internal condition variable data.
An architecture dependent type holding the necessary data for a
condition variable.
*/
class CArchCondImpl;
/*!
\var CArchCond
\brief Opaque condition variable type.
An opaque type representing a condition variable.
*/
typedef CArchCondImpl* CArchCond;
/*!
\class CArchMutexImpl
\brief Internal mutex data.
An architecture dependent type holding the necessary data for a mutex.
*/
class CArchMutexImpl;
/*!
\var CArchMutex
\brief Opaque mutex type.
An opaque type representing a mutex.
*/
typedef CArchMutexImpl* CArchMutex;
/*!
\class CArchThreadImpl
\brief Internal thread data.
An architecture dependent type holding the necessary data for a thread.
*/
class CArchThreadImpl;
/*!
\var CArchThread
\brief Opaque thread type.
An opaque type representing a thread.
*/
typedef CArchThreadImpl* CArchThread;
//! Interface for architecture dependent multithreading
/*!
This interface defines the multithreading operations required by
synergy. Each architecture must implement this interface.
*/
class IArchMultithread : public IInterface {
public:
//! Type of thread entry point
typedef void* (*ThreadFunc)(void*);
//! Type of thread identifier
typedef unsigned int ThreadID;
//! Types of signals
/*!
Not all platforms support all signals. Unsupported signals are
ignored.
*/
enum ESignal {
kINTERRUPT, //!< Interrupt (e.g. Ctrl+C)
kTERMINATE, //!< Terminate (e.g. Ctrl+Break)
kHANGUP, //!< Hangup (SIGHUP)
kUSER, //!< User (SIGUSR2)
kNUM_SIGNALS
};
//! Type of signal handler function
typedef void (*SignalFunc)(ESignal, void* userData);
//! @name manipulators
//@{
//
// condition variable methods
//
//! Create a condition variable
/*!
The condition variable is an opaque data type.
*/
virtual CArchCond newCondVar() = 0;
//! Destroy a condition variable
virtual void closeCondVar(CArchCond) = 0;
//! Signal a condition variable
/*!
Signalling a condition variable releases one waiting thread.
*/
virtual void signalCondVar(CArchCond) = 0;
//! Broadcast a condition variable
/*!
Broadcasting a condition variable releases all waiting threads.
*/
virtual void broadcastCondVar(CArchCond) = 0;
//! Wait on a condition variable
/*!
Wait on a conditation variable for up to \c timeout seconds.
If \c timeout is < 0 then there is no timeout. The mutex must
be locked when this method is called. The mutex is unlocked
during the wait and locked again before returning. Returns
true if the condition variable was signalled and false on
timeout.
(Cancellation point)
*/
virtual bool waitCondVar(CArchCond, CArchMutex, double timeout) = 0;
//
// mutex methods
//
//! Create a recursive mutex
/*!
Creates a recursive mutex. A thread may lock a recursive mutex
when it already holds a lock on that mutex. The mutex is an
opaque data type.
*/
virtual CArchMutex newMutex() = 0;
//! Destroy a mutex
virtual void closeMutex(CArchMutex) = 0;
//! Lock a mutex
virtual void lockMutex(CArchMutex) = 0;
//! Unlock a mutex
virtual void unlockMutex(CArchMutex) = 0;
//
// thread methods
//
//! Start a new thread
/*!
Creates and starts a new thread, using \c func as the entry point
and passing it \c userData. The thread is an opaque data type.
*/
virtual CArchThread newThread(ThreadFunc func, void* userData) = 0;
//! Get a reference to the calling thread
/*!
Returns a thread representing the current (i.e. calling) thread.
*/
virtual CArchThread newCurrentThread() = 0;
//! Copy a thread object
/*!
Returns a reference to to thread referred to by \c thread.
*/
virtual CArchThread copyThread(CArchThread thread) = 0;
//! Release a thread reference
/*!
Deletes the given thread object. This does not destroy the thread
the object referred to, even if there are no remaining references.
Use cancelThread() and waitThread() to stop a thread and wait for
it to exit.
*/
virtual void closeThread(CArchThread) = 0;
//! Force a thread to exit
/*!
Causes \c thread to exit when it next calls a cancellation point.
A thread avoids cancellation as long as it nevers calls a
cancellation point. Once it begins the cancellation process it
must always let cancellation go to completion but may take as
long as necessary to clean up.
*/
virtual void cancelThread(CArchThread thread) = 0;
//! Change thread priority
/*!
Changes the priority of \c thread by \c n. If \c n is positive
the thread has a lower priority and if negative a higher priority.
Some architectures may not support either or both directions.
*/
virtual void setPriorityOfThread(CArchThread, int n) = 0;
//! Cancellation point
/*!
This method does nothing but is a cancellation point. Clients
can make their own functions cancellation points by calling this
method at appropriate times.
(Cancellation point)
*/
virtual void testCancelThread() = 0;
//! Wait for a thread to exit
/*!
Waits for up to \c timeout seconds for \c thread to exit (normally
or by cancellation). Waits forever if \c timeout < 0. Returns
true if the thread exited, false otherwise. Waiting on the current
thread returns immediately with false.
(Cancellation point)
*/
virtual bool wait(CArchThread thread, double timeout) = 0;
//! Compare threads
/*!
Returns true iff two thread objects refer to the same thread.
Note that comparing thread objects directly is meaningless.
*/
virtual bool isSameThread(CArchThread, CArchThread) = 0;
//! Test if thread exited
/*!
Returns true iff \c thread has exited.
*/
virtual bool isExitedThread(CArchThread thread) = 0;
//! Returns the exit code of a thread
/*!
Waits indefinitely for \c thread to exit (if it hasn't yet) then
returns the thread's exit code.
(Cancellation point)
*/
virtual void* getResultOfThread(CArchThread thread) = 0;
//! Returns an ID for a thread
/*!
Returns some ID number for \c thread. This is for logging purposes.
All thread objects referring to the same thread return the same ID.
However, clients should us isSameThread() to compare thread objects
instead of comparing IDs.
*/
virtual ThreadID getIDOfThread(CArchThread thread) = 0;
//! Set the interrupt handler
/*!
Sets the function to call on receipt of an external interrupt.
By default and when \p func is NULL, the main thread is cancelled.
*/
virtual void setSignalHandler(ESignal, SignalFunc func,
void* userData) = 0;
//! Invoke the signal handler
/*!
Invokes the signal handler for \p signal, if any. If no handler
cancels the main thread for \c kINTERRUPT and \c kTERMINATE and
ignores the call otherwise.
*/
virtual void raiseSignal(ESignal signal) = 0;
//@}
};
#endif

284
src/lib/arch/IArchNetwork.h Normal file
View File

@@ -0,0 +1,284 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef IARCHNETWORK_H
#define IARCHNETWORK_H
#include "IInterface.h"
#include "stdstring.h"
class CArchThreadImpl;
typedef CArchThreadImpl* CArchThread;
/*!
\class CArchSocketImpl
\brief Internal socket data.
An architecture dependent type holding the necessary data for a socket.
*/
class CArchSocketImpl;
/*!
\var CArchSocket
\brief Opaque socket type.
An opaque type representing a socket.
*/
typedef CArchSocketImpl* CArchSocket;
/*!
\class CArchNetAddressImpl
\brief Internal network address data.
An architecture dependent type holding the necessary data for a network
address.
*/
class CArchNetAddressImpl;
/*!
\var CArchNetAddress
\brief Opaque network address type.
An opaque type representing a network address.
*/
typedef CArchNetAddressImpl* CArchNetAddress;
//! Interface for architecture dependent networking
/*!
This interface defines the networking operations required by
synergy. Each architecture must implement this interface.
*/
class IArchNetwork : public IInterface {
public:
//! Supported address families
enum EAddressFamily {
kUNKNOWN,
kINET,
};
//! Supported socket types
enum ESocketType {
kDGRAM,
kSTREAM
};
//! Events for \c poll()
/*!
Events for \c poll() are bitmasks and can be combined using the
bitwise operators.
*/
enum {
kPOLLIN = 1, //!< Socket is readable
kPOLLOUT = 2, //!< Socket is writable
kPOLLERR = 4, //!< The socket is in an error state
kPOLLNVAL = 8 //!< The socket is invalid
};
//! A socket query for \c poll()
class CPollEntry {
public:
//! The socket to query
CArchSocket m_socket;
//! The events to query for
/*!
The events to query for can be any combination of kPOLLIN and
kPOLLOUT.
*/
unsigned short m_events;
//! The result events
unsigned short m_revents;
};
//! @name manipulators
//@{
//! Create a new socket
/*!
The socket is an opaque data type.
*/
virtual CArchSocket newSocket(EAddressFamily, ESocketType) = 0;
//! Copy a socket object
/*!
Returns a reference to to socket referred to by \c s.
*/
virtual CArchSocket copySocket(CArchSocket s) = 0;
//! Release a socket reference
/*!
Deletes the given socket object. This does not destroy the socket
the object referred to until there are no remaining references for
the socket.
*/
virtual void closeSocket(CArchSocket s) = 0;
//! Close socket for further reads
/*!
Calling this disallows future reads on socket \c s.
*/
virtual void closeSocketForRead(CArchSocket s) = 0;
//! Close socket for further writes
/*!
Calling this disallows future writes on socket \c s.
*/
virtual void closeSocketForWrite(CArchSocket s) = 0;
//! Bind socket to address
/*!
Binds socket \c s to the address \c addr.
*/
virtual void bindSocket(CArchSocket s, CArchNetAddress addr) = 0;
//! Listen for connections on socket
/*!
Causes the socket \c s to begin listening for incoming connections.
*/
virtual void listenOnSocket(CArchSocket s) = 0;
//! Accept connection on socket
/*!
Accepts a connection on socket \c s, returning a new socket for the
connection and filling in \c addr with the address of the remote
end. \c addr may be NULL if the remote address isn't required.
The original socket \c s is unaffected and remains in the listening
state. The new socket shares most of the properties of \c s except
it's not in the listening state and it's connected. Returns NULL
if there are no pending connection requests.
*/
virtual CArchSocket acceptSocket(CArchSocket s, CArchNetAddress* addr) = 0;
//! Connect socket
/*!
Connects the socket \c s to the remote address \c addr. Returns
true if the connection succeed immediately, false if the connection
is in progress, and throws if the connection failed immediately.
If it returns false, \c pollSocket() can be used to wait on the
socket for writing to detect when the connection finally succeeds
or fails.
*/
virtual bool connectSocket(CArchSocket s, CArchNetAddress addr) = 0;
//! Check socket state
/*!
Tests the state of \c num sockets for readability and/or writability.
Waits up to \c timeout seconds for some socket to become readable
and/or writable (or indefinitely if \c timeout < 0). Returns the
number of sockets that were readable (if readability was being
queried) or writable (if writablility was being queried) and sets
the \c m_revents members of the entries. \c kPOLLERR and \c kPOLLNVAL
are set in \c m_revents as appropriate. If a socket indicates
\c kPOLLERR then \c throwErrorOnSocket() can be used to determine
the type of error. Returns 0 immediately regardless of the \c timeout
if no valid sockets are selected for testing.
(Cancellation point)
*/
virtual int pollSocket(CPollEntry[], int num, double timeout) = 0;
//! Unblock thread in pollSocket()
/*!
Cause a thread that's in a pollSocket() call to return. This
call may return before the thread is unblocked. If the thread is
not in a pollSocket() call this call has no effect.
*/
virtual void unblockPollSocket(CArchThread thread) = 0;
//! Read data from socket
/*!
Read up to \c len bytes from socket \c s in \c buf and return the
number of bytes read. The number of bytes can be less than \c len
if not enough data is available. Returns 0 if the remote end has
disconnected and/or there is no more queued received data.
*/
virtual size_t readSocket(CArchSocket s, void* buf, size_t len) = 0;
//! Write data from socket
/*!
Write up to \c len bytes to socket \c s from \c buf and return the
number of bytes written. The number of bytes can be less than
\c len if the remote end disconnected or the internal buffers fill
up.
*/
virtual size_t writeSocket(CArchSocket s,
const void* buf, size_t len) = 0;
//! Check error on socket
/*!
If the socket \c s is in an error state then throws an appropriate
XArchNetwork exception.
*/
virtual void throwErrorOnSocket(CArchSocket s) = 0;
//! Turn Nagle algorithm on or off on socket
/*!
Set socket to send messages immediately (true) or to collect small
messages into one packet (false). Returns the previous state.
*/
virtual bool setNoDelayOnSocket(CArchSocket, bool noDelay) = 0;
//! Turn address reuse on or off on socket
/*!
Allows the address this socket is bound to to be reused while in the
TIME_WAIT state. Returns the previous state.
*/
virtual bool setReuseAddrOnSocket(CArchSocket, bool reuse) = 0;
//! Return local host's name
virtual std::string getHostName() = 0;
//! Create an "any" network address
virtual CArchNetAddress newAnyAddr(EAddressFamily) = 0;
//! Copy a network address
virtual CArchNetAddress copyAddr(CArchNetAddress) = 0;
//! Convert a name to a network address
virtual CArchNetAddress nameToAddr(const std::string&) = 0;
//! Destroy a network address
virtual void closeAddr(CArchNetAddress) = 0;
//! Convert an address to a host name
virtual std::string addrToName(CArchNetAddress) = 0;
//! Convert an address to a string
virtual std::string addrToString(CArchNetAddress) = 0;
//! Get an address's family
virtual EAddressFamily getAddrFamily(CArchNetAddress) = 0;
//! Set the port of an address
virtual void setAddrPort(CArchNetAddress, int port) = 0;
//! Get the port of an address
virtual int getAddrPort(CArchNetAddress) = 0;
//! Test addresses for equality
virtual bool isEqualAddr(CArchNetAddress, CArchNetAddress) = 0;
//! Test for the "any" address
/*!
Returns true if \c addr is the "any" address. \c newAnyAddr()
returns an "any" address.
*/
virtual bool isAnyAddr(CArchNetAddress addr) = 0;
//@}
virtual void init() = 0;
};
#endif

View File

@@ -0,0 +1,41 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012 Nick Bolton
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define PLUGINS_DIR "plugins"
#include "IInterface.h"
//! Interface for plugin manager.
/*!
A plugin manager should load all 3rd party plugins from the plugins dir,
and then look for common function names in the plugins.
*/
class IArchPlugin : public IInterface {
public:
//! @name manipulators
//@{
//! Load plugins
/*!
Scan the plugins dir and load plugins.
*/
virtual void init(void* eventTarget) = 0;
//@}
};

46
src/lib/arch/IArchSleep.h Normal file
View File

@@ -0,0 +1,46 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef IARCHSLEEP_H
#define IARCHSLEEP_H
#include "IInterface.h"
//! Interface for architecture dependent sleeping
/*!
This interface defines the sleep operations required by
synergy. Each architecture must implement this interface.
*/
class IArchSleep : public IInterface {
public:
//! @name manipulators
//@{
//! Sleep
/*!
Blocks the calling thread for \c timeout seconds. If
\c timeout < 0.0 then the call returns immediately. If \c timeout
== 0.0 then the calling thread yields the CPU.
(cancellation point)
*/
virtual void sleep(double timeout) = 0;
//@}
};
#endif

View File

@@ -0,0 +1,187 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2011 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "IArchString.h"
#include "common.h"
#include "CArch.h"
#include <climits>
#include <cstring>
#include <cstdlib>
static CArchMutex s_mutex = NULL;
//
// use C library non-reentrant multibyte conversion with mutex
//
IArchString::~IArchString()
{
if (s_mutex != NULL) {
ARCH->closeMutex(s_mutex);
s_mutex = NULL;
}
}
int
IArchString::convStringWCToMB(char* dst,
const wchar_t* src, UInt32 n, bool* errors)
{
int len = 0;
bool dummyErrors;
if (errors == NULL) {
errors = &dummyErrors;
}
if (s_mutex == NULL) {
s_mutex = ARCH->newMutex();
}
ARCH->lockMutex(s_mutex);
if (dst == NULL) {
char dummy[MB_LEN_MAX];
for (const wchar_t* scan = src; n > 0; ++scan, --n) {
int mblen = wctomb(dummy, *scan);
if (mblen == -1) {
*errors = true;
mblen = 1;
}
len += mblen;
}
int mblen = wctomb(dummy, L'\0');
if (mblen != -1) {
len += mblen - 1;
}
}
else {
char* dst0 = dst;
for (const wchar_t* scan = src; n > 0; ++scan, --n) {
int mblen = wctomb(dst, *scan);
if (mblen == -1) {
*errors = true;
*dst++ = '?';
}
else {
dst += mblen;
}
}
int mblen = wctomb(dst, L'\0');
if (mblen != -1) {
// don't include nul terminator
dst += mblen - 1;
}
len = (int)(dst - dst0);
}
ARCH->unlockMutex(s_mutex);
return len;
}
int
IArchString::convStringMBToWC(wchar_t* dst,
const char* src, UInt32 n, bool* errors)
{
int len = 0;
wchar_t dummy;
bool dummyErrors;
if (errors == NULL) {
errors = &dummyErrors;
}
if (s_mutex == NULL) {
s_mutex = ARCH->newMutex();
}
ARCH->lockMutex(s_mutex);
if (dst == NULL) {
for (const char* scan = src; n > 0; ) {
int mblen = mbtowc(&dummy, scan, n);
switch (mblen) {
case -2:
// incomplete last character. convert to unknown character.
*errors = true;
len += 1;
n = 0;
break;
case -1:
// invalid character. count one unknown character and
// start at the next byte.
*errors = true;
len += 1;
scan += 1;
n -= 1;
break;
case 0:
len += 1;
scan += 1;
n -= 1;
break;
default:
// normal character
len += 1;
scan += mblen;
n -= mblen;
break;
}
}
}
else {
wchar_t* dst0 = dst;
for (const char* scan = src; n > 0; ++dst) {
int mblen = mbtowc(dst, scan, n);
switch (mblen) {
case -2:
// incomplete character. convert to unknown character.
*errors = true;
*dst = (wchar_t)0xfffd;
n = 0;
break;
case -1:
// invalid character. count one unknown character and
// start at the next byte.
*errors = true;
*dst = (wchar_t)0xfffd;
scan += 1;
n -= 1;
break;
case 0:
*dst = (wchar_t)0x0000;
scan += 1;
n -= 1;
break;
default:
// normal character
scan += mblen;
n -= mblen;
break;
}
}
len = (int)(dst - dst0);
}
ARCH->unlockMutex(s_mutex);
return len;
}

View File

@@ -0,0 +1,73 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef IARCHSTRING_H
#define IARCHSTRING_H
#include "IInterface.h"
#include "BasicTypes.h"
#include <stdarg.h>
//! Interface for architecture dependent string operations
/*!
This interface defines the string operations required by
synergy. Each architecture must implement this interface.
*/
class IArchString : public IInterface {
public:
virtual ~IArchString();
//! Wide character encodings
/*!
The known wide character encodings
*/
enum EWideCharEncoding {
kUCS2, //!< The UCS-2 encoding
kUCS4, //!< The UCS-4 encoding
kUTF16, //!< The UTF-16 encoding
kUTF32 //!< The UTF-32 encoding
};
//! @name manipulators
//@{
//! printf() to limited size buffer with va_list
/*!
This method is equivalent to vsprintf() except it will not write
more than \c n bytes to the buffer, returning -1 if the output
was truncated and the number of bytes written not including the
trailing NUL otherwise.
*/
virtual int vsnprintf(char* str,
int size, const char* fmt, va_list ap);
//! Convert multibyte string to wide character string
virtual int convStringMBToWC(wchar_t*,
const char*, UInt32 n, bool* errors);
//! Convert wide character string to multibyte string
virtual int convStringWCToMB(char*,
const wchar_t*, UInt32 n, bool* errors);
//! Return the architecture's native wide character encoding
virtual EWideCharEncoding
getWideCharEncoding() = 0;
//@}
};
#endif

View File

@@ -0,0 +1,61 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef IARCHSYSTEM_H
#define IARCHSYSTEM_H
#include "IInterface.h"
#include "stdstring.h"
//! Interface for architecture dependent system queries
/*!
This interface defines operations for querying system info.
*/
class IArchSystem : public IInterface {
public:
//! @name accessors
//@{
//! Identify the OS
/*!
Returns a string identifying the operating system.
*/
virtual std::string getOSName() const = 0;
//! Identify the platform
/*!
Returns a string identifying the platform this OS is running on.
*/
virtual std::string getPlatformName() const = 0;
//@}
//! Get a Synergy setting
/*!
Reads a Synergy setting from the system.
*/
virtual std::string setting(const std::string& valueName) const = 0;
//@}
//! Set a Synergy setting
/*!
Writes a Synergy setting from the system.
*/
virtual void setting(const std::string& valueName, const std::string& valueString) const = 0;
//@}
};
#endif

View File

@@ -0,0 +1,65 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef IARCHTASKBAR_H
#define IARCHTASKBAR_H
#include "IInterface.h"
class IArchTaskBarReceiver;
//! Interface for architecture dependent task bar control
/*!
This interface defines the task bar icon operations required
by synergy. Each architecture must implement this interface
though each operation can be a no-op.
*/
class IArchTaskBar : public IInterface {
public:
//! @name manipulators
//@{
//! Add a receiver
/*!
Add a receiver object to be notified of user and application
events. This should be called before other methods. When
the receiver is added to the task bar, its icon appears on
the task bar.
*/
virtual void addReceiver(IArchTaskBarReceiver*) = 0;
//! Remove a receiver
/*!
Remove a receiver object from the task bar. This removes the
icon from the task bar.
*/
virtual void removeReceiver(IArchTaskBarReceiver*) = 0;
//! Update a receiver
/*!
Updates the display of the receiver on the task bar. This
should be called when the receiver appearance may have changed
(e.g. it's icon or tool tip has changed).
*/
virtual void updateReceiver(IArchTaskBarReceiver*) = 0;
//@}
virtual void init() = 0;
};
#endif

View File

@@ -0,0 +1,100 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef IARCHTASKBARRECEIVER_H
#define IARCHTASKBARRECEIVER_H
#include "IInterface.h"
#include "stdstring.h"
class IScreen;
class INode;
//! Interface for architecture dependent task bar event handling
/*!
This interface defines the task bar icon event handlers required
by synergy. Each architecture must implement this interface
though each operation can be a no-op.
*/
class IArchTaskBarReceiver : public IInterface {
public:
// Icon data is architecture dependent
typedef void* Icon;
//! @name manipulators
//@{
//! Show status window
/*!
Open a window displaying current status. This should return
immediately without waiting for the window to be closed.
*/
virtual void showStatus() = 0;
//! Popup menu
/*!
Popup a menu of operations at or around \c x,y and perform the
chosen operation.
*/
virtual void runMenu(int x, int y) = 0;
//! Perform primary action
/*!
Perform the primary (default) action.
*/
virtual void primaryAction() = 0;
//@}
//! @name accessors
//@{
//! Lock receiver
/*!
Locks the receiver from changing state. The receiver should be
locked when querying it's state to ensure consistent results.
Each call to \c lock() must have a matching \c unlock() and
locks cannot be nested.
*/
virtual void lock() const = 0;
//! Unlock receiver
virtual void unlock() const = 0;
//! Get icon
/*!
Returns the icon to display in the task bar. The interface
to set the icon is left to subclasses. Getting and setting
the icon must be thread safe.
*/
virtual const Icon getIcon() const = 0;
//! Get tooltip
/*!
Returns the tool tip to display in the task bar. The interface
to set the tooltip is left to sublclasses. Getting and setting
the icon must be thread safe.
*/
virtual std::string getToolTip() const = 0;
virtual void updateStatus(INode*, const CString& errorMsg) = 0;
virtual void cleanup() {}
//@}
};
#endif

43
src/lib/arch/IArchTime.h Normal file
View File

@@ -0,0 +1,43 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef IARCHTIME_H
#define IARCHTIME_H
#include "IInterface.h"
//! Interface for architecture dependent time operations
/*!
This interface defines the time operations required by
synergy. Each architecture must implement this interface.
*/
class IArchTime : public IInterface {
public:
//! @name manipulators
//@{
//! Get the current time
/*!
Returns the number of seconds since some arbitrary starting time.
This should return as high a precision as reasonable.
*/
virtual double time() = 0;
//@}
};
#endif

36
src/lib/arch/XArch.cpp Normal file
View File

@@ -0,0 +1,36 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "XArch.h"
//
// XArch
//
std::string
XArch::what() const throw()
{
try {
if (m_what.empty() && m_eval != NULL) {
m_what = m_eval->eval();
}
}
catch (...) {
// ignore
}
return m_what;
}

173
src/lib/arch/XArch.h Normal file
View File

@@ -0,0 +1,173 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef XARCH_H
#define XARCH_H
#include "common.h"
#include "stdstring.h"
//! Generic thread exception
/*!
Exceptions derived from this class are used by the multithreading
library to perform stack unwinding when a thread terminates. These
exceptions must always be rethrown by clients when caught.
*/
class XThread { };
//! Thread exception to cancel
/*!
Thrown to cancel a thread. Clients must not throw this type, but
must rethrow it if caught (by XThreadCancel, XThread, or ...).
*/
class XThreadCancel : public XThread { };
/*!
\def RETHROW_XTHREAD
Convenience macro to rethrow an XThread exception but ignore other
exceptions. Put this in your catch (...) handler after necessary
cleanup but before leaving or returning from the handler.
*/
#define RETHROW_XTHREAD \
try { throw; } catch (XThread&) { throw; } catch (...) { }
//! Lazy error message string evaluation
/*!
This class encapsulates platform dependent error string lookup.
Platforms subclass this type, taking an appropriate error code
type in the c'tor and overriding eval() to return the error
string for that error code.
*/
class XArchEval {
public:
XArchEval() { }
virtual ~XArchEval() { }
virtual XArchEval* clone() const throw() = 0;
virtual std::string eval() const throw() = 0;
};
//! Generic exception architecture dependent library
class XArch {
public:
XArch(XArchEval* adoptedEvaluator) : m_eval(adoptedEvaluator) { }
XArch(const std::string& msg) : m_eval(NULL), m_what(msg) { }
XArch(const XArch& e) : m_eval(e.m_eval != NULL ? e.m_eval->clone() : NULL),
m_what(e.m_what) { }
~XArch() { delete m_eval; }
std::string what() const throw();
private:
XArchEval* m_eval;
mutable std::string m_what;
};
// Macro to declare XArch derived types
#define XARCH_SUBCLASS(name_, super_) \
class name_ : public super_ { \
public: \
name_(XArchEval* adoptedEvaluator) : super_(adoptedEvaluator) { } \
name_(const std::string& msg) : super_(msg) { } \
}
//! Generic network exception
/*!
Exceptions derived from this class are used by the networking
library to indicate various errors.
*/
XARCH_SUBCLASS(XArchNetwork, XArch);
//! Operation was interrupted
XARCH_SUBCLASS(XArchNetworkInterrupted, XArchNetwork);
//! Network insufficient permission
XARCH_SUBCLASS(XArchNetworkAccess, XArchNetwork);
//! Network insufficient resources
XARCH_SUBCLASS(XArchNetworkResource, XArchNetwork);
//! No support for requested network resource/service
XARCH_SUBCLASS(XArchNetworkSupport, XArchNetwork);
//! Network I/O error
XARCH_SUBCLASS(XArchNetworkIO, XArchNetwork);
//! Network address is unavailable or not local
XARCH_SUBCLASS(XArchNetworkNoAddress, XArchNetwork);
//! Network address in use
XARCH_SUBCLASS(XArchNetworkAddressInUse, XArchNetwork);
//! No route to address
XARCH_SUBCLASS(XArchNetworkNoRoute, XArchNetwork);
//! Socket not connected
XARCH_SUBCLASS(XArchNetworkNotConnected, XArchNetwork);
//! Remote read end of socket has closed
XARCH_SUBCLASS(XArchNetworkShutdown, XArchNetwork);
//! Remote end of socket has disconnected
XARCH_SUBCLASS(XArchNetworkDisconnected, XArchNetwork);
//! Remote end of socket refused connection
XARCH_SUBCLASS(XArchNetworkConnectionRefused, XArchNetwork);
//! Remote end of socket is not responding
XARCH_SUBCLASS(XArchNetworkTimedOut, XArchNetwork);
//! Generic network name lookup erros
XARCH_SUBCLASS(XArchNetworkName, XArchNetwork);
//! The named host is unknown
XARCH_SUBCLASS(XArchNetworkNameUnknown, XArchNetworkName);
//! The named host is known but has no address
XARCH_SUBCLASS(XArchNetworkNameNoAddress, XArchNetworkName);
//! Non-recoverable name server error
XARCH_SUBCLASS(XArchNetworkNameFailure, XArchNetworkName);
//! Temporary name server error
XARCH_SUBCLASS(XArchNetworkNameUnavailable, XArchNetworkName);
//! The named host is known but no supported address
XARCH_SUBCLASS(XArchNetworkNameUnsupported, XArchNetworkName);
//! Generic daemon exception
/*!
Exceptions derived from this class are used by the daemon
library to indicate various errors.
*/
XARCH_SUBCLASS(XArchDaemon, XArch);
//! Could not daemonize
XARCH_SUBCLASS(XArchDaemonFailed, XArchDaemon);
//! Could not install daemon
XARCH_SUBCLASS(XArchDaemonInstallFailed, XArchDaemon);
//! Could not uninstall daemon
XARCH_SUBCLASS(XArchDaemonUninstallFailed, XArchDaemon);
//! Attempted to uninstall a daemon that was not installed
XARCH_SUBCLASS(XArchDaemonUninstallNotInstalled, XArchDaemonUninstallFailed);
#endif

View File

@@ -0,0 +1,36 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "XArchUnix.h"
#include <cstring>
//
// XArchEvalUnix
//
XArchEval*
XArchEvalUnix::clone() const throw()
{
return new XArchEvalUnix(m_errno);
}
std::string
XArchEvalUnix::eval() const throw()
{
// FIXME -- not thread safe
return strerror(m_errno);
}

37
src/lib/arch/XArchUnix.h Normal file
View File

@@ -0,0 +1,37 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef XARCHUNIX_H
#define XARCHUNIX_H
#include "XArch.h"
//! Lazy error message string evaluation for unix
class XArchEvalUnix : public XArchEval {
public:
XArchEvalUnix(int err) : m_errno(err) { }
virtual ~XArchEvalUnix() { }
// XArchEval overrides
virtual XArchEval* clone() const throw();
virtual std::string eval() const throw();
private:
int m_errno;
};
#endif

View File

@@ -0,0 +1,130 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "XArchWindows.h"
#include "CArchNetworkWinsock.h"
//
// XArchEvalWindows
//
XArchEval*
XArchEvalWindows::clone() const throw()
{
return new XArchEvalWindows(m_errno);
}
std::string
XArchEvalWindows::eval() const throw()
{
char* cmsg;
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_SYSTEM,
0,
m_errno,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&cmsg,
0,
NULL) == 0) {
cmsg = NULL;
return "Unknown error";
}
std::string smsg(cmsg);
LocalFree(cmsg);
return smsg;
}
//
// XArchEvalWinsock
//
XArchEval*
XArchEvalWinsock::clone() const throw()
{
return new XArchEvalWinsock(m_errno);
}
std::string
XArchEvalWinsock::eval() const throw()
{
// built-in windows function for looking up error message strings
// may not look up network error messages correctly. we'll have
// to do it ourself.
static const struct { int m_code; const char* m_msg; } s_netErrorCodes[] = {
/* 10004 */{WSAEINTR, "The (blocking) call was canceled via WSACancelBlockingCall"},
/* 10009 */{WSAEBADF, "Bad file handle"},
/* 10013 */{WSAEACCES, "The requested address is a broadcast address, but the appropriate flag was not set"},
/* 10014 */{WSAEFAULT, "WSAEFAULT"},
/* 10022 */{WSAEINVAL, "WSAEINVAL"},
/* 10024 */{WSAEMFILE, "No more file descriptors available"},
/* 10035 */{WSAEWOULDBLOCK, "Socket is marked as non-blocking and no connections are present or the receive operation would block"},
/* 10036 */{WSAEINPROGRESS, "A blocking Windows Sockets operation is in progress"},
/* 10037 */{WSAEALREADY, "The asynchronous routine being canceled has already completed"},
/* 10038 */{WSAENOTSOCK, "At least on descriptor is not a socket"},
/* 10039 */{WSAEDESTADDRREQ, "A destination address is required"},
/* 10040 */{WSAEMSGSIZE, "The datagram was too large to fit into the specified buffer and was truncated"},
/* 10041 */{WSAEPROTOTYPE, "The specified protocol is the wrong type for this socket"},
/* 10042 */{WSAENOPROTOOPT, "The option is unknown or unsupported"},
/* 10043 */{WSAEPROTONOSUPPORT,"The specified protocol is not supported"},
/* 10044 */{WSAESOCKTNOSUPPORT,"The specified socket type is not supported by this address family"},
/* 10045 */{WSAEOPNOTSUPP, "The referenced socket is not a type that supports that operation"},
/* 10046 */{WSAEPFNOSUPPORT, "BSD: Protocol family not supported"},
/* 10047 */{WSAEAFNOSUPPORT, "The specified address family is not supported"},
/* 10048 */{WSAEADDRINUSE, "The specified address is already in use"},
/* 10049 */{WSAEADDRNOTAVAIL, "The specified address is not available from the local machine"},
/* 10050 */{WSAENETDOWN, "The Windows Sockets implementation has detected that the network subsystem has failed"},
/* 10051 */{WSAENETUNREACH, "The network can't be reached from this host at this time"},
/* 10052 */{WSAENETRESET, "The connection must be reset because the Windows Sockets implementation dropped it"},
/* 10053 */{WSAECONNABORTED, "The virtual circuit was aborted due to timeout or other failure"},
/* 10054 */{WSAECONNRESET, "The virtual circuit was reset by the remote side"},
/* 10055 */{WSAENOBUFS, "No buffer space is available or a buffer deadlock has occured. The socket cannot be created"},
/* 10056 */{WSAEISCONN, "The socket is already connected"},
/* 10057 */{WSAENOTCONN, "The socket is not connected"},
/* 10058 */{WSAESHUTDOWN, "The socket has been shutdown"},
/* 10059 */{WSAETOOMANYREFS, "BSD: Too many references"},
/* 10060 */{WSAETIMEDOUT, "Attempt to connect timed out without establishing a connection"},
/* 10061 */{WSAECONNREFUSED, "Connection was refused"},
/* 10062 */{WSAELOOP, "Undocumented WinSock error code used in BSD"},
/* 10063 */{WSAENAMETOOLONG, "Undocumented WinSock error code used in BSD"},
/* 10064 */{WSAEHOSTDOWN, "Undocumented WinSock error code used in BSD"},
/* 10065 */{WSAEHOSTUNREACH, "No route to host"},
/* 10066 */{WSAENOTEMPTY, "Undocumented WinSock error code"},
/* 10067 */{WSAEPROCLIM, "Undocumented WinSock error code"},
/* 10068 */{WSAEUSERS, "Undocumented WinSock error code"},
/* 10069 */{WSAEDQUOT, "Undocumented WinSock error code"},
/* 10070 */{WSAESTALE, "Undocumented WinSock error code"},
/* 10071 */{WSAEREMOTE, "Undocumented WinSock error code"},
/* 10091 */{WSASYSNOTREADY, "Underlying network subsytem is not ready for network communication"},
/* 10092 */{WSAVERNOTSUPPORTED, "The version of WinSock API support requested is not provided in this implementation"},
/* 10093 */{WSANOTINITIALISED, "WinSock subsystem not properly initialized"},
/* 10101 */{WSAEDISCON, "Virtual circuit has gracefully terminated connection"},
/* 11001 */{WSAHOST_NOT_FOUND, "The specified host is unknown"},
/* 11002 */{WSATRY_AGAIN, "A temporary error occurred on an authoritative name server"},
/* 11003 */{WSANO_RECOVERY, "A non-recoverable name server error occurred"},
/* 11004 */{WSANO_DATA, "The requested name is valid but does not have an IP address"},
/* end */{0, NULL}
};
for (unsigned int i = 0; s_netErrorCodes[i].m_code != 0; ++i) {
if (s_netErrorCodes[i].m_code == m_errno) {
return s_netErrorCodes[i].m_msg;
}
}
return "Unknown error";
}

View File

@@ -0,0 +1,55 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef XARCHWINDOWS_H
#define XARCHWINDOWS_H
#define WIN32_LEAN_AND_MEAN
#include "XArch.h"
#include <windows.h>
//! Lazy error message string evaluation for windows
class XArchEvalWindows : public XArchEval {
public:
XArchEvalWindows() : m_errno(GetLastError()) { }
XArchEvalWindows(DWORD err) : m_errno(err) { }
virtual ~XArchEvalWindows() { }
// XArchEval overrides
virtual XArchEval* clone() const throw();
virtual std::string eval() const throw();
private:
DWORD m_errno;
};
//! Lazy error message string evaluation for winsock
class XArchEvalWinsock : public XArchEval {
public:
XArchEvalWinsock(int err) : m_errno(err) { }
virtual ~XArchEvalWinsock() { }
// XArchEval overrides
virtual XArchEval* clone() const throw();
virtual std::string eval() const throw();
private:
int m_errno;
};
#endif

66
src/lib/arch/vsnprintf.h Normal file
View File

@@ -0,0 +1,66 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "IArchString.h"
#if HAVE_VSNPRINTF
#if !defined(ARCH_VSNPRINTF)
# define ARCH_VSNPRINTF vsnprintf
#endif
int
IArchString::vsnprintf(char* str, int size, const char* fmt, va_list ap)
{
int n = ::ARCH_VSNPRINTF(str, size, fmt, ap);
if (n > size) {
n = -1;
}
return n;
}
#elif SYSAPI_UNIX // !HAVE_VSNPRINTF
#include <stdio.h>
int
IArchString::vsnprintf(char* str, int size, const char* fmt, va_list ap)
{
static FILE* bitbucket = fopen("/dev/null", "w");
if (bitbucket == NULL) {
// uh oh
if (size > 0) {
str[0] = '\0';
}
return 0;
}
else {
// count the characters using the bitbucket
int n = vfprintf(bitbucket, fmt, ap);
if (n + 1 <= size) {
// it'll fit so print it into str
vsprintf(str, fmt, ap);
}
return n;
}
}
#else // !HAVE_VSNPRINTF && !SYSAPI_UNIX
#error vsnprintf not implemented
#endif // !HAVE_VSNPRINTF

83
src/lib/base/CEvent.cpp Normal file
View File

@@ -0,0 +1,83 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CEvent.h"
#include "CEventQueue.h"
//
// CEvent
//
CEvent::CEvent() :
m_type(kUnknown),
m_target(NULL),
m_data(NULL),
m_flags(0)
{
// do nothing
}
CEvent::CEvent(Type type, void* target, void* data, Flags flags) :
m_type(type),
m_target(target),
m_data(data),
m_flags(flags)
{
// do nothing
}
CEvent::Type
CEvent::getType() const
{
return m_type;
}
void*
CEvent::getTarget() const
{
return m_target;
}
void*
CEvent::getData() const
{
return m_data;
}
CEvent::Flags
CEvent::getFlags() const
{
return m_flags;
}
void
CEvent::deleteData(const CEvent& event)
{
switch (event.getType()) {
case kUnknown:
case kQuit:
case kSystem:
case kTimer:
break;
default:
if ((event.getFlags() & kDontFreeData) == 0) {
free(event.getData());
}
break;
}
}

105
src/lib/base/CEvent.h Normal file
View File

@@ -0,0 +1,105 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CEVENT_H
#define CEVENT_H
#include "BasicTypes.h"
#include "stdmap.h"
//! Event
/*!
A \c CEvent holds an event type and a pointer to event data.
*/
class CEvent {
public:
typedef UInt32 Type;
enum {
kUnknown, //!< The event type is unknown
kQuit, //!< The quit event
kSystem, //!< The data points to a system event type
kTimer, //!< The data points to timer info
kLast //!< Must be last
};
typedef UInt32 Flags;
enum {
kNone = 0x00, //!< No flags
kDeliverImmediately = 0x01, //!< Dispatch and free event immediately
kDontFreeData = 0x02 //!< Don't free data in deleteData
};
CEvent();
//! Create \c CEvent with data
/*!
The \p type must have been registered using \c registerType().
The \p data must be POD (plain old data) allocated by malloc(),
which means it cannot have a constructor, destructor or be
composed of any types that do. \p target is the intended
recipient of the event. \p flags is any combination of \c Flags.
*/
CEvent(Type type, void* target = NULL, void* data = NULL,
Flags flags = kNone);
//! @name manipulators
//@{
//! Release event data
/*!
Deletes event data for the given event (using free()).
*/
static void deleteData(const CEvent&);
//@}
//! @name accessors
//@{
//! Get event type
/*!
Returns the event type.
*/
Type getType() const;
//! Get the event target
/*!
Returns the event target.
*/
void* getTarget() const;
//! Get the event data
/*!
Returns the event data.
*/
void* getData() const;
//! Get event flags
/*!
Returns the event flags.
*/
Flags getFlags() const;
//@}
private:
Type m_type;
void* m_target;
void* m_data;
Flags m_flags;
};
#endif

View File

@@ -0,0 +1,541 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CEventQueue.h"
#include "CLog.h"
#include "CSimpleEventQueueBuffer.h"
#include "CStopwatch.h"
#include "IEventJob.h"
#include "CArch.h"
// interrupt handler. this just adds a quit event to the queue.
static
void
interrupt(CArch::ESignal, void*)
{
EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
}
//
// CEventQueue
//
CEventQueue::CEventQueue() :
m_nextType(CEvent::kLast)
{
setInstance(this);
m_mutex = ARCH->newMutex();
ARCH->setSignalHandler(CArch::kINTERRUPT, &interrupt, NULL);
ARCH->setSignalHandler(CArch::kTERMINATE, &interrupt, NULL);
m_buffer = new CSimpleEventQueueBuffer;
}
CEventQueue::~CEventQueue()
{
delete m_buffer;
ARCH->setSignalHandler(CArch::kINTERRUPT, NULL, NULL);
ARCH->setSignalHandler(CArch::kTERMINATE, NULL, NULL);
ARCH->closeMutex(m_mutex);
setInstance(NULL);
}
CEvent::Type
CEventQueue::registerType(const char* name)
{
CArchMutexLock lock(m_mutex);
m_typeMap.insert(std::make_pair(m_nextType, name));
m_nameMap.insert(std::make_pair(name, m_nextType));
LOG((CLOG_DEBUG1 "registered event type %s as %d", name, m_nextType));
return m_nextType++;
}
CEvent::Type
CEventQueue::registerTypeOnce(CEvent::Type& type, const char* name)
{
CArchMutexLock lock(m_mutex);
if (type == CEvent::kUnknown) {
m_typeMap.insert(std::make_pair(m_nextType, name));
m_nameMap.insert(std::make_pair(name, m_nextType));
LOG((CLOG_DEBUG1 "registered event type %s as %d", name, m_nextType));
type = m_nextType++;
}
return type;
}
const char*
CEventQueue::getTypeName(CEvent::Type type)
{
switch (type) {
case CEvent::kUnknown:
return "nil";
case CEvent::kQuit:
return "quit";
case CEvent::kSystem:
return "system";
case CEvent::kTimer:
return "timer";
default:
CTypeMap::const_iterator i = m_typeMap.find(type);
if (i == m_typeMap.end()) {
return "<unknown>";
}
else {
return i->second;
}
}
}
void
CEventQueue::adoptBuffer(IEventQueueBuffer* buffer)
{
CArchMutexLock lock(m_mutex);
// discard old buffer and old events
delete m_buffer;
for (CEventTable::iterator i = m_events.begin(); i != m_events.end(); ++i) {
CEvent::deleteData(i->second);
}
m_events.clear();
m_oldEventIDs.clear();
// use new buffer
m_buffer = buffer;
if (m_buffer == NULL) {
m_buffer = new CSimpleEventQueueBuffer;
}
}
bool
CEventQueue::getEvent(CEvent& event, double timeout)
{
CStopwatch timer(true);
retry:
// if no events are waiting then handle timers and then wait
while (m_buffer->isEmpty()) {
// handle timers first
if (hasTimerExpired(event)) {
return true;
}
// get time remaining in timeout
double timeLeft = timeout - timer.getTime();
if (timeout >= 0.0 && timeLeft <= 0.0) {
return false;
}
// get time until next timer expires. if there is a timer
// and it'll expire before the client's timeout then use
// that duration for our timeout instead.
double timerTimeout = getNextTimerTimeout();
if (timeout < 0.0 || (timerTimeout >= 0.0 && timerTimeout < timeLeft)) {
timeLeft = timerTimeout;
}
// wait for an event
m_buffer->waitForEvent(timeLeft);
}
// get the event
UInt32 dataID;
IEventQueueBuffer::Type type = m_buffer->getEvent(event, dataID);
switch (type) {
case IEventQueueBuffer::kNone:
if (timeout < 0.0 || timeout <= timer.getTime()) {
// don't want to fail if client isn't expecting that
// so if getEvent() fails with an infinite timeout
// then just try getting another event.
goto retry;
}
return false;
case IEventQueueBuffer::kSystem:
return true;
case IEventQueueBuffer::kUser:
{
CArchMutexLock lock(m_mutex);
event = removeEvent(dataID);
return true;
}
default:
assert(0 && "invalid event type");
return false;
}
}
bool
CEventQueue::dispatchEvent(const CEvent& event)
{
void* target = event.getTarget();
IEventJob* job = getHandler(event.getType(), target);
if (job == NULL) {
job = getHandler(CEvent::kUnknown, target);
}
if (job != NULL) {
job->run(event);
return true;
}
return false;
}
void
CEventQueue::addEvent(const CEvent& event)
{
// discard bogus event types
switch (event.getType()) {
case CEvent::kUnknown:
case CEvent::kSystem:
case CEvent::kTimer:
return;
default:
break;
}
if ((event.getFlags() & CEvent::kDeliverImmediately) != 0) {
dispatchEvent(event);
CEvent::deleteData(event);
}
else {
CArchMutexLock lock(m_mutex);
// store the event's data locally
UInt32 eventID = saveEvent(event);
// add it
if (!m_buffer->addEvent(eventID)) {
// failed to send event
removeEvent(eventID);
CEvent::deleteData(event);
}
}
}
CEventQueueTimer*
CEventQueue::newTimer(double duration, void* target)
{
assert(duration > 0.0);
CEventQueueTimer* timer = m_buffer->newTimer(duration, false);
if (target == NULL) {
target = timer;
}
CArchMutexLock lock(m_mutex);
m_timers.insert(timer);
// initial duration is requested duration plus whatever's on
// the clock currently because the latter will be subtracted
// the next time we check for timers.
m_timerQueue.push(CTimer(timer, duration,
duration + m_time.getTime(), target, false));
return timer;
}
CEventQueueTimer*
CEventQueue::newOneShotTimer(double duration, void* target)
{
assert(duration > 0.0);
CEventQueueTimer* timer = m_buffer->newTimer(duration, true);
if (target == NULL) {
target = timer;
}
CArchMutexLock lock(m_mutex);
m_timers.insert(timer);
// initial duration is requested duration plus whatever's on
// the clock currently because the latter will be subtracted
// the next time we check for timers.
m_timerQueue.push(CTimer(timer, duration,
duration + m_time.getTime(), target, true));
return timer;
}
void
CEventQueue::deleteTimer(CEventQueueTimer* timer)
{
CArchMutexLock lock(m_mutex);
for (CTimerQueue::iterator index = m_timerQueue.begin();
index != m_timerQueue.end(); ++index) {
if (index->getTimer() == timer) {
m_timerQueue.erase(index);
break;
}
}
CTimers::iterator index = m_timers.find(timer);
if (index != m_timers.end()) {
m_timers.erase(index);
}
m_buffer->deleteTimer(timer);
}
void
CEventQueue::adoptHandler(CEvent::Type type, void* target, IEventJob* handler)
{
CArchMutexLock lock(m_mutex);
IEventJob*& job = m_handlers[target][type];
delete job;
job = handler;
}
void
CEventQueue::removeHandler(CEvent::Type type, void* target)
{
IEventJob* handler = NULL;
{
CArchMutexLock lock(m_mutex);
CHandlerTable::iterator index = m_handlers.find(target);
if (index != m_handlers.end()) {
CTypeHandlerTable& typeHandlers = index->second;
CTypeHandlerTable::iterator index2 = typeHandlers.find(type);
if (index2 != typeHandlers.end()) {
handler = index2->second;
typeHandlers.erase(index2);
}
}
}
delete handler;
}
void
CEventQueue::removeHandlers(void* target)
{
std::vector<IEventJob*> handlers;
{
CArchMutexLock lock(m_mutex);
CHandlerTable::iterator index = m_handlers.find(target);
if (index != m_handlers.end()) {
// copy to handlers array and clear table for target
CTypeHandlerTable& typeHandlers = index->second;
for (CTypeHandlerTable::iterator index2 = typeHandlers.begin();
index2 != typeHandlers.end(); ++index2) {
handlers.push_back(index2->second);
}
typeHandlers.clear();
}
}
// delete handlers
for (std::vector<IEventJob*>::iterator index = handlers.begin();
index != handlers.end(); ++index) {
delete *index;
}
}
bool
CEventQueue::isEmpty() const
{
return (m_buffer->isEmpty() && getNextTimerTimeout() != 0.0);
}
IEventJob*
CEventQueue::getHandler(CEvent::Type type, void* target) const
{
CArchMutexLock lock(m_mutex);
CHandlerTable::const_iterator index = m_handlers.find(target);
if (index != m_handlers.end()) {
const CTypeHandlerTable& typeHandlers = index->second;
CTypeHandlerTable::const_iterator index2 = typeHandlers.find(type);
if (index2 != typeHandlers.end()) {
return index2->second;
}
}
return NULL;
}
UInt32
CEventQueue::saveEvent(const CEvent& event)
{
// choose id
UInt32 id;
if (!m_oldEventIDs.empty()) {
// reuse an id
id = m_oldEventIDs.back();
m_oldEventIDs.pop_back();
}
else {
// make a new id
id = static_cast<UInt32>(m_events.size());
}
// save data
m_events[id] = event;
return id;
}
CEvent
CEventQueue::removeEvent(UInt32 eventID)
{
// look up id
CEventTable::iterator index = m_events.find(eventID);
if (index == m_events.end()) {
return CEvent();
}
// get data
CEvent event = index->second;
m_events.erase(index);
// save old id for reuse
m_oldEventIDs.push_back(eventID);
return event;
}
bool
CEventQueue::hasTimerExpired(CEvent& event)
{
// return true if there's a timer in the timer priority queue that
// has expired. if returning true then fill in event appropriately
// and reset and reinsert the timer.
if (m_timerQueue.empty()) {
return false;
}
// get time elapsed since last check
const double time = m_time.getTime();
m_time.reset();
// countdown elapsed time
for (CTimerQueue::iterator index = m_timerQueue.begin();
index != m_timerQueue.end(); ++index) {
(*index) -= time;
}
// done if no timers are expired
if (m_timerQueue.top() > 0.0) {
return false;
}
// remove timer from queue
CTimer timer = m_timerQueue.top();
m_timerQueue.pop();
// prepare event and reset the timer's clock
timer.fillEvent(m_timerEvent);
event = CEvent(CEvent::kTimer, timer.getTarget(), &m_timerEvent);
timer.reset();
// reinsert timer into queue if it's not a one-shot
if (!timer.isOneShot()) {
m_timerQueue.push(timer);
}
return true;
}
double
CEventQueue::getNextTimerTimeout() const
{
// return -1 if no timers, 0 if the top timer has expired, otherwise
// the time until the top timer in the timer priority queue will
// expire.
if (m_timerQueue.empty()) {
return -1.0;
}
if (m_timerQueue.top() <= 0.0) {
return 0.0;
}
return m_timerQueue.top();
}
CEvent::Type
CEventQueue::getRegisteredType(const CString& name) const
{
CNameMap::const_iterator found = m_nameMap.find(name);
if (found != m_nameMap.end())
return found->second;
return CEvent::kUnknown;
}
//
// CEventQueue::CTimer
//
CEventQueue::CTimer::CTimer(CEventQueueTimer* timer, double timeout,
double initialTime, void* target, bool oneShot) :
m_timer(timer),
m_timeout(timeout),
m_target(target),
m_oneShot(oneShot),
m_time(initialTime)
{
assert(m_timeout > 0.0);
}
CEventQueue::CTimer::~CTimer()
{
// do nothing
}
void
CEventQueue::CTimer::reset()
{
m_time = m_timeout;
}
CEventQueue::CTimer&
CEventQueue::CTimer::operator-=(double dt)
{
m_time -= dt;
return *this;
}
CEventQueue::CTimer::operator double() const
{
return m_time;
}
bool
CEventQueue::CTimer::isOneShot() const
{
return m_oneShot;
}
CEventQueueTimer*
CEventQueue::CTimer::getTimer() const
{
return m_timer;
}
void*
CEventQueue::CTimer::getTarget() const
{
return m_target;
}
void
CEventQueue::CTimer::fillEvent(CTimerEvent& event) const
{
event.m_timer = m_timer;
event.m_count = 0;
if (m_time <= 0.0) {
event.m_count = static_cast<UInt32>((m_timeout - m_time) / m_timeout);
}
}
bool
CEventQueue::CTimer::operator<(const CTimer& t) const
{
return m_time < t.m_time;
}

130
src/lib/base/CEventQueue.h Normal file
View File

@@ -0,0 +1,130 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CEVENTQUEUE_H
#define CEVENTQUEUE_H
#include "IEventQueue.h"
#include "CEvent.h"
#include "CPriorityQueue.h"
#include "CStopwatch.h"
#include "IArchMultithread.h"
#include "stdmap.h"
#include "stdset.h"
//! Event queue
/*!
An event queue that implements the platform independent parts and
delegates the platform dependent parts to a subclass.
*/
class CEventQueue : public IEventQueue {
public:
CEventQueue();
virtual ~CEventQueue();
// IEventQueue overrides
virtual void adoptBuffer(IEventQueueBuffer*);
virtual bool getEvent(CEvent& event, double timeout = -1.0);
virtual bool dispatchEvent(const CEvent& event);
virtual void addEvent(const CEvent& event);
virtual CEventQueueTimer*
newTimer(double duration, void* target);
virtual CEventQueueTimer*
newOneShotTimer(double duration, void* target);
virtual void deleteTimer(CEventQueueTimer*);
virtual void adoptHandler(CEvent::Type type,
void* target, IEventJob* handler);
virtual void removeHandler(CEvent::Type type, void* target);
virtual void removeHandlers(void* target);
virtual CEvent::Type
registerType(const char* name);
virtual CEvent::Type
registerTypeOnce(CEvent::Type& type, const char* name);
virtual bool isEmpty() const;
virtual IEventJob* getHandler(CEvent::Type type, void* target) const;
virtual const char* getTypeName(CEvent::Type type);
virtual CEvent::Type
getRegisteredType(const CString& name) const;
private:
UInt32 saveEvent(const CEvent& event);
CEvent removeEvent(UInt32 eventID);
bool hasTimerExpired(CEvent& event);
double getNextTimerTimeout() const;
private:
class CTimer {
public:
CTimer(CEventQueueTimer*, double timeout, double initialTime,
void* target, bool oneShot);
~CTimer();
void reset();
CTimer& operator-=(double);
operator double() const;
bool isOneShot() const;
CEventQueueTimer*
getTimer() const;
void* getTarget() const;
void fillEvent(CTimerEvent&) const;
bool operator<(const CTimer&) const;
private:
CEventQueueTimer* m_timer;
double m_timeout;
void* m_target;
bool m_oneShot;
double m_time;
};
typedef std::set<CEventQueueTimer*> CTimers;
typedef CPriorityQueue<CTimer> CTimerQueue;
typedef std::map<UInt32, CEvent> CEventTable;
typedef std::vector<UInt32> CEventIDList;
typedef std::map<CEvent::Type, const char*> CTypeMap;
typedef std::map<CString, CEvent::Type> CNameMap;
typedef std::map<CEvent::Type, IEventJob*> CTypeHandlerTable;
typedef std::map<void*, CTypeHandlerTable> CHandlerTable;
CArchMutex m_mutex;
// registered events
CEvent::Type m_nextType;
CTypeMap m_typeMap;
CNameMap m_nameMap;
// buffer of events
IEventQueueBuffer* m_buffer;
// saved events
CEventTable m_events;
CEventIDList m_oldEventIDs;
// timers
CStopwatch m_time;
CTimers m_timers;
CTimerQueue m_timerQueue;
CTimerEvent m_timerEvent;
// event handlers
CHandlerTable m_handlers;
};
#endif

View File

@@ -0,0 +1,43 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CFunctionEventJob.h"
//
// CFunctionEventJob
//
CFunctionEventJob::CFunctionEventJob(
void (*func)(const CEvent&, void*), void* arg) :
m_func(func),
m_arg(arg)
{
// do nothing
}
CFunctionEventJob::~CFunctionEventJob()
{
// do nothing
}
void
CFunctionEventJob::run(const CEvent& event)
{
if (m_func != NULL) {
m_func(event, m_arg);
}
}

View File

@@ -0,0 +1,41 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CFUNCTIONEVENTJOB_H
#define CFUNCTIONEVENTJOB_H
#include "IEventJob.h"
//! Use a function as an event job
/*!
An event job class that invokes a function.
*/
class CFunctionEventJob : public IEventJob {
public:
//! run() invokes \c func(arg)
CFunctionEventJob(void (*func)(const CEvent&, void*), void* arg = NULL);
virtual ~CFunctionEventJob();
// IEventJob overrides
virtual void run(const CEvent&);
private:
void (*m_func)(const CEvent&, void*);
void* m_arg;
};
#endif

View File

@@ -0,0 +1,42 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CFunctionJob.h"
//
// CFunctionJob
//
CFunctionJob::CFunctionJob(void (*func)(void*), void* arg) :
m_func(func),
m_arg(arg)
{
// do nothing
}
CFunctionJob::~CFunctionJob()
{
// do nothing
}
void
CFunctionJob::run()
{
if (m_func != NULL) {
m_func(m_arg);
}
}

View File

@@ -0,0 +1,41 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CFUNCTIONJOB_H
#define CFUNCTIONJOB_H
#include "IJob.h"
//! Use a function as a job
/*!
A job class that invokes a function.
*/
class CFunctionJob : public IJob {
public:
//! run() invokes \c func(arg)
CFunctionJob(void (*func)(void*), void* arg = NULL);
virtual ~CFunctionJob();
// IJob overrides
virtual void run();
private:
void (*m_func)(void*);
void* m_arg;
};
#endif

297
src/lib/base/CLog.cpp Normal file
View File

@@ -0,0 +1,297 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CLog.h"
#include "CString.h"
#include "CStringUtil.h"
#include "LogOutputters.h"
#include "CArch.h"
#include "Version.h"
#include "XArch.h"
#include <cstdio>
#include <cstring>
#include <iostream>
#include <ctime>
// names of priorities
static const char* g_priority[] = {
"FATAL",
"ERROR",
"WARNING",
"NOTE",
"INFO",
"DEBUG",
"DEBUG1",
"DEBUG2",
"DEBUG3",
"DEBUG4",
"DEBUG5"
};
// number of priorities
static const int g_numPriority = (int)(sizeof(g_priority) / sizeof(g_priority[0]));
// the default priority
#if defined(_DEBUG) || defined(DEBUG)
static const int g_defaultMaxPriority = kDEBUG;
#else
static const int g_defaultMaxPriority = kINFO;
#endif
// length of longest string in g_priority
static const int g_maxPriorityLength = 7;
// length of suffix string (": ")
static const int g_prioritySuffixLength = 2;
// amount of padded required to fill in the priority prefix
static const int g_priorityPad = g_maxPriorityLength +
g_prioritySuffixLength;
//
// CLog
//
CLog* CLog::s_log = NULL;
CLog::CLog()
{
// create mutex for multithread safe operation
m_mutex = ARCH->newMutex();
// other initalization
m_maxPriority = g_defaultMaxPriority;
m_maxNewlineLength = 0;
insert(new CConsoleLogOutputter);
}
CLog::~CLog()
{
// clean up
for (COutputterList::iterator index = m_outputters.begin();
index != m_outputters.end(); ++index) {
delete *index;
}
for (COutputterList::iterator index = m_alwaysOutputters.begin();
index != m_alwaysOutputters.end(); ++index) {
delete *index;
}
ARCH->closeMutex(m_mutex);
}
CLog*
CLog::getInstance()
{
if (s_log == NULL)
s_log = new CLog();
return s_log;
}
const char*
CLog::getFilterName() const
{
return getFilterName(getFilter());
}
const char*
CLog::getFilterName(int level) const
{
return g_priority[level];
}
void
CLog::print(const char* file, int line, const char* fmt, ...)
{
// check if fmt begins with a priority argument
ELevel priority = kINFO;
if ((strlen(fmt) > 2) && (fmt[0] == '%' && fmt[1] == 'z')) {
// 060 in octal is 0 (48 in decimal), so subtracting this converts ascii
// number it a true number. we could use atoi instead, but this is how
// it was done originally.
priority = (ELevel)(fmt[2] - '\060');
// move the pointer on past the debug priority char
fmt += 3;
}
// done if below priority threshold
if (priority > getFilter()) {
return;
}
// compute prefix padding length
char stack[1024];
// compute suffix padding length
int sPad = m_maxNewlineLength;
// print to buffer, leaving space for a newline at the end and prefix
// at the beginning.
char* buffer = stack;
int len = (int)(sizeof(stack) / sizeof(stack[0]));
while (true) {
// try printing into the buffer
va_list args;
va_start(args, fmt);
int n = ARCH->vsnprintf(buffer, len - sPad, fmt, args);
va_end(args);
// if the buffer wasn't big enough then make it bigger and try again
if (n < 0 || n > (int)len) {
if (buffer != stack) {
delete[] buffer;
}
len *= 2;
buffer = new char[len];
}
// if the buffer was big enough then continue
else {
break;
}
}
// print the prefix to the buffer. leave space for priority label.
// do not prefix time and file for kPRINT (CLOG_PRINT)
if (priority != kPRINT) {
char message[2048];
#if defined(_DEBUG) || defined(DEBUG)
struct tm *tm;
char tmp[220];
time_t t;
time(&t);
tm = localtime(&t);
sprintf(tmp, "%04i-%02i-%02iT%02i:%02i:%02i", tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
sprintf(message, "%s %s: %s\n\t%s,%d", tmp, g_priority[priority], buffer, file, line);
#else
sprintf(message, "%s: %s", g_priority[priority], buffer);
#endif
output(priority, message);
} else {
output(priority, buffer);
}
// clean up
if (buffer != stack) {
delete[] buffer;
}
}
void
CLog::insert(ILogOutputter* outputter, bool alwaysAtHead)
{
assert(outputter != NULL);
CArchMutexLock lock(m_mutex);
if (alwaysAtHead) {
m_alwaysOutputters.push_front(outputter);
}
else {
m_outputters.push_front(outputter);
}
outputter->open(kAppVersion);
// Issue 41
// don't show log unless user requests it, as some users find this
// feature irritating (i.e. when they lose network connectivity).
// in windows the log window can be displayed by selecting "show log"
// from the synergy system tray icon.
// if this causes problems for other architectures, then a different
// work around should be attempted.
//outputter->show(false);
}
void
CLog::remove(ILogOutputter* outputter)
{
CArchMutexLock lock(m_mutex);
m_outputters.remove(outputter);
m_alwaysOutputters.remove(outputter);
}
void
CLog::pop_front(bool alwaysAtHead)
{
CArchMutexLock lock(m_mutex);
COutputterList* list = alwaysAtHead ? &m_alwaysOutputters : &m_outputters;
if (!list->empty()) {
delete list->front();
list->pop_front();
}
}
bool
CLog::setFilter(const char* maxPriority)
{
if (maxPriority != NULL) {
for (int i = 0; i < g_numPriority; ++i) {
if (strcmp(maxPriority, g_priority[i]) == 0) {
setFilter(i);
return true;
}
}
return false;
}
return true;
}
void
CLog::setFilter(int maxPriority)
{
CArchMutexLock lock(m_mutex);
m_maxPriority = maxPriority;
}
int
CLog::getFilter() const
{
CArchMutexLock lock(m_mutex);
return m_maxPriority;
}
void
CLog::output(ELevel priority, char* msg)
{
assert(priority >= -1 && priority < g_numPriority);
assert(msg != NULL);
if (!msg) return;
CArchMutexLock lock(m_mutex);
COutputterList::const_iterator i;
for (i = m_alwaysOutputters.begin(); i != m_alwaysOutputters.end(); ++i) {
// write to outputter
(*i)->write(priority, msg);
}
for (i = m_outputters.begin(); i != m_outputters.end(); ++i) {
// write to outputter and break out of loop if it returns false
if (!(*i)->write(priority, msg)) {
break;
}
}
}

207
src/lib/base/CLog.h Normal file
View File

@@ -0,0 +1,207 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CLOG_H
#define CLOG_H
#include "common.h"
#include "IArchMultithread.h"
#include "stdlist.h"
#include <stdarg.h>
#include "CArch.h"
#define CLOG (CLog::getInstance())
class ILogOutputter;
class CThread;
//! Logging facility
/*!
The logging class; all console output should go through this class.
It supports multithread safe operation, several message priority levels,
filtering by priority, and output redirection. The macros LOG() and
LOGC() provide convenient access.
*/
class CLog {
public:
~CLog();
//! @name manipulators
//@{
//! Add an outputter to the head of the list
/*!
Inserts an outputter to the head of the outputter list. When the
logger writes a message, it goes to the outputter at the head of
the outputter list. If that outputter's \c write() method returns
true then it also goes to the next outputter, as so on until an
outputter returns false or there are no more outputters. Outputters
still in the outputter list when the log is destroyed will be
deleted. If \c alwaysAtHead is true then the outputter is always
called before all outputters with \c alwaysAtHead false and the
return value of the outputter is ignored.
By default, the logger has one outputter installed which writes to
the console.
*/
void insert(ILogOutputter* adopted,
bool alwaysAtHead = false);
//! Remove an outputter from the list
/*!
Removes the first occurrence of the given outputter from the
outputter list. It does nothing if the outputter is not in the
list. The outputter is not deleted.
*/
void remove(ILogOutputter* orphaned);
//! Remove the outputter from the head of the list
/*!
Removes and deletes the outputter at the head of the outputter list.
This does nothing if the outputter list is empty. Only removes
outputters that were inserted with the matching \c alwaysAtHead.
*/
void pop_front(bool alwaysAtHead = false);
//! Set the minimum priority filter.
/*!
Set the filter. Messages below this priority are discarded.
The default priority is 4 (INFO) (unless built without NDEBUG
in which case it's 5 (DEBUG)). setFilter(const char*) returns
true if the priority \c name was recognized; if \c name is NULL
then it simply returns true.
*/
bool setFilter(const char* name);
void setFilter(int);
//@}
//! @name accessors
//@{
//! Print a log message
/*!
Print a log message using the printf-like \c format and arguments
preceded by the filename and line number. If \c file is NULL then
neither the file nor the line are printed.
*/
void print(const char* file, int line,
const char* format, ...);
//! Get the minimum priority level.
int getFilter() const;
//! Get the filter name of the current filter level.
const char* getFilterName() const;
//! Get the filter name of a specified filter level.
const char* getFilterName(int level) const;
//! Get the singleton instance of the log
static CLog* getInstance();
//! Get the console filter level (messages above this are not sent to console).
int getConsoleMaxLevel() const { return kDEBUG1; }
//@}
private:
CLog();
void output(ELevel priority, char* msg);
private:
typedef std::list<ILogOutputter*> COutputterList;
static CLog* s_log;
CArchMutex m_mutex;
COutputterList m_outputters;
COutputterList m_alwaysOutputters;
int m_maxNewlineLength;
int m_maxPriority;
};
/*!
\def LOG(arg)
Write to the log. Because macros cannot accept variable arguments, this
should be invoked like so:
\code
LOG((CLOG_XXX "%d and %d are %s", x, y, x == y ? "equal" : "not equal"));
\endcode
In particular, notice the double open and close parentheses. Also note
that there is no comma after the \c CLOG_XXX. The \c XXX should be
replaced by one of enumerants in \c CLog::ELevel without the leading
\c k. For example, \c CLOG_INFO. The special \c CLOG_PRINT level will
not be filtered and is never prefixed by the filename and line number.
If \c NOLOGGING is defined during the build then this macro expands to
nothing. If \c NDEBUG is defined during the build then it expands to a
call to CLog::print. Otherwise it expands to a call to CLog::printt,
which includes the filename and line number.
*/
/*!
\def LOGC(expr, arg)
Write to the log if and only if expr is true. Because macros cannot accept
variable arguments, this should be invoked like so:
\code
LOGC(x == y, (CLOG_XXX "%d and %d are equal", x, y));
\endcode
In particular, notice the parentheses around everything after the boolean
expression. Also note that there is no comma after the \c CLOG_XXX.
The \c XXX should be replaced by one of enumerants in \c CLog::ELevel
without the leading \c k. For example, \c CLOG_INFO. The special
\c CLOG_PRINT level will not be filtered and is never prefixed by the
filename and line number.
If \c NOLOGGING is defined during the build then this macro expands to
nothing. If \c NDEBUG is not defined during the build then it expands
to a call to CLog::print that prints the filename and line number,
otherwise it expands to a call that doesn't.
*/
#if defined(NOLOGGING)
#define LOG(_a1)
#define LOGC(_a1, _a2)
#define CLOG_TRACE
#elif defined(NDEBUG)
#define LOG(_a1) CLOG->print _a1
#define LOGC(_a1, _a2) if (_a1) CLOG->print _a2
#define CLOG_TRACE NULL, 0,
#else
#define LOG(_a1) CLOG->print _a1
#define LOGC(_a1, _a2) if (_a1) CLOG->print _a2
#define CLOG_TRACE __FILE__, __LINE__,
#endif
// the CLOG_* defines are line and file plus %z and an octal number (060=0,
// 071=9), but the limitation is that once we run out of numbers at either
// end, then we resort to using non-numerical chars. this still works (since
// to deduce the number we subtract octal \060, so '/' is -1, and ':' is 10
#define CLOG_PRINT CLOG_TRACE "%z\057" // char is '/'
#define CLOG_CRIT CLOG_TRACE "%z\060" // char is '0'
#define CLOG_ERR CLOG_TRACE "%z\061"
#define CLOG_WARN CLOG_TRACE "%z\062"
#define CLOG_NOTE CLOG_TRACE "%z\063"
#define CLOG_INFO CLOG_TRACE "%z\064"
#define CLOG_DEBUG CLOG_TRACE "%z\065"
#define CLOG_DEBUG1 CLOG_TRACE "%z\066"
#define CLOG_DEBUG2 CLOG_TRACE "%z\067"
#define CLOG_DEBUG3 CLOG_TRACE "%z\070"
#define CLOG_DEBUG4 CLOG_TRACE "%z\071" // char is '9'
#define CLOG_DEBUG5 CLOG_TRACE "%z\072" // char is ':'
#endif

View File

@@ -0,0 +1,78 @@
# synergy -- mouse and keyboard sharing utility
# Copyright (C) 2009 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
set(inc
CEvent.h
CEventQueue.h
CFunctionEventJob.h
CFunctionJob.h
CLog.h
CPriorityQueue.h
CSimpleEventQueueBuffer.h
CStopwatch.h
CString.h
CStringUtil.h
CUnicode.h
IEventJob.h
IEventQueue.h
IEventQueueBuffer.h
IJob.h
ILogOutputter.h
LogOutputters.h
TMethodEventJob.h
TMethodJob.h
XBase.h
ELevel.h
)
set(src
CEvent.cpp
CEventQueue.cpp
CFunctionEventJob.cpp
CFunctionJob.cpp
CLog.cpp
CSimpleEventQueueBuffer.cpp
CStopwatch.cpp
CStringUtil.cpp
CUnicode.cpp
IEventQueue.cpp
LogOutputters.cpp
XBase.cpp
)
if (WIN32)
list(APPEND src ${inc})
endif()
set(inc
../arch
../common
../mt
../synergy
)
if (UNIX)
list(APPEND inc
../../..
../base
)
endif()
include_directories(${inc})
add_library(base STATIC ${src})
if (UNIX)
target_link_libraries(base common)
endif()

View File

@@ -0,0 +1,139 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CPRIORITYQUEUE_H
#define CPRIORITYQUEUE_H
#include "stdvector.h"
#include <algorithm>
#include <functional>
//! A priority queue with an iterator
/*!
This priority queue is the same as a standard priority queue except:
it sorts by std::greater, it has a forward iterator through the elements
(which can appear in any order), and its contents can be swapped.
*/
template <class T, class Container = std::vector<T>,
#if defined(_MSC_VER)
class Compare = std::greater<Container::value_type> >
#else
class Compare = std::greater<typename Container::value_type> >
#endif
class CPriorityQueue {
public:
typedef typename Container::value_type value_type;
typedef typename Container::size_type size_type;
typedef typename Container::iterator iterator;
typedef typename Container::const_iterator const_iterator;
typedef Container container_type;
CPriorityQueue() { }
CPriorityQueue(Container& swappedIn) { swap(swappedIn); }
~CPriorityQueue() { }
//! @name manipulators
//@{
//! Add element
void push(const value_type& v)
{
c.push_back(v);
std::push_heap(c.begin(), c.end(), comp);
}
//! Remove head element
void pop()
{
std::pop_heap(c.begin(), c.end(), comp);
c.pop_back();
}
//! Erase element
void erase(iterator i)
{
c.erase(i);
std::make_heap(c.begin(), c.end(), comp);
}
//! Get start iterator
iterator begin()
{
return c.begin();
}
//! Get end iterator
iterator end()
{
return c.end();
}
//! Swap contents with another priority queue
void swap(CPriorityQueue<T, Container, Compare>& q)
{
c.swap(q.c);
}
//! Swap contents with another container
void swap(Container& c2)
{
c.swap(c2);
std::make_heap(c.begin(), c.end(), comp);
}
//@}
//! @name accessors
//@{
//! Returns true if there are no elements
bool empty() const
{
return c.empty();
}
//! Returns the number of elements
size_type size() const
{
return c.size();
}
//! Returns the head element
const value_type& top() const
{
return c.front();
}
//! Get start iterator
const_iterator begin() const
{
return c.begin();
}
//! Get end iterator
const_iterator end() const
{
return c.end();
}
//@}
private:
Container c;
Compare comp;
};
#endif

View File

@@ -0,0 +1,100 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CSimpleEventQueueBuffer.h"
#include "CStopwatch.h"
#include "CArch.h"
class CEventQueueTimer { };
//
// CSimpleEventQueueBuffer
//
CSimpleEventQueueBuffer::CSimpleEventQueueBuffer()
{
m_queueMutex = ARCH->newMutex();
m_queueReadyCond = ARCH->newCondVar();
m_queueReady = false;
}
CSimpleEventQueueBuffer::~CSimpleEventQueueBuffer()
{
ARCH->closeCondVar(m_queueReadyCond);
ARCH->closeMutex(m_queueMutex);
}
void
CSimpleEventQueueBuffer::waitForEvent(double timeout)
{
CArchMutexLock lock(m_queueMutex);
CStopwatch timer(true);
while (!m_queueReady) {
double timeLeft = timeout;
if (timeLeft >= 0.0) {
timeLeft -= timer.getTime();
if (timeLeft < 0.0) {
return;
}
}
ARCH->waitCondVar(m_queueReadyCond, m_queueMutex, timeLeft);
}
}
IEventQueueBuffer::Type
CSimpleEventQueueBuffer::getEvent(CEvent&, UInt32& dataID)
{
CArchMutexLock lock(m_queueMutex);
if (!m_queueReady) {
return kNone;
}
dataID = m_queue.back();
m_queue.pop_back();
m_queueReady = !m_queue.empty();
return kUser;
}
bool
CSimpleEventQueueBuffer::addEvent(UInt32 dataID)
{
CArchMutexLock lock(m_queueMutex);
m_queue.push_front(dataID);
if (!m_queueReady) {
m_queueReady = true;
ARCH->broadcastCondVar(m_queueReadyCond);
}
return true;
}
bool
CSimpleEventQueueBuffer::isEmpty() const
{
CArchMutexLock lock(m_queueMutex);
return !m_queueReady;
}
CEventQueueTimer*
CSimpleEventQueueBuffer::newTimer(double, bool) const
{
return new CEventQueueTimer;
}
void
CSimpleEventQueueBuffer::deleteTimer(CEventQueueTimer* timer) const
{
delete timer;
}

View File

@@ -0,0 +1,52 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CSIMPLEEVENTQUEUEBUFFER_H
#define CSIMPLEEVENTQUEUEBUFFER_H
#include "IEventQueueBuffer.h"
#include "IArchMultithread.h"
#include "stddeque.h"
//! In-memory event queue buffer
/*!
An event queue buffer provides a queue of events for an IEventQueue.
*/
class CSimpleEventQueueBuffer : public IEventQueueBuffer {
public:
CSimpleEventQueueBuffer();
~CSimpleEventQueueBuffer();
// 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:
typedef std::deque<UInt32> CEventDeque;
CArchMutex m_queueMutex;
CArchCond m_queueReadyCond;
bool m_queueReady;
CEventDeque m_queue;
};
#endif

129
src/lib/base/CStopwatch.cpp Normal file
View File

@@ -0,0 +1,129 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CStopwatch.h"
#include "CArch.h"
//
// CStopwatch
//
CStopwatch::CStopwatch(bool triggered) :
m_mark(0.0),
m_triggered(triggered),
m_stopped(triggered)
{
if (!triggered) {
m_mark = ARCH->time();
}
}
CStopwatch::~CStopwatch()
{
// do nothing
}
double
CStopwatch::reset()
{
if (m_stopped) {
const double dt = m_mark;
m_mark = 0.0;
return dt;
}
else {
const double t = ARCH->time();
const double dt = t - m_mark;
m_mark = t;
return dt;
}
}
void
CStopwatch::stop()
{
if (m_stopped) {
return;
}
// save the elapsed time
m_mark = ARCH->time() - m_mark;
m_stopped = true;
}
void
CStopwatch::start()
{
m_triggered = false;
if (!m_stopped) {
return;
}
// set the mark such that it reports the time elapsed at stop()
m_mark = ARCH->time() - m_mark;
m_stopped = false;
}
void
CStopwatch::setTrigger()
{
stop();
m_triggered = true;
}
double
CStopwatch::getTime()
{
if (m_triggered) {
const double dt = m_mark;
start();
return dt;
}
else if (m_stopped) {
return m_mark;
}
else {
return ARCH->time() - m_mark;
}
}
CStopwatch::operator double()
{
return getTime();
}
bool
CStopwatch::isStopped() const
{
return m_stopped;
}
double
CStopwatch::getTime() const
{
if (m_stopped) {
return m_mark;
}
else {
return ARCH->time() - m_mark;
}
}
CStopwatch::operator double() const
{
return getTime();
}

111
src/lib/base/CStopwatch.h Normal file
View File

@@ -0,0 +1,111 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CSTOPWATCH_H
#define CSTOPWATCH_H
#include "common.h"
//! A timer class
/*!
This class measures time intervals. All time interval measurement
should use this class.
*/
class CStopwatch {
public:
/*!
The default constructor does an implicit reset() or setTrigger().
If triggered == false then the clock starts ticking.
*/
CStopwatch(bool triggered = false);
~CStopwatch();
//! @name manipulators
//@{
//! Reset the timer to zero
/*!
Set the start time to the current time, returning the time since
the last reset. This does not remove the trigger if it's set nor
does it start a stopped clock. If the clock is stopped then
subsequent reset()'s will return 0.
*/
double reset();
//! Stop the timer
/*!
Stop the stopwatch. The time interval while stopped is not
counted by the stopwatch. stop() does not remove the trigger.
Has no effect if already stopped.
*/
void stop();
//! Start the timer
/*!
Start the stopwatch. start() removes the trigger, even if the
stopwatch was already started.
*/
void start();
//! Stop the timer and set the trigger
/*!
setTrigger() stops the clock like stop() except there's an
implicit start() the next time (non-const) getTime() is called.
This is useful when you want the clock to start the first time
you check it.
*/
void setTrigger();
//! Get elapsed time
/*!
Returns the time since the last reset() (or calls reset() and
returns zero if the trigger is set).
*/
double getTime();
//! Same as getTime()
operator double();
//@}
//! @name accessors
//@{
//! Check if timer is stopped
/*!
Returns true if the stopwatch is stopped.
*/
bool isStopped() const;
// return the time since the last reset().
//! Get elapsed time
/*!
Returns the time since the last reset(). This cannot trigger the
stopwatch to start and will not clear the trigger.
*/
double getTime() const;
//! Same as getTime() const
operator double() const;
//@}
private:
double getClock() const;
private:
double m_mark;
bool m_triggered;
bool m_stopped;
};
#endif

Some files were not shown because too many files have changed in this diff Show More