Fix timing issue in incremental data retrieval mechanism

Make sure that we are not reading wrong PropertyNotify events.

From icccm:

Requestors may receive a property of type INCR in response to any
target that results in selection data. This indicates that the owner
will send the actual data incrementally.

The selection requestor:
- Waits for the SelectionNotify event.
- Loops:
  + Retrieving data using GetProperty with the delete argument True.
  + Waiting for a PropertyNotify with the state argument NewValue.
- Waits until the property named by the PropertyNotify event is zero-length.
- Deletes the zero-length property.

The issue with the current approach was that after receiving INCR we
simply fetched the first PropertyNotify event from the queue, where
timestamp indicates that it was generated before INCR, which is not
what need. We need PropertyNotify events with a timestamp older than
the one on INCR.

Task-number: QTBUG-32045
Change-Id: I3b9a006f7d346f67c51e1a296d67a9dc89efadb1
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
This commit is contained in:
Gatis Paeglis 2013-08-07 13:46:21 +02:00 committed by The Qt Project
parent bc6f5b3ff6
commit 663c5186e8
2 changed files with 13 additions and 5 deletions

View File

@ -272,6 +272,7 @@ QXcbClipboard::QXcbClipboard(QXcbConnection *c)
, m_owner(XCB_NONE) , m_owner(XCB_NONE)
, m_incr_active(false) , m_incr_active(false)
, m_clipboard_closing(false) , m_clipboard_closing(false)
, m_incr_receive_time(0)
{ {
Q_ASSERT(QClipboard::Clipboard == 0); Q_ASSERT(QClipboard::Clipboard == 0);
Q_ASSERT(QClipboard::Selection == 1); Q_ASSERT(QClipboard::Selection == 1);
@ -733,7 +734,7 @@ static inline int maxSelectionIncr(xcb_connection_t *c)
return (l > 65536 ? 65536*4 : l*4) - 100; return (l > 65536 ? 65536*4 : l*4) - 100;
} }
bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property, bool deleteProperty, QByteArray *buffer, int *size, xcb_atom_t *type, int *format) const bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property, bool deleteProperty, QByteArray *buffer, int *size, xcb_atom_t *type, int *format)
{ {
int maxsize = maxSelectionIncr(xcb_connection()); int maxsize = maxSelectionIncr(xcb_connection());
ulong bytes_left; // bytes_after ulong bytes_left; // bytes_after
@ -809,7 +810,8 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property,
// correct size, not 0-term. // correct size, not 0-term.
if (size) if (size)
*size = buffer_offset; *size = buffer_offset;
if (*type == atom(QXcbAtom::INCR))
m_incr_receive_time = connection()->getTimestamp();
if (deleteProperty) if (deleteProperty)
xcb_delete_property(xcb_connection(), win, property); xcb_delete_property(xcb_connection(), win, property);
@ -913,6 +915,7 @@ QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb
bool alloc_error = false; bool alloc_error = false;
int length; int length;
int offset = 0; int offset = 0;
xcb_timestamp_t prev_time = m_incr_receive_time;
if (nbytes > 0) { if (nbytes > 0) {
// Reserve buffer + zero-terminator (for text data) // Reserve buffer + zero-terminator (for text data)
@ -927,10 +930,14 @@ QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb
xcb_generic_event_t *ge = waitForClipboardEvent(win, XCB_PROPERTY_NOTIFY, clipboard_timeout); xcb_generic_event_t *ge = waitForClipboardEvent(win, XCB_PROPERTY_NOTIFY, clipboard_timeout);
if (!ge) if (!ge)
break; break;
xcb_property_notify_event_t *event = (xcb_property_notify_event_t *)ge; xcb_property_notify_event_t *event = (xcb_property_notify_event_t *)ge;
if (event->atom != property || event->state != XCB_PROPERTY_NEW_VALUE)
if (event->atom != property
|| event->state != XCB_PROPERTY_NEW_VALUE
|| event->time < prev_time)
continue; continue;
prev_time = event->time;
if (clipboardReadProperty(win, property, true, &tmp_buf, &length, 0, 0)) { if (clipboardReadProperty(win, property, true, &tmp_buf, &length, 0, 0)) {
if (length == 0) { // no more data, we're done if (length == 0) { // no more data, we're done
if (nullterm) { if (nullterm) {

View File

@ -78,7 +78,7 @@ public:
void handleSelectionClearRequest(xcb_selection_clear_event_t *event); void handleSelectionClearRequest(xcb_selection_clear_event_t *event);
void handleXFixesSelectionRequest(xcb_xfixes_selection_notify_event_t *event); void handleXFixesSelectionRequest(xcb_xfixes_selection_notify_event_t *event);
bool clipboardReadProperty(xcb_window_t win, xcb_atom_t property, bool deleteProperty, QByteArray *buffer, int *size, xcb_atom_t *type, int *format) const; bool clipboardReadProperty(xcb_window_t win, xcb_atom_t property, bool deleteProperty, QByteArray *buffer, int *size, xcb_atom_t *type, int *format);
QByteArray clipboardReadIncrementalProperty(xcb_window_t win, xcb_atom_t property, int nbytes, bool nullterm); QByteArray clipboardReadIncrementalProperty(xcb_window_t win, xcb_atom_t property, int nbytes, bool nullterm);
QByteArray getDataInFormat(xcb_atom_t modeAtom, xcb_atom_t fmtatom); QByteArray getDataInFormat(xcb_atom_t modeAtom, xcb_atom_t fmtatom);
@ -113,6 +113,7 @@ private:
bool m_incr_active; bool m_incr_active;
bool m_clipboard_closing; bool m_clipboard_closing;
xcb_timestamp_t m_incr_receive_time;
}; };
#endif // QT_NO_CLIPBOARD #endif // QT_NO_CLIPBOARD