From 68e7af55572053259de4c14b93a8784edd6be3e1 Mon Sep 17 00:00:00 2001
From: Friedemann Kleint <Friedemann.Kleint@qt.io>
Date: Mon, 26 Sep 2016 09:46:15 +0200
Subject: [PATCH] Allow to peek into XCB event queue 56

Change-Id: Ide30dd8f59c51c2113ab9b457c965556e62ce32a
---
 src/platformheaders/platformheaders.pro            |  1 +
 .../xcbheaders/qxcbabstracteventpeeker.h           | 55 +++++++++++++++++++
 .../xcbheaders/qxcbabstracteventpeeker.qdoc        | 62 ++++++++++++++++++++++
 src/platformheaders/xcbheaders/xcbheaders.pri      |  2 +
 src/plugins/platforms/xcb/qxcbconnection.cpp       | 32 ++++++++++-
 src/plugins/platforms/xcb/qxcbconnection.h         | 10 +++-
 src/plugins/platforms/xcb/qxcbnativeinterface.cpp  | 12 ++++-
 src/plugins/platforms/xcb/qxcbnativeinterface.h    |  5 +-
 8 files changed, 173 insertions(+), 6 deletions(-)
 create mode 100644 src/platformheaders/xcbheaders/qxcbabstracteventpeeker.h
 create mode 100644 src/platformheaders/xcbheaders/qxcbabstracteventpeeker.qdoc
 create mode 100644 src/platformheaders/xcbheaders/xcbheaders.pri

diff --git a/src/platformheaders/platformheaders.pro b/src/platformheaders/platformheaders.pro
index 1ced4f1..e6dfe2c 100644
--- a/src/platformheaders/platformheaders.pro
+++ b/src/platformheaders/platformheaders.pro
@@ -5,6 +5,7 @@ MODULE_INCNAME = QtPlatformHeaders
 
 include(nativecontexts/nativecontexts.pri)
 include(xcbfunctions/xcbfunctions.pri)
+include(xcbheaders/xcbheaders.pri)
 include(eglfsfunctions/eglfsfunctions.pri)
 include(windowsfunctions/windowsfunctions.pri)
 include(helper/helper.pri)
