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 "CClientTaskBarReceiver.h"
#include "CClient.h" #include "CClient.h"
#include "CLock.h" #include "CLock.h"
#include "TMethodJob.h" #include "IEventQueue.h"
#include "CArch.h" #include "CArch.h"
// //
@ -23,71 +23,73 @@
// //
CClientTaskBarReceiver::CClientTaskBarReceiver() : CClientTaskBarReceiver::CClientTaskBarReceiver() :
m_quit(NULL), m_state(kNotRunning)
m_state(kNotRunning),
m_client(NULL)
{ {
// create a job for getting notification when the client's // do nothing
// status changes.
m_job = new TMethodJob<CClientTaskBarReceiver>(this,
&CClientTaskBarReceiver::statusChanged, NULL);
} }
CClientTaskBarReceiver::~CClientTaskBarReceiver() CClientTaskBarReceiver::~CClientTaskBarReceiver()
{ {
if (m_client != NULL) { // do nothing
m_client->removeStatusJob(m_job);
}
delete m_job;
delete m_quit;
} }
void void
CClientTaskBarReceiver::setClient(CClient* client) CClientTaskBarReceiver::updateStatus(CClient* client, const CString& errorMsg)
{ {
{ {
// update our status
CLock lock(&m_mutex); CLock lock(&m_mutex);
if (m_client != client) { m_errorMessage = errorMsg;
if (m_client != NULL) { if (client == NULL) {
m_client->removeStatusJob(m_job); if (m_errorMessage.empty()) {
m_state = kNotRunning;
} }
m_client = client; else {
if (m_client != NULL) { m_state = kNotWorking;
m_client->addStatusJob(m_job); }
}
else {
if (client->isConnected()) {
m_state = kConnected;
}
else if (client->isConnecting()) {
m_state = kConnecting;
}
else {
m_state = kNotConnected;
} }
} }
}
ARCH->updateReceiver(this);
}
void // let subclasses have a go
CClientTaskBarReceiver::setState(EState state) onStatusChanged(client);
{
{
CLock lock(&m_mutex);
m_state = state;
} }
ARCH->updateReceiver(this);
}
void // tell task bar
CClientTaskBarReceiver::setQuitJob(IJob* job) ARCH->updateReceiver(this);
{
CLock lock(&m_mutex);
delete m_quit;
m_quit = job;
} }
CClientTaskBarReceiver::EState CClientTaskBarReceiver::EState
CClientTaskBarReceiver::getState() const CClientTaskBarReceiver::getStatus() const
{ {
return m_state; return m_state;
} }
CClient* const CString&
CClientTaskBarReceiver::getClient() const CClientTaskBarReceiver::getErrorMessage() const
{ {
return m_client; return m_errorMessage;
}
void
CClientTaskBarReceiver::quit()
{
EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
}
void
CClientTaskBarReceiver::onStatusChanged(CClient*)
{
// do nothing
} }
void void
@ -113,7 +115,10 @@ CClientTaskBarReceiver::getToolTip() const
return CString("Synergy: ") + m_errorMessage; return CString("Synergy: ") + m_errorMessage;
case kNotConnected: case kNotConnected:
return "Synergy: Waiting for clients"; return CString("Synergy: Not connected: ") + m_errorMessage;
case kConnecting:
return "Synergy: Connecting...";
case kConnected: case kConnected:
return "Synergy: Connected"; return "Synergy: Connected";
@ -122,42 +127,3 @@ CClientTaskBarReceiver::getToolTip() const
return ""; 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" #include "IArchTaskBarReceiver.h"
class CClient; class CClient;
class IJob;
//! Implementation of IArchTaskBarReceiver for the synergy server //! Implementation of IArchTaskBarReceiver for the synergy server
class CClientTaskBarReceiver : public IArchTaskBarReceiver { class CClientTaskBarReceiver : public IArchTaskBarReceiver {
public: public:
enum EState {
kNotRunning,
kNotWorking,
kNotConnected,
kConnected,
kMaxState
};
CClientTaskBarReceiver(); CClientTaskBarReceiver();
virtual ~CClientTaskBarReceiver(); virtual ~CClientTaskBarReceiver();
//! @name manipulators //! @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*); void updateStatus(CClient*, const CString& errorMsg);
//! 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;
//@} //@}
@ -86,24 +48,36 @@ public:
virtual std::string getToolTip() const; virtual std::string getToolTip() const;
protected: 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(); void quit();
//! Status change notification //! Status change notification
/*! /*!
Called when status changes. The default implementation does Called when status changes. The default implementation does nothing.
nothing.
*/ */
virtual void onStatusChanged(); virtual void onStatusChanged(CClient* client);
private:
void statusChanged(void*);
private: private:
CMutex m_mutex; CMutex m_mutex;
IJob* m_quit;
EState m_state; EState m_state;
CClient* m_client;
IJob* m_job;
CString m_errorMessage; CString m_errorMessage;
}; };

