Checkpoint. synergys now works. Still need to do lib/client and

synergyc.
This commit is contained in:
crs 2004-02-14 14:04:36 +00:00
parent c44c18bfdc
commit 1861f21fb5
68 changed files with 3812 additions and 4114 deletions

View File

@ -14,8 +14,8 @@
#include "CServerTaskBarReceiver.h"
#include "CServer.h"
#include "CEventQueue.h"
#include "CLock.h"
#include "TMethodJob.h"
#include "CArch.h"
//
@ -23,71 +23,80 @@
//
CServerTaskBarReceiver::CServerTaskBarReceiver() :
m_quit(NULL),
m_state(kNotRunning),
m_server(NULL)
m_state(kNotRunning)
{
// create a job for getting notification when the server's
// status changes.
m_job = new TMethodJob<CServerTaskBarReceiver>(this,
&CServerTaskBarReceiver::statusChanged, NULL);
// do nothing
}
CServerTaskBarReceiver::~CServerTaskBarReceiver()
{
if (m_server != NULL) {
m_server->removeStatusJob(m_job);
}
delete m_job;
delete m_quit;
// do nothing
}
#include "CLog.h"
void
CServerTaskBarReceiver::setServer(CServer* server)
CServerTaskBarReceiver::updateStatus(CServer* server, const CString& errorMsg)
{
{
// update our status
CLock lock(&m_mutex);
if (m_server != server) {
if (m_server != NULL) {
m_server->removeStatusJob(m_job);
m_errorMessage = errorMsg;
if (server == NULL) {
if (m_errorMessage.empty()) {
m_state = kNotRunning;
}
m_server = server;
if (m_server != NULL) {
m_server->addStatusJob(m_job);
else {
m_state = kNotWorking;
}
}
else {
m_clients.clear();
server->getClients(m_clients);
if (m_clients.size() <= 1) {
m_state = kNotConnected;
}
else {
m_state = kConnected;
}
}
// let subclasses have a go
onStatusChanged(server);
LOG((CLOG_INFO "### status: %s", getToolTip().c_str()));
}
// tell task bar
ARCH->updateReceiver(this);
}
void
CServerTaskBarReceiver::setState(EState state)
{
{
CLock lock(&m_mutex);
m_state = state;
}
ARCH->updateReceiver(this);
}
void
CServerTaskBarReceiver::setQuitJob(IJob* job)
{
CLock lock(&m_mutex);
delete m_quit;
m_quit = job;
}
CServerTaskBarReceiver::EState
CServerTaskBarReceiver::getState() const
CServerTaskBarReceiver::getStatus() const
{
return m_state;
}
CServer*
CServerTaskBarReceiver::getServer() const
const CString&
CServerTaskBarReceiver::getErrorMessage() const
{
return m_server;
return m_errorMessage;
}
const CServerTaskBarReceiver::CClients&
CServerTaskBarReceiver::getClients() const
{
return m_clients;
}
void
CServerTaskBarReceiver::quit()
{
EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
}
void
CServerTaskBarReceiver::onStatusChanged(CServer*)
{
// do nothing
}
void
@ -110,7 +119,7 @@ CServerTaskBarReceiver::getToolTip() const
return "Synergy: Not running";
case kNotWorking:
return CString("Synergy: ") + m_errorMessage;
return std::string("Synergy: ") + m_errorMessage;
case kNotConnected:
return "Synergy: Waiting for clients";
@ -122,50 +131,3 @@ CServerTaskBarReceiver::getToolTip() const
return "";
}
}
void
CServerTaskBarReceiver::quit()
{
if (m_quit != NULL) {
m_quit->run();
}
}
void
CServerTaskBarReceiver::onStatusChanged()
{
// do nothing
}
void
CServerTaskBarReceiver::statusChanged(void*)
{
// update our status
switch (m_server->getStatus(&m_errorMessage)) {
case CServer::kNotRunning:
setState(kNotRunning);
break;
case CServer::kRunning:
if (m_server->getNumClients() > 1)
setState(kConnected);
else
setState(kNotConnected);
break;
case CServer::kServerNameUnknown:
m_errorMessage = "Server name is not in configuration";
setState(kNotWorking);
break;
case CServer::kError:
setState(kNotWorking);
break;
default:
break;
}
// let subclasses have a go
onStatusChanged();
}

View File

@ -18,61 +18,24 @@
#include "CMutex.h"
#include "CString.h"
#include "IArchTaskBarReceiver.h"
#include "stdvector.h"
class CServer;
class IJob;
//! Implementation of IArchTaskBarReceiver for the synergy server
class CServerTaskBarReceiver : public IArchTaskBarReceiver {
public:
enum EState {
kNotRunning,
kNotWorking,
kNotConnected,
kConnected,
kMaxState
};
CServerTaskBarReceiver();
virtual ~CServerTaskBarReceiver();
//! @name manipulators
//@{
//! Set server
//! Update status
/*!
Sets the server. The receiver will query state from this server.
Determine the status and query required information from the server.
*/
void setServer(CServer*);
//! Set state
/*!
Sets the current server state.
*/
void setState(EState);
//! Set the quit job that causes the server to quit
/*!
Set the job that causes the server to quit.
*/
void setQuitJob(IJob* adopted);
//@}
//! @name accessors
//@{
//! Get state
/*!
Returns the current server state. The receiver is not locked
by this call; the caller must do the locking.
*/
EState getState() const;
//! Get server
/*!
Returns the server set by \c setServer().
*/
CServer* getServer() const;
void updateStatus(CServer*, const CString& errorMsg);
//@}
@ -86,6 +49,28 @@ public:
virtual std::string getToolTip() const;
protected:
typedef std::vector<CString> CClients;
enum EState {
kNotRunning,
kNotWorking,
kNotConnected,
kConnected,
kMaxState
};
//! Get status
EState getStatus() const;
//! Get error message
const CString& getErrorMessage() const;
//! Get connected clients
const CClients& getClients() const;
//! Quit app
/*!
Causes the application to quit gracefully
*/
void quit();
//! Status change notification
@ -93,18 +78,13 @@ protected:
Called when status changes. The default implementation does
nothing.
*/
virtual void onStatusChanged();
private:
void statusChanged(void*);
virtual void onStatusChanged(CServer* server);
private:
CMutex m_mutex;
IJob* m_quit;
EState m_state;
CServer* m_server;
IJob* m_job;
CString m_errorMessage;
CClients m_clients;
};
#endif

View File

@ -53,9 +53,3 @@ CXWindowsServerTaskBarReceiver::getIcon() const
{
return NULL;
}
void
CXWindowsServerTaskBarReceiver::onStatusChanged()
{
// do nothing
}

View File

@ -28,10 +28,6 @@ public:
virtual void runMenu(int x, int y);
virtual void primaryAction();
virtual const Icon getIcon() const;
protected:
// CServerTaskBarReceiver overrides
virtual void onStatusChanged();
};
#endif

View File

@ -12,22 +12,25 @@
* GNU General Public License for more details.
*/
#include "CServer.h"
#include "CClientListener.h"
#include "CClientProxy.h"
#include "CConfig.h"
#include "IScreenFactory.h"
#include "CPrimaryClient.h"
#include "CServer.h"
#include "CScreen.h"
#include "ProtocolTypes.h"
#include "Version.h"
#include "XScreen.h"
#include "CSocketMultiplexer.h"
#include "CTCPSocketFactory.h"
#include "XSocket.h"
#include "CLock.h"
#include "CMutex.h"
#include "CThread.h"
#include "XThread.h"
#include "CFunctionJob.h"
#include "CEventQueue.h"
#include "CFunctionEventJob.h"
#include "CLog.h"
#include "LogOutputters.h"
#include "CArch.h"
#include "XArch.h"
#include "stdfstream.h"
#include <cstring>
@ -98,82 +101,243 @@ CArgs* CArgs::s_instance = NULL;
// platform dependent factories
//
//! Factory for creating screens
/*!
Objects of this type create screens appropriate for the platform.
*/
class CScreenFactory : public IScreenFactory {
public:
CScreenFactory() { }
virtual ~CScreenFactory() { }
// IScreenFactory overrides
virtual IPlatformScreen*
create(IScreenReceiver*, IPrimaryScreenReceiver*);
};
IPlatformScreen*
CScreenFactory::create(IScreenReceiver* receiver,
IPrimaryScreenReceiver* primaryReceiver)
static
CScreen*
createScreen()
{
#if WINDOWS_LIKE
return new CMSWindowsScreen(receiver, primaryReceiver);
return new CScreen(new CMSWindowsScreen(true));
#elif UNIX_LIKE
return new CXWindowsScreen(receiver, primaryReceiver);
return new CScreen(new CXWindowsScreen(true));
#endif
}
//! CQuitJob
/*!
A job that cancels a given thread.
*/
class CQuitJob : public IJob {
public:
CQuitJob(const CThread& thread);
~CQuitJob();
// IJob overrides
virtual void run();
private:
CThread m_thread;
};
CQuitJob::CQuitJob(const CThread& thread) :
m_thread(thread)
{
// do nothing
}
CQuitJob::~CQuitJob()
{
// do nothing
}
void
CQuitJob::run()
{
m_thread.cancel();
}
//
// platform independent main
//
static CServer* s_server = NULL;
static CPrimaryClient* s_primaryClient = NULL;
static CClientListener* s_listener = NULL;
static CServerTaskBarReceiver* s_taskBarReceiver = NULL;
static
int
realMain(void)
void
updateStatus()
{
int result = kExitSuccess;
do {
bool opened = false;
bool locked = true;
s_taskBarReceiver->updateStatus(s_server, "");
}
static
void
updateStatus(const CString& msg)
{
s_taskBarReceiver->updateStatus(s_server, msg);
}
static
void
handleClientConnected(const CEvent&, void* vlistener)
{
CClientListener* listener = reinterpret_cast<CClientListener*>(vlistener);
CClientProxy* client = listener->getNextClient();
if (client != NULL) {
s_server->adoptClient(client);
updateStatus();
}
}
static
CClientListener*
openClientListener(const CNetworkAddress& address)
{
CClientListener* listen =
new CClientListener(address, new CTCPSocketFactory, NULL);
EVENTQUEUE->adoptHandler(CClientListener::getConnectedEvent(), listen,
new CFunctionEventJob(
&handleClientConnected, listen));
return listen;
}
static
void
closeClientListener(CClientListener* listen)
{
if (listen != NULL) {
EVENTQUEUE->removeHandler(CClientListener::getConnectedEvent(), listen);
delete listen;
}
}
static
void
handleScreenError(const CEvent&, void*)
{
EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
}
static
CPrimaryClient*
openPrimaryClient(const CString& name)
{
LOG((CLOG_DEBUG1 "creating primary screen"));
CScreen* screen = createScreen();
CPrimaryClient* primaryClient = new CPrimaryClient(name, screen);
EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(),
primaryClient->getEventTarget(),
new CFunctionEventJob(
&handleScreenError));
return primaryClient;
}
static
void
closePrimaryClient(CPrimaryClient* primaryClient)
{
if (primaryClient != NULL) {
EVENTQUEUE->removeHandler(IScreen::getErrorEvent(),
primaryClient->getEventTarget());
delete primaryClient;
}
}
static
void
handleNoClients(const CEvent&, void*)
{
updateStatus();
}
static
void
handleClientsDisconnected(const CEvent&, void*)
{
EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
}
static
CServer*
openServer(const CConfig& config, CPrimaryClient* primaryClient)
{
CServer* server = new CServer(config, primaryClient);
EVENTQUEUE->adoptHandler(CServer::getDisconnectedEvent(), server,
new CFunctionEventJob(handleNoClients));
return server;
}
static
void
closeServer(CServer* server)
{
if (server == NULL) {
return;
}
// tell all clients to disconnect
server->disconnect();
// wait for clients to disconnect for up to timeout seconds
double timeout = 3.0;
CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(timeout, NULL);
EVENTQUEUE->adoptHandler(CEvent::kTimer, timer,
new CFunctionEventJob(handleClientsDisconnected));
EVENTQUEUE->adoptHandler(CServer::getDisconnectedEvent(), server,
new CFunctionEventJob(handleClientsDisconnected));
CEvent event;
EVENTQUEUE->getEvent(event);
while (event.getType() != CEvent::kQuit) {
EVENTQUEUE->dispatchEvent(event);
CEvent::deleteData(event);
EVENTQUEUE->getEvent(event);
}
EVENTQUEUE->removeHandler(CEvent::kTimer, timer);
EVENTQUEUE->deleteTimer(timer);
EVENTQUEUE->removeHandler(CServer::getDisconnectedEvent(), server);
// done with server
delete server;
}
static bool startServer();
static
void
retryStartHandler(const CEvent&, void* vtimer)
{
// discard old timer
CEventQueueTimer* timer = reinterpret_cast<CEventQueueTimer*>(vtimer);
EVENTQUEUE->deleteTimer(timer);
EVENTQUEUE->removeHandler(CEvent::kTimer, NULL);
// try starting the server again
LOG((CLOG_DEBUG1 "retry starting server"));
startServer();
}
static
bool
startServer()
{
double retryTime;
CPrimaryClient* primaryClient = NULL;
CClientListener* listener = NULL;
try {
CString name = ARG->m_config.getCanonicalName(ARG->m_name);
primaryClient = openPrimaryClient(name);
listener = openClientListener(ARG->m_config.getSynergyAddress());
s_server = openServer(ARG->m_config, primaryClient);
s_primaryClient = primaryClient;
s_listener = listener;
updateStatus();
LOG((CLOG_NOTE "started server"));
return true;
}
catch (XScreenUnavailable& e) {
LOG((CLOG_WARN "cannot open primary screen: %s", e.what()));
closeClientListener(listener);
closePrimaryClient(primaryClient);
updateStatus(CString("cannot open primary screen: ") + e.what());
retryTime = e.getRetryTime();
}
catch (XSocketAddressInUse& e) {
LOG((CLOG_WARN "cannot listen for clients: %s", e.what()));
closeClientListener(listener);
closePrimaryClient(primaryClient);
updateStatus(CString("cannot listen for clients: ") + e.what());
retryTime = 10.0;
}
catch (XScreenOpenFailure& e) {
LOG((CLOG_CRIT "cannot open primary screen: %s", e.what()));
closeClientListener(listener);
closePrimaryClient(primaryClient);
return false;
}
catch (XBase& e) {
LOG((CLOG_CRIT "failed to start server: %s", e.what()));
closeClientListener(listener);
closePrimaryClient(primaryClient);
return false;
}
if (ARG->m_restartable) {
// install a timer and handler to retry later
LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime));
CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL);
EVENTQUEUE->adoptHandler(CEvent::kTimer, timer,
new CFunctionEventJob(&retryStartHandler, timer));
return true;
}
else {
// don't try again
return false;
}
}
static
int
realMain()
{
// if configuration has no screens then add this system
// as the default
if (ARG->m_config.begin() == ARG->m_config.end()) {
@ -190,74 +354,48 @@ realMain(void)
ARG->m_config.setSynergyAddress(CNetworkAddress(kDefaultPort));
}
// create server
s_server = new CServer(ARG->m_name);
s_server->setConfig(ARG->m_config);
s_server->setScreenFactory(new CScreenFactory);
s_server->setSocketFactory(new CTCPSocketFactory);
s_server->setStreamFilterFactory(NULL);
// canonicalize the primary screen name
CString primaryName = ARG->m_config.getCanonicalName(ARG->m_name);
if (primaryName.empty()) {
LOG((CLOG_CRIT "unknown screen name `%s'", ARG->m_name.c_str()));
return kExitFailed;
}
// open server
try {
s_taskBarReceiver->setServer(s_server);
s_server->open();
opened = true;
// start the server. if this return false then we've failed and
// we shouldn't retry.
LOG((CLOG_DEBUG1 "starting server"));
if (!startServer()) {
return kExitFailed;
}
// run server
// run event loop. if startServer() failed we're supposed to retry
// later. the timer installed by startServer() will take care of
// that.
DAEMON_RUNNING(true);
locked = false;
s_server->mainLoop();
CEvent event;
EVENTQUEUE->getEvent(event);
while (event.getType() != CEvent::kQuit) {
EVENTQUEUE->dispatchEvent(event);
CEvent::deleteData(event);
EVENTQUEUE->getEvent(event);
}
DAEMON_RUNNING(false);
// clean up
#define FINALLY do { \
if (!locked) { \
DAEMON_RUNNING(false); \
locked = true; \
} \
if (opened) { \
s_server->close(); \
} \
s_taskBarReceiver->setServer(NULL); \
delete s_server; \
s_server = NULL; \
} while (false)
FINALLY;
}
catch (XScreenUnavailable& e) {
// wait before retrying if we're going to retry
if (ARG->m_restartable) {
ARCH->sleep(e.getRetryTime());
}
else {
result = kExitFailed;
}
FINALLY;
}
catch (XThread&) {
FINALLY;
throw;
}
catch (...) {
// don't try to restart and fail
ARG->m_restartable = false;
result = kExitFailed;
FINALLY;
}
#undef FINALLY
}
catch (XBase& e) {
LOG((CLOG_CRIT "failed: %s", e.what()));
}
catch (XThread&) {
// terminated
ARG->m_restartable = false;
result = kExitTerminated;
}
} while (ARG->m_restartable);
// close down
LOG((CLOG_DEBUG1 "stopping server"));
closeClientListener(s_listener);
closeServer(s_server);
closePrimaryClient(s_primaryClient);
s_server = NULL;
s_listener = NULL;
s_primaryClient = NULL;
updateStatus();
LOG((CLOG_NOTE "stopped server"));
return result;
return kExitSuccess;
}
/* XXX
static
void
realMainEntry(void* vresult)
@ -293,7 +431,7 @@ runMainInThread(void)
throw;
}
}
*/
//
// command line parsing
@ -666,9 +804,6 @@ daemonStartup(int argc, const char** argv)
{
CSystemLogger sysLogger(DAEMON_NAME);
// have to cancel this thread to quit
s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread()));
// catch errors that would normally exit
bye = &byeThrow;
@ -768,7 +903,6 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int)
// through the task bar.
s_taskBarReceiver = new CMSWindowsServerTaskBarReceiver(instance,
&logBuffer);
s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread()));
int result;
try {
@ -817,15 +951,20 @@ main(int argc, char** argv)
{
CArch arch;
CLOG;
CArgs args;
// go really fast
CThread::getCurrentThread().setPriority(-14);
CSocketMultiplexer multiplexer;
CEventQueue eventQueue;
// get program name
CArgs args;
ARG->m_pname = ARCH->getBasename(argv[0]);
// make the task bar receiver. the user can control this app
// through the task bar.
s_taskBarReceiver = new CXWindowsServerTaskBarReceiver;
s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread()));
// parse command line
parse(argc, argv);

View File

