Checkpoint. Conversion to event driven system complete for Unix.

Still need to convert win32 platform specific files.
This commit is contained in:
crs 2004-02-15 17:32:11 +00:00
parent 901a76df0d
commit 48908242d2
42 changed files with 1543 additions and 1645 deletions

View File

@ -15,7 +15,7 @@
#include "CClientTaskBarReceiver.h"
#include "CClient.h"
#include "CLock.h"
#include "TMethodJob.h"
#include "IEventQueue.h"
#include "CArch.h"
//
@ -23,71 +23,73 @@
//
CClientTaskBarReceiver::CClientTaskBarReceiver() :
m_quit(NULL),
m_state(kNotRunning),
m_client(NULL)
m_state(kNotRunning)
{
// create a job for getting notification when the client's
// status changes.
m_job = new TMethodJob<CClientTaskBarReceiver>(this,
&CClientTaskBarReceiver::statusChanged, NULL);
// do nothing
}
CClientTaskBarReceiver::~CClientTaskBarReceiver()
{
if (m_client != NULL) {
m_client->removeStatusJob(m_job);
}
delete m_job;
delete m_quit;
// do nothing
}
void
CClientTaskBarReceiver::setClient(CClient* client)
CClientTaskBarReceiver::updateStatus(CClient* client, const CString& errorMsg)
{
{
// update our status
CLock lock(&m_mutex);
if (m_client != client) {
if (m_client != NULL) {
m_client->removeStatusJob(m_job);
m_errorMessage = errorMsg;
if (client == NULL) {
if (m_errorMessage.empty()) {
m_state = kNotRunning;
}
m_client = client;
if (m_client != NULL) {
m_client->addStatusJob(m_job);
else {
m_state = kNotWorking;
}
}
else {
if (client->isConnected()) {
m_state = kConnected;
}
else if (client->isConnecting()) {
m_state = kConnecting;
}
else {
m_state = kNotConnected;
}
}
}
ARCH->updateReceiver(this);
}
void
CClientTaskBarReceiver::setState(EState state)
{
{
CLock lock(&m_mutex);
m_state = state;
// let subclasses have a go
onStatusChanged(client);
}
ARCH->updateReceiver(this);
}
void
CClientTaskBarReceiver::setQuitJob(IJob* job)
{
CLock lock(&m_mutex);
delete m_quit;
m_quit = job;
// tell task bar
ARCH->updateReceiver(this);
}
CClientTaskBarReceiver::EState
CClientTaskBarReceiver::getState() const
CClientTaskBarReceiver::getStatus() const
{
return m_state;
}
CClient*
CClientTaskBarReceiver::getClient() const
const CString&
CClientTaskBarReceiver::getErrorMessage() const
{
return m_client;
return m_errorMessage;
}
void
CClientTaskBarReceiver::quit()
{
EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
}
void
CClientTaskBarReceiver::onStatusChanged(CClient*)
{
// do nothing
}
void
@ -113,7 +115,10 @@ CClientTaskBarReceiver::getToolTip() const
return CString("Synergy: ") + m_errorMessage;
case kNotConnected:
return "Synergy: Waiting for clients";
return CString("Synergy: Not connected: ") + m_errorMessage;
case kConnecting:
return "Synergy: Connecting...";
case kConnected:
return "Synergy: Connected";
@ -122,42 +127,3 @@ CClientTaskBarReceiver::getToolTip() const
return "";
}
}
void
CClientTaskBarReceiver::quit()
{
if (m_quit != NULL) {
m_quit->run();
}
}
void
CClientTaskBarReceiver::onStatusChanged()
{
// do nothing
}
void
CClientTaskBarReceiver::statusChanged(void*)
{
// update our status
switch (m_client->getStatus(&m_errorMessage)) {
case CClient::kNotRunning:
setState(kNotRunning);
break;
case CClient::kRunning:
setState(kConnected);
break;
case CClient::kError:
setState(kNotWorking);
break;
default:
break;
}
// let subclasses have a go
onStatusChanged();
}

View File

@ -20,59 +20,21 @@
#include "IArchTaskBarReceiver.h"
class CClient;
class IJob;
//! Implementation of IArchTaskBarReceiver for the synergy server
class CClientTaskBarReceiver : public IArchTaskBarReceiver {
public:
enum EState {
kNotRunning,
kNotWorking,
kNotConnected,
kConnected,
kMaxState
};
CClientTaskBarReceiver();
virtual ~CClientTaskBarReceiver();
//! @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 client.
*/
void setClient(CClient*);
//! 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 setClient().
*/
CClient* getClient() const;
void updateStatus(CClient*, const CString& errorMsg);
//@}
@ -86,24 +48,36 @@ public:
virtual std::string getToolTip() const;
protected:
enum EState {
kNotRunning,
kNotWorking,
kNotConnected,
kConnecting,
kConnected,
kMaxState
};
//! Get status
EState getStatus() const;
//! Get error message
const CString& getErrorMessage() const;
//! Quit app
/*!
Causes the application to quit gracefully
*/
void quit();
//! Status change notification
/*!
Called when status changes. The default implementation does
nothing.
Called when status changes. The default implementation does nothing.
*/
virtual void onStatusChanged();
private:
void statusChanged(void*);
virtual void onStatusChanged(CClient* client);
private:
CMutex m_mutex;
IJob* m_quit;
EState m_state;
CClient* m_client;
IJob* m_job;
CString m_errorMessage;
};

View File

@ -53,9 +53,3 @@ CXWindowsClientTaskBarReceiver::getIcon() const
{
return NULL;
}
void
CXWindowsClientTaskBarReceiver::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:
// CClientTaskBarReceiver overrides
virtual void onStatusChanged();
};
#endif

View File

@ -13,23 +13,22 @@
*/
#include "CClient.h"
#include "IScreenFactory.h"
#include "CScreen.h"
#include "ProtocolTypes.h"
#include "Version.h"
#include "XScreen.h"
#include "CNetworkAddress.h"
#include "CSocketMultiplexer.h"
#include "CTCPSocketFactory.h"
#include "XSocket.h"
#include "CCondVar.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 "CString.h"
#include "LogOutputters.h"
#include "CArch.h"
#include "XArch.h"
#include <cstring>
#define DAEMON_RUNNING(running_)
@ -87,160 +86,290 @@ 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(false));
#elif UNIX_LIKE
return new CXWindowsScreen(receiver, primaryReceiver);
return new CScreen(new CXWindowsScreen(false));
#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 CClient* s_client = NULL;
static CClient* s_client = NULL;
static CScreen* s_clientScreen = NULL;
static CClientTaskBarReceiver* s_taskBarReceiver = NULL;
static double s_retryTime = 0.0;
static
void
updateStatus()
{
s_taskBarReceiver->updateStatus(s_client, "");
}
static
void
updateStatus(const CString& msg)
{
s_taskBarReceiver->updateStatus(s_client, msg);
}
static
void
resetRestartTimeout()
{
s_retryTime = 0.0;
}
static
double
nextRestartTimeout()
{
// choose next restart timeout. we start with rapid retries
// then slow down.
if (s_retryTime < 1.0) {
s_retryTime = 1.0;
}
else if (s_retryTime < 3.0) {
s_retryTime = 3.0;
}
else if (s_retryTime < 5.0) {
s_retryTime = 5.0;
}
else if (s_retryTime < 15.0) {
s_retryTime = 15.0;
}
else if (s_retryTime < 30.0) {
s_retryTime = 30.0;
}
else {
s_retryTime = 60.0;
}
return s_retryTime;
}
static
void
handleScreenError(const CEvent&, void*)
{
LOG((CLOG_CRIT "error on screen"));
EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
}
static
CScreen*
openClientScreen()
{
CScreen* screen = createScreen();
EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(),
screen->getEventTarget(),
new CFunctionEventJob(
&handleScreenError));
return screen;
}
static
void
closeClientScreen(CScreen* screen)
{
if (screen != NULL) {
EVENTQUEUE->removeHandler(IScreen::getErrorEvent(),
screen->getEventTarget());
delete screen;
}
}
static
void
handleClientRestart(const CEvent&, void* vtimer)
{
// discard old timer
CEventQueueTimer* timer = reinterpret_cast<CEventQueueTimer*>(vtimer);
EVENTQUEUE->deleteTimer(timer);
EVENTQUEUE->removeHandler(CEvent::kTimer, NULL);
// reconnect
s_client->connect();
updateStatus();
}
static
void
scheduleClientRestart(double retryTime)
{
// install a timer and handler to retry later
LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime));
CEventQueueTimer* timer = EVENTQUEUE->newOneShotTimer(retryTime, NULL);
EVENTQUEUE->adoptHandler(CEvent::kTimer, timer,
new CFunctionEventJob(&handleClientRestart, timer));
}
static
void
handleClientConnected(const CEvent&, void*)
{
LOG((CLOG_DEBUG "connected to server"));
resetRestartTimeout();
updateStatus();
}
static
void
handleClientFailed(const CEvent& e, void*)
{
CClient::CFailInfo* info =
reinterpret_cast<CClient::CFailInfo*>(e.getData());
updateStatus(CString("Failed to connect to server: ") + info->m_what);
if (!ARG->m_restartable || !info->m_retry) {
LOG((CLOG_ERR "failed to connect to server: %s", info->m_what));
}
else {
LOG((CLOG_WARN "failed to connect to server: %s", info->m_what));
scheduleClientRestart(nextRestartTimeout());
}
}
static
void
handleClientDisconnected(const CEvent&, void*)
{
LOG((CLOG_NOTE "disconnected from server"));
if (!ARG->m_restartable) {
EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
}
else {
s_client->connect();
}
updateStatus();
}
static
CClient*
openClient(const CString& name, const CNetworkAddress& address, CScreen* screen)
{
CClient* client = new CClient(name, address,
new CTCPSocketFactory, NULL, screen);
EVENTQUEUE->adoptHandler(CClient::getConnectedEvent(),
client->getEventTarget(),
new CFunctionEventJob(handleClientConnected));
EVENTQUEUE->adoptHandler(CClient::getConnectionFailedEvent(),
client->getEventTarget(),
new CFunctionEventJob(handleClientFailed));
EVENTQUEUE->adoptHandler(CClient::getDisconnectedEvent(),
client->getEventTarget(),
new CFunctionEventJob(handleClientDisconnected));
return client;
}
static
void
closeClient(CClient* client)
{
if (client == NULL) {
return;
}
EVENTQUEUE->removeHandler(CClient::getConnectedEvent(), client);
EVENTQUEUE->removeHandler(CClient::getConnectionFailedEvent(), client);
EVENTQUEUE->removeHandler(CClient::getDisconnectedEvent(), client);
delete client;
}
static
bool
startClient()
{
double retryTime;
CScreen* clientScreen = NULL;
try {
clientScreen = openClientScreen();
s_client = openClient(ARG->m_name,
ARG->m_serverAddress, clientScreen);
s_clientScreen = clientScreen;
LOG((CLOG_NOTE "started client"));
s_client->connect();
updateStatus();
return true;
}
catch (XScreenUnavailable& e) {
LOG((CLOG_WARN "cannot open secondary screen: %s", e.what()));
closeClientScreen(clientScreen);
updateStatus(CString("Cannot open secondary screen: ") + e.what());
retryTime = e.getRetryTime();
}
catch (XScreenOpenFailure& e) {
LOG((CLOG_CRIT "cannot open secondary screen: %s", e.what()));
closeClientScreen(clientScreen);
return false;
}
catch (XBase& e) {
LOG((CLOG_CRIT "failed to start client: %s", e.what()));
closeClientScreen(clientScreen);
return false;
}
if (ARG->m_restartable) {
scheduleClientRestart(retryTime);
return true;
}
else {
// don't try again
return false;
}
}
static
void
stopClient()
{
closeClient(s_client);
closeClientScreen(s_clientScreen);
s_client = NULL;
s_clientScreen = NULL;
}
static
int
realMain(void)
realMain()
{
int result = kExitSuccess;
do {
bool opened = false;
bool locked = true;
try {
// create client
s_client = new CClient(ARG->m_name);
s_client->setAddress(ARG->m_serverAddress);
s_client->setScreenFactory(new CScreenFactory);
s_client->setSocketFactory(new CTCPSocketFactory);
s_client->setStreamFilterFactory(NULL);
// start the client. if this return false then we've failed and
// we shouldn't retry.
LOG((CLOG_DEBUG1 "starting client"));
if (!startClient()) {
return kExitFailed;
}
// open client
try {
s_taskBarReceiver->setClient(s_client);
s_client->open();
opened = true;
// run event loop. if startClient() failed we're supposed to retry
// later. the timer installed by startClient() will take care of
// that.
DAEMON_RUNNING(true);
CEvent event;
EVENTQUEUE->getEvent(event);
while (event.getType() != CEvent::kQuit) {
EVENTQUEUE->dispatchEvent(event);
CEvent::deleteData(event);
EVENTQUEUE->getEvent(event);
}
DAEMON_RUNNING(false);
// run client
DAEMON_RUNNING(true);
locked = false;
s_client->mainLoop();
locked = true;
DAEMON_RUNNING(false);
// close down
LOG((CLOG_DEBUG1 "stopping client"));
stopClient();
updateStatus();
LOG((CLOG_NOTE "stopped client"));
// get client status
if (s_client->wasRejected()) {
// try again later. we don't want to bother
// the server very often if it doesn't want us.
throw XScreenUnavailable(60.0);
}
// clean up
#define FINALLY do { \
if (!locked) { \
DAEMON_RUNNING(false); \
locked = true; \
} \
if (opened) { \
s_client->close(); \
} \
s_taskBarReceiver->setClient(NULL); \
delete s_client; \
s_client = NULL; \
} while (false)
FINALLY;
}
catch (XScreenUnavailable& e) {
// wait before retrying if we're going to retry
if (ARG->m_restartable) {
LOG((CLOG_DEBUG "waiting %.0f seconds to retry", e.getRetryTime()));
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);
return result;
return kExitSuccess;
}
/*
static
void
realMainEntry(void* vresult)
@ -278,6 +407,7 @@ runMainInThread(void)
throw;
}
}
*/
//
@ -292,14 +422,12 @@ static
void
version()
{
LOG((CLOG_PRINT
"%s %s, protocol version %d.%d\n"
"%s",
ARG->m_pname,
kVersion,
kProtocolMajorVersion,
kProtocolMinorVersion,
kCopyright));
LOG((CLOG_PRINT "%s %s, protocol version %d.%d\n%s",
ARG->m_pname,
kVersion,
kProtocolMajorVersion,
kProtocolMinorVersion,
kCopyright));
}
static
@ -697,15 +825,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 CXWindowsClientTaskBarReceiver;
s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread()));
// parse command line
parse(argc, argv);

