From 710e1bdd47e75511d59d89c1f0ac5e7dc13b4b1d Mon Sep 17 00:00:00 2001 From: crs Date: Wed, 10 Jul 2002 20:18:32 +0000 Subject: [PATCH] refactored client code. it now uses IClient and IServer and has a CServerProxy, making it's design similar to the server code. --- client/CClient.cpp | 995 ++++++++++--------------- client/CClient.h | 104 +-- client/CServerProxy.cpp | 576 ++++++++++++++ client/CServerProxy.h | 89 +++ {synergy => client}/ISecondaryScreen.h | 0 client/Makefile.am | 5 +- client/client.cpp | 8 +- server/CClientProxy.h | 3 +- server/CClientProxy1_0.cpp | 10 +- server/CClientProxy1_0.h | 3 +- server/CPrimaryClient.cpp | 10 +- server/CPrimaryClient.h | 3 +- server/CServer.cpp | 62 +- synergy/IClient.h | 7 +- synergy/Makefile.am | 1 - 15 files changed, 1207 insertions(+), 669 deletions(-) create mode 100644 client/CServerProxy.cpp create mode 100644 client/CServerProxy.h rename {synergy => client}/ISecondaryScreen.h (100%) diff --git a/client/CClient.cpp b/client/CClient.cpp index 83d2164f..09b31c0f 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -1,9 +1,11 @@ #include "CClient.h" +#include "CServerProxy.h" #include "CClipboard.h" #include "CInputPacketStream.h" #include "COutputPacketStream.h" #include "CProtocolUtil.h" #include "ISecondaryScreen.h" +#include "IServer.h" #include "ProtocolTypes.h" #include "XScreen.h" #include "XSynergy.h" @@ -15,15 +17,6 @@ #include "CLog.h" #include "CStopwatch.h" #include "TMethodJob.h" -#include - -// hack to work around operator=() bug in STL in g++ prior to v3 -#if defined(__GNUC__) && (__GNUC__ < 3) -#define assign(_dst, _src, _type) _dst.reset(_src) -#else -#define assign(_dst, _src, _type) _dst = std::auto_ptr<_type >(_src) -#endif - // // CClient @@ -31,13 +24,12 @@ CClient::CClient(const CString& clientName) : m_name(clientName), - m_input(NULL), - m_output(NULL), m_screen(NULL), + m_server(NULL), m_camp(false), m_active(false), m_seqNum(0), - m_ignoreMove(false) + m_rejected(true) { // do nothing } @@ -50,9 +42,69 @@ CClient::~CClient() void CClient::camp(bool on) { + CLock lock(&m_mutex); m_camp = on; } +void +CClient::setAddress(const CNetworkAddress& serverAddress) +{ + CLock lock(&m_mutex); + m_serverAddress = serverAddress; +} + +void +CClient::quit() +{ + m_screen->stop(); +} + +bool +CClient::wasRejected() const +{ + return m_rejected; +} + +void +CClient::onClipboardChanged(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; + } + + // grab ownership + m_server->onGrabClipboard(m_name, id, m_seqNum); + + // 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); + } +} + +void +CClient::onResolutionChanged() +{ + log((CLOG_DEBUG "resolution changed")); + + CLock lock(&m_mutex); + if (m_server != NULL) { + CClientInfo info; + m_screen->getShape(info.m_x, info.m_y, info.m_w, info.m_h); + m_screen->getMousePos(info.m_mx, info.m_my); + info.m_zoneSize = m_screen->getJumpZoneSize(); + m_server->onInfoChanged("", info); + } +} + bool CClient::open() { @@ -70,368 +122,205 @@ CClient::open() } } -bool -CClient::run(const CNetworkAddress& serverAddress) +void +CClient::run() { - // check preconditions { CLock lock(&m_mutex); + + // check preconditions assert(m_screen != NULL); + assert(m_server == NULL); + + // connection starts as unsuccessful + m_rejected = true; } CThread* thread = NULL; try { - log((CLOG_NOTE "starting client")); + log((CLOG_NOTE "starting client \"%s\"", m_name.c_str())); // start server interactions - m_serverAddress = &serverAddress; - thread = new CThread(new TMethodJob(this, &CClient::runSession)); + thread = new CThread(new TMethodJob( + this, &CClient::runSession)); // handle events log((CLOG_DEBUG "starting event handling")); m_screen->run(); + log((CLOG_DEBUG "stopped event handling")); // clean up - log((CLOG_NOTE "stopping client")); - thread->cancel(); - void* result = thread->getResult(); - delete thread; - closeSecondaryScreen(); - return (result != NULL); + deleteSession(thread); + log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); } catch (XBase& e) { log((CLOG_ERR "client error: %s", e.what())); // clean up - log((CLOG_NOTE "stopping client")); - if (thread != NULL) { - thread->cancel(); - thread->wait(); - delete thread; - } - closeSecondaryScreen(); - return true; + log((CLOG_DEBUG "stopped event handling")); + deleteSession(thread); + log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); + CLock lock(&m_mutex); + m_rejected = false; } catch (XThread&) { // clean up - log((CLOG_NOTE "stopping client")); - if (thread != NULL) { - thread->cancel(); - thread->wait(); - delete thread; - } - closeSecondaryScreen(); + log((CLOG_DEBUG "stopped event handling")); + deleteSession(thread); + log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); throw; } catch (...) { log((CLOG_DEBUG "unknown client error")); // clean up - log((CLOG_NOTE "stopping client")); - if (thread != NULL) { - thread->cancel(); - thread->wait(); - delete thread; - } - closeSecondaryScreen(); + log((CLOG_DEBUG "stopped event handling")); + deleteSession(thread); + log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); throw; } } void -CClient::quit() +CClient::close() { - m_screen->stop(); + closeSecondaryScreen(); } void -CClient::onClipboardChanged(ClipboardID id) +CClient::enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, bool) { - log((CLOG_DEBUG "sending clipboard %d changed", id)); - CLock lock(&m_mutex); - if (m_output != NULL) { - // m_output can be NULL if the screen calls this method - // before we've gotten around to connecting to the server. - CProtocolUtil::writef(m_output, kMsgCClipboard, id, m_seqNum); - } - - // 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) { - // get clipboard - CClipboard clipboard; - m_screen->getClipboard(id, &clipboard); - - // save new time - m_timeClipboard[id] = clipboard.getTime(); - - // marshall the data - CString data = clipboard.marshall(); - - // send data - log((CLOG_DEBUG "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size())); - if (m_output != NULL) { -// FIXME -- will we send the clipboard when we connect? - CProtocolUtil::writef(m_output, kMsgDClipboard, id, m_seqNum, &data); - } - } -} - -void -CClient::onResolutionChanged() -{ - log((CLOG_DEBUG "resolution changed")); - - CLock lock(&m_mutex); - - // start ignoring mouse movement until we get an acknowledgment - m_ignoreMove = true; - - // send notification of resolution change - onQueryInfoNoLock(); -} - -#include "CTCPSocket.h" // FIXME -void -CClient::runSession(void*) -{ - log((CLOG_DEBUG "starting client \"%s\"", m_name.c_str())); - - std::auto_ptr socket; - std::auto_ptr input; - std::auto_ptr output; - try { - for (;;) { - try { - // allow connect this much time to succeed - // FIXME -- timeout in member - CTimerThread timer(m_camp ? -1.0 : 30.0); - - // create socket and attempt to connect to server - log((CLOG_DEBUG1 "connecting to server")); - assign(socket, new CTCPSocket(), IDataSocket); // FIXME -- use factory - socket->connect(*m_serverAddress); - log((CLOG_INFO "connected to server")); - break; - } - catch (XSocketConnect&) { - // failed to connect. if not camping then rethrow. - if (!m_camp) { - throw; - } - - // we're camping. wait a bit before retrying - CThread::sleep(5.0); - } - } - - // get the input and output streams - IInputStream* srcInput = socket->getInputStream(); - IOutputStream* srcOutput = socket->getOutputStream(); - - // attach the encryption layer - bool own = false; -/* FIXME -- implement ISecurityFactory - if (m_securityFactory != NULL) { - input.reset(m_securityFactory->createInputFilter(srcInput, own)); - output.reset(m_securityFactory->createOutputFilter(srcOutput, own)); - srcInput = input.get(); - srcOutput = output.get(); - own = true; - } -*/ - - // give handshake some time - CTimerThread timer(30.0); - - // attach the packetizing filters - assign(input, new CInputPacketStream(srcInput, own), IInputStream); - assign(output, new COutputPacketStream(srcOutput, own), IOutputStream); - - // wait for hello from server - log((CLOG_DEBUG1 "wait for hello")); - SInt16 major, minor; - CProtocolUtil::readf(input.get(), "Synergy%2i%2i", &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.get(), "Synergy%2i%2i%s", - kProtocolMajorVersion, - kProtocolMinorVersion, &m_name); - - // record streams in a more useful place + { CLock lock(&m_mutex); - m_input = input.get(); - m_output = output.get(); - } - catch (XIncompatibleClient& e) { - log((CLOG_ERR "server has incompatible version %d.%d", e.getMajor(), e.getMinor())); - m_screen->stop(); - CThread::exit(NULL); - } - catch (XThread&) { - log((CLOG_ERR "connection timed out")); - m_screen->stop(); - throw; - } - catch (XBase& e) { - log((CLOG_ERR "connection failed: %s", e.what())); - m_screen->stop(); - CThread::exit(NULL); - } - catch (...) { - log((CLOG_ERR "connection failed: ")); - m_screen->stop(); - CThread::exit(NULL); + m_active = true; + m_seqNum = seqNum; } - bool fail = false; - try { - // no compressed mouse motion yet - m_compressMouse = false; + m_screen->enter(xAbs, yAbs, mask); +} - // handle messages from server - CStopwatch heartbeat; - for (;;) { - // if no input is pending then flush compressed mouse motion - if (input->getSize() == 0) { - flushCompressedMouse(); - } +bool +CClient::leave() +{ + m_screen->leave(); - // wait for a message - log((CLOG_DEBUG2 "waiting for message")); - UInt8 code[4]; - UInt32 n = input->read(code, 4, kHeartRate); + CLock lock(&m_mutex); + m_active = false; - // check if server hungup - if (n == 0) { - log((CLOG_NOTE "server disconnected")); - break; - } - - // check for time out - if (n == (UInt32)-1 || heartbeat.getTime() > kHeartRate) { - // send heartbeat - CProtocolUtil::writef(m_output, kMsgCNoop); - heartbeat.reset(); - if (n == (UInt32)-1) { - // no message to process - continue; - } - } - - // verify we got an entire code - if (n != 4) { - // client sent an incomplete message - log((CLOG_ERR "incomplete message from server")); - break; - } - - // 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) { - onMouseMove(); - } - else if (memcmp(code, kMsgDMouseWheel, 4) == 0) { - onMouseWheel(); - } - else if (memcmp(code, kMsgDKeyDown, 4) == 0) { - onKeyDown(); - } - else if (memcmp(code, kMsgDKeyUp, 4) == 0) { - onKeyUp(); - } - else if (memcmp(code, kMsgDMouseDown, 4) == 0) { - onMouseDown(); - } - else if (memcmp(code, kMsgDMouseUp, 4) == 0) { - onMouseUp(); - } - else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) { - onKeyRepeat(); - } - else if (memcmp(code, kMsgCNoop, 4) == 0) { - // accept and discard no-op - continue; - } - else if (memcmp(code, kMsgCEnter, 4) == 0) { - onEnter(); - } - else if (memcmp(code, kMsgCLeave, 4) == 0) { - onLeave(); - } - else if (memcmp(code, kMsgCClipboard, 4) == 0) { - onGrabClipboard(); - } - else if (memcmp(code, kMsgCScreenSaver, 4) == 0) { - onScreenSaver(); - } - else if (memcmp(code, kMsgQInfo, 4) == 0) { - onQueryInfo(); - } - else if (memcmp(code, kMsgCInfoAck, 4) == 0) { - onInfoAcknowledgment(); - } - else if (memcmp(code, kMsgDClipboard, 4) == 0) { - onSetClipboard(); - } - 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) { - onErrorIncompatible(); - fail = true; - break; - } - else if (memcmp(code, kMsgEBusy, 4) == 0) { - onErrorBusy(); - fail = true; - break; - } - else if (memcmp(code, kMsgEUnknown, 4) == 0) { - onErrorUnknown(); - fail = true; - break; - } - else if (memcmp(code, kMsgEBad, 4) == 0) { - onErrorBad(); - fail = true; - break; - } - else { - // unknown message - log((CLOG_ERR "unknown message from server")); - break; - } + // send clipboards that we own and that have changed + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + if (m_ownClipboard[id]) { + sendClipboard(id); } } - catch (XBase& e) { - log((CLOG_ERR "error: %s", e.what())); - m_screen->stop(); - CThread::exit(reinterpret_cast(1)); +} + +void +CClient::setClipboard(ClipboardID id, const CString& data) +{ + // unmarshall + CClipboard clipboard; + clipboard.unmarshall(data, 0); + + // set screen's 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; } - // done with socket - log((CLOG_DEBUG "disconnecting from server")); - socket->close(); + m_screen->grabClipboard(id); +} - // exit event loop - m_screen->stop(); +void +CClient::setClipboardDirty(ClipboardID, bool) +{ + assert(0 && "shouldn't be called"); +} - CThread::exit(fail ? NULL : reinterpret_cast(1)); +void +CClient::keyDown(KeyID id, KeyModifierMask mask) +{ + m_screen->keyDown(id, mask); +} + +void +CClient::keyRepeat(KeyID id, KeyModifierMask mask, SInt32 count) +{ + m_screen->keyRepeat(id, mask, count); +} + +void +CClient::keyUp(KeyID id, KeyModifierMask mask) +{ + m_screen->keyUp(id, mask); +} + +void +CClient::mouseDown(ButtonID id) +{ + m_screen->mouseDown(id); +} + +void +CClient::mouseUp(ButtonID id) +{ + m_screen->mouseUp(id); +} + +void +CClient::mouseMove(SInt32 x, SInt32 y) +{ + m_screen->mouseMove(x, y); +} + +void +CClient::mouseWheel(SInt32 delta) +{ + m_screen->mouseWheel(delta); +} + +void +CClient::screenSaver(bool activate) +{ + m_screen->screenSaver(activate); +} + +CString +CClient::getName() const +{ + return m_name; +} + +void +CClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + m_screen->getShape(x, y, w, h); +} + +void +CClient::getCenter(SInt32&, SInt32&) const +{ + assert(0 && "shouldn't be called"); +} + +void +CClient::getMousePos(SInt32& x, SInt32& y) const +{ + m_screen->getMousePos(x, y); +} + +SInt32 +CClient::getJumpZoneSize() const +{ + return m_screen->getJumpZoneSize(); } // FIXME -- use factory to create screen @@ -489,310 +378,228 @@ CClient::closeSecondaryScreen() } void -CClient::flushCompressedMouse() +CClient::sendClipboard(ClipboardID id) { - if (m_compressMouse) { - m_compressMouse = false; - m_screen->mouseMove(m_xMouse, m_yMouse); + // note -- m_mutex must be locked on entry + assert(m_screen != NULL); + assert(m_server != NULL); + + // get clipboard data. set the clipboard time to the last + // clipboard time before getting the data from the screen + // as the screen may detect an unchanged clipboard and + // avoid copying the data. + CClipboard clipboard; + if (clipboard.open(m_timeClipboard[id])) { + clipboard.close(); + } + m_screen->getClipboard(id, &clipboard); + + // check time + if (m_timeClipboard[id] == 0 || + clipboard.getTime() != m_timeClipboard[id]) { + // save new time + m_timeClipboard[id] = clipboard.getTime(); + + // marshall the data + CString data = clipboard.marshall(); + + // save and send data if different + if (data != m_dataClipboard[id]) { + m_dataClipboard[id] = data; + m_server->onClipboardChanged(id, m_seqNum, data); + } } } void -CClient::onEnter() +CClient::runSession(void*) { - SInt16 x, y; - UInt16 mask; - { - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y, &m_seqNum, &mask); - m_active = true; + try { + log((CLOG_DEBUG "starting server proxy")); + runServer(); + m_screen->stop(); + log((CLOG_DEBUG "stopping server proxy")); + } + catch (...) { + m_screen->stop(); + log((CLOG_DEBUG "stopping server proxy")); + throw; } - log((CLOG_DEBUG1 "recv enter, %d,%d %d %04x", x, y, m_seqNum, mask)); - - // discard old compressed mouse motion, if any - m_compressMouse = false; - - // tell screen we're entering - m_screen->enter(x, y, static_cast(mask)); } void -CClient::onLeave() +CClient::deleteSession(CThread* thread) { - log((CLOG_DEBUG1 "recv leave")); + if (thread != NULL) { + thread->cancel(); + thread->wait(); + delete thread; + } +} - // send last mouse motion - flushCompressedMouse(); +#include "CTCPSocket.h" // FIXME +void +CClient::runServer() +{ + IDataSocket* socket = NULL; + CServerProxy* proxy = NULL; + try { + for (;;) { + try { + // allow connect this much time to succeed + // FIXME -- timeout in member + CTimerThread timer(m_camp ? -1.0 : 30.0); - // tell screen we're leaving - m_screen->leave(); - - // no longer the active screen - CLock lock(&m_mutex); - m_active = false; - - // send clipboards that we own and that have changed - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - if (m_ownClipboard[id]) { - // get clipboard data. set the clipboard time to the last - // clipboard time before getting the data from the screen - // as the screen may detect an unchanged clipboard and - // avoid copying the data. - CClipboard clipboard; - if (clipboard.open(m_timeClipboard[id])) - clipboard.close(); - m_screen->getClipboard(id, &clipboard); - - // check time - if (m_timeClipboard[id] == 0 || - clipboard.getTime() != m_timeClipboard[id]) { - // save new time - m_timeClipboard[id] = clipboard.getTime(); - - // marshall the data - CString data = clipboard.marshall(); - - // save and send data if different - if (data != m_dataClipboard[id]) { - log((CLOG_DEBUG "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size())); - m_dataClipboard[id] = data; - CProtocolUtil::writef(m_output, - kMsgDClipboard, id, m_seqNum, &data); + // create socket and attempt to connect to server + log((CLOG_DEBUG1 "connecting to server")); + socket = new CTCPSocket; // FIXME -- use factory + socket->connect(m_serverAddress); + log((CLOG_INFO "connected to server")); + break; + } + catch (XSocketConnect&) { + // failed to connect. if not camping then rethrow. + if (!m_camp) { + throw; } + + // we're camping. wait a bit before retrying + CThread::sleep(5.0); } } - } -} -void -CClient::onGrabClipboard() -{ - ClipboardID id; - UInt32 seqNum; - { + // create proxy + log((CLOG_DEBUG1 "negotiating with server")); + proxy = handshakeServer(socket); CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgCClipboard + 4, &id, &seqNum); - log((CLOG_DEBUG "recv grab clipboard %d", id)); - - // validate - if (id >= kClipboardEnd) { - return; - } - - // we no longer own the clipboard - m_ownClipboard[id] = false; + m_server = proxy; } - m_screen->grabClipboard(id); -} - -void -CClient::onScreenSaver() -{ - SInt8 on; - { - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgCScreenSaver + 4, &on); + catch (XThread&) { + log((CLOG_ERR "connection timed out")); + delete socket; + throw; } - log((CLOG_DEBUG1 "recv screen saver on=%d", on)); - m_screen->screenSaver(on != 0); -} - -void -CClient::onQueryInfo() -{ - CLock lock(&m_mutex); - onQueryInfoNoLock(); -} - -void -CClient::onQueryInfoNoLock() -{ - SInt32 mx, my, x, y, w, h; - m_screen->getMousePos(mx, my); - m_screen->getShape(x, y, w, h); - SInt32 zoneSize = m_screen->getJumpZoneSize(); - - log((CLOG_DEBUG1 "sending info shape=%d,%d %dx%d zone=%d pos=%d,%d", x, y, w, h, zoneSize, mx, my)); - CProtocolUtil::writef(m_output, kMsgDInfo, x, y, w, h, zoneSize, mx, my); -} - -void -CClient::onInfoAcknowledgment() -{ - log((CLOG_DEBUG1 "recv info acknowledgment")); - CLock lock(&m_mutex); - m_ignoreMove = false; -} - -void -CClient::onSetClipboard() -{ - ClipboardID id; - CString data; - { - // parse message - UInt32 seqNum; - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgDClipboard + 4, &id, &seqNum, &data); + catch (XBase& e) { + log((CLOG_ERR "connection failed: %s", e.what())); + log((CLOG_DEBUG "disconnecting from server")); + delete socket; + return; } - log((CLOG_DEBUG "recv clipboard %d size=%d", id, data.size())); - - // validate - if (id >= kClipboardEnd) { + catch (...) { + log((CLOG_ERR "connection failed: ")); + log((CLOG_DEBUG "disconnecting from server")); + delete socket; return; } - // unmarshall - CClipboard clipboard; - clipboard.unmarshall(data, 0); - - // set screen's clipboard - m_screen->setClipboard(id, &clipboard); -} - -void -CClient::onKeyDown() -{ - // get mouse up to date - flushCompressedMouse(); - - UInt16 id, mask; - { - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgDKeyDown + 4, &id, &mask); - } - log((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x", id, mask)); - m_screen->keyDown(static_cast(id), - static_cast(mask)); -} - -void -CClient::onKeyRepeat() -{ - // get mouse up to date - flushCompressedMouse(); - - UInt16 id, mask, count; - { - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgDKeyRepeat + 4, &id, &mask, &count); - } - log((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d", id, mask, count)); - m_screen->keyRepeat(static_cast(id), - static_cast(mask), - count); -} - -void -CClient::onKeyUp() -{ - // get mouse up to date - flushCompressedMouse(); - - UInt16 id, mask; - { - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgDKeyUp + 4, &id, &mask); - } - log((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x", id, mask)); - m_screen->keyUp(static_cast(id), - static_cast(mask)); -} - -void -CClient::onMouseDown() -{ - // get mouse up to date - flushCompressedMouse(); - - SInt8 id; - { - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgDMouseDown + 4, &id); - } - log((CLOG_DEBUG1 "recv mouse down id=%d", id)); - m_screen->mouseDown(static_cast(id)); -} - -void -CClient::onMouseUp() -{ - // get mouse up to date - flushCompressedMouse(); - - SInt8 id; - { - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgDMouseUp + 4, &id); - } - log((CLOG_DEBUG1 "recv mouse up id=%d", id)); - m_screen->mouseUp(static_cast(id)); -} - -void -CClient::onMouseMove() -{ - bool ignore; - SInt16 x, y; - { - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgDMouseMove + 4, &x, &y); - ignore = m_ignoreMove; - - // compress mouse motion events if more input follows - if (!ignore && !m_compressMouse && m_input->getSize() > 0) { - m_compressMouse = true; + try { + // process messages + bool rejected = true; + if (proxy != NULL) { + log((CLOG_DEBUG1 "communicating with server")); + rejected = !proxy->run(); } - if (m_compressMouse) { - ignore = true; - m_xMouse = x; - m_yMouse = y; - } - } - log((CLOG_DEBUG2 "recv mouse move %d,%d", x, y)); - if (!ignore) { - m_screen->mouseMove(x, y); - } -} -void -CClient::onMouseWheel() -{ - // get mouse up to date - flushCompressedMouse(); - - SInt16 delta; - { + // clean up CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgDMouseWheel + 4, &delta); + m_rejected = rejected; + m_server = NULL; + delete proxy; + log((CLOG_DEBUG "disconnecting from server")); + socket->close(); + delete socket; + } + catch (...) { + CLock lock(&m_mutex); + m_rejected = false; + m_server = NULL; + delete proxy; + log((CLOG_DEBUG "disconnecting from server")); + socket->close(); + delete socket; + throw; } - log((CLOG_DEBUG2 "recv mouse wheel %+d", delta)); - m_screen->mouseWheel(delta); } -void -CClient::onErrorIncompatible() +CServerProxy* +CClient::handshakeServer(IDataSocket* socket) { - SInt32 major, minor; - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgEIncompatible + 4, &major, &minor); - log((CLOG_ERR "server has incompatible version %d.%d", major, minor)); -} + // get the input and output streams + IInputStream* input = socket->getInputStream(); + IOutputStream* output = socket->getOutputStream(); + bool own = false; -void -CClient::onErrorBusy() -{ - log((CLOG_ERR "server already has a connected client with name \"%s\"", m_name.c_str())); -} + // attach the encryption layer +/* FIXME -- implement ISecurityFactory + if (m_securityFactory != NULL) { + input = m_securityFactory->createInputFilter(input, own); + output = m_securityFactory->createOutputFilter(output, own); + own = true; + } +*/ -void -CClient::onErrorUnknown() -{ - log((CLOG_ERR "server refused client with name \"%s\"", m_name.c_str())); -} + // attach the packetizing filters + input = new CInputPacketStream(input, own); + output = new COutputPacketStream(output, own); + own = true; -void -CClient::onErrorBad() -{ - log((CLOG_ERR "server disconnected due to a protocol error")); + CServerProxy* proxy = NULL; + try { + // give handshake some time + CTimerThread timer(30.0); + + // wait for hello from server + log((CLOG_DEBUG1 "wait for hello")); + SInt16 major, minor; + CProtocolUtil::readf(input, "Synergy%2i%2i", &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, "Synergy%2i%2i%s", + 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())); + } + catch (XBase& e) { + log((CLOG_WARN "error communicating with server: %s", 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/client/CClient.h b/client/CClient.h index 66927660..73ef8714 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -1,17 +1,18 @@ #ifndef CCLIENT_H #define CCLIENT_H -#include "ClipboardTypes.h" +#include "IClient.h" #include "IClipboard.h" +#include "CNetworkAddress.h" #include "CMutex.h" -#include "CString.h" -class CNetworkAddress; -class IInputStream; -class IOutputStream; +class CServerProxy; +class CThread; +class IDataSocket; class ISecondaryScreen; +class IServer; -class CClient { +class CClient : public IClient { public: CClient(const CString& clientName); ~CClient(); @@ -24,74 +25,87 @@ public: // not call this while in run(). void camp(bool on); - // open the client's screen - bool open(); - - // start the client. does not return until quit() is called. - // returns true if the client ever connected to the server - // successfully. may also throw exceptions after successfully - // connecting. a successful open() must preceed this call. - bool run(const CNetworkAddress& serverAddress); + // set the server's address that the client should connect to + void setAddress(const CNetworkAddress& serverAddress); // tell client to exit run() gracefully. this must only be called // after a successful open(). void quit(); // handle events on client's screen +// FIXME -- this should mimic methods on IServer +// FIXME -- maybe create a IScreenReceiver with these methods and +// have CPrimaryClient and CClient inherit from them. IServer +// still needs similar methods with extra parameters, though. so +// CServerProxy +// CPrimaryClient +// CClient +// need IScreenReceiver. these classes effective receive notifications +// from screens. note that there's another class of notifications that +// only the server needs (key, mouyse, screensaver). so maybe we have +// IPrimaryScreenReceiver and ISecondaryScreenReceiver (the latter is +// derived with no extra methods from IScreenReceiver). void onClipboardChanged(ClipboardID); void onResolutionChanged(); // accessors + // returns true if the server rejected us + bool wasRejected() const; + + // IClient overrides + virtual bool open(); + virtual void run(); + virtual void close(); + virtual void enter(SInt32 xAbs, SInt32 yAbs, + UInt32 seqNum, KeyModifierMask mask, + bool screenSaver); + virtual bool leave(); + virtual void setClipboard(ClipboardID, const CString&); + virtual void grabClipboard(ClipboardID); + virtual void setClipboardDirty(ClipboardID, bool dirty); + virtual void keyDown(KeyID, KeyModifierMask); + virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); + virtual void keyUp(KeyID, KeyModifierMask); + virtual void mouseDown(ButtonID); + virtual void mouseUp(ButtonID); + virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); + virtual void mouseWheel(SInt32 delta); + virtual void screenSaver(bool activate); + virtual CString getName() const; + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + virtual void getCenter(SInt32& x, SInt32& y) const; + virtual void getMousePos(SInt32& x, SInt32& y) const; + virtual SInt32 getJumpZoneSize() const; private: - void runSession(void*); - // open/close the secondary screen void openSecondaryScreen(); void closeSecondaryScreen(); - // if compressing mouse motion then send the last motion now - void flushCompressedMouse(); + // send the clipboard to the server + void sendClipboard(ClipboardID); - // message handlers - void onEnter(); - void onLeave(); - void onGrabClipboard(); - void onScreenSaver(); - void onQueryInfo(); - void onQueryInfoNoLock(); - void onInfoAcknowledgment(); - void onSetClipboard(); - void onKeyDown(); - void onKeyRepeat(); - void onKeyUp(); - void onMouseDown(); - void onMouseUp(); - void onMouseMove(); - void onMouseWheel(); - void onErrorIncompatible(); - void onErrorBusy(); - void onErrorUnknown(); - void onErrorBad(); + // handle server messaging + void runSession(void*); + void deleteSession(CThread*); + void runServer(); + CServerProxy* handshakeServer(IDataSocket*); private: CMutex m_mutex; CString m_name; - IInputStream* m_input; - IOutputStream* m_output; ISecondaryScreen* m_screen; - const CNetworkAddress* m_serverAddress; + IServer* m_server; + CNetworkAddress m_serverAddress; bool m_camp; bool m_active; UInt32 m_seqNum; - bool m_ignoreMove; + bool m_rejected; bool m_ownClipboard[kClipboardEnd]; IClipboard::Time m_timeClipboard[kClipboardEnd]; CString m_dataClipboard[kClipboardEnd]; - - bool m_compressMouse; - SInt32 m_xMouse, m_yMouse; }; #endif diff --git a/client/CServerProxy.cpp b/client/CServerProxy.cpp new file mode 100644 index 00000000..b8e5866d --- /dev/null +++ b/client/CServerProxy.cpp @@ -0,0 +1,576 @@ +#include "CServerProxy.h" +#include "CProtocolUtil.h" +#include "IClient.h" +#include "ProtocolTypes.h" +#include "IInputStream.h" +#include "IOutputStream.h" +#include "CLock.h" +#include "CLog.h" +#include "CStopwatch.h" +#include "XBase.h" +#include + +// +// CServerProxy +// + +CServerProxy::CServerProxy(IClient* client, + IInputStream* input, IOutputStream* output) : + m_client(client), + m_input(input), + m_output(output) +{ + assert(m_client != NULL); + assert(m_input != NULL); + assert(m_output != NULL); +} + +CServerProxy::~CServerProxy() +{ + delete m_input; + delete m_output; +} + +bool +CServerProxy::run() +{ + bool failedToConnect = false; + try { + // no compressed mouse motion yet + m_compressMouse = false; + + // not ignoring mouse motions + m_ignoreMouse = false; + + // handle messages from server + CStopwatch heartbeat; + for (;;) { + // if no input is pending then flush compressed mouse motion + if (getInputStream()->getSize() == 0) { + flushCompressedMouse(); + } + + // wait for a message + log((CLOG_DEBUG2 "waiting for message")); + UInt8 code[4]; + UInt32 n = getInputStream()->read(code, 4, kHeartRate); + + // check if server hungup + if (n == 0) { + log((CLOG_NOTE "server disconnected")); + break; + } + + // check for time out + if (n == (UInt32)-1 || heartbeat.getTime() > kHeartRate) { + // send heartbeat + CLock lock(&m_mutex); + CProtocolUtil::writef(getOutputStream(), kMsgCNoop); + heartbeat.reset(); + if (n == (UInt32)-1) { + // no message to process + continue; + } + } + + // verify we got an entire code + if (n != 4) { + // client sent an incomplete message + log((CLOG_ERR "incomplete message from server")); + break; + } + + // 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, kMsgDMouseWheel, 4) == 0) { + mouseWheel(); + } + + else if (memcmp(code, kMsgDKeyDown, 4) == 0) { + keyDown(); + } + + else if (memcmp(code, kMsgDKeyUp, 4) == 0) { + keyUp(); + } + + else if (memcmp(code, kMsgDMouseDown, 4) == 0) { + mouseDown(); + } + + else if (memcmp(code, kMsgDMouseUp, 4) == 0) { + mouseUp(); + } + + else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) { + keyRepeat(); + } + + else if (memcmp(code, kMsgCNoop, 4) == 0) { + // accept and discard no-op + } + + else if (memcmp(code, kMsgCEnter, 4) == 0) { + enter(); + } + + else if (memcmp(code, kMsgCLeave, 4) == 0) { + leave(); + } + + else if (memcmp(code, kMsgCClipboard, 4) == 0) { + grabClipboard(); + } + + else if (memcmp(code, kMsgCScreenSaver, 4) == 0) { + screenSaver(); + } + + else if (memcmp(code, kMsgQInfo, 4) == 0) { + queryInfo(); + } + + else if (memcmp(code, kMsgCInfoAck, 4) == 0) { + infoAcknowledgment(); + } + + else if (memcmp(code, kMsgDClipboard, 4) == 0) { + setClipboard(); + } + + 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")); + failedToConnect = true; + break; + } + } + } + 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; +} + +void +CServerProxy::onError() +{ + // ignore +} + +void +CServerProxy::onInfoChanged(const CString&, const CClientInfo& info) +{ + // ignore mouse motion until we receive acknowledgment of our info + // change message. + CLock lock(&m_mutex); + m_ignoreMouse = true; + + // send info update + sendInfo(info); +} + +bool +CServerProxy::onGrabClipboard(const CString&, ClipboardID id, UInt32 seqNum) +{ + log((CLOG_DEBUG1 "sending clipboard %d changed", id)); + CLock lock(&m_mutex); + CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, seqNum); +} + +void +CServerProxy::onClipboardChanged(ClipboardID id, + UInt32 seqNum, const CString& data) +{ + log((CLOG_DEBUG1 "sending clipboard %d seqnum=%d, size=%d", id, seqNum, data.size())); + CLock lock(&m_mutex); + CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, seqNum, &data); +} + +void +CServerProxy::onKeyDown(KeyID, KeyModifierMask) +{ + // ignore +} + +void +CServerProxy::onKeyUp(KeyID, KeyModifierMask) +{ + // ignore +} + +void +CServerProxy::onKeyRepeat(KeyID, KeyModifierMask, SInt32) +{ + // ignore +} + +void +CServerProxy::onMouseDown(ButtonID) +{ + // ignore +} + +void +CServerProxy::onMouseUp(ButtonID) +{ + // ignore +} + +bool +CServerProxy::onMouseMovePrimary(SInt32, SInt32) +{ + return false; +} + +void +CServerProxy::onMouseMoveSecondary(SInt32, SInt32) +{ + // ignore +} + +void +CServerProxy::onMouseWheel(SInt32) +{ + // ignore +} + +void +CServerProxy::onScreenSaver(bool) +{ + // ignore +} + +void +CServerProxy::flushCompressedMouse() +{ + bool send = false; + SInt32 x, y; + { + CLock lock(&m_mutex); + if (m_compressMouse) { + m_compressMouse = false; + x = m_xMouse; + y = m_yMouse; + send = true; + } + } + + if (send) { + getClient()->mouseMove(x, y); + } +} + +void +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, + info.m_x, info.m_y, + info.m_w, info.m_h, + info.m_zoneSize, + info.m_mx, info.m_my); +} + +void +CServerProxy::enter() +{ + // parse + SInt16 x, y; + UInt16 mask; + UInt32 seqNum; + CProtocolUtil::readf(getInputStream(), + 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; + } + + // forward + getClient()->enter(x, y, seqNum, static_cast(mask), false); +} + +void +CServerProxy::leave() +{ + // parse + log((CLOG_DEBUG1 "recv leave")); + + // send last mouse motion + flushCompressedMouse(); + + // forward + getClient()->leave(); +} + +void +CServerProxy::setClipboard() +{ + // parse + ClipboardID id; + UInt32 seqNum; + CString data; + CProtocolUtil::readf(getInputStream(), + kMsgDClipboard + 4, &id, &seqNum, &data); + log((CLOG_DEBUG "recv clipboard %d size=%d", id, data.size())); + + // validate + if (id >= kClipboardEnd) { + return; + } + + // forward + getClient()->setClipboard(id, data); +} + +void +CServerProxy::grabClipboard() +{ + // parse + ClipboardID id; + UInt32 seqNum; + CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id, &seqNum); + log((CLOG_DEBUG "recv grab clipboard %d", id)); + + // validate + if (id >= kClipboardEnd) { + return; + } + + // forward + getClient()->grabClipboard(id); +} + +void +CServerProxy::keyDown() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + UInt16 id, mask; + CProtocolUtil::readf(getInputStream(), kMsgDKeyDown + 4, &id, &mask); + log((CLOG_DEBUG1 "recv key down id=%d, mask=0x%04x", id, mask)); + + // forward + getClient()->keyDown(static_cast(id), + static_cast(mask)); +} + +void +CServerProxy::keyRepeat() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + UInt16 id, mask, count; + CProtocolUtil::readf(getInputStream(), + kMsgDKeyRepeat + 4, &id, &mask, &count); + log((CLOG_DEBUG1 "recv key repeat id=%d, mask=0x%04x, count=%d", id, mask, count)); + + // forward + getClient()->keyRepeat(static_cast(id), + static_cast(mask), + count); +} + +void +CServerProxy::keyUp() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + UInt16 id, mask; + CProtocolUtil::readf(getInputStream(), kMsgDKeyUp + 4, &id, &mask); + log((CLOG_DEBUG1 "recv key up id=%d, mask=0x%04x", id, mask)); + + // forward + getClient()->keyUp(static_cast(id), + static_cast(mask)); +} + +void +CServerProxy::mouseDown() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + SInt8 id; + CProtocolUtil::readf(getInputStream(), kMsgDMouseDown + 4, &id); + log((CLOG_DEBUG1 "recv mouse down id=%d", id)); + + // forward + getClient()->mouseDown(static_cast(id)); +} + +void +CServerProxy::mouseUp() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + SInt8 id; + CProtocolUtil::readf(getInputStream(), kMsgDMouseUp + 4, &id); + log((CLOG_DEBUG1 "recv mouse up id=%d", id)); + + // forward + getClient()->mouseUp(static_cast(id)); +} + +void +CServerProxy::mouseMove() +{ + // parse + bool ignore; + SInt16 x, y; + CProtocolUtil::readf(getInputStream(), kMsgDMouseMove + 4, &x, &y); + + { + // note if we should ignore the move + CLock lock(&m_mutex); + ignore = m_ignoreMouse; + + // compress mouse motion events if more input follows + if (!ignore && !m_compressMouse && getInputStream()->getSize() > 0) { + m_compressMouse = true; + } + + // 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); + } +} + +void +CServerProxy::mouseWheel() +{ + // get mouse up to date + flushCompressedMouse(); + + // parse + SInt16 delta; + CProtocolUtil::readf(getInputStream(), kMsgDMouseWheel + 4, &delta); + log((CLOG_DEBUG2 "recv mouse wheel %+d", delta)); + + // forward + getClient()->mouseWheel(delta); +} + +void +CServerProxy::screenSaver() +{ + // parse + SInt8 on; + CProtocolUtil::readf(getInputStream(), kMsgCScreenSaver + 4, &on); + log((CLOG_DEBUG1 "recv screen saver on=%d", on)); + + // forward + getClient()->screenSaver(on != 0); +} + +void +CServerProxy::queryInfo() +{ + // get current info + CClientInfo info; + getClient()->getShape(info.m_x, info.m_y, info.m_w, info.m_h); + getClient()->getMousePos(info.m_mx, info.m_my); + info.m_zoneSize = getClient()->getJumpZoneSize(); + + // send it + CLock lock(&m_mutex); + 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/client/CServerProxy.h b/client/CServerProxy.h new file mode 100644 index 00000000..c404f7ed --- /dev/null +++ b/client/CServerProxy.h @@ -0,0 +1,89 @@ +#ifndef CSERVERPROXY_H +#define CSERVERPROXY_H + +#include "IServer.h" +#include "CMutex.h" + +class IClient; +class IInputStream; +class IOutputStream; + +class CServerProxy : public IServer { +public: + CServerProxy(IClient* client, + IInputStream* adoptedInput, + IOutputStream* adoptedOutput); + ~CServerProxy(); + + // manipulators + + // handle messages. returns true iff server didn't reject our + // connection. + bool run(); + + // accessors + + // get the client + IClient* getClient() const; + + // get the client name + CString getName() const; + + // get the input and output streams for the server + IInputStream* getInputStream() const; + IOutputStream* getOutputStream() const; + + // IServer overrides + virtual void onError(); + virtual void onInfoChanged(const CString& clientName, + const CClientInfo&); + virtual bool onGrabClipboard(const CString& clientName, + ClipboardID, UInt32 seqNum); + virtual void onClipboardChanged(ClipboardID, + UInt32 seqNum, const CString& data); + virtual void onKeyDown(KeyID, KeyModifierMask); + virtual void onKeyUp(KeyID, KeyModifierMask); + virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); + virtual void onMouseDown(ButtonID); + virtual void onMouseUp(ButtonID); + virtual bool onMouseMovePrimary(SInt32 x, SInt32 y); + virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy); + virtual void onMouseWheel(SInt32 delta); + virtual void onScreenSaver(bool activated); + +private: + // if compressing mouse motion then send the last motion now + void flushCompressedMouse(); + + void sendInfo(const CClientInfo&); + + // message handlers + void enter(); + void leave(); + void setClipboard(); + void grabClipboard(); + void keyDown(); + void keyRepeat(); + void keyUp(); + void mouseDown(); + void mouseUp(); + void mouseMove(); + void mouseWheel(); + void screenSaver(); + void queryInfo(); + void infoAcknowledgment(); + +private: + CMutex m_mutex; + + IClient* m_client; + IInputStream* m_input; + IOutputStream* m_output; + + bool m_compressMouse; + SInt32 m_xMouse, m_yMouse; + + bool m_ignoreMouse; +}; + +#endif diff --git a/synergy/ISecondaryScreen.h b/client/ISecondaryScreen.h similarity index 100% rename from synergy/ISecondaryScreen.h rename to client/ISecondaryScreen.h diff --git a/client/Makefile.am b/client/Makefile.am index 744ba730..27d35cc9 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -4,11 +4,14 @@ DEPTH = .. bin_PROGRAMS = synergy synergy_SOURCES = \ - CXWindowsSecondaryScreen.cpp \ CClient.cpp \ + CServerProxy.cpp \ + CXWindowsSecondaryScreen.cpp \ client.cpp \ CClient.h \ + CServerProxy.h \ CXWindowsSecondaryScreen.h \ + ISecondaryScreen.h \ $(NULL) synergy_LDADD = \ $(DEPTH)/platform/libplatform.a \ diff --git a/client/client.cpp b/client/client.cpp index 21d23b2d..620e7846 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -81,6 +81,7 @@ realMain(CMutex* mutex) // create client s_client = new CClient(s_name); s_client->camp(s_camp); + s_client->setAddress(s_serverAddress); if (!s_client->open()) { delete s_client; s_client = NULL; @@ -92,13 +93,17 @@ realMain(CMutex* mutex) mutex->unlock(); } locked = false; - bool success = s_client->run(s_serverAddress); + s_client->run(); locked = true; if (mutex != NULL) { mutex->lock(); } + // get client status + bool success = !s_client->wasRejected(); + // clean up + s_client->close(); delete s_client; s_client = NULL; CLog::setLock(NULL); @@ -111,6 +116,7 @@ realMain(CMutex* mutex) if (!locked && mutex != NULL) { mutex->lock(); } + s_client->close(); delete s_client; s_client = NULL; CLog::setLock(NULL); diff --git a/server/CClientProxy.h b/server/CClientProxy.h index 1b6c17bb..ef5d158f 100644 --- a/server/CClientProxy.h +++ b/server/CClientProxy.h @@ -27,7 +27,7 @@ public: IOutputStream* getOutputStream() const; // IClient overrides - virtual void open() = 0; + virtual bool open() = 0; virtual void run() = 0; virtual void close() = 0; virtual void enter(SInt32 xAbs, SInt32 yAbs, @@ -49,6 +49,7 @@ public: virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const = 0; virtual void getCenter(SInt32& x, SInt32& y) const = 0; + virtual void getMousePos(SInt32& x, SInt32& y) const = 0; virtual SInt32 getJumpZoneSize() const = 0; private: diff --git a/server/CClientProxy1_0.cpp b/server/CClientProxy1_0.cpp index b1854d65..6a214d0d 100644 --- a/server/CClientProxy1_0.cpp +++ b/server/CClientProxy1_0.cpp @@ -29,7 +29,7 @@ CClientProxy1_0::~CClientProxy1_0() // do nothing } -void +bool CClientProxy1_0::open() { // send request @@ -55,6 +55,8 @@ CClientProxy1_0::open() // handle reply recvInfo(false); + + return true; } void @@ -258,6 +260,12 @@ CClientProxy1_0::getCenter(SInt32& x, SInt32& y) const y = m_info.m_my; } +void +CClientProxy1_0::getMousePos(SInt32&, SInt32&) const +{ + assert(0 && "shouldn't be called"); +} + SInt32 CClientProxy1_0::getJumpZoneSize() const { diff --git a/server/CClientProxy1_0.h b/server/CClientProxy1_0.h index fae25eb7..59d463b7 100644 --- a/server/CClientProxy1_0.h +++ b/server/CClientProxy1_0.h @@ -13,7 +13,7 @@ public: ~CClientProxy1_0(); // IClient overrides - virtual void open(); + virtual bool open(); virtual void run(); virtual void close(); virtual void enter(SInt32 xAbs, SInt32 yAbs, @@ -34,6 +34,7 @@ public: virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; virtual void getCenter(SInt32& x, SInt32& y) const; + virtual void getMousePos(SInt32& x, SInt32& y) const; virtual SInt32 getJumpZoneSize() const; private: diff --git a/server/CPrimaryClient.cpp b/server/CPrimaryClient.cpp index 57f11963..a36aaa83 100644 --- a/server/CPrimaryClient.cpp +++ b/server/CPrimaryClient.cpp @@ -149,7 +149,7 @@ CPrimaryClient::onScreenSaver(bool activated) m_server->onScreenSaver(activated); } -void +bool CPrimaryClient::open() { // all clipboards are clean and owned by us @@ -160,6 +160,8 @@ CPrimaryClient::open() // now open the screen m_screen->open(); + + return true; } void @@ -296,6 +298,12 @@ CPrimaryClient::getCenter(SInt32& x, SInt32& y) const y = m_info.m_my; } +void +CPrimaryClient::getMousePos(SInt32&, SInt32&) const +{ + assert(0 && "shouldn't be called"); +} + SInt32 CPrimaryClient::getJumpZoneSize() const { diff --git a/server/CPrimaryClient.h b/server/CPrimaryClient.h index bfc4244f..a8fd12d3 100644 --- a/server/CPrimaryClient.h +++ b/server/CPrimaryClient.h @@ -52,7 +52,7 @@ public: virtual void onScreenSaver(bool activated); // IClient overrides - virtual void open(); + virtual bool open(); virtual void run(); virtual void close(); virtual void enter(SInt32 xAbs, SInt32 yAbs, @@ -74,6 +74,7 @@ public: virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; virtual void getCenter(SInt32& x, SInt32& y) const; + virtual void getMousePos(SInt32& x, SInt32& y) const; virtual SInt32 getJumpZoneSize() const; private: diff --git a/server/CServer.cpp b/server/CServer.cpp index 8ddd9e13..89b72575 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -21,14 +21,6 @@ #include "CLog.h" #include "CStopwatch.h" #include "TMethodJob.h" -#include - -// hack to work around operator=() bug in STL in g++ prior to v3 -#if defined(__GNUC__) && (__GNUC__ < 3) -#define assign(_dst, _src, _type) _dst.reset(_src) -#else -#define assign(_dst, _src, _type) _dst = std::auto_ptr<_type >(_src) -#endif // // CServer @@ -1106,11 +1098,11 @@ CServer::acceptClients(void*) { log((CLOG_DEBUG1 "starting to wait for clients")); - std::auto_ptr listen; + IListenSocket* listen = NULL; try { // create socket listener // listen = std::auto_ptr(m_socketFactory->createListen()); - assign(listen, new CTCPListenSocket, IListenSocket); // FIXME + listen = new CTCPListenSocket; // FIXME -- use factory // bind to the desired port. keep retrying if we can't bind // the address immediately. @@ -1147,11 +1139,19 @@ CServer::acceptClients(void*) startThread(new TMethodJob( this, &CServer::runClient, socket)); } + + // clean up + delete listen; } catch (XBase& e) { log((CLOG_ERR "cannot listen for clients: %s", e.what())); + delete listen; quit(); } + catch (...) { + delete listen; + throw; + } } void @@ -1159,12 +1159,20 @@ CServer::runClient(void* vsocket) { // get the socket pointer from the argument assert(vsocket != NULL); - std::auto_ptr socket(reinterpret_cast(vsocket)); + IDataSocket* socket = reinterpret_cast(vsocket); // create proxy - CClientProxy* proxy = handshakeClient(socket.get()); - if (proxy == NULL) { - return; + CClientProxy* proxy = NULL; + try { + proxy = handshakeClient(socket); + if (proxy == NULL) { + delete socket; + return; + } + } + catch (...) { + delete socket; + throw; } // add the connection @@ -1181,6 +1189,7 @@ CServer::runClient(void* vsocket) log((CLOG_WARN "a client with name \"%s\" is already connected", e.getName().c_str())); CProtocolUtil::writef(proxy->getOutputStream(), kMsgEBusy); delete proxy; + delete socket; return; } catch (XUnknownClient& e) { @@ -1188,10 +1197,12 @@ CServer::runClient(void* vsocket) log((CLOG_WARN "a client with name \"%s\" is not in the map", e.getName().c_str())); CProtocolUtil::writef(proxy->getOutputStream(), kMsgEUnknown); delete proxy; + delete socket; return; } catch (...) { delete proxy; + delete socket; throw; } @@ -1222,11 +1233,13 @@ CServer::runClient(void* vsocket) catch (...) { // run() was probably cancelled removeConnection(proxy->getName()); + delete socket; throw; } - // remove the connection + // clean up removeConnection(proxy->getName()); + delete socket; } CClientProxy* @@ -1244,13 +1257,14 @@ CServer::handshakeClient(IDataSocket* socket) /* FIXME -- implement ISecurityFactory input = m_securityFactory->createInputFilter(input, own); output = m_securityFactory->createOutputFilter(output, own); - own = true; + own = true; */ } // attach the packetizing filters input = new CInputPacketStream(input, own); output = new COutputPacketStream(output, own); + own = true; CClientProxy* proxy = NULL; CString name(""); @@ -1344,7 +1358,7 @@ CServer::handshakeClient(IDataSocket* socket) if (proxy != NULL) { delete proxy; } - else { + else if (own) { delete input; delete output; } @@ -1355,7 +1369,7 @@ CServer::handshakeClient(IDataSocket* socket) if (proxy != NULL) { delete proxy; } - else { + else if (own) { delete input; delete output; } @@ -1368,11 +1382,11 @@ CServer::acceptHTTPClients(void*) { log((CLOG_DEBUG1 "starting to wait for HTTP clients")); - std::auto_ptr listen; + IListenSocket* listen = NULL; try { // create socket listener // listen = std::auto_ptr(m_socketFactory->createListen()); - assign(listen, new CTCPListenSocket, IListenSocket); // FIXME + listen = new CTCPListenSocket; // FIXME -- use factory // bind to the desired port. keep retrying if we can't bind // the address immediately. @@ -1419,12 +1433,20 @@ CServer::acceptHTTPClients(void*) startThread(new TMethodJob( this, &CServer::processHTTPRequest, socket)); } + + // clean up + delete listen; } catch (XBase& e) { log((CLOG_ERR "cannot listen for HTTP clients: %s", e.what())); + delete listen; // FIXME -- quit? quit(); } + catch (...) { + delete listen; + throw; + } } void diff --git a/synergy/IClient.h b/synergy/IClient.h index d538294f..2c363368 100644 --- a/synergy/IClient.h +++ b/synergy/IClient.h @@ -11,8 +11,8 @@ class IClient : public IInterface { public: // manipulators - // open client - virtual void open() = 0; + // open client. return true iff successful. + virtual bool open() = 0; // service client virtual void run() = 0; @@ -68,6 +68,9 @@ public: // get the center pixel virtual void getCenter(SInt32& x, SInt32& y) const = 0; + // get the mouse position + virtual void getMousePos(SInt32& x, SInt32& y) const = 0; + // get the size of jump zone virtual SInt32 getJumpZoneSize() const = 0; }; diff --git a/synergy/Makefile.am b/synergy/Makefile.am index 78a56b29..58a9a15f 100644 --- a/synergy/Makefile.am +++ b/synergy/Makefile.am @@ -20,7 +20,6 @@ libsynergy_a_SOURCES = \ IClient.h \ IClipboard.h \ IScreenSaver.h \ - ISecondaryScreen.h \ IServer.h \ ISocketFactory.h \ KeyTypes.h \