@ -555,6 +555,12 @@ CArch::isAnyAddr(CArchNetAddress addr)
return m_net->isAnyAddr(addr);
}
bool
CArch::isEqualAddr(CArchNetAddress a, CArchNetAddress b)
{
return m_net->isEqualAddr(a, b);
}
void
CArch::sleep(double timeout)
{

View File

@ -153,6 +153,7 @@ public:
virtual void setAddrPort(CArchNetAddress, int port);
virtual int getAddrPort(CArchNetAddress);
virtual bool isAnyAddr(CArchNetAddress);
virtual bool isEqualAddr(CArchNetAddress, CArchNetAddress);
// IArchSleep overrides
virtual void sleep(double timeout);

View File

@ -575,6 +575,7 @@ CArchMultithreadPosix::interrupt()
lockMutex(m_threadMutex);
if (m_signalFunc != NULL) {
m_signalFunc(m_signalUserData);
pthread_kill(m_mainThread->m_thread, SIGWAKEUP);
}
else {
ARCH->cancelThread(m_mainThread);

View File

@ -29,6 +29,7 @@
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#if HAVE_POLL
# include <sys/poll.h>
@ -750,6 +751,13 @@ CArchNetworkBSD::isAnyAddr(CArchNetAddress addr)
}
}
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);
}
void
CArchNetworkBSD::throwError(int err)
{

View File

@ -77,6 +77,7 @@ public:
virtual void setAddrPort(CArchNetAddress, int port);
virtual int getAddrPort(CArchNetAddress);
virtual bool isAnyAddr(CArchNetAddress);
virtual bool isEqualAddr(CArchNetAddress, CArchNetAddress);
private:
void throwError(int);

View File

@ -265,6 +265,9 @@ public:
//! 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()

View File

@ -56,7 +56,6 @@ ARCH_STRING::vsnprintf(char* str, int size, const char* fmt, va_list ap)
#else // !HAVE_VSNPRINTF && !UNIX_LIKE
// FIXME
#error vsnprintf not implemented
#endif // !HAVE_VSNPRINTF

View File

@ -13,13 +13,12 @@
*/
#include "CEvent.h"
#include "CEventQueue.h"
//
// CEvent
//
CEvent::Type CEvent::s_nextType = kLast;
CEvent::CEvent() :
m_type(kUnknown),
m_target(NULL),
@ -55,20 +54,21 @@ CEvent::getData() const
}
CEvent::Type
CEvent::registerType()
CEvent::registerType(const char* name)
{
// FIXME -- lock mutex (need a mutex)
return s_nextType++;
return EVENTQUEUE->registerType(name);
}
CEvent::Type
CEvent::registerTypeOnce(Type& type)
CEvent::registerTypeOnce(Type& type, const char* name)
{
// FIXME -- lock mutex (need a mutex)
if (type == CEvent::kUnknown) {
type = s_nextType++;
return EVENTQUEUE->registerTypeOnce(type, name);
}
return type;
const char*
CEvent::getTypeName(Type type)
{
return EVENTQUEUE->getTypeName(type);
}
void

View File

@ -16,6 +16,7 @@
#define CEVENT_H
#include "BasicTypes.h"
#include "stdmap.h"
//! Event
/*!
@ -46,6 +47,33 @@ public:
//! @name manipulators
//@{
//! Creates a new event type
/*!
Returns a unique event type id.
*/
static Type registerType(const char* name);
//! Creates a new event type
/*!
If \p type contains \c kUnknown then it is set to a unique event
type id otherwise it is left alone. The final value of \p type
is returned.
*/
static Type registerTypeOnce(Type& type, const char* name);
//! Get name for event
/*!
Returns the name for the event \p type. This is primarily for
debugging.
*/
static const char* getTypeName(Type type);
//! Release event data
/*!
Deletes event data for the given event.
*/
static void deleteData(const CEvent&);
//@}
//! @name accessors
//@{
@ -68,33 +96,12 @@ public:
*/
void* getData() const;
//! Creates a new event type
/*!
Returns a unique event type id.
*/
static Type registerType();
//! Creates a new event type
/*!
If \p type contains \c kUnknown then it is set to a unique event
type id otherwise it is left alone. The final value of \p type
is returned.
*/
static Type registerTypeOnce(Type& type);
//! Release event data
/*!
Deletes event data for the given event.
*/
static void deleteData(const CEvent&);
//@}
private:
Type m_type;
void* m_target;
void* m_data;
static Type s_nextType;
};
#endif

View File