View File

@ -14,8 +14,8 @@
#include "CServerTaskBarReceiver.h"
#include "CServer.h"
#include "CEventQueue.h"
#include "CLock.h"
#include "IEventQueue.h"
#include "CArch.h"
//

View File

@ -171,25 +171,35 @@ closeClientListener(CClientListener* listen)
}
}
static
void
handleScreenError(const CEvent&, void*)
{
LOG((CLOG_CRIT "error on screen"));
EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
}
static
CScreen*
openServerScreen()
{
return createScreen();
CScreen* screen = createScreen();
EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(),
screen->getEventTarget(),
new CFunctionEventJob(
&handleScreenError));
return screen;
}
static
void
closeServerScreen(CScreen* screen)
{
delete screen;
}
static
void
handleScreenError(const CEvent&, void*)
{
EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
if (screen != NULL) {
EVENTQUEUE->removeHandler(IScreen::getErrorEvent(),
screen->getEventTarget());
delete screen;
}
}
static
@ -197,23 +207,14 @@ CPrimaryClient*
openPrimaryClient(const CString& name, CScreen* screen)
{
LOG((CLOG_DEBUG1 "creating primary screen"));
CPrimaryClient* primaryClient = new CPrimaryClient(name, screen);
EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(),
primaryClient->getEventTarget(),
new CFunctionEventJob(
&handleScreenError));
return primaryClient;
return new CPrimaryClient(name, screen);
}
static
void
closePrimaryClient(CPrimaryClient* primaryClient)
{
if (primaryClient != NULL) {
EVENTQUEUE->removeHandler(IScreen::getErrorEvent(),
primaryClient->getEventTarget());
delete primaryClient;
}
delete primaryClient;
}
static
@ -711,7 +712,7 @@ parse(int argc, const char* const* argv)
static
bool
loadConfig(const char* pathname, bool require)
loadConfig(const char* pathname)
{
assert(pathname != NULL);
@ -727,15 +728,8 @@ loadConfig(const char* pathname, bool require)
return true;
}
catch (XConfigRead& e) {
if (require) {
LOG((CLOG_PRINT "%s: cannot read configuration '%s': %s",
ARG->m_pname, pathname, e.what()));
bye(kExitConfig);
}
else {
LOG((CLOG_DEBUG "cannot read configuration \"%s\": %s",
LOG((CLOG_DEBUG "cannot read configuration \"%s\": %s",
pathname, e.what()));
}
}
return false;
}
@ -744,34 +738,38 @@ static
void
loadConfig()
{
bool loaded = false;
// load the config file, if specified
if (ARG->m_configFile != NULL) {
// require the user specified file to load correctly
loadConfig(ARG->m_configFile, true);
loaded = loadConfig(ARG->m_configFile);
}
// load the default configuration if no explicit file given
else {
// get the user's home directory. use the effective user id
// so a user can't get a setuid root program to load his file.
bool loaded = false;
// get the user's home directory
CString path = ARCH->getUserDirectory();
if (!path.empty()) {
// complete path
path = ARCH->concatPath(path, USR_CONFIG_NAME);
// now try loading the user's configuration
loaded = loadConfig(path.c_str(), false);
loaded = loadConfig(path.c_str());
}
if (!loaded) {
// try the system-wide config file
path = ARCH->getSystemDirectory();
if (!path.empty()) {
path = ARCH->concatPath(path, SYS_CONFIG_NAME);
loadConfig(path.c_str(), false);
loaded = loadConfig(path.c_str());
}
}
}
if (!loaded) {
LOG((CLOG_PRINT "%s: no configuration available", ARG->m_pname));
bye(kExitConfig);
}
}

View File

@ -620,6 +620,13 @@ CArchNetworkBSD::nameToAddr(const std::string& name)
sizeof(inaddr.sin_addr));
memcpy(&addr->m_addr, &inaddr, addr->m_len);
}
else {
ARCH->unlockMutex(m_mutex);
delete addr;
throw XArchNetworkNameUnsupported(
"The requested name is valid but "
"does not have a supported address family");
}
// done with static buffer
ARCH->unlockMutex(m_mutex);

View File

