checkpoint. trying to fix a delay when sending clipboards on X.
This commit is contained in:
parent
c4fea1c32b
commit
3d27de39bb
|
@ -2,7 +2,7 @@
|
||||||
#include "CXWindowsUtil.h"
|
#include "CXWindowsUtil.h"
|
||||||
#include "CThread.h"
|
#include "CThread.h"
|
||||||
#include "CLog.h"
|
#include "CLog.h"
|
||||||
#include "TMethodJob.h"
|
#include "CStopwatch.h"
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <X11/Xatom.h>
|
#include <X11/Xatom.h>
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ CXWindowsClipboard::addRequest(Window owner, Window requestor,
|
||||||
// at the given time.
|
// at the given time.
|
||||||
bool success = false;
|
bool success = false;
|
||||||
if (owner == m_window) {
|
if (owner == m_window) {
|
||||||
log((CLOG_DEBUG "request for clipboard %d, target %d by 0x%08x (property=%d)", m_selection, target, requestor, property));
|
log((CLOG_DEBUG1 "request for clipboard %d, target %d by 0x%08x (property=%d)", m_selection, target, requestor, property));
|
||||||
if (wasOwnedAtTime(time)) {
|
if (wasOwnedAtTime(time)) {
|
||||||
if (target == m_atomMultiple) {
|
if (target == m_atomMultiple) {
|
||||||
// add a multiple request. property may not be None
|
// add a multiple request. property may not be None
|
||||||
|
@ -96,13 +96,13 @@ CXWindowsClipboard::addRequest(Window owner, Window requestor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log((CLOG_DEBUG "failed, not owned at time %d", time));
|
log((CLOG_DEBUG1 "failed, not owned at time %d", time));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
// send failure
|
// send failure
|
||||||
log((CLOG_DEBUG "failed"));
|
log((CLOG_DEBUG1 "failed"));
|
||||||
insertReply(new CReply(requestor, target, time));
|
insertReply(new CReply(requestor, target, time));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,14 +138,14 @@ CXWindowsClipboard::addSimpleRequest(Window requestor,
|
||||||
|
|
||||||
if (type != None) {
|
if (type != None) {
|
||||||
// success
|
// success
|
||||||
log((CLOG_DEBUG "success"));
|
log((CLOG_DEBUG1 "success"));
|
||||||
insertReply(new CReply(requestor, target, time,
|
insertReply(new CReply(requestor, target, time,
|
||||||
property, data, type, format));
|
property, data, type, format));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// failure
|
// failure
|
||||||
log((CLOG_DEBUG "failed"));
|
log((CLOG_DEBUG1 "failed"));
|
||||||
insertReply(new CReply(requestor, target, time));
|
insertReply(new CReply(requestor, target, time));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -286,24 +286,9 @@ CXWindowsClipboard::open(Time time) const
|
||||||
m_open = true;
|
m_open = true;
|
||||||
m_time = time;
|
m_time = time;
|
||||||
|
|
||||||
// get the time the clipboard ownership was taken by the current
|
// be sure to flush the cache later if it's dirty
|
||||||
// owner.
|
m_checkCache = true;
|
||||||
if (m_motif) {
|
checkCache();
|
||||||
m_timeOwned = motifGetTime();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
m_timeOwned = icccmGetTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we can't get the time then use the time passed to us
|
|
||||||
if (m_timeOwned == 0) {
|
|
||||||
m_timeOwned = m_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the cache is dirty then flush it
|
|
||||||
if (m_timeOwned != m_cacheTime) {
|
|
||||||
clearCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -327,6 +312,7 @@ CXWindowsClipboard::close() const
|
||||||
IClipboard::Time
|
IClipboard::Time
|
||||||
CXWindowsClipboard::getTime() const
|
CXWindowsClipboard::getTime() const
|
||||||
{
|
{
|
||||||
|
checkCache();
|
||||||
return m_timeOwned;
|
return m_timeOwned;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,6 +347,34 @@ CXWindowsClipboard::getFormat(Atom src) const
|
||||||
return IClipboard::kNumFormats;
|
return IClipboard::kNumFormats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CXWindowsClipboard::checkCache() const
|
||||||
|
{
|
||||||
|
if (!m_checkCache) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_checkCache = false;
|
||||||
|
|
||||||
|
// get the time the clipboard ownership was taken by the current
|
||||||
|
// owner.
|
||||||
|
if (m_motif) {
|
||||||
|
m_timeOwned = motifGetTime();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_timeOwned = icccmGetTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we can't get the time then use the time passed to us
|
||||||
|
if (m_timeOwned == 0) {
|
||||||
|
m_timeOwned = m_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the cache is dirty then flush it
|
||||||
|
if (m_timeOwned != m_cacheTime) {
|
||||||
|
clearCache();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CXWindowsClipboard::clearCache() const
|
CXWindowsClipboard::clearCache() const
|
||||||
{
|
{
|
||||||
|
@ -370,7 +384,8 @@ CXWindowsClipboard::clearCache() const
|
||||||
void
|
void
|
||||||
CXWindowsClipboard::doClearCache()
|
CXWindowsClipboard::doClearCache()
|
||||||
{
|
{
|
||||||
m_cached = false;
|
m_checkCache = false;
|
||||||
|
m_cached = false;
|
||||||
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;
|
||||||
|
@ -381,6 +396,7 @@ void
|
||||||
CXWindowsClipboard::fillCache() const
|
CXWindowsClipboard::fillCache() const
|
||||||
{
|
{
|
||||||
// get the selection data if not already cached
|
// get the selection data if not already cached
|
||||||
|
checkCache();
|
||||||
if (!m_cached) {
|
if (!m_cached) {
|
||||||
const_cast<CXWindowsClipboard*>(this)->doFillCache();
|
const_cast<CXWindowsClipboard*>(this)->doFillCache();
|
||||||
}
|
}
|
||||||
|
@ -395,8 +411,9 @@ CXWindowsClipboard::doFillCache()
|
||||||
else {
|
else {
|
||||||
icccmFillCache();
|
icccmFillCache();
|
||||||
}
|
}
|
||||||
m_cached = true;
|
m_checkCache = false;
|
||||||
m_cacheTime = m_timeOwned;
|
m_cached = true;
|
||||||
|
m_cacheTime = m_timeOwned;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1007,6 +1024,7 @@ bool
|
||||||
CXWindowsClipboard::wasOwnedAtTime(::Time time) const
|
CXWindowsClipboard::wasOwnedAtTime(::Time time) const
|
||||||
{
|
{
|
||||||
// not owned if we've never owned the selection
|
// not owned if we've never owned the selection
|
||||||
|
checkCache();
|
||||||
if (m_timeOwned == 0) {
|
if (m_timeOwned == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1066,6 +1084,7 @@ CXWindowsClipboard::getTimestampData(CString& data, int* format) const
|
||||||
assert(format != NULL);
|
assert(format != NULL);
|
||||||
|
|
||||||
assert(sizeof(m_timeOwned) == 4);
|
assert(sizeof(m_timeOwned) == 4);
|
||||||
|
checkCache();
|
||||||
data.append(reinterpret_cast<const char*>(&m_timeOwned), 4);
|
data.append(reinterpret_cast<const char*>(&m_timeOwned), 4);
|
||||||
*format = 32;
|
*format = 32;
|
||||||
return m_atomTimestamp;
|
return m_atomTimestamp;
|
||||||
|
@ -1129,9 +1148,6 @@ CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display,
|
||||||
*m_actualTarget = None;
|
*m_actualTarget = None;
|
||||||
*m_data = "";
|
*m_data = "";
|
||||||
|
|
||||||
// get timeout atom
|
|
||||||
m_timeout = XInternAtom(display, "SYNERGY_TIMEOUT", False);
|
|
||||||
|
|
||||||
// delete target property
|
// delete target property
|
||||||
XDeleteProperty(display, m_requestor, m_property);
|
XDeleteProperty(display, m_requestor, m_property);
|
||||||
|
|
||||||
|
@ -1145,21 +1161,36 @@ CXWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display,
|
||||||
XConvertSelection(display, selection, target,
|
XConvertSelection(display, selection, target,
|
||||||
m_property, m_requestor, m_time);
|
m_property, m_requestor, m_time);
|
||||||
|
|
||||||
// process selection events. have a separate thread send us an
|
// Xlib inexplicably omits the ability to wait for an event with
|
||||||
// event after a timeout so we don't get locked up by badly
|
// a timeout. (it's inexplicable because there's no portable way
|
||||||
// behaved selection owners.
|
// to do it.) we'll poll until we have what we're looking for or
|
||||||
CThread timer(new TMethodJob<CXWindowsClipboard::CICCCMGetClipboard>(
|
// a timeout expires. we use a timeout so we don't get locked up
|
||||||
this,
|
// by badly behaved selection owners.
|
||||||
&CXWindowsClipboard::CICCCMGetClipboard::timeout,
|
|
||||||
display));
|
|
||||||
XEvent xevent;
|
XEvent xevent;
|
||||||
|
SInt32 lastPending = 0;
|
||||||
|
CStopwatch timeout(true);
|
||||||
|
static const double s_timeout = 0.2; // FIXME -- is this too short?
|
||||||
while (!m_done && !m_failed) {
|
while (!m_done && !m_failed) {
|
||||||
// process events
|
// fail if timeout has expired
|
||||||
XIfEvent(display, &xevent,
|
if (timeout.getTime() < s_timeout) {
|
||||||
|
m_failed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get how many events are pending now
|
||||||
|
SInt32 pending = XPending(display);
|
||||||
|
|
||||||
|
// process events if there are more otherwise sleep
|
||||||
|
if (pending > lastPending) {
|
||||||
|
lastPending = pending;
|
||||||
|
XCheckIfEvent(display, &xevent,
|
||||||
&CXWindowsClipboard::CICCCMGetClipboard::eventPredicate,
|
&CXWindowsClipboard::CICCCMGetClipboard::eventPredicate,
|
||||||
reinterpret_cast<XPointer>(this));
|
reinterpret_cast<XPointer>(this));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
CThread::sleep(0.01);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
timer.cancel();
|
|
||||||
|
|
||||||
// restore mask
|
// restore mask
|
||||||
XSelectInput(display, m_requestor, attr.your_event_mask);
|
XSelectInput(display, m_requestor, attr.your_event_mask);
|
||||||
|
@ -1216,17 +1247,6 @@ CXWindowsClipboard::CICCCMGetClipboard::doEventPredicate(
|
||||||
// otherwise not interested
|
// otherwise not interested
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
case ClientMessage:
|
|
||||||
// done if this is the timeout message
|
|
||||||
if (xevent->xclient.window == m_requestor &&
|
|
||||||
xevent->xclient.message_type == m_timeout) {
|
|
||||||
m_failed = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise not interested
|
|
||||||
return false;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// not interested
|
// not interested
|
||||||
return false;
|
return false;
|
||||||
|
@ -1314,24 +1334,6 @@ CXWindowsClipboard::CICCCMGetClipboard::eventPredicate(
|
||||||
return self->doEventPredicate(display, xevent) ? True : False;
|
return self->doEventPredicate(display, xevent) ? True : False;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
CXWindowsClipboard::CICCCMGetClipboard::timeout(void* vdisplay)
|
|
||||||
{
|
|
||||||
// wait
|
|
||||||
CThread::sleep(0.2); // FIXME -- is this too short?
|
|
||||||
|
|
||||||
// send wake up
|
|
||||||
Display* display = reinterpret_cast<Display*>(vdisplay);
|
|
||||||
XEvent event;
|
|
||||||
event.xclient.type = ClientMessage;
|
|
||||||
event.xclient.display = display;
|
|
||||||
event.xclient.window = m_requestor;
|
|
||||||
event.xclient.message_type = m_timeout;
|
|
||||||
event.xclient.format = 8;
|
|
||||||
CXWindowsUtil::CErrorLock lock;
|
|
||||||
XSendEvent(display, m_requestor, False, 0, &event);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// CXWindowsClipboard::CReply
|
// CXWindowsClipboard::CReply
|
||||||
|
|
|
@ -62,6 +62,10 @@ private:
|
||||||
Window requestor, Atom target,
|
Window requestor, Atom target,
|
||||||
::Time time, Atom property);
|
::Time time, Atom property);
|
||||||
|
|
||||||
|
// if not already checked then see if the cache is stale and, if so,
|
||||||
|
// clear it. this has the side effect of updating m_timeOwned.
|
||||||
|
void checkCache() const;
|
||||||
|
|
||||||
// clear the cache, resetting the cached flag and the added flag for
|
// clear the cache, resetting the cached flag and the added flag for
|
||||||
// each format.
|
// each format.
|
||||||
void clearCache() const;
|
void clearCache() const;
|
||||||
|
@ -84,7 +88,6 @@ private:
|
||||||
bool motifOwnsClipboard() const;
|
bool motifOwnsClipboard() const;
|
||||||
Time motifGetTime() const;
|
Time motifGetTime() const;
|
||||||
void motifFillCache();
|
void motifFillCache();
|
||||||
// FIXME
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// helper classes
|
// helper classes
|
||||||
|
@ -109,7 +112,6 @@ private:
|
||||||
static Bool eventPredicate(Display* display,
|
static Bool eventPredicate(Display* display,
|
||||||
XEvent* event,
|
XEvent* event,
|
||||||
XPointer arg);
|
XPointer arg);
|
||||||
void timeout(void*);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Window m_requestor;
|
Window m_requestor;
|
||||||
|
@ -129,9 +131,6 @@ private:
|
||||||
// selection owner cannot convert to the requested type.
|
// selection owner cannot convert to the requested type.
|
||||||
Atom* m_actualTarget;
|
Atom* m_actualTarget;
|
||||||
|
|
||||||
// property used in event to wake up event loop
|
|
||||||
Atom m_timeout;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// true iff the selection owner didn't follow ICCCM conventions
|
// true iff the selection owner didn't follow ICCCM conventions
|
||||||
bool m_error;
|
bool m_error;
|
||||||
|
@ -240,6 +239,7 @@ private:
|
||||||
mutable bool m_motif;
|
mutable bool m_motif;
|
||||||
|
|
||||||
// the added/cached clipboard data
|
// the added/cached clipboard data
|
||||||
|
mutable bool m_checkCache;
|
||||||
bool m_cached;
|
bool m_cached;
|
||||||
Time m_cacheTime;
|
Time m_cacheTime;
|
||||||
bool m_added[kNumFormats];
|
bool m_added[kNumFormats];
|
||||||
|
|
Loading…
Reference in New Issue