diff --git a/cmd/synergyc/CClientTaskBarReceiver.cpp b/cmd/synergyc/CClientTaskBarReceiver.cpp index 9c60c5bf..e1cbcfac 100644 --- a/cmd/synergyc/CClientTaskBarReceiver.cpp +++ b/cmd/synergyc/CClientTaskBarReceiver.cpp @@ -15,7 +15,7 @@ #include "CClientTaskBarReceiver.h" #include "CClient.h" #include "CLock.h" -#include "TMethodJob.h" +#include "IEventQueue.h" #include "CArch.h" // @@ -23,71 +23,73 @@ // CClientTaskBarReceiver::CClientTaskBarReceiver() : - m_quit(NULL), - m_state(kNotRunning), - m_client(NULL) + m_state(kNotRunning) { - // create a job for getting notification when the client's - // status changes. - m_job = new TMethodJob(this, - &CClientTaskBarReceiver::statusChanged, NULL); + // do nothing } CClientTaskBarReceiver::~CClientTaskBarReceiver() { - if (m_client != NULL) { - m_client->removeStatusJob(m_job); - } - delete m_job; - delete m_quit; + // do nothing } void -CClientTaskBarReceiver::setClient(CClient* client) +CClientTaskBarReceiver::updateStatus(CClient* client, const CString& errorMsg) { { + // update our status CLock lock(&m_mutex); - if (m_client != client) { - if (m_client != NULL) { - m_client->removeStatusJob(m_job); + m_errorMessage = errorMsg; + if (client == NULL) { + if (m_errorMessage.empty()) { + m_state = kNotRunning; } - m_client = client; - if (m_client != NULL) { - m_client->addStatusJob(m_job); + else { + m_state = kNotWorking; + } + } + else { + if (client->isConnected()) { + m_state = kConnected; + } + else if (client->isConnecting()) { + m_state = kConnecting; + } + else { + m_state = kNotConnected; } } - } - ARCH->updateReceiver(this); -} -void -CClientTaskBarReceiver::setState(EState state) -{ - { - CLock lock(&m_mutex); - m_state = state; + // let subclasses have a go + onStatusChanged(client); } - ARCH->updateReceiver(this); -} -void -CClientTaskBarReceiver::setQuitJob(IJob* job) -{ - CLock lock(&m_mutex); - delete m_quit; - m_quit = job; + // tell task bar + ARCH->updateReceiver(this); } CClientTaskBarReceiver::EState -CClientTaskBarReceiver::getState() const +CClientTaskBarReceiver::getStatus() const { return m_state; } -CClient* -CClientTaskBarReceiver::getClient() const +const CString& +CClientTaskBarReceiver::getErrorMessage() const { - return m_client; + return m_errorMessage; +} + +void +CClientTaskBarReceiver::quit() +{ + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); +} + +void +CClientTaskBarReceiver::onStatusChanged(CClient*) +{ + // do nothing } void @@ -113,7 +115,10 @@ CClientTaskBarReceiver::getToolTip() const return CString("Synergy: ") + m_errorMessage; case kNotConnected: - return "Synergy: Waiting for clients"; + return CString("Synergy: Not connected: ") + m_errorMessage; + + case kConnecting: + return "Synergy: Connecting..."; case kConnected: return "Synergy: Connected"; @@ -122,42 +127,3 @@ CClientTaskBarReceiver::getToolTip() const return ""; } } - -void -CClientTaskBarReceiver::quit() -{ - if (m_quit != NULL) { - m_quit->run(); - } -} - -void -CClientTaskBarReceiver::onStatusChanged() -{ - // do nothing -} - -void -CClientTaskBarReceiver::statusChanged(void*) -{ - // update our status - switch (m_client->getStatus(&m_errorMessage)) { - case CClient::kNotRunning: - setState(kNotRunning); - break; - - case CClient::kRunning: - setState(kConnected); - break; - - case CClient::kError: - setState(kNotWorking); - break; - - default: - break; - } - - // let subclasses have a go - onStatusChanged(); -} diff --git a/cmd/synergyc/CClientTaskBarReceiver.h b/cmd/synergyc/CClientTaskBarReceiver.h index aa52b9d6..ab9c371c 100644 --- a/cmd/synergyc/CClientTaskBarReceiver.h +++ b/cmd/synergyc/CClientTaskBarReceiver.h @@ -20,59 +20,21 @@ #include "IArchTaskBarReceiver.h" class CClient; -class IJob; //! Implementation of IArchTaskBarReceiver for the synergy server class CClientTaskBarReceiver : public IArchTaskBarReceiver { public: - enum EState { - kNotRunning, - kNotWorking, - kNotConnected, - kConnected, - kMaxState - }; - CClientTaskBarReceiver(); virtual ~CClientTaskBarReceiver(); //! @name manipulators //@{ - //! Set server + //! Update status /*! - Sets the server. The receiver will query state from this server. + Determine the status and query required information from the client. */ - void setClient(CClient*); - - //! Set state - /*! - Sets the current server state. - */ - void setState(EState); - - //! Set the quit job that causes the server to quit - /*! - Set the job that causes the server to quit. - */ - void setQuitJob(IJob* adopted); - - //@} - //! @name accessors - //@{ - - //! Get state - /*! - Returns the current server state. The receiver is not locked - by this call; the caller must do the locking. - */ - EState getState() const; - - //! Get server - /*! - Returns the server set by \c setClient(). - */ - CClient* getClient() const; + void updateStatus(CClient*, const CString& errorMsg); //@} @@ -86,24 +48,36 @@ public: virtual std::string getToolTip() const; protected: + enum EState { + kNotRunning, + kNotWorking, + kNotConnected, + kConnecting, + kConnected, + kMaxState + }; + + //! Get status + EState getStatus() const; + + //! Get error message + const CString& getErrorMessage() const; + + //! Quit app + /*! + Causes the application to quit gracefully + */ void quit(); //! Status change notification /*! - Called when status changes. The default implementation does - nothing. + Called when status changes. The default implementation does nothing. */ - virtual void onStatusChanged(); - -private: - void statusChanged(void*); + virtual void onStatusChanged(CClient* client); private: CMutex m_mutex; - IJob* m_quit; EState m_state; - CClient* m_client; - IJob* m_job; CString m_errorMessage; }; diff --git a/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp index f60585ea..13fac23f 100644 --- a/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp +++ b/cmd/synergyc/CXWindowsClientTaskBarReceiver.cpp @@ -53,9 +53,3 @@ CXWindowsClientTaskBarReceiver::getIcon() const { return NULL; } - -void -CXWindowsClientTaskBarReceiver::onStatusChanged() -{ - // do nothing -} diff --git a/cmd/synergyc/CXWindowsClientTaskBarReceiver.h b/cmd/synergyc/CXWindowsClientTaskBarReceiver.h index e6028ce2..fab3a649 100644 --- a/cmd/synergyc/CXWindowsClientTaskBarReceiver.h +++ b/cmd/synergyc/CXWindowsClientTaskBarReceiver.h @@ -28,10 +28,6 @@ public: virtual void runMenu(int x, int y); virtual void primaryAction(); virtual const Icon getIcon() const; - -protected: - // CClientTaskBarReceiver overrides - virtual void onStatusChanged(); }; #endif diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index 02f84c3d..1515acac 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -13,23 +13,22 @@ */ #include "CClient.h" -#include "IScreenFactory.h" +#include "CScreen.h" #include "ProtocolTypes.h" #include "Version.h" #include "XScreen.h" #include "CNetworkAddress.h" +#include "CSocketMultiplexer.h" #include "CTCPSocketFactory.h" #include "XSocket.h" -#include "CCondVar.h" -#include "CLock.h" -#include "CMutex.h" #include "CThread.h" -#include "XThread.h" -#include "CFunctionJob.h" +#include "CEventQueue.h" +#include "CFunctionEventJob.h" #include "CLog.h" -#include "LogOutputters.h" #include "CString.h" +#include "LogOutputters.h" #include "CArch.h" +#include "XArch.h" #include #define DAEMON_RUNNING(running_) @@ -87,160 +86,290 @@ CArgs* CArgs::s_instance = NULL; // platform dependent factories // -//! Factory for creating screens -/*! -Objects of this type create screens appropriate for the platform. -*/ -class CScreenFactory : public IScreenFactory { -public: - CScreenFactory() { } - virtual ~CScreenFactory() { } - - // IScreenFactory overrides - virtual IPlatformScreen* - create(IScreenReceiver*, IPrimaryScreenReceiver*); -}; - -IPlatformScreen* -CScreenFactory::create(IScreenReceiver* receiver, - IPrimaryScreenReceiver* primaryReceiver) +static +CScreen* +createScreen() { #if WINDOWS_LIKE - return new CMSWindowsScreen(receiver, primaryReceiver); + return new CScreen(new CMSWindowsScreen(false)); #elif UNIX_LIKE - return new CXWindowsScreen(receiver, primaryReceiver); + return new CScreen(new CXWindowsScreen(false)); #endif } -//! CQuitJob -/*! -A job that cancels a given thread. -*/ -class CQuitJob : public IJob { -public: - CQuitJob(const CThread& thread); - ~CQuitJob(); - - // IJob overrides - virtual void run(); - -private: - CThread m_thread; -}; - -CQuitJob::CQuitJob(const CThread& thread) : - m_thread(thread) -{ - // do nothing -} - -CQuitJob::~CQuitJob() -{ - // do nothing -} - -void -CQuitJob::run() -{ - m_thread.cancel(); -} - - // // platform independent main // -static CClient* s_client = NULL; +static CClient* s_client = NULL; +static CScreen* s_clientScreen = NULL; static CClientTaskBarReceiver* s_taskBarReceiver = NULL; +static double s_retryTime = 0.0; + +static +void +updateStatus() +{ + s_taskBarReceiver->updateStatus(s_client, ""); +} + +static +void +updateStatus(const CString& msg) +{ + s_taskBarReceiver->updateStatus(s_client, msg); +} + +static +void +resetRestartTimeout() +{ + s_retryTime = 0.0; +} + +static +double +nextRestartTimeout() +{ + // choose next restart timeout. we start with rapid retries + // then slow down. + if (s_retryTime < 1.0) { + s_retryTime = 1.0; + } + else if (s_retryTime < 3.0) { + s_retryTime = 3.0; + } + else if (s_retryTime < 5.0) { + s_retryTime = 5.0; + } + else if (s_retryTime < 15.0) { + s_retryTime = 15.0; + } + else if (s_retryTime < 30.0) { + s_retryTime = 30.0; + } + else { + s_retryTime = 60.0; + } + return s_retryTime; +} + +static +void +handleScreenError(const CEvent&, void*) +{ + LOG((CLOG_CRIT "error on screen")); + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); +} + +static +CScreen* +openClientScreen() +{ + CScreen* screen = createScreen(); + EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(), + screen->getEventTarget(), + new CFunctionEventJob( + &handleScreenError)); + return screen; +} + +static +void +closeClientScreen(CScreen* screen) +{ + if (screen != NULL) { + EVENTQUEUE->removeHandler(IScreen::getErrorEvent(), + screen->getEventTarget()); + delete screen; + } +} + +static +void +handleClientRestart(const CEvent&, void* vtimer) +{ + // discard old timer + CEventQueueTimer* timer = reinterpret_cast(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(e.getData()); + + updateStatus(CString("Failed to connect to server: ") + info->m_what); + if (!ARG->m_restartable || !info->m_retry) { + LOG((CLOG_ERR "failed to connect to server: %s", info->m_what)); + } + else { + LOG((CLOG_WARN "failed to connect to server: %s", info->m_what)); + scheduleClientRestart(nextRestartTimeout()); + } +} + +static +void +handleClientDisconnected(const CEvent&, void*) +{ + LOG((CLOG_NOTE "disconnected from server")); + if (!ARG->m_restartable) { + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); + } + else { + s_client->connect(); + } + updateStatus(); +} + +static +CClient* +openClient(const CString& name, const CNetworkAddress& address, CScreen* screen) +{ + CClient* client = new CClient(name, address, + new CTCPSocketFactory, NULL, screen); + EVENTQUEUE->adoptHandler(CClient::getConnectedEvent(), + client->getEventTarget(), + new CFunctionEventJob(handleClientConnected)); + EVENTQUEUE->adoptHandler(CClient::getConnectionFailedEvent(), + client->getEventTarget(), + new CFunctionEventJob(handleClientFailed)); + EVENTQUEUE->adoptHandler(CClient::getDisconnectedEvent(), + client->getEventTarget(), + new CFunctionEventJob(handleClientDisconnected)); + return client; +} + +static +void +closeClient(CClient* client) +{ + if (client == NULL) { + return; + } + + EVENTQUEUE->removeHandler(CClient::getConnectedEvent(), client); + EVENTQUEUE->removeHandler(CClient::getConnectionFailedEvent(), client); + EVENTQUEUE->removeHandler(CClient::getDisconnectedEvent(), client); + delete client; +} + +static +bool +startClient() +{ + double retryTime; + CScreen* clientScreen = NULL; + try { + clientScreen = openClientScreen(); + s_client = openClient(ARG->m_name, + ARG->m_serverAddress, clientScreen); + s_clientScreen = clientScreen; + LOG((CLOG_NOTE "started client")); + s_client->connect(); + updateStatus(); + return true; + } + catch (XScreenUnavailable& e) { + LOG((CLOG_WARN "cannot open secondary screen: %s", e.what())); + closeClientScreen(clientScreen); + updateStatus(CString("Cannot open secondary screen: ") + e.what()); + retryTime = e.getRetryTime(); + } + catch (XScreenOpenFailure& e) { + LOG((CLOG_CRIT "cannot open secondary screen: %s", e.what())); + closeClientScreen(clientScreen); + return false; + } + catch (XBase& e) { + LOG((CLOG_CRIT "failed to start client: %s", e.what())); + closeClientScreen(clientScreen); + return false; + } + + if (ARG->m_restartable) { + scheduleClientRestart(retryTime); + return true; + } + else { + // don't try again + return false; + } +} + +static +void +stopClient() +{ + closeClient(s_client); + closeClientScreen(s_clientScreen); + s_client = NULL; + s_clientScreen = NULL; +} static int -realMain(void) +realMain() { - int result = kExitSuccess; - do { - bool opened = false; - bool locked = true; - try { - // create client - s_client = new CClient(ARG->m_name); - s_client->setAddress(ARG->m_serverAddress); - s_client->setScreenFactory(new CScreenFactory); - s_client->setSocketFactory(new CTCPSocketFactory); - s_client->setStreamFilterFactory(NULL); + // start the client. if this return false then we've failed and + // we shouldn't retry. + LOG((CLOG_DEBUG1 "starting client")); + if (!startClient()) { + return kExitFailed; + } - // open client - try { - s_taskBarReceiver->setClient(s_client); - s_client->open(); - opened = true; + // run event loop. if startClient() failed we're supposed to retry + // later. the timer installed by startClient() will take care of + // that. + DAEMON_RUNNING(true); + CEvent event; + EVENTQUEUE->getEvent(event); + while (event.getType() != CEvent::kQuit) { + EVENTQUEUE->dispatchEvent(event); + CEvent::deleteData(event); + EVENTQUEUE->getEvent(event); + } + DAEMON_RUNNING(false); - // run client - DAEMON_RUNNING(true); - locked = false; - s_client->mainLoop(); - locked = true; - DAEMON_RUNNING(false); + // close down + LOG((CLOG_DEBUG1 "stopping client")); + stopClient(); + updateStatus(); + LOG((CLOG_NOTE "stopped client")); - // get client status - if (s_client->wasRejected()) { - // try again later. we don't want to bother - // the server very often if it doesn't want us. - throw XScreenUnavailable(60.0); - } - - // clean up -#define FINALLY do { \ - if (!locked) { \ - DAEMON_RUNNING(false); \ - locked = true; \ - } \ - if (opened) { \ - s_client->close(); \ - } \ - s_taskBarReceiver->setClient(NULL); \ - delete s_client; \ - s_client = NULL; \ - } while (false) - FINALLY; - } - catch (XScreenUnavailable& e) { - // wait before retrying if we're going to retry - if (ARG->m_restartable) { - LOG((CLOG_DEBUG "waiting %.0f seconds to retry", e.getRetryTime())); - ARCH->sleep(e.getRetryTime()); - } - else { - result = kExitFailed; - } - FINALLY; - } - catch (XThread&) { - FINALLY; - throw; - } - catch (...) { - // don't try to restart and fail - ARG->m_restartable = false; - result = kExitFailed; - FINALLY; - } -#undef FINALLY - } - catch (XBase& e) { - LOG((CLOG_CRIT "failed: %s", e.what())); - } - catch (XThread&) { - // terminated - ARG->m_restartable = false; - result = kExitTerminated; - } - } while (ARG->m_restartable); - - return result; + return kExitSuccess; } +/* static void realMainEntry(void* vresult) @@ -278,6 +407,7 @@ runMainInThread(void) throw; } } +*/ // @@ -292,14 +422,12 @@ static void version() { - LOG((CLOG_PRINT -"%s %s, protocol version %d.%d\n" -"%s", - ARG->m_pname, - kVersion, - kProtocolMajorVersion, - kProtocolMinorVersion, - kCopyright)); + LOG((CLOG_PRINT "%s %s, protocol version %d.%d\n%s", + ARG->m_pname, + kVersion, + kProtocolMajorVersion, + kProtocolMinorVersion, + kCopyright)); } static @@ -697,15 +825,20 @@ main(int argc, char** argv) { CArch arch; CLOG; - CArgs args; + + // go really fast + CThread::getCurrentThread().setPriority(-14); + + CSocketMultiplexer multiplexer; + CEventQueue eventQueue; // get program name + CArgs args; ARG->m_pname = ARCH->getBasename(argv[0]); // make the task bar receiver. the user can control this app // through the task bar. s_taskBarReceiver = new CXWindowsClientTaskBarReceiver; - s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); // parse command line parse(argc, argv); diff --git a/cmd/synergys/CServerTaskBarReceiver.cpp b/cmd/synergys/CServerTaskBarReceiver.cpp index fb634d50..b3858827 100644 --- a/cmd/synergys/CServerTaskBarReceiver.cpp +++ b/cmd/synergys/CServerTaskBarReceiver.cpp @@ -14,8 +14,8 @@ #include "CServerTaskBarReceiver.h" #include "CServer.h" -#include "CEventQueue.h" #include "CLock.h" +#include "IEventQueue.h" #include "CArch.h" // diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 5956d011..98d1402d 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -171,25 +171,35 @@ closeClientListener(CClientListener* listen) } } +static +void +handleScreenError(const CEvent&, void*) +{ + LOG((CLOG_CRIT "error on screen")); + EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); +} + static CScreen* openServerScreen() { - return createScreen(); + CScreen* screen = createScreen(); + EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(), + screen->getEventTarget(), + new CFunctionEventJob( + &handleScreenError)); + return screen; } static void closeServerScreen(CScreen* screen) { - delete screen; -} - -static -void -handleScreenError(const CEvent&, void*) -{ - EVENTQUEUE->addEvent(CEvent(CEvent::kQuit)); + if (screen != NULL) { + EVENTQUEUE->removeHandler(IScreen::getErrorEvent(), + screen->getEventTarget()); + delete screen; + } } static @@ -197,23 +207,14 @@ CPrimaryClient* openPrimaryClient(const CString& name, CScreen* screen) { LOG((CLOG_DEBUG1 "creating primary screen")); - CPrimaryClient* primaryClient = new CPrimaryClient(name, screen); - EVENTQUEUE->adoptHandler(IScreen::getErrorEvent(), - primaryClient->getEventTarget(), - new CFunctionEventJob( - &handleScreenError)); - return primaryClient; + return new CPrimaryClient(name, screen); } static void closePrimaryClient(CPrimaryClient* primaryClient) { - if (primaryClient != NULL) { - EVENTQUEUE->removeHandler(IScreen::getErrorEvent(), - primaryClient->getEventTarget()); - delete primaryClient; - } + delete primaryClient; } static @@ -711,7 +712,7 @@ parse(int argc, const char* const* argv) static bool -loadConfig(const char* pathname, bool require) +loadConfig(const char* pathname) { assert(pathname != NULL); @@ -727,15 +728,8 @@ loadConfig(const char* pathname, bool require) return true; } catch (XConfigRead& e) { - if (require) { - LOG((CLOG_PRINT "%s: cannot read configuration '%s': %s", - ARG->m_pname, pathname, e.what())); - bye(kExitConfig); - } - else { - LOG((CLOG_DEBUG "cannot read configuration \"%s\": %s", + LOG((CLOG_DEBUG "cannot read configuration \"%s\": %s", pathname, e.what())); - } } return false; } @@ -744,34 +738,38 @@ static void loadConfig() { + bool loaded = false; + // load the config file, if specified if (ARG->m_configFile != NULL) { - // require the user specified file to load correctly - loadConfig(ARG->m_configFile, true); + loaded = loadConfig(ARG->m_configFile); } // load the default configuration if no explicit file given else { - // get the user's home directory. use the effective user id - // so a user can't get a setuid root program to load his file. - bool loaded = false; + // get the user's home directory CString path = ARCH->getUserDirectory(); if (!path.empty()) { // complete path path = ARCH->concatPath(path, USR_CONFIG_NAME); // now try loading the user's configuration - loaded = loadConfig(path.c_str(), false); + loaded = loadConfig(path.c_str()); } if (!loaded) { // try the system-wide config file path = ARCH->getSystemDirectory(); if (!path.empty()) { path = ARCH->concatPath(path, SYS_CONFIG_NAME); - loadConfig(path.c_str(), false); + loaded = loadConfig(path.c_str()); } } } + + if (!loaded) { + LOG((CLOG_PRINT "%s: no configuration available", ARG->m_pname)); + bye(kExitConfig); + } } diff --git a/lib/arch/CArchNetworkBSD.cpp b/lib/arch/CArchNetworkBSD.cpp index 36daf926..29f71586 100644 --- a/lib/arch/CArchNetworkBSD.cpp +++ b/lib/arch/CArchNetworkBSD.cpp @@ -620,6 +620,13 @@ CArchNetworkBSD::nameToAddr(const std::string& name) sizeof(inaddr.sin_addr)); memcpy(&addr->m_addr, &inaddr, addr->m_len); } + else { + ARCH->unlockMutex(m_mutex); + delete addr; + throw XArchNetworkNameUnsupported( + "The requested name is valid but " + "does not have a supported address family"); + } // done with static buffer ARCH->unlockMutex(m_mutex); diff --git a/lib/arch/XArch.h b/lib/arch/XArch.h index 027868ad..45b7a0e8 100644 --- a/lib/arch/XArch.h +++ b/lib/arch/XArch.h @@ -64,7 +64,8 @@ class XArch { public: XArch(XArchEval* adoptedEvaluator) : m_eval(adoptedEvaluator) { } XArch(const std::string& msg) : m_eval(NULL), m_what(msg) { } - XArch(const XArch& e) : m_eval(e.m_eval->clone()), m_what(e.m_what) { } + XArch(const XArch& e) : m_eval(e.m_eval != NULL ? e.m_eval->clone() : NULL), + m_what(e.m_what) { } ~XArch() { delete m_eval; } std::string what() const throw(); @@ -137,7 +138,7 @@ XARCH_SUBCLASS(XArchNetworkName, XArchNetwork); //! The named host is unknown XARCH_SUBCLASS(XArchNetworkNameUnknown, XArchNetworkName); -//! The named host is known but has to address +//! The named host is known but has no address XARCH_SUBCLASS(XArchNetworkNameNoAddress, XArchNetworkName); //! Non-recoverable name server error @@ -146,6 +147,9 @@ XARCH_SUBCLASS(XArchNetworkNameFailure, XArchNetworkName); //! Temporary name server error XARCH_SUBCLASS(XArchNetworkNameUnavailable, XArchNetworkName); +//! The named host is known but no supported address +XARCH_SUBCLASS(XArchNetworkNameUnsupported, XArchNetworkName); + //! Generic daemon exception /*! Exceptions derived from this class are used by the daemon diff --git a/lib/base/CEvent.cpp b/lib/base/CEvent.cpp index 67451d6a..5d828989 100644 --- a/lib/base/CEvent.cpp +++ b/lib/base/CEvent.cpp @@ -82,8 +82,7 @@ CEvent::deleteData(const CEvent& event) break; default: - // yes, really delete void* - delete event.getData(); + free(event.getData()); break; } } diff --git a/lib/base/CEvent.h b/lib/base/CEvent.h index a2c44735..272369ba 100644 --- a/lib/base/CEvent.h +++ b/lib/base/CEvent.h @@ -38,9 +38,10 @@ public: //! Create \c CEvent with data /*! The \p type must have been registered using \c registerType(). - The \p data must be POD (plain old data) which means it cannot - have a destructor or be composed of any types that do. \p target - is the intended recipient of the event. + The \p data must be POD (plain old data) allocated by malloc(), + which means it cannot have a constructor, destructor or be + composed of any types that do. \p target is the intended + recipient of the event. */ CEvent(Type type, void* target = NULL, void* data = NULL); @@ -70,7 +71,7 @@ public: //! Release event data /*! - Deletes event data for the given event. + Deletes event data for the given event (using free()). */ static void deleteData(const CEvent&); diff --git a/lib/client/CClient.cpp b/lib/client/CClient.cpp index 1cc8a688..da227d2a 100644 --- a/lib/client/CClient.cpp +++ b/lib/client/CClient.cpp @@ -15,297 +15,163 @@ #include "CClient.h" #include "CServerProxy.h" #include "CScreen.h" -#include "IScreenFactory.h" #include "CClipboard.h" -#include "CInputPacketStream.h" -#include "COutputPacketStream.h" +#include "CPacketStreamFilter.h" #include "CProtocolUtil.h" -#include "IPlatformScreen.h" -#include "IServer.h" #include "ProtocolTypes.h" -#include "XScreen.h" #include "XSynergy.h" #include "IDataSocket.h" #include "ISocketFactory.h" -#include "XSocket.h" #include "IStreamFilterFactory.h" -#include "CLock.h" -#include "CThread.h" -#include "CTimerThread.h" -#include "XMT.h" -#include "XThread.h" #include "CLog.h" -#include "CStopwatch.h" -#include "TMethodJob.h" -#include "CArch.h" +#include "IEventQueue.h" +#include "TMethodEventJob.h" // // CClient // -CClient::CClient(const CString& clientName) : - m_name(clientName), - m_screen(NULL), +CEvent::Type CClient::s_connectedEvent = CEvent::kUnknown; +CEvent::Type CClient::s_connectionFailedEvent = CEvent::kUnknown; +CEvent::Type CClient::s_disconnectedEvent = CEvent::kUnknown; + +CClient::CClient(const CString& name, const CNetworkAddress& address, + ISocketFactory* socketFactory, + IStreamFilterFactory* streamFilterFactory, + CScreen* screen) : + m_name(name), + m_serverAddress(address), + m_socketFactory(socketFactory), + m_streamFilterFactory(streamFilterFactory), + m_screen(screen), + m_stream(NULL), + m_timer(NULL), m_server(NULL), - m_screenFactory(NULL), - m_socketFactory(NULL), - m_streamFilterFactory(NULL), - m_session(NULL), - m_active(false), - m_rejected(true), - m_status(kNotRunning) + + m_active(false) { + assert(m_socketFactory != NULL); + assert(m_screen != NULL); + // do nothing } CClient::~CClient() { - delete m_screenFactory; + cleanupTimer(); + cleanupScreen(); + cleanupConnecting(); + cleanupConnection(); delete m_socketFactory; delete m_streamFilterFactory; } void -CClient::setAddress(const CNetworkAddress& serverAddress) +CClient::connect() { - CLock lock(&m_mutex); - m_serverAddress = serverAddress; -} - -void -CClient::setScreenFactory(IScreenFactory* adopted) -{ - CLock lock(&m_mutex); - delete m_screenFactory; - m_screenFactory = adopted; -} - -void -CClient::setSocketFactory(ISocketFactory* adopted) -{ - CLock lock(&m_mutex); - delete m_socketFactory; - m_socketFactory = adopted; -} - -void -CClient::setStreamFilterFactory(IStreamFilterFactory* adopted) -{ - CLock lock(&m_mutex); - delete m_streamFilterFactory; - m_streamFilterFactory = adopted; -} - -void -CClient::exitMainLoop() -{ - m_screen->exitMainLoop(); -} - -void -CClient::addStatusJob(IJob* job) -{ - m_statusJobs.addJob(job); -} - -void -CClient::removeStatusJob(IJob* job) -{ - m_statusJobs.removeJob(job); -} - -bool -CClient::wasRejected() const -{ - return m_rejected; -} - -CClient::EStatus -CClient::getStatus(CString* msg) const -{ - CLock lock(&m_mutex); - if (msg != NULL) { - *msg = m_statusMessage; - } - return m_status; -} - -void -CClient::runStatusJobs() const -{ - m_statusJobs.runJobs(); -} - -void -CClient::setStatus(EStatus status, const char* msg) -{ - { - CLock lock(&m_mutex); - m_status = status; - if (m_status == kError) { - m_statusMessage = (msg == NULL) ? "Error" : msg; - } - else { - m_statusMessage = (msg == NULL) ? "" : msg; - } - } - runStatusJobs(); -} - -void -CClient::onError() -{ - setStatus(kError); - - // close down session but don't wait too long - deleteSession(3.0); -} - -void -CClient::onInfoChanged(const CClientInfo& info) -{ - LOG((CLOG_DEBUG "resolution changed")); - - CLock lock(&m_mutex); - if (m_server != NULL) { - m_server->onInfoChanged(info); - } -} - -bool -CClient::onGrabClipboard(ClipboardID id) -{ - CLock lock(&m_mutex); - if (m_server == NULL) { - // m_server can be NULL if the screen calls this method - // before we've gotten around to connecting to the server. - // we simply ignore the clipboard change in that case. - return false; - } - - // grab ownership - m_server->onGrabClipboard(id); - - // we now own the clipboard and it has not been sent to the server - m_ownClipboard[id] = true; - m_timeClipboard[id] = 0; - - // if we're not the active screen then send the clipboard now, - // otherwise we'll wait until we leave. - if (!m_active) { - sendClipboard(id); - } - - return true; -} - -void -CClient::onClipboardChanged(ClipboardID, const CString&) -{ - // ignore -- we'll check the clipboard when we leave -} - -void -CClient::open() -{ - // open the screen - try { - LOG((CLOG_DEBUG "opening screen")); - openSecondaryScreen(); - setStatus(kNotRunning); - } - catch (XScreenOpenFailure& e) { - // can't open screen - setStatus(kError, e.what()); - LOG((CLOG_DEBUG "failed to open screen")); - throw; - } -} - -void -CClient::mainLoop() -{ - { - CLock lock(&m_mutex); - - // check preconditions - assert(m_screen != NULL); - assert(m_server == NULL); - - // connection starts as unsuccessful - m_rejected = true; + if (m_stream != NULL) { + return; } try { - setStatus(kNotRunning); - LOG((CLOG_DEBUG "starting client \"%s\"", m_name.c_str())); + IDataSocket* socket = m_socketFactory->create(); - // start server interactions - { - CLock lock(&m_mutex); - m_session = new CThread(new TMethodJob( - this, &CClient::runSession)); + // filter socket messages, including a packetizing filter + m_stream = socket; + if (m_streamFilterFactory != NULL) { + m_stream = m_streamFilterFactory->create(m_stream, true); } + m_stream = new CPacketStreamFilter(m_stream, true); - // handle events - m_screen->mainLoop(); - - // clean up - deleteSession(); - LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); - } - catch (XMT& e) { - LOG((CLOG_ERR "client error: %s", e.what())); - setStatus(kError, e.what()); - - // clean up - deleteSession(); - LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); - throw; + // connect + LOG((CLOG_DEBUG1 "connecting to server")); + setupConnecting(); + setupTimer(); + socket->connect(m_serverAddress); } catch (XBase& e) { - LOG((CLOG_ERR "client error: %s", e.what())); - setStatus(kError, e.what()); - - // clean up - deleteSession(); - LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); - CLock lock(&m_mutex); - m_rejected = false; - } - catch (XThread&) { - setStatus(kNotRunning); - - // clean up - deleteSession(); - LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); - throw; - } - catch (...) { - LOG((CLOG_ERR "client error: ")); - setStatus(kError); - - // clean up - deleteSession(); - LOG((CLOG_DEBUG "stopping client \"%s\"", m_name.c_str())); - throw; + delete m_stream; + m_stream = NULL; + sendConnectionFailedEvent(e.what()); + return; } } void -CClient::close() +CClient::disconnect(const char* msg) { - closeSecondaryScreen(); - LOG((CLOG_DEBUG "closed screen")); + cleanupTimer(); + cleanupScreen(); + cleanupConnection(); + if (msg != NULL) { + sendConnectionFailedEvent(msg); + } + else { + sendEvent(getDisconnectedEvent(), NULL); + } +} + +bool +CClient::isConnected() const +{ + return (m_server != NULL); +} + +bool +CClient::isConnecting() const +{ + return (m_timer != NULL); +} + +CEvent::Type +CClient::getConnectedEvent() +{ + return CEvent::registerTypeOnce(s_connectedEvent, + "CClient::connected"); +} + +CEvent::Type +CClient::getConnectionFailedEvent() +{ + return CEvent::registerTypeOnce(s_connectionFailedEvent, + "CClient::failed"); +} + +CEvent::Type +CClient::getDisconnectedEvent() +{ + return CEvent::registerTypeOnce(s_disconnectedEvent, + "CClient::disconnected"); +} + +void* +CClient::getEventTarget() const +{ + return m_screen->getEventTarget(); +} + +bool +CClient::getClipboard(ClipboardID id, IClipboard* clipboard) const +{ + return m_screen->getClipboard(id, clipboard); +} + +void +CClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + m_screen->getShape(x, y, w, h); +} + +void +CClient::getCursorPos(SInt32& x, SInt32& y) const +{ + m_screen->getCursorPos(x, y); } void CClient::enter(SInt32 xAbs, SInt32 yAbs, UInt32, KeyModifierMask mask, bool) { - { - CLock lock(&m_mutex); - m_active = true; - } + m_active = true; m_screen->mouseMove(xAbs, yAbs); m_screen->enter(); m_screen->setToggleState(mask); @@ -316,7 +182,6 @@ CClient::leave() { m_screen->leave(); - CLock lock(&m_mutex); m_active = false; // send clipboards that we own and that have changed @@ -330,26 +195,16 @@ CClient::leave() } void -CClient::setClipboard(ClipboardID id, const CString& data) +CClient::setClipboard(ClipboardID id, const IClipboard* clipboard) { - // unmarshall - CClipboard clipboard; - clipboard.unmarshall(data, 0); - - // set screen's clipboard - m_screen->setClipboard(id, &clipboard); + m_screen->setClipboard(id, clipboard); } void CClient::grabClipboard(ClipboardID id) { - // we no longer own the clipboard - { - CLock lock(&m_mutex); - m_ownClipboard[id] = false; - } - m_screen->grabClipboard(id); + m_ownClipboard[id] = false; } void @@ -425,89 +280,6 @@ CClient::getName() const return m_name; } -SInt32 -CClient::getJumpZoneSize() const -{ - return m_screen->getJumpZoneSize(); -} - -void -CClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const -{ - m_screen->getShape(x, y, w, h); -} - -void -CClient::getCursorPos(SInt32& x, SInt32& y) const -{ - m_screen->getCursorPos(x, y); -} - -void -CClient::getCursorCenter(SInt32&, SInt32&) const -{ - assert(0 && "shouldn't be called"); -} - -void -CClient::openSecondaryScreen() -{ - assert(m_screen == NULL); - - // not active - m_active = false; - - // reset clipboard state - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - m_ownClipboard[id] = false; - m_timeClipboard[id] = 0; - } - - // create screen - LOG((CLOG_DEBUG1 "creating secondary screen")); - if (m_screenFactory != NULL) { - IPlatformScreen* platformScreen = m_screenFactory->create(this, NULL); - if (platformScreen != NULL) { - m_screen = new CScreen(platformScreen, this); - } - } - if (m_screen == NULL) { - throw XScreenOpenFailure(); - } - - // open screen - try { - LOG((CLOG_DEBUG1 "opening secondary screen")); - m_screen->open(); - } - catch (...) { - LOG((CLOG_DEBUG1 "destroying secondary screen")); - delete m_screen; - m_screen = NULL; - throw; - } -} - -void -CClient::closeSecondaryScreen() -{ - // close the secondary screen - try { - if (m_screen != NULL) { - LOG((CLOG_DEBUG1 "closing secondary screen")); - m_screen->close(); - } - } - catch (...) { - // ignore - } - - // clean up - LOG((CLOG_DEBUG1 "destroying secondary screen")); - delete m_screen; - m_screen = NULL; -} - void CClient::sendClipboard(ClipboardID id) { @@ -537,227 +309,305 @@ CClient::sendClipboard(ClipboardID id) // save and send data if different if (data != m_dataClipboard[id]) { m_dataClipboard[id] = data; - m_server->onClipboardChanged(id, data); + m_server->onClipboardChanged(id, &clipboard); } } } void -CClient::runSession(void*) +CClient::sendEvent(CEvent::Type type, void* data) { - try { - LOG((CLOG_DEBUG "starting server proxy")); - runServer(); - m_screen->exitMainLoop(); - LOG((CLOG_DEBUG "stopping server proxy")); - } - catch (...) { - m_screen->exitMainLoop(); - LOG((CLOG_DEBUG "stopping server proxy")); - throw; + EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), data)); +} + +void +CClient::sendConnectionFailedEvent(const char* msg) +{ + CFailInfo* info = (CFailInfo*)malloc(sizeof(CFailInfo) + strlen(msg)); + info->m_retry = true; + strcpy(info->m_what, msg); + sendEvent(getConnectionFailedEvent(), info); +} + +void +CClient::setupConnecting() +{ + assert(m_stream != NULL); + + EVENTQUEUE->adoptHandler(IDataSocket::getConnectedEvent(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &CClient::handleConnected)); + EVENTQUEUE->adoptHandler(IDataSocket::getConnectionFailedEvent(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &CClient::handleConnectionFailed)); +} + +void +CClient::setupConnection() +{ + assert(m_stream != NULL); + + EVENTQUEUE->adoptHandler(ISocket::getDisconnectedEvent(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &CClient::handleDisconnected)); + EVENTQUEUE->adoptHandler(IStream::getInputReadyEvent(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &CClient::handleHello)); + EVENTQUEUE->adoptHandler(IStream::getOutputErrorEvent(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &CClient::handleOutputError)); + EVENTQUEUE->adoptHandler(IStream::getInputShutdownEvent(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &CClient::handleDisconnected)); + EVENTQUEUE->adoptHandler(IStream::getOutputShutdownEvent(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &CClient::handleDisconnected)); +} + +void +CClient::setupScreen() +{ + assert(m_server == NULL); + + m_ready = false; + m_server = new CServerProxy(this, m_stream); + EVENTQUEUE->adoptHandler(IScreen::getShapeChangedEvent(), + getEventTarget(), + new TMethodEventJob(this, + &CClient::handleShapeChanged)); + EVENTQUEUE->adoptHandler(IScreen::getClipboardGrabbedEvent(), + getEventTarget(), + new TMethodEventJob(this, + &CClient::handleClipboardGrabbed)); + EVENTQUEUE->adoptHandler(IScreen::getClipboardChangedEvent(), + getEventTarget(), + new TMethodEventJob(this, + &CClient::handleClipboardChanged)); + EVENTQUEUE->adoptHandler(CServerProxy::getHandshakeCompleteEvent(), + m_server, + new TMethodEventJob(this, + &CClient::handleHandshakeComplete)); +} + +void +CClient::setupTimer() +{ + assert(m_timer == NULL); + + m_timer = EVENTQUEUE->newOneShotTimer(15.0, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, m_timer, + new TMethodEventJob(this, + &CClient::handleConnectTimeout)); +} + +void +CClient::cleanupConnecting() +{ + if (m_stream != NULL) { + EVENTQUEUE->removeHandler(IDataSocket::getConnectedEvent(), + m_stream->getEventTarget()); + EVENTQUEUE->removeHandler(IDataSocket::getConnectionFailedEvent(), + m_stream->getEventTarget()); } } void -CClient::deleteSession(double timeout) +CClient::cleanupConnection() { - // get session thread object - CThread* thread; - { - CLock lock(&m_mutex); - thread = m_session; - m_session = NULL; - } - - // shut it down - if (thread != NULL) { - thread->cancel(); - thread->wait(timeout); - delete thread; + if (m_stream != NULL) { + EVENTQUEUE->removeHandler(IStream::getInputReadyEvent(), + m_stream->getEventTarget()); + EVENTQUEUE->removeHandler(IStream::getOutputErrorEvent(), + m_stream->getEventTarget()); + EVENTQUEUE->removeHandler(IStream::getInputShutdownEvent(), + m_stream->getEventTarget()); + EVENTQUEUE->removeHandler(IStream::getOutputShutdownEvent(), + m_stream->getEventTarget()); + EVENTQUEUE->removeHandler(ISocket::getDisconnectedEvent(), + m_stream->getEventTarget()); + delete m_stream; + m_stream = NULL; } } void -CClient::runServer() +CClient::cleanupScreen() { - IDataSocket* socket = NULL; - CServerProxy* proxy = NULL; - bool timedOut; - try { - // allow connect and handshake this much time to succeed - CTimerThread timer(15.0, &timedOut); - - // create socket and attempt to connect to server - LOG((CLOG_DEBUG1 "connecting to server")); - if (m_socketFactory != NULL) { - socket = m_socketFactory->create(); - } - assert(socket != NULL); - socket->connect(m_serverAddress); - - // create proxy - LOG((CLOG_INFO "connected to server")); - LOG((CLOG_DEBUG1 "negotiating with server")); - proxy = handshakeServer(socket); - } - catch (XThread&) { - if (timedOut) { - LOG((CLOG_ERR "connection timed out")); - setStatus(kError, "connection timed out"); + if (m_server != NULL) { + if (!m_ready) { + EVENTQUEUE->removeHandler(CServerProxy::getHandshakeCompleteEvent(), + m_server); } else { - // cancelled by some thread other than the timer - } - delete proxy; - delete socket; - throw; - } - catch (XSocketConnect& e) { - LOG((CLOG_ERR "connection failed: %s", e.what())); - setStatus(kError, e.what()); - delete socket; - return; - } - catch (XBase& e) { - LOG((CLOG_ERR "connection failed: %s", e.what())); - setStatus(kError, e.what()); - LOG((CLOG_INFO "disconnecting from server")); - delete socket; - return; - } - catch (...) { - LOG((CLOG_ERR "connection failed: ")); - setStatus(kError); - LOG((CLOG_INFO "disconnecting from server")); - delete socket; - return; - } - - // saver server proxy object - { - CLock lock(&m_mutex); - m_server = proxy; - } - - bool enabled = false; - try { - // enable the screen - m_screen->enable(); - enabled = true; - - // process messages - bool rejected = true; - if (proxy != NULL) { - LOG((CLOG_DEBUG1 "communicating with server")); - setStatus(kRunning); - rejected = !proxy->mainLoop(); - setStatus(kNotRunning); - } - - // disable the screen - m_screen->disable(); - - // clean up - CLock lock(&m_mutex); - m_rejected = rejected; - m_server = NULL; - delete proxy; - LOG((CLOG_DEBUG "disconnecting from server")); - socket->close(); - delete socket; - } - catch (...) { - setStatus(kNotRunning); - if (enabled) { m_screen->disable(); + m_ready = false; } - CLock lock(&m_mutex); - m_rejected = false; - m_server = NULL; - delete proxy; - LOG((CLOG_DEBUG "disconnecting from server")); - socket->close(); - delete socket; - throw; + EVENTQUEUE->removeHandler(IScreen::getShapeChangedEvent(), + getEventTarget()); + EVENTQUEUE->removeHandler(IScreen::getClipboardGrabbedEvent(), + getEventTarget()); + EVENTQUEUE->removeHandler(IScreen::getClipboardChangedEvent(), + getEventTarget()); + delete m_server; + m_server = NULL; } } -CServerProxy* -CClient::handshakeServer(IDataSocket* socket) +void +CClient::cleanupTimer() { - // get the input and output streams - IInputStream* input = socket->getInputStream(); - IOutputStream* output = socket->getOutputStream(); - bool own = false; - - // attach filters - if (m_streamFilterFactory != NULL) { - input = m_streamFilterFactory->createInput(input, own); - output = m_streamFilterFactory->createOutput(output, own); - own = true; + if (m_timer != NULL) { + EVENTQUEUE->removeHandler(CEvent::kTimer, m_timer); + EVENTQUEUE->deleteTimer(m_timer); + m_timer = NULL; + } +} + +void +CClient::handleConnected(const CEvent&, void*) +{ + LOG((CLOG_DEBUG1 "connected; wait for hello")); + cleanupConnecting(); + setupConnection(); + + // reset clipboard state + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_ownClipboard[id] = false; + m_timeClipboard[id] = 0; + } +} + +void +CClient::handleConnectionFailed(const CEvent& event, void*) +{ + IDataSocket::CConnectionFailedInfo* info = + reinterpret_cast(event.getData()); + + cleanupTimer(); + cleanupConnecting(); + delete m_stream; + m_stream = NULL; + LOG((CLOG_DEBUG1 "connection failed")); + sendConnectionFailedEvent(info->m_what); +} + +void +CClient::handleConnectTimeout(const CEvent&, void*) +{ + cleanupTimer(); + cleanupConnecting(); + delete m_stream; + m_stream = NULL; + LOG((CLOG_DEBUG1 "connection timed out")); + sendConnectionFailedEvent("Timed out"); +} + +void +CClient::handleOutputError(const CEvent&, void*) +{ + cleanupTimer(); + cleanupScreen(); + cleanupConnection(); + LOG((CLOG_WARN "error sending to server")); + sendEvent(getDisconnectedEvent(), NULL); +} + +void +CClient::handleDisconnected(const CEvent&, void*) +{ + cleanupTimer(); + cleanupScreen(); + cleanupConnection(); + LOG((CLOG_DEBUG1 "disconnected")); + sendEvent(getDisconnectedEvent(), NULL); +} + +void +CClient::handleHandshakeComplete(const CEvent&, void*) +{ + m_ready = true; + EVENTQUEUE->removeHandler(CServerProxy::getHandshakeCompleteEvent(), + m_server); + sendEvent(getConnectedEvent(), NULL); + m_screen->enable(); +} + +void +CClient::handleShapeChanged(const CEvent&, void*) +{ + LOG((CLOG_DEBUG "resolution changed")); + m_server->onInfoChanged(); +} + +void +CClient::handleClipboardGrabbed(const CEvent& event, void*) +{ + const IScreen::CClipboardInfo* info = + reinterpret_cast(event.getData()); + + // grab ownership + m_server->onGrabClipboard(info->m_id); + + // we now own the clipboard and it has not been sent to the server + m_ownClipboard[info->m_id] = true; + m_timeClipboard[info->m_id] = 0; + + // if we're not the active screen then send the clipboard now, + // otherwise we'll wait until we leave. + if (!m_active) { + sendClipboard(info->m_id); + } +} + +void +CClient::handleClipboardChanged(const CEvent&, void*) +{ + // ignore -- we'll check the clipboard when we leave +} + +void +CClient::handleHello(const CEvent&, void*) +{ + SInt16 major, minor; + if (!CProtocolUtil::readf(m_stream, kMsgHello, &major, &minor)) { + sendConnectionFailedEvent("Protocol error from server"); + cleanupTimer(); + cleanupConnection(); + return; + } + + // check versions + LOG((CLOG_DEBUG1 "got hello version %d.%d", major, minor)); + if (major < kProtocolMajorVersion || + (major == kProtocolMajorVersion && minor < kProtocolMinorVersion)) { + sendConnectionFailedEvent(XIncompatibleClient(major, minor).what()); + cleanupTimer(); + cleanupConnection(); + return; + } + + // say hello back + LOG((CLOG_DEBUG1 "say hello version %d.%d", kProtocolMajorVersion, kProtocolMinorVersion)); + CProtocolUtil::writef(m_stream, kMsgHelloBack, + kProtocolMajorVersion, + kProtocolMinorVersion, &m_name); + + // now connected but waiting to complete handshake + setupScreen(); + cleanupTimer(); + + // make sure we process any remaining messages later. we won't + // receive another event for already pending messages so we fake + // one. + if (m_stream->isReady()) { + EVENTQUEUE->addEvent(CEvent(IStream::getInputReadyEvent(), + m_stream->getEventTarget())); } - - // attach the packetizing filters - input = new CInputPacketStream(input, own); - output = new COutputPacketStream(output, own); - own = true; - - CServerProxy* proxy = NULL; - try { - // wait for hello from server - LOG((CLOG_DEBUG1 "wait for hello")); - SInt16 major, minor; - CProtocolUtil::readf(input, kMsgHello, &major, &minor); - - // check versions - LOG((CLOG_DEBUG1 "got hello version %d.%d", major, minor)); - if (major < kProtocolMajorVersion || - (major == kProtocolMajorVersion && minor < kProtocolMinorVersion)) { - throw XIncompatibleClient(major, minor); - } - - // say hello back - LOG((CLOG_DEBUG1 "say hello version %d.%d", kProtocolMajorVersion, kProtocolMinorVersion)); - CProtocolUtil::writef(output, kMsgHelloBack, - kProtocolMajorVersion, - kProtocolMinorVersion, &m_name); - - // create server proxy - proxy = new CServerProxy(this, input, output); - - // negotiate - // FIXME - - return proxy; - } - catch (XIncompatibleClient& e) { - LOG((CLOG_ERR "server has incompatible version %d.%d", e.getMajor(), e.getMinor())); - setStatus(kError, e.what()); - } - catch (XBase& e) { - LOG((CLOG_WARN "error communicating with server: %s", e.what())); - setStatus(kError, e.what()); - } - catch (...) { - // probably timed out - if (proxy != NULL) { - delete proxy; - } - else if (own) { - delete input; - delete output; - } - throw; - } - - // failed - if (proxy != NULL) { - delete proxy; - } - else if (own) { - delete input; - delete output; - } - - return NULL; } diff --git a/lib/client/CClient.h b/lib/client/CClient.h index 4a802a5a..3595a719 100644 --- a/lib/client/CClient.h +++ b/lib/client/CClient.h @@ -15,133 +15,113 @@ #ifndef CCLIENT_H #define CCLIENT_H -#include "IScreenReceiver.h" #include "IClient.h" #include "IClipboard.h" #include "CNetworkAddress.h" -#include "CMutex.h" -#include "CJobList.h" +class CEventQueueTimer; class CScreen; class CServerProxy; -class CThread; class IDataSocket; -class IScreenReceiver; -class IScreenFactory; class ISocketFactory; +class IStream; class IStreamFilterFactory; //! Synergy client /*! This class implements the top-level client algorithms for synergy. */ -class CClient : public IScreenReceiver, public IClient { +class CClient : public IClient { public: - enum EStatus { - kNotRunning, - kRunning, - kError, - kMaxStatus + class CFailInfo { + public: + bool m_retry; + char m_what[1]; }; /*! - This client will attempt to connect the server using \c clientName - as its name. + This client will attempt to connect to the server using \p name + as its name and \p address as the server's address and \p factory + to create the socket. \p screen is the local screen. */ - CClient(const CString& clientName); + CClient(const CString& name, const CNetworkAddress& address, + ISocketFactory* socketFactory, + IStreamFilterFactory* streamFilterFactory, + CScreen* screen); ~CClient(); //! @name manipulators //@{ - //! Set server address + //! Connect to server /*! - Sets the server's address that the client should connect to. + Starts an attempt to connect to the server. This is ignored if + the client is trying to connect or is already connected. */ - void setAddress(const CNetworkAddress& serverAddress); + void connect(); - //! Set screen factory + //! Disconnect /*! - Sets the factory for creating screens. This must be set before - calling open(). This object takes ownership of the factory. + Disconnects from the server with an optional error message. */ - void setScreenFactory(IScreenFactory*); - - //! Set socket factory - /*! - Sets the factory used to create a socket to connect to the server. - This must be set before calling mainLoop(). This object takes - ownership of the factory. - */ - void setSocketFactory(ISocketFactory*); - - //! Set stream filter factory - /*! - Sets the factory used to filter the socket streams used to - communicate with the server. This object takes ownership - of the factory. - */ - void setStreamFilterFactory(IStreamFilterFactory*); - - //! Exit event loop - /*! - Force mainLoop() to return. This call can return before - mainLoop() does (i.e. asynchronously). This may only be - called between a successful open() and close(). - */ - void exitMainLoop(); - - //! Add a job to notify of status changes - /*! - The added job is run whenever the server's status changes in - certain externally visible ways. The client keeps ownership - of the job. - */ - void addStatusJob(IJob*); - - //! Remove a job to notify of status changes - /*! - Removes a previously added status notification job. A job can - remove itself when called but must not remove any other jobs. - The client keeps ownership of the job. - */ - void removeStatusJob(IJob*); + void disconnect(const char* msg); //@} //! @name accessors //@{ - //! + //! Test if connected /*! - Returns true if the server rejected our connection. + Returns true iff the client is successfully connected to the server. */ - bool wasRejected() const; + bool isConnected() const; - //! Get the status + //! Test if connecting /*! - Returns the current status and status message. + Returns true iff the client is currently attempting to connect to + the server. */ - EStatus getStatus(CString* = NULL) const; + bool isConnecting() const; + + //! Get connected event type + /*! + Returns the connected event type. This is sent when the client has + successfully connected to the server. + */ + static CEvent::Type getConnectedEvent(); + + //! Get connection failed event type + /*! + Returns the connection failed event type. This is sent when the + server fails for some reason. The event data is a CFailInfo*. + */ + static CEvent::Type getConnectionFailedEvent(); + + //! Get disconnected event type + /*! + Returns the disconnected event type. This is sent when the client + has disconnected from the server (and only after having successfully + connected). + */ + static CEvent::Type getDisconnectedEvent(); //@} - // IScreenReceiver overrides - virtual void onError(); - virtual void onInfoChanged(const CClientInfo&); - virtual bool onGrabClipboard(ClipboardID); - virtual void onClipboardChanged(ClipboardID, const CString&); + // IScreen overrides + virtual void* getEventTarget() const; + virtual bool getClipboard(ClipboardID id, IClipboard*) const; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + virtual void getCursorPos(SInt32& x, SInt32& y) const; // IClient overrides - virtual void open(); - virtual void mainLoop(); - virtual void close(); virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, bool forScreensaver); virtual bool leave(); - virtual void setClipboard(ClipboardID, const CString&); + virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); - virtual void setClipboardDirty(ClipboardID, bool dirty); + virtual void setClipboardDirty(ClipboardID, bool); virtual void keyDown(KeyID, KeyModifierMask, KeyButton); virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count, KeyButton); @@ -154,52 +134,48 @@ public: virtual void resetOptions(); virtual void setOptions(const COptionsList& options); virtual CString getName() const; - virtual SInt32 getJumpZoneSize() const; - virtual void getShape(SInt32& x, SInt32& y, - SInt32& width, SInt32& height) const; - virtual void getCursorPos(SInt32& x, SInt32& y) const; - virtual void getCursorCenter(SInt32& x, SInt32& y) const; private: - // notify status jobs of a change - void runStatusJobs() const; - - // set new status - void setStatus(EStatus, const char* msg = NULL); - - // open/close the secondary screen - void openSecondaryScreen(); - void closeSecondaryScreen(); - - // send the clipboard to the server void sendClipboard(ClipboardID); - - // handle server messaging - void runSession(void*); - void deleteSession(double timeout = -1.0); - void runServer(); - CServerProxy* handshakeServer(IDataSocket*); + void sendEvent(CEvent::Type, void*); + void sendConnectionFailedEvent(const char* msg); + void setupConnecting(); + void setupConnection(); + void setupScreen(); + void setupTimer(); + void cleanupConnecting(); + void cleanupConnection(); + void cleanupScreen(); + void cleanupTimer(); + void handleConnected(const CEvent&, void*); + void handleConnectionFailed(const CEvent&, void*); + void handleConnectTimeout(const CEvent&, void*); + void handleOutputError(const CEvent&, void*); + void handleDisconnected(const CEvent&, void*); + void handleHandshakeComplete(const CEvent&, void*); + void handleShapeChanged(const CEvent&, void*); + void handleClipboardGrabbed(const CEvent&, void*); + void handleClipboardChanged(const CEvent&, void*); + void handleHello(const CEvent&, void*); private: - CMutex m_mutex; - CString m_name; - CScreen* m_screen; - IScreenReceiver* m_server; - CNetworkAddress m_serverAddress; - IScreenFactory* m_screenFactory; - ISocketFactory* m_socketFactory; + CString m_name; + CNetworkAddress m_serverAddress; + ISocketFactory* m_socketFactory; IStreamFilterFactory* m_streamFilterFactory; - CThread* m_session; - bool m_active; - bool m_rejected; + CScreen* m_screen; + IStream* m_stream; + CEventQueueTimer* m_timer; + CServerProxy* m_server; + bool m_ready; + bool m_active; bool m_ownClipboard[kClipboardEnd]; IClipboard::Time m_timeClipboard[kClipboardEnd]; CString m_dataClipboard[kClipboardEnd]; - // the status change jobs and status - CJobList m_statusJobs; - EStatus m_status; - CString m_statusMessage; + static CEvent::Type s_connectedEvent; + static CEvent::Type s_connectionFailedEvent; + static CEvent::Type s_disconnectedEvent; }; #endif diff --git a/lib/client/CServerProxy.cpp b/lib/client/CServerProxy.cpp index 435cdc55..2935e195 100644 --- a/lib/client/CServerProxy.cpp +++ b/lib/client/CServerProxy.cpp @@ -13,15 +13,15 @@ */ #include "CServerProxy.h" +#include "CClient.h" +#include "CClipboard.h" #include "CProtocolUtil.h" -#include "IClient.h" #include "OptionTypes.h" #include "ProtocolTypes.h" -#include "IInputStream.h" -#include "IOutputStream.h" -#include "CLock.h" +#include "IStream.h" #include "CLog.h" -#include "CStopwatch.h" +#include "IEventQueue.h" +#include "TMethodEventJob.h" #include "XBase.h" #include @@ -29,293 +29,243 @@ // CServerProxy // -CServerProxy::CServerProxy(IClient* client, - IInputStream* adoptedInput, IOutputStream* adoptedOutput) : +CEvent::Type CServerProxy::s_handshakeCompleteEvent = + CEvent::kUnknown; + +CServerProxy::CServerProxy(CClient* client, IStream* stream) : m_client(client), - m_input(adoptedInput), - m_output(adoptedOutput), + m_stream(stream), + m_timer(NULL), m_seqNum(0), - m_heartRate(kHeartRate) + m_compressMouse(false), + m_ignoreMouse(false), + m_heartRate(0.0) { assert(m_client != NULL); - assert(m_input != NULL); - assert(m_output != NULL); + assert(m_stream != NULL); // initialize modifier translation table for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) m_modifierTranslationTable[id] = id; + + // handle data on stream + EVENTQUEUE->adoptHandler(IStream::getInputReadyEvent(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &CServerProxy::handleMessage)); + + // send heartbeat + installHeartBeat(kHeartRate); } CServerProxy::~CServerProxy() { - delete m_input; - delete m_output; + installHeartBeat(-1.0); } -bool -CServerProxy::mainLoop() +CEvent::Type +CServerProxy::getHandshakeCompleteEvent() { - bool failedToConnect = false; - try { - // no compressed mouse motion yet - m_compressMouse = false; + return CEvent::registerTypeOnce(s_handshakeCompleteEvent, + "CServerProxy::handshakeComplete"); +} - // not ignoring mouse motions - m_ignoreMouse = false; +void +CServerProxy::installHeartBeat(double heartRate) +{ + if (m_timer != NULL) { + EVENTQUEUE->removeHandler(CEvent::kTimer, m_timer); + EVENTQUEUE->deleteTimer(m_timer); + } + m_heartRate = heartRate; + if (m_heartRate > 0.0) { + m_timer = EVENTQUEUE->newTimer(m_heartRate, NULL); + EVENTQUEUE->adoptHandler(CEvent::kTimer, m_timer, + new TMethodEventJob(this, + &CServerProxy::handleHeartBeat)); + } +} - // reset sequence number - m_seqNum = 0; +void +CServerProxy::handleMessage(const CEvent&, void*) +{ + while (m_stream->isReady()) { + // read next code + UInt8 code[4]; + UInt32 n = m_stream->read(code, sizeof(code)); + if (n == 0) { + break; + } + if (n != 4) { + // client sent an incomplete message + LOG((CLOG_ERR "incomplete message from server")); + m_client->disconnect("incomplete message from server"); + return; + } - // handle messages from server - CStopwatch heartbeat; - for (;;) { - // if no input is pending then flush compressed mouse motion - if (getInputStream()->getSize() == 0) { - flushCompressedMouse(); - } + // parse message + LOG((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3])); + if (memcmp(code, kMsgDMouseMove, 4) == 0) { + mouseMove(); + } - // wait for a message - LOG((CLOG_DEBUG2 "waiting for message")); - UInt8 code[4]; - UInt32 n = getInputStream()->read(code, 4, m_heartRate); + else if (memcmp(code, kMsgDMouseWheel, 4) == 0) { + mouseWheel(); + } - // check if server hungup - if (n == 0) { - LOG((CLOG_NOTE "server disconnected")); - break; - } + else if (memcmp(code, kMsgDKeyDown, 4) == 0) { + keyDown(); + } - // check for time out - if (n == (UInt32)-1 || - (m_heartRate >= 0.0 && heartbeat.getTime() > m_heartRate)) { - // send heartbeat - CLock lock(&m_mutex); - CProtocolUtil::writef(getOutputStream(), kMsgCNoop); - heartbeat.reset(); - if (n == (UInt32)-1) { - // no message to process - continue; - } - } + else if (memcmp(code, kMsgDKeyUp, 4) == 0) { + keyUp(); + } - // verify we got an entire code - if (n != 4) { - // client sent an incomplete message - LOG((CLOG_ERR "incomplete message from server")); - break; - } + else if (memcmp(code, kMsgDMouseDown, 4) == 0) { + mouseDown(); + } - // parse message - LOG((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3])); - if (memcmp(code, kMsgDMouseMove, 4) == 0) { - mouseMove(); - } + else if (memcmp(code, kMsgDMouseUp, 4) == 0) { + mouseUp(); + } - else if (memcmp(code, kMsgDMouseWheel, 4) == 0) { - mouseWheel(); - } + else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) { + keyRepeat(); + } - else if (memcmp(code, kMsgDKeyDown, 4) == 0) { - keyDown(); - } + else if (memcmp(code, kMsgCNoop, 4) == 0) { + // accept and discard no-op + } - else if (memcmp(code, kMsgDKeyUp, 4) == 0) { - keyUp(); - } + else if (memcmp(code, kMsgCEnter, 4) == 0) { + enter(); + } - else if (memcmp(code, kMsgDMouseDown, 4) == 0) { - mouseDown(); - } + else if (memcmp(code, kMsgCLeave, 4) == 0) { + leave(); + } - else if (memcmp(code, kMsgDMouseUp, 4) == 0) { - mouseUp(); - } + else if (memcmp(code, kMsgCClipboard, 4) == 0) { + grabClipboard(); + } - else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) { - keyRepeat(); - } + else if (memcmp(code, kMsgCScreenSaver, 4) == 0) { + screensaver(); + } - else if (memcmp(code, kMsgCNoop, 4) == 0) { - // accept and discard no-op - } + else if (memcmp(code, kMsgQInfo, 4) == 0) { + queryInfo(); + } - else if (memcmp(code, kMsgCEnter, 4) == 0) { - enter(); - } + else if (memcmp(code, kMsgCInfoAck, 4) == 0) { + infoAcknowledgment(); + } - else if (memcmp(code, kMsgCLeave, 4) == 0) { - leave(); - } + else if (memcmp(code, kMsgDClipboard, 4) == 0) { + setClipboard(); + } - else if (memcmp(code, kMsgCClipboard, 4) == 0) { - grabClipboard(); - } + else if (memcmp(code, kMsgCResetOptions, 4) == 0) { + resetOptions(); + } - else if (memcmp(code, kMsgCScreenSaver, 4) == 0) { - screensaver(); - } + else if (memcmp(code, kMsgDSetOptions, 4) == 0) { + setOptions(); + } - else if (memcmp(code, kMsgQInfo, 4) == 0) { - queryInfo(); - } + else if (memcmp(code, kMsgCClose, 4) == 0) { + // server wants us to hangup + LOG((CLOG_DEBUG1 "recv close")); + m_client->disconnect(NULL); + break; + } - else if (memcmp(code, kMsgCInfoAck, 4) == 0) { - infoAcknowledgment(); - } + else if (memcmp(code, kMsgEIncompatible, 4) == 0) { + SInt32 major, minor; + CProtocolUtil::readf(m_stream, + kMsgEIncompatible + 4, &major, &minor); + LOG((CLOG_ERR "server has incompatible version %d.%d", major, minor)); + m_client->disconnect("server has incompatible version"); + return; + } - else if (memcmp(code, kMsgDClipboard, 4) == 0) { - setClipboard(); - } + else if (memcmp(code, kMsgEBusy, 4) == 0) { + LOG((CLOG_ERR "server already has a connected client with name \"%s\"", m_client->getName().c_str())); + m_client->disconnect("server already has a connected client with our name"); + return; + } - else if (memcmp(code, kMsgCResetOptions, 4) == 0) { - resetOptions(); - } + else if (memcmp(code, kMsgEUnknown, 4) == 0) { + LOG((CLOG_ERR "server refused client with name \"%s\"", m_client->getName().c_str())); + m_client->disconnect("server refused client with our name"); + return; + } - else if (memcmp(code, kMsgDSetOptions, 4) == 0) { - setOptions(); - } + else if (memcmp(code, kMsgEBad, 4) == 0) { + LOG((CLOG_ERR "server disconnected due to a protocol error")); + m_client->disconnect("server reported a protocol error"); + return; + } - else if (memcmp(code, kMsgCClose, 4) == 0) { - // server wants us to hangup - LOG((CLOG_DEBUG1 "recv close")); - break; - } - - else if (memcmp(code, kMsgEIncompatible, 4) == 0) { - SInt32 major, minor; - CProtocolUtil::readf(getInputStream(), - kMsgEIncompatible + 4, &major, &minor); - LOG((CLOG_ERR "server has incompatible version %d.%d", major, minor)); - failedToConnect = true; - break; - } - - else if (memcmp(code, kMsgEBusy, 4) == 0) { - LOG((CLOG_ERR "server already has a connected client with name \"%s\"", getName().c_str())); - failedToConnect = true; - break; - } - - else if (memcmp(code, kMsgEUnknown, 4) == 0) { - LOG((CLOG_ERR "server refused client with name \"%s\"", getName().c_str())); - failedToConnect = true; - break; - } - - else if (memcmp(code, kMsgEBad, 4) == 0) { - LOG((CLOG_ERR "server disconnected due to a protocol error")); - failedToConnect = true; - break; - } - - else { - // unknown message - LOG((CLOG_ERR "unknown message from server")); - LOG((CLOG_ERR "unknown message: %d %d %d %d [%c%c%c%c]", code[0], code[1], code[2], code[3], code[0], code[1], code[2], code[3])); - failedToConnect = true; - break; - } + else { + // unknown message + LOG((CLOG_ERR "unknown message: %d %d %d %d [%c%c%c%c]", code[0], code[1], code[2], code[3], code[0], code[1], code[2], code[3])); + m_client->disconnect("unknown message from server"); + return; } } - catch (XBase& e) { - LOG((CLOG_ERR "error: %s", e.what())); - } - catch (...) { - throw; - } - - return !failedToConnect; -} - -IClient* -CServerProxy::getClient() const -{ - return m_client; -} - -CString -CServerProxy::getName() const -{ - return m_client->getName(); -} - -IInputStream* -CServerProxy::getInputStream() const -{ - return m_input; -} - -IOutputStream* -CServerProxy::getOutputStream() const -{ - return m_output; + flushCompressedMouse(); } void -CServerProxy::onError() +CServerProxy::handleHeartBeat(const CEvent&, void*) { - // ignore + CProtocolUtil::writef(m_stream, kMsgCNoop); } void -CServerProxy::onInfoChanged(const CClientInfo& info) +CServerProxy::onInfoChanged() { // ignore mouse motion until we receive acknowledgment of our info // change message. - CLock lock(&m_mutex); m_ignoreMouse = true; // send info update - sendInfo(info); + queryInfo(); } bool CServerProxy::onGrabClipboard(ClipboardID id) { LOG((CLOG_DEBUG1 "sending clipboard %d changed", id)); - CLock lock(&m_mutex); - CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, m_seqNum); + CProtocolUtil::writef(m_stream, kMsgCClipboard, id, m_seqNum); return true; } void -CServerProxy::onClipboardChanged(ClipboardID id, const CString& data) +CServerProxy::onClipboardChanged(ClipboardID id, const IClipboard* clipboard) { - CLock lock(&m_mutex); + CString data = IClipboard::marshall(clipboard); LOG((CLOG_DEBUG1 "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size())); - CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, m_seqNum, &data); + CProtocolUtil::writef(m_stream, kMsgDClipboard, id, m_seqNum, &data); } void CServerProxy::flushCompressedMouse() { - bool send = false; - SInt32 x = 0, y = 0; - { - CLock lock(&m_mutex); - if (m_compressMouse) { - m_compressMouse = false; - x = m_xMouse; - y = m_yMouse; - send = true; - } - } - - if (send) { - getClient()->mouseMove(x, y); + if (m_compressMouse) { + m_compressMouse = false; + m_client->mouseMove(m_xMouse, m_yMouse); } } void CServerProxy::sendInfo(const CClientInfo& info) { - // note -- m_mutex should be locked on entry - LOG((CLOG_DEBUG1 "sending info shape=%d,%d %dx%d zone=%d pos=%d,%d", info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize, info.m_mx, info.m_my)); - CProtocolUtil::writef(getOutputStream(), kMsgDInfo, + LOG((CLOG_DEBUG1 "sending info shape=%d,%d %dx%d", info.m_x, info.m_y, info.m_w, info.m_h)); + CProtocolUtil::writef(m_stream, kMsgDInfo, info.m_x, info.m_y, - info.m_w, info.m_h, - info.m_zoneSize, - info.m_mx, info.m_my); + info.m_w, info.m_h, 0, 0, 0); } KeyID @@ -434,19 +384,15 @@ CServerProxy::enter() SInt16 x, y; UInt16 mask; UInt32 seqNum; - CProtocolUtil::readf(getInputStream(), - kMsgCEnter + 4, &x, &y, &seqNum, &mask); + CProtocolUtil::readf(m_stream, kMsgCEnter + 4, &x, &y, &seqNum, &mask); LOG((CLOG_DEBUG1 "recv enter, %d,%d %d %04x", x, y, seqNum, mask)); // discard old compressed mouse motion, if any - { - CLock lock(&m_mutex); - m_compressMouse = false; - m_seqNum = seqNum; - } + m_compressMouse = false; + m_seqNum = seqNum; // forward - getClient()->enter(x, y, seqNum, static_cast(mask), false); + m_client->enter(x, y, seqNum, static_cast(mask), false); } void @@ -459,7 +405,7 @@ CServerProxy::leave() flushCompressedMouse(); // forward - getClient()->leave(); + m_client->leave(); } void @@ -469,8 +415,7 @@ CServerProxy::setClipboard() ClipboardID id; UInt32 seqNum; CString data; - CProtocolUtil::readf(getInputStream(), - kMsgDClipboard + 4, &id, &seqNum, &data); + CProtocolUtil::readf(m_stream, kMsgDClipboard + 4, &id, &seqNum, &data); LOG((CLOG_DEBUG "recv clipboard %d size=%d", id, data.size())); // validate @@ -479,7 +424,9 @@ CServerProxy::setClipboard() } // forward - getClient()->setClipboard(id, data); + CClipboard clipboard; + clipboard.unmarshall(data, 0); + m_client->setClipboard(id, &clipboard); } void @@ -488,7 +435,7 @@ CServerProxy::grabClipboard() // parse ClipboardID id; UInt32 seqNum; - CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id, &seqNum); + CProtocolUtil::readf(m_stream, kMsgCClipboard + 4, &id, &seqNum); LOG((CLOG_DEBUG "recv grab clipboard %d", id)); // validate @@ -497,7 +444,7 @@ CServerProxy::grabClipboard() } // forward - getClient()->grabClipboard(id); + m_client->grabClipboard(id); } void @@ -508,8 +455,7 @@ CServerProxy::keyDown() // parse UInt16 id, mask, button; - CProtocolUtil::readf(getInputStream(), kMsgDKeyDown + 4, - &id, &mask, &button); + CProtocolUtil::readf(m_stream, kMsgDKeyDown + 4, &id, &mask, &button); LOG((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x, button=0x%04x", id, mask, button)); // translate @@ -521,7 +467,7 @@ CServerProxy::keyDown() LOG((CLOG_DEBUG1 "key down translated to id=%d, mask=0x%04x", id2, mask2)); // forward - getClient()->keyDown(id2, mask2, button); + m_client->keyDown(id2, mask2, button); } void @@ -532,7 +478,7 @@ CServerProxy::keyRepeat() // parse UInt16 id, mask, count, button; - CProtocolUtil::readf(getInputStream(), kMsgDKeyRepeat + 4, + CProtocolUtil::readf(m_stream, kMsgDKeyRepeat + 4, &id, &mask, &count, &button); LOG((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d, button=0x%04x", id, mask, count, button)); @@ -545,7 +491,7 @@ CServerProxy::keyRepeat() LOG((CLOG_DEBUG1 "key repeat translated to id=%d, mask=0x%04x", id2, mask2)); // forward - getClient()->keyRepeat(id2, mask2, count, button); + m_client->keyRepeat(id2, mask2, count, button); } void @@ -556,7 +502,7 @@ CServerProxy::keyUp() // parse UInt16 id, mask, button; - CProtocolUtil::readf(getInputStream(), kMsgDKeyUp + 4, &id, &mask, &button); + CProtocolUtil::readf(m_stream, kMsgDKeyUp + 4, &id, &mask, &button); LOG((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x, button=0x%04x", id, mask, button)); // translate @@ -568,7 +514,7 @@ CServerProxy::keyUp() LOG((CLOG_DEBUG1 "key up translated to id=%d, mask=0x%04x", id2, mask2)); // forward - getClient()->keyUp(id2, mask2, button); + m_client->keyUp(id2, mask2, button); } void @@ -579,11 +525,11 @@ CServerProxy::mouseDown() // parse SInt8 id; - CProtocolUtil::readf(getInputStream(), kMsgDMouseDown + 4, &id); + CProtocolUtil::readf(m_stream, kMsgDMouseDown + 4, &id); LOG((CLOG_DEBUG1 "recv mouse down id=%d", id)); // forward - getClient()->mouseDown(static_cast(id)); + m_client->mouseDown(static_cast(id)); } void @@ -594,11 +540,11 @@ CServerProxy::mouseUp() // parse SInt8 id; - CProtocolUtil::readf(getInputStream(), kMsgDMouseUp + 4, &id); + CProtocolUtil::readf(m_stream, kMsgDMouseUp + 4, &id); LOG((CLOG_DEBUG1 "recv mouse up id=%d", id)); // forward - getClient()->mouseUp(static_cast(id)); + m_client->mouseUp(static_cast(id)); } void @@ -607,30 +553,27 @@ CServerProxy::mouseMove() // parse bool ignore; SInt16 x, y; - CProtocolUtil::readf(getInputStream(), kMsgDMouseMove + 4, &x, &y); + CProtocolUtil::readf(m_stream, kMsgDMouseMove + 4, &x, &y); - { - // note if we should ignore the move - CLock lock(&m_mutex); - ignore = m_ignoreMouse; + // note if we should ignore the move + ignore = m_ignoreMouse; - // compress mouse motion events if more input follows - if (!ignore && !m_compressMouse && getInputStream()->getSize() > 0) { - m_compressMouse = true; - } + // compress mouse motion events if more input follows + if (!ignore && !m_compressMouse && m_stream->isReady()) { + m_compressMouse = true; + } - // if compressing then ignore the motion but record it - if (m_compressMouse) { - ignore = true; - m_xMouse = x; - m_yMouse = y; - } + // if compressing then ignore the motion but record it + if (m_compressMouse) { + ignore = true; + m_xMouse = x; + m_yMouse = y; } LOG((CLOG_DEBUG2 "recv mouse move %d,%d", x, y)); // forward if (!ignore) { - getClient()->mouseMove(x, y); + m_client->mouseMove(x, y); } } @@ -642,11 +585,11 @@ CServerProxy::mouseWheel() // parse SInt16 delta; - CProtocolUtil::readf(getInputStream(), kMsgDMouseWheel + 4, &delta); + CProtocolUtil::readf(m_stream, kMsgDMouseWheel + 4, &delta); LOG((CLOG_DEBUG2 "recv mouse wheel %+d", delta)); // forward - getClient()->mouseWheel(delta); + m_client->mouseWheel(delta); } void @@ -654,11 +597,11 @@ CServerProxy::screensaver() { // parse SInt8 on; - CProtocolUtil::readf(getInputStream(), kMsgCScreenSaver + 4, &on); + CProtocolUtil::readf(m_stream, kMsgCScreenSaver + 4, &on); LOG((CLOG_DEBUG1 "recv screen saver on=%d", on)); // forward - getClient()->screensaver(on != 0); + m_client->screensaver(on != 0); } void @@ -668,22 +611,18 @@ CServerProxy::resetOptions() LOG((CLOG_DEBUG1 "recv reset options")); // forward - getClient()->resetOptions(); + m_client->resetOptions(); - CLock lock(&m_mutex); - - // reset heart rate - m_heartRate = kHeartRate; + // reset heart rate and send heartbeat if necessary + installHeartBeat(kHeartRate); + if (m_heartRate >= 0.0) { + CProtocolUtil::writef(m_stream, kMsgCNoop); + } // reset modifier translation table for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) { m_modifierTranslationTable[id] = id; } - - // send heartbeat if necessary - if (m_heartRate >= 0.0) { - CProtocolUtil::writef(getOutputStream(), kMsgCNoop); - } } void @@ -691,13 +630,11 @@ CServerProxy::setOptions() { // parse COptionsList options; - CProtocolUtil::readf(getInputStream(), kMsgDSetOptions + 4, &options); + CProtocolUtil::readf(m_stream, kMsgDSetOptions + 4, &options); LOG((CLOG_DEBUG1 "recv set options size=%d", options.size())); // forward - getClient()->setOptions(options); - - CLock lock(&m_mutex); + m_client->setOptions(options); // update modifier table for (UInt32 i = 0, n = options.size(); i < n; i += 2) { @@ -718,12 +655,10 @@ CServerProxy::setOptions() id = kKeyModifierIDSuper; } else if (options[i] == kOptionHeartbeat) { - // update heart rate - m_heartRate = 1.0e-3 * static_cast(options[i + 1]); - - // send heartbeat if necessary + // update heart rate and send heartbeat if necessary + installHeartBeat(1.0e-3 * static_cast(options[i + 1])); if (m_heartRate >= 0.0) { - CProtocolUtil::writef(getOutputStream(), kMsgCNoop); + CProtocolUtil::writef(m_stream, kMsgCNoop); } } if (id != kKeyModifierIDNull) { @@ -737,24 +672,14 @@ CServerProxy::setOptions() void CServerProxy::queryInfo() { - // get current info CClientInfo info; - getClient()->getShape(info.m_x, info.m_y, info.m_w, info.m_h); - getClient()->getCursorPos(info.m_mx, info.m_my); - info.m_zoneSize = getClient()->getJumpZoneSize(); - - // send it - CLock lock(&m_mutex); + m_client->getShape(info.m_x, info.m_y, info.m_w, info.m_h); sendInfo(info); } void CServerProxy::infoAcknowledgment() { - // parse LOG((CLOG_DEBUG1 "recv info acknowledgment")); - - // now allow mouse motion - CLock lock(&m_mutex); m_ignoreMouse = false; } diff --git a/lib/client/CServerProxy.h b/lib/client/CServerProxy.h index cbb58f1b..f54cbe60 100644 --- a/lib/client/CServerProxy.h +++ b/lib/client/CServerProxy.h @@ -15,88 +15,66 @@ #ifndef CSERVERPROXY_H #define CSERVERPROXY_H -#include "IScreenReceiver.h" +#include "ClipboardTypes.h" #include "KeyTypes.h" -#include "CMutex.h" +#include "CEvent.h" -class IClient; -class IInputStream; -class IOutputStream; +class CClient; +class CClientInfo; +class CEventQueueTimer; +class IClipboard; +class IStream; //! Proxy for server /*! This class acts a proxy for the server, converting calls into messages to the server and messages from the server to calls on the client. */ -class CServerProxy : public IScreenReceiver { +class CServerProxy { public: - /*! \c adoptedInput is the stream from the server and - \c adoptedOutput is the stream to the server. This object - takes ownership of both and destroys them in the d'tor. - Messages from the server are converted to calls on \c client. + /*! + Process messages from the server on \p stream and forward to + \p client. */ - CServerProxy(IClient* client, - IInputStream* adoptedInput, - IOutputStream* adoptedOutput); + CServerProxy(CClient* client, IStream* stream); ~CServerProxy(); //! @name manipulators //@{ - //! Run event loop - /*! - Run the event loop and return when the server disconnects or - requests the client to disconnect. Return true iff the server - didn't reject our connection. - - (cancellation point) - */ - bool mainLoop(); + virtual void onInfoChanged(); + virtual bool onGrabClipboard(ClipboardID); + virtual void onClipboardChanged(ClipboardID, const IClipboard*); //@} //! @name accessors //@{ - //! Get client + //! Get handshake complete event type /*! - Returns the client passed to the c'tor. + Returns the handshake complete event type. This is sent when the + client has completed the handshake with the server. */ - IClient* getClient() const; - - //! Get input stream - /*! - Return the input stream passed to the c'tor. - */ - IInputStream* getInputStream() const; - - //! Get output stream - /*! - Return the output stream passed to the c'tor. - */ - IOutputStream* getOutputStream() const; + static CEvent::Type getHandshakeCompleteEvent(); //@} - // IScreenReceiver overrides - virtual void onError(); - virtual void onInfoChanged(const CClientInfo&); - virtual bool onGrabClipboard(ClipboardID); - virtual void onClipboardChanged(ClipboardID, const CString& data); - private: - - // get the client name (from the client) - CString getName() const; - // if compressing mouse motion then send the last motion now void flushCompressedMouse(); void sendInfo(const CClientInfo&); + void installHeartBeat(double); + // modifier key translation KeyID translateKey(KeyID) const; KeyModifierMask translateModifierMask(KeyModifierMask) const; + // event handlers + void handleMessage(const CEvent&, void*); + void handleHeartBeat(const CEvent&, void*); + // message handlers void enter(); void leave(); @@ -116,11 +94,9 @@ private: void infoAcknowledgment(); private: - CMutex m_mutex; - - IClient* m_client; - IInputStream* m_input; - IOutputStream* m_output; + CClient* m_client; + IStream* m_stream; + CEventQueueTimer* m_timer; UInt32 m_seqNum; @@ -131,6 +107,8 @@ private: KeyModifierID m_modifierTranslationTable[kKeyModifierIDLast]; double m_heartRate; + + static CEvent::Type s_handshakeCompleteEvent; }; #endif diff --git a/lib/io/CStreamFilter.cpp b/lib/io/CStreamFilter.cpp index 00c064a0..d5a2a350 100644 --- a/lib/io/CStreamFilter.cpp +++ b/lib/io/CStreamFilter.cpp @@ -77,7 +77,7 @@ CStreamFilter::setEventFilter(IEventJob* filter) void* CStreamFilter::getEventTarget() const { - return const_cast(reinterpret_cast(this)); + return getStream()->getEventTarget(); } bool diff --git a/lib/net/CNetworkAddress.cpp b/lib/net/CNetworkAddress.cpp index 3b2e3032..758f76dc 100644 --- a/lib/net/CNetworkAddress.cpp +++ b/lib/net/CNetworkAddress.cpp @@ -113,6 +113,9 @@ CNetworkAddress::CNetworkAddress(const CString& hostname_, int port) : catch (XArchNetworkNameNoAddress&) { throw XSocketAddress(XSocketAddress::kNoAddress, hostname, port); } + catch (XArchNetworkNameUnsupported&) { + throw XSocketAddress(XSocketAddress::kUnsupported, hostname, port); + } catch (XArchNetworkName&) { throw XSocketAddress(XSocketAddress::kUnknown, hostname, port); } diff --git a/lib/net/CTCPListenSocket.cpp b/lib/net/CTCPListenSocket.cpp index bca77cd4..3f503d11 100644 --- a/lib/net/CTCPListenSocket.cpp +++ b/lib/net/CTCPListenSocket.cpp @@ -19,10 +19,9 @@ #include "TSocketMultiplexerMethodJob.h" #include "XSocket.h" #include "XIO.h" -#include "CEvent.h" -#include "CEventQueue.h" #include "CLock.h" #include "CMutex.h" +#include "IEventQueue.h" #include "CArch.h" #include "XArch.h" @@ -125,8 +124,7 @@ CTCPListenSocket::serviceListening(ISocketMultiplexerJob* job, return NULL; } if (read) { - CEventQueue::getInstance()->addEvent( - CEvent(getConnectingEvent(), this, NULL)); + EVENTQUEUE->addEvent(CEvent(getConnectingEvent(), this, NULL)); // stop polling on this socket until the client accepts return NULL; } diff --git a/lib/net/CTCPSocket.cpp b/lib/net/CTCPSocket.cpp index 0b7b6ddf..78dc540a 100644 --- a/lib/net/CTCPSocket.cpp +++ b/lib/net/CTCPSocket.cpp @@ -18,11 +18,12 @@ #include "TSocketMultiplexerMethodJob.h" #include "XSocket.h" #include "CLock.h" -#include "CEventQueue.h" #include "CLog.h" +#include "IEventQueue.h" #include "IEventJob.h" #include "CArch.h" #include "XArch.h" +#include // // CTCPSocket @@ -84,6 +85,9 @@ CTCPSocket::bind(const CNetworkAddress& addr) void CTCPSocket::close() { + // remove ourself from the multiplexer + setJob(NULL); + CLock lock(&m_mutex); // clear buffers and enter disconnected state @@ -92,9 +96,6 @@ CTCPSocket::close() } onDisconnected(); - // remove ourself from the multiplexer - setJob(NULL); - // close the socket if (m_socket != NULL) { CArchSocket socket = m_socket; @@ -141,26 +142,29 @@ CTCPSocket::read(void* buffer, UInt32 n) void CTCPSocket::write(const void* buffer, UInt32 n) { - CLock lock(&m_mutex); + bool wasEmpty; + { + CLock lock(&m_mutex); - // must not have shutdown output - if (!m_writable) { - sendStreamEvent(getOutputErrorEvent()); - return; + // must not have shutdown output + if (!m_writable) { + sendStreamEvent(getOutputErrorEvent()); + return; + } + + // ignore empty writes + if (n == 0) { + return; + } + + // copy data to the output buffer + wasEmpty = (m_outputBuffer.getSize() == 0); + m_outputBuffer.write(buffer, n); + + // there's data to write + m_flushed = false; } - // ignore empty writes - if (n == 0) { - return; - } - - // copy data to the output buffer - bool wasEmpty = (m_outputBuffer.getSize() == 0); - m_outputBuffer.write(buffer, n); - - // there's data to write - m_flushed = false; - // make sure we're waiting to write if (wasEmpty) { setJob(newJob()); @@ -179,20 +183,26 @@ CTCPSocket::flush() void CTCPSocket::shutdownInput() { - CLock lock(&m_mutex); + bool useNewJob = false; + { + CLock lock(&m_mutex); - // shutdown socket for reading - try { - ARCH->closeSocketForRead(m_socket); - } - catch (XArchNetwork&) { - // ignore - } + // shutdown socket for reading + try { + ARCH->closeSocketForRead(m_socket); + } + catch (XArchNetwork&) { + // ignore + } - // shutdown buffer for reading - if (m_readable) { - sendStreamEvent(getInputShutdownEvent()); - onInputShutdown(); + // shutdown buffer for reading + if (m_readable) { + sendStreamEvent(getInputShutdownEvent()); + onInputShutdown(); + useNewJob = true; + } + } + if (useNewJob) { setJob(newJob()); } } @@ -200,20 +210,26 @@ CTCPSocket::shutdownInput() void CTCPSocket::shutdownOutput() { - CLock lock(&m_mutex); + bool useNewJob = false; + { + CLock lock(&m_mutex); - // shutdown socket for writing - try { - ARCH->closeSocketForWrite(m_socket); - } - catch (XArchNetwork&) { - // ignore - } + // shutdown socket for writing + try { + ARCH->closeSocketForWrite(m_socket); + } + catch (XArchNetwork&) { + // ignore + } - // shutdown buffer for writing - if (m_writable) { - sendStreamEvent(getOutputShutdownEvent()); - onOutputShutdown(); + // shutdown buffer for writing + if (m_writable) { + sendStreamEvent(getOutputShutdownEvent()); + onOutputShutdown(); + useNewJob = true; + } + } + if (useNewJob) { setJob(newJob()); } } @@ -249,28 +265,29 @@ CTCPSocket::getEventFilter() const void CTCPSocket::connect(const CNetworkAddress& addr) { - CLock lock(&m_mutex); + { + CLock lock(&m_mutex); - // fail on attempts to reconnect - if (m_socket == NULL || m_connected) { - sendSocketEvent(getConnectionFailedEvent()); - return; - } + // fail on attempts to reconnect + if (m_socket == NULL || m_connected) { + sendConnectionFailedEvent("busy"); + return; + } - try { - ARCH->connectSocket(m_socket, addr.getAddress()); - sendSocketEvent(getConnectedEvent()); - onConnected(); - setJob(newJob()); - } - catch (XArchNetworkConnecting&) { - // connection is in progress - m_writable = true; - setJob(newJob()); - } - catch (XArchNetwork& e) { - throw XSocketConnect(e.what()); + try { + ARCH->connectSocket(m_socket, addr.getAddress()); + sendSocketEvent(getConnectedEvent()); + onConnected(); + } + catch (XArchNetworkConnecting&) { + // connection is in progress + m_writable = true; + } + catch (XArchNetwork& e) { + throw XSocketConnect(e.what()); + } } + setJob(newJob()); } void @@ -339,17 +356,27 @@ CTCPSocket::newJob() void CTCPSocket::sendSocketEvent(CEvent::Type type) { - EVENTQUEUE->addEvent(CEvent(type, this, NULL)); + EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), NULL)); +} + +void +CTCPSocket::sendConnectionFailedEvent(const char* msg) +{ + CConnectionFailedInfo* info = (CConnectionFailedInfo*)malloc( + sizeof(CConnectionFailedInfo) + strlen(msg)); + strcpy(info->m_what, msg); + EVENTQUEUE->addEvent(CEvent(getConnectionFailedEvent(), + getEventTarget(), info)); } void CTCPSocket::sendStreamEvent(CEvent::Type type) { if (m_eventFilter != NULL) { - m_eventFilter->run(CEvent(type, this, NULL)); + m_eventFilter->run(CEvent(type, getEventTarget(), NULL)); } else { - EVENTQUEUE->addEvent(CEvent(type, this, NULL)); + EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), NULL)); } } @@ -394,22 +421,18 @@ CTCPSocket::serviceConnecting(ISocketMultiplexerJob* job, { CLock lock(&m_mutex); - if (write && !error) { + if (error) { try { // connection may have failed or succeeded ARCH->throwErrorOnSocket(m_socket); } - catch (XArchNetwork&) { - error = true; + catch (XArchNetwork& e) { + sendConnectionFailedEvent(e.what().c_str()); + onDisconnected(); + return newJob(); } } - if (error) { - sendSocketEvent(getConnectionFailedEvent()); - onDisconnected(); - return newJob(); - } - if (write) { sendSocketEvent(getConnectedEvent()); onConnected(); diff --git a/lib/net/CTCPSocket.h b/lib/net/CTCPSocket.h index 0335dce1..8b60d5b6 100644 --- a/lib/net/CTCPSocket.h +++ b/lib/net/CTCPSocket.h @@ -60,6 +60,7 @@ private: void setJob(ISocketMultiplexerJob*); ISocketMultiplexerJob* newJob(); void sendSocketEvent(CEvent::Type); + void sendConnectionFailedEvent(const char*); void sendStreamEvent(CEvent::Type); void onConnected(); diff --git a/lib/net/IDataSocket.h b/lib/net/IDataSocket.h index fd071669..bc0f2d3f 100644 --- a/lib/net/IDataSocket.h +++ b/lib/net/IDataSocket.h @@ -25,6 +25,12 @@ represent a full-duplex data stream. */ class IDataSocket : public ISocket, public IStream { public: + class CConnectionFailedInfo { + public: + // pointer to a string describing the failure + char m_what[1]; + }; + //! @name manipulators //@{ @@ -52,6 +58,7 @@ public: /*! Returns the socket connection failed event type. A socket sends this event when an attempt to connect to a remote port has failed. + The data is a pointer to a CConnectionFailedInfo. */ static CEvent::Type getConnectionFailedEvent(); diff --git a/lib/net/XSocket.cpp b/lib/net/XSocket.cpp index cf8b3ca6..8471538a 100644 --- a/lib/net/XSocket.cpp +++ b/lib/net/XSocket.cpp @@ -53,12 +53,14 @@ XSocketAddress::getWhat() const throw() "XSocketAddressUnknown", "XSocketAddressNotFound", "XSocketAddressNoAddress", + "XSocketAddressUnsupported", "XSocketAddressBadPort" }; static const char* s_errorMsg[] = { "unknown error for: %{1}:%{2}", "address not found for: %{1}", "no address for: %{1}", + "unsupported address for: %{1}", "invalid port" // m_port may not be set to the bad port }; return format(s_errorID[m_error], s_errorMsg[m_error], diff --git a/lib/net/XSocket.h b/lib/net/XSocket.h index c3663033..f84285b9 100644 --- a/lib/net/XSocket.h +++ b/lib/net/XSocket.h @@ -34,6 +34,7 @@ public: kUnknown, //!< Unknown error kNotFound, //!< The hostname is unknown kNoAddress, //!< The hostname is valid but has no IP address + kUnsupported, //!< The hostname is valid but has no supported address kBadPort //!< The port is invalid }; diff --git a/lib/platform/CXWindowsScreen.cpp b/lib/platform/CXWindowsScreen.cpp index c7e5f1cc..3b4f6104 100644 --- a/lib/platform/CXWindowsScreen.cpp +++ b/lib/platform/CXWindowsScreen.cpp @@ -835,7 +835,7 @@ CXWindowsScreen::sendEvent(CEvent::Type type, void* data) void CXWindowsScreen::sendClipboardEvent(CEvent::Type type, ClipboardID id) { - CClipboardInfo* info = new CClipboardInfo; + CClipboardInfo* info = (CClipboardInfo*)malloc(sizeof(CClipboardInfo)); info->m_id = id; info->m_sequenceNumber = m_sequenceNumber; sendEvent(type, info); @@ -1028,11 +1028,11 @@ CXWindowsScreen::onKeyPress(XKeyEvent& xkey) } // handle key - sendEvent(getKeyDownEvent(), new CKeyInfo(key, mask, keycode, 1)); + sendEvent(getKeyDownEvent(), CKeyInfo::alloc(key, mask, keycode, 1)); KeyModifierMask keyMask = m_keyState->getMaskForKey(keycode); if (m_keyState->isHalfDuplex(keyMask)) { sendEvent(getKeyUpEvent(), - new CKeyInfo(key, mask | keyMask, keycode, 1)); + CKeyInfo::alloc(key, mask | keyMask, keycode, 1)); } } } @@ -1040,7 +1040,7 @@ CXWindowsScreen::onKeyPress(XKeyEvent& xkey) Bool CXWindowsScreen::findKeyEvent(Display*, XEvent* xevent, XPointer arg) { - CKeyEventInfo* filter = reinterpret_cast(arg); + CKeyEventFilter* filter = reinterpret_cast(arg); return (xevent->type == filter->m_event && xevent->xkey.window == filter->m_window && xevent->xkey.time == filter->m_time && @@ -1057,7 +1057,7 @@ CXWindowsScreen::onKeyRelease(XKeyEvent& xkey) // KeyPress event that has the same key and time as // this release event, if any. first prepare the // filter info. - CKeyEventInfo filter; + CKeyEventFilter filter; filter.m_event = KeyPress; filter.m_window = xkey.window; filter.m_time = xkey.time; @@ -1089,9 +1089,9 @@ CXWindowsScreen::onKeyRelease(XKeyEvent& xkey) KeyModifierMask keyMask = m_keyState->getMaskForKey(keycode); if (m_keyState->isHalfDuplex(keyMask)) { sendEvent(getKeyDownEvent(), - new CKeyInfo(key, mask, keycode, 1)); + CKeyInfo::alloc(key, mask, keycode, 1)); } - sendEvent(getKeyUpEvent(), new CKeyInfo(key, mask, keycode, 1)); + sendEvent(getKeyUpEvent(), CKeyInfo::alloc(key, mask, keycode, 1)); } else { // found a press event following so it's a repeat. @@ -1099,7 +1099,8 @@ CXWindowsScreen::onKeyRelease(XKeyEvent& xkey) // repeats but we'll just send a repeat of 1. // note that we discard the press event. LOG((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", keycode, xkey.state)); - sendEvent(getKeyRepeatEvent(), new CKeyInfo(key, mask, keycode, 1)); + sendEvent(getKeyRepeatEvent(), + CKeyInfo::alloc(key, mask, keycode, 1)); } } } @@ -1110,7 +1111,7 @@ CXWindowsScreen::onMousePress(const XButtonEvent& xbutton) LOG((CLOG_DEBUG1 "event: ButtonPress button=%d", xbutton.button)); const ButtonID button = mapButtonFromX(&xbutton); if (button != kButtonNone) { - sendEvent(getButtonDownEvent(), new CButtonInfo(button)); + sendEvent(getButtonDownEvent(), CButtonInfo::alloc(button)); } } @@ -1120,15 +1121,15 @@ CXWindowsScreen::onMouseRelease(const XButtonEvent& xbutton) LOG((CLOG_DEBUG1 "event: ButtonRelease button=%d", xbutton.button)); const ButtonID button = mapButtonFromX(&xbutton); if (button != kButtonNone) { - sendEvent(getButtonUpEvent(), new CButtonInfo(button)); + sendEvent(getButtonUpEvent(), CButtonInfo::alloc(button)); } else if (xbutton.button == 4) { // wheel forward (away from user) - sendEvent(getWheelEvent(), new CWheelInfo(120)); + sendEvent(getWheelEvent(), CWheelInfo::alloc(120)); } else if (xbutton.button == 5) { // wheel backward (toward user) - sendEvent(getWheelEvent(), new CWheelInfo(-120)); + sendEvent(getWheelEvent(), CWheelInfo::alloc(-120)); } } @@ -1160,7 +1161,7 @@ CXWindowsScreen::onMouseMove(const XMotionEvent& xmotion) else if (m_isOnScreen) { // motion on primary screen sendEvent(getMotionOnPrimaryEvent(), - new CMotionInfo(m_xCursor, m_yCursor)); + CMotionInfo::alloc(m_xCursor, m_yCursor)); } else { // motion on secondary screen. warp mouse back to @@ -1190,7 +1191,7 @@ CXWindowsScreen::onMouseMove(const XMotionEvent& xmotion) // warping to the primary screen's enter position, // effectively overriding it. if (x != 0 || y != 0) { - sendEvent(getMotionOnSecondaryEvent(), new CMotionInfo(x, y)); + sendEvent(getMotionOnSecondaryEvent(), CMotionInfo::alloc(x, y)); } } } diff --git a/lib/platform/CXWindowsScreen.h b/lib/platform/CXWindowsScreen.h index 65ecfcee..b56c693d 100644 --- a/lib/platform/CXWindowsScreen.h +++ b/lib/platform/CXWindowsScreen.h @@ -108,7 +108,7 @@ private: static int ioErrorHandler(Display*); private: - class CKeyEventInfo { + class CKeyEventFilter { public: int m_event; Window m_window; diff --git a/lib/server/CClientProxy.h b/lib/server/CClientProxy.h index aad57f6a..4a95ebc5 100644 --- a/lib/server/CClientProxy.h +++ b/lib/server/CClientProxy.h @@ -78,7 +78,7 @@ public: UInt32 seqNum, KeyModifierMask mask, bool forScreensaver) = 0; virtual bool leave() = 0; - virtual void setClipboard(ClipboardID, const CString&) = 0; + virtual void setClipboard(ClipboardID, const IClipboard*) = 0; virtual void grabClipboard(ClipboardID) = 0; virtual void setClipboardDirty(ClipboardID, bool) = 0; virtual void keyDown(KeyID, KeyModifierMask, KeyButton) = 0; diff --git a/lib/server/CClientProxy1_0.cpp b/lib/server/CClientProxy1_0.cpp index 505eacfa..617e12a2 100644 --- a/lib/server/CClientProxy1_0.cpp +++ b/lib/server/CClientProxy1_0.cpp @@ -218,11 +218,9 @@ CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const } void -CClientProxy1_0::getCursorPos(SInt32& x, SInt32& y) const +CClientProxy1_0::getCursorPos(SInt32&, SInt32&) const { assert(0 && "shouldn't be called"); - x = m_info.m_mx; - y = m_info.m_my; } void @@ -245,13 +243,15 @@ CClientProxy1_0::leave() } void -CClientProxy1_0::setClipboard(ClipboardID id, const CString& data) +CClientProxy1_0::setClipboard(ClipboardID id, const IClipboard* clipboard) { // ignore if this clipboard is already clean if (m_clipboard[id].m_dirty) { // this clipboard is now clean m_clipboard[id].m_dirty = false; + CClipboard::copy(&m_clipboard[id].m_clipboard, clipboard); + CString data = m_clipboard[id].m_clipboard.marshall(); LOG((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getName().c_str(), data.size())); CProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, &data); } @@ -366,29 +366,23 @@ bool CClientProxy1_0::recvInfo() { // parse the message - SInt16 x, y, w, h, zoneSize, mx, my; + SInt16 x, y, w, h, dummy1, dummy2, dummy3; if (!CProtocolUtil::readf(getStream(), kMsgDInfo + 4, - &x, &y, &w, &h, &zoneSize, &mx, &my)) { + &x, &y, &w, &h, &dummy1, &dummy2, &dummy3)) { return false; } - LOG((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d, zone=%d, pos=%d,%d", getName().c_str(), x, y, w, h, zoneSize, mx, my)); + LOG((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d", getName().c_str(), x, y, w, h)); // validate - if (w <= 0 || h <= 0 || zoneSize < 0) { - return false; - } - if (mx < x || my < y || mx >= x + w || my >= y + h) { + if (w <= 0 || h <= 0) { return false; } // save - m_info.m_x = x; - m_info.m_y = y; - m_info.m_w = w; - m_info.m_h = h; - m_info.m_zoneSize = zoneSize; - m_info.m_mx = mx; - m_info.m_my = my; + m_info.m_x = x; + m_info.m_y = y; + m_info.m_w = w; + m_info.m_h = h; // acknowledge receipt LOG((CLOG_DEBUG1 "send info ack to \"%s\"", getName().c_str())); diff --git a/lib/server/CClientProxy1_0.h b/lib/server/CClientProxy1_0.h index 26584e97..503dcba9 100644 --- a/lib/server/CClientProxy1_0.h +++ b/lib/server/CClientProxy1_0.h @@ -39,7 +39,7 @@ public: UInt32 seqNum, KeyModifierMask mask, bool forScreensaver); virtual bool leave(); - virtual void setClipboard(ClipboardID, const CString&); + virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); virtual void setClipboardDirty(ClipboardID, bool); virtual void keyDown(KeyID, KeyModifierMask, KeyButton); diff --git a/lib/server/CConfig.cpp b/lib/server/CConfig.cpp index 0b40a666..a4893cc8 100644 --- a/lib/server/CConfig.cpp +++ b/lib/server/CConfig.cpp @@ -761,8 +761,9 @@ CConfig::readSectionOptions(std::istream& s) try { m_synergyAddress = CNetworkAddress(value, kDefaultPort); } - catch (XSocketAddress&) { - throw XConfigRead("invalid address argument"); + catch (XSocketAddress& e) { + throw XConfigRead(CString("invalid address argument: ") + + e.what()); } } else if (name == "heartbeat") { diff --git a/lib/server/CPrimaryClient.cpp b/lib/server/CPrimaryClient.cpp index 5a4de474..9da849bd 100644 --- a/lib/server/CPrimaryClient.cpp +++ b/lib/server/CPrimaryClient.cpp @@ -42,6 +42,12 @@ CPrimaryClient::reconfigure(UInt32 activeSides) m_screen->reconfigure(activeSides); } +SInt32 +CPrimaryClient::getJumpZoneSize() const +{ + return m_screen->getJumpZoneSize(); +} + void CPrimaryClient::getCursorCenter(SInt32& x, SInt32& y) const { @@ -72,12 +78,6 @@ CPrimaryClient::getClipboard(ClipboardID id, IClipboard* clipboard) const return m_screen->getClipboard(id, clipboard); } -SInt32 -CPrimaryClient::getJumpZoneSize() const -{ - return m_screen->getJumpZoneSize(); -} - void CPrimaryClient::getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const @@ -121,19 +121,15 @@ CPrimaryClient::leave() } void -CPrimaryClient::setClipboard(ClipboardID id, const CString& data) +CPrimaryClient::setClipboard(ClipboardID id, const IClipboard* clipboard) { // ignore if this clipboard is already clean if (m_clipboardDirty[id]) { // this clipboard is now clean m_clipboardDirty[id] = false; - // unmarshall data - CClipboard clipboard; - clipboard.unmarshall(data, 0); - // set clipboard - m_screen->setClipboard(id, &clipboard); + m_screen->setClipboard(id, clipboard); } } diff --git a/lib/server/CPrimaryClient.h b/lib/server/CPrimaryClient.h index 61eec5b6..985fc4a3 100644 --- a/lib/server/CPrimaryClient.h +++ b/lib/server/CPrimaryClient.h @@ -47,6 +47,13 @@ public: //! @name accessors //@{ + //! Get jump zone size + /*! + Return the jump zone size, the size of the regions on the edges of + the screen that cause the cursor to jump to another screen. + */ + SInt32 getJumpZoneSize() const; + //! Get cursor center position /*! Return the cursor center position which is where we park the @@ -76,7 +83,6 @@ public: // IScreen overrides virtual void* getEventTarget() const; virtual bool getClipboard(ClipboardID id, IClipboard*) const; - virtual SInt32 getJumpZoneSize() const; virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; virtual void getCursorPos(SInt32& x, SInt32& y) const; @@ -86,7 +92,7 @@ public: UInt32 seqNum, KeyModifierMask mask, bool forScreensaver); virtual bool leave(); - virtual void setClipboard(ClipboardID, const CString&); + virtual void setClipboard(ClipboardID, const IClipboard*); virtual void grabClipboard(ClipboardID); virtual void setClipboardDirty(ClipboardID, bool); virtual void keyDown(KeyID, KeyModifierMask, KeyButton); diff --git a/lib/server/CServer.cpp b/lib/server/CServer.cpp index cc2282f5..654ef383 100644 --- a/lib/server/CServer.cpp +++ b/lib/server/CServer.cpp @@ -16,7 +16,6 @@ #include "CClientProxy.h" #include "CClientProxyUnknown.h" #include "CPrimaryClient.h" -#include "CPacketStreamFilter.h" #include "IPlatformScreen.h" #include "OptionTypes.h" #include "ProtocolTypes.h" @@ -205,6 +204,11 @@ CServer::adoptClient(IClient* client) { assert(client != NULL); + // watch for client disconnection + EVENTQUEUE->adoptHandler(CClientProxy::getDisconnectedEvent(), client, + new TMethodEventJob(this, + &CServer::handleClientDisconnected, client)); + // name must be in our configuration if (!m_config.isScreen(client->getName())) { LOG((CLOG_WARN "a client with name \"%s\" is not in the map", client->getName().c_str())); @@ -221,11 +225,6 @@ CServer::adoptClient(IClient* client) } LOG((CLOG_NOTE "client \"%s\" has connected", getName(client).c_str())); - // watch for client disconnection - EVENTQUEUE->adoptHandler(CClientProxy::getDisconnectedEvent(), client, - new TMethodEventJob(this, - &CServer::handleClientDisconnected, client)); - // send configuration options to client sendOptions(client); @@ -287,7 +286,11 @@ CServer::onCommandKey(KeyID /*id*/, KeyModifierMask /*mask*/, bool /*down*/) CString CServer::getName(const IClient* client) const { - return m_config.getCanonicalName(client->getName()); + CString name = m_config.getCanonicalName(client->getName()); + if (name.empty()) { + name = client->getName(); + } + return name; } UInt32 @@ -397,7 +400,7 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) // send the clipboard data to new active screen for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - m_active->setClipboard(id, m_clipboards[id].m_clipboardData); + m_active->setClipboard(id, &m_clipboards[id].m_clipboard); } } else { @@ -1078,7 +1081,7 @@ CServer::onClipboardChanged(IClient* sender, ClipboardID id, UInt32 seqNum) } // send the new clipboard to the active screen - m_active->setClipboard(id, clipboard.m_clipboardData); + m_active->setClipboard(id, &clipboard.m_clipboard); } void diff --git a/lib/synergy/CClipboard.cpp b/lib/synergy/CClipboard.cpp index d82448ac..d0388a01 100644 --- a/lib/synergy/CClipboard.cpp +++ b/lib/synergy/CClipboard.cpp @@ -101,126 +101,14 @@ CClipboard::get(EFormat format) const return m_data[format]; } -bool -CClipboard::copy(IClipboard* dst, const IClipboard* src) -{ - assert(dst != NULL); - assert(src != NULL); - - return copy(dst, src, src->getTime()); -} - -bool -CClipboard::copy(IClipboard* dst, const IClipboard* src, Time time) -{ - assert(dst != NULL); - assert(src != NULL); - - bool success = false; - if (src->open(time)) { - if (dst->open(time)) { - if (dst->empty()) { - for (SInt32 format = 0; - format != IClipboard::kNumFormats; ++format) { - IClipboard::EFormat eFormat = (IClipboard::EFormat)format; - if (src->has(eFormat)) { - dst->add(eFormat, src->get(eFormat)); - } - } - success = true; - } - dst->close(); - } - src->close(); - } - - return success; -} - void CClipboard::unmarshall(const CString& data, Time time) { - const char* index = data.data(); - - // clear existing data - open(time); - empty(); - - // read the number of formats - const UInt32 numFormats = readUInt32(index); - index += 4; - - // read each format - for (UInt32 i = 0; i < numFormats; ++i) { - // get the format id - UInt32 format = readUInt32(index); - index += 4; - - // get the size of the format data - UInt32 size = readUInt32(index); - index += 4; - - // save the data if it's a known format. if either the client - // or server supports more clipboard formats than the other - // then one of them will get a format >= kNumFormats here. - if (format < static_cast(IClipboard::kNumFormats)) { - m_added[format] = true; - m_data[format] = CString(index, size); - } - index += size; - } - - // done - close(); + IClipboard::unmarshall(this, data, time); } CString CClipboard::marshall() const { - CString data; - - // compute size of marshalled data - UInt32 size = 4; - UInt32 numFormats = 0; - UInt32 format; - for (format = 0; format != IClipboard::kNumFormats; ++format) { - if (m_added[format]) { - ++numFormats; - size += 4 + 4 + m_data[format].size(); - } - } - - // allocate space - data.reserve(size); - - // marshall the data - writeUInt32(&data, numFormats); - for (format = 0; format != IClipboard::kNumFormats; ++format) { - if (m_added[format]) { - writeUInt32(&data, format); - writeUInt32(&data, m_data[format].size()); - data += m_data[format]; - } - } - - return data; -} - -UInt32 -CClipboard::readUInt32(const char* buf) const -{ - const unsigned char* ubuf = reinterpret_cast(buf); - return (static_cast(ubuf[0]) << 24) | - (static_cast(ubuf[1]) << 16) | - (static_cast(ubuf[2]) << 8) | - static_cast(ubuf[3]); -} - -void -CClipboard::writeUInt32(CString* buf, UInt32 v) const -{ - *buf += static_cast((v >> 24) & 0xff); - *buf += static_cast((v >> 16) & 0xff); - *buf += static_cast((v >> 8) & 0xff); - *buf += static_cast( v & 0xff); + return IClipboard::marshall(this); } diff --git a/lib/synergy/CClipboard.h b/lib/synergy/CClipboard.h index 55d12938..f8d10aff 100644 --- a/lib/synergy/CClipboard.h +++ b/lib/synergy/CClipboard.h @@ -47,25 +47,6 @@ public: */ CString marshall() const; - //! Copy clipboard - /*! - Transfers all the data in one clipboard to another. The - clipboards can be of any concrete clipboard type (and - they don't have to be the same type). This also sets - the destination clipboard's timestamp to source clipboard's - timestamp. Returns true iff the copy succeeded. - */ - static bool copy(IClipboard* dst, const IClipboard* src); - - //! Copy clipboard - /*! - Transfers all the data in one clipboard to another. The - clipboards can be of any concrete clipboard type (and they - don't have to be the same type). This also sets the - timestamp to \c time. Returns true iff the copy succeeded. - */ - static bool copy(IClipboard* dst, const IClipboard* src, Time); - //@} // IClipboard overrides @@ -77,10 +58,6 @@ public: virtual bool has(EFormat) const; virtual CString get(EFormat) const; -private: - UInt32 readUInt32(const char*) const; - void writeUInt32(CString*, UInt32) const; - private: mutable bool m_open; mutable Time m_time; diff --git a/lib/synergy/CPacketStreamFilter.cpp b/lib/synergy/CPacketStreamFilter.cpp index 0ce5e7c3..d59d2fa1 100644 --- a/lib/synergy/CPacketStreamFilter.cpp +++ b/lib/synergy/CPacketStreamFilter.cpp @@ -34,7 +34,9 @@ CPacketStreamFilter::CPacketStreamFilter(IStream* stream, bool adoptStream) : CPacketStreamFilter::~CPacketStreamFilter() { - delete getStream()->getEventFilter(); + IEventJob* job = getStream()->getEventFilter(); + getStream()->setEventFilter(NULL); + delete job; } void diff --git a/lib/synergy/IClient.h b/lib/synergy/IClient.h index 3e7c8b08..b4ef2eca 100644 --- a/lib/synergy/IClient.h +++ b/lib/synergy/IClient.h @@ -58,7 +58,7 @@ public: already known to be up to date then this may do nothing. \c data has marshalled clipboard data. */ - virtual void setClipboard(ClipboardID, const CString& data) = 0; + virtual void setClipboard(ClipboardID, const IClipboard*) = 0; //! Grab clipboard /*! diff --git a/lib/synergy/IClipboard.cpp b/lib/synergy/IClipboard.cpp new file mode 100644 index 00000000..7a055612 --- /dev/null +++ b/lib/synergy/IClipboard.cpp @@ -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(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 add(format, CString(index, size)); + } + index += size; + } + + // done + clipboard->close(); +} + +CString +IClipboard::marshall(const IClipboard* clipboard) +{ + assert(clipboard != NULL); + + CString data; + + std::vector 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(format))) { + ++numFormats; + formatData[format] = + clipboard->get(static_cast(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(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(buf); + return (static_cast(ubuf[0]) << 24) | + (static_cast(ubuf[1]) << 16) | + (static_cast(ubuf[2]) << 8) | + static_cast(ubuf[3]); +} + +void +IClipboard::writeUInt32(CString* buf, UInt32 v) +{ + *buf += static_cast((v >> 24) & 0xff); + *buf += static_cast((v >> 16) & 0xff); + *buf += static_cast((v >> 8) & 0xff); + *buf += static_cast( v & 0xff); +} diff --git a/lib/synergy/IClipboard.h b/lib/synergy/IClipboard.h index 25defae6..c0bfbeb4 100644 --- a/lib/synergy/IClipboard.h +++ b/lib/synergy/IClipboard.h @@ -111,7 +111,45 @@ public: */ virtual CString get(EFormat) const = 0; + //! Marshall clipboard data + /*! + Merge \p clipboard's data into a single buffer that can be later + unmarshalled to restore the clipboard and return the buffer. + */ + static CString marshall(const IClipboard* clipboard); + + //! Unmarshall clipboard data + /*! + Extract marshalled clipboard data and store it in \p clipboard. + Sets the clipboard time to \c time. + */ + static void unmarshall(IClipboard* clipboard, + const CString& data, Time time); + + //! Copy clipboard + /*! + Transfers all the data in one clipboard to another. The + clipboards can be of any concrete clipboard type (and + they don't have to be the same type). This also sets + the destination clipboard's timestamp to source clipboard's + timestamp. Returns true iff the copy succeeded. + */ + static bool copy(IClipboard* dst, const IClipboard* src); + + //! Copy clipboard + /*! + Transfers all the data in one clipboard to another. The + clipboards can be of any concrete clipboard type (and they + don't have to be the same type). This also sets the + timestamp to \c time. Returns true iff the copy succeeded. + */ + static bool copy(IClipboard* dst, const IClipboard* src, Time); + //@} + +private: + static UInt32 readUInt32(const char*); + static void writeUInt32(CString*, UInt32); }; #endif diff --git a/lib/synergy/IPlatformScreen.cpp b/lib/synergy/IPlatformScreen.cpp index 78d1e436..a9a37418 100644 --- a/lib/synergy/IPlatformScreen.cpp +++ b/lib/synergy/IPlatformScreen.cpp @@ -104,14 +104,16 @@ IPlatformScreen::getScreensaverDeactivatedEvent() // IPlatformScreen::CKeyInfo // -IPlatformScreen::CKeyInfo::CKeyInfo(KeyID id, - KeyModifierMask mask, KeyButton button, SInt32 count) : - m_key(id), - m_mask(mask), - m_button(button), - m_count(count) +IPlatformScreen::CKeyInfo* +IPlatformScreen::CKeyInfo::alloc(KeyID id, + KeyModifierMask mask, KeyButton button, SInt32 count) { - // do nothing + CKeyInfo* info = (CKeyInfo*)malloc(sizeof(CKeyInfo)); + info->m_key = id; + info->m_mask = mask; + info->m_button = button; + info->m_count = count; + return info; } @@ -119,10 +121,12 @@ IPlatformScreen::CKeyInfo::CKeyInfo(KeyID id, // IPlatformScreen::CButtonInfo // -IPlatformScreen::CButtonInfo::CButtonInfo(ButtonID id) : - m_button(id) +IPlatformScreen::CButtonInfo* +IPlatformScreen::CButtonInfo::alloc(ButtonID id) { - // do nothing + CButtonInfo* info = (CButtonInfo*)malloc(sizeof(CButtonInfo)); + info->m_button = id; + return info; } @@ -130,11 +134,13 @@ IPlatformScreen::CButtonInfo::CButtonInfo(ButtonID id) : // IPlatformScreen::CMotionInfo // -IPlatformScreen::CMotionInfo::CMotionInfo(SInt32 x, SInt32 y) : - m_x(x), - m_y(y) +IPlatformScreen::CMotionInfo* +IPlatformScreen::CMotionInfo::alloc(SInt32 x, SInt32 y) { - // do nothing + CMotionInfo* info = (CMotionInfo*)malloc(sizeof(CMotionInfo)); + info->m_x = x; + info->m_y = y; + return info; } @@ -142,8 +148,10 @@ IPlatformScreen::CMotionInfo::CMotionInfo(SInt32 x, SInt32 y) : // IPlatformScreen::CWheelInfo // -IPlatformScreen::CWheelInfo::CWheelInfo(SInt32 wheel) : - m_wheel(wheel) +IPlatformScreen::CWheelInfo* +IPlatformScreen::CWheelInfo::alloc(SInt32 wheel) { - // do nothing + CWheelInfo* info = (CWheelInfo*)malloc(sizeof(CWheelInfo)); + info->m_wheel = wheel; + return info; } diff --git a/lib/synergy/IPlatformScreen.h b/lib/synergy/IPlatformScreen.h index 456d58d9..bae81e86 100644 --- a/lib/synergy/IPlatformScreen.h +++ b/lib/synergy/IPlatformScreen.h @@ -40,7 +40,7 @@ public: //! Key event data class CKeyInfo { public: - CKeyInfo(KeyID, KeyModifierMask, KeyButton, SInt32 count); + static CKeyInfo* alloc(KeyID, KeyModifierMask, KeyButton, SInt32 count); public: KeyID m_key; @@ -51,7 +51,7 @@ public: //! Button event data class CButtonInfo { public: - CButtonInfo(ButtonID); + static CButtonInfo* alloc(ButtonID); public: ButtonID m_button; @@ -59,7 +59,7 @@ public: //! Motion event data class CMotionInfo { public: - CMotionInfo(SInt32 x, SInt32 y); + static CMotionInfo* alloc(SInt32 x, SInt32 y); public: SInt32 m_x; @@ -68,7 +68,7 @@ public: //! Wheel motion event data class CWheelInfo { public: - CWheelInfo(SInt32); + static CWheelInfo* alloc(SInt32); public: SInt32 m_wheel; diff --git a/lib/synergy/Makefile.am b/lib/synergy/Makefile.am index ed4763fa..e34363d7 100644 --- a/lib/synergy/Makefile.am +++ b/lib/synergy/Makefile.am @@ -29,6 +29,7 @@ libsynergy_a_SOURCES = \ CPacketStreamFilter.cpp \ CProtocolUtil.cpp \ CScreen.cpp \ + IClipboard.cpp \ IPlatformScreen.cpp \ IScreen.cpp \ XScreen.cpp \ diff --git a/lib/synergy/ProtocolTypes.h b/lib/synergy/ProtocolTypes.h index 539ddaf6..c1162c2d 100644 --- a/lib/synergy/ProtocolTypes.h +++ b/lib/synergy/ProtocolTypes.h @@ -267,19 +267,11 @@ public: */ SInt32 m_w, m_h; - //! Jump zone size - /*! - This is the size of the jump zone. The cursor jumps to the adjacent - screen when it comes within this many pixels of the edge of the screen. - */ - SInt32 m_zoneSize; + //! Obsolete (jump zone size) + SInt32 obsolete1; - //! Mouse position - /*! - The position of the cursor. This is not kept up-to-date so it's - only meaningful when receiving an update. - */ - SInt32 m_mx, m_my; + //! Obsolete (mouse position) + SInt32 obsolete2, obsolete3; }; #endif