@ -64,7 +64,8 @@ class XArch {
public:
XArch(XArchEval* adoptedEvaluator) : m_eval(adoptedEvaluator) { }
XArch(const std::string& msg) : m_eval(NULL), m_what(msg) { }
XArch(const XArch& e) : m_eval(e.m_eval->clone()), m_what(e.m_what) { }
XArch(const XArch& e) : m_eval(e.m_eval != NULL ? e.m_eval->clone() : NULL),
m_what(e.m_what) { }
~XArch() { delete m_eval; }
std::string what() const throw();
@ -137,7 +138,7 @@ XARCH_SUBCLASS(XArchNetworkName, XArchNetwork);
//! The named host is unknown
XARCH_SUBCLASS(XArchNetworkNameUnknown, XArchNetworkName);
//! The named host is known but has to address
//! The named host is known but has no address
XARCH_SUBCLASS(XArchNetworkNameNoAddress, XArchNetworkName);
//! Non-recoverable name server error
@ -146,6 +147,9 @@ XARCH_SUBCLASS(XArchNetworkNameFailure, XArchNetworkName);
//! Temporary name server error
XARCH_SUBCLASS(XArchNetworkNameUnavailable, XArchNetworkName);
//! The named host is known but no supported address
XARCH_SUBCLASS(XArchNetworkNameUnsupported, XArchNetworkName);
//! Generic daemon exception
/*!
Exceptions derived from this class are used by the daemon

View File

@ -82,8 +82,7 @@ CEvent::deleteData(const CEvent& event)
break;
default:
// yes, really delete void*
delete event.getData();
free(event.getData());
break;
}
}

View File

@ -38,9 +38,10 @@ public:
//! Create \c CEvent with data
/*!
The \p type must have been registered using \c registerType().
The \p data must be POD (plain old data) which means it cannot
have a destructor or be composed of any types that do. \p target
is the intended recipient of the event.
The \p data must be POD (plain old data) allocated by malloc(),
which means it cannot have a constructor, destructor or be
composed of any types that do. \p target is the intended
recipient of the event.
*/
CEvent(Type type, void* target = NULL, void* data = NULL);
@ -70,7 +71,7 @@ public:
//! Release event data
/*!
Deletes event data for the given event.
Deletes event data for the given event (using free()).
*/
static void deleteData(const CEvent&);

File diff suppressed because it is too large Load Diff

View File

@ -15,133 +15,113 @@
#ifndef CCLIENT_H
#define CCLIENT_H
#include "IScreenReceiver.h"
#include "IClient.h"
#include "IClipboard.h"
#include "CNetworkAddress.h"
#include "CMutex.h"
#include "CJobList.h"
class CEventQueueTimer;
class CScreen;
class CServerProxy;
class CThread;
class IDataSocket;
class IScreenReceiver;
class IScreenFactory;
class ISocketFactory;
class IStream;
class IStreamFilterFactory;
//! Synergy client
/*!
This class implements the top-level client algorithms for synergy.
*/
class CClient : public IScreenReceiver, public IClient {
class CClient : public IClient {
public:
enum EStatus {
kNotRunning,
kRunning,
kError,
kMaxStatus
class CFailInfo {
public:
bool m_retry;
char m_what[1];
};
/*!
This client will attempt to connect the server using \c clientName
as its name.
This client will attempt to connect to the server using \p name
as its name and \p address as the server's address and \p factory
to create the socket. \p screen is the local screen.
*/
CClient(const CString& clientName);
CClient(const CString& name, const CNetworkAddress& address,
ISocketFactory* socketFactory,
IStreamFilterFactory* streamFilterFactory,
CScreen* screen);
~CClient();
//! @name manipulators
//@{
//! Set server address
//! Connect to server
/*!
Sets the server's address that the client should connect to.
Starts an attempt to connect to the server. This is ignored if
the client is trying to connect or is already connected.
*/
void setAddress(const CNetworkAddress& serverAddress);
void connect();
//! Set screen factory
//! Disconnect
/*!
Sets the factory for creating screens. This must be set before
calling open(). This object takes ownership of the factory.
Disconnects from the server with an optional error message.
*/
void setScreenFactory(IScreenFactory*);
//! Set socket factory
/*!
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.
*/
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*);
//! 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();
//! 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(const char* msg);
//@}
//! @name accessors
//@{
//!
//! Test if connected
/*!
Returns true if the server rejected our connection.
Returns true iff the client is successfully connected to the server.
*/
bool wasRejected() const;
bool isConnected() const;
//! Get the status
//! Test if connecting
/*!
Returns the current status and status message.
Returns true iff the client is currently attempting to connect to
the server.
*/
EStatus getStatus(CString* = NULL) const;
bool isConnecting() const;
//! Get connected event type
/*!
Returns the connected event type. This is sent when the client has
successfully connected to the server.
*/
static CEvent::Type getConnectedEvent();
//! Get connection failed event type
/*!
Returns the connection failed event type. This is sent when the
server fails for some reason. The event data is a CFailInfo*.
*/
static CEvent::Type getConnectionFailedEvent();
//! Get disconnected event type
/*!
Returns the disconnected event type. This is sent when the client
has disconnected from the server (and only after having successfully
connected).
*/
static CEvent::Type getDisconnectedEvent();
//@}
// IScreenReceiver overrides
virtual void onError();
virtual void onInfoChanged(const CClientInfo&);
virtual bool onGrabClipboard(ClipboardID);
virtual void onClipboardChanged(ClipboardID, const CString&);
// 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;
// IClient overrides
virtual void open();
virtual void mainLoop();
virtual void close();
virtual void enter(SInt32 xAbs, SInt32 yAbs,
UInt32 seqNum, KeyModifierMask mask,
bool forScreensaver);
virtual bool leave();
virtual void setClipboard(ClipboardID, const CString&);
virtual void setClipboard(ClipboardID, const IClipboard*);
virtual void grabClipboard(ClipboardID);
virtual void setClipboardDirty(ClipboardID, bool dirty);
virtual void setClipboardDirty(ClipboardID, bool);
virtual void keyDown(KeyID, KeyModifierMask, KeyButton);
virtual void keyRepeat(KeyID, KeyModifierMask,
SInt32 count, KeyButton);
@ -154,52 +134,48 @@ 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:
// notify status jobs of a change
void runStatusJobs() const;
// set new status
void setStatus(EStatus, const char* msg = NULL);
// open/close the secondary screen
void openSecondaryScreen();
void closeSecondaryScreen();
// send the clipboard to the server
void sendClipboard(ClipboardID);
// handle server messaging
void runSession(void*);
void deleteSession(double timeout = -1.0);
void runServer();
CServerProxy* handshakeServer(IDataSocket*);
void sendEvent(CEvent::Type, void*);
void sendConnectionFailedEvent(const char* msg);
void setupConnecting();
void setupConnection();
void setupScreen();
void setupTimer();
void cleanupConnecting();
void cleanupConnection();
void cleanupScreen();
void cleanupTimer();
void handleConnected(const CEvent&, void*);
void handleConnectionFailed(const CEvent&, void*);
void handleConnectTimeout(const CEvent&, void*);
void handleOutputError(const CEvent&, void*);
void handleDisconnected(const CEvent&, void*);
void handleHandshakeComplete(const CEvent&, void*);
void handleShapeChanged(const CEvent&, void*);
void handleClipboardGrabbed(const CEvent&, void*);
void handleClipboardChanged(const CEvent&, void*);
void handleHello(const CEvent&, void*);
private:
CMutex m_mutex;
CString m_name;
CScreen* m_screen;
IScreenReceiver* m_server;
CNetworkAddress m_serverAddress;
IScreenFactory* m_screenFactory;
ISocketFactory* m_socketFactory;
CString m_name;
CNetworkAddress m_serverAddress;
ISocketFactory* m_socketFactory;
IStreamFilterFactory* m_streamFilterFactory;
CThread* m_session;
bool m_active;
bool m_rejected;
CScreen* m_screen;
IStream* m_stream;
CEventQueueTimer* m_timer;
CServerProxy* m_server;
bool m_ready;
bool m_active;
bool m_ownClipboard[kClipboardEnd];
IClipboard::Time m_timeClipboard[kClipboardEnd];
CString m_dataClipboard[kClipboardEnd];
// the status change jobs and status
CJobList m_statusJobs;
EStatus m_status;
CString m_statusMessage;
static CEvent::Type s_connectedEvent;
static CEvent::Type s_connectionFailedEvent;
static CEvent::Type s_disconnectedEvent;
};
#endif

View File

@ -13,15 +13,15 @@
*/
#include "CServerProxy.h"
#include "CClient.h"
#include "CClipboard.h"
#include "CProtocolUtil.h"
#include "IClient.h"
#include "OptionTypes.h"
#include "ProtocolTypes.h"
#include "IInputStream.h"
#include "IOutputStream.h"
#include "CLock.h"
#include "IStream.h"
#include "CLog.h"
#include "CStopwatch.h"
#include "IEventQueue.h"
#include "TMethodEventJob.h"
#include "XBase.h"
#include <memory>
@ -29,293 +29,243 @@
// CServerProxy
//
CServerProxy::CServerProxy(IClient* client,
IInputStream* adoptedInput, IOutputStream* adoptedOutput) :
CEvent::Type CServerProxy::s_handshakeCompleteEvent =
CEvent::kUnknown;
CServerProxy::CServerProxy(CClient* client, IStream* stream) :
m_client(client),
m_input(adoptedInput),
m_output(adoptedOutput),
m_stream(stream),
m_timer(NULL),
m_seqNum(0),
m_heartRate(kHeartRate)
m_compressMouse(false),
m_ignoreMouse(false),
m_heartRate(0.0)
{
assert(m_client != NULL);
assert(m_input != NULL);
assert(m_output != NULL);
assert(m_stream != NULL);
// initialize modifier translation table
for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id)
m_modifierTranslationTable[id] = id;
// handle data on stream
EVENTQUEUE->adoptHandler(IStream::getInputReadyEvent(),
m_stream->getEventTarget(),
new TMethodEventJob<CServerProxy>(this,
&CServerProxy::handleMessage));
// send heartbeat
installHeartBeat(kHeartRate);
}
CServerProxy::~CServerProxy()
{
delete m_input;
delete m_output;
installHeartBeat(-1.0);
}
bool
CServerProxy::mainLoop()
CEvent::Type
CServerProxy::getHandshakeCompleteEvent()
{
bool failedToConnect = false;
try {
// no compressed mouse motion yet
m_compressMouse = false;
return CEvent::registerTypeOnce(s_handshakeCompleteEvent,
"CServerProxy::handshakeComplete");
}
// not ignoring mouse motions
m_ignoreMouse = false;
void
CServerProxy::installHeartBeat(double heartRate)
{
if (m_timer != NULL) {
EVENTQUEUE->removeHandler(CEvent::kTimer, m_timer);
EVENTQUEUE->deleteTimer(m_timer);
}
m_heartRate = heartRate;
if (m_heartRate > 0.0) {
m_timer = EVENTQUEUE->newTimer(m_heartRate, NULL);
EVENTQUEUE->adoptHandler(CEvent::kTimer, m_timer,
new TMethodEventJob<CServerProxy>(this,
&CServerProxy::handleHeartBeat));
}
}
// reset sequence number
m_seqNum = 0;
void
CServerProxy::handleMessage(const CEvent&, void*)
{
while (m_stream->isReady()) {
// read next code
UInt8 code[4];
UInt32 n = m_stream->read(code, sizeof(code));
if (n == 0) {
break;
}
if (n != 4) {
// client sent an incomplete message
LOG((CLOG_ERR "incomplete message from server"));
m_client->disconnect("incomplete message from server");
return;
}
// handle messages from server
CStopwatch heartbeat;
for (;;) {
// if no input is pending then flush compressed mouse motion
if (getInputStream()->getSize() == 0) {
flushCompressedMouse();
}
// parse message
LOG((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3]));
if (memcmp(code, kMsgDMouseMove, 4) == 0) {
mouseMove();
}
// wait for a message
LOG((CLOG_DEBUG2 "waiting for message"));
UInt8 code[4];
UInt32 n = getInputStream()->read(code, 4, m_heartRate);
else if (memcmp(code, kMsgDMouseWheel, 4) == 0) {
mouseWheel();
}
// check if server hungup
if (n == 0) {
LOG((CLOG_NOTE "server disconnected"));
break;
}
else if (memcmp(code, kMsgDKeyDown, 4) == 0) {
keyDown();
}
// check for time out
if (n == (UInt32)-1 ||
(m_heartRate >= 0.0 && heartbeat.getTime() > m_heartRate)) {
// send heartbeat
CLock lock(&m_mutex);
CProtocolUtil::writef(getOutputStream(), kMsgCNoop);
heartbeat.reset();
if (n == (UInt32)-1) {
// no message to process
continue;
}
}
else if (memcmp(code, kMsgDKeyUp, 4) == 0) {
keyUp();
}
// verify we got an entire code
if (n != 4) {
// client sent an incomplete message
LOG((CLOG_ERR "incomplete message from server"));
break;
}
else if (memcmp(code, kMsgDMouseDown, 4) == 0) {
mouseDown();
}
// parse message
LOG((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3]));
if (memcmp(code, kMsgDMouseMove, 4) == 0) {
mouseMove();
}
else if (memcmp(code, kMsgDMouseUp, 4) == 0) {
mouseUp();
}
else if (memcmp(code, kMsgDMouseWheel, 4) == 0) {
mouseWheel();
}
else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) {
keyRepeat();
}
else if (memcmp(code, kMsgDKeyDown, 4) == 0) {
keyDown();
}
else if (memcmp(code, kMsgCNoop, 4) == 0) {
// accept and discard no-op
}
else if (memcmp(code, kMsgDKeyUp, 4) == 0) {
keyUp();
}
else if (memcmp(code, kMsgCEnter, 4) == 0) {
enter();
}
else if (memcmp(code, kMsgDMouseDown, 4) == 0) {
mouseDown();
}
else if (memcmp(code, kMsgCLeave, 4) == 0) {
leave();
}
else if (memcmp(code, kMsgDMouseUp, 4) == 0) {
mouseUp();
}
else if (memcmp(code, kMsgCClipboard, 4) == 0) {
grabClipboard();
}
else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) {
keyRepeat();
}
else if (memcmp(code, kMsgCScreenSaver, 4) == 0) {
screensaver();
}
else if (memcmp(code, kMsgCNoop, 4) == 0) {
// accept and discard no-op
}
else if (memcmp(code, kMsgQInfo, 4) == 0) {
queryInfo();
}
else if (memcmp(code, kMsgCEnter, 4) == 0) {
enter();
}
else if (memcmp(code, kMsgCInfoAck, 4) == 0) {
infoAcknowledgment();
}
else if (memcmp(code, kMsgCLeave, 4) == 0) {
leave();
}
else if (memcmp(code, kMsgDClipboard, 4) == 0) {
setClipboard();
}
else if (memcmp(code, kMsgCClipboard, 4) == 0) {
grabClipboard();
}
else if (memcmp(code, kMsgCResetOptions, 4) == 0) {
resetOptions();
}
else if (memcmp(code, kMsgCScreenSaver, 4) == 0) {
screensaver();
}
else if (memcmp(code, kMsgDSetOptions, 4) == 0) {
setOptions();
}
else if (memcmp(code, kMsgQInfo, 4) == 0) {
queryInfo();
}
else if (memcmp(code, kMsgCClose, 4) == 0) {
// server wants us to hangup
LOG((CLOG_DEBUG1 "recv close"));
m_client->disconnect(NULL);
break;
}
else if (memcmp(code, kMsgCInfoAck, 4) == 0) {
infoAcknowledgment();
}
else if (memcmp(code, kMsgEIncompatible, 4) == 0) {
SInt32 major, minor;
CProtocolUtil::readf(m_stream,
kMsgEIncompatible + 4, &major, &minor);
LOG((CLOG_ERR "server has incompatible version %d.%d", major, minor));
m_client->disconnect("server has incompatible version");
return;
}
else if (memcmp(code, kMsgDClipboard, 4) == 0) {
setClipboard();
}
else if (memcmp(code, kMsgEBusy, 4) == 0) {
LOG((CLOG_ERR "server already has a connected client with name \"%s\"", m_client->getName().c_str()));
m_client->disconnect("server already has a connected client with our name");
return;
}
else if (memcmp(code, kMsgCResetOptions, 4) == 0) {
resetOptions();
}
else if (memcmp(code, kMsgEUnknown, 4) == 0) {
LOG((CLOG_ERR "server refused client with name \"%s\"", m_client->getName().c_str()));
m_client->disconnect("server refused client with our name");
return;
}
else if (memcmp(code, kMsgDSetOptions, 4) == 0) {
setOptions();
}
else if (memcmp(code, kMsgEBad, 4) == 0) {
LOG((CLOG_ERR "server disconnected due to a protocol error"));
m_client->disconnect("server reported a protocol error");
return;
}
else if (memcmp(code, kMsgCClose, 4) == 0) {
// server wants us to hangup
LOG((CLOG_DEBUG1 "recv close"));
break;
}
else if (memcmp(code, kMsgEIncompatible, 4) == 0) {
SInt32 major, minor;
CProtocolUtil::readf(getInputStream(),
kMsgEIncompatible + 4, &major, &minor);
LOG((CLOG_ERR "server has incompatible version %d.%d", major, minor));
failedToConnect = true;
break;
}
else if (memcmp(code, kMsgEBusy, 4) == 0) {
LOG((CLOG_ERR "server already has a connected client with name \"%s\"", getName().c_str()));
failedToConnect = true;
break;
}
else if (memcmp(code, kMsgEUnknown, 4) == 0) {
LOG((CLOG_ERR "server refused client with name \"%s\"", getName().c_str()));
failedToConnect = true;
break;
}
else if (memcmp(code, kMsgEBad, 4) == 0) {
LOG((CLOG_ERR "server disconnected due to a protocol error"));
failedToConnect = true;
break;
}
else {
// unknown message
LOG((CLOG_ERR "unknown message from server"));
LOG((CLOG_ERR "unknown message: %d %d %d %d [%c%c%c%c]", code[0], code[1], code[2], code[3], code[0], code[1], code[2], code[3]));
failedToConnect = true;
break;
}
else {
// unknown message
LOG((CLOG_ERR "unknown message: %d %d %d %d [%c%c%c%c]", code[0], code[1], code[2], code[3], code[0], code[1], code[2], code[3]));
m_client->disconnect("unknown message from server");
return;
}
}
catch (XBase& e) {
LOG((CLOG_ERR "error: %s", e.what()));
}
catch (...) {
throw;
}
return !failedToConnect;
}
IClient*
CServerProxy::getClient() const
{
return m_client;
}
CString
CServerProxy::getName() const
{
return m_client->getName();
}
IInputStream*
CServerProxy::getInputStream() const
{
return m_input;
}
IOutputStream*
CServerProxy::getOutputStream() const
{
return m_output;
flushCompressedMouse();
}
void
CServerProxy::onError()
CServerProxy::handleHeartBeat(const CEvent&, void*)
{
// ignore
CProtocolUtil::writef(m_stream, kMsgCNoop);
}
void
CServerProxy::onInfoChanged(const CClientInfo& info)
CServerProxy::onInfoChanged()
{
// ignore mouse motion until we receive acknowledgment of our info
// change message.
CLock lock(&m_mutex);
m_ignoreMouse = true;
// send info update
sendInfo(info);
queryInfo();
}
bool
CServerProxy::onGrabClipboard(ClipboardID id)
{
LOG((CLOG_DEBUG1 "sending clipboard %d changed", id));
CLock lock(&m_mutex);
CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, m_seqNum);
CProtocolUtil::writef(m_stream, kMsgCClipboard, id, m_seqNum);
return true;
}
void
CServerProxy::onClipboardChanged(ClipboardID id, const CString& data)
CServerProxy::onClipboardChanged(ClipboardID id, const IClipboard* clipboard)
{
CLock lock(&m_mutex);
CString data = IClipboard::marshall(clipboard);
LOG((CLOG_DEBUG1 "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size()));
CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, m_seqNum, &data);
CProtocolUtil::writef(m_stream, kMsgDClipboard, id, m_seqNum, &data);
}
void
CServerProxy::flushCompressedMouse()
{
bool send = false;
SInt32 x = 0, y = 0;
{
CLock lock(&m_mutex);
if (m_compressMouse) {
m_compressMouse = false;
x = m_xMouse;
y = m_yMouse;
send = true;
}
}
if (send) {
getClient()->mouseMove(x, y);
if (m_compressMouse) {
m_compressMouse = false;
m_client->mouseMove(m_xMouse, m_yMouse);
}
}
void
CServerProxy::sendInfo(const CClientInfo& info)
{
// note -- m_mutex should be locked on entry
LOG((CLOG_DEBUG1 "sending info shape=%d,%d %dx%d zone=%d pos=%d,%d", info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize, info.m_mx, info.m_my));
CProtocolUtil::writef(getOutputStream(), kMsgDInfo,
LOG((CLOG_DEBUG1 "sending info shape=%d,%d %dx%d", info.m_x, info.m_y, info.m_w, info.m_h));
CProtocolUtil::writef(m_stream, kMsgDInfo,
info.m_x, info.m_y,
info.m_w, info.m_h,
info.m_zoneSize,
info.m_mx, info.m_my);
info.m_w, info.m_h, 0, 0, 0);
}
KeyID
@ -434,19 +384,15 @@ CServerProxy::enter()
SInt16 x, y;
UInt16 mask;
UInt32 seqNum;
CProtocolUtil::readf(getInputStream(),
kMsgCEnter + 4, &x, &y, &seqNum, &mask);
CProtocolUtil::readf(m_stream, kMsgCEnter + 4, &x, &y, &seqNum, &mask);
LOG((CLOG_DEBUG1 "recv enter, %d,%d %d %04x", x, y, seqNum, mask));
// discard old compressed mouse motion, if any
{
CLock lock(&m_mutex);
m_compressMouse = false;
m_seqNum = seqNum;
}
m_compressMouse = false;
m_seqNum = seqNum;
// forward
getClient()->enter(x, y, seqNum, static_cast<KeyModifierMask>(mask), false);
m_client->enter(x, y, seqNum, static_cast<KeyModifierMask>(mask), false);
}
void
@ -459,7 +405,7 @@ CServerProxy::leave()
flushCompressedMouse();
// forward
getClient()->leave();
m_client->leave();
}
void
@ -469,8 +415,7 @@ CServerProxy::setClipboard()
ClipboardID id;
UInt32 seqNum;
CString data;
CProtocolUtil::readf(getInputStream(),
kMsgDClipboard + 4, &id, &seqNum, &data);
CProtocolUtil::readf(m_stream, kMsgDClipboard + 4, &id, &seqNum, &data);
LOG((CLOG_DEBUG "recv clipboard %d size=%d", id, data.size()));
// validate
@ -479,7 +424,9 @@ CServerProxy::setClipboard()
}
// forward
getClient()->setClipboard(id, data);
CClipboard clipboard;
clipboard.unmarshall(data, 0);
m_client->setClipboard(id, &clipboard);
}
void
@ -488,7 +435,7 @@ CServerProxy::grabClipboard()
// parse
ClipboardID id;
UInt32 seqNum;
CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id, &seqNum);
CProtocolUtil::readf(m_stream, kMsgCClipboard + 4, &id, &seqNum);
LOG((CLOG_DEBUG "recv grab clipboard %d", id));
// validate
@ -497,7 +444,7 @@ CServerProxy::grabClipboard()
}
// forward
getClient()->grabClipboard(id);
m_client->grabClipboard(id);
}
void
@ -508,8 +455,7 @@ CServerProxy::keyDown()
// parse
UInt16 id, mask, button;
CProtocolUtil::readf(getInputStream(), kMsgDKeyDown + 4,
&id, &mask, &button);
CProtocolUtil::readf(m_stream, kMsgDKeyDown + 4, &id, &mask, &button);
LOG((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x, button=0x%04x", id, mask, button));
// translate
@ -521,7 +467,7 @@ CServerProxy::keyDown()
LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2));
// forward
getClient()->keyDown(id2, mask2, button);
m_client->keyDown(id2, mask2, button);
}
void
@ -532,7 +478,7 @@ CServerProxy::keyRepeat()
// parse
UInt16 id, mask, count, button;
CProtocolUtil::readf(getInputStream(), kMsgDKeyRepeat + 4,
CProtocolUtil::readf(m_stream, kMsgDKeyRepeat + 4,
&id, &mask, &count, &button);
LOG((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d, button=0x%04x", id, mask, count, button));
@ -545,7 +491,7 @@ CServerProxy::keyRepeat()
LOG((CLOG_DEBUG1 "key repeat translated to id=%d, mask=0x%04x", id2, mask2));
// forward
getClient()->keyRepeat(id2, mask2, count, button);
m_client->keyRepeat(id2, mask2, count, button);
}
void
@ -556,7 +502,7 @@ CServerProxy::keyUp()
// parse
UInt16 id, mask, button;
CProtocolUtil::readf(getInputStream(), kMsgDKeyUp + 4, &id, &mask, &button);
CProtocolUtil::readf(m_stream, kMsgDKeyUp + 4, &id, &mask, &button);
LOG((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x, button=0x%04x", id, mask, button));
// translate
@ -568,7 +514,7 @@ CServerProxy::keyUp()
LOG((CLOG_DEBUG1 "key up translated to id=%d, mask=0x%04x", id2, mask2));
// forward
getClient()->keyUp(id2, mask2, button);
m_client->keyUp(id2, mask2, button);
}
void
@ -579,11 +525,11 @@ CServerProxy::mouseDown()
// parse
SInt8 id;
CProtocolUtil::readf(getInputStream(), kMsgDMouseDown + 4, &id);
CProtocolUtil::readf(m_stream, kMsgDMouseDown + 4, &id);
LOG((CLOG_DEBUG1 "recv mouse down id=%d", id));
// forward
getClient()->mouseDown(static_cast<ButtonID>(id));
m_client->mouseDown(static_cast<ButtonID>(id));
}
void
@ -594,11 +540,11 @@ CServerProxy::mouseUp()
// parse
SInt8 id;
CProtocolUtil::readf(getInputStream(), kMsgDMouseUp + 4, &id);
CProtocolUtil::readf(m_stream, kMsgDMouseUp + 4, &id);
LOG((CLOG_DEBUG1 "recv mouse up id=%d", id));
// forward
getClient()->mouseUp(static_cast<ButtonID>(id));
m_client->mouseUp(static_cast<ButtonID>(id));
}
void
@ -607,30 +553,27 @@ CServerProxy::mouseMove()
// parse
bool ignore;
SInt16 x, y;
CProtocolUtil::readf(getInputStream(), kMsgDMouseMove + 4, &x, &y);
CProtocolUtil::readf(m_stream, kMsgDMouseMove + 4, &x, &y);
{
// note if we should ignore the move
CLock lock(&m_mutex);
ignore = m_ignoreMouse;
// note if we should ignore the move
ignore = m_ignoreMouse;
// compress mouse motion events if more input follows
if (!ignore && !m_compressMouse && getInputStream()->getSize() > 0) {
m_compressMouse = true;
}
// compress mouse motion events if more input follows
if (!ignore && !m_compressMouse && m_stream->isReady()) {
m_compressMouse = true;
}
// if compressing then ignore the motion but record it
if (m_compressMouse) {
ignore = true;
m_xMouse = x;
m_yMouse = y;
}
// if compressing then ignore the motion but record it
if (m_compressMouse) {
ignore = true;
m_xMouse = x;
m_yMouse = y;
}
LOG((CLOG_DEBUG2 "recv mouse move %d,%d", x, y));
// forward
if (!ignore) {
getClient()->mouseMove(x, y);
m_client->mouseMove(x, y);
}
}
@ -642,11 +585,11 @@ CServerProxy::mouseWheel()
// parse
SInt16 delta;
CProtocolUtil::readf(getInputStream(), kMsgDMouseWheel + 4, &delta);
CProtocolUtil::readf(m_stream, kMsgDMouseWheel + 4, &delta);
LOG((CLOG_DEBUG2 "recv mouse wheel %+d", delta));
// forward
getClient()->mouseWheel(delta);
m_client->mouseWheel(delta);
}
void
@ -654,11 +597,11 @@ CServerProxy::screensaver()
{
// parse
SInt8 on;
CProtocolUtil::readf(getInputStream(), kMsgCScreenSaver + 4, &on);
CProtocolUtil::readf(m_stream, kMsgCScreenSaver + 4, &on);
LOG((CLOG_DEBUG1 "recv screen saver on=%d", on));
// forward
getClient()->screensaver(on != 0);
m_client->screensaver(on != 0);
}
void
@ -668,22 +611,18 @@ CServerProxy::resetOptions()
LOG((CLOG_DEBUG1 "recv reset options"));
// forward
getClient()->resetOptions();
m_client->resetOptions();
CLock lock(&m_mutex);
// reset heart rate
m_heartRate = kHeartRate;
// reset heart rate and send heartbeat if necessary
installHeartBeat(kHeartRate);
if (m_heartRate >= 0.0) {
CProtocolUtil::writef(m_stream, kMsgCNoop);
}
// reset modifier translation table
for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) {
m_modifierTranslationTable[id] = id;
}
// send heartbeat if necessary
if (m_heartRate >= 0.0) {
CProtocolUtil::writef(getOutputStream(), kMsgCNoop);
}
}
void
@ -691,13 +630,11 @@ CServerProxy::setOptions()
{
// parse
COptionsList options;
CProtocolUtil::readf(getInputStream(), kMsgDSetOptions + 4, &options);
CProtocolUtil::readf(m_stream, kMsgDSetOptions + 4, &options);
LOG((CLOG_DEBUG1 "recv set options size=%d", options.size()));
// forward
getClient()->setOptions(options);
CLock lock(&m_mutex);
m_client->setOptions(options);
// update modifier table
for (UInt32 i = 0, n = options.size(); i < n; i += 2) {
@ -718,12 +655,10 @@ CServerProxy::setOptions()
id = kKeyModifierIDSuper;
}
else if (options[i] == kOptionHeartbeat) {
// update heart rate
m_heartRate = 1.0e-3 * static_cast<double>(options[i + 1]);
// send heartbeat if necessary
// update heart rate and send heartbeat if necessary
installHeartBeat(1.0e-3 * static_cast<double>(options[i + 1]));
if (m_heartRate >= 0.0) {
CProtocolUtil::writef(getOutputStream(), kMsgCNoop);
CProtocolUtil::writef(m_stream, kMsgCNoop);
}
}
if (id != kKeyModifierIDNull) {
@ -737,24 +672,14 @@ CServerProxy::setOptions()
void
CServerProxy::queryInfo()
{
// get current info
CClientInfo info;
getClient()->getShape(info.m_x, info.m_y, info.m_w, info.m_h);
getClient()->getCursorPos(info.m_mx, info.m_my);
info.m_zoneSize = getClient()->getJumpZoneSize();
// send it
CLock lock(&m_mutex);
m_client->getShape(info.m_x, info.m_y, info.m_w, info.m_h);
sendInfo(info);
}
void
CServerProxy::infoAcknowledgment()
{
// parse
LOG((CLOG_DEBUG1 "recv info acknowledgment"));
// now allow mouse motion
CLock lock(&m_mutex);
m_ignoreMouse = false;
}

View File

@ -15,88 +15,66 @@
#ifndef CSERVERPROXY_H
#define CSERVERPROXY_H
#include "IScreenReceiver.h"
#include "ClipboardTypes.h"
#include "KeyTypes.h"
#include "CMutex.h"
#include "CEvent.h"
class IClient;
class IInputStream;
class IOutputStream;
class CClient;
class CClientInfo;
class CEventQueueTimer;
class IClipboard;
class IStream;
//! Proxy for server
/*!
This class acts a proxy for the server, converting calls into messages
to the server and messages from the server to calls on the client.
*/
class CServerProxy : public IScreenReceiver {
class CServerProxy {
public:
/*! \c adoptedInput is the stream from the server and
\c adoptedOutput is the stream to the server. This object
takes ownership of both and destroys them in the d'tor.
Messages from the server are converted to calls on \c client.
/*!
Process messages from the server on \p stream and forward to
\p client.
*/
CServerProxy(IClient* client,
IInputStream* adoptedInput,
IOutputStream* adoptedOutput);
CServerProxy(CClient* client, IStream* stream);
~CServerProxy();
//! @name manipulators
//@{
//! Run event loop
/*!
Run the event loop and return when the server disconnects or
requests the client to disconnect. Return true iff the server
didn't reject our connection.
(cancellation point)
*/
bool mainLoop();
virtual void onInfoChanged();
virtual bool onGrabClipboard(ClipboardID);
virtual void onClipboardChanged(ClipboardID, const IClipboard*);
//@}
//! @name accessors
//@{
//! Get client
//! Get handshake complete event type
/*!
Returns the client passed to the c'tor.
Returns the handshake complete event type. This is sent when the
client has completed the handshake with the server.
*/
IClient* getClient() const;
//! Get input stream
/*!
Return the input stream passed to the c'tor.
*/
IInputStream* getInputStream() const;
//! Get output stream
/*!
Return the output stream passed to the c'tor.
*/
IOutputStream* getOutputStream() const;
static CEvent::Type getHandshakeCompleteEvent();
//@}
// IScreenReceiver overrides
virtual void onError();
virtual void onInfoChanged(const CClientInfo&);
virtual bool onGrabClipboard(ClipboardID);
virtual void onClipboardChanged(ClipboardID, const CString& data);
private:
// get the client name (from the client)
CString getName() const;
// if compressing mouse motion then send the last motion now
void flushCompressedMouse();
void sendInfo(const CClientInfo&);
void installHeartBeat(double);
// modifier key translation
KeyID translateKey(KeyID) const;
KeyModifierMask translateModifierMask(KeyModifierMask) const;
// event handlers
void handleMessage(const CEvent&, void*);
void handleHeartBeat(const CEvent&, void*);
// message handlers
void enter();
void leave();
@ -116,11 +94,9 @@ private:
void infoAcknowledgment();
private:
CMutex m_mutex;
IClient* m_client;
IInputStream* m_input;
IOutputStream* m_output;
CClient* m_client;
IStream* m_stream;
CEventQueueTimer* m_timer;
UInt32 m_seqNum;
@ -131,6 +107,8 @@ private:
KeyModifierID m_modifierTranslationTable[kKeyModifierIDLast];
double m_heartRate;
static CEvent::Type s_handshakeCompleteEvent;
};
#endif

View File

@ -77,7 +77,7 @@ CStreamFilter::setEventFilter(IEventJob* filter)
void*
CStreamFilter::getEventTarget() const
{
return const_cast<void*>(reinterpret_cast<const void*>(this));
return getStream()->getEventTarget();
}
bool

View File

@ -113,6 +113,9 @@ CNetworkAddress::CNetworkAddress(const CString& hostname_, int port) :
catch (XArchNetworkNameNoAddress&) {
throw XSocketAddress(XSocketAddress::kNoAddress, hostname, port);
}
catch (XArchNetworkNameUnsupported&) {
throw XSocketAddress(XSocketAddress::kUnsupported, hostname, port);
}
catch (XArchNetworkName&) {
throw XSocketAddress(XSocketAddress::kUnknown, hostname, port);
}

View File

@ -19,10 +19,9 @@
#include "TSocketMultiplexerMethodJob.h"
#include "XSocket.h"
#include "XIO.h"
#include "CEvent.h"
#include "CEventQueue.h"
#include "CLock.h"
#include "CMutex.h"
#include "IEventQueue.h"
#include "CArch.h"
#include "XArch.h"
@ -125,8 +124,7 @@ CTCPListenSocket::serviceListening(ISocketMultiplexerJob* job,
return NULL;
}
if (read) {
CEventQueue::getInstance()->addEvent(
CEvent(getConnectingEvent(), this, NULL));
EVENTQUEUE->addEvent(CEvent(getConnectingEvent(), this, NULL));
// stop polling on this socket until the client accepts
return NULL;
}

View File

@ -18,11 +18,12 @@
#include "TSocketMultiplexerMethodJob.h"
#include "XSocket.h"
#include "CLock.h"
#include "CEventQueue.h"
#include "CLog.h"
#include "IEventQueue.h"
#include "IEventJob.h"
#include "CArch.h"
#include "XArch.h"
#include <string.h>
//
// CTCPSocket
@ -84,6 +85,9 @@ CTCPSocket::bind(const CNetworkAddress& addr)
void
CTCPSocket::close()
{
// remove ourself from the multiplexer
setJob(NULL);
CLock lock(&m_mutex);
// clear buffers and enter disconnected state
@ -92,9 +96,6 @@ CTCPSocket::close()
}
onDisconnected();
// remove ourself from the multiplexer
setJob(NULL);
// close the socket
if (m_socket != NULL) {
CArchSocket socket = m_socket;
@ -141,26 +142,29 @@ CTCPSocket::read(void* buffer, UInt32 n)
void
CTCPSocket::write(const void* buffer, UInt32 n)
{
CLock lock(&m_mutex);
bool wasEmpty;
{
CLock lock(&m_mutex);
// must not have shutdown output
if (!m_writable) {
sendStreamEvent(getOutputErrorEvent());
return;
// must not have shutdown output
if (!m_writable) {
sendStreamEvent(getOutputErrorEvent());
return;
}
// ignore empty writes
if (n == 0) {
return;
}
// copy data to the output buffer
wasEmpty = (m_outputBuffer.getSize() == 0);
m_outputBuffer.write(buffer, n);
// there's data to write
m_flushed = false;
}
// ignore empty writes
if (n == 0) {
return;
}
// copy data to the output buffer
bool wasEmpty = (m_outputBuffer.getSize() == 0);
m_outputBuffer.write(buffer, n);
// there's data to write
m_flushed = false;
// make sure we're waiting to write
if (wasEmpty) {
setJob(newJob());
@ -179,20 +183,26 @@ CTCPSocket::flush()
void
CTCPSocket::shutdownInput()
{
CLock lock(&m_mutex);
bool useNewJob = false;
{
CLock lock(&m_mutex);
// shutdown socket for reading
try {
ARCH->closeSocketForRead(m_socket);
}
catch (XArchNetwork&) {
// ignore
}
// shutdown socket for reading
try {
ARCH->closeSocketForRead(m_socket);
}
catch (XArchNetwork&) {
// ignore
}
// shutdown buffer for reading
if (m_readable) {
sendStreamEvent(getInputShutdownEvent());
onInputShutdown();
// shutdown buffer for reading
if (m_readable) {
sendStreamEvent(getInputShutdownEvent());
onInputShutdown();
useNewJob = true;
}
}
if (useNewJob) {
setJob(newJob());
}
}
@ -200,20 +210,26 @@ CTCPSocket::shutdownInput()
void
CTCPSocket::shutdownOutput()
{
CLock lock(&m_mutex);
bool useNewJob = false;
{
CLock lock(&m_mutex);
// shutdown socket for writing
try {
ARCH->closeSocketForWrite(m_socket);
}
catch (XArchNetwork&) {
// ignore
}
// shutdown socket for writing
try {
ARCH->closeSocketForWrite(m_socket);
}
catch (XArchNetwork&) {
// ignore
}
// shutdown buffer for writing
if (m_writable) {
sendStreamEvent(getOutputShutdownEvent());
onOutputShutdown();
// shutdown buffer for writing
if (m_writable) {
sendStreamEvent(getOutputShutdownEvent());
onOutputShutdown();
useNewJob = true;
}
}
if (useNewJob) {
setJob(newJob());
}
}
@ -249,28 +265,29 @@ CTCPSocket::getEventFilter() const
void
CTCPSocket::connect(const CNetworkAddress& addr)
{
CLock lock(&m_mutex);
{
CLock lock(&m_mutex);
// fail on attempts to reconnect
if (m_socket == NULL || m_connected) {
sendSocketEvent(getConnectionFailedEvent());
return;
}
// fail on attempts to reconnect
if (m_socket == NULL || m_connected) {
sendConnectionFailedEvent("busy");
return;
}
try {
ARCH->connectSocket(m_socket, addr.getAddress());
sendSocketEvent(getConnectedEvent());
onConnected();
setJob(newJob());
}
catch (XArchNetworkConnecting&) {
// connection is in progress
m_writable = true;
setJob(newJob());
}
catch (XArchNetwork& e) {
throw XSocketConnect(e.what());
try {
ARCH->connectSocket(m_socket, addr.getAddress());
sendSocketEvent(getConnectedEvent());
onConnected();
}
catch (XArchNetworkConnecting&) {
// connection is in progress
m_writable = true;
}
catch (XArchNetwork& e) {
throw XSocketConnect(e.what());
}
}
setJob(newJob());
}
void
@ -339,17 +356,27 @@ CTCPSocket::newJob()
void
CTCPSocket::sendSocketEvent(CEvent::Type type)
{
EVENTQUEUE->addEvent(CEvent(type, this, NULL));
EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), NULL));
}
void
CTCPSocket::sendConnectionFailedEvent(const char* msg)
{
CConnectionFailedInfo* info = (CConnectionFailedInfo*)malloc(
sizeof(CConnectionFailedInfo) + strlen(msg));
strcpy(info->m_what, msg);
EVENTQUEUE->addEvent(CEvent(getConnectionFailedEvent(),
getEventTarget(), info));
}
void
CTCPSocket::sendStreamEvent(CEvent::Type type)
{
if (m_eventFilter != NULL) {
m_eventFilter->run(CEvent(type, this, NULL));
m_eventFilter->run(CEvent(type, getEventTarget(), NULL));
}
else {
EVENTQUEUE->addEvent(CEvent(type, this, NULL));
EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), NULL));
}
}
@ -394,22 +421,18 @@ CTCPSocket::serviceConnecting(ISocketMultiplexerJob* job,
{
CLock lock(&m_mutex);
if (write && !error) {
if (error) {
try {
// connection may have failed or succeeded
ARCH->throwErrorOnSocket(m_socket);
}
catch (XArchNetwork&) {
error = true;
catch (XArchNetwork& e) {
sendConnectionFailedEvent(e.what().c_str());
onDisconnected();
return newJob();
}
}
if (error) {
sendSocketEvent(getConnectionFailedEvent());
onDisconnected();
return newJob();
}
if (write) {
sendSocketEvent(getConnectedEvent());
onConnected();

View File

@ -60,6 +60,7 @@ private:
void setJob(ISocketMultiplexerJob*);
ISocketMultiplexerJob* newJob();
void sendSocketEvent(CEvent::Type);
void sendConnectionFailedEvent(const char*);
void sendStreamEvent(CEvent::Type);
void onConnected();

View File

@ -25,6 +25,12 @@ represent a full-duplex data stream.
*/
class IDataSocket : public ISocket, public IStream {
public:
class CConnectionFailedInfo {
public:
// pointer to a string describing the failure
char m_what[1];
};
//! @name manipulators
//@{
@ -52,6 +58,7 @@ public:
/*!
Returns the socket connection failed event type. A socket sends
this event when an attempt to connect to a remote port has failed.
The data is a pointer to a CConnectionFailedInfo.
*/
static CEvent::Type getConnectionFailedEvent();

View File

@ -53,12 +53,14 @@ XSocketAddress::getWhat() const throw()
"XSocketAddressUnknown",
"XSocketAddressNotFound",
"XSocketAddressNoAddress",
"XSocketAddressUnsupported",
"XSocketAddressBadPort"
};
static const char* s_errorMsg[] = {
"unknown error for: %{1}:%{2}",
"address not found for: %{1}",
"no address for: %{1}",
"unsupported address for: %{1}",
"invalid port" // m_port may not be set to the bad port
};
return format(s_errorID[m_error], s_errorMsg[m_error],

View File

@ -34,6 +34,7 @@ public:
kUnknown, //!< Unknown error
kNotFound, //!< The hostname is unknown
kNoAddress, //!< The hostname is valid but has no IP address
kUnsupported, //!< The hostname is valid but has no supported address
kBadPort //!< The port is invalid
};

View File

@ -835,7 +835,7 @@ CXWindowsScreen::sendEvent(CEvent::Type type, void* data)
void
CXWindowsScreen::sendClipboardEvent(CEvent::Type type, ClipboardID id)
{
CClipboardInfo* info = new CClipboardInfo;
CClipboardInfo* info = (CClipboardInfo*)malloc(sizeof(CClipboardInfo));
info->m_id = id;
info->m_sequenceNumber = m_sequenceNumber;
sendEvent(type, info);
@ -1028,11 +1028,11 @@ CXWindowsScreen::onKeyPress(XKeyEvent& xkey)
}
// handle key
sendEvent(getKeyDownEvent(), new CKeyInfo(key, mask, keycode, 1));
sendEvent(getKeyDownEvent(), CKeyInfo::alloc(key, mask, keycode, 1));
KeyModifierMask keyMask = m_keyState->getMaskForKey(keycode);
if (m_keyState->isHalfDuplex(keyMask)) {
sendEvent(getKeyUpEvent(),
new CKeyInfo(key, mask | keyMask, keycode, 1));
CKeyInfo::alloc(key, mask | keyMask, keycode, 1));
}
}
}
@ -1040,7 +1040,7 @@ CXWindowsScreen::onKeyPress(XKeyEvent& xkey)
Bool
CXWindowsScreen::findKeyEvent(Display*, XEvent* xevent, XPointer arg)
{
CKeyEventInfo* filter = reinterpret_cast<CKeyEventInfo*>(arg);
CKeyEventFilter* filter = reinterpret_cast<CKeyEventFilter*>(arg);
return (xevent->type == filter->m_event &&
xevent->xkey.window == filter->m_window &&
xevent->xkey.time == filter->m_time &&
@ -1057,7 +1057,7 @@ CXWindowsScreen::onKeyRelease(XKeyEvent& xkey)
// KeyPress event that has the same key and time as
// this release event, if any. first prepare the
// filter info.
CKeyEventInfo filter;
CKeyEventFilter filter;
filter.m_event = KeyPress;
filter.m_window = xkey.window;
filter.m_time = xkey.time;
@ -1089,9 +1089,9 @@ CXWindowsScreen::onKeyRelease(XKeyEvent& xkey)
KeyModifierMask keyMask = m_keyState->getMaskForKey(keycode);
if (m_keyState->isHalfDuplex(keyMask)) {
sendEvent(getKeyDownEvent(),
new CKeyInfo(key, mask, keycode, 1));
CKeyInfo::alloc(key, mask, keycode, 1));
}
sendEvent(getKeyUpEvent(), new CKeyInfo(key, mask, keycode, 1));
sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask, keycode, 1));
}
else {
// found a press event following so it's a repeat.
@ -1099,7 +1099,8 @@ CXWindowsScreen::onKeyRelease(XKeyEvent& xkey)
// repeats but we'll just send a repeat of 1.
// note that we discard the press event.
LOG((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", keycode, xkey.state));
sendEvent(getKeyRepeatEvent(), new CKeyInfo(key, mask, keycode, 1));
sendEvent(getKeyRepeatEvent(),
CKeyInfo::alloc(key, mask, keycode, 1));
}
}
}
@ -1110,7 +1111,7 @@ CXWindowsScreen::onMousePress(const XButtonEvent& xbutton)
LOG((CLOG_DEBUG1 "event: ButtonPress button=%d", xbutton.button));
const ButtonID button = mapButtonFromX(&xbutton);
if (button != kButtonNone) {
sendEvent(getButtonDownEvent(), new CButtonInfo(button));
sendEvent(getButtonDownEvent(), CButtonInfo::alloc(button));
}
}
@ -1120,15 +1121,15 @@ CXWindowsScreen::onMouseRelease(const XButtonEvent& xbutton)
LOG((CLOG_DEBUG1 "event: ButtonRelease button=%d", xbutton.button));
const ButtonID button = mapButtonFromX(&xbutton);
if (button != kButtonNone) {
sendEvent(getButtonUpEvent(), new CButtonInfo(button));
sendEvent(getButtonUpEvent(), CButtonInfo::alloc(button));
}
else if (xbutton.button == 4) {
// wheel forward (away from user)
sendEvent(getWheelEvent(), new CWheelInfo(120));
sendEvent(getWheelEvent(), CWheelInfo::alloc(120));
}
else if (xbutton.button == 5) {
// wheel backward (toward user)
sendEvent(getWheelEvent(), new CWheelInfo(-120));
sendEvent(getWheelEvent(), CWheelInfo::alloc(-120));
}
}
@ -1160,7 +1161,7 @@ CXWindowsScreen::onMouseMove(const XMotionEvent& xmotion)
else if (m_isOnScreen) {
// motion on primary screen
sendEvent(getMotionOnPrimaryEvent(),
new CMotionInfo(m_xCursor, m_yCursor));
CMotionInfo::alloc(m_xCursor, m_yCursor));
}
else {
// motion on secondary screen. warp mouse back to
@ -1190,7 +1191,7 @@ CXWindowsScreen::onMouseMove(const XMotionEvent& xmotion)
// warping to the primary screen's enter position,
// effectively overriding it.
if (x != 0 || y != 0) {
sendEvent(getMotionOnSecondaryEvent(), new CMotionInfo(x, y));
sendEvent(getMotionOnSecondaryEvent(), CMotionInfo::alloc(x, y));
}
}
}

