Make sure timestamp is initialized before using it for seting selection owner.
authorGatis Paeglis <gatis.paeglis@digia.com>
Wed, 3 Oct 2012 12:53:33 +0000 (14:53 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Sat, 13 Oct 2012 03:51:19 +0000 (05:51 +0200)
Convention from icccm: Clients attempting to acquire a selection must set
the time value of the xcb_set_selection_owner request to the timestamp of
the event triggering the acquisition attempt, not to XCB_CURRENT_TIME. In
some cases it happened that timestamp was set to XCB_CURRENT_TIME.

A zero-length append to a property is a way to obtain a timestamp for this
purpose; the timestamp is in the corresponding XCB_PROPERTY_NOTIFY event.
We used to have this mechanism in 4.8, it was achieved by XWindowEvent.

AFAIK there isn't an equivalent for XWindowEvent in XCB. Therefore i had to
introduce a new mechanism in QXcbConnection - getTimestamp. This function
blocks until it receives the requested event.

Change-Id: Ide46a4fdd44cf026fdd17a79d3c4b17741d1b7d4
Task-number: QTBUG-26783
Reviewed-by: Uli Schlachter <psychon@znc.in>
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
src/plugins/platforms/xcb/qxcbclipboard.cpp
src/plugins/platforms/xcb/qxcbconnection.cpp
src/plugins/platforms/xcb/qxcbconnection.h

index f021ab8..142a8df 100644 (file)
@@ -300,6 +300,9 @@ void QXcbClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)
         m_timestamp[mode] = XCB_CURRENT_TIME;
     }
 
+    if (connection()->time() == XCB_CURRENT_TIME)
+        connection()->setTime(connection()->getTimestamp());
+
     if (data) {
         newOwner = owner();
 
index 401739f..ad9fb1d 100644 (file)
@@ -864,6 +864,59 @@ void QXcbConnection::sendConnectionEvent(QXcbAtom::Atom a, uint id)
     xcb_flush(xcb_connection());
 }
 
+namespace
+{
+    class PropertyNotifyEvent {
+    public:
+        PropertyNotifyEvent(xcb_window_t win, xcb_atom_t property)
+            : window(win), type(XCB_PROPERTY_NOTIFY), atom(property) {}
+        xcb_window_t window;
+        int type;
+        xcb_atom_t atom;
+        bool checkEvent(xcb_generic_event_t *event) const {
+            if (!event)
+                return false;
+            if ((event->response_type & ~0x80) != type) {
+                return false;
+            } else {
+                xcb_property_notify_event_t *pn = (xcb_property_notify_event_t *)event;
+                if ((pn->window == window) && (pn->atom == atom))
+                    return true;
+            }
+            return false;
+        }
+    };
+}
+
+xcb_timestamp_t QXcbConnection::getTimestamp()
+{
+    // send a dummy event to myself to get the timestamp from X server.
+    xcb_window_t rootWindow = screens().at(primaryScreen())->root();
+    xcb_change_property(xcb_connection(), XCB_PROP_MODE_APPEND, rootWindow, atom(QXcbAtom::CLIP_TEMPORARY),
+                        XCB_ATOM_INTEGER, 32, 0, NULL);
+
+    connection()->flush();
+    PropertyNotifyEvent checker(rootWindow, atom(QXcbAtom::CLIP_TEMPORARY));
+
+    xcb_generic_event_t *event = 0;
+    // lets keep this inside a loop to avoid a possible race condition, where
+    // reader thread has not yet had the time to acquire the mutex in order
+    // to add the new set of events to its event queue
+    while (true) {
+        connection()->sync();
+        if (event = checkEvent(checker))
+            break;
+    }
+
+    xcb_property_notify_event_t *pn = (xcb_property_notify_event_t *)event;
+    xcb_timestamp_t timestamp = pn->time;
+    free(event);
+
+    xcb_delete_property(xcb_connection(), rootWindow, atom(QXcbAtom::CLIP_TEMPORARY));
+
+    return timestamp;
+}
+
 void QXcbConnection::processXcbEvents()
 {
     QXcbEventArray *eventqueue = m_reader->lock();
index 08dd304..8a6c418 100644 (file)
@@ -319,7 +319,6 @@ public:
     QByteArray atomName(xcb_atom_t atom);
 
     const char *displayName() const { return m_displayName.constData(); }
-
     xcb_connection_t *xcb_connection() const { return m_connection; }
     const xcb_setup_t *setup() const { return m_setup; }
     const xcb_format_t *formatForDepth(uint8_t depth) const;
@@ -380,6 +379,8 @@ public:
     bool hasXRandr() const { return has_randr_extension; }
     bool hasInputShape() const { return has_input_shape; }
 
+    xcb_timestamp_t getTimestamp();
+
 private slots:
     void processXcbEvents();