checkpoint. changed protocol to better handle clipboards. now

sending a sequence number with enter messages.  screens use that
sequence number in clipboard grab and data messages.  the server
uses the sequence number to order messages across clients.  also
changed secondary screens to send clipboard updates on leaving
(or when grab occurs when not active) instead of on a query from
the server.  primary effectively does the same.  the query
message has been removed.
This commit is contained in:
crs 2002-04-29 13:31:44 +00:00
parent 3be014f8f5
commit c5f6b34d85
15 changed files with 325 additions and 163 deletions

View File

@ -30,7 +30,9 @@ CClient::CClient(const CString& clientName) :
m_name(clientName), m_name(clientName),
m_input(NULL), m_input(NULL),
m_output(NULL), m_output(NULL),
m_screen(NULL) m_screen(NULL),
m_active(false),
m_seqNum(0)
{ {
// do nothing // do nothing
} }
@ -92,7 +94,29 @@ void CClient::onClipboardChanged(ClipboardID id)
if (m_output != NULL) { if (m_output != NULL) {
// m_output can be NULL if the screen calls this method // m_output can be NULL if the screen calls this method
// before we've gotten around to connecting to the server. // 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) { else if (memcmp(code, kMsgQInfo, 4) == 0) {
onQueryInfo(); onQueryInfo();
} }
else if (memcmp(code, kMsgQClipboard, 4) == 0) {
onQueryClipboard();
}
else if (memcmp(code, kMsgDClipboard, 4) == 0) { else if (memcmp(code, kMsgDClipboard, 4) == 0) {
onSetClipboard(); onSetClipboard();
} }
@ -271,6 +292,18 @@ void CClient::openSecondaryScreen()
{ {
assert(m_screen == NULL); 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 // open screen
log((CLOG_DEBUG1 "creating secondary screen")); log((CLOG_DEBUG1 "creating secondary screen"));
#if defined(CONFIG_PLATFORM_WIN32) #if defined(CONFIG_PLATFORM_WIN32)
@ -306,22 +339,61 @@ void CClient::onEnter()
SInt16 x, y; SInt16 x, y;
{ {
CLock lock(&m_mutex); 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); m_screen->enter(x, y);
} }
void CClient::onLeave() void CClient::onLeave()
{ {
// tell screen we're leaving
m_screen->leave(); 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() void CClient::onGrabClipboard()
{ {
ClipboardID id; ClipboardID id;
UInt32 seqNum;
{ {
CLock lock(&m_mutex); 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); m_screen->grabClipboard(id);
} }
@ -347,32 +419,6 @@ void CClient::onQueryInfo()
CProtocolUtil::writef(m_output, kMsgDInfo, w, h, zoneSize); 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() void CClient::onSetClipboard()
{ {
ClipboardID id; ClipboardID id;
@ -387,7 +433,7 @@ void CClient::onSetClipboard()
// unmarshall // unmarshall
CClipboard clipboard; CClipboard clipboard;
clipboard.unmarshall(data); clipboard.unmarshall(data, 0);
// set screen's clipboard // set screen's clipboard
m_screen->setClipboard(id, &clipboard); m_screen->setClipboard(id, &clipboard);

View File

@ -5,6 +5,7 @@
#include "CString.h" #include "CString.h"
#include "BasicTypes.h" #include "BasicTypes.h"
#include "ClipboardTypes.h" #include "ClipboardTypes.h"
#include "IClipboard.h"
class CNetworkAddress; class CNetworkAddress;
class IInputStream; class IInputStream;
@ -39,7 +40,6 @@ class CClient {
void onGrabClipboard(); void onGrabClipboard();
void onScreenSaver(); void onScreenSaver();
void onQueryInfo(); void onQueryInfo();
void onQueryClipboard();
void onSetClipboard(); void onSetClipboard();
void onKeyDown(); void onKeyDown();
void onKeyRepeat(); void onKeyRepeat();
@ -56,6 +56,10 @@ class CClient {
IOutputStream* m_output; IOutputStream* m_output;
ISecondaryScreen* m_screen; ISecondaryScreen* m_screen;
const CNetworkAddress* m_serverAddress; const CNetworkAddress* m_serverAddress;
bool m_active;
UInt32 m_seqNum;
bool m_ownClipboard[kClipboardEnd];
IClipboard::Time m_timeClipboard[kClipboardEnd];
}; };
#endif #endif

View File

@ -43,7 +43,8 @@ else { wait(0); exit(1); }
CServer::CServer() : m_primary(NULL), CServer::CServer() : m_primary(NULL),
m_active(NULL), m_active(NULL),
m_primaryInfo(NULL) m_primaryInfo(NULL),
m_seqNum(0)
{ {
m_socketFactory = NULL; m_socketFactory = NULL;
m_securityFactory = NULL; m_securityFactory = NULL;
@ -94,6 +95,11 @@ void CServer::run()
} }
} }
void CServer::quit()
{
m_primary->stop();
}
void CServer::setScreenMap(const CScreenMap& screenMap) void CServer::setScreenMap(const CScreenMap& screenMap)
{ {
CLock lock(&m_mutex); CLock lock(&m_mutex);
@ -150,35 +156,43 @@ void CServer::setInfo(const CString& client,
void CServer::grabClipboard(ClipboardID id) void CServer::grabClipboard(ClipboardID id)
{ {
grabClipboard(id, m_primaryInfo->m_name); grabClipboard(id, 0, m_primaryInfo->m_name);
} }
void CServer::grabClipboard( void CServer::grabClipboard(
ClipboardID id, const CString& client) ClipboardID id, UInt32 seqNum,
const CString& client)
{ {
CLock lock(&m_mutex); 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); CScreenList::iterator index = m_screens.find(client);
if (index == m_screens.end()) { if (index == m_screens.end()) {
throw XBadClient(); 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())); log((CLOG_NOTE "client \"%s\" grabbed clipboard %d from \"%s\"", client.c_str(), id, clipboard.m_clipboardOwner.c_str()));
// save the clipboard owner
clipboard.m_clipboardOwner = client; clipboard.m_clipboardOwner = client;
clipboard.m_clipboardSeqNum = seqNum;
// mark client as having the clipboard data // no screens have the new clipboard except the sender
clearGotClipboard(id);
index->second->m_gotClipboard[id] = true; index->second->m_gotClipboard[id] = true;
// tell all other clients to take ownership of clipboard and mark // tell all other screens to take ownership of clipboard
// them as not having the data yet.
for (index = m_screens.begin(); index != m_screens.end(); ++index) { for (index = m_screens.begin(); index != m_screens.end(); ++index) {
if (index->first != client) { if (index->first != client) {
CScreenInfo* info = index->second; CScreenInfo* info = index->second;
info->m_gotClipboard[id] = false;
if (info->m_protocol == NULL) { if (info->m_protocol == NULL) {
m_primary->grabClipboard(id); m_primary->grabClipboard(id);
} }
@ -188,11 +202,8 @@ void CServer::grabClipboard(
} }
} }
// increment the clipboard sequence number so we can identify the // get the clipboard data if primary has it, otherwise mark the
// clipboard query's response. // clipboard data as unknown.
++clipboard.m_clipboardSeqNum;
// begin getting the clipboard data
if (m_active->m_protocol == NULL) { if (m_active->m_protocol == NULL) {
// get clipboard immediately from primary screen // get clipboard immediately from primary screen
m_primary->getClipboard(id, &clipboard.m_clipboard); m_primary->getClipboard(id, &clipboard.m_clipboard);
@ -201,43 +212,42 @@ void CServer::grabClipboard(
} }
else { else {
// clear out the clipboard since existing data is now out of date. // 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_clipboard.close();
} }
clipboard.m_clipboardReady = false; 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, void CServer::setClipboard(ClipboardID id,
UInt32 seqNum, const CString& data) UInt32 seqNum, const CString& data)
{ {
// update the clipboard if the sequence number matches
CLock lock(&m_mutex); CLock lock(&m_mutex);
ClipboardInfo& clipboard = m_clipboards[id]; CClipboardInfo& 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;
// if the active client doesn't have the clipboard data // ignore update if sequence number is old
// (and it won't unless the client is the one sending us if (seqNum < clipboard.m_clipboardSeqNum) {
// the data) then send the data now. log((CLOG_INFO "ignored client \"%s\" update of clipboard %d", clipboard.m_clipboardOwner.c_str(), id));
if (!m_active->m_gotClipboard[id]) { return;
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;
} }
// 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*/, 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 // since that's a waste of time we skip that and just warp the
// mouse. // mouse.
if (m_active != dst) { 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 // leave active screen
if (m_active->m_protocol == NULL) { if (leavingPrimary) {
m_primary->leave(); m_primary->leave();
} }
else { else {
@ -503,27 +524,20 @@ void CServer::switchScreen(CScreenInfo* dst,
// cut over // cut over
m_active = dst; m_active = dst;
// increment enter sequence number
++m_seqNum;
// enter new screen // enter new screen
if (m_active->m_protocol == NULL) { if (m_active->m_protocol == NULL) {
m_primary->enter(x, y); m_primary->enter(x, y);
} }
else { 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) { for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
ClipboardInfo& clipboard = m_clipboards[id]; sendClipboard(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 { 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 // FIXME -- use factory to create screen
@ -904,6 +966,9 @@ void CServer::openPrimaryScreen()
{ {
assert(m_primary == NULL); assert(m_primary == NULL);
// reset sequence number
m_seqNum = 0;
// open screen // open screen
log((CLOG_DEBUG1 "creating primary screen")); log((CLOG_DEBUG1 "creating primary screen"));
#if defined(CONFIG_PLATFORM_WIN32) #if defined(CONFIG_PLATFORM_WIN32)
@ -927,7 +992,7 @@ void CServer::openPrimaryScreen()
// set the clipboard owner to the primary screen and then get the // set the clipboard owner to the primary screen and then get the
// current clipboard data. // current clipboard data.
for (ClipboardID id = 0; id < kClipboardEnd; ++id) { for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
ClipboardInfo& clipboard = m_clipboards[id]; CClipboardInfo& clipboard = m_clipboards[id];
m_primary->getClipboard(id, &clipboard.m_clipboard); m_primary->getClipboard(id, &clipboard.m_clipboard);
clipboard.m_clipboardData = clipboard.m_clipboard.marshall(); clipboard.m_clipboardData = clipboard.m_clipboard.marshall();
clipboard.m_clipboardReady = true; clipboard.m_clipboardReady = true;
@ -1098,10 +1163,10 @@ CServer::CScreenInfo::~CScreenInfo()
// //
// CServer::ClipboardInfo // CServer::CClipboardInfo
// //
CServer::ClipboardInfo::ClipboardInfo() : CServer::CClipboardInfo::CClipboardInfo() :
m_clipboard(), m_clipboard(),
m_clipboardData(), m_clipboardData(),
m_clipboardOwner(), m_clipboardOwner(),

View File

@ -49,7 +49,8 @@ class CServer {
// handle messages from clients // handle messages from clients
void setInfo(const CString& clientName, void setInfo(const CString& clientName,
SInt32 w, SInt32 h, SInt32 zoneSize); SInt32 w, SInt32 h, SInt32 zoneSize);
void grabClipboard(ClipboardID, const CString& clientName); void grabClipboard(ClipboardID,
UInt32 seqNum, const CString& clientName);
void setClipboard(ClipboardID, void setClipboard(ClipboardID,
UInt32 seqNum, const CString& data); UInt32 seqNum, const CString& data);
@ -126,6 +127,15 @@ class CServer {
void openPrimaryScreen(); void openPrimaryScreen();
void closePrimaryScreen(); 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 // cancel running threads
void cleanupThreads(); void cleanupThreads();
@ -148,9 +158,9 @@ class CServer {
private: private:
typedef std::list<CThread*> CThreadList; typedef std::list<CThread*> CThreadList;
typedef std::map<CString, CScreenInfo*> CScreenList; typedef std::map<CString, CScreenInfo*> CScreenList;
class ClipboardInfo { class CClipboardInfo {
public: public:
ClipboardInfo(); CClipboardInfo();
public: public:
CClipboard m_clipboard; CClipboard m_clipboard;
@ -174,11 +184,14 @@ class CServer {
CScreenInfo* m_active; CScreenInfo* m_active;
CScreenInfo* m_primaryInfo; CScreenInfo* m_primaryInfo;
// the sequence number of enter messages
UInt32 m_seqNum;
SInt32 m_x, m_y; SInt32 m_x, m_y;
CScreenMap m_screenMap; CScreenMap m_screenMap;
ClipboardInfo m_clipboards[kClipboardEnd]; CClipboardInfo m_clipboards[kClipboardEnd];
}; };
#endif #endif

View File

@ -31,11 +31,10 @@ class CServerProtocol : public IServerProtocol {
virtual void run() = 0; virtual void run() = 0;
virtual void queryInfo() = 0; virtual void queryInfo() = 0;
virtual void sendClose() = 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 sendLeave() = 0;
virtual void sendClipboard(ClipboardID, const CString&) = 0; virtual void sendClipboard(ClipboardID, const CString&) = 0;
virtual void sendGrabClipboard(ClipboardID) = 0; virtual void sendGrabClipboard(ClipboardID) = 0;
virtual void sendQueryClipboard(ClipboardID, UInt32 seqNum) = 0;
virtual void sendScreenSaver(bool on) = 0; virtual void sendScreenSaver(bool on) = 0;
virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; virtual void sendKeyDown(KeyID, KeyModifierMask) = 0;
virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0;

View File

@ -91,10 +91,10 @@ void CServerProtocol1_0::sendClose()
} }
void CServerProtocol1_0::sendEnter( 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)); log((CLOG_DEBUG1 "send enter to \"%s\", %d,%d %d", getClient().c_str(), xAbs, yAbs, seqNum));
CProtocolUtil::writef(getOutputStream(), kMsgCEnter, xAbs, yAbs); CProtocolUtil::writef(getOutputStream(), kMsgCEnter, xAbs, yAbs, seqNum);
} }
void CServerProtocol1_0::sendLeave() void CServerProtocol1_0::sendLeave()
@ -113,14 +113,7 @@ void CServerProtocol1_0::sendClipboard(
void CServerProtocol1_0::sendGrabClipboard(ClipboardID id) void CServerProtocol1_0::sendGrabClipboard(ClipboardID id)
{ {
log((CLOG_DEBUG "send grab clipboard %d to \"%s\"", id, getClient().c_str())); log((CLOG_DEBUG "send grab clipboard %d to \"%s\"", id, getClient().c_str()));
CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id); CProtocolUtil::writef(getOutputStream(), kMsgCClipboard, id, 0);
}
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);
} }
void CServerProtocol1_0::sendScreenSaver(bool on) void CServerProtocol1_0::sendScreenSaver(bool on)
@ -207,7 +200,8 @@ void CServerProtocol1_0::recvClipboard()
void CServerProtocol1_0::recvGrabClipboard() void CServerProtocol1_0::recvGrabClipboard()
{ {
ClipboardID id; ClipboardID id;
CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id); UInt32 seqNum;
log((CLOG_DEBUG "received client \"%s\" grabbed clipboard %d", getClient().c_str(), id)); CProtocolUtil::readf(getInputStream(), kMsgCClipboard + 4, &id, &seqNum);
getServer()->grabClipboard(id, getClient()); log((CLOG_DEBUG "received client \"%s\" grabbed clipboard %d seqnum=%d", getClient().c_str(), id, seqNum));
getServer()->grabClipboard(id, seqNum, getClient());
} }

View File

@ -16,11 +16,10 @@ class CServerProtocol1_0 : public CServerProtocol {
virtual void run(); virtual void run();
virtual void queryInfo(); virtual void queryInfo();
virtual void sendClose(); virtual void sendClose();
virtual void sendEnter(SInt32 xAbs, SInt32 yAbs); virtual void sendEnter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum);
virtual void sendLeave(); virtual void sendLeave();
virtual void sendClipboard(ClipboardID, const CString&); virtual void sendClipboard(ClipboardID, const CString&);
virtual void sendGrabClipboard(ClipboardID); virtual void sendGrabClipboard(ClipboardID);
virtual void sendQueryClipboard(ClipboardID, UInt32 seqNum);
virtual void sendScreenSaver(bool on); virtual void sendScreenSaver(bool on);
virtual void sendKeyDown(KeyID, KeyModifierMask); virtual void sendKeyDown(KeyID, KeyModifierMask);
virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count); virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count);

View File

@ -6,7 +6,8 @@
CClipboard::CClipboard() CClipboard::CClipboard()
{ {
// do nothing open(0);
close();
} }
CClipboard::~CClipboard() CClipboard::~CClipboard()
@ -14,13 +15,17 @@ CClipboard::~CClipboard()
// do nothing // do nothing
} }
bool CClipboard::open() bool CClipboard::open(Time time)
{ {
// clear all data // clear all data
for (SInt32 index = 0; index < kNumFormats; ++index) { for (SInt32 index = 0; index < kNumFormats; ++index) {
m_data[index] = ""; m_data[index] = "";
m_added[index] = false; m_added[index] = false;
} }
// save time
m_time = time;
return true; return true;
} }
@ -35,6 +40,11 @@ void CClipboard::add(EFormat format, const CString& data)
m_added[format] = true; m_added[format] = true;
} }
CClipboard::Time CClipboard::getTime() const
{
return m_time;
}
bool CClipboard::has(EFormat format) const bool CClipboard::has(EFormat format) const
{ {
return m_added[format]; return m_added[format];
@ -47,7 +57,19 @@ CString CClipboard::get(EFormat format) const
void CClipboard::copy(IClipboard* dst, const IClipboard* src) 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) { for (SInt32 format = 0; format != IClipboard::kNumFormats; ++format) {
IClipboard::EFormat eFormat = (IClipboard::EFormat)format; IClipboard::EFormat eFormat = (IClipboard::EFormat)format;
if (src->has(eFormat)) { 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(); const char* index = data.data();
// clear existing data // clear existing data
open(); open(time);
// read the number of formats // read the number of formats
const UInt32 numFormats = readUInt32(index); const UInt32 numFormats = readUInt32(index);

View File

@ -16,7 +16,7 @@ class CClipboard : public IClipboard {
// manipulators // manipulators
// unmarshall clipboard data // unmarshall clipboard data
void unmarshall(const CString& data); void unmarshall(const CString& data, Time);
// accessors // accessors
@ -24,9 +24,10 @@ class CClipboard : public IClipboard {
CString marshall() const; CString marshall() const;
// IClipboard overrides // IClipboard overrides
virtual bool open(); virtual bool open(Time);
virtual void close(); virtual void close();
virtual void add(EFormat, const CString& data); virtual void add(EFormat, const CString& data);
virtual Time getTime() const;
virtual bool has(EFormat) const; virtual bool has(EFormat) const;
virtual CString get(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 // transfer all the data in one clipboard to another. the
// clipboards can be of any concrete clipboard type (and // 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);
static void copy(IClipboard* dst, const IClipboard* src, Time);
private: private:
UInt32 readUInt32(const char*) const; UInt32 readUInt32(const char*) const;
void writeUInt32(CString*, UInt32) const; void writeUInt32(CString*, UInt32) const;
private: private:
Time m_time;
bool m_added[kNumFormats]; bool m_added[kNumFormats];
CString m_data[kNumFormats]; CString m_data[kNumFormats];
}; };

View File

@ -222,16 +222,15 @@ bool CXWindowsScreen::setDisplayClipboard(
if (XGetSelectionOwner(m_display, m_atomClipboard[id]) == requestor) { if (XGetSelectionOwner(m_display, m_atomClipboard[id]) == requestor) {
// we got the selection // we got the selection
log((CLOG_INFO "grabbed clipboard at %d", timestamp)); log((CLOG_INFO "grabbed clipboard at %d", timestamp));
m_clipboards[id].m_gotClipboard = timestamp;
m_clipboards[id].m_lostClipboard = CurrentTime; m_clipboards[id].m_lostClipboard = CurrentTime;
if (clipboard != NULL) { if (clipboard != NULL) {
// save clipboard to serve requests // save clipboard to serve requests
CClipboard::copy(&m_clipboards[id].m_clipboard, clipboard); CClipboard::copy(&m_clipboards[id].m_clipboard,
clipboard, timestamp);
} }
else { else {
// clear clipboard // clear clipboard
if (m_clipboards[id].m_clipboard.open()) { if (m_clipboards[id].m_clipboard.open(timestamp)) {
m_clipboards[id].m_clipboard.close(); m_clipboards[id].m_clipboard.close();
} }
} }
@ -250,8 +249,10 @@ void CXWindowsScreen::getDisplayClipboard(
assert(clipboard != NULL); assert(clipboard != NULL);
assert(requestor != None); assert(requestor != None);
// FIXME -- don't update clipboard object if clipboard hasn't changed
// clear the clipboard object // clear the clipboard object
if (!clipboard->open()) if (!clipboard->open(timestamp))
return; return;
// block others from using the display while we get the clipboard. // 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")); log((CLOG_DEBUG1 "handling clipboard request for TIMESTAMP"));
// FIXME -- handle Alloc errors (by returning false) // FIXME -- handle Alloc errors (by returning false)
Time time = m_clipboards[id].m_clipboard.getTime();
XChangeProperty(m_display, requestor, property, XChangeProperty(m_display, requestor, property,
m_atomInteger, m_atomInteger,
8 * sizeof(m_clipboards[id].m_gotClipboard), 32,
PropModeReplace, PropModeReplace,
reinterpret_cast<unsigned char*>( reinterpret_cast<unsigned char*>(time),
&m_clipboards[id].m_gotClipboard),
1); 1);
return true; return true;
} }
@ -935,7 +936,7 @@ bool CXWindowsScreen::wasOwnedAtTime(
const CClipboardInfo& clipboard = m_clipboards[id]; const CClipboardInfo& clipboard = m_clipboards[id];
// not owned if we've never owned the selection // not owned if we've never owned the selection
if (clipboard.m_gotClipboard == CurrentTime) if (clipboard.m_clipboard.getTime() == CurrentTime)
return false; return false;
// if time is CurrentTime then return true if we still own the // if time is CurrentTime then return true if we still own the
@ -953,8 +954,8 @@ bool CXWindowsScreen::wasOwnedAtTime(
return false; return false;
// compare time to range // compare time to range
Time duration = clipboard.m_lostClipboard - clipboard.m_gotClipboard; Time duration = clipboard.m_lostClipboard - clipboard.m_clipboard.getTime();
Time when = time - clipboard.m_gotClipboard; Time when = time - clipboard.m_clipboard.getTime();
return (/*when >= 0 &&*/ when < duration); return (/*when >= 0 &&*/ when < duration);
} }
@ -1001,7 +1002,6 @@ Time CXWindowsScreen::getCurrentTimeNoLock(
CXWindowsScreen::CClipboardInfo::CClipboardInfo() : CXWindowsScreen::CClipboardInfo::CClipboardInfo() :
m_clipboard(), m_clipboard(),
m_gotClipboard(CurrentTime),
m_lostClipboard(CurrentTime), m_lostClipboard(CurrentTime),
m_requests() m_requests()
{ {

View File

@ -1,10 +1,10 @@
#ifndef CXWINDOWSSCREEN_H #ifndef CXWINDOWSSCREEN_H
#define CXWINDOWSSCREEN_H #define CXWINDOWSSCREEN_H
#include "CClipboard.h"
#include "CMutex.h"
#include "BasicTypes.h" #include "BasicTypes.h"
#include "ClipboardTypes.h" #include "ClipboardTypes.h"
#include "CClipboard.h"
#include "CMutex.h"
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <map> #include <map>
#include <list> #include <list>
@ -74,6 +74,7 @@ class CXWindowsScreen {
// copy the clipboard contents to clipboard. requestor must be a // copy the clipboard contents to clipboard. requestor must be a
// valid window; it will be used to receive the transfer. timestamp // valid window; it will be used to receive the transfer. timestamp
// should be the timestamp of the provoking event and not CurrentTime. // should be the timestamp of the provoking event and not CurrentTime.
// if force is false then only update clipboard
void getDisplayClipboard(ClipboardID, void getDisplayClipboard(ClipboardID,
IClipboard* clipboard, IClipboard* clipboard,
Window requestor, Time timestamp) const; Window requestor, Time timestamp) const;
@ -149,11 +150,10 @@ class CXWindowsScreen {
CClipboardInfo(); CClipboardInfo();
public: public:
// the contents of the clipboard // the contents of the clipboard and the time we got it
CClipboard m_clipboard; CClipboard m_clipboard;
// when we got the clipboard and when we lost it // when we lost the clipboard
Time m_gotClipboard;
Time m_lostClipboard; Time m_lostClipboard;
// the request queues // the request queues

View File

@ -8,6 +8,13 @@ class CString;
class IClipboard : public IInterface { class IClipboard : public IInterface {
public: 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 }; enum EFormat { kText, kNumFormats };
// manipulators // manipulators
@ -16,8 +23,10 @@ class IClipboard : public IInterface {
// only add() may be called between an open() and its // only add() may be called between an open() and its
// corresponding close(). if open() returns false then // corresponding close(). if open() returns false then
// the clipboard could not be opened or grabbed; do not // the clipboard could not be opened or grabbed; do not
// call close() in that case. // call close() in that case. iff open() returns true it
virtual bool open() = 0; // 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(). // close the clipboard. close() must match a preceding open().
// this signals that the clipboard has been filled with all the // this signals that the clipboard has been filled with all the
@ -33,6 +42,9 @@ class IClipboard : public IInterface {
// accessors // 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 // returns true iff the clipboard contains data in the given
// format. // format.
virtual bool has(EFormat) const = 0; virtual bool has(EFormat) const = 0;

View File

@ -75,7 +75,9 @@ class IPrimaryScreen : public IInterface {
// get the size of jump zone // get the size of jump zone
virtual SInt32 getJumpZoneSize() const = 0; 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; virtual void getClipboard(ClipboardID, IClipboard*) const = 0;
}; };

View File

@ -24,11 +24,10 @@ class IServerProtocol : public IInterface {
// send various messages to client // send various messages to client
virtual void sendClose() = 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 sendLeave() = 0;
virtual void sendClipboard(ClipboardID, const CString&) = 0; virtual void sendClipboard(ClipboardID, const CString&) = 0;
virtual void sendGrabClipboard(ClipboardID) = 0; virtual void sendGrabClipboard(ClipboardID) = 0;
virtual void sendQueryClipboard(ClipboardID, UInt32 seqNum) = 0;
virtual void sendScreenSaver(bool on) = 0; virtual void sendScreenSaver(bool on) = 0;
virtual void sendKeyDown(KeyID, KeyModifierMask) = 0; virtual void sendKeyDown(KeyID, KeyModifierMask) = 0;
virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; virtual void sendKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0;

View File

@ -22,17 +22,26 @@ static const char kMsgCClose[] = "CBYE";
// enter screen: primary -> secondary // enter screen: primary -> secondary
// entering screen at screen position $1 = x, $2 = y. x,y are // entering screen at screen position $1 = x, $2 = y. x,y are
// absolute screen coordinates. // absolute screen coordinates. $3 = sequence number, which is
static const char kMsgCEnter[] = "CINN%2i%2i"; // 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 // 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"; static const char kMsgCLeave[] = "COUT";
// grab clipboard: primary <-> secondary // grab clipboard: primary <-> secondary
// sent by screen when some other app on that screen grabs a // sent by screen when some other app on that screen grabs a
// clipboard. $1 = the clipboard identifier. // clipboard. $1 = the clipboard identifier, $2 = sequence number.
static const char kMsgCClipboard[] = "CCLP%1i"; // 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 change: primary -> secondary
// screensaver on primary has started ($1 == 1) or closed ($1 == 0) // 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 // clipboard data: primary <-> secondary
// $2 = sequence number, $3 = clipboard data. the sequence number // $2 = sequence number, $3 = clipboard data. the sequence number
// is 0 when sent by the primary. the secondary sends this message // is 0 when sent by the primary. secondary screens should use the
// in response to a kMsgQClipboard and uses the sequence number from // sequence number from the most recent kMsgCEnter. $1 = clipboard
// that message. $1 = clipboard identifier. // identifier.
static const char kMsgDClipboard[] = "DCLP%1i%4i%s"; static const char kMsgDClipboard[] = "DCLP%1i%4i%s";
// client data: secondary -> primary // client data: secondary -> primary
@ -88,12 +97,6 @@ static const char kMsgDInfo[] = "DINF%2i%2i%2i";
// query codes // 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 // query screen info: primary -> secondary
// client should reply with a kMsgDInfo. // client should reply with a kMsgDInfo.
static const char kMsgQInfo[] = "QINF"; static const char kMsgQInfo[] = "QINF";