View File

@ -108,7 +108,7 @@ private:
static int ioErrorHandler(Display*);
private:
class CKeyEventInfo {
class CKeyEventFilter {
public:
int m_event;
Window m_window;

View File

@ -78,7 +78,7 @@ public:
UInt32 seqNum, KeyModifierMask mask,
bool forScreensaver) = 0;
virtual bool leave() = 0;
virtual void setClipboard(ClipboardID, const CString&) = 0;
virtual void setClipboard(ClipboardID, const IClipboard*) = 0;
virtual void grabClipboard(ClipboardID) = 0;
virtual void setClipboardDirty(ClipboardID, bool) = 0;
virtual void keyDown(KeyID, KeyModifierMask, KeyButton) = 0;

View File

@ -218,11 +218,9 @@ CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
}
void
CClientProxy1_0::getCursorPos(SInt32& x, SInt32& y) const
CClientProxy1_0::getCursorPos(SInt32&, SInt32&) const
{
assert(0 && "shouldn't be called");
x = m_info.m_mx;
y = m_info.m_my;
}
void
@ -245,13 +243,15 @@ CClientProxy1_0::leave()
}
void
CClientProxy1_0::setClipboard(ClipboardID id, const CString& data)
CClientProxy1_0::setClipboard(ClipboardID id, const IClipboard* clipboard)
{
// ignore if this clipboard is already clean
if (m_clipboard[id].m_dirty) {
// this clipboard is now clean
m_clipboard[id].m_dirty = false;
CClipboard::copy(&m_clipboard[id].m_clipboard, clipboard);
CString data = m_clipboard[id].m_clipboard.marshall();
LOG((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getName().c_str(), data.size()));
CProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, &data);
}
@ -366,29 +366,23 @@ bool
CClientProxy1_0::recvInfo()
{
// parse the message
SInt16 x, y, w, h, zoneSize, mx, my;
SInt16 x, y, w, h, dummy1, dummy2, dummy3;
if (!CProtocolUtil::readf(getStream(), kMsgDInfo + 4,
&x, &y, &w, &h, &zoneSize, &mx, &my)) {
&x, &y, &w, &h, &dummy1, &dummy2, &dummy3)) {
return false;
}
LOG((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d, zone=%d, pos=%d,%d", getName().c_str(), x, y, w, h, zoneSize, mx, my));
LOG((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d", getName().c_str(), x, y, w, h));
// validate
if (w <= 0 || h <= 0 || zoneSize < 0) {
return false;
}
if (mx < x || my < y || mx >= x + w || my >= y + h) {
if (w <= 0 || h <= 0) {
return false;
}
// save
m_info.m_x = x;
m_info.m_y = y;
m_info.m_w = w;
m_info.m_h = h;
m_info.m_zoneSize = zoneSize;
m_info.m_mx = mx;
m_info.m_my = my;
m_info.m_x = x;
m_info.m_y = y;
m_info.m_w = w;
m_info.m_h = h;
// acknowledge receipt
LOG((CLOG_DEBUG1 "send info ack to \"%s\"", getName().c_str()));

View File

@ -39,7 +39,7 @@ public:
UInt32 seqNum, KeyModifierMask mask,
bool forScreensaver);
virtual bool leave();
virtual void setClipboard(ClipboardID, const CString&);
virtual void setClipboard(ClipboardID, const IClipboard*);
virtual void grabClipboard(ClipboardID);
virtual void setClipboardDirty(ClipboardID, bool);
virtual void keyDown(KeyID, KeyModifierMask, KeyButton);

View File

@ -761,8 +761,9 @@ CConfig::readSectionOptions(std::istream& s)
try {
m_synergyAddress = CNetworkAddress(value, kDefaultPort);
}
catch (XSocketAddress&) {
throw XConfigRead("invalid address argument");
catch (XSocketAddress& e) {
throw XConfigRead(CString("invalid address argument: ") +
e.what());
}
}
else if (name == "heartbeat") {

View File

@ -42,6 +42,12 @@ CPrimaryClient::reconfigure(UInt32 activeSides)
m_screen->reconfigure(activeSides);
}
SInt32
CPrimaryClient::getJumpZoneSize() const
{
return m_screen->getJumpZoneSize();
}
void
CPrimaryClient::getCursorCenter(SInt32& x, SInt32& y) const
{
@ -72,12 +78,6 @@ CPrimaryClient::getClipboard(ClipboardID id, IClipboard* clipboard) const
return m_screen->getClipboard(id, clipboard);
}
SInt32
CPrimaryClient::getJumpZoneSize() const
{
return m_screen->getJumpZoneSize();
}
void
CPrimaryClient::getShape(SInt32& x, SInt32& y,
SInt32& width, SInt32& height) const
@ -121,19 +121,15 @@ CPrimaryClient::leave()
}
void
CPrimaryClient::setClipboard(ClipboardID id, const CString& data)
CPrimaryClient::setClipboard(ClipboardID id, const IClipboard* clipboard)
{
// ignore if this clipboard is already clean
if (m_clipboardDirty[id]) {
// this clipboard is now clean
m_clipboardDirty[id] = false;
// unmarshall data
CClipboard clipboard;
clipboard.unmarshall(data, 0);
// set clipboard
m_screen->setClipboard(id, &clipboard);
m_screen->setClipboard(id, clipboard);
}
}

View File

@ -47,6 +47,13 @@ public:
//! @name accessors
//@{
//! Get jump zone size
/*!
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.
*/
SInt32 getJumpZoneSize() const;
//! Get cursor center position
/*!
Return the cursor center position which is where we park the
@ -76,7 +83,6 @@ public:
// 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;
@ -86,7 +92,7 @@ public:
UInt32 seqNum, KeyModifierMask mask,
bool forScreensaver);
virtual bool leave();
virtual void setClipboard(ClipboardID, const CString&);
virtual void setClipboard(ClipboardID, const IClipboard*);
virtual void grabClipboard(ClipboardID);
virtual void setClipboardDirty(ClipboardID, bool);
virtual void keyDown(KeyID, KeyModifierMask, KeyButton);

View File

@ -16,7 +16,6 @@
#include "CClientProxy.h"
#include "CClientProxyUnknown.h"
#include "CPrimaryClient.h"
#include "CPacketStreamFilter.h"
#include "IPlatformScreen.h"
#include "OptionTypes.h"
#include "ProtocolTypes.h"
@ -205,6 +204,11 @@ CServer::adoptClient(IClient* client)
{
assert(client != NULL);
// watch for client disconnection
EVENTQUEUE->adoptHandler(CClientProxy::getDisconnectedEvent(), client,
new TMethodEventJob<CServer>(this,
&CServer::handleClientDisconnected, client));
// name must be in our configuration
if (!m_config.isScreen(client->getName())) {
LOG((CLOG_WARN "a client with name \"%s\" is not in the map", client->getName().c_str()));
@ -221,11 +225,6 @@ CServer::adoptClient(IClient* client)
}
LOG((CLOG_NOTE "client \"%s\" has connected", getName(client).c_str()));
// watch for client disconnection
EVENTQUEUE->adoptHandler(CClientProxy::getDisconnectedEvent(), client,
new TMethodEventJob<CServer>(this,
&CServer::handleClientDisconnected, client));
// send configuration options to client
sendOptions(client);
@ -287,7 +286,11 @@ CServer::onCommandKey(KeyID /*id*/, KeyModifierMask /*mask*/, bool /*down*/)
CString
CServer::getName(const IClient* client) const
{
return m_config.getCanonicalName(client->getName());
CString name = m_config.getCanonicalName(client->getName());
if (name.empty()) {
name = client->getName();
}
return name;
}
UInt32
@ -397,7 +400,7 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver)
// send the clipboard data to new active screen
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
m_active->setClipboard(id, m_clipboards[id].m_clipboardData);
m_active->setClipboard(id, &m_clipboards[id].m_clipboard);
}
}
else {
@ -1078,7 +1081,7 @@ CServer::onClipboardChanged(IClient* sender, ClipboardID id, UInt32 seqNum)
}
// send the new clipboard to the active screen
m_active->setClipboard(id, clipboard.m_clipboardData);
m_active->setClipboard(id, &clipboard.m_clipboard);
}
void

