diff --git a/client/CClient.cpp b/client/CClient.cpp index 69738f02..b5f00de0 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -30,7 +30,9 @@ CClient::CClient(const CString& clientName) : m_name(clientName), m_input(NULL), m_output(NULL), - m_screen(NULL) + m_screen(NULL), + m_active(false), + m_seqNum(0) { // do nothing } @@ -92,7 +94,29 @@ void CClient::onClipboardChanged(ClipboardID id) 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); + 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 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())); + CProtocolUtil::writef(m_output, kMsgDClipboard, id, m_seqNum, &data); } } @@ -230,9 +254,6 @@ void CClient::runSession(void*) else if (memcmp(code, kMsgQInfo, 4) == 0) { onQueryInfo(); } - else if (memcmp(code, kMsgQClipboard, 4) == 0) { - onQueryClipboard(); - } else if (memcmp(code, kMsgDClipboard, 4) == 0) { onSetClipboard(); } @@ -271,6 +292,18 @@ void CClient::openSecondaryScreen() { assert(m_screen == NULL); + // not active + m_active = false; + + // reset last sequence number + m_seqNum = 0; + + // reset clipboard state + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_ownClipboard[id] = false; + m_timeClipboard[id] = 0; + } + // open screen log((CLOG_DEBUG1 "creating secondary screen")); #if defined(CONFIG_PLATFORM_WIN32) @@ -306,22 +339,61 @@ void CClient::onEnter() SInt16 x, y; { CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y); + CProtocolUtil::readf(m_input, kMsgCEnter + 4, &x, &y, &m_seqNum); + m_active = true; } m_screen->enter(x, y); } void CClient::onLeave() { + // 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(); + + // send data + log((CLOG_DEBUG "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size())); + CProtocolUtil::writef(m_output, + kMsgDClipboard, id, m_seqNum, &data); + } + } + } } void CClient::onGrabClipboard() { ClipboardID id; + UInt32 seqNum; { CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgCClipboard + 4, &id); + CProtocolUtil::readf(m_input, kMsgCClipboard + 4, &id, &seqNum); + + // we no longer own the clipboard + m_ownClipboard[id] = false; } m_screen->grabClipboard(id); } @@ -347,32 +419,6 @@ void CClient::onQueryInfo() CProtocolUtil::writef(m_output, kMsgDInfo, w, h, zoneSize); } -void CClient::onQueryClipboard() -{ - // parse message - ClipboardID id; - UInt32 seqNum; - CClipboard clipboard; - { - CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgQClipboard + 4, &id, &seqNum); - } - log((CLOG_DEBUG "received query clipboard %d seqnum=%d", id, seqNum)); - - // get screen's clipboard data - m_screen->getClipboard(id, &clipboard); - - // marshall the data - CString data = clipboard.marshall(); - - // send it - log((CLOG_DEBUG "sending clipboard %d seqnum=%d, size=%d", id, seqNum, data.size())); - { - CLock lock(&m_mutex); - CProtocolUtil::writef(m_output, kMsgDClipboard, id, seqNum, &data); - } -} - void CClient::onSetClipboard() { ClipboardID id; @@ -387,7 +433,7 @@ void CClient::onSetClipboard() // unmarshall CClipboard clipboard; - clipboard.unmarshall(data); + clipboard.unmarshall(data, 0); // set screen's clipboard m_screen->setClipboard(id, &clipboard); diff --git a/client/CClient.h b/client/CClient.h index 7776ad3d..f2cf0628 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -5,6 +5,7 @@ #include "CString.h" #include "BasicTypes.h" #include "ClipboardTypes.h" +#include "IClipboard.h" class CNetworkAddress; class IInputStream; @@ -39,7 +40,6 @@ class CClient { void onGrabClipboard(); void onScreenSaver(); void onQueryInfo(); - void onQueryClipboard(); void onSetClipboard(); void onKeyDown(); void onKeyRepeat(); @@ -56,6 +56,10 @@ class CClient { IOutputStream* m_output; ISecondaryScreen* m_screen; const CNetworkAddress* m_serverAddress; + bool m_active; + UInt32 m_seqNum; + bool m_ownClipboard[kClipboardEnd]; + IClipboard::Time m_timeClipboard[kClipboardEnd]; }; #endif diff --git a/server/CServer.cpp b/server/CServer.cpp index 94c2806b..bd24f766 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -43,7 +43,8 @@ else { wait(0); exit(1); } CServer::CServer() : m_primary(NULL), m_active(NULL), - m_primaryInfo(NULL) + m_primaryInfo(NULL), + m_seqNum(0) { m_socketFactory = NULL; m_securityFactory = NULL; @@ -94,6 +95,11 @@ void CServer::run() } } +void CServer::quit() +{ + m_primary->stop(); +} + void CServer::setScreenMap(const CScreenMap& screenMap) { CLock lock(&m_mutex); @@ -150,35 +156,43 @@ void CServer::setInfo(const CString& client, void CServer::grabClipboard(ClipboardID id) { - grabClipboard(id, m_primaryInfo->m_name); + grabClipboard(id, 0, m_primaryInfo->m_name); } void CServer::grabClipboard( - ClipboardID id, const CString& client) + ClipboardID id, UInt32 seqNum, + const CString& client) { CLock lock(&m_mutex); - ClipboardInfo& clipboard = m_clipboards[id]; + CClipboardInfo& clipboard = m_clipboards[id]; - // client must be connected + // screen must be connected CScreenList::iterator index = m_screens.find(client); if (index == m_screens.end()) { throw XBadClient(); } + // ignore grab if sequence number is old. always allow primary + // screen to grab. + if (client != m_primaryInfo->m_name && + seqNum < clipboard.m_clipboardSeqNum) { + log((CLOG_INFO "ignored client \"%s\" grab of clipboard %d", client.c_str(), id)); + return; + } + + // mark screen as owning clipboard log((CLOG_NOTE "client \"%s\" grabbed clipboard %d from \"%s\"", client.c_str(), id, clipboard.m_clipboardOwner.c_str())); + clipboard.m_clipboardOwner = client; + clipboard.m_clipboardSeqNum = seqNum; - // save the clipboard owner - clipboard.m_clipboardOwner = client; - - // mark client as having the clipboard data + // no screens have the new clipboard except the sender + clearGotClipboard(id); index->second->m_gotClipboard[id] = true; - // tell all other clients to take ownership of clipboard and mark - // them as not having the data yet. + // tell all other screens to take ownership of clipboard for (index = m_screens.begin(); index != m_screens.end(); ++index) { if (index->first != client) { CScreenInfo* info = index->second; - info->m_gotClipboard[id] = false; if (info->m_protocol == NULL) { m_primary->grabClipboard(id); } @@ -186,13 +200,10 @@ void CServer::grabClipboard( info->m_protocol->sendGrabClipboard(id); } } - } + } - // increment the clipboard sequence number so we can identify the - // clipboard query's response. - ++clipboard.m_clipboardSeqNum; - - // begin getting the clipboard data + // get the clipboard data if primary has it, otherwise mark the + // clipboard data as unknown. if (m_active->m_protocol == NULL) { // get clipboard immediately from primary screen m_primary->getClipboard(id, &clipboard.m_clipboard); @@ -201,43 +212,42 @@ void CServer::grabClipboard( } else { // clear out the clipboard since existing data is now out of date. - if (clipboard.m_clipboard.open()) { + if (clipboard.m_clipboard.open(0)) { clipboard.m_clipboard.close(); } clipboard.m_clipboardReady = false; - - // send request but don't wait for reply - m_active->m_protocol->sendQueryClipboard(id, - clipboard.m_clipboardSeqNum); } } void CServer::setClipboard(ClipboardID id, UInt32 seqNum, const CString& data) { - // update the clipboard if the sequence number matches CLock lock(&m_mutex); - ClipboardInfo& clipboard = m_clipboards[id]; - if (seqNum == clipboard.m_clipboardSeqNum) { - // unmarshall into our clipboard buffer - clipboard.m_clipboardData = data; - clipboard.m_clipboard.unmarshall(clipboard.m_clipboardData); - clipboard.m_clipboardReady = true; + CClipboardInfo& clipboard = m_clipboards[id]; - // if the active client doesn't have the clipboard data - // (and it won't unless the client is the one sending us - // the data) then send the data now. - if (!m_active->m_gotClipboard[id]) { - if (m_active->m_protocol == NULL) { - m_primary->setClipboard(id, &clipboard.m_clipboard); - } - else { - m_active->m_protocol->sendClipboard(id, - clipboard.m_clipboardData); - } - m_active->m_gotClipboard[id] = true; - } + // ignore update if sequence number is old + if (seqNum < clipboard.m_clipboardSeqNum) { + log((CLOG_INFO "ignored client \"%s\" update of clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); + return; } + + // unmarshall into our clipboard buffer + log((CLOG_NOTE "client \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id)); + clipboard.m_clipboardReady = true; + clipboard.m_clipboardData = data; + clipboard.m_clipboard.unmarshall(clipboard.m_clipboardData, 0); + + // all screens have an out-of-date clipboard except the sender + clearGotClipboard(id); + CScreenList::const_iterator index = + m_screens.find(clipboard.m_clipboardOwner); + if (index != m_screens.end()) { + index->second->m_gotClipboard[id] = true; + } + + // send the new clipboard to the active screen (will do nothing if + // the active screen is the one sending the new clipboard) + sendClipboard(id); } bool CServer::onCommandKey(KeyID /*id*/, @@ -492,8 +502,19 @@ void CServer::switchScreen(CScreenInfo* dst, // since that's a waste of time we skip that and just warp the // mouse. if (m_active != dst) { + // note if we're leaving the primary screen + const bool leavingPrimary = (m_active->m_protocol == NULL); + + // if leaving the primary screen then update the clipboards + // that it owns + if (leavingPrimary) { + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + updatePrimaryClipboard(id); + } + } + // leave active screen - if (m_active->m_protocol == NULL) { + if (leavingPrimary) { m_primary->leave(); } else { @@ -503,27 +524,20 @@ void CServer::switchScreen(CScreenInfo* dst, // cut over m_active = dst; + // increment enter sequence number + ++m_seqNum; + // enter new screen if (m_active->m_protocol == NULL) { m_primary->enter(x, y); } else { - m_active->m_protocol->sendEnter(x, y); + m_active->m_protocol->sendEnter(x, y, m_seqNum); } - // send the clipboard data if we haven't done so yet + // send the clipboard data to new active screen for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - ClipboardInfo& clipboard = m_clipboards[id]; - if (clipboard.m_clipboardReady && !m_active->m_gotClipboard[id]) { - if (m_active->m_protocol == NULL) { - m_primary->setClipboard(id, &clipboard.m_clipboard); - } - else { - m_active->m_protocol->sendClipboard(id, - clipboard.m_clipboardData); - } - m_active->m_gotClipboard[id] = true; - } + sendClipboard(id); } } else { @@ -889,9 +903,57 @@ void CServer::handshakeClient(void* vsocket) } } -void CServer::quit() +void CServer::clearGotClipboard(ClipboardID id) { - m_primary->stop(); + for (CScreenList::const_iterator index = m_screens.begin(); + index != m_screens.end(); ++index) { + index->second->m_gotClipboard[id] = false; + } +} + +void CServer::sendClipboard(ClipboardID id) +{ + // do nothing if clipboard was already sent + if (!m_active->m_gotClipboard[id]) { + CClipboardInfo& clipboard = m_clipboards[id]; + if (clipboard.m_clipboardReady) { + // send it + if (m_active->m_protocol == NULL) { + m_primary->setClipboard(id, &clipboard.m_clipboard); + } + else { + m_active->m_protocol->sendClipboard(id, + clipboard.m_clipboardData); + } + + // clipboard has been sent + m_active->m_gotClipboard[id] = true; + } + } +} + +void CServer::updatePrimaryClipboard(ClipboardID id) +{ + CClipboardInfo& clipboard = m_clipboards[id]; + + // if leaving primary and the primary owns the clipboard + // then update it. + if (clipboard.m_clipboardOwner == m_primaryInfo->m_name) { + assert(clipboard.m_clipboardReady == true); + + // save clipboard time + IClipboard::Time time = clipboard.m_clipboard.getTime(); + + // update + m_primary->getClipboard(id, &clipboard.m_clipboard); + + // if clipboard changed then other screens have an + // out-of-date clipboard. + if (time != clipboard.m_clipboard.getTime()) { + clearGotClipboard(id); + m_primaryInfo->m_gotClipboard[id] = true; + } + } } // FIXME -- use factory to create screen @@ -904,6 +966,9 @@ void CServer::openPrimaryScreen() { assert(m_primary == NULL); + // reset sequence number + m_seqNum = 0; + // open screen log((CLOG_DEBUG1 "creating primary screen")); #if defined(CONFIG_PLATFORM_WIN32) @@ -927,7 +992,7 @@ void CServer::openPrimaryScreen() // set the clipboard owner to the primary screen and then get the // current clipboard data. for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - ClipboardInfo& clipboard = m_clipboards[id]; + CClipboardInfo& clipboard = m_clipboards[id]; m_primary->getClipboard(id, &clipboard.m_clipboard); clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); clipboard.m_clipboardReady = true; @@ -1098,10 +1163,10 @@ CServer::CScreenInfo::~CScreenInfo() // -// CServer::ClipboardInfo +// CServer::CClipboardInfo // -CServer::ClipboardInfo::ClipboardInfo() : +CServer::CClipboardInfo::CClipboardInfo() : m_clipboard(), m_clipboardData(), m_clipboardOwner(), diff --git a/server/CServer.h b/server/CServer.h index d4bf8553..1f0ab721 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -49,7 +49,8 @@ class CServer { // handle messages from clients void setInfo(const CString& clientName, SInt32 w, SInt32 h, SInt32 zoneSize); - void grabClipboard(ClipboardID, const CString& clientName); + void grabClipboard(ClipboardID, + UInt32 seqNum, const CString& clientName); void setClipboard(ClipboardID, UInt32 seqNum, const CString& data); @@ -126,6 +127,15 @@ class CServer { void openPrimaryScreen(); void closePrimaryScreen(); + // clear gotClipboard flags in all screens + void clearGotClipboard(ClipboardID); + + // send clipboard to the active screen if it doesn't already have it + void sendClipboard(ClipboardID); + + // update the clipboard if owned by the primary screen + void updatePrimaryClipboard(ClipboardID); + // cancel running threads void cleanupThreads(); @@ -148,9 +158,9 @@ class CServer { private: typedef std::list CThreadList; typedef std::map CScreenList; - class ClipboardInfo { + class CClipboardInfo { public: - ClipboardInfo(); + CClipboardInfo(); public: CClipboard m_clipboard; @@ -174,11 +184,14 @@ class CServer { CScreenInfo* m_active; CScreenInfo* m_primaryInfo; + // the sequence number of enter messages + UInt32 m_seqNum; + SInt32 m_x, m_y; CScreenMap m_screenMap; - ClipboardInfo m_clipboards[kClipboardEnd]; + CClipboardInfo m_clipboards[kClipboardEnd]; }; #endif diff --git a/server/CServerProtocol.h b/server/CServerProtocol.h index 1dda0969..e605049f 100644 --- a/server/CServerProtocol.h +++ b/server/CServerProtocol.h @@ -31,11 +31,10 @@ class CServerProtocol : public IServerProtocol { virtual void run() = 0; virtual void queryInfo() = 0; virtual void sendClose() = 0; - virtual void sendEnter(SInt32 xAbs, SInt32 yAbs) = 0; + virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum) = 0; virtual void sendLeave() = 0; virtual void sendClipboard(ClipboardID, const CString&) = 0; virtual void sendGrabClipboard(ClipboardID) = 0; - virtual void sendQueryClipboard(ClipboardID, UInt32 seqNum) = 0; virtual void sendScreenSaver(bool on) = 0; virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; diff --git a/server/CServerProtocol1_0.cpp b/server/CServerProtocol1_0.cpp index 02f27a71..8cfcff0a 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -91,10 +91,10 @@ void CServerProtocol1_0::sendClose() } void CServerProtocol1_0::sendEnter( - SInt32 xAbs, SInt32 yAbs) + SInt32 xAbs, SInt32 yAbs, UInt32 seqNum) { - log((CLOG_DEBUG1 "send enter to \"%s\", %d,%d", getClient().c_str(), xAbs, yAbs)); - CProtocolUtil::writef(getOutputStream(), kMsgCEnter, xAbs, yAbs); + log((CLOG_DEBUG1 "send enter to \"%s\", %d,%d %d", getClient().c_str(), xAbs, yAbs, seqNum)); + CProtocolUtil::writef(getOutputStream(), kMsgCEnter, xAbs, yAbs, seqNum); } void CServerProtocol1_0::sendLeave() @@ -113,14 +113,7 @@ void CServerProtocol1_0::sendClipboard( void CServerProtocol1_0::sendGrabClipboard(ClipboardID id) { log((CLOG_DEBUG "send grab clipboard %d to \"%s\"", id, getClient().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id); -} - -void CServerProtocol1_0::sendQueryClipboard( - ClipboardID id, UInt32 seqNum) -{ - log((CLOG_DEBUG "query clipboard %d to \"%s\"", id, getClient().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgQClipboard, id, seqNum); + CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, 0); } void CServerProtocol1_0::sendScreenSaver(bool on) @@ -207,7 +200,8 @@ void CServerProtocol1_0::recvClipboard() void CServerProtocol1_0::recvGrabClipboard() { ClipboardID id; - CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id); - log((CLOG_DEBUG "received client \"%s\" grabbed clipboard %d", getClient().c_str(), id)); - getServer()->grabClipboard(id, getClient()); + UInt32 seqNum; + CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id, &seqNum); + log((CLOG_DEBUG "received client \"%s\" grabbed clipboard %d seqnum=%d", getClient().c_str(), id, seqNum)); + getServer()->grabClipboard(id, seqNum, getClient()); } diff --git a/server/CServerProtocol1_0.h b/server/CServerProtocol1_0.h index 63c64f73..e2cacd8d 100644 --- a/server/CServerProtocol1_0.h +++ b/server/CServerProtocol1_0.h @@ -16,11 +16,10 @@ class CServerProtocol1_0 : public CServerProtocol { virtual void run(); virtual void queryInfo(); virtual void sendClose(); - virtual void sendEnter(SInt32 xAbs, SInt32 yAbs); + virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum); virtual void sendLeave(); virtual void sendClipboard(ClipboardID, const CString&); virtual void sendGrabClipboard(ClipboardID); - virtual void sendQueryClipboard(ClipboardID, UInt32 seqNum); virtual void sendScreenSaver(bool on); virtual void sendKeyDown(KeyID, KeyModifierMask); virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count); diff --git a/synergy/CClipboard.cpp b/synergy/CClipboard.cpp index 1e572af1..e5021aab 100644 --- a/synergy/CClipboard.cpp +++ b/synergy/CClipboard.cpp @@ -6,7 +6,8 @@ CClipboard::CClipboard() { - // do nothing + open(0); + close(); } CClipboard::~CClipboard() @@ -14,13 +15,17 @@ CClipboard::~CClipboard() // do nothing } -bool CClipboard::open() +bool CClipboard::open(Time time) { // clear all data for (SInt32 index = 0; index < kNumFormats; ++index) { m_data[index] = ""; m_added[index] = false; } + + // save time + m_time = time; + return true; } @@ -35,6 +40,11 @@ void CClipboard::add(EFormat format, const CString& data) m_added[format] = true; } +CClipboard::Time CClipboard::getTime() const +{ + return m_time; +} + bool CClipboard::has(EFormat format) const { return m_added[format]; @@ -47,7 +57,19 @@ CString CClipboard::get(EFormat format) const void CClipboard::copy(IClipboard* dst, const IClipboard* src) { - if (dst->open()) { + assert(dst != NULL); + assert(src != NULL); + + copy(dst, src, src->getTime()); +} + +void CClipboard::copy(IClipboard* dst, + const IClipboard* src, Time time) +{ + assert(dst != NULL); + assert(src != NULL); + + if (dst->open(time)) { for (SInt32 format = 0; format != IClipboard::kNumFormats; ++format) { IClipboard::EFormat eFormat = (IClipboard::EFormat)format; if (src->has(eFormat)) { @@ -58,12 +80,12 @@ void CClipboard::copy(IClipboard* dst, const IClipboard* src) } } -void CClipboard::unmarshall(const CString& data) +void CClipboard::unmarshall(const CString& data, Time time) { const char* index = data.data(); // clear existing data - open(); + open(time); // read the number of formats const UInt32 numFormats = readUInt32(index); diff --git a/synergy/CClipboard.h b/synergy/CClipboard.h index e5a63ccc..8fcf5cc0 100644 --- a/synergy/CClipboard.h +++ b/synergy/CClipboard.h @@ -16,7 +16,7 @@ class CClipboard : public IClipboard { // manipulators // unmarshall clipboard data - void unmarshall(const CString& data); + void unmarshall(const CString& data, Time); // accessors @@ -24,9 +24,10 @@ class CClipboard : public IClipboard { CString marshall() const; // IClipboard overrides - virtual bool open(); + virtual bool open(Time); virtual void close(); virtual void add(EFormat, const CString& data); + virtual Time getTime() const; virtual bool has(EFormat) const; virtual CString get(EFormat) const; @@ -34,14 +35,17 @@ class CClipboard : public IClipboard { // transfer 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). + // they don't have to be the same type). this also sets + // the timestamp to time, if provided, or the time in src. static void copy(IClipboard* dst, const IClipboard* src); + static void copy(IClipboard* dst, const IClipboard* src, Time); private: UInt32 readUInt32(const char*) const; void writeUInt32(CString*, UInt32) const; private: + Time m_time; bool m_added[kNumFormats]; CString m_data[kNumFormats]; }; diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index 1c1ad88d..51109584 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -222,16 +222,15 @@ bool CXWindowsScreen::setDisplayClipboard( if (XGetSelectionOwner(m_display, m_atomClipboard[id]) == requestor) { // we got the selection log((CLOG_INFO "grabbed clipboard at %d", timestamp)); - m_clipboards[id].m_gotClipboard = timestamp; m_clipboards[id].m_lostClipboard = CurrentTime; - if (clipboard != NULL) { // save clipboard to serve requests - CClipboard::copy(&m_clipboards[id].m_clipboard, clipboard); + CClipboard::copy(&m_clipboards[id].m_clipboard, + clipboard, timestamp); } else { // clear clipboard - if (m_clipboards[id].m_clipboard.open()) { + if (m_clipboards[id].m_clipboard.open(timestamp)) { m_clipboards[id].m_clipboard.close(); } } @@ -250,8 +249,10 @@ void CXWindowsScreen::getDisplayClipboard( assert(clipboard != NULL); assert(requestor != None); + // FIXME -- don't update clipboard object if clipboard hasn't changed + // clear the clipboard object - if (!clipboard->open()) + if (!clipboard->open(timestamp)) return; // block others from using the display while we get the clipboard. @@ -904,12 +905,12 @@ bool CXWindowsScreen::sendClipboardTimestamp( log((CLOG_DEBUG1 "handling clipboard request for TIMESTAMP")); // FIXME -- handle Alloc errors (by returning false) + Time time = m_clipboards[id].m_clipboard.getTime(); XChangeProperty(m_display, requestor, property, m_atomInteger, - 8 * sizeof(m_clipboards[id].m_gotClipboard), + 32, PropModeReplace, - reinterpret_cast( - &m_clipboards[id].m_gotClipboard), + reinterpret_cast(time), 1); return true; } @@ -935,7 +936,7 @@ bool CXWindowsScreen::wasOwnedAtTime( const CClipboardInfo& clipboard = m_clipboards[id]; // not owned if we've never owned the selection - if (clipboard.m_gotClipboard == CurrentTime) + if (clipboard.m_clipboard.getTime() == CurrentTime) return false; // if time is CurrentTime then return true if we still own the @@ -953,8 +954,8 @@ bool CXWindowsScreen::wasOwnedAtTime( return false; // compare time to range - Time duration = clipboard.m_lostClipboard - clipboard.m_gotClipboard; - Time when = time - clipboard.m_gotClipboard; + Time duration = clipboard.m_lostClipboard - clipboard.m_clipboard.getTime(); + Time when = time - clipboard.m_clipboard.getTime(); return (/*when >= 0 &&*/ when < duration); } @@ -1001,7 +1002,6 @@ Time CXWindowsScreen::getCurrentTimeNoLock( CXWindowsScreen::CClipboardInfo::CClipboardInfo() : m_clipboard(), - m_gotClipboard(CurrentTime), m_lostClipboard(CurrentTime), m_requests() { diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h index 00c7a703..ec8cfb7e 100644 --- a/synergy/CXWindowsScreen.h +++ b/synergy/CXWindowsScreen.h @@ -1,10 +1,10 @@ #ifndef CXWINDOWSSCREEN_H #define CXWINDOWSSCREEN_H -#include "CClipboard.h" -#include "CMutex.h" #include "BasicTypes.h" #include "ClipboardTypes.h" +#include "CClipboard.h" +#include "CMutex.h" #include #include #include @@ -74,6 +74,7 @@ class CXWindowsScreen { // copy the clipboard contents to clipboard. requestor must be a // valid window; it will be used to receive the transfer. timestamp // should be the timestamp of the provoking event and not CurrentTime. + // if force is false then only update clipboard void getDisplayClipboard(ClipboardID, IClipboard* clipboard, Window requestor, Time timestamp) const; @@ -149,11 +150,10 @@ class CXWindowsScreen { CClipboardInfo(); public: - // the contents of the clipboard + // the contents of the clipboard and the time we got it CClipboard m_clipboard; - // when we got the clipboard and when we lost it - Time m_gotClipboard; + // when we lost the clipboard Time m_lostClipboard; // the request queues diff --git a/synergy/IClipboard.h b/synergy/IClipboard.h index 08551e37..a75ec70a 100644 --- a/synergy/IClipboard.h +++ b/synergy/IClipboard.h @@ -8,6 +8,13 @@ class CString; class IClipboard : public IInterface { public: + // timestamp type. timestamps are in milliseconds from some + // arbitrary starting time. timestamps will wrap around to 0 + // after about 49 3/4 days. + typedef UInt32 Time; + + // known clipboard formats. kNumFormats must be last and + // formats must be sequential starting from zero. enum EFormat { kText, kNumFormats }; // manipulators @@ -16,8 +23,10 @@ class IClipboard : public IInterface { // only add() may be called between an open() and its // corresponding close(). if open() returns false then // the clipboard could not be opened or grabbed; do not - // call close() in that case. - virtual bool open() = 0; + // call close() in that case. iff open() returns true it + // should have saved the timestamp. the timestamp should + // be zero before the first successful open. + virtual bool open(Time) = 0; // close the clipboard. close() must match a preceding open(). // this signals that the clipboard has been filled with all the @@ -33,6 +42,9 @@ class IClipboard : public IInterface { // accessors + // returns the timestamp passed to the last successful open(). + virtual Time getTime() const = 0; + // returns true iff the clipboard contains data in the given // format. virtual bool has(EFormat) const = 0; diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index a398f226..91a698bd 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -75,7 +75,9 @@ class IPrimaryScreen : public IInterface { // get the size of jump zone virtual SInt32 getJumpZoneSize() const = 0; - // get the screen's clipboard contents + // get the screen's clipboard contents. the implementation can + // and should avoid setting the clipboard object if the screen's + // clipboard hasn't changed. virtual void getClipboard(ClipboardID, IClipboard*) const = 0; }; diff --git a/synergy/IServerProtocol.h b/synergy/IServerProtocol.h index 49a38898..84ed10ef 100644 --- a/synergy/IServerProtocol.h +++ b/synergy/IServerProtocol.h @@ -24,11 +24,10 @@ class IServerProtocol : public IInterface { // send various messages to client virtual void sendClose() = 0; - virtual void sendEnter(SInt32 xAbs, SInt32 yAbs) = 0; + virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum) = 0; virtual void sendLeave() = 0; virtual void sendClipboard(ClipboardID, const CString&) = 0; virtual void sendGrabClipboard(ClipboardID) = 0; - virtual void sendQueryClipboard(ClipboardID, UInt32 seqNum) = 0; virtual void sendScreenSaver(bool on) = 0; virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h index ca54ff04..f9b5edf7 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -22,17 +22,26 @@ static const char kMsgCClose[] = "CBYE"; // enter screen: primary -> secondary // entering screen at screen position $1 = x, $2 = y. x,y are -// absolute screen coordinates. -static const char kMsgCEnter[] = "CINN%2i%2i"; +// absolute screen coordinates. $3 = sequence number, which is +// used to order messages between screens. the secondary screen +// must return this number with some messages. +static const char kMsgCEnter[] = "CINN%2i%2i%4i"; // leave screen: primary -> secondary -// leaving screen +// leaving screen. the secondary screen should send clipboard +// data in response to this message for those clipboards that +// it has grabbed (i.e. has sent a kMsgCClipboard for and has +// not received a kMsgCClipboard for with a greater sequence +// number) and that were grabbed or have changed since the +// last leave. static const char kMsgCLeave[] = "COUT"; // grab clipboard: primary <-> secondary // sent by screen when some other app on that screen grabs a -// clipboard. $1 = the clipboard identifier. -static const char kMsgCClipboard[] = "CCLP%1i"; +// clipboard. $1 = the clipboard identifier, $2 = sequence number. +// secondary screens must use the sequence number passed in the +// most recent kMsgCEnter. the primary always sends 0. +static const char kMsgCClipboard[] = "CCLP%1i%4i"; // screensaver change: primary -> secondary // screensaver on primary has started ($1 == 1) or closed ($1 == 0) @@ -73,9 +82,9 @@ static const char kMsgDMouseWheel[] = "DMWM%2i"; // clipboard data: primary <-> secondary // $2 = sequence number, $3 = clipboard data. the sequence number -// is 0 when sent by the primary. the secondary sends this message -// in response to a kMsgQClipboard and uses the sequence number from -// that message. $1 = clipboard identifier. +// is 0 when sent by the primary. secondary screens should use the +// sequence number from the most recent kMsgCEnter. $1 = clipboard +// identifier. static const char kMsgDClipboard[] = "DCLP%1i%4i%s"; // client data: secondary -> primary @@ -88,12 +97,6 @@ static const char kMsgDInfo[] = "DINF%2i%2i%2i"; // query codes // -// query clipboard: primary -> secondary -// $2 = sequence number. the sequence number is an arbitrary value -// used by primary to identify the kMsgDClipboard response to a -// query. $1 = clipboard identifier. -static const char kMsgQClipboard[] = "QCLP%1i%4i"; - // query screen info: primary -> secondary // client should reply with a kMsgDInfo. static const char kMsgQInfo[] = "QINF";