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_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);

View File

@ -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

View File

@ -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(),

View File

@ -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<CThread*> CThreadList;
typedef std::map<CString, CScreenInfo*> 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

View File

@ -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;

View File

@ -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());
}

View File

@ -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);

View File

@ -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);

View File

@ -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];
};

View File

@ -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<unsigned char*>(
&m_clipboards[id].m_gotClipboard),
reinterpret_cast<unsigned char*>(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()
{

View File

@ -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 <X11/Xlib.h>
#include <map>
#include <list>
@ -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

View File

@ -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;

View File

@ -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;
};

View File

@ -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;

View File

@ -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";