View File

@ -101,126 +101,14 @@ CClipboard::get(EFormat format) const
return m_data[format];
}
bool
CClipboard::copy(IClipboard* dst, const IClipboard* src)
{
assert(dst != NULL);
assert(src != NULL);
return copy(dst, src, src->getTime());
}
bool
CClipboard::copy(IClipboard* dst, const IClipboard* src, Time time)
{
assert(dst != NULL);
assert(src != NULL);
bool success = false;
if (src->open(time)) {
if (dst->open(time)) {
if (dst->empty()) {
for (SInt32 format = 0;
format != IClipboard::kNumFormats; ++format) {
IClipboard::EFormat eFormat = (IClipboard::EFormat)format;
if (src->has(eFormat)) {
dst->add(eFormat, src->get(eFormat));
}
}
success = true;
}
dst->close();
}
src->close();
}
return success;
}
void
CClipboard::unmarshall(const CString& data, Time time)
{
const char* index = data.data();
// clear existing data
open(time);
empty();
// read the number of formats
const UInt32 numFormats = readUInt32(index);
index += 4;
// read each format
for (UInt32 i = 0; i < numFormats; ++i) {
// get the format id
UInt32 format = readUInt32(index);
index += 4;
// get the size of the format data
UInt32 size = readUInt32(index);
index += 4;
// save the data if it's a known format. if either the client
// or server supports more clipboard formats than the other
// then one of them will get a format >= kNumFormats here.
if (format < static_cast<UInt32>(IClipboard::kNumFormats)) {
m_added[format] = true;
m_data[format] = CString(index, size);
}
index += size;
}
// done
close();
IClipboard::unmarshall(this, data, time);
}
CString
CClipboard::marshall() const
{
CString data;
// compute size of marshalled data
UInt32 size = 4;
UInt32 numFormats = 0;
UInt32 format;
for (format = 0; format != IClipboard::kNumFormats; ++format) {
if (m_added[format]) {
++numFormats;
size += 4 + 4 + m_data[format].size();
}
}
// allocate space
data.reserve(size);
// marshall the data
writeUInt32(&data, numFormats);
for (format = 0; format != IClipboard::kNumFormats; ++format) {
if (m_added[format]) {
writeUInt32(&data, format);
writeUInt32(&data, m_data[format].size());
data += m_data[format];
}
}
return data;
}
UInt32
CClipboard::readUInt32(const char* buf) const
{
const unsigned char* ubuf = reinterpret_cast<const unsigned char*>(buf);
return (static_cast<UInt32>(ubuf[0]) << 24) |
(static_cast<UInt32>(ubuf[1]) << 16) |
(static_cast<UInt32>(ubuf[2]) << 8) |
static_cast<UInt32>(ubuf[3]);
}
void
CClipboard::writeUInt32(CString* buf, UInt32 v) const
{
*buf += static_cast<UInt8>((v >> 24) & 0xff);
*buf += static_cast<UInt8>((v >> 16) & 0xff);
*buf += static_cast<UInt8>((v >> 8) & 0xff);
*buf += static_cast<UInt8>( v & 0xff);
return IClipboard::marshall(this);
}

