diff --git a/client/CClient.cpp b/client/CClient.cpp index 5721ac5c..811ac7ee 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -85,14 +85,14 @@ void CClient::run(const CNetworkAddress& serverAddress) } } -void CClient::onClipboardChanged() +void CClient::onClipboardChanged(ClipboardID id) { - log((CLOG_DEBUG "sending clipboard changed")); + 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); + CProtocolUtil::writef(m_output, kMsgCClipboard, id); } } @@ -318,7 +318,12 @@ void CClient::onLeave() void CClient::onGrabClipboard() { - m_screen->grabClipboard(); + ClipboardID id; + { + CLock lock(&m_mutex); + CProtocolUtil::readf(m_input, kMsgCClipboard + 4, &id); + } + m_screen->grabClipboard(id); } void CClient::onScreenSaver() @@ -345,45 +350,47 @@ void CClient::onQueryInfo() void CClient::onQueryClipboard() { // parse message + ClipboardID id; UInt32 seqNum; CClipboard clipboard; { CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgQClipboard + 4, &seqNum); + CProtocolUtil::readf(m_input, kMsgQClipboard + 4, &id, &seqNum); } - log((CLOG_DEBUG "received query clipboard seqnum=%d", seqNum)); + log((CLOG_DEBUG "received query clipboard %d seqnum=%d", id, seqNum)); // get screen's clipboard data - m_screen->getClipboard(&clipboard); + m_screen->getClipboard(id, &clipboard); // marshall the data CString data = clipboard.marshall(); // send it - log((CLOG_DEBUG "sending clipboard seqnum=%d, size=%d", seqNum, data.size())); + log((CLOG_DEBUG "sending clipboard %d seqnum=%d, size=%d", id, seqNum, data.size())); { CLock lock(&m_mutex); - CProtocolUtil::writef(m_output, kMsgDClipboard, seqNum, &data); + CProtocolUtil::writef(m_output, kMsgDClipboard, id, seqNum, &data); } } void CClient::onSetClipboard() { + ClipboardID id; CString data; { // parse message UInt32 seqNum; CLock lock(&m_mutex); - CProtocolUtil::readf(m_input, kMsgDClipboard + 4, &seqNum, &data); + CProtocolUtil::readf(m_input, kMsgDClipboard + 4, &id, &seqNum, &data); } - log((CLOG_DEBUG "received clipboard size=%d", data.size())); + log((CLOG_DEBUG "received clipboard %d size=%d", id, data.size())); // unmarshall CClipboard clipboard; clipboard.unmarshall(data); // set screen's clipboard - m_screen->setClipboard(&clipboard); + m_screen->setClipboard(id, &clipboard); } void CClient::onKeyDown() diff --git a/client/CClient.h b/client/CClient.h index b214dfc3..7776ad3d 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -4,6 +4,7 @@ #include "CMutex.h" #include "CString.h" #include "BasicTypes.h" +#include "ClipboardTypes.h" class CNetworkAddress; class IInputStream; @@ -20,7 +21,7 @@ class CClient { void run(const CNetworkAddress& serverAddress); // handle events on client's screen - void onClipboardChanged(); + void onClipboardChanged(ClipboardID); // accessors diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 8c081d8b..f5fe3461 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -63,7 +63,8 @@ void CXWindowsSecondaryScreen::run() // selection owner. report that to the server. if (lostClipboard(xevent.xselectionclear.selection, xevent.xselectionclear.time)) { - m_client->onClipboardChanged(); + m_client->onClipboardChanged(getClipboardID( + xevent.xselectionclear.selection)); } break; @@ -260,16 +261,16 @@ void CXWindowsSecondaryScreen::mouseWheel(SInt32) } void CXWindowsSecondaryScreen::setClipboard( - const IClipboard* clipboard) + ClipboardID id, const IClipboard* clipboard) { // FIXME -- don't use CurrentTime - setDisplayClipboard(clipboard, m_window, CurrentTime); + setDisplayClipboard(id, clipboard, m_window, CurrentTime); } -void CXWindowsSecondaryScreen::grabClipboard() +void CXWindowsSecondaryScreen::grabClipboard(ClipboardID id) { // FIXME -- don't use CurrentTime - setDisplayClipboard(NULL, m_window, CurrentTime); + setDisplayClipboard(id, NULL, m_window, CurrentTime); } void CXWindowsSecondaryScreen::getSize( @@ -284,10 +285,10 @@ SInt32 CXWindowsSecondaryScreen::getJumpZoneSize() const } void CXWindowsSecondaryScreen::getClipboard( - IClipboard* clipboard) const + ClipboardID id, IClipboard* clipboard) const { // FIXME -- don't use CurrentTime - getDisplayClipboard(clipboard, m_window, CurrentTime); + getDisplayClipboard(id, clipboard, m_window, CurrentTime); } void CXWindowsSecondaryScreen::onOpenDisplay() diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 5597dfe7..5f4eda9f 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -24,11 +24,11 @@ class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute); virtual void mouseWheel(SInt32 delta); - virtual void setClipboard(const IClipboard*); - virtual void grabClipboard(); + virtual void setClipboard(ClipboardID, const IClipboard*); + virtual void grabClipboard(ClipboardID); virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; - virtual void getClipboard(IClipboard*) const; + virtual void getClipboard(ClipboardID, IClipboard*) const; protected: // CXWindowsScreen overrides diff --git a/server/CServer.cpp b/server/CServer.cpp index 10158e26..84617aa4 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -43,9 +43,7 @@ else { wait(0); exit(1); } CServer::CServer() : m_primary(NULL), m_active(NULL), - m_primaryInfo(NULL), - m_clipboardSeqNum(0), - m_clipboardReady(false) + m_primaryInfo(NULL) { m_socketFactory = NULL; m_securityFactory = NULL; @@ -150,14 +148,16 @@ void CServer::setInfo(const CString& client, log((CLOG_NOTE "client \"%s\" size=%dx%d zone=%d", client.c_str(), w, h, zoneSize)); } -void CServer::grabClipboard() +void CServer::grabClipboard(ClipboardID id) { - grabClipboard(m_primaryInfo->m_name); + grabClipboard(id, m_primaryInfo->m_name); } -void CServer::grabClipboard(const CString& client) +void CServer::grabClipboard( + ClipboardID id, const CString& client) { CLock lock(&m_mutex); + ClipboardInfo& clipboard = m_clipboards[id]; // client must be connected CScreenList::iterator index = m_screens.find(client); @@ -165,74 +165,77 @@ void CServer::grabClipboard(const CString& client) throw XBadClient(); } - log((CLOG_NOTE "client \"%s\" grabbed clipboard from \"%s\"", client.c_str(), m_clipboardOwner.c_str())); + log((CLOG_NOTE "client \"%s\" grabbed clipboard %d from \"%s\"", client.c_str(), id, clipboard.m_clipboardOwner.c_str())); // save the clipboard owner - m_clipboardOwner = client; + clipboard.m_clipboardOwner = client; // mark client as having the clipboard data - index->second->m_gotClipboard = true; + index->second->m_gotClipboard[id] = true; // tell all other clients to take ownership of clipboard and mark // them as not having the data yet. for (index = m_screens.begin(); index != m_screens.end(); ++index) { if (index->first != client) { CScreenInfo* info = index->second; - info->m_gotClipboard = false; + info->m_gotClipboard[id] = false; if (info->m_protocol == NULL) { - m_primary->grabClipboard(); + m_primary->grabClipboard(id); } else { - info->m_protocol->sendGrabClipboard(); + info->m_protocol->sendGrabClipboard(id); } } } // increment the clipboard sequence number so we can identify the // clipboard query's response. - ++m_clipboardSeqNum; + ++clipboard.m_clipboardSeqNum; // begin getting the clipboard data if (m_active->m_protocol == NULL) { // get clipboard immediately from primary screen - m_primary->getClipboard(&m_clipboard); - m_clipboardData = m_clipboard.marshall(); - m_clipboardReady = true; + m_primary->getClipboard(id, &clipboard.m_clipboard); + clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); + clipboard.m_clipboardReady = true; } else { // clear out the clipboard since existing data is now out of date. - if (m_clipboard.open()) { - m_clipboard.close(); + if (clipboard.m_clipboard.open()) { + clipboard.m_clipboard.close(); } - m_clipboardReady = false; + clipboard.m_clipboardReady = false; // send request but don't wait for reply - m_active->m_protocol->sendQueryClipboard(m_clipboardSeqNum); + m_active->m_protocol->sendQueryClipboard(id, + clipboard.m_clipboardSeqNum); } } -void CServer::setClipboard( +void CServer::setClipboard(ClipboardID id, UInt32 seqNum, const CString& data) { // update the clipboard if the sequence number matches CLock lock(&m_mutex); - if (seqNum == m_clipboardSeqNum) { + ClipboardInfo& clipboard = m_clipboards[id]; + if (seqNum == clipboard.m_clipboardSeqNum) { // unmarshall into our clipboard buffer - m_clipboardData = data; - m_clipboard.unmarshall(m_clipboardData); - m_clipboardReady = true; + clipboard.m_clipboardData = data; + clipboard.m_clipboard.unmarshall(clipboard.m_clipboardData); + clipboard.m_clipboardReady = true; // 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) { + if (!m_active->m_gotClipboard[id]) { if (m_active->m_protocol == NULL) { - m_primary->setClipboard(&m_clipboard); + m_primary->setClipboard(id, &clipboard.m_clipboard); } else { - m_active->m_protocol->sendClipboard(m_clipboardData); + m_active->m_protocol->sendClipboard(id, + clipboard.m_clipboardData); } - m_active->m_gotClipboard = true; + m_active->m_gotClipboard[id] = true; } } } @@ -509,14 +512,18 @@ void CServer::switchScreen(CScreenInfo* dst, } // send the clipboard data if we haven't done so yet - if (m_clipboardReady && !m_active->m_gotClipboard) { - if (m_active->m_protocol == NULL) { - m_primary->setClipboard(&m_clipboard); + 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; } - else { - m_active->m_protocol->sendClipboard(m_clipboardData); - } - m_active->m_gotClipboard = true; } } else { @@ -919,10 +926,13 @@ void CServer::openPrimaryScreen() // set the clipboard owner to the primary screen and then get the // current clipboard data. - m_primary->getClipboard(&m_clipboard); - m_clipboardData = m_clipboard.marshall(); - m_clipboardReady = true; - m_clipboardOwner = m_active->m_name; + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + ClipboardInfo& clipboard = m_clipboards[id]; + m_primary->getClipboard(id, &clipboard.m_clipboard); + clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); + clipboard.m_clipboardReady = true; + clipboard.m_clipboardOwner = m_active->m_name; + } } void CServer::closePrimaryScreen() @@ -1075,13 +1085,28 @@ CServer::CScreenInfo::CScreenInfo(const CString& name, m_name(name), m_protocol(protocol), m_width(0), m_height(0), - m_zoneSize(0), - m_gotClipboard(false) + m_zoneSize(0) { - // do nothing + for (ClipboardID id = 0; id < kClipboardEnd; ++id) + m_gotClipboard[id] = false; } CServer::CScreenInfo::~CScreenInfo() { // do nothing } + + +// +// CServer::ClipboardInfo +// + +CServer::ClipboardInfo::ClipboardInfo() : + m_clipboard(), + m_clipboardData(), + m_clipboardOwner(), + m_clipboardSeqNum(0), + m_clipboardReady(false) +{ + // do nothing +} diff --git a/server/CServer.h b/server/CServer.h index 3ca443d2..d4bf8553 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -1,6 +1,7 @@ #ifndef CSERVER_H #define CSERVER_H +#include "ClipboardTypes.h" #include "KeyTypes.h" #include "MouseTypes.h" #include "CScreenMap.h" @@ -43,13 +44,14 @@ class CServer { bool onMouseMovePrimary(SInt32 x, SInt32 y); void onMouseMoveSecondary(SInt32 dx, SInt32 dy); void onMouseWheel(SInt32 delta); - void grabClipboard(); + void grabClipboard(ClipboardID); // handle messages from clients void setInfo(const CString& clientName, SInt32 w, SInt32 h, SInt32 zoneSize); - void grabClipboard(const CString& clientName); - void setClipboard(UInt32 seqNum, const CString& data); + void grabClipboard(ClipboardID, const CString& clientName); + void setClipboard(ClipboardID, + UInt32 seqNum, const CString& data); // accessors @@ -95,7 +97,7 @@ class CServer { IServerProtocol* m_protocol; SInt32 m_width, m_height; SInt32 m_zoneSize; - bool m_gotClipboard; + bool m_gotClipboard[kClipboardEnd]; }; // change the active screen @@ -146,6 +148,17 @@ class CServer { private: typedef std::list CThreadList; typedef std::map CScreenList; + class ClipboardInfo { + public: + ClipboardInfo(); + + public: + CClipboard m_clipboard; + CString m_clipboardData; + CString m_clipboardOwner; + UInt32 m_clipboardSeqNum; + bool m_clipboardReady; + }; CMutex m_mutex; @@ -165,11 +178,7 @@ class CServer { CScreenMap m_screenMap; - CClipboard m_clipboard; - CString m_clipboardData; - CString m_clipboardOwner; - UInt32 m_clipboardSeqNum; - bool m_clipboardReady; + ClipboardInfo m_clipboards[kClipboardEnd]; }; #endif diff --git a/server/CServerProtocol.h b/server/CServerProtocol.h index d5a69a4d..1dda0969 100644 --- a/server/CServerProtocol.h +++ b/server/CServerProtocol.h @@ -33,9 +33,9 @@ class CServerProtocol : public IServerProtocol { virtual void sendClose() = 0; virtual void sendEnter(SInt32 xAbs, SInt32 yAbs) = 0; virtual void sendLeave() = 0; - virtual void sendClipboard(const CString&) = 0; - virtual void sendGrabClipboard() = 0; - virtual void sendQueryClipboard(UInt32 seqNum) = 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 a2e02811..5a97c3e5 100644 --- a/server/CServerProtocol1_0.cpp +++ b/server/CServerProtocol1_0.cpp @@ -103,22 +103,24 @@ void CServerProtocol1_0::sendLeave() CProtocolUtil::writef(getOutputStream(), kMsgCLeave); } -void CServerProtocol1_0::sendClipboard(const CString& data) +void CServerProtocol1_0::sendClipboard( + ClipboardID id, const CString& data) { - log((CLOG_INFO "send clipboard to \"%s\" size=%d", getClient().c_str(), data.size())); - CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, 0, &data); + log((CLOG_INFO "send clipboard %d to \"%s\" size=%d", id, getClient().c_str(), data.size())); + CProtocolUtil::writef(getOutputStream(), kMsgDClipboard, id, 0, &data); } -void CServerProtocol1_0::sendGrabClipboard() +void CServerProtocol1_0::sendGrabClipboard(ClipboardID id) { - log((CLOG_INFO "send grab clipboard to \"%s\"", getClient().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgCClipboard); + log((CLOG_INFO "send grab clipboard %d to \"%s\"", id, getClient().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id); } -void CServerProtocol1_0::sendQueryClipboard(UInt32 seqNum) +void CServerProtocol1_0::sendQueryClipboard( + ClipboardID id, UInt32 seqNum) { - log((CLOG_INFO "query clipboard to \"%s\"", getClient().c_str())); - CProtocolUtil::writef(getOutputStream(), kMsgQClipboard, seqNum); + log((CLOG_INFO "query clipboard %d to \"%s\"", id, getClient().c_str())); + CProtocolUtil::writef(getOutputStream(), kMsgQClipboard, id, seqNum); } void CServerProtocol1_0::sendScreenSaver(bool on) @@ -194,15 +196,18 @@ void CServerProtocol1_0::recvInfo() void CServerProtocol1_0::recvClipboard() { + ClipboardID id; UInt32 seqNum; CString data; - CProtocolUtil::readf(getInputStream(), kMsgDClipboard + 4, &seqNum, &data); - log((CLOG_INFO "received client \"%s\" clipboard seqnum=%d, size=%d", getClient().c_str(), seqNum, data.size())); - getServer()->setClipboard(seqNum, data); + CProtocolUtil::readf(getInputStream(), kMsgDClipboard + 4, &id, &seqNum, &data); + log((CLOG_INFO "received client \"%s\" clipboard %d seqnum=%d, size=%d", getClient().c_str(), id, seqNum, data.size())); + getServer()->setClipboard(id, seqNum, data); } void CServerProtocol1_0::recvGrabClipboard() { - log((CLOG_INFO "received client \"%s\" grabbed clipboard", getClient().c_str())); - getServer()->grabClipboard(getClient()); + ClipboardID id; + CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id); + log((CLOG_INFO "received client \"%s\" grabbed clipboard %d", getClient().c_str(), id)); + getServer()->grabClipboard(id, getClient()); } diff --git a/server/CServerProtocol1_0.h b/server/CServerProtocol1_0.h index 97745298..63c64f73 100644 --- a/server/CServerProtocol1_0.h +++ b/server/CServerProtocol1_0.h @@ -18,9 +18,9 @@ class CServerProtocol1_0 : public CServerProtocol { virtual void sendClose(); virtual void sendEnter(SInt32 xAbs, SInt32 yAbs); virtual void sendLeave(); - virtual void sendClipboard(const CString&); - virtual void sendGrabClipboard(); - virtual void sendQueryClipboard(UInt32 seqNum); + 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/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 2f1efaa9..f1315116 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -137,7 +137,8 @@ void CXWindowsPrimaryScreen::run() // selection owner. report that to the server. if (lostClipboard(xevent.xselectionclear.selection, xevent.xselectionclear.time)) { - m_server->grabClipboard(); + m_server->grabClipboard(getClipboardID( + xevent.xselectionclear.selection)); } break; @@ -270,7 +271,7 @@ void CXWindowsPrimaryScreen::leave() assert(result != GrabNotViewable); if (result != GrabSuccess) { log((CLOG_DEBUG "waiting to grab pointer")); - CThread::sleep(0.25); + CThread::sleep(0.1); } } while (result != GrabSuccess); log((CLOG_DEBUG "grabbed pointer")); @@ -283,7 +284,7 @@ void CXWindowsPrimaryScreen::leave() // back off to avoid grab deadlock XUngrabPointer(display, CurrentTime); log((CLOG_DEBUG "ungrabbed pointer, waiting to grab keyboard")); - CThread::sleep(0.25); + CThread::sleep(0.1); } } while (result != GrabSuccess); log((CLOG_DEBUG "grabbed keyboard")); @@ -323,16 +324,16 @@ void CXWindowsPrimaryScreen::warpCursorNoLock( } void CXWindowsPrimaryScreen::setClipboard( - const IClipboard* clipboard) + ClipboardID id, const IClipboard* clipboard) { // FIXME -- don't use CurrentTime - setDisplayClipboard(clipboard, m_window, CurrentTime); + setDisplayClipboard(id, clipboard, m_window, CurrentTime); } -void CXWindowsPrimaryScreen::grabClipboard() +void CXWindowsPrimaryScreen::grabClipboard(ClipboardID id) { // FIXME -- don't use CurrentTime - setDisplayClipboard(NULL, m_window, CurrentTime); + setDisplayClipboard(id, NULL, m_window, CurrentTime); } void CXWindowsPrimaryScreen::getSize( @@ -347,10 +348,10 @@ SInt32 CXWindowsPrimaryScreen::getJumpZoneSize() const } void CXWindowsPrimaryScreen::getClipboard( - IClipboard* clipboard) const + ClipboardID id, IClipboard* clipboard) const { // FIXME -- don't use CurrentTime - getDisplayClipboard(clipboard, m_window, CurrentTime); + getDisplayClipboard(id, clipboard, m_window, CurrentTime); } void CXWindowsPrimaryScreen::onOpenDisplay() diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 562f9048..1cdc1563 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -19,11 +19,11 @@ class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute); virtual void leave(); virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); - virtual void setClipboard(const IClipboard*); - virtual void grabClipboard(); + virtual void setClipboard(ClipboardID, const IClipboard*); + virtual void grabClipboard(ClipboardID); virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; - virtual void getClipboard(IClipboard*) const; + virtual void getClipboard(ClipboardID, IClipboard*) const; protected: // CXWindowsScreen overrides diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp index e3cfee8f..4aa74a41 100644 --- a/synergy/CXWindowsScreen.cpp +++ b/synergy/CXWindowsScreen.cpp @@ -14,7 +14,6 @@ // CXWindowsScreen // -static const Atom kClipboardSelection = XA_PRIMARY; static const UInt32 kMaxRequestSize = 4096; CXWindowsScreen::CXWindowsScreen() : @@ -66,6 +65,11 @@ void CXWindowsScreen::openDisplay() m_atomText = XInternAtom(m_display, "TEXT", False); m_atomCompoundText = XInternAtom(m_display, "COMPOUND_TEXT", False); + // clipboard atoms + m_atomClipboard[kClipboardClipboard] = + XInternAtom(m_display, "CLIPBOARD", False); + m_atomClipboard[kClipboardSelection] = XA_PRIMARY; + // let subclass prep display onOpenDisplay(); } @@ -78,16 +82,19 @@ void CXWindowsScreen::closeDisplay() onCloseDisplay(); // clear out the clipboard request lists - for (CRequestMap::iterator index = m_requests.begin(); - index != m_requests.end(); ++index) { - CRequestList* list = index->second; - for (CRequestList::iterator index2 = list->begin(); + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + ClipboardInfo& clipboard = m_clipboards[id]; + for (CRequestMap::iterator index = clipboard.m_requests.begin(); + index != clipboard.m_requests.end(); ++index) { + CRequestList* list = index->second; + for (CRequestList::iterator index2 = list->begin(); index2 != list->end(); ++index2) { - delete *index2; + delete *index2; + } + delete list; } - delete list; + clipboard.m_requests.clear(); } - m_requests.clear(); // close the display XCloseDisplay(m_display); @@ -179,39 +186,50 @@ void CXWindowsScreen::doStop() m_stop = true; } +ClipboardID CXWindowsScreen::getClipboardID(Atom selection) +{ + for (ClipboardID id = 0; id < kClipboardEnd; ++id) + if (selection == m_atomClipboard[id]) + return id; + return kClipboardEnd; +} + bool CXWindowsScreen::lostClipboard( Atom selection, Time timestamp) { - if (selection == kClipboardSelection) { - // note the time - CLock lock(&m_mutex); - m_lostClipboard = timestamp; - log((CLOG_INFO "lost clipboard ownership at %d", timestamp)); - return true; + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + if (selection == m_atomClipboard[id]) { + // note the time + CLock lock(&m_mutex); + m_clipboards[id].m_lostClipboard = timestamp; + log((CLOG_INFO "lost clipboard %d ownership at %d", id, timestamp)); + return true; + } } return false; } bool CXWindowsScreen::setDisplayClipboard( + ClipboardID id, const IClipboard* clipboard, Window requestor, Time timestamp) { CLock lock(&m_mutex); - XSetSelectionOwner(m_display, kClipboardSelection, requestor, timestamp); - if (XGetSelectionOwner(m_display, kClipboardSelection) == requestor) { + XSetSelectionOwner(m_display, m_atomClipboard[id], requestor, timestamp); + if (XGetSelectionOwner(m_display, m_atomClipboard[id]) == requestor) { // we got the selection log((CLOG_INFO "grabbed clipboard at %d", timestamp)); - m_gotClipboard = timestamp; + m_clipboards[id].m_gotClipboard = timestamp; if (clipboard != NULL) { // save clipboard to serve requests - CClipboard::copy(&m_clipboard, clipboard); + CClipboard::copy(&m_clipboards[id].m_clipboard, clipboard); } else { // clear clipboard - if (m_clipboard.open()) { - m_clipboard.close(); + if (m_clipboards[id].m_clipboard.open()) { + m_clipboards[id].m_clipboard.close(); } } @@ -222,6 +240,7 @@ bool CXWindowsScreen::setDisplayClipboard( } void CXWindowsScreen::getDisplayClipboard( + ClipboardID id, IClipboard* clipboard, Window requestor, Time timestamp) const { @@ -236,7 +255,7 @@ void CXWindowsScreen::getDisplayClipboard( // in particular, this prevents the event thread from stealing the // selection notify event we're expecting. CLock lock(&m_mutex); - Atom selection = kClipboardSelection; + Atom selection = m_atomClipboard[id]; // ask the selection for all the formats it has. some owners return // the TARGETS atom and some the ATOM atom when TARGETS is requested. @@ -541,10 +560,13 @@ void CXWindowsScreen::addClipboardRequest( Atom selection, Atom target, Atom property, Time time) { - // we can only own kClipboardSelection - if (selection != kClipboardSelection) { + // see if it's a selection we know about + ClipboardID id; + for (id = 0; id < kClipboardEnd; ++id) + if (selection == m_atomClipboard[id]) + break; + if (id == kClipboardEnd) return; - } // mutex the display CLock lock(&m_mutex); @@ -554,16 +576,16 @@ void CXWindowsScreen::addClipboardRequest( if (target == m_atomMultiple) { // add a multiple request if (property != None) { - success = sendClipboardMultiple(requestor, property, time); + success = sendClipboardMultiple(id, requestor, property, time); } } else { // handle remaining request formats - success = sendClipboardData(requestor, target, property, time); + success = sendClipboardData(id, requestor, target, property, time); } // send success or failure - sendNotify(requestor, target, success ? property : None, time); + sendNotify(id, requestor, target, success ? property : None, time); } void CXWindowsScreen::processClipboardRequest( @@ -572,62 +594,71 @@ void CXWindowsScreen::processClipboardRequest( { CLock lock(&m_mutex); - // find the request list - CRequestMap::iterator index = m_requests.find(requestor); - if (index == m_requests.end()) { - return; - } - CRequestList* list = index->second; - assert(list != NULL); + // check every clipboard + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + ClipboardInfo& clipboard = m_clipboards[id]; - // find the property in the list - CRequestList::iterator index2; - for (index2 = list->begin(); index2 != list->end(); ++index2) { - if ((*index2)->m_property == property) { - break; + // find the request list + CRequestMap::iterator index = clipboard.m_requests.find(requestor); + if (index == clipboard.m_requests.end()) { + // this clipboard isn't servicing this requestor window + continue; } - } - if (index2 == list->end()) { - log((CLOG_WARN "received property event on unexpected property")); - return; - } - CClipboardRequest* request = *index2; - assert(request != NULL); + CRequestList* list = index->second; + assert(list != NULL); - // compute amount of data to send - assert(request->m_sent <= request->m_data.size()); - UInt32 count = request->m_data.size() - request->m_sent; - if (count > kMaxRequestSize) { - // limit maximum chunk size - count = kMaxRequestSize; + // find the property in the list + CRequestList::iterator index2; + for (index2 = list->begin(); index2 != list->end(); ++index2) { + if ((*index2)->m_property == property) { + break; + } + } + if (index2 == list->end()) { + // this clipboard isn't waiting on this property + continue; + } + CClipboardRequest* request = *index2; + assert(request != NULL); - // make it a multiple of the size - count &= ~((request->m_size >> 3) - 1); - } + // compute amount of data to send + assert(request->m_sent <= request->m_data.size()); + UInt32 count = request->m_data.size() - request->m_sent; + if (count > kMaxRequestSize) { + // limit maximum chunk size + count = kMaxRequestSize; - // send more data - // FIXME -- handle Alloc errors (by returning false) - XChangeProperty(m_display, request->m_requestor, request->m_property, + // make it a multiple of the size + count &= ~((request->m_size >> 3) - 1); + } + + // send more data + // FIXME -- handle Alloc errors (by returning false) + XChangeProperty(m_display, request->m_requestor, request->m_property, request->m_type, request->m_size, PropModeReplace, reinterpret_cast( request->m_data.data() + request->m_sent), count / (request->m_size >> 3)); - // account for sent data - request->m_sent += count; + // account for sent data + request->m_sent += count; - // if we sent zero bytes then we're done sending this data. remove - // it from the list and, if the list is empty, the list from the - // map. also stop watching the requestor for events. - if (count == 0) { - list->erase(index2); - delete request; - if (list->empty()) { - m_requests.erase(index); - delete list; + // if we sent zero bytes then we're done sending this data. remove + // it from the list and, if the list is empty, the list from the + // map. also stop watching the requestor for events. + if (count == 0) { + list->erase(index2); + delete request; + if (list->empty()) { + clipboard.m_requests.erase(index); + delete list; + } + XSelectInput(m_display, requestor, NoEventMask); } - XSelectInput(m_display, requestor, NoEventMask); + + // request has been serviced + break; } } @@ -636,37 +667,43 @@ void CXWindowsScreen::destroyClipboardRequest( { CLock lock(&m_mutex); - // find the request list - CRequestMap::iterator index = m_requests.find(requestor); - if (index == m_requests.end()) { - return; - } - CRequestList* list = index->second; - assert(list != NULL); + // check every clipboard + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + ClipboardInfo& clipboard = m_clipboards[id]; - // destroy every request in the list - for (CRequestList::iterator index2 = list->begin(); + // find the request list + CRequestMap::iterator index = clipboard.m_requests.find(requestor); + if (index == clipboard.m_requests.end()) { + continue; + } + CRequestList* list = index->second; + assert(list != NULL); + + // destroy every request in the list + for (CRequestList::iterator index2 = list->begin(); index2 != list->end(); ++index2) { - delete *index2; - } + delete *index2; + } - // remove and destroy the list - m_requests.erase(index); - delete list; + // remove and destroy the list + clipboard.m_requests.erase(index); + delete list; + } // note -- we don't stop watching the window for events because // we're called in response to the window being destroyed. } bool CXWindowsScreen::sendClipboardData( + ClipboardID id, Window requestor, Atom target, Atom property, Time time) { if (target == m_atomTargets) { - return sendClipboardTargets(requestor, property, time); + return sendClipboardTargets(id, requestor, property, time); } else if (target == m_atomTimestamp) { - return sendClipboardTimestamp(requestor, property, time); + return sendClipboardTimestamp(id, requestor, property, time); } else { // compute the type and size for the requested target and @@ -675,10 +712,10 @@ bool CXWindowsScreen::sendClipboardData( int size = 0; CString data; if (target == m_atomText || target == m_atomString) { - if (m_clipboard.has(IClipboard::kText)) { + if (m_clipboards[id].m_clipboard.has(IClipboard::kText)) { type = m_atomString; size = 8; - data = m_clipboard.get(IClipboard::kText); + data = m_clipboards[id].m_clipboard.get(IClipboard::kText); } } @@ -691,10 +728,10 @@ bool CXWindowsScreen::sendClipboardData( log((CLOG_DEBUG "handling clipboard request for %d as INCR", target)); // get the appropriate list, creating it if necessary - CRequestList* list = m_requests[requestor]; + CRequestList* list = m_clipboards[id].m_requests[requestor]; if (list == NULL) { list = new CRequestList; - m_requests[requestor] = list; + m_clipboards[id].m_requests[requestor] = list; } // create request object @@ -718,7 +755,8 @@ bool CXWindowsScreen::sendClipboardData( // set property to INCR const UInt32 zero = 0; XChangeProperty(m_display, requestor, property, - m_atomINCR, 8 * sizeof(zero), + m_atomINCR, + 8 * sizeof(zero), PropModeReplace, reinterpret_cast(&zero), 1); @@ -730,7 +768,8 @@ bool CXWindowsScreen::sendClipboardData( XChangeProperty(m_display, requestor, property, type, size, PropModeReplace, - reinterpret_cast(data.data()), + reinterpret_cast( + data.data()), data.size() / (size >> 3)); } return true; @@ -739,6 +778,7 @@ bool CXWindowsScreen::sendClipboardData( } bool CXWindowsScreen::sendClipboardMultiple( + ClipboardID id, Window requestor, Atom property, Time time) { @@ -768,7 +808,7 @@ bool CXWindowsScreen::sendClipboardMultiple( // handle target if (property != None) { - if (!sendClipboardData(requestor, target, property, time)) { + if (!sendClipboardData(id, requestor, target, property, time)) { // couldn't handle target. change property to None. const Atom none = None; data.replace((2 * index + 1) * sizeof(Atom), sizeof(Atom), @@ -786,15 +826,18 @@ bool CXWindowsScreen::sendClipboardMultiple( if (updated) { // FIXME -- handle Alloc errors (by returning false) XChangeProperty(m_display, requestor, property, - m_atomAtomPair, 8 * sizeof(Atom), + m_atomAtomPair, + 8 * sizeof(Atom), PropModeReplace, - reinterpret_cast(data.data()), + reinterpret_cast( + data.data()), data.length()); } // send notify if any format was successful if (success) { - sendNotify(requestor, m_atomMultiple, success ? property : None, time); + sendNotify(id, requestor, m_atomMultiple, + success ? property : None, time); return true; } @@ -802,6 +845,7 @@ bool CXWindowsScreen::sendClipboardMultiple( } bool CXWindowsScreen::sendClipboardTargets( + ClipboardID id, Window requestor, Atom property, Time /*time*/) { @@ -809,7 +853,7 @@ bool CXWindowsScreen::sendClipboardTargets( // count the number of targets, plus TARGETS and MULTIPLE SInt32 numTargets = 2; - if (m_clipboard.has(IClipboard::kText)) { + if (m_clipboards[id].m_clipboard.has(IClipboard::kText)) { numTargets += 2; } @@ -818,7 +862,7 @@ bool CXWindowsScreen::sendClipboardTargets( SInt32 count = 0; response[count++] = m_atomTargets; response[count++] = m_atomMultiple; - if (m_clipboard.has(IClipboard::kText)) { + if (m_clipboards[id].m_clipboard.has(IClipboard::kText)) { response[count++] = m_atomText; response[count++] = m_atomString; } @@ -826,7 +870,8 @@ bool CXWindowsScreen::sendClipboardTargets( // send response (we assume we can transfer the entire list at once) // FIXME -- handle Alloc errors (by returning false) XChangeProperty(m_display, requestor, property, - m_atomAtom, 8 * sizeof(Atom), + m_atomAtom, + 8 * sizeof(Atom), PropModeReplace, reinterpret_cast(response), count); @@ -838,6 +883,7 @@ bool CXWindowsScreen::sendClipboardTargets( } bool CXWindowsScreen::sendClipboardTimestamp( + ClipboardID id, Window requestor, Atom property, Time /*time*/) { @@ -845,22 +891,24 @@ bool CXWindowsScreen::sendClipboardTimestamp( // FIXME -- handle Alloc errors (by returning false) XChangeProperty(m_display, requestor, property, - m_atomInteger, 8 * sizeof(m_gotClipboard), + m_atomInteger, + 8 * sizeof(m_clipboards[id].m_gotClipboard), PropModeReplace, - reinterpret_cast(&m_gotClipboard), + reinterpret_cast( + &m_clipboards[id].m_gotClipboard), 1); return true; } void CXWindowsScreen::sendNotify( - Window requestor, Atom target, - Atom property, Time time) + ClipboardID id, Window requestor, + Atom target, Atom property, Time time) { XEvent event; event.xselection.type = SelectionNotify; event.xselection.display = m_display; event.xselection.requestor = requestor; - event.xselection.selection = kClipboardSelection; + event.xselection.selection = m_atomClipboard[id]; event.xselection.target = target; event.xselection.property = property; event.xselection.time = time; diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h index 0166a473..2e61e5f4 100644 --- a/synergy/CXWindowsScreen.h +++ b/synergy/CXWindowsScreen.h @@ -4,6 +4,7 @@ #include "CClipboard.h" #include "CMutex.h" #include "BasicTypes.h" +#include "ClipboardTypes.h" #include #include #include @@ -56,19 +57,25 @@ class CXWindowsScreen { // cause getEvent() to return false immediately and forever after void doStop(); + // determine the clipboard from the X selection. returns + // kClipboardEnd if no such clipboard. + ClipboardID getClipboardID(Atom selection); + // call when we lose the clipboard ownership (i.e. when we receive // a SelectionClear event). returns true iff we've actually lost // a selection we care about. bool lostClipboard(Atom selection, Time timestamp); // set the contents of the clipboard (i.e. primary selection) - bool setDisplayClipboard(const IClipboard* clipboard, + bool setDisplayClipboard(ClipboardID, + const IClipboard* clipboard, Window requestor, Time timestamp); // 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. - void getDisplayClipboard(IClipboard* clipboard, + void getDisplayClipboard(ClipboardID, + IClipboard* clipboard, Window requestor, Time timestamp) const; // add a selection request to the request list @@ -120,18 +127,31 @@ class CXWindowsScreen { static Bool findPropertyNotify(Display*, XEvent* xevent, XPointer arg); - bool sendClipboardData(Window requestor, Atom target, + bool sendClipboardData(ClipboardID, Window requestor, + Atom target, Atom property, Time time); + bool sendClipboardMultiple(ClipboardID, Window requestor, Atom property, Time time); - bool sendClipboardMultiple(Window requestor, + bool sendClipboardTargets(ClipboardID, Window requestor, Atom property, Time time); - bool sendClipboardTargets(Window requestor, - Atom property, Time time); - bool sendClipboardTimestamp(Window requestor, - Atom property, Time time); - void sendNotify(Window requestor, Atom target, + bool sendClipboardTimestamp(ClipboardID, Window requestor, Atom property, Time time); + void sendNotify(ClipboardID, Window requestor, + Atom target, Atom property, Time time); private: + class ClipboardInfo { + public: + // the contents of the clipboard + CClipboard m_clipboard; + + // when we got the clipboard and when we lost it + Time m_gotClipboard; + Time m_lostClipboard; + + // the request queues + CRequestMap m_requests; + }; + Display* m_display; int m_screen; Window m_root; @@ -150,7 +170,11 @@ class CXWindowsScreen { Atom m_atomString; Atom m_atomText; Atom m_atomCompoundText; + Atom m_atomClipboard[kClipboardEnd]; + // clipboard info + ClipboardInfo m_clipboards[kClipboardEnd]; +/* // the contents of our selection CClipboard m_clipboard; @@ -160,6 +184,7 @@ class CXWindowsScreen { // the request queues CRequestMap m_requests; +*/ // X is not thread safe CMutex m_mutex; diff --git a/synergy/ClipboardTypes.h b/synergy/ClipboardTypes.h new file mode 100644 index 00000000..63c44c91 --- /dev/null +++ b/synergy/ClipboardTypes.h @@ -0,0 +1,21 @@ +#ifndef CLIPBOARDTYPES_H +#define CLIPBOARDTYPES_H + +#include "BasicTypes.h" + +// type to hold a clipboard identifier +typedef UInt8 ClipboardID; + +// clipboard identifiers. kClipboardClipboard is what is normally +// considered the clipboard (e.g. the cut/copy/paste menu items +// affect it). kClipboardSelection is the selection on those +// platforms that can treat the selection as a clipboard (e.g. X +// windows). clipboard identifiers must be sequential starting +// at zero. +static const ClipboardID kClipboardClipboard = 0; +static const ClipboardID kClipboardSelection = 1; + +// the number of clipboards (i.e. one greater than the last clipboard id) +static const ClipboardID kClipboardEnd = 2; + +#endif diff --git a/synergy/IPrimaryScreen.h b/synergy/IPrimaryScreen.h index 92572e33..a398f226 100644 --- a/synergy/IPrimaryScreen.h +++ b/synergy/IPrimaryScreen.h @@ -3,6 +3,7 @@ #include "IInterface.h" #include "BasicTypes.h" +#include "ClipboardTypes.h" class CServer; class IClipboard; @@ -49,7 +50,7 @@ class IPrimaryScreen : public IInterface { // set the screen's clipboard contents. this is usually called // soon after an enter(). - virtual void setClipboard(const IClipboard*) = 0; + virtual void setClipboard(ClipboardID, const IClipboard*) = 0; /* // show or hide the screen saver @@ -57,7 +58,7 @@ class IPrimaryScreen : public IInterface { */ // synergy should own the clipboard - virtual void grabClipboard() = 0; + virtual void grabClipboard(ClipboardID) = 0; // accessors @@ -75,7 +76,7 @@ class IPrimaryScreen : public IInterface { virtual SInt32 getJumpZoneSize() const = 0; // get the screen's clipboard contents - virtual void getClipboard(IClipboard*) const = 0; + virtual void getClipboard(ClipboardID, IClipboard*) const = 0; }; #endif diff --git a/synergy/ISecondaryScreen.h b/synergy/ISecondaryScreen.h index 2e886fd5..9599d25c 100644 --- a/synergy/ISecondaryScreen.h +++ b/synergy/ISecondaryScreen.h @@ -3,6 +3,7 @@ #include "IInterface.h" #include "BasicTypes.h" +#include "ClipboardTypes.h" #include "KeyTypes.h" #include "MouseTypes.h" @@ -52,7 +53,7 @@ class ISecondaryScreen : public IInterface { // set the screen's clipboard contents. this is usually called // soon after an enter(). - virtual void setClipboard(const IClipboard*) = 0; + virtual void setClipboard(ClipboardID, const IClipboard*) = 0; /* // show or hide the screen saver @@ -60,7 +61,7 @@ class ISecondaryScreen : public IInterface { */ // take ownership of clipboard - virtual void grabClipboard() = 0; + virtual void grabClipboard(ClipboardID) = 0; // accessors @@ -71,7 +72,7 @@ class ISecondaryScreen : public IInterface { virtual SInt32 getJumpZoneSize() const = 0; // get the screen's clipboard contents - virtual void getClipboard(IClipboard*) const = 0; + virtual void getClipboard(ClipboardID, IClipboard*) const = 0; }; #endif diff --git a/synergy/IServerProtocol.h b/synergy/IServerProtocol.h index ecd396be..49a38898 100644 --- a/synergy/IServerProtocol.h +++ b/synergy/IServerProtocol.h @@ -1,6 +1,7 @@ #ifndef ISERVERPROTOCOL_H #define ISERVERPROTOCOL_H +#include "ClipboardTypes.h" #include "KeyTypes.h" #include "MouseTypes.h" #include "IInterface.h" @@ -25,9 +26,9 @@ class IServerProtocol : public IInterface { virtual void sendClose() = 0; virtual void sendEnter(SInt32 xAbs, SInt32 yAbs) = 0; virtual void sendLeave() = 0; - virtual void sendClipboard(const CString&) = 0; - virtual void sendGrabClipboard() = 0; - virtual void sendQueryClipboard(UInt32 seqNum) = 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 c806ae3a..0e70502c 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -7,30 +7,101 @@ static const SInt32 kMajorVersion = 0; static const SInt32 kMinorVersion = 1; -// message codes (trailing NUL is not part of code). codes are -// grouped into: -// commands -- request an action, no reply expected -// queries -- request info -// data -- send info -// errors -- notify of error -static const char kMsgCClose[] = "CBYE"; // server -static const char kMsgCEnter[] = "CINN%2i%2i"; // server -static const char kMsgCLeave[] = "COUT"; // server -static const char kMsgCClipboard[] = "CCLP"; // server/client -static const char kMsgCScreenSaver[] = "CSEC%1i"; // server +// +// message codes (trailing NUL is not part of code). in comments, $n +// refers to the n'th argument (counting from one). message codes are +// always 4 bytes optionally followed by message specific parameters. +// -static const char kMsgDKeyDown[] = "DKDN%2i%2i"; // server -static const char kMsgDKeyRepeat[] = "DKRP%2i%2i%2i"; // server -static const char kMsgDKeyUp[] = "DKUP%2i%2i"; // server -static const char kMsgDMouseDown[] = "DMDN%1i"; // server -static const char kMsgDMouseUp[] = "DMUP%1i"; // server -static const char kMsgDMouseMove[] = "DMMV%2i%2i"; // server -static const char kMsgDMouseWheel[] = "DMWM%2i"; // server -static const char kMsgDClipboard[] = "DCLP%4i%s"; // server/client -static const char kMsgDInfo[] = "DINF%2i%2i%2i"; // client +// +// command codes +// -static const char kMsgQClipboard[] = "QCLP%4i"; // server -static const char kMsgQInfo[] = "QINF"; // server +// close connection; primary -> secondary +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"; + +// leave screen: primary -> secondary +// leaving screen +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"; + +// screensaver change: primary -> secondary +// screensaver on primary has started ($1 == 1) or closed ($1 == 0) +static const char kMsgCScreenSaver[] = "CSEC%1i"; + + +// +// data codes +// + +// key pressed: primary -> secondary +// $1 = KeyID, $2 = KeyModifierMask +static const char kMsgDKeyDown[] = "DKDN%2i%2i"; + +// key auto-repeat: primary -> secondary +// $1 = KeyID, $2 = KeyModifierMask, $3 = number of repeats +static const char kMsgDKeyRepeat[] = "DKRP%2i%2i%2i"; + +// key released: primary -> secondary +// $1 = KeyID, $2 = KeyModifierMask +static const char kMsgDKeyUp[] = "DKUP%2i%2i"; + +// mouse button pressed: primary -> secondary +// $1 = ButtonID +static const char kMsgDMouseDown[] = "DMDN%1i"; + +// mouse button released: primary -> secondary +// $1 = ButtonID +static const char kMsgDMouseUp[] = "DMUP%1i"; + +// mouse moved: primary -> secondary +// $1 = x, $2 = y. x,y are absolute screen coordinates. +static const char kMsgDMouseMove[] = "DMMV%2i%2i"; + +// mouse button pressed: primary -> secondary +// $1 = delta +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. +static const char kMsgDClipboard[] = "DCLP%1i%4i%s"; + +// client data: seconary -> primary +// $1 = seconary screen width in pixels, $2 = screen height, $3 = +// size of warp zone. +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"; + + +// +// error codes +// static const char kMsgEIncompatible[] = "EICV";