@ -13,7 +13,9 @@
*/
#include "CEventQueue.h"
#include "CLog.h"
#include "CSimpleEventQueueBuffer.h"
#include "CStopwatch.h"
#include "IEventJob.h"
#include "CArch.h"
@ -30,7 +32,8 @@ interrupt(void*)
// CEventQueue
//
CEventQueue::CEventQueue()
CEventQueue::CEventQueue() :
m_nextType(CEvent::kLast)
{
setInstance(this);
m_mutex = ARCH->newMutex();
@ -46,6 +49,54 @@ CEventQueue::~CEventQueue()
setInstance(NULL);
}
CEvent::Type
CEventQueue::registerType(const char* name)
{
CArchMutexLock lock(m_mutex);
m_typeMap.insert(std::make_pair(m_nextType, name));
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));
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)
{
@ -69,34 +120,44 @@ CEventQueue::adoptBuffer(IEventQueueBuffer* buffer)
bool
CEventQueue::getEvent(CEvent& event, double timeout)
{
CStopwatch timer(true);
retry:
// if no events are waiting then handle timers and then wait
if (m_buffer->isEmpty()) {
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 (timerTimeout >= 0.0 && timerTimeout < timeout) {
timeout = timerTimeout;
if (timeout < 0.0 || (timerTimeout >= 0.0 && timerTimeout < timeLeft)) {
timeLeft = timerTimeout;
}
// wait for an event
m_buffer->waitForEvent(timeout);
}
// if no events are pending then do the timers
if (m_buffer->isEmpty()) {
return hasTimerExpired(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:
@ -156,9 +217,16 @@ 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);
m_timerQueue.push(CTimer(timer, duration, target, false));
// 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;
}
@ -168,9 +236,16 @@ 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);
m_timerQueue.push(CTimer(timer, duration, target, true));
// 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;
}
@ -401,13 +476,13 @@ CEventQueue::CTypeTarget::operator<(const CTypeTarget& tt) const
// CEventQueue::CTimer
//
CEventQueue::CTimer::CTimer(CEventQueueTimer* timer,
double timeout, void* target, bool oneShot) :
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(timeout)
m_time(initialTime)
{
assert(m_timeout > 0.0);
}

View File

@ -39,9 +39,9 @@ public:
virtual bool dispatchEvent(const CEvent& event);
virtual void addEvent(const CEvent& event);
virtual CEventQueueTimer*
newTimer(double duration, void* target = NULL);
newTimer(double duration, void* target);
virtual CEventQueueTimer*
newOneShotTimer(double duration, void* target = NULL);
newOneShotTimer(double duration, void* target);
virtual void deleteTimer(CEventQueueTimer*);
virtual void adoptHandler(void* target, IEventJob* dispatcher);
virtual void adoptHandler(CEvent::Type type,
@ -50,8 +50,13 @@ public:
virtual IEventJob* orphanHandler(CEvent::Type type, void* target);
virtual void removeHandler(void* target);
virtual void removeHandler(CEvent::Type type, 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);
private:
void doAdoptHandler(CEvent::Type type,
@ -77,7 +82,8 @@ private:
};
class CTimer {
public:
CTimer(CEventQueueTimer*, double timeout, void* target, bool oneShot);
CTimer(CEventQueueTimer*, double timeout, double initialTime,
void* target, bool oneShot);
~CTimer();
void reset();
@ -106,19 +112,28 @@ private:
typedef std::map<UInt32, CEvent> CEventTable;
typedef std::vector<UInt32> CEventIDList;
typedef std::map<CTypeTarget, IEventJob*> CHandlerTable;
typedef std::map<CEvent::Type, const char*> CTypeMap;
CArchMutex m_mutex;
// registered events
CEvent::Type m_nextType;
CTypeMap m_typeMap;
// 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;
};

View File

@ -0,0 +1,97 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "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& event, 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,49 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef 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

View File

@ -176,7 +176,7 @@ CStringUtil::CaselessCmp::cmpEqual(
const CString::value_type& a,
const CString::value_type& b)
{
// FIXME -- use std::tolower but not in all versions of libstdc++ have it
// should use std::tolower but not in all versions of libstdc++ have it
return tolower(a) == tolower(b);
}
@ -185,7 +185,7 @@ CStringUtil::CaselessCmp::cmpLess(
const CString::value_type& a,
const CString::value_type& b)
{
// FIXME -- use std::tolower but not in all versions of libstdc++ have it
// should use std::tolower but not in all versions of libstdc++ have it
return tolower(a) < tolower(b);
}

View File

@ -80,7 +80,8 @@ public:
is returned the data points to a \c CTimerEvent. The client must pass
the returned timer to \c deleteTimer() (whether or not the timer has
expired) to release the timer. The returned timer event uses the
given \p target.
given \p target. If \p target is NULL it uses the returned timer as
the target.
Events for a single timer don't accumulate in the queue, even if the
client reading events can't keep up. Instead, the \c m_count member
@ -89,7 +90,7 @@ public:
removed (or since the timer was added).
*/
virtual CEventQueueTimer*
newTimer(double duration, void* target = NULL) = 0;
newTimer(double duration, void* target) = 0;
//! Create a one-shot timer
/*!
@ -99,11 +100,12 @@ public:
The \m c_count member of the \c CTimerEvent is always 1. The client
must pass the returned timer to \c deleteTimer() (whether or not the
timer has expired) to release the timer. The returned timer event
uses the given \p target.
uses the given \p target. If \p target is NULL it uses the returned
timer as the target.
*/
virtual CEventQueueTimer*
newOneShotTimer(double duration,
void* target = NULL) = 0;
void* target) = 0;
//! Destroy a timer
/*!
@ -160,6 +162,23 @@ public:
*/
virtual void removeHandler(CEvent::Type type, void* target) = 0;
//! Creates a new event type
/*!
Returns a unique event type id.
*/
virtual CEvent::Type
registerType(const char* name) = 0;
//! Creates a new event type
/*!
If \p type contains \c kUnknown then it is set to a unique event
type id otherwise it is left alone. The final value of \p type
is returned.
*/
virtual CEvent::Type
registerTypeOnce(CEvent::Type& type,
const char* name) = 0;
//@}
//! @name accessors
//@{
@ -179,6 +198,13 @@ public:
*/
virtual IEventJob* getHandler(CEvent::Type type, void* target) const = 0;
//! Get name for event
/*!
Returns the name for the event \p type. This is primarily for
debugging.
*/
virtual const char* getTypeName(CEvent::Type type) = 0;
//! Get the system event type target
/*!
Returns the target to use for dispatching \c CEvent::kSystem events.

View File

@ -0,0 +1,94 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef IEVENTQUEUEBUFFER_H
#define IEVENTQUEUEBUFFER_H
#include "IInterface.h"
#include "BasicTypes.h"
class CEvent;
class CEventQueueTimer;
//! Event queue buffer interface
/*!
An event queue buffer provides a queue of events for an IEventQueue.
*/
class IEventQueueBuffer : public IInterface {
public:
enum Type {
kNone, //!< No event is available
kSystem, //!< Event is a system event
kUser //!< Event is a user event
};
//! @name manipulators
//@{
//! Block waiting for an event
/*!
Wait for an event in the event queue buffer for up to \p timeout
seconds.
*/
virtual void waitForEvent(double timeout) = 0;
//! Get the next event
/*!
Get the next event from the buffer. Return kNone if no event is
available. If a system event is next, return kSystem and fill in
event. The event data in a system event can point to a static
buffer (because CEvent::deleteData() will not attempt to delete
data in a kSystem event). Otherwise, return kUser and fill in
\p dataID with the value passed to \c addEvent().
*/
virtual Type getEvent(CEvent& event, UInt32& dataID) = 0;
//! Post an event
/*!
Add the given event to the end of the queue buffer. This is a user
event and \c getEvent() must be able to identify it as such and
return \p dataID. This method must cause \c waitForEvent() to
return at some future time if it's blocked waiting on an event.
*/
virtual bool addEvent(UInt32 dataID) = 0;
//@}
//! @name accessors
//@{
//! Check if event queue buffer is empty
/*!
Return true iff the event queue buffer is empty.
*/
virtual bool isEmpty() const = 0;
//! Create a timer object
/*!
Create and return a timer object. The object is opaque and is
used only by the buffer but it must be a valid object (i.e.
not NULL).
*/
virtual CEventQueueTimer*
newTimer(double duration, bool oneShot) const = 0;
//! Destroy a timer object
/*!
Destroy a timer object previously returned by \c newTimer().
*/
virtual void deleteTimer(CEventQueueTimer*) const = 0;
//@}
};
#endif

View File

@ -27,29 +27,34 @@ CEvent::Type IStream::s_outputShutdownEvent = CEvent::kUnknown;
CEvent::Type
IStream::getInputReadyEvent()
{
return CEvent::registerTypeOnce(s_inputReadyEvent);
return CEvent::registerTypeOnce(s_inputReadyEvent,
"IStream::inputReady");
}
CEvent::Type
IStream::getOutputFlushedEvent()
{
return CEvent::registerTypeOnce(s_outputFlushedEvent);
return CEvent::registerTypeOnce(s_outputFlushedEvent,
"IStream::outputFlushed");
}
CEvent::Type
IStream::getOutputErrorEvent()
{
return CEvent::registerTypeOnce(s_outputErrorEvent);
return CEvent::registerTypeOnce(s_outputErrorEvent,
"IStream::outputError");
}
CEvent::Type
IStream::getInputShutdownEvent()
{
return CEvent::registerTypeOnce(s_inputShutdownEvent);
return CEvent::registerTypeOnce(s_inputShutdownEvent,
"IStream::inputShutdown");
}
CEvent::Type
IStream::getOutputShutdownEvent()
{
return CEvent::registerTypeOnce(s_outputShutdownEvent);
return CEvent::registerTypeOnce(s_outputShutdownEvent,
"IStream::outputShutdown");
}

View File

@ -141,6 +141,18 @@ CNetworkAddress::operator=(const CNetworkAddress& addr)
return *this;
}
bool
CNetworkAddress::operator==(const CNetworkAddress& addr) const
{
return ARCH->isEqualAddr(m_address, addr.m_address);
}
bool
CNetworkAddress::operator!=(const CNetworkAddress& addr) const
{
return !operator==(addr);
}
bool
CNetworkAddress::isValid() const
{

View File

@ -55,6 +55,18 @@ public:
//! @name accessors
//@{
//! Check address equality
/*!
Returns true if this address is equal to \p address.
*/
bool operator==(const CNetworkAddress&) const;
//! Check address inequality
/*!
Returns true if this address is not equal to \p address.
*/
bool operator!=(const CNetworkAddress&) const;
//! Check address validity
/*!
Returns true if this is not the invalid address.

View File

@ -18,6 +18,7 @@
#include "CLock.h"
#include "CMutex.h"
#include "CThread.h"
#include "CLog.h"
#include "TMethodJob.h"
#include "CArch.h"
#include "XArch.h"
@ -207,8 +208,8 @@ CSocketMultiplexer::serviceThread(void*)
// check for status
status = ARCH->pollSocket(&pfds[0], pfds.size(), -1);
}
catch (XArchNetwork&) {
// FIXME -- uh oh
catch (XArchNetwork& e) {
LOG((CLOG_WARN "error in socket multiplexer: %s", e.what().c_str()));
status = 0;
}

View File

@ -62,6 +62,7 @@ CTCPListenSocket::bind(const CNetworkAddress& addr)
CLock lock(m_mutex);
ARCH->bindSocket(m_socket, addr.getAddress());
ARCH->listenOnSocket(m_socket);
ARCH->setBlockingOnSocket(m_socket, false);
CSocketMultiplexer::getInstance()->addSocket(this,
new TSocketMultiplexerMethodJob<CTCPListenSocket>(
this, &CTCPListenSocket::serviceListening,
@ -102,11 +103,13 @@ IDataSocket*
CTCPListenSocket::accept()
{
try {
IDataSocket* socket =
new CTCPSocket(ARCH->acceptSocket(m_socket, NULL));
CSocketMultiplexer::getInstance()->addSocket(this,
new TSocketMultiplexerMethodJob<CTCPListenSocket>(
this, &CTCPListenSocket::serviceListening,
m_socket, true, false));
return new CTCPSocket(ARCH->acceptSocket(m_socket, NULL));
return socket;
}
catch (XArchNetwork&) {
return NULL;

View File

@ -19,6 +19,7 @@
#include "XSocket.h"
#include "CLock.h"
#include "CEventQueue.h"
#include "CLog.h"
#include "IEventJob.h"
#include "CArch.h"
#include "XArch.h"
@ -102,8 +103,8 @@ CTCPSocket::close()
ARCH->closeSocket(socket);
}
catch (XArchNetwork& e) {
// FIXME -- just discard this for now
//throw XSocketIOClose(e.what());
// ignore, there's not much we can do
LOG((CLOG_WARN "error closing socket: %s", e.what().c_str()));
}
}
}
@ -257,7 +258,6 @@ CTCPSocket::connect(const CNetworkAddress& addr)
}
try {
// FIXME -- don't throw if in progress, just return that info
ARCH->connectSocket(m_socket, addr.getAddress());
sendSocketEvent(getConnectedEvent());
onConnected();
@ -281,15 +281,13 @@ CTCPSocket::init()
m_readable = false;
m_writable = false;
try {
// make socket non-blocking
// FIXME -- check for error
ARCH->setBlockingOnSocket(m_socket, false);
// turn off Nagle algorithm. we send lots of very short messages
// that should be sent without (much) delay. for example, the
// mouse motion messages are much less useful if they're delayed.
// FIXME -- the client should do this
try {
ARCH->setNoDelayOnSocket(m_socket, true);
}
catch (XArchNetwork& e) {

View File

@ -24,11 +24,13 @@ CEvent::Type IDataSocket::s_failedEvent = CEvent::kUnknown;
CEvent::Type
IDataSocket::getConnectedEvent()
{
return CEvent::registerTypeOnce(s_connectedEvent);
return CEvent::registerTypeOnce(s_connectedEvent,
"IDataSocket::connected");
}
CEvent::Type
IDataSocket::getConnectionFailedEvent()
{
return CEvent::registerTypeOnce(s_failedEvent);
return CEvent::registerTypeOnce(s_failedEvent,
"IDataSocket::failed");
}

View File

@ -23,5 +23,6 @@ CEvent::Type IListenSocket::s_connectingEvent = CEvent::kUnknown;
CEvent::Type
IListenSocket::getConnectingEvent()
{
return CEvent::registerTypeOnce(s_connectingEvent);
return CEvent::registerTypeOnce(s_connectingEvent,
"IListenSocket::connecting");
}

View File

@ -23,5 +23,6 @@ CEvent::Type ISocket::s_disconnectedEvent = CEvent::kUnknown;
CEvent::Type
ISocket::getDisconnectedEvent()
{
return CEvent::registerTypeOnce(s_disconnectedEvent);
return CEvent::registerTypeOnce(s_disconnectedEvent,
"ISocket::disconnected");
}

View File

@ -12,9 +12,10 @@
* GNU General Public License for more details.
*/
#include "CXWindowsEventQueue.h"
#include "CEvent.h"
#include "CXWindowsEventQueueBuffer.h"
#include "CThread.h"
#include "CEvent.h"
#include "IEventQueue.h"
#if UNIX_LIKE
# if HAVE_POLL
# include <sys/poll.h>
@ -42,52 +43,27 @@ class CEventQueueTimer { };
//
// CXWindowsEventQueue
// CXWindowsEventQueueBuffer
//
CXWindowsEventQueue::CXWindowsEventQueue(Display* display) :
m_display(display)
CXWindowsEventQueueBuffer::CXWindowsEventQueueBuffer(
Display* display, Window window) :
m_display(display),
m_window(window)
{
assert(m_display != NULL);
assert(m_window != None);
m_userEvent = XInternAtom(m_display, "SYNERGY_USER_EVENT", False);
XSetWindowAttributes attr;
m_window = XCreateWindow(m_display, DefaultRootWindow(m_display),
0, 0, 1, 1, 0, 0, InputOnly, CopyFromParent,
0, &attr);
}
CXWindowsEventQueue::~CXWindowsEventQueue()
CXWindowsEventQueueBuffer::~CXWindowsEventQueueBuffer()
{
XDestroyWindow(m_display, m_window);
// do nothing
}
void
CXWindowsEventQueue::processSystemEvent(CEvent& event)
{
event = CEvent(CEvent::kSystem, getSystemTarget(), &m_event);
}
void
CXWindowsEventQueue::processClientMessage(CEvent& event)
{
assert(m_event.xany.type == ClientMessage);
// handle user events specially
if (m_event.xclient.message_type == m_userEvent) {
// get event data
CEventData data = removeEventData(m_event.xclient.data.l[1]);
// create event
event = CEvent(static_cast<size_t>(m_event.xclient.data.l[0]),
data.first, data.second);
}
else {
processSystemEvent(event);
}
}
void
CXWindowsEventQueue::waitForEvent(double dtimeout)
CXWindowsEventQueueBuffer::waitForEvent(double dtimeout)
{
// use poll() to wait for a message from the X server or for timeout.
// this is a good deal more efficient than polling and sleeping.
@ -132,25 +108,27 @@ CXWindowsEventQueue::waitForEvent(double dtimeout)
CThread::testCancel();
}
bool
CXWindowsEventQueue::doGetEvent(CEvent& event)
IEventQueueBuffer::Type
CXWindowsEventQueueBuffer::getEvent(CEvent& event, UInt32& dataID)
{
// get next event
XNextEvent(m_display, &m_event);
// process event
if (m_event.xany.type == ClientMessage) {
processClientMessage(event);
if (m_event.xany.type == ClientMessage &&
m_event.xclient.message_type == m_userEvent) {
dataID = static_cast<UInt32>(m_event.xclient.data.l[0]);
return kUser;
}
else {
processSystemEvent(event);
event = CEvent(CEvent::kSystem,
IEventQueue::getSystemTarget(), &m_event);
return kSystem;
}
return true;
}
bool
CXWindowsEventQueue::doAddEvent(CEvent::Type type, UInt32 dataID)
CXWindowsEventQueueBuffer::addEvent(UInt32 dataID)
{
// send ourself a message
XEvent xevent;
@ -158,25 +136,29 @@ CXWindowsEventQueue::doAddEvent(CEvent::Type type, UInt32 dataID)
xevent.xclient.window = m_window;
xevent.xclient.message_type = m_userEvent;
xevent.xclient.format = 32;
xevent.xclient.data.l[0] = static_cast<long>(type);
xevent.xclient.data.l[1] = static_cast<long>(dataID);
return (XSendEvent(m_display, m_window, False, 0, &xevent) != 0);
xevent.xclient.data.l[0] = static_cast<long>(dataID);
if (XSendEvent(m_display, m_window, False, 0, &xevent) == 0) {
return false;
}
// force waitForEvent() to return
XFlush(m_display);
}
bool
CXWindowsEventQueue::doIsEmpty() const
CXWindowsEventQueueBuffer::isEmpty() const
{
return (XPending(m_display) == 0);
}
CEventQueueTimer*
CXWindowsEventQueue::doNewTimer(double, bool) const
CXWindowsEventQueueBuffer::newTimer(double, bool) const
{
return new CEventQueueTimer();
}
void
CXWindowsEventQueue::doDeleteTimer(CEventQueueTimer* timer) const
CXWindowsEventQueueBuffer::deleteTimer(CEventQueueTimer* timer) const
{
delete timer;
}

View File

@ -12,44 +12,30 @@
* GNU General Public License for more details.
*/
#ifndef CXWINDOWSEVENTQUEUE_H
#define CXWINDOWSEVENTQUEUE_H
#ifndef CXWINDOWSEVENTQUEUEBUFFER_H
#define CXWINDOWSEVENTQUEUEBUFFER_H
#include "CEventQueue.h"
#include "IEventQueueBuffer.h"
#if defined(X_DISPLAY_MISSING)
# error X11 is required to build synergy
#else
# include <X11/Xlib.h>
#endif
//! Event queue for X11
class CXWindowsEventQueue : public CEventQueue {
//! Event queue buffer for X11
class CXWindowsEventQueueBuffer : public IEventQueueBuffer {
public:
CXWindowsEventQueue(Display*);
virtual ~CXWindowsEventQueue();
CXWindowsEventQueueBuffer(Display*, Window);
virtual ~CXWindowsEventQueueBuffer();
//! @name manipulators
//@{
//@}
//! @name accessors
//@{
//@}
protected:
// CEventQueue overrides
// IEventQueueBuffer overrides
virtual void waitForEvent(double timeout);
virtual bool doGetEvent(CEvent& event);
virtual bool doAddEvent(CEvent::Type type, UInt32 dataID);
virtual bool doIsEmpty() const;
virtual Type getEvent(CEvent& event, UInt32& dataID);
virtual bool addEvent(UInt32 dataID);
virtual bool isEmpty() const;
virtual CEventQueueTimer*
doNewTimer(double duration, bool oneShot) const;
virtual void doDeleteTimer(CEventQueueTimer*) const;
private:
void processSystemEvent(CEvent& event);
void processClientMessage(CEvent& event);
newTimer(double duration, bool oneShot) const;
virtual void deleteTimer(CEventQueueTimer*) const;
private:
Display* m_display;

File diff suppressed because it is too large Load Diff

View File

@ -17,9 +17,6 @@
#include "IPlatformScreen.h"
#include "CXWindowsKeyMapper.h"
#include "CMutex.h"
#include "CStopwatch.h"
#include "CPriorityQueue.h"
#include "stdvector.h"
#if defined(X_DISPLAY_MISSING)
# error X11 is required to build synergy
@ -29,43 +26,22 @@
class CXWindowsClipboard;
class CXWindowsScreenSaver;
class IJob;
class IScreenReceiver;
class IPrimaryScreenReceiver;
//! Implementation of IPlatformScreen for X11
class CXWindowsScreen : public IPlatformScreen {
public:
CXWindowsScreen(IScreenReceiver*, IPrimaryScreenReceiver*);
CXWindowsScreen(bool isPrimary);
virtual ~CXWindowsScreen();
//! @name manipulators
//@{
//! Add timer
/*!
Add a job to invoke every timeout seconds. The job is called
with the display locked. If a job timeout expires twice or
more before the job can be called then the job is called just
once. The caller retains ownership of the job.
*/
void addTimer(IJob*, double timeout);
//! Remove timer
/*!
Remove a job. The caller retains ownership of the job.
*/
void removeTimer(IJob*);
//@}
// IPlatformScreen overrides
virtual void open(IKeyState*);
virtual void close();
virtual void setKeyState(IKeyState*);
virtual void enable();
virtual void disable();
virtual void mainLoop();
virtual void exitMainLoop();
virtual void enter();
virtual bool leave();
virtual bool setClipboard(ClipboardID, const IClipboard*);
@ -76,17 +52,22 @@ public:
virtual void resetOptions();
virtual void setOptions(const COptionsList& options);
virtual void updateKeys();
virtual void setSequenceNumber(UInt32);
virtual bool isPrimary() const;
virtual bool getClipboard(ClipboardID, IClipboard*) const;
virtual void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const;
virtual void getCursorPos(SInt32&, SInt32&) const;
// IScreen overrides
virtual void* getEventTarget() const;
virtual bool getClipboard(ClipboardID id, IClipboard*) const;
virtual void getShape(SInt32& x, SInt32& y,
SInt32& width, SInt32& height) const;
virtual void getCursorPos(SInt32& x, SInt32& y) const;
// IPrimaryScreen overrides
virtual void reconfigure(UInt32 activeSides);
virtual void warpCursor(SInt32 x, SInt32 y);
virtual UInt32 addOneShotTimer(double timeout);
virtual SInt32 getJumpZoneSize() const;
virtual bool isAnyMouseButtonDown() const;
virtual void getCursorCenter(SInt32& x, SInt32& y) const;
virtual const char* getKeyName(KeyButton) const;
// ISecondaryScreen overrides
@ -101,18 +82,16 @@ public:
bool isAutoRepeat) const;
private:
// process events before dispatching to receiver
void onEvent(XEvent* event);
// event sending
void sendEvent(CEvent::Type, void* = NULL);
void sendClipboardEvent(CEvent::Type, ClipboardID);
// event handling
void handleSystemEvent(const CEvent&, void*);
// create the transparent cursor
Cursor createBlankCursor() const;
// remove a timer without locking
void removeTimerNoLock(IJob*);
// process timers
bool processTimers();
// determine the clipboard from the X selection. returns
// kClipboardEnd if no such clipboard.
ClipboardID getClipboardID(Atom selection) const;
@ -125,40 +104,10 @@ private:
void destroyClipboardRequest(Window window);
// X I/O error handler
void onError();
static int ioErrorHandler(Display*);
private:
// a timer priority queue element
class CTimer {
public:
CTimer(IJob* job, double startTime, double resetTime);
~CTimer();
// manipulators
void run();
void reset();
CTimer& operator-=(double);
// accessors
IJob* getJob() const
{
return m_job;
}
operator double() const;
bool operator<(const CTimer&) const;
private:
IJob* m_job;
double m_timeout;
double m_time;
double m_startTime;
};
class CKeyEventInfo {
public:
int m_event;
@ -167,9 +116,9 @@ private:
KeyCode m_keycode;
};
bool isQuitEvent(XEvent*) const;
Window createWindow() const;
Display* openDisplay() const;
void saveShape();
Window openWindow() const;
void openIM();
bool grabMouseAndKeyboard();
@ -193,21 +142,13 @@ private:
static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg);
private:
typedef CPriorityQueue<CTimer> CTimerPriorityQueue;
// true if screen is being used as a primary screen, false otherwise
bool m_isPrimary;
// X is not thread safe
CMutex m_mutex;
Display* m_display;
Window m_root;
Window m_window;
IScreenReceiver* m_receiver;
IPrimaryScreenReceiver* m_primaryReceiver;
// true if mouse has entered the screen
bool m_isOnScreen;
@ -230,20 +171,11 @@ private:
// clipboards
CXWindowsClipboard* m_clipboard[kClipboardEnd];
// the quit message
Atom m_atomQuit;
UInt32 m_sequenceNumber;
// screen saver stuff
CXWindowsScreenSaver* m_screensaver;
bool m_screensaverNotify;
Atom m_atomScreensaver;
// timers, the stopwatch used to time, and a mutex for the timers
CTimerPriorityQueue m_timers;
CStopwatch m_time;
CMutex m_timersMutex;
CTimer* m_oneShotTimer;
// logical to physical button mapping. m_buttons[i] gives the
// physical button for logical button i+1.

View File

@ -13,10 +13,12 @@
*/
#include "CXWindowsScreenSaver.h"
#include "CXWindowsScreen.h"
#include "CXWindowsUtil.h"
#include "IPlatformScreen.h"
#include "CLog.h"
#include "TMethodJob.h"
#include "CEvent.h"
#include "IEventQueue.h"
#include "TMethodEventJob.h"
#include <X11/Xatom.h>
#if defined(HAVE_X11_EXTENSIONS_XTEST_H)
# include <X11/extensions/XTest.h>
@ -29,20 +31,16 @@
//
CXWindowsScreenSaver::CXWindowsScreenSaver(
CXWindowsScreen* screen, Display* display) :
m_screen(screen),
Display* display, Window window, void* eventTarget) :
m_display(display),
m_notify(None),
m_xscreensaverSink(window),
m_eventTarget(eventTarget),
m_xscreensaver(None),
m_xscreensaverActive(false),
m_disabled(false),
m_suppressDisable(false),
m_disableJobInstalled(false)
m_disableTimer(NULL)
{
// screen saver disable callback
m_disableJob = new TMethodJob<CXWindowsScreenSaver>(this,
&CXWindowsScreenSaver::disableCallback);
// get atoms
m_atomScreenSaver = XInternAtom(m_display,
"SCREENSAVER", False);
@ -52,24 +50,6 @@ CXWindowsScreenSaver::CXWindowsScreenSaver(
"ACTIVATE", False);
m_atomScreenSaverDeactivate = XInternAtom(m_display,
"DEACTIVATE", False);
m_atomSynergyScreenSaver = XInternAtom(m_display,
"SYNERGY_SCREENSAVER", False);
// create dummy window to receive xscreensaver responses. this
// shouldn't be necessary (we should be able to send responses
// to None) but it doesn't hurt.
XSetWindowAttributes attr;
attr.event_mask = 0;//PropertyChangeMask;
attr.do_not_propagate_mask = 0;
attr.override_redirect = True;
m_xscreensaverSink = XCreateWindow(m_display,
DefaultRootWindow(m_display),
0, 0, 1, 1, 0, 0,
InputOnly, CopyFromParent,
CWDontPropagate | CWEventMask |
CWOverrideRedirect,
&attr);
LOG((CLOG_DEBUG "xscreensaver sink window is 0x%08x", m_xscreensaverSink));
// watch top-level windows for changes
{
@ -94,28 +74,39 @@ CXWindowsScreenSaver::CXWindowsScreenSaver(
// get the built-in settings
XGetScreenSaver(m_display, &m_timeout, &m_interval,
&m_preferBlanking, &m_allowExposures);
// install disable timer event handler
EVENTQUEUE->adoptHandler(CEvent::kTimer, this,
new TMethodEventJob<CXWindowsScreenSaver>(this,
&CXWindowsScreenSaver::handleDisableTimer));
}
CXWindowsScreenSaver::~CXWindowsScreenSaver()
{
// clear watch list
clearWatchForXScreenSaver();
// stop watching root for events
CXWindowsUtil::CErrorLock lock(m_display);
Window root = DefaultRootWindow(m_display);
XSelectInput(m_display, root, m_rootEventMask);
// destroy dummy sink window
XDestroyWindow(m_display, m_xscreensaverSink);
// done with disable job
m_screen->removeTimer(m_disableJob);
delete m_disableJob;
if (m_disableTimer != NULL) {
EVENTQUEUE->deleteTimer(m_disableTimer);
}
EVENTQUEUE->removeHandler(CEvent::kTimer, this);
if (m_display != NULL) {
XSetScreenSaver(m_display, m_timeout, m_interval,
m_preferBlanking, m_allowExposures);
clearWatchForXScreenSaver();
CXWindowsUtil::CErrorLock lock(m_display);
XSelectInput(m_display, DefaultRootWindow(m_display), m_rootEventMask);
}
}
void
CXWindowsScreenSaver::destroy()
{
m_display = NULL;
delete this;
}
bool
CXWindowsScreenSaver::onPreDispatch(const XEvent* xevent)
CXWindowsScreenSaver::handleXEvent(const XEvent* xevent)
{
switch (xevent->type) {
case CreateNotify:
@ -175,18 +166,12 @@ CXWindowsScreenSaver::onPreDispatch(const XEvent* xevent)
return false;
}
void
CXWindowsScreenSaver::setNotify(Window notify)
{
m_notify = notify;
}
void
CXWindowsScreenSaver::enable()
{
// for xscreensaver
m_disabled = false;
updateDisableJob();
updateDisableTimer();
// for built-in X screen saver
XSetScreenSaver(m_display, m_timeout, m_interval,
@ -198,7 +183,7 @@ CXWindowsScreenSaver::disable()
{
// for xscreensaver
m_disabled = true;
updateDisableJob();
updateDisableTimer();
// use built-in X screen saver
XGetScreenSaver(m_display, &m_timeout, &m_interval,
@ -213,7 +198,7 @@ CXWindowsScreenSaver::activate()
{
// remove disable job timer
m_suppressDisable = true;
updateDisableJob();
updateDisableTimer();
// try xscreensaver
findXScreenSaver();
@ -231,7 +216,7 @@ CXWindowsScreenSaver::deactivate()
{
// reinstall disable job timer
m_suppressDisable = false;
updateDisableJob();
updateDisableTimer();
// try xscreensaver
findXScreenSaver();
@ -256,27 +241,6 @@ CXWindowsScreenSaver::isActive() const
return false;
}
void
CXWindowsScreenSaver::sendNotify(bool activated)
{
if (m_notify != None) {
XEvent event;
event.xclient.type = ClientMessage;
event.xclient.display = m_display;
event.xclient.window = m_notify;
event.xclient.message_type = m_atomSynergyScreenSaver;
event.xclient.format = 32;
event.xclient.data.l[0] = activated ? 1 : 0;
event.xclient.data.l[1] = 0;
event.xclient.data.l[2] = 0;
event.xclient.data.l[3] = 0;
event.xclient.data.l[4] = 0;
CXWindowsUtil::CErrorLock lock(m_display);
XSendEvent(m_display, m_notify, False, 0, &event);
}
}
bool
CXWindowsScreenSaver::findXScreenSaver()
{
@ -351,9 +315,18 @@ CXWindowsScreenSaver::setXScreenSaverActive(bool activated)
// from activating since that'll just pop up the password
// dialog if locking is enabled.
m_suppressDisable = activated;
updateDisableJob();
updateDisableTimer();
sendNotify(activated);
if (activated) {
EVENTQUEUE->addEvent(CEvent(
IPlatformScreen::getScreensaverActivatedEvent(),
m_eventTarget));
}
else {
EVENTQUEUE->addEvent(CEvent(
IPlatformScreen::getScreensaverDeactivatedEvent(),
m_eventTarget));
}
}
}
@ -441,23 +414,20 @@ CXWindowsScreenSaver::addWatchXScreenSaver(Window window)
}
void
CXWindowsScreenSaver::updateDisableJob()
CXWindowsScreenSaver::updateDisableTimer()
{
assert(m_disableJob != NULL);
if (m_disabled && !m_suppressDisable && !m_disableJobInstalled) {
if (m_disabled && !m_suppressDisable && m_disableTimer == NULL) {
// 5 seconds should be plenty often to suppress the screen saver
m_disableJobInstalled = true;
m_screen->addTimer(m_disableJob, 5.0);
m_disableTimer = EVENTQUEUE->newTimer(5.0, this);
}
else if ((!m_disabled || m_suppressDisable) && m_disableJobInstalled) {
m_disableJobInstalled = false;
m_screen->removeTimer(m_disableJob);
else if ((!m_disabled || m_suppressDisable) && m_disableTimer != NULL) {
EVENTQUEUE->deleteTimer(m_disableTimer);
m_disableTimer = NULL;
}
}
void
CXWindowsScreenSaver::disableCallback(void*)
CXWindowsScreenSaver::handleDisableTimer(const CEvent&, void*)
{
// send fake mouse motion directly to xscreensaver
if (m_xscreensaver != None) {

View File

@ -23,16 +23,13 @@
# include <X11/Xlib.h>
#endif
class IJob;
class CXWindowsScreen;
class CEvent;
class CEventQueueTimer;
//! X11 screen saver implementation
class CXWindowsScreenSaver : public IScreenSaver {
public:
// note -- the caller must ensure that Display* passed to c'tor isn't
// being used in another call to Xlib when calling any method on this
// object (including during the c'tor and d'tor).
CXWindowsScreenSaver(CXWindowsScreen*, Display*);
CXWindowsScreenSaver(Display*, Window, void* eventTarget);
virtual ~CXWindowsScreenSaver();
//! @name manipulators
@ -40,22 +37,17 @@ public:
//! Event filtering
/*!
Called for each event before event translation and dispatch. Return
true to skip translation and dispatch. Subclasses should call the
superclass's version first and return true if it returns true.
Should be called for each system event before event translation and
dispatch. Returns true to skip translation and dispatch.
*/
bool onPreDispatch(const XEvent*);
bool handleXEvent(const XEvent*);
//! Set notify target
//! Destroy without the display
/*!
Tells this object to send a ClientMessage to the given window
when the screen saver activates or deactivates. Only one window
can be notified at a time. The message type is the "SCREENSAVER"
atom, the format is 32, and the data.l[0] member is non-zero
if activated, zero if deactivated. Pass None to disable
notification.
Tells this object to delete itself without using the X11 display.
It may leak some resources as a result.
*/
void setNotify(Window);
void destroy();
//@}
@ -67,9 +59,6 @@ public:
virtual bool isActive() const;
private:
// send a notification
void sendNotify(bool activated);
// find and set the running xscreensaver's window. returns true iff
// found.
bool findXScreenSaver();
@ -98,25 +87,22 @@ private:
void addWatchXScreenSaver(Window window);
// install/uninstall the job used to suppress the screensaver
void updateDisableJob();
void updateDisableTimer();
// called periodically to prevent the screen saver from starting
void disableCallback(void*);
void handleDisableTimer(const CEvent&, void*);
private:
typedef std::map<Window, long> CWatchList;
// the event loop object
CXWindowsScreen* m_screen;
// the X display
Display* m_display;
// old event mask on root window
long m_rootEventMask;
// window to receive xscreensaver repsonses
Window m_xscreensaverSink;
// window to notify on screen saver activation/deactivation
Window m_notify;
// the target for the events we generate
void* m_eventTarget;
// xscreensaver's window
Window m_xscreensaver;
@ -124,8 +110,8 @@ private:
// xscreensaver activation state
bool m_xscreensaverActive;
// dummy window to receive xscreensaver repsonses
Window m_xscreensaverSink;
// old event mask on root window
long m_rootEventMask;
// potential xscreensaver windows being watched
CWatchList m_watchWindows;
@ -135,7 +121,6 @@ private:
Atom m_atomScreenSaverVersion;
Atom m_atomScreenSaverActivate;
Atom m_atomScreenSaverDeactivate;
Atom m_atomSynergyScreenSaver;
// built-in screen saver settings
int m_timeout;
@ -151,11 +136,8 @@ private:
// to activate the screen saver even if disabled.
bool m_suppressDisable;
// true iff the disabled job timer is installed
bool m_disableJobInstalled;
// the job used to invoke disableCallback
IJob* m_disableJob;
// the disable timer (NULL if not installed)
CEventQueueTimer* m_disableTimer;
};
#endif

View File

@ -49,7 +49,7 @@ libplatform_a_SOURCES = \
CXWindowsClipboardTextConverter.cpp \
CXWindowsClipboardUCS2Converter.cpp \
CXWindowsClipboardUTF8Converter.cpp \
CXWindowsEventQueue.cpp \
CXWindowsEventQueueBuffer.cpp \
CXWindowsKeyMapper.cpp \
CXWindowsScreen.cpp \
CXWindowsScreenSaver.cpp \
@ -58,7 +58,7 @@ libplatform_a_SOURCES = \
CXWindowsClipboardTextConverter.h \
CXWindowsClipboardUCS2Converter.h \
CXWindowsClipboardUTF8Converter.h \
CXWindowsEventQueue.h \
CXWindowsEventQueueBuffer.h \
CXWindowsKeyMapper.h \
CXWindowsScreen.h \
CXWindowsScreenSaver.h \

View File

@ -0,0 +1,189 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CClientListener.h"
#include "CClientProxy.h"
#include "CClientProxyUnknown.h"
#include "CPacketStreamFilter.h"
#include "IStreamFilterFactory.h"
#include "IDataSocket.h"
#include "IListenSocket.h"
#include "ISocketFactory.h"
#include "XSocket.h"
#include "CLog.h"
#include "IEventQueue.h"
#include "TMethodEventJob.h"
//
// CClientListener
//
CEvent::Type CClientListener::s_connectedEvent = CEvent::kUnknown;
CClientListener::CClientListener(const CNetworkAddress& address,
ISocketFactory* socketFactory,
IStreamFilterFactory* streamFilterFactory) :
m_socketFactory(socketFactory),
m_streamFilterFactory(streamFilterFactory)
{
assert(m_socketFactory != NULL);
try {
// create listen socket
m_listen = m_socketFactory->createListen();
// bind listen address
LOG((CLOG_DEBUG1 "binding listen socket"));
m_listen->bind(address);
}
catch (XSocketAddressInUse& e) {
delete m_listen;
delete m_socketFactory;
delete m_streamFilterFactory;
throw;
}
catch (XBase& e) {
delete m_listen;
delete m_socketFactory;
delete m_streamFilterFactory;
throw;
}
LOG((CLOG_DEBUG1 "listening for clients"));
// setup event handler
EVENTQUEUE->adoptHandler(IListenSocket::getConnectingEvent(), m_listen,
new TMethodEventJob<CClientListener>(this,
&CClientListener::handleClientConnecting));
}
CClientListener::~CClientListener()
{
LOG((CLOG_DEBUG1 "stop listening for clients"));
// discard already connected clients
for (CNewClients::iterator index = m_newClients.begin();
index != m_newClients.end(); ++index) {
CClientProxyUnknown* client = *index;
EVENTQUEUE->removeHandler(client);
delete client;
}
// discard waiting clients
CClientProxy* client = getNextClient();
while (client != NULL) {
delete client;
client = getNextClient();
}
EVENTQUEUE->removeHandler(IListenSocket::getConnectingEvent(), m_listen);
delete m_listen;
delete m_socketFactory;
delete m_streamFilterFactory;
}
CClientProxy*
CClientListener::getNextClient()
{
CClientProxy* client = NULL;
if (!m_waitingClients.empty()) {
client = m_waitingClients.front();
m_waitingClients.pop_front();
EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(), client);
}
return client;
}
CEvent::Type
CClientListener::getConnectedEvent()
{
return CEvent::registerTypeOnce(s_connectedEvent,
"CClientListener::connected");
}
void
CClientListener::handleClientConnecting(const CEvent&, void*)
{
// accept client connection
IStream* stream = m_listen->accept();
if (stream == NULL) {
return;
}
LOG((CLOG_NOTE "accepted client connection"));
// filter socket messages, including a packetizing filter
if (m_streamFilterFactory != NULL) {
stream = m_streamFilterFactory->create(stream, true);
}
stream = new CPacketStreamFilter(stream, true);
// create proxy for unknown client
CClientProxyUnknown* client = new CClientProxyUnknown(stream, 30.0);
m_newClients.insert(client);
// watch for events from unknown client
EVENTQUEUE->adoptHandler(CClientProxyUnknown::getSuccessEvent(), client,
new TMethodEventJob<CClientListener>(this,
&CClientListener::handleUnknownClient, client));
EVENTQUEUE->adoptHandler(CClientProxyUnknown::getFailureEvent(), client,
new TMethodEventJob<CClientListener>(this,
&CClientListener::handleUnknownClient, client));
}
void
CClientListener::handleUnknownClient(const CEvent&, void* vclient)
{
CClientProxyUnknown* unknownClient =
reinterpret_cast<CClientProxyUnknown*>(vclient);
// we should have the client in our new client list
assert(m_newClients.count(unknownClient) == 1);
// get the real client proxy and install it
CClientProxy* client = unknownClient->orphanClientProxy();
if (client != NULL) {
// handshake was successful
m_waitingClients.push_back(client);
EVENTQUEUE->addEvent(CEvent(getConnectedEvent(), this));
// watch for client to disconnect while it's in our queue
EVENTQUEUE->adoptHandler(CClientProxy::getDisconnectedEvent(), client,
new TMethodEventJob<CClientListener>(this,
&CClientListener::handleClientDisconnected,
client));
}
// now finished with unknown client
EVENTQUEUE->removeHandler(CClientProxyUnknown::getSuccessEvent(), client);
EVENTQUEUE->removeHandler(CClientProxyUnknown::getFailureEvent(), client);
m_newClients.erase(unknownClient);
delete unknownClient;
}
void
CClientListener::handleClientDisconnected(const CEvent&, void* vclient)
{
CClientProxy* client = reinterpret_cast<CClientProxy*>(vclient);
// find client in waiting clients queue
for (CWaitingClients::iterator i = m_waitingClients.begin(),
n = m_waitingClients.end(); i != n; ++i) {
if (*i == client) {
m_waitingClients.erase(i);
EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(),
client);
delete client;
break;
}
}
}

View File

@ -0,0 +1,76 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CCLIENTLISTENER_H
#define CCLIENTLISTENER_H
#include "CConfig.h"
#include "CEvent.h"
#include "stddeque.h"
#include "stdset.h"
class CClientProxy;
class CClientProxyUnknown;
class CNetworkAddress;
class IListenSocket;
class ISocketFactory;
class IStreamFilterFactory;
class CClientListener {
public:
// The factories are adopted.
CClientListener(const CNetworkAddress&,
ISocketFactory*, IStreamFilterFactory*);
~CClientListener();
//! @name accessors
//@{
//! Get next connected client
/*!
Returns the next connected client and removes it from the internal
list. The client is responsible for deleting the returned client.
Returns NULL if no clients are available.
*/
CClientProxy* getNextClient();
//! Get connected event type
/*!
Returns the connected event type. This is sent whenever a
a client connects.
*/
static CEvent::Type getConnectedEvent();
//@}
private:
// client connection event handlers
void handleClientConnecting(const CEvent&, void*);
void handleUnknownClient(const CEvent&, void*);
void handleClientDisconnected(const CEvent&, void*);
private:
typedef std::set<CClientProxyUnknown*> CNewClients;
typedef std::deque<CClientProxy*> CWaitingClients;
IListenSocket* m_listen;
ISocketFactory* m_socketFactory;
IStreamFilterFactory* m_streamFilterFactory;
CNewClients m_newClients;
CWaitingClients m_waitingClients;
static CEvent::Type s_connectedEvent;
};
#endif

View File

@ -13,15 +13,18 @@
*/
#include "CClientProxy.h"
#include "CProtocolUtil.h"
#include "IStream.h"
#include "CLog.h"
//
// CClientProxy
//
CClientProxy::CClientProxy(IServer* server,
const CString& name, IStream* stream) :
m_server(server),
CEvent::Type CClientProxy::s_readyEvent = CEvent::kUnknown;
CEvent::Type CClientProxy::s_disconnectedEvent = CEvent::kUnknown;
CClientProxy::CClientProxy(const CString& name, IStream* stream) :
m_name(name),
m_stream(stream)
{
@ -33,10 +36,14 @@ CClientProxy::~CClientProxy()
delete m_stream;
}
IServer*
CClientProxy::getServer() const
void
CClientProxy::close(const char* msg)
{
return m_server;
LOG((CLOG_DEBUG1 "send close \"%s\" to \"%s\"", msg, getName().c_str()));
CProtocolUtil::writef(getStream(), msg);
// force the close to be sent before we return
getStream()->flush();
}
IStream*
@ -51,8 +58,22 @@ CClientProxy::getName() const
return m_name;
}
const CMutex*
CClientProxy::getMutex() const
CEvent::Type
CClientProxy::getReadyEvent()
{
return &m_mutex;
return CEvent::registerTypeOnce(s_readyEvent,
"CClientProxy::ready");
}
CEvent::Type
CClientProxy::getDisconnectedEvent()
{
return CEvent::registerTypeOnce(s_disconnectedEvent,
"CClientProxy::disconnected");
}
void*
CClientProxy::getEventTarget() const
{
return static_cast<IScreen*>(const_cast<CClientProxy*>(this));
}

View File

@ -16,11 +16,10 @@
#define CCLIENTPROXY_H
#include "IClient.h"
#include "CMutex.h"
#include "CEvent.h"
#include "CString.h"
class IStream;
class IServer;
//! Generic proxy for client
class CClientProxy : public IClient {
@ -28,17 +27,21 @@ public:
/*!
\c name is the name of the client.
*/
CClientProxy(IServer* server, const CString& name, IStream* adoptedStream);
CClientProxy(const CString& name, IStream* adoptedStream);
~CClientProxy();
//! @name accessors
//! @name manipulators
//@{
//! Get server
//! Disconnect
/*!
Returns the server passed to the c'tor.
Ask the client to disconnect, using \p msg as the reason.
*/
IServer* getServer() const;
void close(const char* msg);
//@}
//! @name accessors
//@{
//! Get stream
/*!
@ -46,12 +49,31 @@ public:
*/
IStream* getStream() const;
//! Get ready event type
/*!
Returns the ready event type. This is sent when the client has
completed the initial handshake. Until it is sent, the client is
not fully connected.
*/
static CEvent::Type getReadyEvent();
//! Get disconnect event type
/*!
Returns the disconnect event type. This is sent when the client
disconnects or is disconnected. The target is getEventTarget().
*/
static CEvent::Type getDisconnectedEvent();
//@}
// IScreen
virtual void* getEventTarget() const;
virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0;
virtual void getShape(SInt32& x, SInt32& y,
SInt32& width, SInt32& height) const = 0;
virtual void getCursorPos(SInt32& x, SInt32& y) const = 0;
// IClient overrides
virtual void open() = 0;
virtual void mainLoop() = 0;
virtual void close() = 0;
virtual void enter(SInt32 xAbs, SInt32 yAbs,
UInt32 seqNum, KeyModifierMask mask,
bool forScreensaver) = 0;
@ -71,25 +93,13 @@ public:
virtual void resetOptions() = 0;
virtual void setOptions(const COptionsList& options) = 0;
virtual CString getName() const;
virtual SInt32 getJumpZoneSize() const = 0;
virtual void getShape(SInt32& x, SInt32& y,
SInt32& width, SInt32& height) const = 0;
virtual void getCursorPos(SInt32& x, SInt32& y) const = 0;
virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0;
protected:
//! Get mutex
/*!
Returns the mutex for this object. Subclasses should use this
mutex to protect their data.
*/
const CMutex* getMutex() const;
private:
CMutex m_mutex;
IServer* m_server;
CString m_name;
IStream* m_stream;
static CEvent::Type s_readyEvent;
static CEvent::Type s_disconnectedEvent;
};
#endif

View File

@ -13,12 +13,9 @@
*/
#include "CClientProxy1_0.h"
#include "CServer.h"
#include "CClipboard.h"
#include "CProtocolUtil.h"
#include "XSynergy.h"
#include "IStream.h"
#include "CLock.h"
#include "CLog.h"
#include "IEventQueue.h"
#include "TMethodEventJob.h"
@ -28,16 +25,12 @@
// CClientProxy1_0
//
CClientProxy1_0::CClientProxy1_0(IServer* server,
const CString& name, IStream* stream) :
CClientProxy(server, name, stream),
CClientProxy1_0::CClientProxy1_0(const CString& name, IStream* stream) :
CClientProxy(name, stream),
m_heartbeatAlarm(kHeartRate * kHeartBeatsUntilDeath),
m_heartbeatTimer(NULL)
m_heartbeatTimer(NULL),
m_parser(&CClientProxy1_0::parseHandshakeMessage)
{
for (UInt32 i = 0; i < kClipboardEnd; ++i) {
m_clipboardDirty[i] = true;
}
// install event handlers
EVENTQUEUE->adoptHandler(IStream::getInputReadyEvent(),
stream->getEventTarget(),
@ -59,7 +52,8 @@ CClientProxy1_0::CClientProxy1_0(IServer* server,
new TMethodEventJob<CClientProxy1_0>(this,
&CClientProxy1_0::handleFlatline, NULL));
// FIXME -- open() replacement must install initial heartbeat timer
LOG((CLOG_DEBUG1 "querying client \"%s\" info", getName().c_str()));
CProtocolUtil::writef(getStream(), kMsgQInfo);
}
CClientProxy1_0::~CClientProxy1_0()
@ -70,11 +64,9 @@ CClientProxy1_0::~CClientProxy1_0()
void
CClientProxy1_0::disconnect()
{
CLock lock(getMutex());
removeHandlers();
// FIXME -- send disconnect event (server should be listening for this)
getStream()->flush();
getStream()->close();
EVENTQUEUE->addEvent(CEvent(getDisconnectedEvent(), getEventTarget()));
}
void
@ -98,7 +90,6 @@ CClientProxy1_0::removeHandlers()
void
CClientProxy1_0::addHeartbeatTimer()
{
CLock lock(getMutex());
if (m_heartbeatAlarm > 0.0) {
m_heartbeatTimer = EVENTQUEUE->newOneShotTimer(m_heartbeatAlarm, this);
}
@ -107,7 +98,6 @@ CClientProxy1_0::addHeartbeatTimer()
void
CClientProxy1_0::removeHeartbeatTimer()
{
CLock lock(getMutex());
if (m_heartbeatTimer != NULL) {
EVENTQUEUE->deleteTimer(m_heartbeatTimer);
m_heartbeatTimer = NULL;
@ -130,7 +120,7 @@ CClientProxy1_0::handleData(const CEvent&, void*)
// parse message
LOG((CLOG_DEBUG2 "msg from \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3]));
if (!parseMessage(code)) {
if (!(this->*m_parser)(code)) {
LOG((CLOG_ERR "invalid message from client \"%s\"", getName().c_str()));
disconnect();
return;
@ -145,11 +135,35 @@ CClientProxy1_0::handleData(const CEvent&, void*)
addHeartbeatTimer();
}
bool
CClientProxy1_0::parseHandshakeMessage(const UInt8* code)
{
if (memcmp(code, kMsgCNoop, 4) == 0) {
// discard no-ops
LOG((CLOG_DEBUG2 "no-op from", getName().c_str()));
return true;
}
else if (memcmp(code, kMsgDInfo, 4) == 0) {
// future messages get parsed by parseMessage
m_parser = &CClientProxy1_0::parseMessage;
if (recvInfo()) {
EVENTQUEUE->addEvent(CEvent(getReadyEvent(), getEventTarget()));
return true;
}
}
return false;
}
bool
CClientProxy1_0::parseMessage(const UInt8* code)
{
if (memcmp(code, kMsgDInfo, 4) == 0) {
return recvInfo(true);
if (recvInfo()) {
EVENTQUEUE->addEvent(
CEvent(getShapeChangedEvent(), getEventTarget()));
return true;
}
return false;
}
else if (memcmp(code, kMsgCNoop, 4) == 0) {
// discard no-ops
@ -168,14 +182,14 @@ CClientProxy1_0::parseMessage(const UInt8* code)
void
CClientProxy1_0::handleDisconnect(const CEvent&, void*)
{
LOG((CLOG_NOTE "client \"%s\" disconnected", getName().c_str()));
LOG((CLOG_NOTE "client \"%s\" has disconnected", getName().c_str()));
disconnect();
}
void
CClientProxy1_0::handleWriteError(const CEvent&, void*)
{
LOG((CLOG_ERR "error writing to client \"%s\"", getName().c_str()));
LOG((CLOG_WARN "error writing to client \"%s\"", getName().c_str()));
disconnect();
}
@ -187,44 +201,28 @@ CClientProxy1_0::handleFlatline(const CEvent&, void*)
disconnect();
}
// FIXME -- replace this
void
CClientProxy1_0::open()
bool
CClientProxy1_0::getClipboard(ClipboardID id, IClipboard* clipboard) const
{
// send request
LOG((CLOG_DEBUG1 "querying client \"%s\" info", getName().c_str()));
CProtocolUtil::writef(getStream(), kMsgQInfo);
getStream()->flush();
// wait for and verify reply
UInt8 code[4];
for (;;) {
UInt32 n = getStream()->read(code, 4);
if (n == 4) {
if (memcmp(code, kMsgCNoop, 4) == 0) {
// discard heartbeats
continue;
}
if (memcmp(code, kMsgDInfo, 4) == 0) {
break;
}
}
throw XBadClient();
CClipboard::copy(clipboard, &m_clipboard[id].m_clipboard);
return true;
}
// handle reply
recvInfo(false);
}
// FIXME -- replace this
void
CClientProxy1_0::close()
CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
{
LOG((CLOG_DEBUG1 "send close to \"%s\"", getName().c_str()));
CProtocolUtil::writef(getStream(), kMsgCClose);
x = m_info.m_x;
y = m_info.m_y;
w = m_info.m_w;
h = m_info.m_h;
}
// force the close to be sent before we return
getStream()->flush();
void
CClientProxy1_0::getCursorPos(SInt32& x, SInt32& y) const
{
assert(0 && "shouldn't be called");
x = m_info.m_mx;
y = m_info.m_my;
}
void
@ -250,10 +248,9 @@ void
CClientProxy1_0::setClipboard(ClipboardID id, const CString& data)
{
// ignore if this clipboard is already clean
CLock lock(getMutex());
if (m_clipboardDirty[id]) {
if (m_clipboard[id].m_dirty) {
// this clipboard is now clean
m_clipboardDirty[id] = false;
m_clipboard[id].m_dirty = false;
LOG((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getName().c_str(), data.size()));
CProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, &data);
@ -267,15 +264,13 @@ CClientProxy1_0::grabClipboard(ClipboardID id)
CProtocolUtil::writef(getStream(), kMsgCClipboard, id, 0);
// this clipboard is now dirty
CLock lock(getMutex());
m_clipboardDirty[id] = true;
m_clipboard[id].m_dirty = true;
}
void
CClientProxy1_0::setClipboardDirty(ClipboardID id, bool dirty)
{
CLock lock(getMutex());
m_clipboardDirty[id] = dirty;
m_clipboard[id].m_dirty = dirty;
}
void
@ -342,7 +337,6 @@ CClientProxy1_0::resetOptions()
CProtocolUtil::writef(getStream(), kMsgCResetOptions);
// reset heart rate and death
CLock lock(getMutex());
m_heartbeatAlarm = kHeartRate * kHeartBeatsUntilDeath;
removeHeartbeatTimer();
addHeartbeatTimer();
@ -355,7 +349,6 @@ CClientProxy1_0::setOptions(const COptionsList& options)
CProtocolUtil::writef(getStream(), kMsgDSetOptions, &options);
// check options
CLock lock(getMutex());
for (UInt32 i = 0, n = options.size(); i < n; i += 2) {
if (options[i] == kOptionHeartbeat) {
double rate = 1.0e-3 * static_cast<double>(options[i + 1]);
@ -369,43 +362,9 @@ CClientProxy1_0::setOptions(const COptionsList& options)
}
}
SInt32
CClientProxy1_0::getJumpZoneSize() const
{
CLock lock(getMutex());
return m_info.m_zoneSize;
}
void
CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
{
CLock lock(getMutex());
x = m_info.m_x;
y = m_info.m_y;
w = m_info.m_w;
h = m_info.m_h;
}
void
CClientProxy1_0::getCursorPos(SInt32&, SInt32&) const
{
assert(0 && "shouldn't be called");
}
void
CClientProxy1_0::getCursorCenter(SInt32& x, SInt32& y) const
{
CLock lock(getMutex());
x = m_info.m_mx;
y = m_info.m_my;
}
bool
CClientProxy1_0::recvInfo(bool notify)
CClientProxy1_0::recvInfo()
{
{
CLock lock(getMutex());
// parse the message
SInt16 x, y, w, h, zoneSize, mx, my;
if (!CProtocolUtil::readf(getStream(), kMsgDInfo + 4,
@ -430,12 +389,6 @@ CClientProxy1_0::recvInfo(bool notify)
m_info.m_zoneSize = zoneSize;
m_info.m_mx = mx;
m_info.m_my = my;
}
// tell server of change
if (notify) {
getServer()->onInfoChanged(getName(), m_info);
}
// acknowledge receipt
LOG((CLOG_DEBUG1 "send info ack to \"%s\"", getName().c_str()));
@ -461,9 +414,17 @@ CClientProxy1_0::recvClipboard()
return false;
}
// send update. this calls us back to reset our clipboard dirty flag
// so don't hold a lock during the call.
getServer()->onClipboardChanged(id, seqNum, data);
// save clipboard
m_clipboard[id].m_clipboard.unmarshall(data, 0);
m_clipboard[id].m_sequenceNumber = seqNum;
// notify
CClipboardInfo* info = new CClipboardInfo;
info->m_id = id;
info->m_sequenceNumber = seqNum;
EVENTQUEUE->addEvent(CEvent(getClipboardChangedEvent(),
getEventTarget(), info));
return true;
}
@ -483,8 +444,25 @@ CClientProxy1_0::recvGrabClipboard()
return false;
}
// send update. this calls us back to reset our clipboard dirty flag
// so don't hold a lock during the call.
getServer()->onGrabClipboard(getName(), id, seqNum);
// notify
CClipboardInfo* info = new CClipboardInfo;
info->m_id = id;
info->m_sequenceNumber = seqNum;
EVENTQUEUE->addEvent(CEvent(getClipboardGrabbedEvent(),
getEventTarget(), info));
return true;
}
//
// CClientProxy1_0::CClientClipboard
//
CClientProxy1_0::CClientClipboard::CClientClipboard() :
m_clipboard(),
m_sequenceNumber(0),
m_dirty(true)
{
// do nothing
}

View File

@ -16,6 +16,7 @@
#define CCLIENTPROXY1_0_H
#include "CClientProxy.h"
#include "CClipboard.h"
#include "ProtocolTypes.h"
class CEvent;
@ -24,14 +25,16 @@ class CEventQueueTimer;
//! Proxy for client implementing protocol version 1.0
class CClientProxy1_0 : public CClientProxy {
public:
CClientProxy1_0(IServer* server, const CString& name,
IStream* adoptedStream);
CClientProxy1_0(const CString& name, IStream* adoptedStream);
~CClientProxy1_0();
// IScreen
virtual bool getClipboard(ClipboardID id, IClipboard*) const;
virtual void getShape(SInt32& x, SInt32& y,
SInt32& width, SInt32& height) const;
virtual void getCursorPos(SInt32& x, SInt32& y) const;
// IClient overrides
virtual void open();
virtual void mainLoop();
virtual void close();
virtual void enter(SInt32 xAbs, SInt32 yAbs,
UInt32 seqNum, KeyModifierMask mask,
bool forScreensaver);
@ -50,13 +53,9 @@ public:
virtual void screensaver(bool activate);
virtual void resetOptions();
virtual void setOptions(const COptionsList& options);
virtual SInt32 getJumpZoneSize() const;
virtual void getShape(SInt32& x, SInt32& y,
SInt32& width, SInt32& height) const;
virtual void getCursorPos(SInt32& x, SInt32& y) const;
virtual void getCursorCenter(SInt32& x, SInt32& y) const;
protected:
virtual bool parseHandshakeMessage(const UInt8* code);
virtual bool parseMessage(const UInt8* code);
private:
@ -70,15 +69,27 @@ private:
void handleWriteError(const CEvent&, void*);
void handleFlatline(const CEvent&, void*);
bool recvInfo(bool notify);
bool recvInfo();
bool recvClipboard();
bool recvGrabClipboard();
private:
typedef bool (CClientProxy1_0::*MessageParser)(const UInt8*);
struct CClientClipboard {
public:
CClientClipboard();
public:
CClipboard m_clipboard;
UInt32 m_sequenceNumber;
bool m_dirty;
};
CClientInfo m_info;
bool m_clipboardDirty[kClipboardEnd];
CClientClipboard m_clipboard[kClipboardEnd];
double m_heartbeatAlarm;
CEventQueueTimer* m_heartbeatTimer;
MessageParser m_parser;
};
#endif

View File

@ -21,9 +21,8 @@
// CClientProxy1_1
//
CClientProxy1_1::CClientProxy1_1(IServer* server,
const CString& name, IStream* stream) :
CClientProxy1_0(server, name, stream)
CClientProxy1_1::CClientProxy1_1(const CString& name, IStream* stream) :
CClientProxy1_0(name, stream)
{
// do nothing
}

View File

@ -20,8 +20,7 @@
//! Proxy for client implementing protocol version 1.1
class CClientProxy1_1 : public CClientProxy1_0 {
public:
CClientProxy1_1(IServer* server, const CString& name,
IStream* adoptedStream);
CClientProxy1_1(const CString& name, IStream* adoptedStream);
~CClientProxy1_1();
// IClient overrides

View File

@ -0,0 +1,277 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "CClientProxyUnknown.h"
#include "CClientProxy1_0.h"
#include "CClientProxy1_1.h"
#include "ProtocolTypes.h"
#include "CProtocolUtil.h"
#include "XSynergy.h"
#include "IStream.h"
#include "XIO.h"
#include "CLog.h"
#include "CString.h"
#include "IEventQueue.h"
#include "TMethodEventJob.h"
//
// CClientProxyUnknown
//
CEvent::Type CClientProxyUnknown::s_successEvent = CEvent::kUnknown;
CEvent::Type CClientProxyUnknown::s_failureEvent = CEvent::kUnknown;
CClientProxyUnknown::CClientProxyUnknown(IStream* stream, double timeout) :
m_stream(stream),
m_proxy(NULL),
m_ready(false)
{
EVENTQUEUE->adoptHandler(CEvent::kTimer, this,
new TMethodEventJob<CClientProxyUnknown>(this,
&CClientProxyUnknown::handleTimeout, NULL));
m_timer = EVENTQUEUE->newOneShotTimer(timeout, this);
addStreamHandlers();
LOG((CLOG_DEBUG1 "saying hello"));
CProtocolUtil::writef(m_stream, kMsgHello,
kProtocolMajorVersion,
kProtocolMinorVersion);
}
CClientProxyUnknown::~CClientProxyUnknown()
{
removeHandlers();
removeTimer();
delete m_stream;
delete m_proxy;
}
CClientProxy*
CClientProxyUnknown::orphanClientProxy()
{
if (m_ready) {
removeHandlers();
CClientProxy* proxy = m_proxy;
m_proxy = NULL;
return proxy;
}
else {
return NULL;
}
}
CEvent::Type
CClientProxyUnknown::getSuccessEvent()
{
return CEvent::registerTypeOnce(s_successEvent,
"CClientProxy::success");
}
CEvent::Type
CClientProxyUnknown::getFailureEvent()
{
return CEvent::registerTypeOnce(s_failureEvent,
"CClientProxy::failure");
}
void
CClientProxyUnknown::sendSuccess()
{
m_ready = true;
removeTimer();
EVENTQUEUE->addEvent(CEvent(getSuccessEvent(), this));
}
void
CClientProxyUnknown::sendFailure()
{
delete m_proxy;
m_proxy = NULL;
m_ready = false;
removeHandlers();
removeTimer();
EVENTQUEUE->addEvent(CEvent(getFailureEvent(), this));
}
void
CClientProxyUnknown::addStreamHandlers()
{
assert(m_stream != NULL);
EVENTQUEUE->adoptHandler(IStream::getInputReadyEvent(),
m_stream->getEventTarget(),
new TMethodEventJob<CClientProxyUnknown>(this,
&CClientProxyUnknown::handleData));
EVENTQUEUE->adoptHandler(IStream::getOutputErrorEvent(),
m_stream->getEventTarget(),
new TMethodEventJob<CClientProxyUnknown>(this,
&CClientProxyUnknown::handleWriteError));
EVENTQUEUE->adoptHandler(IStream::getInputShutdownEvent(),
m_stream->getEventTarget(),
new TMethodEventJob<CClientProxyUnknown>(this,
&CClientProxyUnknown::handleDisconnect));
EVENTQUEUE->adoptHandler(IStream::getOutputShutdownEvent(),
m_stream->getEventTarget(),
new TMethodEventJob<CClientProxyUnknown>(this,
&CClientProxyUnknown::handleWriteError));
}
void
CClientProxyUnknown::addProxyHandlers()
{
assert(m_proxy != NULL);
EVENTQUEUE->adoptHandler(CClientProxy::getReadyEvent(),
m_proxy,
new TMethodEventJob<CClientProxyUnknown>(this,
&CClientProxyUnknown::handleReady));
EVENTQUEUE->adoptHandler(CClientProxy::getDisconnectedEvent(),
m_proxy,
new TMethodEventJob<CClientProxyUnknown>(this,
&CClientProxyUnknown::handleDisconnect));
}
void
CClientProxyUnknown::removeHandlers()
{
if (m_stream != NULL) {
EVENTQUEUE->removeHandler(IStream::getInputReadyEvent(),
m_stream->getEventTarget());
EVENTQUEUE->removeHandler(IStream::getOutputErrorEvent(),
m_stream->getEventTarget());
EVENTQUEUE->removeHandler(IStream::getInputShutdownEvent(),
m_stream->getEventTarget());
EVENTQUEUE->removeHandler(IStream::getOutputShutdownEvent(),
m_stream->getEventTarget());
}
if (m_proxy != NULL) {
EVENTQUEUE->removeHandler(CClientProxy::getReadyEvent(),
m_proxy);
EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(),
m_proxy);
}
}
void
CClientProxyUnknown::removeTimer()
{
if (m_timer != NULL) {
EVENTQUEUE->deleteTimer(m_timer);
EVENTQUEUE->removeHandler(CEvent::kTimer, this);
m_timer = NULL;
}
}
void
CClientProxyUnknown::handleData(const CEvent&, void*)
{
LOG((CLOG_DEBUG1 "parsing hello reply"));
CString name("<unknown>");
try {
// limit the maximum length of the hello
UInt32 n = m_stream->getSize();
if (n > kMaxHelloLength) {
LOG((CLOG_DEBUG1 "hello reply too long"));
throw XBadClient();
}
// parse the reply to hello
SInt16 major, minor;
if (!CProtocolUtil::readf(m_stream, kMsgHelloBack,
&major, &minor, &name)) {
throw XBadClient();
}
// disallow invalid version numbers
if (major <= 0 || minor < 0) {
throw XIncompatibleClient(major, minor);
}
// remove stream event handlers. the proxy we're about to create
// may install its own handlers and we don't want to accidentally
// remove those later.
removeHandlers();
// create client proxy for highest version supported by the client
if (major == 1) {
switch (minor) {
case 0:
m_proxy = new CClientProxy1_0(name, m_stream);
break;
case 1:
m_proxy = new CClientProxy1_1(name, m_stream);
break;
}
}
// hangup (with error) if version isn't supported
if (m_proxy == NULL) {
throw XIncompatibleClient(major, minor);
}
// the proxy is created and now proxy now owns the stream
LOG((CLOG_DEBUG1 "created proxy for client \"%s\" version %d.%d", name.c_str(), major, minor));
m_stream = NULL;
// wait until the proxy signals that it's ready or has disconnected
addProxyHandlers();
return;
}
catch (XIncompatibleClient& e) {
// client is incompatible
LOG((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor()));
CProtocolUtil::writef(m_stream,
kMsgEIncompatible,
kProtocolMajorVersion, kProtocolMinorVersion);
}
catch (XBadClient&) {
// client not behaving
LOG((CLOG_WARN "protocol error from client \"%s\"", name.c_str()));
CProtocolUtil::writef(m_stream, kMsgEBad);
}
catch (XBase& e) {
// misc error
LOG((CLOG_WARN "error communicating with client \"%s\": %s", name.c_str(), e.what()));
}
sendFailure();
}
void
CClientProxyUnknown::handleWriteError(const CEvent&, void*)
{
LOG((CLOG_NOTE "error communicating with new client"));
sendFailure();
}
void
CClientProxyUnknown::handleTimeout(const CEvent&, void*)
{
LOG((CLOG_NOTE "new client is unresponsive"));
sendFailure();
}
void
CClientProxyUnknown::handleDisconnect(const CEvent&, void*)
{
LOG((CLOG_NOTE "new client disconnected"));
sendFailure();
}
void
CClientProxyUnknown::handleReady(const CEvent&, void*)
{
sendSuccess();
}

View File

@ -0,0 +1,83 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef CCLIENTPROXYUNKNOWN_H
#define CCLIENTPROXYUNKNOWN_H
#include "CEvent.h"
class CClientProxy;
class CEventQueueTimer;
class IStream;
class CClientProxyUnknown {
public:
CClientProxyUnknown(IStream* stream, double timeout);
~CClientProxyUnknown();
//! @name manipulators
//@{
//! Get the client proxy
/*!
Returns the client proxy created after a successful handshake
(i.e. when this object sends a success event). Returns NULL
if the handshake is unsuccessful or incomplete.
*/
CClientProxy* orphanClientProxy();
//@}
//! @name accessors
//@{
//! Get success event type
/*!
Returns the success event type. This is sent when the client has
correctly responded to the hello message. The target is this.
*/
static CEvent::Type getSuccessEvent();
//! Get failure event type
/*!
Returns the failure event type. This is sent when a client fails
to correctly respond to the hello message. The target is this.
*/
static CEvent::Type getFailureEvent();
//@}
private:
void sendSuccess();
void sendFailure();
void addStreamHandlers();
void addProxyHandlers();
void removeHandlers();
void removeTimer();
void handleData(const CEvent&, void*);
void handleWriteError(const CEvent&, void*);
void handleTimeout(const CEvent&, void*);
void handleDisconnect(const CEvent&, void*);
void handleReady(const CEvent&, void*);
private:
IStream* m_stream;
CEventQueueTimer* m_timer;
CClientProxy* m_proxy;
bool m_ready;
static CEvent::Type s_successEvent;
static CEvent::Type s_failureEvent;
};
#endif

View File

@ -446,11 +446,9 @@ CConfig::getOptions(const CString& name) const
bool
CConfig::operator==(const CConfig& x) const
{
/* FIXME -- no compare available for CNetworkAddress
if (m_synergyAddress != x.m_synergyAddress) {
return false;
}
*/
if (m_map.size() != x.m_map.size()) {
return false;
}

View File

@ -14,10 +14,6 @@
#include "CPrimaryClient.h"
#include "CScreen.h"
#include "IScreenFactory.h"
#include "IServer.h"
#include "XScreen.h"
#include "XSynergy.h"
#include "CClipboard.h"
#include "CLog.h"
@ -25,66 +21,31 @@
// CPrimaryClient
//
CPrimaryClient::CPrimaryClient(IScreenFactory* screenFactory,
IServer* server,
IPrimaryScreenReceiver* receiver,
const CString& name) :
m_server(server),
CPrimaryClient::CPrimaryClient(const CString& name, CScreen* screen) :
m_name(name),
m_seqNum(0)
m_screen(screen)
{
assert(m_server != NULL);
// create screen
LOG((CLOG_DEBUG1 "creating primary screen"));
if (screenFactory != NULL) {
IPlatformScreen* platformScreen =
screenFactory->create(this, receiver);
if (platformScreen != NULL) {
m_screen = new CScreen(platformScreen, this);
}
}
if (m_screen == NULL) {
throw XScreenOpenFailure();
// all clipboards are clean
for (UInt32 i = 0; i < kClipboardEnd; ++i) {
m_clipboardDirty[i] = false;
}
}
CPrimaryClient::~CPrimaryClient()
{
LOG((CLOG_DEBUG1 "destroying primary screen"));
delete m_screen;
}
void
CPrimaryClient::exitMainLoop()
{
m_screen->exitMainLoop();
}
void
CPrimaryClient::reconfigure(UInt32 activeSides)
{
m_screen->reconfigure(activeSides);
}
UInt32
CPrimaryClient::addOneShotTimer(double timeout)
{
return m_screen->addOneShotTimer(timeout);
}
void
CPrimaryClient::getClipboard(ClipboardID id, CString& data) const
CPrimaryClient::getCursorCenter(SInt32& x, SInt32& y) const
{
CClipboard clipboard;
m_screen->getClipboard(id, &clipboard);
data = clipboard.marshall();
}
bool
CPrimaryClient::isLockedToScreen() const
{
return m_screen->isLockedToScreen();
m_screen->getCursorCenter(x, y);
}
KeyModifierMask
@ -93,64 +54,41 @@ CPrimaryClient::getToggleMask() const
return m_screen->getActiveModifiers();
}
void
CPrimaryClient::onError()
bool
CPrimaryClient::isLockedToScreen() const
{
// forward to server
m_server->onError();
return m_screen->isLockedToScreen();
}
void
CPrimaryClient::onInfoChanged(const CClientInfo& info)
void*
CPrimaryClient::getEventTarget() const
{
m_info = info;
try {
m_server->onInfoChanged(getName(), m_info);
}
catch (XBadClient&) {
// ignore
}
return m_screen->getEventTarget();
}
bool
CPrimaryClient::onGrabClipboard(ClipboardID id)
CPrimaryClient::getClipboard(ClipboardID id, IClipboard* clipboard) const
{
try {
return m_server->onGrabClipboard(getName(), id, m_seqNum);
}
catch (XBadClient&) {
return false;
return m_screen->getClipboard(id, clipboard);
}
SInt32
CPrimaryClient::getJumpZoneSize() const
{
return m_screen->getJumpZoneSize();
}
void
CPrimaryClient::onClipboardChanged(ClipboardID id, const CString& data)
CPrimaryClient::getShape(SInt32& x, SInt32& y,
SInt32& width, SInt32& height) const
{
m_server->onClipboardChanged(id, m_seqNum, data);
m_screen->getShape(x, y, width, height);
}
void
CPrimaryClient::open()
CPrimaryClient::getCursorPos(SInt32& x, SInt32& y) const
{
// all clipboards are clean
for (UInt32 i = 0; i < kClipboardEnd; ++i) {
m_clipboardDirty[i] = false;
}
// now open the screen
m_screen->open();
}
void
CPrimaryClient::mainLoop()
{
m_screen->mainLoop();
}
void
CPrimaryClient::close()
{
m_screen->close();
m_screen->getCursorPos(x, y);
}
void
@ -169,8 +107,7 @@ void
CPrimaryClient::enter(SInt32 xAbs, SInt32 yAbs,
UInt32 seqNum, KeyModifierMask, bool screensaver)
{
// note -- we must not call any server methods except onError().
m_seqNum = seqNum;
m_screen->setSequenceNumber(seqNum);
if (!screensaver) {
m_screen->warpCursor(xAbs, yAbs);
}
@ -180,15 +117,12 @@ CPrimaryClient::enter(SInt32 xAbs, SInt32 yAbs,
bool
CPrimaryClient::leave()
{
// note -- we must not call any server methods except onError().
return m_screen->leave();
}
void
CPrimaryClient::setClipboard(ClipboardID id, const CString& data)
{
// note -- we must not call any server methods except onError().
// ignore if this clipboard is already clean
if (m_clipboardDirty[id]) {
// this clipboard is now clean
@ -284,31 +218,3 @@ CPrimaryClient::getName() const
{
return m_name;
}
SInt32
CPrimaryClient::getJumpZoneSize() const
{
return m_info.m_zoneSize;
}
void
CPrimaryClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
{
x = m_info.m_x;
y = m_info.m_y;
w = m_info.m_w;
h = m_info.m_h;
}
void
CPrimaryClient::getCursorPos(SInt32&, SInt32&) const
{
assert(0 && "shouldn't be called");
}
void
CPrimaryClient::getCursorCenter(SInt32& x, SInt32& y) const
{
x = m_info.m_mx;
y = m_info.m_my;
}

View File

@ -16,14 +16,9 @@
#define CPRIMARYCLIENT_H
#include "IClient.h"
#include "IScreenReceiver.h"
#include "ProtocolTypes.h"
class CScreen;
class IClipboard;
class IScreenFactory;
class IPrimaryScreenReceiver;
class IServer;
//! Primary screen as pseudo-client
/*!
@ -31,50 +26,34 @@ The primary screen does not have a client associated with it. This
class provides a pseudo-client to allow the primary screen to be
treated as if it was on a client.
*/
class CPrimaryClient : public IScreenReceiver, public IClient {
class CPrimaryClient : public IClient {
public:
/*!
\c name is the name of the server. The caller retains ownership of
\c factory. Throws XScreenOpenFailure or whatever the factory can
throw if the screen cannot be created.
\c name is the name of the server. \p screen is adopted.
*/
CPrimaryClient(IScreenFactory* factory, IServer*,
IPrimaryScreenReceiver*, const CString& name);
CPrimaryClient(const CString& name, CScreen* screen);
~CPrimaryClient();
//! @name manipulators
//@{
//! Exit event loop
/*!
Force mainLoop() to return. This call can return before
mainLoop() does (i.e. asynchronously). This may only be
called between a successful open() and close().
*/
void exitMainLoop();
//! Update configuration
/*!
Handles reconfiguration of jump zones.
*/
void reconfigure(UInt32 activeSides);
//! Install a one-shot timer
/*!
Installs a one-shot timer for \c timeout seconds and returns the
id of the timer (which will be passed to \c onTimerExpired()).
*/
UInt32 addOneShotTimer(double timeout);
//@}
//! @name accessors
//@{
//! Get clipboard
//! Get cursor center position
/*!
Save the marshalled contents of the clipboard indicated by \c id.
Return the cursor center position which is where we park the
cursor to compute cursor motion deltas and should be far from
the edges of the screen, typically the center.
*/
void getClipboard(ClipboardID, CString&) const;
void getCursorCenter(SInt32& x, SInt32& y) const;
//! Get toggle key state
/*!
@ -90,19 +69,19 @@ public:
//@}
// IScreenReceiver overrides
virtual void onError();
virtual void onInfoChanged(const CClientInfo&);
virtual bool onGrabClipboard(ClipboardID);
virtual void onClipboardChanged(ClipboardID, const CString&);
// XXX -- these go in IClient
// FIXME -- these probably belong on IScreen
virtual void enable();
virtual void disable();
// IScreen overrides
virtual void* getEventTarget() const;
virtual bool getClipboard(ClipboardID id, IClipboard*) const;
virtual SInt32 getJumpZoneSize() const;
virtual void getShape(SInt32& x, SInt32& y,
SInt32& width, SInt32& height) const;
virtual void getCursorPos(SInt32& x, SInt32& y) const;
// IClient overrides
virtual void open();
virtual void mainLoop();
virtual void close();
virtual void enter(SInt32 xAbs, SInt32 yAbs,
UInt32 seqNum, KeyModifierMask mask,
bool forScreensaver);
@ -122,18 +101,10 @@ public:
virtual void resetOptions();
virtual void setOptions(const COptionsList& options);
virtual CString getName() const;
virtual SInt32 getJumpZoneSize() const;
virtual void getShape(SInt32& x, SInt32& y,
SInt32& width, SInt32& height) const;
virtual void getCursorPos(SInt32& x, SInt32& y) const;
virtual void getCursorCenter(SInt32& x, SInt32& y) const;
private:
IServer* m_server;
CScreen* m_screen;
CString m_name;
UInt32 m_seqNum;
CClientInfo m_info;
CScreen* m_screen;
bool m_clipboardDirty[kClipboardEnd];
};

File diff suppressed because it is too large Load Diff

View File

@ -15,84 +15,40 @@
#ifndef CSERVER_H
#define CSERVER_H
#include "IServer.h"
#include "IPrimaryScreenReceiver.h"
#include "CConfig.h"
#include "CClipboard.h"
#include "CCondVar.h"
#include "CMutex.h"
#include "CThread.h"
#include "CJobList.h"
#include "ClipboardTypes.h"
#include "KeyTypes.h"
#include "MouseTypes.h"
#include "CEvent.h"
#include "CStopwatch.h"
#include "stdlist.h"
#include "stdmap.h"
#include "stdset.h"
#include "stdvector.h"
class CClientProxy;
class CClientProxyUnknown;
class CEventQueueTimer;
class CPrimaryClient;
class IClient;
class IDataSocket;
class IScreenFactory;
class IServerProtocol;
class ISocketFactory;
class IStreamFilterFactory;
//! Synergy server
/*!
This class implements the top-level server algorithms for synergy.
*/
class CServer : public IServer, public IPrimaryScreenReceiver {
class CServer {
public:
enum EStatus {
kNotRunning,
kRunning,
kServerNameUnknown,
kError,
kMaxStatus
};
/*!
The server will look itself up in the configuration using \c serverName
as its name.
Start the server with the configuration \p config and the primary
client (local screen) \p primaryClient. The client retains
ownership of \p primaryClient.
*/
CServer(const CString& serverName);
CServer(const CConfig& config, CPrimaryClient* primaryClient);
~CServer();
//! @name manipulators
//@{
//! Open server
/*!
Open the server. Throws XScreenUnavailable if the server's
screen cannot be opened but might be available after some time.
Otherwise throws some other exception if the server's screen or
the server cannot be opened and retrying won't help.
*/
void open();
//! Server main loop
/*!
Run server's event loop and return when exitMainLoop() is called.
This must be called between a successful open() and close().
(cancellation point)
*/
void mainLoop();
//! Exit event loop
/*!
Force mainLoop() to return. This call can return before
mainLoop() does (i.e. asynchronously). This may only be
called between a successful open() and close().
*/
void exitMainLoop();
//! Close server
/*!
Close the server.
*/
void close();
//! Set configuration
/*!
Change the server's configuration. Returns true iff the new
@ -101,67 +57,26 @@ public:
*/
bool setConfig(const CConfig&);
//! Set screen factory
//! Add a client
/*!
Sets the factory for creating screens. This must be set before
calling open(). This object takes ownership of the factory.
Adds \p client to the server. The client is adopted and will be
destroyed when the client disconnects or is disconnected.
*/
void setScreenFactory(IScreenFactory*);
void adoptClient(IClient* client);
//! Set socket factory
//! Disconnect clients
/*!
Sets the factory used to create a socket to connect to the server.
This must be set before calling mainLoop(). This object takes
ownership of the factory.
Disconnect clients. This tells them to disconnect but does not wait
for them to actually do so. The server sends the disconnected event
when they're all disconnected (or immediately if none are connected).
The caller can also just destroy this object to force the disconnection.
*/
void setSocketFactory(ISocketFactory*);
//! Set stream filter factory
/*!
Sets the factory used to filter the socket streams used to
communicate with the server. This object takes ownership
of the factory.
*/
void setStreamFilterFactory(IStreamFilterFactory*);
//! Add a job to notify of status changes
/*!
The added job is run whenever the server's status changes in
certain externally visible ways. The client keeps ownership
of the job.
*/
void addStatusJob(IJob*);
//! Remove a job to notify of status changes
/*!
Removes a previously added status notification job. A job can
remove itself when called but must not remove any other jobs.
The client keeps ownership of the job.
*/
void removeStatusJob(IJob*);
void disconnect();
//@}
//! @name accessors
//@{
//! Get configuration
/*!
Returns the current configuration.
*/
void getConfig(CConfig*) const;
//! Get canonical screen name
/*!
Returns the canonical version of a screen name.
*/
CString getCanonicalName(const CString& name) const;
//! Get name
/*!
Returns the server's name passed to the c'tor
*/
CString getPrimaryScreenName() const;
//! Get number of connected clients
/*!
Returns the number of connected clients, including the server itself.
@ -174,33 +89,22 @@ public:
*/
void getClients(std::vector<CString>& list) const;
//! Get the status
//! Get error event type
/*!
Returns the current status and status message.
Returns the error event type. This is sent when the server fails
for some reason.
*/
EStatus getStatus(CString* = NULL) const;
static CEvent::Type getErrorEvent();
//! Get disconnected event type
/*!
Returns the disconnected event type. This is sent when all the
clients have disconnected.
*/
static CEvent::Type getDisconnectedEvent();
//@}
// IServer overrides
virtual void onError();
virtual void onInfoChanged(const CString&, const CClientInfo&);
virtual bool onGrabClipboard(const CString&, ClipboardID, UInt32);
virtual void onClipboardChanged(ClipboardID, UInt32, const CString&);
// IPrimaryScreenReceiver overrides
virtual void onScreensaver(bool activated);
virtual void onOneShotTimerExpired(UInt32 id);
virtual void onKeyDown(KeyID, KeyModifierMask, KeyButton);
virtual void onKeyUp(KeyID, KeyModifierMask, KeyButton);
virtual void onKeyRepeat(KeyID, KeyModifierMask,
SInt32 count, KeyButton);
virtual void onMouseDown(ButtonID);
virtual void onMouseUp(ButtonID);
virtual bool onMouseMovePrimary(SInt32 x, SInt32 y);
virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy);
virtual void onMouseWheel(SInt32 delta);
protected:
//! Handle special keys
/*!
@ -208,36 +112,18 @@ protected:
*/
bool onCommandKey(KeyID, KeyModifierMask, bool down);
//! Exit event loop and note an error condition
/*!
Force mainLoop() to return by throwing an exception. This call
can return before mainLoop() does (i.e. asynchronously). This
may only be called between a successful open() and close().
*/
void exitMainLoopWithError();
private:
typedef std::list<CThread> CThreadList;
// notify status jobs of a change
void runStatusJobs() const;
// set new status
void setStatus(EStatus, const char* msg = NULL);
// get canonical name of client
CString getName(const IClient*) const;
// get the sides of the primary screen that have neighbors
UInt32 getActivePrimarySides() const;
// handle mouse motion
bool onMouseMovePrimaryNoLock(SInt32 x, SInt32 y);
void onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy);
// set the clipboard
void onClipboardChangedNoLock(ClipboardID,
UInt32 seqNum, const CString& data);
// returns true iff mouse should be locked to the current screen
bool isLockedToScreenNoLock() const;
bool isLockedToScreen() const;
// returns the jump zone of the client
SInt32 getJumpZoneSize(IClient*) const;
// change the active screen
void switchScreen(IClient*,
@ -260,57 +146,98 @@ private:
bool isSwitchOkay(IClient* dst, EDirection,
SInt32 x, SInt32 y);
// update switch state due to a mouse move that doesn't try to
// switch screens.
void onNoSwitch(bool inTapZone);
// update switch state due to a mouse move at \p x, \p y that
// doesn't switch screens.
void noSwitch(SInt32 x, SInt32 y);
// reset switch wait state
void clearSwitchState();
// stop switch timers
void stopSwitch();
// start two tap switch timer
void startSwitchTwoTap();
// arm the two tap switch timer if \p x, \p y is outside the tap zone
void armSwitchTwoTap(SInt32 x, SInt32 y);
// stop the two tap switch timer
void stopSwitchTwoTap();
// returns true iff the two tap switch timer is started
bool isSwitchTwoTapStarted() const;
// returns true iff should switch because of two tap
bool shouldSwitchTwoTap() const;
// start delay switch timer
void startSwitchWait(SInt32 x, SInt32 y);
// stop delay switch timer
void stopSwitchWait();
// returns true iff the delay switch timer is started
bool isSwitchWaitStarted() const;
// send screen options to \c client
void sendOptions(IClient* client) const;
// open/close the primary screen
void openPrimaryScreen();
void closePrimaryScreen();
// process options from configuration
void processOptions();
// update the clipboard if owned by the primary screen
void updatePrimaryClipboard(ClipboardID);
// event handlers
void handleShapeChanged(const CEvent&, void*);
void handleClipboardGrabbed(const CEvent&, void*);
void handleClipboardChanged(const CEvent&, void*);
void handleKeyDownEvent(const CEvent&, void*);
void handleKeyUpEvent(const CEvent&, void*);
void handleKeyRepeatEvent(const CEvent&, void*);
void handleButtonDownEvent(const CEvent&, void*);
void handleButtonUpEvent(const CEvent&, void*);
void handleMotionPrimaryEvent(const CEvent&, void*);
void handleMotionSecondaryEvent(const CEvent&, void*);
void handleWheelEvent(const CEvent&, void*);
void handleScreensaverActivatedEvent(const CEvent&, void*);
void handleScreensaverDeactivatedEvent(const CEvent&, void*);
void handleSwitchWaitTimeout(const CEvent&, void*);
void handleClientDisconnected(const CEvent&, void*);
void handleClientCloseTimeout(const CEvent&, void*);
// close all clients that are *not* in config, not including the
// primary client.
// event processing
void onClipboardChanged(IClient* sender,
ClipboardID id, UInt32 seqNum);
void onScreensaver(bool activated);
void onKeyDown(KeyID, KeyModifierMask, KeyButton);
void onKeyUp(KeyID, KeyModifierMask, KeyButton);
void onKeyRepeat(KeyID, KeyModifierMask, SInt32, KeyButton);
void onMouseDown(ButtonID);
void onMouseUp(ButtonID);
bool onMouseMovePrimary(SInt32 x, SInt32 y);
void onMouseMoveSecondary(SInt32 dx, SInt32 dy);
void onMouseWheel(SInt32 delta);
// add client to list and attach event handlers for client
bool addClient(IClient*);
// remove client from list and detach event handlers for client
bool removeClient(IClient*);
// close a client
void closeClient(IClient*, const char* msg);
// close clients not in \p config
void closeClients(const CConfig& config);
// start a thread, adding it to the list of threads
CThread startThread(IJob* adopted);
// close all clients whether they've completed the handshake or not,
// except the primary client
void closeAllClients();
// cancel running threads, waiting at most timeout seconds for
// them to finish.
void stopThreads(double timeout = -1.0);
// remove clients from internal state
void removeActiveClient(IClient*);
void removeOldClient(IClient*);
// reap threads, clearing finished threads from the thread list.
// doReapThreads does the work on the given thread list.
void reapThreads();
void doReapThreads(CThreadList&);
// thread method to accept incoming client connections
void acceptClients(void*);
// thread method to do client interaction
void runClient(void*);
CClientProxy* handshakeClient(IDataSocket*);
// connection list maintenance
void addConnection(IClient*);
void removeConnection(const CString& name);
// force the cursor off of \p client
void forceLeaveClient(IClient* client);
private:
class XServerRethrow : public XBase {
protected:
// XBase overrides
virtual CString getWhat() const throw();
};
class CClipboardInfo {
public:
CClipboardInfo();
@ -322,52 +249,27 @@ private:
UInt32 m_clipboardSeqNum;
};
CMutex m_mutex;
// the name of the primary screen
CString m_name;
// true if we should exit the main loop by throwing an exception.
// this is used to propagate an exception from one of our threads
// to the mainLoop() thread. but, since we can't make a copy of
// the original exception, we return an arbitrary, unique
// exception type. the caller of mainLoop() cannot catch this
// exception except through XBase or ....
bool m_error;
// how long to wait to bind our socket until we give up
double m_bindTimeout;
// factories
IScreenFactory* m_screenFactory;
ISocketFactory* m_socketFactory;
IStreamFilterFactory* m_streamFilterFactory;
// running threads
CThreadList m_threads;
CThread* m_acceptClientThread;
// the screens
typedef std::map<CString, IClient*> CClientList;
typedef std::map<CString, CThread> CClientThreadList;
// all clients indexed by name
CClientList m_clients;
// run thread of all secondary screen clients. does not include the
// primary screen's run thread.
CClientThreadList m_clientThreads;
// the primary screen client
CPrimaryClient* m_primaryClient;
// all clients (including the primary client) indexed by name
typedef std::map<CString, IClient*> CClientList;
typedef std::set<IClient*> CClientSet;
CClientList m_clients;
CClientSet m_clientSet;
// all old connections that we're waiting to hangup
typedef std::map<IClient*, CEventQueueTimer*> COldClients;
COldClients m_oldClients;
// the client with focus
IClient* m_active;
// the sequence number of enter messages
UInt32 m_seqNum;
// current mouse position (in absolute secondary screen coordinates)
// current mouse position (in absolute screen coordinates) on
// whichever screen is active
SInt32 m_x, m_y;
// current configuration
@ -387,8 +289,7 @@ private:
// state for delayed screen switching
double m_switchWaitDelay;
UInt32 m_switchWaitTimer;
bool m_switchWaitEngaged;
CEventQueueTimer* m_switchWaitTimer;
SInt32 m_switchWaitX, m_switchWaitY;
// state for double-tap screen switching
@ -398,19 +299,8 @@ private:
bool m_switchTwoTapArmed;
SInt32 m_switchTwoTapZone;
// the status change jobs and status
CJobList m_statusJobs;
EStatus m_status;
CString m_statusMessage;
//---
/*
IListenSocket* m_listen;
typedef std::map<CProvisionalClient*,
CEventQueueTimer*> CProvisionalClients;
CProvisionalClients m_provisional;
*/
static CEvent::Type s_errorEvent;
static CEvent::Type s_disconnectedEvent;
};
#endif

View File

@ -25,19 +25,21 @@ MAINTAINERCLEANFILES = \
noinst_LIBRARIES = libserver.a
libserver_a_SOURCES = \
CClientListener.cpp \
CClientProxy.cpp \
CClientProxy1_0.cpp \
CClientProxy1_1.cpp \
CClientProxyUnknown.cpp \
CConfig.cpp \
CPrimaryClient.cpp \
CProvisionalClient.cpp \
CServer.cpp \
CClientListener.h \
CClientProxy.h \
CClientProxy1_0.h \
CClientProxy1_1.h \
CClientProxyUnknown.h \
CConfig.h \
CPrimaryClient.h \
CProvisionalClient.h \
CServer.h \
$(NULL)
INCLUDES = \

View File

@ -73,7 +73,6 @@ CProtocolUtil::vwritef(IStream* stream,
}
// fill buffer
// FIXME -- can we use alloca?
UInt8* buffer = new UInt8[size];
writef(buffer, fmt, args);

View File

@ -14,41 +14,26 @@
#include "CScreen.h"
#include "IPlatformScreen.h"
#include "IScreenReceiver.h"
#include "ISecondaryScreen.h"
#include "ProtocolTypes.h"
#include "CLock.h"
#include "CThread.h"
#include "CLog.h"
#include "IEventQueue.h"
//
// CScreen
//
CScreen::CScreen(IPlatformScreen* platformScreen, IScreenReceiver* receiver) :
CScreen::CScreen(IPlatformScreen* platformScreen) :
m_screen(platformScreen),
m_receiver(receiver),
m_isPrimary(platformScreen->isPrimary()),
m_enabled(false),
m_entered(m_isPrimary),
m_toggleKeys(0),
m_screenSaverSync(true)
{
// do nothing
}
CScreen::~CScreen()
{
delete m_screen;
}
void
CScreen::open()
{
CLock lock(&m_mutex);
assert(m_screen != NULL);
// open screen
m_screen->open(this);
m_screen->setKeyState(this);
// reset options
resetOptions();
@ -56,23 +41,20 @@ CScreen::open()
LOG((CLOG_DEBUG "opened display"));
}
void
CScreen::close()
CScreen::~CScreen()
{
CLock lock(&m_mutex);
if (m_enabled) {
disable();
}
assert(!m_enabled);
assert(m_entered == m_isPrimary);
// close screen
m_screen->close();
delete m_screen;
LOG((CLOG_DEBUG "closed display"));
}
void
CScreen::enable()
{
CLock lock(&m_mutex);
assert(!m_enabled);
m_screen->enable();
@ -90,7 +72,6 @@ CScreen::enable()
void
CScreen::disable()
{
CLock lock(&m_mutex);
assert(m_enabled);
if (!m_isPrimary && m_entered) {
@ -111,34 +92,9 @@ CScreen::disable()
m_enabled = false;
}
void
CScreen::mainLoop()
{
// change our priority
CThread::getCurrentThread().setPriority(-14);
// run event loop
try {
LOG((CLOG_DEBUG "entering event loop"));
m_screen->mainLoop();
LOG((CLOG_DEBUG "exiting event loop"));
}
catch (...) {
LOG((CLOG_DEBUG "exiting event loop"));
throw;
}
}
void
CScreen::exitMainLoop()
{
m_screen->exitMainLoop();
}
void
CScreen::enter()
{
CLock lock(&m_mutex);
assert(m_entered == false);
LOG((CLOG_INFO "entering screen"));
@ -157,7 +113,6 @@ CScreen::enter()
bool
CScreen::leave()
{
CLock lock(&m_mutex);
assert(m_entered == true);
LOG((CLOG_INFO "leaving screen"));
@ -209,8 +164,6 @@ CScreen::grabClipboard(ClipboardID id)
void
CScreen::screensaver(bool activate)
{
CLock lock(&m_mutex);
if (!m_isPrimary) {
// activate/deactivation screen saver iff synchronization enabled
if (m_screenSaverSync) {
@ -222,7 +175,6 @@ CScreen::screensaver(bool activate)
void
CScreen::keyDown(KeyID id, KeyModifierMask mask, KeyButton button)
{
CLock lock(&m_mutex);
assert(!m_isPrimary);
// check for ctrl+alt+del emulation
@ -256,7 +208,6 @@ void
CScreen::keyRepeat(KeyID id,
KeyModifierMask mask, SInt32 count, KeyButton button)
{
CLock lock(&m_mutex);
assert(!m_isPrimary);
// if we haven't seen this button go down then ignore it
@ -316,7 +267,6 @@ CScreen::keyRepeat(KeyID id,
void
CScreen::keyUp(KeyID, KeyModifierMask, KeyButton button)
{
CLock lock(&m_mutex);
assert(!m_isPrimary);
// if we haven't seen this button go down then ignore it
@ -372,8 +322,6 @@ CScreen::mouseWheel(SInt32 delta)
void
CScreen::resetOptions()
{
CLock lock(&m_mutex);
// reset options
m_numLockHalfDuplex = false;
m_capsLockHalfDuplex = false;
@ -394,8 +342,6 @@ CScreen::resetOptions()
void
CScreen::setOptions(const COptionsList& options)
{
CLock lock(&m_mutex);
// update options
bool oldScreenSaverSync = m_screenSaverSync;
for (UInt32 i = 0, n = options.size(); i < n; i += 2) {
@ -427,37 +373,18 @@ CScreen::setOptions(const COptionsList& options)
m_screen->setOptions(options);
}
UInt32
CScreen::addOneShotTimer(double timeout)
void
CScreen::setSequenceNumber(UInt32 seqNum)
{
return m_screen->addOneShotTimer(timeout);
return m_screen->setSequenceNumber(seqNum);
}
bool
CScreen::isOnScreen() const
{
CLock lock(&m_mutex);
return m_entered;
}
void
CScreen::getClipboard(ClipboardID id,
IClipboard* clipboard) const
{
m_screen->getClipboard(id, clipboard);
}
SInt32
CScreen::getJumpZoneSize() const
{
if (!m_isPrimary) {
return 0;
}
else {
return m_screen->getJumpZoneSize();
}
}
bool
CScreen::isLockedToScreen() const
{
@ -488,6 +415,35 @@ CScreen::isLockedToScreen() const
return false;
}
SInt32
CScreen::getJumpZoneSize() const
{
if (!m_isPrimary) {
return 0;
}
else {
return m_screen->getJumpZoneSize();
}
}
void
CScreen::getCursorCenter(SInt32& x, SInt32& y) const
{
m_screen->getCursorCenter(x, y);
}
void*
CScreen::getEventTarget() const
{
return m_screen;
}
bool
CScreen::getClipboard(ClipboardID id, IClipboard* clipboard) const
{
return m_screen->getClipboard(id, clipboard);
}
void
CScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
{
@ -503,8 +459,6 @@ CScreen::getCursorPos(SInt32& x, SInt32& y) const
void
CScreen::updateKeys()
{
CLock lock(&m_mutex);
// clear key state
memset(m_keys, 0, sizeof(m_keys));
memset(m_fakeKeys, 0, sizeof(m_fakeKeys));
@ -522,8 +476,6 @@ CScreen::updateKeys()
void
CScreen::releaseKeys()
{
CLock lock(&m_mutex);
// release keys that we've synthesized a press for and only those
// keys. we don't want to synthesize a release on a key the user
// is still physically pressing.
@ -539,16 +491,12 @@ CScreen::releaseKeys()
void
CScreen::setKeyDown(KeyButton key)
{
CLock lock(&m_mutex);
m_keys[key & 0xffu] |= kDown;
}
void
CScreen::setToggled(KeyModifierMask mask)
{
CLock lock(&m_mutex);
if (!isToggle(mask)) {
return;
}
@ -565,8 +513,6 @@ CScreen::setToggled(KeyModifierMask mask)
void
CScreen::addModifier(KeyModifierMask mask, KeyButtons& keys)
{
CLock lock(&m_mutex);
// the modifier must have associated keys
if (keys.empty()) {
return;
@ -608,8 +554,6 @@ CScreen::setToggleState(KeyModifierMask mask)
KeyButton
CScreen::isAnyKeyDown() const
{
CLock lock(&m_mutex);
for (UInt32 i = 1; i < 256; ++i) {
if ((m_keys[i] & kDown) != 0) {
return static_cast<KeyButton>(i);
@ -621,8 +565,6 @@ CScreen::isAnyKeyDown() const
bool
CScreen::isKeyDown(KeyButton key) const
{
CLock lock(&m_mutex);
key &= 0xffu;
return (key != 0 && ((m_keys[key] & kDown) != 0));
}
@ -638,8 +580,6 @@ CScreen::isToggle(KeyModifierMask mask) const
bool
CScreen::isHalfDuplex(KeyModifierMask mask) const
{
CLock lock(&m_mutex);
return ((mask == KeyModifierCapsLock && m_capsLockHalfDuplex) ||
(mask == KeyModifierNumLock && m_numLockHalfDuplex));
}
@ -647,8 +587,6 @@ CScreen::isHalfDuplex(KeyModifierMask mask) const
bool
CScreen::isModifierActive(KeyModifierMask mask) const
{
CLock lock(&m_mutex);
MaskToKeys::const_iterator i = m_maskToKeys.find(mask);
if (i == m_maskToKeys.end()) {
return false;
@ -675,7 +613,6 @@ CScreen::isModifierActive(KeyModifierMask mask) const
KeyModifierMask
CScreen::getActiveModifiers() const
{
CLock lock(&m_mutex);
if (m_isPrimary) {
// we don't keep primary key state up to date so get the
// current state.
@ -688,8 +625,6 @@ bool
CScreen::mapModifier(Keystrokes& keys, Keystrokes& undo,
KeyModifierMask mask, bool desireActive) const
{
CLock lock(&m_mutex);
// look up modifier
MaskToKeys::const_iterator i = m_maskToKeys.find(mask);
if (i == m_maskToKeys.end()) {
@ -751,7 +686,6 @@ CScreen::mapModifier(Keystrokes& keys, Keystrokes& undo,
KeyModifierMask
CScreen::getMaskForKey(KeyButton key) const
{
CLock lock(&m_mutex);
KeyToMask::const_iterator i = m_keyToMask.find(key);
if (i == m_keyToMask.end()) {
return 0;
@ -767,12 +701,8 @@ CScreen::enablePrimary()
// get notified of screen saver activation/deactivation
m_screen->openScreensaver(true);
// collect and send screen info
CClientInfo info;
m_screen->getShape(info.m_x, info.m_y, info.m_w, info.m_h);
m_screen->getCursorPos(info.m_mx, info.m_my);
info.m_zoneSize = getJumpZoneSize();
m_receiver->onInfoChanged(info);
// claim screen changed size
EVENTQUEUE->addEvent(CEvent(getShapeChangedEvent(), getEventTarget()));
}
void

View File

@ -12,45 +12,32 @@
* GNU General Public License for more details.
*/
#ifndef CSECONDARYSCREEN_H
#define CSECONDARYSCREEN_H
#ifndef CSCREEN_H
#define CSCREEN_H
#include "IKeyState.h"
#include "IScreen.h"
#include "ClipboardTypes.h"
#include "MouseTypes.h"
#include "OptionTypes.h"
#include "CMutex.h"
#include "stdmap.h"
class IClipboard;
class IPlatformScreen;
class IScreenReceiver;
//! Platform independent screen
/*!
This is a platform independent screen. It can work as either a
primary or secondary screen.
*/
class CScreen : public IKeyState {
class CScreen : public IScreen, public IKeyState {
public:
CScreen(IPlatformScreen* platformScreen, IScreenReceiver*);
CScreen(IPlatformScreen* platformScreen);
virtual ~CScreen();
//! @name manipulators
//@{
//! Open screen
/*!
Opens the screen.
*/
void open();
//! Close screen
/*!
Closes the screen.
*/
void close();
//! Activate screen
/*!
Activate the screen, preparing it to report system and user events.
@ -66,21 +53,6 @@ public:
*/
void disable();
//! Run event loop
/*!
Run the screen's event loop. This returns when it detects
the application should terminate or when exitMainLoop() is called.
mainLoop() may only be called between open() and close().
*/
void mainLoop();
//! Exit event loop
/*!
Force mainLoop() to return. This call can return before
mainLoop() does (i.e. asynchronously).
*/
void exitMainLoop();
//! Enter screen
/*!
Called when the user navigates to this screen.
@ -196,12 +168,11 @@ public:
*/
void setOptions(const COptionsList& options);
//! Install a one-shot timer
//! Set clipboard sequence number
/*!
Installs a one-shot timer for \c timeout seconds and returns the
id of the timer.
Sets the sequence number to use in subsequent clipboard events.
*/
UInt32 addOneShotTimer(double timeout);
void setSequenceNumber(UInt32);
//@}
//! @name accessors
@ -213,19 +184,6 @@ public:
*/
bool isOnScreen() const;
//! Get clipboard
/*!
Saves the contents of the system clipboard indicated by \c id.
*/
void getClipboard(ClipboardID id, IClipboard*) const;
//! Get jump zone size
/*!
Returns the jump zone size, the size of the regions on the edges of
the screen that cause the cursor to jump to another screen.
*/
SInt32 getJumpZoneSize() const;
//! Get screen lock state
/*!
Returns true if there's any reason that the user should not be
@ -236,22 +194,30 @@ public:
*/
bool isLockedToScreen() const;
//! Get screen shape
//! Get jump zone size
/*!
Returns the position of the upper-left corner of the screen in \c x
and \c y and the size of the screen in \c width and \c height.
Return the jump zone size, the size of the regions on the edges of
the screen that cause the cursor to jump to another screen.
*/
void getShape(SInt32& x, SInt32& y,
SInt32& width, SInt32& height) const;
SInt32 getJumpZoneSize() const;
//! Get cursor position
//! Get cursor center position
/*!
Returns the current position of the cursor in \c x,y.
Return the cursor center position which is where we park the
cursor to compute cursor motion deltas and should be far from
the edges of the screen, typically the center.
*/
void getCursorPos(SInt32& x, SInt32& y) const;
void getCursorCenter(SInt32& x, SInt32& y) const;
//@}
// IScreen overrides
virtual void* getEventTarget() const;
virtual bool getClipboard(ClipboardID id, IClipboard*) const;
virtual void getShape(SInt32& x, SInt32& y,
SInt32& width, SInt32& height) const;
virtual void getCursorPos(SInt32& x, SInt32& y) const;
// IKeyState overrides
virtual void updateKeys();
virtual void releaseKeys();
@ -307,14 +273,9 @@ private:
typedef std::map<KeyModifierMask, KeyButtons> MaskToKeys;
typedef std::map<KeyButton, KeyModifierMask> KeyToMask;
CMutex m_mutex;
// our platform dependent screen
IPlatformScreen* m_screen;
// our screen receiver
IScreenReceiver* m_receiver;
// true if screen is being used as a primary screen, false otherwise
bool m_isPrimary;

View File

@ -15,7 +15,7 @@
#ifndef ICLIENT_H
#define ICLIENT_H
#include "IInterface.h"
#include "IScreen.h"
#include "ClipboardTypes.h"
#include "KeyTypes.h"
#include "MouseTypes.h"
@ -27,43 +27,18 @@
This interface defines the methods necessary for the server to
communicate with a client.
*/
class IClient : public IInterface {
class IClient : public IScreen {
public:
//! @name manipulators
//@{
//! Open client
/*!
Open the client. Throw if the client cannot be opened. If the
screen cannot be opened but retrying later may succeed then throw
XScreenUnavailable.
*/
virtual void open() = 0;
//! Client main loop
/*!
Run client's event loop. This method is typically called in a
separate thread and is exited by cancelling the thread. This
must be called between a successful open() and close().
(cancellation point)
*/
virtual void mainLoop() = 0;
//! Close client
/*!
Close the client.
*/
virtual void close() = 0;
//! Enter screen
/*!
Enter the screen. The cursor should be warped to \c xAbs,yAbs.
The client should record seqNum for future reporting of
clipboard changes. \c mask is the expected toggle button state
and the client should update its state to match. \c forScreensaver
is true iff the screen is being entered because the screen saver is
starting.
Enter the screen. The cursor should be warped to \p xAbs,yAbs.
\p mask is the expected toggle button state and the client should
update its state to match. \p forScreensaver is true iff the
screen is being entered because the screen saver is starting.
Subsequent clipboard events should report \p seqNum.
*/
virtual void enter(SInt32 xAbs, SInt32 yAbs,
UInt32 seqNum, KeyModifierMask mask,
@ -180,35 +155,14 @@ public:
*/
virtual CString getName() const = 0;
//! Get jump zone size
/*!
Called to get the jump zone size.
*/
virtual SInt32 getJumpZoneSize() const = 0;
//@}
//! Get screen shape
/*!
Return the position of the upper-left corner of the screen in \c x and
\c y and the size of the screen in \c width and \c height.
*/
// IScreen overrides
virtual void* getEventTarget() const = 0;
virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0;
virtual void getShape(SInt32& x, SInt32& y,
SInt32& width, SInt32& height) const = 0;
//! Get cursor position
/*!
Return the current position of the cursor in \c x and \c y.
*/
virtual void getCursorPos(SInt32& x, SInt32& y) const = 0;
//! Get cursor center position
/*!
Return the cursor center position which is where we park the
cursor to compute cursor motion deltas and should be far from
the edges of the screen, typically the center.
*/
virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0;
//@}
};
#endif

View File

@ -0,0 +1,149 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "IPlatformScreen.h"
//
// IPlatformScreen
//
CEvent::Type IPlatformScreen::s_keyDownEvent = CEvent::kUnknown;
CEvent::Type IPlatformScreen::s_keyUpEvent = CEvent::kUnknown;
CEvent::Type IPlatformScreen::s_keyRepeatEvent = CEvent::kUnknown;
CEvent::Type IPlatformScreen::s_buttonDownEvent = CEvent::kUnknown;
CEvent::Type IPlatformScreen::s_buttonUpEvent = CEvent::kUnknown;
CEvent::Type IPlatformScreen::s_motionPrimaryEvent = CEvent::kUnknown;
CEvent::Type IPlatformScreen::s_motionSecondaryEvent = CEvent::kUnknown;
CEvent::Type IPlatformScreen::s_wheelEvent = CEvent::kUnknown;
CEvent::Type IPlatformScreen::s_ssActivatedEvent = CEvent::kUnknown;
CEvent::Type IPlatformScreen::s_ssDeactivatedEvent = CEvent::kUnknown;
CEvent::Type
IPlatformScreen::getKeyDownEvent()
{
return CEvent::registerTypeOnce(s_keyDownEvent,
"IPlatformScreen::keyDown");
}
CEvent::Type
IPlatformScreen::getKeyUpEvent()
{
return CEvent::registerTypeOnce(s_keyUpEvent,
"IPlatformScreen::keyUp");
}
CEvent::Type
IPlatformScreen::getKeyRepeatEvent()
{
return CEvent::registerTypeOnce(s_keyRepeatEvent,
"IPlatformScreen::keyRepeat");
}
CEvent::Type
IPlatformScreen::getButtonDownEvent()
{
return CEvent::registerTypeOnce(s_buttonDownEvent,
"IPlatformScreen::buttonDown");
}
CEvent::Type
IPlatformScreen::getButtonUpEvent()
{
return CEvent::registerTypeOnce(s_buttonUpEvent,
"IPlatformScreen::buttonUp");
}
CEvent::Type
IPlatformScreen::getMotionOnPrimaryEvent()
{
return CEvent::registerTypeOnce(s_motionPrimaryEvent,
"IPlatformScreen::motionPrimary");
}
CEvent::Type
IPlatformScreen::getMotionOnSecondaryEvent()
{
return CEvent::registerTypeOnce(s_motionSecondaryEvent,
"IPlatformScreen::motionSecondary");
}
CEvent::Type
IPlatformScreen::getWheelEvent()
{
return CEvent::registerTypeOnce(s_wheelEvent,
"IPlatformScreen::wheel");
}
CEvent::Type
IPlatformScreen::getScreensaverActivatedEvent()
{
return CEvent::registerTypeOnce(s_ssActivatedEvent,
"IPlatformScreen::screensaverActivated");
}
CEvent::Type
IPlatformScreen::getScreensaverDeactivatedEvent()
{
return CEvent::registerTypeOnce(s_ssDeactivatedEvent,
"IPlatformScreen::screensaverDeactivated");
}
//
// IPlatformScreen::CKeyInfo
//
IPlatformScreen::CKeyInfo::CKeyInfo(KeyID id,
KeyModifierMask mask, KeyButton button, SInt32 count) :
m_key(id),
m_mask(mask),
m_button(button),
m_count(count)
{
// do nothing
}
//
// IPlatformScreen::CButtonInfo
//
IPlatformScreen::CButtonInfo::CButtonInfo(ButtonID id) :
m_button(id)
{
// do nothing
}
//
// IPlatformScreen::CMotionInfo
//
IPlatformScreen::CMotionInfo::CMotionInfo(SInt32 x, SInt32 y) :
m_x(x),
m_y(y)
{
// do nothing
}
//
// IPlatformScreen::CWheelInfo
//
IPlatformScreen::CWheelInfo::CWheelInfo(SInt32 wheel) :
m_wheel(wheel)
{
// do nothing
}

View File

@ -15,10 +15,12 @@
#ifndef IPLATFORMSCREEN_H
#define IPLATFORMSCREEN_H
#include "IScreen.h"
#include "IPrimaryScreen.h"
#include "ISecondaryScreen.h"
#include "ClipboardTypes.h"
#include "OptionTypes.h"
#include "CEvent.h"
class IClipboard;
class IKeyState;
@ -27,27 +29,60 @@ class IKeyState;
/*!
This interface defines the methods common to all platform dependent
screen implementations that are used by both primary and secondary
screens.
screens. A platform screen is expected to post the events defined
in \c IScreen when appropriate. It should also post events defined
in \c IPlatformScreen if acting as the primary screen. The target
on the events should be the value returned by \c getEventTarget().
*/
class IPlatformScreen : public IPrimaryScreen, public ISecondaryScreen {
class IPlatformScreen : public IScreen,
public IPrimaryScreen, public ISecondaryScreen {
public:
//! Key event data
class CKeyInfo {
public:
CKeyInfo(KeyID, KeyModifierMask, KeyButton, SInt32 count);
public:
KeyID m_key;
KeyModifierMask m_mask;
KeyButton m_button;
SInt32 m_count;
};
//! Button event data
class CButtonInfo {
public:
CButtonInfo(ButtonID);
public:
ButtonID m_button;
};
//! Motion event data
class CMotionInfo {
public:
CMotionInfo(SInt32 x, SInt32 y);
public:
SInt32 m_x;
SInt32 m_y;
};
//! Wheel motion event data
class CWheelInfo {
public:
CWheelInfo(SInt32);
public:
SInt32 m_wheel;
};
//! @name manipulators
//@{
//! Open screen
//! Set the key state
/*!
Called to open and initialize the screen. Throw XScreenUnavailable
if the screen cannot be opened but retrying later may succeed.
Otherwise throw some other XScreenOpenFailure exception.
Sets the key state object. This object tracks keyboard state and
the screen is expected to keep it up to date.
*/
virtual void open(IKeyState*) = 0;
//! Close screen
/*!
Called to close the screen. close() should quietly ignore calls
that don't have a matching successful call to open().
*/
virtual void close() = 0;
virtual void setKeyState(IKeyState*) = 0;
//! Enable screen
/*!
@ -64,21 +99,6 @@ public:
*/
virtual void disable() = 0;
//! Run event loop
/*!
Run the event loop and return when exitMainLoop() is called.
This must be called between a successful open() and close().
*/
virtual void mainLoop() = 0;
//! Exit event loop
/*!
Force mainLoop() to return. This call can return before
mainLoop() does (i.e. asynchronously). This may only be
called between a successful open() and close().
*/
virtual void exitMainLoop() = 0;
//! Enter screen
/*!
Called when the user navigates to this screen.
@ -102,22 +122,19 @@ public:
//! Check clipboard owner
/*!
Check ownership of all clipboards and notify an IScreenReceiver (set
through some other interface) if any changed. This is used as a
backup in case the system doesn't reliably report clipboard ownership
changes.
Check ownership of all clipboards and post grab events for any that
have changed. This is used as a backup in case the system doesn't
reliably report clipboard ownership changes.
*/
virtual void checkClipboards() = 0;
//! Open screen saver
/*!
Open the screen saver. If \c notify is true then this object must
call an IScreenEventHandler's (set through some other interface)
onScreenSaver() when the screensaver activates or deactivates until
it's closed. If \c notify is false then the screen saver is
disabled on open and restored on close.
send events when the screen saver activates or deactivates until
\c closeScreensaver() is called. If \c notify is false then the
screen saver is disabled and restored on \c closeScreensaver().
*/
// XXX -- pass an interface pointer, not a notify flag
virtual void openScreensaver(bool notify) = 0;
//! Close screen saver
@ -154,6 +171,12 @@ public:
*/
virtual void updateKeys() = 0;
//! Set clipboard sequence number
/*!
Sets the sequence number to use in subsequent clipboard events.
*/
virtual void setSequenceNumber(UInt32) = 0;
//@}
//! @name accessors
//@{
@ -164,35 +187,49 @@ public:
*/
virtual bool isPrimary() const = 0;
//! Get clipboard
//! Get key down event type. Event data is CKeyInfo*, count == 1.
static CEvent::Type getKeyDownEvent();
//! Get key up event type. Event data is CKeyInfo*, count == 1.
static CEvent::Type getKeyUpEvent();
//! Get key repeat event type. Event data is CKeyInfo*.
static CEvent::Type getKeyRepeatEvent();
//! Get button down event type. Event data is CButtonInfo*.
static CEvent::Type getButtonDownEvent();
//! Get button up event type. Event data is CButtonInfo*.
static CEvent::Type getButtonUpEvent();
//! Get mouse motion on the primary screen event type
/*!
Save the contents of the clipboard indicated by \c id and return
true iff successful.
Event data is CMotionInfo* and the values are an absolute position.
*/
virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0;
//! Get screen shape
static CEvent::Type getMotionOnPrimaryEvent();
//! Get mouse motion on a secondary screen event type
/*!
Return the position of the upper-left corner of the screen in \c x and
\c y and the size of the screen in \c w (width) and \c h (height).
Event data is CMotionInfo* and the values are motion deltas not
absolute coordinates.
*/
virtual void getShape(SInt32& x, SInt32& y,
SInt32& w, SInt32& h) const = 0;
//! Get cursor position
/*!
Return the current position of the cursor in \c x and \c y.
*/
virtual void getCursorPos(SInt32& x, SInt32& y) const = 0;
static CEvent::Type getMotionOnSecondaryEvent();
//! Get mouse wheel event type. Event data is CWheelInfo*.
static CEvent::Type getWheelEvent();
//! Get screensaver activated event type
static CEvent::Type getScreensaverActivatedEvent();
//! Get screensaver deactivated event type
static CEvent::Type getScreensaverDeactivatedEvent();
//@}
// IScreen overrides
virtual void* getEventTarget() const = 0;
virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0;
virtual void getShape(SInt32& x, SInt32& y,
SInt32& width, SInt32& height) const = 0;
virtual void getCursorPos(SInt32& x, SInt32& y) const = 0;
// IPrimaryScreen overrides
virtual void reconfigure(UInt32 activeSides) = 0;
virtual void warpCursor(SInt32 x, SInt32 y) = 0;
virtual UInt32 addOneShotTimer(double timeout) = 0;
virtual SInt32 getJumpZoneSize() const = 0;
virtual bool isAnyMouseButtonDown() const = 0;
virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0;
virtual const char* getKeyName(KeyButton) const = 0;
// ISecondaryScreen overrides
@ -205,6 +242,18 @@ public:
const IKeyState& keyState, KeyID id,
KeyModifierMask desiredMask,
bool isAutoRepeat) const = 0;
private:
static CEvent::Type s_keyDownEvent;
static CEvent::Type s_keyUpEvent;
static CEvent::Type s_keyRepeatEvent;
static CEvent::Type s_buttonDownEvent;
static CEvent::Type s_buttonUpEvent;
static CEvent::Type s_motionPrimaryEvent;
static CEvent::Type s_motionSecondaryEvent;
static CEvent::Type s_wheelEvent;
static CEvent::Type s_ssActivatedEvent;
static CEvent::Type s_ssDeactivatedEvent;
};
#endif

View File

@ -25,7 +25,6 @@ primary screen implementations.
*/
class IPrimaryScreen : public IInterface {
public:
// XXX -- may need an interface for sending events
//! @name manipulators
//@{
@ -46,15 +45,6 @@ public:
*/
virtual void warpCursor(SInt32 x, SInt32 y) = 0;
//! Install a one-shot timer
/*!
Installs a one-shot timer for \c timeout seconds and returns the
id of the timer.
*/
// XXX -- need to specify the receiver of the event. or we should
// pass a job. need a method to remove the timer?
virtual UInt32 addOneShotTimer(double timeout) = 0;
//@}
//! @name accessors
//@{
@ -72,6 +62,14 @@ public:
*/
virtual bool isAnyMouseButtonDown() const = 0;
//! Get cursor center position
/*!
Return the cursor center position which is where we park the
cursor to compute cursor motion deltas and should be far from
the edges of the screen, typically the center.
*/
virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0;
//! Get name of key
/*!
Return a string describing the given key.

View File

@ -1,73 +0,0 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef IPRIMARYSCREENRECEIVER_H
#define IPRIMARYSCREENRECEIVER_H
#include "IInterface.h"
#include "KeyTypes.h"
#include "MouseTypes.h"
//! Primary screen event receiver interface
/*!
The interface for receiving notification of events on the primary
screen. The server implements this interface to handle user input.
Platform dependent primary screen implementation will need to take
an IPrimaryScreenReceiver* and notify it of events.
*/
class IPrimaryScreenReceiver : public IInterface {
public:
//! Notify of screen saver change
/*!
Called when the screensaver is activated or deactivated.
*/
virtual void onScreensaver(bool activated) = 0;
//! Notify of one-shot timer expiration
/*!
Called when a one-shot timer expires.
*/
virtual void onOneShotTimerExpired(UInt32 id) = 0;
// call to notify of events. onMouseMovePrimary() returns
// true iff the mouse enters a jump zone and jumps.
//! Notify of key press
virtual void onKeyDown(KeyID, KeyModifierMask, KeyButton) = 0;
//! Notify of key release
virtual void onKeyUp(KeyID, KeyModifierMask, KeyButton) = 0;
//! Notify of key repeat
virtual void onKeyRepeat(KeyID, KeyModifierMask,
SInt32 count, KeyButton) = 0;
//! Notify of mouse button press
virtual void onMouseDown(ButtonID) = 0;
//! Notify of mouse button release
virtual void onMouseUp(ButtonID) = 0;
//! Notify of mouse motion
/*!
Called when the mouse has moved while on the primary screen. \c x
and \c y are the absolute screen position of the mouse. Return
true iff the mouse enters a jump zone and jumps.
*/
virtual bool onMouseMovePrimary(SInt32 x, SInt32 y) = 0;
//! Notify of mouse motion
/*!
Called when the mouse has moved while on the secondary screen.
\c dx and \c dy are the relative motion from the last position.
*/
virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy) = 0;
//! Notify of mouse wheen motion
virtual void onMouseWheel(SInt32 delta) = 0;
};
#endif

52
lib/synergy/IScreen.cpp Normal file
View File

@ -0,0 +1,52 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "IScreen.h"
//
// IScreen
//
CEvent::Type IScreen::s_errorEvent = CEvent::kUnknown;
CEvent::Type IScreen::s_shapeChangedEvent = CEvent::kUnknown;
CEvent::Type IScreen::s_clipboardGrabbedEvent = CEvent::kUnknown;
CEvent::Type IScreen::s_clipboardChangedEvent = CEvent::kUnknown;
CEvent::Type
IScreen::getErrorEvent()
{
return CEvent::registerTypeOnce(s_errorEvent,
"IScreen::error");
}
CEvent::Type
IScreen::getShapeChangedEvent()
{
return CEvent::registerTypeOnce(s_shapeChangedEvent,
"IScreen::shapeChanged");
}
CEvent::Type
IScreen::getClipboardGrabbedEvent()
{
return CEvent::registerTypeOnce(s_clipboardGrabbedEvent,
"IScreen::clipboardGrabbed");
}
CEvent::Type
IScreen::getClipboardChangedEvent()
{
return CEvent::registerTypeOnce(s_clipboardChangedEvent,
"IScreen::clipboardChanged");
}

106
lib/synergy/IScreen.h Normal file
View File

@ -0,0 +1,106 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef ISCREEN_H
#define ISCREEN_H
#include "IInterface.h"
#include "ClipboardTypes.h"
#include "CEvent.h"
class IClipboard;
//! Screen interface
/*!
This interface defines the methods common to all screens.
*/
class IScreen : public IInterface {
public:
struct CClipboardInfo {
public:
ClipboardID m_id;
UInt32 m_sequenceNumber;
};
//! @name accessors
//@{
//! Get event target
/*!
Returns the target used for events created by this object.
*/
virtual void* getEventTarget() const = 0;
//! Get clipboard
/*!
Save the contents of the clipboard indicated by \c id and return
true iff successful.
*/
virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0;
//! Get screen shape
/*!
Return the position of the upper-left corner of the screen in \c x and
\c y and the size of the screen in \c width and \c height.
*/
virtual void getShape(SInt32& x, SInt32& y,
SInt32& width, SInt32& height) const = 0;
//! Get cursor position
/*!
Return the current position of the cursor in \c x and \c y.
*/
virtual void getCursorPos(SInt32& x, SInt32& y) const = 0;
//! Get error event type
/*!
Returns the error event type. This is sent whenever the screen has
failed for some reason (e.g. the X Windows server died).
*/
static CEvent::Type getErrorEvent();
//! Get shape changed event type
/*!
Returns the shape changed event type. This is sent whenever the
screen's shape changes, the cursor center moves, or the jump zone
size changes.
*/
static CEvent::Type getShapeChangedEvent();
//! Get clipboard grabbed event type
/*!
Returns the clipboard grabbed event type. This is sent whenever the
clipboard is grabbed by some other application so we don't own it
anymore. The data is a pointer to a CClipboardInfo.
*/
static CEvent::Type getClipboardGrabbedEvent();
//! Get clipboard changed event type
/*!
Returns the clipboard changed event type. This is sent whenever the
contents of the clipboard has changed. The data is a pointer to a
CClipboardInfo.
*/
static CEvent::Type getClipboardChangedEvent();
//@}
private:
static CEvent::Type s_errorEvent;
static CEvent::Type s_shapeChangedEvent;
static CEvent::Type s_clipboardGrabbedEvent;
static CEvent::Type s_clipboardChangedEvent;
};
#endif

View File

@ -1,41 +0,0 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef ISCREENFACTORY_H
#define ISCREENFACTORY_H
#include "IInterface.h"
class IPrimaryScreenReceiver;
class IPlatformScreen;
class IScreenReceiver;
//! Primary screen factory interface
/*!
This interface provides factory methods to create primary and
secondary screens.
*/
class IScreenFactory : public IInterface {
public:
//! Create screen
/*!
Create and return a screen. The caller must delete the returned
object. The screen is a primary screen iff the IPrimaryScreenReceiver
is not NULL.
*/
virtual IPlatformScreen*
create(IScreenReceiver*, IPrimaryScreenReceiver*) = 0;
};
#endif

View File

@ -1,65 +0,0 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef ISCREENRECEIVER_H
#define ISCREENRECEIVER_H
#include "IInterface.h"
#include "ClipboardTypes.h"
#include "ProtocolTypes.h"
#include "CString.h"
//! Screen event receiver interface
/*!
This interface defines the methods common to most types that receive
events for changes to a screen. Note that the methods in this
interface are similar to the methods in IServer but have different
parameters. This interface is suitable for client-side types.
*/
class IScreenReceiver : public IInterface {
public:
//! Notify of error
/*!
Called when the screen is unexpectedly closing. This implies that
the screen is no longer usable and that the program should close
the screen and probably terminate.
*/
virtual void onError() = 0;
//! Notify of client screen change
/*!
Called when the client's info has changed. For example, when the
screen resolution has changed.
*/
virtual void onInfoChanged(const CClientInfo&) = 0;
//! Notify of clipboad grab
/*!
Called when the clipboard was grabbed by another program and,
therefore, we no longer own it. Returns true if the grab was
honored, false otherwise.
*/
virtual bool onGrabClipboard(ClipboardID) = 0;
//! Notify of new clipboard data
/*!
Called when the data on the clipboard has changed because some
other program has changed it. \c data will have marshalled
clipboard data.
*/
virtual void onClipboardChanged(ClipboardID,
const CString& data) = 0;
};
#endif

View File

@ -16,6 +16,7 @@
#define ISCREENSAVER_H
#include "IInterface.h"
#include "CEvent.h"
//! Screen saver interface
/*!

View File

@ -1,75 +0,0 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef ISERVER_H
#define ISERVER_H
#include "IInterface.h"
#include "ClipboardTypes.h"
#include "CString.h"
class CClientInfo;
//! Server interface
/*!
This interface defines the methods necessary for clients to
communicate with the server. Note that the methods in this
interface are similar to the methods in IScreenReceiver but
include extra parameters. This interface is suitable for
server-side client proxies. Client-side objects should use
the IScreenReceiver interface since the extra parameters are
meaningless on the client-side.
*/
class IServer : public IInterface {
public:
//! @name manipulators
//@{
//! Notify of error
/*!
Called when the screen is unexpectedly closing. This implies that
the screen is no longer usable and that the program should close
the screen and probably terminate.
*/
virtual void onError() = 0;
//! Notify of client screen change
/*!
Called when the client's info has changed.
*/
virtual void onInfoChanged(const CString& clientName,
const CClientInfo&) = 0;
//! Notify of clipboad grab
/*!
Called when the clipboard was grabbed by another program and,
therefore, we no longer own it. Returns true if the grab was
honored, false otherwise.
*/
virtual bool onGrabClipboard(const CString& clientName,
ClipboardID, UInt32 seqNum) = 0;
//! Notify of new clipboard data
/*!
Called when the data on the clipboard has changed because some
other program has changed it. \c data has the marshalled clipboard
data.
*/
virtual void onClipboardChanged(ClipboardID,
UInt32 seqNum, const CString& data) = 0;
//@}
};
#endif

View File

@ -29,6 +29,8 @@ libsynergy_a_SOURCES = \
CPacketStreamFilter.cpp \
CProtocolUtil.cpp \
CScreen.cpp \
IPlatformScreen.cpp \
IScreen.cpp \
XScreen.cpp \
XSynergy.cpp \
CClipboard.h \
@ -41,12 +43,9 @@ libsynergy_a_SOURCES = \
IKeyState.h \
IPlatformScreen.h \
IPrimaryScreen.h \
IPrimaryScreenReceiver.h \
IScreenFactory.h \
IScreenReceiver.h \
IScreen.h \
IScreenSaver.h \
ISecondaryScreen.h \
IServer.h \
KeyTypes.h \
MouseTypes.h \
OptionTypes.h \