View File

@ -47,25 +47,6 @@ public:
*/
CString marshall() const;
//! Copy clipboard
/*!
Transfers all the data in one clipboard to another. The
clipboards can be of any concrete clipboard type (and
they don't have to be the same type). This also sets
the destination clipboard's timestamp to source clipboard's
timestamp. Returns true iff the copy succeeded.
*/
static bool copy(IClipboard* dst, const IClipboard* src);
//! Copy clipboard
/*!
Transfers all the data in one clipboard to another. The
clipboards can be of any concrete clipboard type (and they
don't have to be the same type). This also sets the
timestamp to \c time. Returns true iff the copy succeeded.
*/
static bool copy(IClipboard* dst, const IClipboard* src, Time);
//@}
// IClipboard overrides
@ -77,10 +58,6 @@ public:
virtual bool has(EFormat) const;
virtual CString get(EFormat) const;
private:
UInt32 readUInt32(const char*) const;
void writeUInt32(CString*, UInt32) const;
private:
mutable bool m_open;
mutable Time m_time;

View File

@ -34,7 +34,9 @@ CPacketStreamFilter::CPacketStreamFilter(IStream* stream, bool adoptStream) :
CPacketStreamFilter::~CPacketStreamFilter()
{
delete getStream()->getEventFilter();
IEventJob* job = getStream()->getEventFilter();
getStream()->setEventFilter(NULL);
delete job;
}
void