View File

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

View File

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

View File

@ -13,23 +13,22 @@
*/ */
#include "CClient.h" #include "CClient.h"
#include "IScreenFactory.h" #include "CScreen.h"
#include "ProtocolTypes.h" #include "ProtocolTypes.h"
#include "Version.h" #include "Version.h"
#include "XScreen.h" #include "XScreen.h"
#include "CNetworkAddress.h" #include "CNetworkAddress.h"
#include "CSocketMultiplexer.h"
#include "CTCPSocketFactory.h" #include "CTCPSocketFactory.h"
#include "XSocket.h" #include "XSocket.h"
#include "CCondVar.h"
#include "CLock.h"
#include "CMutex.h"
#include "CThread.h" #include "CThread.h"
#include "XThread.h" #include "CEventQueue.h"
#include "CFunctionJob.h" #include "CFunctionEventJob.h"
#include "CLog.h" #include "CLog.h"
#include "LogOutputters.h"
#include "CString.h" #include "CString.h"
#include "LogOutputters.h"
#include "CArch.h" #include "CArch.h"
#include "XArch.h"
#include <cstring> #include <cstring>
#define DAEMON_RUNNING(running_) #define DAEMON_RUNNING(running_)
@ -87,160 +86,290 @@ CArgs* CArgs::s_instance = NULL;
// platform dependent factories // platform dependent factories
// //
//! Factory for creating screens static
/*! CScreen*
Objects of this type create screens appropriate for the platform. createScreen()
*/
class CScreenFactory : public IScreenFactory {
public:
CScreenFactory() { }
virtual ~CScreenFactory() { }
// IScreenFactory overrides
virtual IPlatformScreen*
create(IScreenReceiver*, IPrimaryScreenReceiver*);
};
IPlatformScreen*
CScreenFactory::create(IScreenReceiver* receiver,
IPrimaryScreenReceiver* primaryReceiver)
{ {
#if WINDOWS_LIKE #if WINDOWS_LIKE
return new CMSWindowsScreen(receiver, primaryReceiver); return new CScreen(new CMSWindowsScreen(false));
#elif UNIX_LIKE #elif UNIX_LIKE
return new CXWindowsScreen(receiver, primaryReceiver); return new CScreen(new CXWindowsScreen(false));
#endif #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 // platform independent main
// //
static CClient* s_client = NULL; static CClient* s_client = NULL;
static CScreen* s_clientScreen = NULL;
static CClientTaskBarReceiver* s_taskBarReceiver = 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 static
int int
realMain(void) realMain()
{ {
int result = kExitSuccess; // start the client. if this return false then we've failed and
do { // we shouldn't retry.
bool opened = false; LOG((CLOG_DEBUG1 "starting client"));
bool locked = true; if (!startClient()) {
try { return kExitFailed;
// 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);
// open client // run event loop. if startClient() failed we're supposed to retry
try { // later. the timer installed by startClient() will take care of
s_taskBarReceiver->setClient(s_client); // that.
s_client->open(); DAEMON_RUNNING(true);
opened = 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 // close down
DAEMON_RUNNING(true); LOG((CLOG_DEBUG1 "stopping client"));
locked = false; stopClient();
s_client->mainLoop(); updateStatus();
locked = true; LOG((CLOG_NOTE "stopped client"));
DAEMON_RUNNING(false);
// get client status return kExitSuccess;
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;
} }
/*
static static
void void
realMainEntry(void* vresult) realMainEntry(void* vresult)
@ -278,6 +407,7 @@ runMainInThread(void)
throw; throw;
} }
} }
*/
// //
@ -292,14 +422,12 @@ static
void void
version() version()
{ {
LOG((CLOG_PRINT LOG((CLOG_PRINT "%s %s, protocol version %d.%d\n%s",
"%s %s, protocol version %d.%d\n" ARG->m_pname,
"%s", kVersion,
ARG->m_pname, kProtocolMajorVersion,
kVersion, kProtocolMinorVersion,
kProtocolMajorVersion, kCopyright));
kProtocolMinorVersion,
kCopyright));
} }
static static
@ -697,15 +825,20 @@ main(int argc, char** argv)
{ {
CArch arch; CArch arch;
CLOG; CLOG;
CArgs args;
// go really fast
CThread::getCurrentThread().setPriority(-14);
CSocketMultiplexer multiplexer;
CEventQueue eventQueue;
// get program name // get program name
CArgs args;
ARG->m_pname = ARCH->getBasename(argv[0]); ARG->m_pname = ARCH->getBasename(argv[0]);
// make the task bar receiver. the user can control this app // make the task bar receiver. the user can control this app
// through the task bar. // through the task bar.
s_taskBarReceiver = new CXWindowsClientTaskBarReceiver; s_taskBarReceiver = new CXWindowsClientTaskBarReceiver;
s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread()));
// parse command line // parse command line
parse(argc, argv); parse(argc, argv);

View File

@ -14,8 +14,8 @@
#include "CServerTaskBarReceiver.h" #include "CServerTaskBarReceiver.h"
#include "CServer.h" #include "CServer.h"
#include "CEventQueue.h"
#include "CLock.h" #include "CLock.h"
#include "IEventQueue.h"
#include "CArch.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 static
CScreen* CScreen*
openServerScreen() openServerScreen()
{ {
return createScreen(); CScreen* screen = createScreen();
EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(),
screen->getEventTarget(),
new CFunctionEventJob(
&handleScreenError));
return screen;
} }
static static
void void
closeServerScreen(CScreen* screen) closeServerScreen(CScreen* screen)
{ {
delete screen; if (screen != NULL) {
} EVENTQUEUE->removeHandler(IScreen::getErrorEvent(),
screen->getEventTarget());
static delete screen;
void }
handleScreenError(const CEvent&, void*)
{
EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
} }
static static
@ -197,23 +207,14 @@ CPrimaryClient*
openPrimaryClient(const CString& name, CScreen* screen) openPrimaryClient(const CString& name, CScreen* screen)
{ {
LOG((CLOG_DEBUG1 "creating primary screen")); LOG((CLOG_DEBUG1 "creating primary screen"));
CPrimaryClient* primaryClient = new CPrimaryClient(name, screen); return new CPrimaryClient(name, screen);
EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(),
primaryClient->getEventTarget(),
new CFunctionEventJob(
&handleScreenError));
return primaryClient;
} }
static static
void void
closePrimaryClient(CPrimaryClient* primaryClient) closePrimaryClient(CPrimaryClient* primaryClient)
{ {
if (primaryClient != NULL) { delete primaryClient;
EVENTQUEUE->removeHandler(IScreen::getErrorEvent(),
primaryClient->getEventTarget());
delete primaryClient;
}
} }
static static
@ -711,7 +712,7 @@ parse(int argc, const char* const* argv)
static static
bool bool
loadConfig(const char* pathname, bool require) loadConfig(const char* pathname)
{ {
assert(pathname != NULL); assert(pathname != NULL);
@ -727,15 +728,8 @@ loadConfig(const char* pathname, bool require)
return true; return true;
} }
catch (XConfigRead& e) { catch (XConfigRead& e) {
if (require) { LOG((CLOG_DEBUG "cannot read configuration \"%s\": %s",
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",
pathname, e.what())); pathname, e.what()));
}
} }
return false; return false;
} }
@ -744,34 +738,38 @@ static
void void
loadConfig() loadConfig()
{ {
bool loaded = false;
// load the config file, if specified // load the config file, if specified
if (ARG->m_configFile != NULL) { if (ARG->m_configFile != NULL) {
// require the user specified file to load correctly loaded = loadConfig(ARG->m_configFile);
loadConfig(ARG->m_configFile, true);
} }
// load the default configuration if no explicit file given // load the default configuration if no explicit file given
else { else {
// get the user's home directory. use the effective user id // get the user's home directory
// so a user can't get a setuid root program to load his file.
bool loaded = false;
CString path = ARCH->getUserDirectory(); CString path = ARCH->getUserDirectory();
if (!path.empty()) { if (!path.empty()) {
// complete path // complete path
path = ARCH->concatPath(path, USR_CONFIG_NAME); path = ARCH->concatPath(path, USR_CONFIG_NAME);
// now try loading the user's configuration // now try loading the user's configuration
loaded = loadConfig(path.c_str(), false); loaded = loadConfig(path.c_str());
} }
if (!loaded) { if (!loaded) {
// try the system-wide config file // try the system-wide config file
path = ARCH->getSystemDirectory(); path = ARCH->getSystemDirectory();
if (!path.empty()) { if (!path.empty()) {
path = ARCH->concatPath(path, SYS_CONFIG_NAME); 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)); sizeof(inaddr.sin_addr));
memcpy(&addr->m_addr, &inaddr, addr->m_len); 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 // done with static buffer
ARCH->unlockMutex(m_mutex); ARCH->unlockMutex(m_mutex);

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -15,133 +15,113 @@
#ifndef CCLIENT_H #ifndef CCLIENT_H
#define CCLIENT_H #define CCLIENT_H
#include "IScreenReceiver.h"
#include "IClient.h" #include "IClient.h"
#include "IClipboard.h" #include "IClipboard.h"
#include "CNetworkAddress.h" #include "CNetworkAddress.h"
#include "CMutex.h"
#include "CJobList.h"
class CEventQueueTimer;
class CScreen; class CScreen;
class CServerProxy; class CServerProxy;
class CThread;
class IDataSocket; class IDataSocket;
class IScreenReceiver;
class IScreenFactory;
class ISocketFactory; class ISocketFactory;
class IStream;
class IStreamFilterFactory; class IStreamFilterFactory;
//! Synergy client //! Synergy client
/*! /*!
This class implements the top-level client algorithms for synergy. This class implements the top-level client algorithms for synergy.
*/ */
class CClient : public IScreenReceiver, public IClient { class CClient : public IClient {
public: public:
enum EStatus { class CFailInfo {
kNotRunning, public:
kRunning, bool m_retry;
kError, char m_what[1];
kMaxStatus
}; };
/*! /*!
This client will attempt to connect the server using \c clientName This client will attempt to connect to the server using \p name
as its 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(); ~CClient();
//! @name manipulators //! @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 Disconnects from the server with an optional error message.
calling open(). This object takes ownership of the factory.
*/ */
void setScreenFactory(IScreenFactory*); void disconnect(const char* msg);
//! 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*);
//@} //@}
//! @name accessors //! @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 // IScreen overrides
virtual void onError(); virtual void* getEventTarget() const;
virtual void onInfoChanged(const CClientInfo&); virtual bool getClipboard(ClipboardID id, IClipboard*) const;
virtual bool onGrabClipboard(ClipboardID); virtual void getShape(SInt32& x, SInt32& y,
virtual void onClipboardChanged(ClipboardID, const CString&); SInt32& width, SInt32& height) const;
virtual void getCursorPos(SInt32& x, SInt32& y) const;
// IClient overrides // IClient overrides
virtual void open();
virtual void mainLoop();
virtual void close();
virtual void enter(SInt32 xAbs, SInt32 yAbs, virtual void enter(SInt32 xAbs, SInt32 yAbs,
UInt32 seqNum, KeyModifierMask mask, UInt32 seqNum, KeyModifierMask mask,
bool forScreensaver); bool forScreensaver);
virtual bool leave(); virtual bool leave();
virtual void setClipboard(ClipboardID, const CString&); virtual void setClipboard(ClipboardID, const IClipboard*);
virtual void grabClipboard(ClipboardID); virtual void grabClipboard(ClipboardID);
virtual void setClipboardDirty(ClipboardID, bool dirty); virtual void setClipboardDirty(ClipboardID, bool);
virtual void keyDown(KeyID, KeyModifierMask, KeyButton); virtual void keyDown(KeyID, KeyModifierMask, KeyButton);
virtual void keyRepeat(KeyID, KeyModifierMask, virtual void keyRepeat(KeyID, KeyModifierMask,
SInt32 count, KeyButton); SInt32 count, KeyButton);
@ -154,52 +134,48 @@ public:
virtual void resetOptions(); virtual void resetOptions();
virtual void setOptions(const COptionsList& options); virtual void setOptions(const COptionsList& options);
virtual CString getName() const; 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: 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); void sendClipboard(ClipboardID);
void sendEvent(CEvent::Type, void*);
// handle server messaging void sendConnectionFailedEvent(const char* msg);
void runSession(void*); void setupConnecting();
void deleteSession(double timeout = -1.0); void setupConnection();
void runServer(); void setupScreen();
CServerProxy* handshakeServer(IDataSocket*); 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: private:
CMutex m_mutex; CString m_name;
CString m_name; CNetworkAddress m_serverAddress;
CScreen* m_screen; ISocketFactory* m_socketFactory;
IScreenReceiver* m_server;
CNetworkAddress m_serverAddress;
IScreenFactory* m_screenFactory;
ISocketFactory* m_socketFactory;
IStreamFilterFactory* m_streamFilterFactory; IStreamFilterFactory* m_streamFilterFactory;
CThread* m_session; CScreen* m_screen;
bool m_active; IStream* m_stream;
bool m_rejected; CEventQueueTimer* m_timer;
CServerProxy* m_server;
bool m_ready;
bool m_active;
bool m_ownClipboard[kClipboardEnd]; bool m_ownClipboard[kClipboardEnd];
IClipboard::Time m_timeClipboard[kClipboardEnd]; IClipboard::Time m_timeClipboard[kClipboardEnd];
CString m_dataClipboard[kClipboardEnd]; CString m_dataClipboard[kClipboardEnd];
// the status change jobs and status static CEvent::Type s_connectedEvent;
CJobList m_statusJobs; static CEvent::Type s_connectionFailedEvent;
EStatus m_status; static CEvent::Type s_disconnectedEvent;
CString m_statusMessage;
}; };
#endif #endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -78,7 +78,7 @@ public:
UInt32 seqNum, KeyModifierMask mask, UInt32 seqNum, KeyModifierMask mask,
bool forScreensaver) = 0; bool forScreensaver) = 0;
virtual bool leave() = 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 grabClipboard(ClipboardID) = 0;
virtual void setClipboardDirty(ClipboardID, bool) = 0; virtual void setClipboardDirty(ClipboardID, bool) = 0;
virtual void keyDown(KeyID, KeyModifierMask, KeyButton) = 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 void
CClientProxy1_0::getCursorPos(SInt32& x, SInt32& y) const CClientProxy1_0::getCursorPos(SInt32&, SInt32&) const
{ {
assert(0 && "shouldn't be called"); assert(0 && "shouldn't be called");
x = m_info.m_mx;
y = m_info.m_my;
} }
void void
@ -245,13 +243,15 @@ CClientProxy1_0::leave()
} }
void void
CClientProxy1_0::setClipboard(ClipboardID id, const CString& data) CClientProxy1_0::setClipboard(ClipboardID id, const IClipboard* clipboard)
{ {
// ignore if this clipboard is already clean // ignore if this clipboard is already clean
if (m_clipboard[id].m_dirty) { if (m_clipboard[id].m_dirty) {
// this clipboard is now clean // this clipboard is now clean
m_clipboard[id].m_dirty = false; 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())); LOG((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getName().c_str(), data.size()));
CProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, &data); CProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, &data);
} }
@ -366,29 +366,23 @@ bool
CClientProxy1_0::recvInfo() CClientProxy1_0::recvInfo()
{ {
// parse the message // 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, if (!CProtocolUtil::readf(getStream(), kMsgDInfo + 4,
&x, &y, &w, &h, &zoneSize, &mx, &my)) { &x, &y, &w, &h, &dummy1, &dummy2, &dummy3)) {
return false; 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 // validate
if (w <= 0 || h <= 0 || zoneSize < 0) { if (w <= 0 || h <= 0) {
return false;
}
if (mx < x || my < y || mx >= x + w || my >= y + h) {
return false; return false;
} }
// save // save
m_info.m_x = x; m_info.m_x = x;
m_info.m_y = y; m_info.m_y = y;
m_info.m_w = w; m_info.m_w = w;
m_info.m_h = h; m_info.m_h = h;
m_info.m_zoneSize = zoneSize;
m_info.m_mx = mx;
m_info.m_my = my;
// acknowledge receipt // acknowledge receipt
LOG((CLOG_DEBUG1 "send info ack to \"%s\"", getName().c_str())); LOG((CLOG_DEBUG1 "send info ack to \"%s\"", getName().c_str()));

View File

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

View File

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

View File

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

View File

@ -47,6 +47,13 @@ public:
//! @name accessors //! @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 //! Get cursor center position
/*! /*!
Return the cursor center position which is where we park the Return the cursor center position which is where we park the
@ -76,7 +83,6 @@ public:
// IScreen overrides // IScreen overrides
virtual void* getEventTarget() const; virtual void* getEventTarget() const;
virtual bool getClipboard(ClipboardID id, IClipboard*) const; virtual bool getClipboard(ClipboardID id, IClipboard*) const;
virtual SInt32 getJumpZoneSize() const;
virtual void getShape(SInt32& x, SInt32& y, virtual void getShape(SInt32& x, SInt32& y,
SInt32& width, SInt32& height) const; SInt32& width, SInt32& height) const;
virtual void getCursorPos(SInt32& x, SInt32& y) const; virtual void getCursorPos(SInt32& x, SInt32& y) const;
@ -86,7 +92,7 @@ public:
UInt32 seqNum, KeyModifierMask mask, UInt32 seqNum, KeyModifierMask mask,
bool forScreensaver); bool forScreensaver);
virtual bool leave(); virtual bool leave();
virtual void setClipboard(ClipboardID, const CString&); virtual void setClipboard(ClipboardID, const IClipboard*);
virtual void grabClipboard(ClipboardID); virtual void grabClipboard(ClipboardID);
virtual void setClipboardDirty(ClipboardID, bool); virtual void setClipboardDirty(ClipboardID, bool);
virtual void keyDown(KeyID, KeyModifierMask, KeyButton); virtual void keyDown(KeyID, KeyModifierMask, KeyButton);

View File

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

View File

@ -101,126 +101,14 @@ CClipboard::get(EFormat format) const
return m_data[format]; 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 void
CClipboard::unmarshall(const CString& data, Time time) CClipboard::unmarshall(const CString& data, Time time)
{ {
const char* index = data.data(); IClipboard::unmarshall(this, data, time);
// 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();
} }
CString CString
CClipboard::marshall() const CClipboard::marshall() const
{ {
CString data; return IClipboard::marshall(this);
// 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);
} }

View File

@ -47,25 +47,6 @@ public:
*/ */
CString marshall() const; 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 // IClipboard overrides
@ -77,10 +58,6 @@ public:
virtual bool has(EFormat) const; virtual bool has(EFormat) const;
virtual CString get(EFormat) const; virtual CString get(EFormat) const;
private:
UInt32 readUInt32(const char*) const;
void writeUInt32(CString*, UInt32) const;
private: private:
mutable bool m_open; mutable bool m_open;
mutable Time m_time; mutable Time m_time;

View File

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

View File

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

View File

@ -104,14 +104,16 @@ IPlatformScreen::getScreensaverDeactivatedEvent()
// IPlatformScreen::CKeyInfo // IPlatformScreen::CKeyInfo
// //
IPlatformScreen::CKeyInfo::CKeyInfo(KeyID id, IPlatformScreen::CKeyInfo*
KeyModifierMask mask, KeyButton button, SInt32 count) : IPlatformScreen::CKeyInfo::alloc(KeyID id,
m_key(id), KeyModifierMask mask, KeyButton button, SInt32 count)
m_mask(mask),
m_button(button),
m_count(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
// //
IPlatformScreen::CButtonInfo::CButtonInfo(ButtonID id) : IPlatformScreen::CButtonInfo*
m_button(id) 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
// //
IPlatformScreen::CMotionInfo::CMotionInfo(SInt32 x, SInt32 y) : IPlatformScreen::CMotionInfo*
m_x(x), IPlatformScreen::CMotionInfo::alloc(SInt32 x, SInt32 y)
m_y(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
// //
IPlatformScreen::CWheelInfo::CWheelInfo(SInt32 wheel) : IPlatformScreen::CWheelInfo*
m_wheel(wheel) 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 //! Key event data
class CKeyInfo { class CKeyInfo {
public: public:
CKeyInfo(KeyID, KeyModifierMask, KeyButton, SInt32 count); static CKeyInfo* alloc(KeyID, KeyModifierMask, KeyButton, SInt32 count);
public: public:
KeyID m_key; KeyID m_key;
@ -51,7 +51,7 @@ public:
//! Button event data //! Button event data
class CButtonInfo { class CButtonInfo {
public: public:
CButtonInfo(ButtonID); static CButtonInfo* alloc(ButtonID);
public: public:
ButtonID m_button; ButtonID m_button;
@ -59,7 +59,7 @@ public:
//! Motion event data //! Motion event data
class CMotionInfo { class CMotionInfo {
public: public:
CMotionInfo(SInt32 x, SInt32 y); static CMotionInfo* alloc(SInt32 x, SInt32 y);
public: public:
SInt32 m_x; SInt32 m_x;
@ -68,7 +68,7 @@ public:
//! Wheel motion event data //! Wheel motion event data
class CWheelInfo { class CWheelInfo {
public: public:
CWheelInfo(SInt32); static CWheelInfo* alloc(SInt32);
public: public:
SInt32 m_wheel; SInt32 m_wheel;

View File

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

View File

@ -267,19 +267,11 @@ public:
*/ */
SInt32 m_w, m_h; SInt32 m_w, m_h;
//! Jump zone size //! Obsolete (jump zone size)
/*! SInt32 obsolete1;
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;
//! Mouse position //! Obsolete (mouse position)
/*! SInt32 obsolete2, obsolete3;
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;
}; };
#endif #endif