diff --git a/src/platformheaders/xcbheaders/qxcbabstracteventpeeker.h b/src/platformheaders/xcbheaders/qxcbabstracteventpeeker.h
new file mode 100644
index 0000000..943a90a
--- /dev/null
+++ b/src/platformheaders/xcbheaders/qxcbabstracteventpeeker.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QXCBABSTRACTEVENTPEEKER_H
+#define QXCBABSTRACTEVENTPEEKER_H
+
+#include <xcb/xcb.h>
+
+QT_BEGIN_NAMESPACE
+
+class QXcbAbstractEventPeeker
+{
+public:
+    enum PeekOption {
+        PeekDefault = 0,
+        PeekFromQueueStart = 1
+    };
+
+    virtual ~QXcbAbstractEventPeeker() {}
+    virtual bool peekEventQueue(xcb_generic_event_t *event) = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif  /*QXCBABSTRACTEVENTPEEKER_H*/
diff --git a/src/platformheaders/xcbheaders/qxcbabstracteventpeeker.qdoc b/src/platformheaders/xcbheaders/qxcbabstracteventpeeker.qdoc
new file mode 100644
index 0000000..9f1e142
--- /dev/null
+++ b/src/platformheaders/xcbheaders/qxcbabstracteventpeeker.qdoc
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+    \class QXcbAbstractEventPeeker
+    \inmodule QtX11Extras
+    \since 5.7
+    \brief A class to peek at queued XCB events.
+
+    This class is used together with QX11Info::peekEventQueue().
+*/
+
+/*!
+    \enum QXcbAbstractEventPeeker::PeekOption
+
+    An enum to alter the default behavior of QX11Info::peekEventQueue().
+
+    \value PeekDefault
+    By default, QX11Info::peekEventQueue() optimizes the peeking algorithm by skipping events
+    that it already has seen in the previous calls to QX11Info::peekEventQueue(). This is
+    done as long as the GUI thread has not read a new event from the event queue
+    since the previous call to QX11Info::peekEventQueue(); otherwise, the search starts from
+    the beginning of the queue.
+
+    \value PeekFromQueueStart
+    Peek from the beginning of the buffered event queue.
+*/
+
+/*!
+    \fn bool QXcbAbstractEventPeeker::peekEventQueue(xcb_generic_event_t *event)
+
+    This function is called for every event in the XCB event queue.
+    The \a event is a native XCB event.
+
+    Return \c false to look at the next queued XCB event. If there are no more events left
+    in the queue, the QX11Info::peekEventQueue() function returns. Return \c true to stop
+    peeking and return from the QX11Info::peekEventQueue() function.
+*/
diff --git a/src/platformheaders/xcbheaders/xcbheaders.pri b/src/platformheaders/xcbheaders/xcbheaders.pri
new file mode 100644
index 0000000..a357266
--- /dev/null
+++ b/src/platformheaders/xcbheaders/xcbheaders.pri
@@ -0,0 +1,2 @@
+HEADERS += \
+    $$PWD/qxcbabstracteventpeeker.h
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index c533a2a..a6481bd 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -569,6 +569,7 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra
     , m_glIntegration(Q_NULLPTR)
     , m_xiGrab(false)
     , m_qtSelectionOwner(0)
+    , m_cachedPeekIndex(0)
 {
 #ifdef XCB_USE_XLIB
     Display *dpy = XOpenDisplay(m_displayName.constData());
@@ -1289,6 +1290,7 @@ void QXcbConnection::addPeekFunc(PeekFunc f)
 
 QXcbEventReader::QXcbEventReader(QXcbConnection *connection)
     : m_connection(connection)
+    , m_mainThreadAccessedEventQueue(false)
 {
     checkXcbPollForQueuedEvent();
 }
@@ -1352,9 +1354,11 @@ void QXcbEventReader::addEvent(xcb_generic_event_t *event)
     m_events << event;
 }
 
-QXcbEventArray *QXcbEventReader::lock()
+QXcbEventArray *QXcbEventReader::lock(bool calledFromPeeker)
 {
     m_mutex.lock();
+    if (!calledFromPeeker)
+        m_mainThreadAccessedEventQueue = true;
     if (!local_xcb_poll_for_queued_event) {
         while (xcb_generic_event_t *event = xcb_poll_for_event(m_connection->xcb_connection()))
             m_events << event;
@@ -1362,8 +1366,10 @@ QXcbEventArray *QXcbEventReader::lock()
     return &m_events;
 }
 
-void QXcbEventReader::unlock()
+void QXcbEventReader::unlock(bool calledFromPeeker)
 {
+    if (calledFromPeeker)
+        m_mainThreadAccessedEventQueue = false;
     m_mutex.unlock();
 }
 
@@ -1581,6 +1587,28 @@ void *QXcbConnection::createVisualInfoForDefaultVisualId() const
 
 #endif
 
+void QXcbConnection::peekEventQueue(QXcbAbstractEventPeeker *peeker, QXcbAbstractEventPeeker::PeekOption option)
+{
+    QXcbEventArray *eventqueue = m_reader->lock(true);
+
+    if (m_reader->mainThreadAccessedEventQueue()
+        || option == QXcbAbstractEventPeeker::PeekFromQueueStart) {
+        m_cachedPeekIndex = 0;
+    }
+
+    for (int i = m_cachedPeekIndex; i < eventqueue->size(); ++i) {
+        m_cachedPeekIndex = i + 1;
+        xcb_generic_event_t *event = eventqueue->at(i);
+        if (!event)
+            continue;
+
+        if (peeker->peekEventQueue(event))
+            break;
+    }
+
+    m_reader->unlock(true);
+}
+
 #if defined(XCB_USE_XINPUT2)
 // it is safe to cast XI_* events here as long as we are only touching the first 32 bytes,
 // after that position event needs memmove, see xi2PrepareXIGenericDeviceEvent
diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h
index e7c3722..c1fc30b 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.h
+++ b/src/plugins/platforms/xcb/qxcbconnection.h
@@ -47,6 +47,7 @@
 #include <QVarLengthArray>
 #include <qpa/qwindowsysteminterface.h>
 #include <QtCore/QLoggingCategory>
+#include <QtPlatformHeaders/QXcbAbstractEventPeeker>
 
 // This is needed to make Qt compile together with XKB. xkb.h is using a variable
 // which is called 'explicit', this is a reserved keyword in c++
@@ -309,12 +310,13 @@ public:
 
     void run() Q_DECL_OVERRIDE;
 
-    QXcbEventArray *lock();
-    void unlock();
+    QXcbEventArray *lock(bool calledFromPeeker = false);
+    void unlock(bool calledFromPeeker = false);
 
     void start();
 
     void registerEventDispatcher(QAbstractEventDispatcher *dispatcher);
+    bool mainThreadAccessedEventQueue() const { return m_mainThreadAccessedEventQueue; }
 
 signals:
     void eventPending();
@@ -328,6 +330,7 @@ private:
     QMutex m_mutex;
     QXcbEventArray m_events;
     QXcbConnection *m_connection;
+    bool m_mainThreadAccessedEventQueue;
 };
 
 class QXcbWindowEventListener
@@ -464,6 +467,8 @@ public:
     bool supportsThreadedRendering() const { return m_reader->isRunning(); }
     bool threadedEventHandling() const { return m_reader->isRunning(); }
 
+    void peekEventQueue(QXcbAbstractEventPeeker *peeker, QXcbAbstractEventPeeker::PeekOption option);
+
     xcb_timestamp_t getTimestamp();
     xcb_window_t getSelectionOwner(xcb_atom_t atom) const;
     xcb_window_t getQtSelectionOwner();
@@ -672,6 +677,7 @@ private:
 
     xcb_window_t m_qtSelectionOwner;
 
+    int m_cachedPeekIndex;
     friend class QXcbEventReader;
 };
 
diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
index 09e7ecf..0daf941 100644
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
@@ -79,7 +79,8 @@ static int resourceType(const QByteArray &key)
         QByteArrayLiteral("rootwindow"),
         QByteArrayLiteral("subpixeltype"), QByteArrayLiteral("antialiasingenabled"),
         QByteArrayLiteral("nofonthinting"),
-        QByteArrayLiteral("atspibus")
+        QByteArrayLiteral("atspibus"),
+        QByteArrayLiteral("peekeventqueue")
     };
     const QByteArray *end = names + sizeof(names) / sizeof(names[0]);
     const QByteArray *result = std::find(names, end, key);
@@ -299,6 +300,9 @@ QPlatformNativeInterface::NativeResourceForIntegrationFunction QXcbNativeInterfa
 
     if (lowerCaseResource == "setstartupid")
         return NativeResourceForIntegrationFunction(setStartupId);
+    if (lowerCaseResource == "peekeventqueue")
+        return NativeResourceForIntegrationFunction(peekEventQueue);
+
     return 0;
 }
 
@@ -446,6 +450,12 @@ void *QXcbNativeInterface::connection()
     return integration->defaultConnection()->xcb_connection();
 }
 
+void QXcbNativeInterface::peekEventQueue(QXcbAbstractEventPeeker *peeker, QXcbAbstractEventPeeker::PeekOption option)
+{
+    QXcbIntegration *integration = QXcbIntegration::instance();
+    integration->defaultConnection()->peekEventQueue(peeker, option);
+}
+
 void *QXcbNativeInterface::atspiBus()
 {
     QXcbIntegration *integration = static_cast<QXcbIntegration *>(QGuiApplicationPrivate::platformIntegration());
diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.h b/src/plugins/platforms/xcb/qxcbnativeinterface.h
index f88b710..7790113 100644
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.h
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.h
@@ -38,6 +38,7 @@
 #include <xcb/xcb.h>
 
 #include <QtCore/QRect>
+#include <QtPlatformHeaders/QXcbAbstractEventPeeker>
 
 #include "qxcbexport.h"
 
@@ -68,7 +69,8 @@ public:
         ScreenSubpixelType,
         ScreenAntialiasingEnabled,
         NoFontHinting,
-        AtspiBus
+        AtspiBus,
+        PeekEventQueue
     };
 
     QXcbNativeInterface();
@@ -104,6 +106,7 @@ public:
     static void setStartupId(const char *);
     static void setAppTime(QScreen *screen, xcb_timestamp_t time);
     static void setAppUserTime(QScreen *screen, xcb_timestamp_t time);
+    static void peekEventQueue(QXcbAbstractEventPeeker *peeker, QXcbAbstractEventPeeker::PeekOption option);
 
     Q_INVOKABLE void beep();
     Q_INVOKABLE bool systemTrayAvailable(const QScreen *screen) const;
-- 
2.7.4