View File

@ -58,7 +58,7 @@ public:
already known to be up to date then this may do nothing. \c data
has marshalled clipboard data.
*/
virtual void setClipboard(ClipboardID, const CString& data) = 0;
virtual void setClipboard(ClipboardID, const IClipboard*) = 0;
//! Grab clipboard
/*!

155
lib/synergy/IClipboard.cpp Normal file
View File

@ -0,0 +1,155 @@
/*
* 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 "IClipboard.h"
#include "stdvector.h"
//
// IClipboard
//
void
IClipboard::unmarshall(IClipboard* clipboard, const CString& data, Time time)
{
assert(clipboard != NULL);
const char* index = data.data();
// clear existing data
clipboard->open(time);
clipboard->empty();
// read the number of formats
const UInt32 numFormats = readUInt32(index);
index += 4;
// read each format
for (UInt32 i = 0; i < numFormats; ++i) {
// get the format id
IClipboard::EFormat format =
static_cast<IClipboard::EFormat>(readUInt32(index));
index += 4;
// get the size of the format data
UInt32 size = readUInt32(index);
index += 4;
// save the data if it's a known format. if either the client
// or server supports more clipboard formats than the other
// then one of them will get a format >= kNumFormats here.
if (format <IClipboard::kNumFormats) {
clipboard->add(format, CString(index, size));
}
index += size;
}
// done
clipboard->close();
}
CString
IClipboard::marshall(const IClipboard* clipboard)
{
assert(clipboard != NULL);
CString data;
std::vector<CString> formatData;
formatData.resize(IClipboard::kNumFormats);
// FIXME -- use current time
clipboard->open(0);
// compute size of marshalled data
UInt32 size = 4;
UInt32 numFormats = 0;
for (UInt32 format = 0; format != IClipboard::kNumFormats; ++format) {
if (clipboard->has(static_cast<IClipboard::EFormat>(format))) {
++numFormats;
formatData[format] =
clipboard->get(static_cast<IClipboard::EFormat>(format));
size += 4 + 4 + formatData[format].size();
}
}
// allocate space
data.reserve(size);
// marshall the data
writeUInt32(&data, numFormats);
for (UInt32 format = 0; format != IClipboard::kNumFormats; ++format) {
if (clipboard->has(static_cast<IClipboard::EFormat>(format))) {
writeUInt32(&data, format);
writeUInt32(&data, formatData[format].size());
data += formatData[format];
}
}
clipboard->close();
return data;
}
bool
IClipboard::copy(IClipboard* dst, const IClipboard* src)
{
assert(dst != NULL);
assert(src != NULL);
return copy(dst, src, src->getTime());
}
bool
IClipboard::copy(IClipboard* dst, const IClipboard* src, Time time)
{
assert(dst != NULL);
assert(src != NULL);
bool success = false;
if (src->open(time)) {
if (dst->open(time)) {
if (dst->empty()) {
for (SInt32 format = 0;
format != IClipboard::kNumFormats; ++format) {
IClipboard::EFormat eFormat = (IClipboard::EFormat)format;
if (src->has(eFormat)) {
dst->add(eFormat, src->get(eFormat));
}
}
success = true;
}
dst->close();
}
src->close();
}
return success;
}
UInt32
IClipboard::readUInt32(const char* buf)
{
const unsigned char* ubuf = reinterpret_cast<const unsigned char*>(buf);
return (static_cast<UInt32>(ubuf[0]) << 24) |
(static_cast<UInt32>(ubuf[1]) << 16) |
(static_cast<UInt32>(ubuf[2]) << 8) |
static_cast<UInt32>(ubuf[3]);
}
void
IClipboard::writeUInt32(CString* buf, UInt32 v)
{
*buf += static_cast<UInt8>((v >> 24) & 0xff);
*buf += static_cast<UInt8>((v >> 16) & 0xff);
*buf += static_cast<UInt8>((v >> 8) & 0xff);
*buf += static_cast<UInt8>( v & 0xff);
}

View File

@ -111,7 +111,45 @@ public:
*/
virtual CString get(EFormat) const = 0;
//! Marshall clipboard data
/*!
Merge \p clipboard's data into a single buffer that can be later
unmarshalled to restore the clipboard and return the buffer.
*/
static CString marshall(const IClipboard* clipboard);
//! Unmarshall clipboard data
/*!
Extract marshalled clipboard data and store it in \p clipboard.
Sets the clipboard time to \c time.
*/
static void unmarshall(IClipboard* clipboard,
const CString& data, Time time);
//! Copy clipboard
/*!
Transfers all the data in one clipboard to another. The
clipboards can be of any concrete clipboard type (and
they don't have to be the same type). This also sets
the destination clipboard's timestamp to source clipboard's
timestamp. Returns true iff the copy succeeded.
*/
static bool copy(IClipboard* dst, const IClipboard* src);
//! Copy clipboard
/*!
Transfers all the data in one clipboard to another. The
clipboards can be of any concrete clipboard type (and they
don't have to be the same type). This also sets the
timestamp to \c time. Returns true iff the copy succeeded.
*/
static bool copy(IClipboard* dst, const IClipboard* src, Time);
//@}
private:
static UInt32 readUInt32(const char*);
static void writeUInt32(CString*, UInt32);
};
#endif

View File

@ -104,14 +104,16 @@ IPlatformScreen::getScreensaverDeactivatedEvent()
// 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)
IPlatformScreen::CKeyInfo*
IPlatformScreen::CKeyInfo::alloc(KeyID id,
KeyModifierMask mask, KeyButton button, SInt32 count)
{
// do nothing
CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo));
info->m_key = id;
info->m_mask = mask;
info->m_button = button;
info->m_count = count;
return info;
}
@ -119,10 +121,12 @@ IPlatformScreen::CKeyInfo::CKeyInfo(KeyID id,
// IPlatformScreen::CButtonInfo
//
IPlatformScreen::CButtonInfo::CButtonInfo(ButtonID id) :
m_button(id)
IPlatformScreen::CButtonInfo*
IPlatformScreen::CButtonInfo::alloc(ButtonID id)
{
// do nothing
CButtonInfo* info = (CButtonInfo*)malloc(sizeof(CButtonInfo));
info->m_button = id;
return info;
}
@ -130,11 +134,13 @@ IPlatformScreen::CButtonInfo::CButtonInfo(ButtonID id) :
// IPlatformScreen::CMotionInfo
//
IPlatformScreen::CMotionInfo::CMotionInfo(SInt32 x, SInt32 y) :
m_x(x),
m_y(y)
IPlatformScreen::CMotionInfo*
IPlatformScreen::CMotionInfo::alloc(SInt32 x, SInt32 y)
{
// do nothing
CMotionInfo* info = (CMotionInfo*)malloc(sizeof(CMotionInfo));
info->m_x = x;
info->m_y = y;
return info;
}
@ -142,8 +148,10 @@ IPlatformScreen::CMotionInfo::CMotionInfo(SInt32 x, SInt32 y) :
// IPlatformScreen::CWheelInfo
//
IPlatformScreen::CWheelInfo::CWheelInfo(SInt32 wheel) :
m_wheel(wheel)
IPlatformScreen::CWheelInfo*
IPlatformScreen::CWheelInfo::alloc(SInt32 wheel)
{
// do nothing
CWheelInfo* info = (CWheelInfo*)malloc(sizeof(CWheelInfo));
info->m_wheel = wheel;
return info;
}

View File

@ -40,7 +40,7 @@ public:
//! Key event data
class CKeyInfo {
public:
CKeyInfo(KeyID, KeyModifierMask, KeyButton, SInt32 count);
static CKeyInfo* alloc(KeyID, KeyModifierMask, KeyButton, SInt32 count);
public:
KeyID m_key;
@ -51,7 +51,7 @@ public:
//! Button event data
class CButtonInfo {
public:
CButtonInfo(ButtonID);
static CButtonInfo* alloc(ButtonID);
public:
ButtonID m_button;
@ -59,7 +59,7 @@ public:
//! Motion event data
class CMotionInfo {
public:
CMotionInfo(SInt32 x, SInt32 y);
static CMotionInfo* alloc(SInt32 x, SInt32 y);
public:
SInt32 m_x;
@ -68,7 +68,7 @@ public:
//! Wheel motion event data
class CWheelInfo {
public:
CWheelInfo(SInt32);
static CWheelInfo* alloc(SInt32);
public:
SInt32 m_wheel;

View File

@ -29,6 +29,7 @@ libsynergy_a_SOURCES = \
CPacketStreamFilter.cpp \
CProtocolUtil.cpp \
CScreen.cpp \
IClipboard.cpp \
IPlatformScreen.cpp \
IScreen.cpp \
XScreen.cpp \

View File

@ -267,19 +267,11 @@ public:
*/
SInt32 m_w, m_h;
//! Jump zone size
/*!
This is the size of the jump zone. The cursor jumps to the adjacent
screen when it comes within this many pixels of the edge of the screen.
*/
SInt32 m_zoneSize;
//! Obsolete (jump zone size)
SInt32 obsolete1;
//! Mouse position
/*!
The position of the cursor. This is not kept up-to-date so it's
only meaningful when receiving an update.
*/
SInt32 m_mx, m_my;
//! Obsolete (mouse position)
SInt32 obsolete2, obsolete3;
};
#endif