summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGatis Paeglis <gatis.paeglis@qt.io>2017-08-04 12:10:12 +0200
committerGatis Paeglis <gatis.paeglis@qt.io>2017-08-11 13:06:38 +0000
commit255abc1e5a3397bf14e9854388881cd80f0bd2e3 (patch)
treec0702be550ffce279ae9af34970a904a2fdb3535
parenta6c4d54eaf467e2db4eef83d737ffcbb263345d0 (diff)
xcb: add support for peeking into the XCB event queue
This will be used by the Qt X11 Extras module. Task-number: QTBUG-50358 Change-Id: Ie095cd211c393ea6d78660b4d53cac28b435a3b2 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp93
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.h15
-rw-r--r--src/plugins/platforms/xcb/qxcbnativeinterface.cpp31
-rw-r--r--src/plugins/platforms/xcb/qxcbnativeinterface.h14
4 files changed, 149 insertions, 4 deletions
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index 6f62589924..23bb5c6410 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -110,6 +110,8 @@ Q_LOGGING_CATEGORY(lcQpaXInputDevices, "qt.qpa.input.devices")
Q_LOGGING_CATEGORY(lcQpaXInputEvents, "qt.qpa.input.events")
Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen")
Q_LOGGING_CATEGORY(lcQpaEvents, "qt.qpa.events")
+Q_LOGGING_CATEGORY(lcQpaXcb, "qt.qpa.xcb") // for general (uncategorized) XCB logging
+Q_LOGGING_CATEGORY(lcQpaPeeker, "qt.qpa.peeker")
// this event type was added in libxcb 1.10,
// but we support also older version
@@ -1227,6 +1229,95 @@ void QXcbConnection::addPeekFunc(PeekFunc f)
m_peekFuncs.append(f);
}
+qint32 QXcbConnection::generatePeekerId()
+{
+ qint32 peekerId = m_peekerIdSource++;
+ m_peekerToCachedIndex.insert(peekerId, 0);
+ return peekerId;
+}
+
+bool QXcbConnection::removePeekerId(qint32 peekerId)
+{
+ if (!m_peekerToCachedIndex.contains(peekerId)) {
+ qCWarning(lcQpaXcb, "failed to remove unknown peeker id: %d", peekerId);
+ return false;
+ }
+ m_peekerToCachedIndex.remove(peekerId);
+ if (m_peekerToCachedIndex.isEmpty()) {
+ m_peekerIdSource = 0; // Once the hash becomes empty, we can start reusing IDs
+ m_peekerIndexCacheDirty = false;
+ }
+ return true;
+}
+
+bool QXcbConnection::peekEventQueue(PeekerCallback peeker, void *peekerData,
+ PeekOptions option, qint32 peekerId)
+{
+ bool peekerIdProvided = peekerId != -1;
+ if (peekerIdProvided && !m_peekerToCachedIndex.contains(peekerId)) {
+ qCWarning(lcQpaXcb, "failed to find index for unknown peeker id: %d", peekerId);
+ return false;
+ }
+
+ bool peekFromCachedIndex = option.testFlag(PeekOption::PeekFromCachedIndex);
+ if (peekFromCachedIndex && !peekerIdProvided) {
+ qCWarning(lcQpaXcb, "PeekOption::PeekFromCachedIndex requires peeker id");
+ return false;
+ }
+
+ if (peekerIdProvided && m_peekerIndexCacheDirty) {
+ // When the main event loop has flushed the buffered XCB events into the window
+ // system event queue, the cached indices are not valid anymore and need reset.
+ auto it = m_peekerToCachedIndex.begin();
+ while (it != m_peekerToCachedIndex.constEnd()) {
+ (*it) = 0;
+ ++it;
+ }
+ m_peekerIndexCacheDirty = false;
+ }
+
+ qint32 peekerIndex = peekFromCachedIndex ? m_peekerToCachedIndex.value(peekerId) : 0;
+ qint32 startingIndex = peekerIndex;
+ bool result = false;
+ m_mainEventLoopFlushedQueue = false;
+
+ QXcbEventArray *eventqueue = m_reader->lock();
+
+ if (Q_UNLIKELY(lcQpaPeeker().isDebugEnabled())) {
+ qCDebug(lcQpaPeeker, "[%d] peeker index: %d | mode: %s | queue size: %d", peekerId,
+ peekerIndex, peekFromCachedIndex ? "cache" : "start", eventqueue->size());
+ }
+ while (peekerIndex < eventqueue->size() && !result && !m_mainEventLoopFlushedQueue) {
+ xcb_generic_event_t *event = eventqueue->at(peekerIndex++);
+ if (!event)
+ continue;
+ if (Q_UNLIKELY(lcQpaPeeker().isDebugEnabled())) {
+ QString debug = QString((QLatin1String("[%1] peeking at index: %2")))
+ .arg(peekerId).arg(peekerIndex - 1);
+ printXcbEvent(lcQpaPeeker(), debug.toLatin1(), event);
+ }
+ // A peeker may call QCoreApplication::processEvents(), which has two implications:
+ // 1) We need to make the lock available for QXcbConnection::processXcbEvents(),
+ // otherwise we will deadlock;
+ // 2) QXcbConnection::processXcbEvents() will flush the queue we are currently
+ // looping through;
+ m_reader->unlock();
+ result = peeker(event, peekerData);
+ m_reader->lock();
+ }
+
+ m_reader->unlock();
+
+ if (peekerIdProvided && peekerIndex != startingIndex && !m_mainEventLoopFlushedQueue) {
+ auto it = m_peekerToCachedIndex.find(peekerId);
+ // Make sure that a peeker callback did not remove the peeker id
+ if (it != m_peekerToCachedIndex.constEnd())
+ (*it) = peekerIndex;
+ }
+
+ return result;
+}
+
QXcbEventReader::QXcbEventReader(QXcbConnection *connection)
: m_connection(connection)
{
@@ -1673,6 +1764,8 @@ void QXcbConnection::processXcbEvents()
m_reader->unlock();
+ m_peekerIndexCacheDirty = m_mainEventLoopFlushedQueue = true;
+
// Indicate with a null event that the event the callbacks are waiting for
// is not in the queue currently.
for (PeekFunc f : qAsConst(m_peekFuncs))
diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h
index a049fc5036..a37ce5f5a6 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.h
+++ b/src/plugins/platforms/xcb/qxcbconnection.h
@@ -90,6 +90,8 @@ Q_DECLARE_LOGGING_CATEGORY(lcQpaXInputDevices)
Q_DECLARE_LOGGING_CATEGORY(lcQpaXInputEvents)
Q_DECLARE_LOGGING_CATEGORY(lcQpaScreen)
Q_DECLARE_LOGGING_CATEGORY(lcQpaEvents)
+Q_DECLARE_LOGGING_CATEGORY(lcQpaXcb)
+Q_DECLARE_LOGGING_CATEGORY(lcQpaPeeker)
class QXcbVirtualDesktop;
class QXcbScreen;
@@ -444,6 +446,15 @@ public:
typedef bool (*PeekFunc)(QXcbConnection *, xcb_generic_event_t *);
void addPeekFunc(PeekFunc f);
+ // Peek at all queued events
+ qint32 generatePeekerId();
+ bool removePeekerId(qint32 peekerId);
+ enum PeekOption { PeekDefault = 0, PeekFromCachedIndex = 1 }; // see qx11info_x11.h
+ Q_DECLARE_FLAGS(PeekOptions, PeekOption)
+ typedef bool (*PeekerCallback)(xcb_generic_event_t *event, void *peekerData);
+ bool peekEventQueue(PeekerCallback peeker, void *peekerData = nullptr,
+ PeekOptions option = PeekDefault, qint32 peekerId = -1);
+
inline xcb_timestamp_t time() const { return m_time; }
inline void setTime(xcb_timestamp_t t) { if (t > m_time) m_time = t; }
@@ -692,6 +703,10 @@ private:
xcb_window_t m_qtSelectionOwner = 0;
+ bool m_mainEventLoopFlushedQueue = false;
+ qint32 m_peekerIdSource = 0;
+ bool m_peekerIndexCacheDirty = false;
+ QHash<qint32, qint32> m_peekerToCachedIndex;
friend class QXcbEventReader;
};
#if QT_CONFIG(xinput2)
diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
index 76ee8f3fb4..caa9499c45 100644
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
@@ -83,7 +83,10 @@ static int resourceType(const QByteArray &key)
QByteArrayLiteral("subpixeltype"), QByteArrayLiteral("antialiasingenabled"),
QByteArrayLiteral("atspibus"),
QByteArrayLiteral("compositingenabled"),
- QByteArrayLiteral("vksurface")
+ QByteArrayLiteral("vksurface"),
+ QByteArrayLiteral("generatepeekerid"),
+ QByteArrayLiteral("removepeekerid"),
+ QByteArrayLiteral("peekeventqueue")
};
const QByteArray *end = names + sizeof(names) / sizeof(names[0]);
const QByteArray *result = std::find(names, end, key);
@@ -304,6 +307,13 @@ QPlatformNativeInterface::NativeResourceForIntegrationFunction QXcbNativeInterfa
if (lowerCaseResource == "setstartupid")
return NativeResourceForIntegrationFunction(setStartupId);
+ if (lowerCaseResource == "generatepeekerid")
+ return NativeResourceForIntegrationFunction(generatePeekerId);
+ if (lowerCaseResource == "removepeekerid")
+ return NativeResourceForIntegrationFunction(removePeekerId);
+ if (lowerCaseResource == "peekeventqueue")
+ return NativeResourceForIntegrationFunction(peekEventQueue);
+
return 0;
}
@@ -482,6 +492,25 @@ void QXcbNativeInterface::setAppUserTime(QScreen* screen, xcb_timestamp_t time)
}
}
+qint32 QXcbNativeInterface::generatePeekerId()
+{
+ QXcbIntegration *integration = QXcbIntegration::instance();
+ return integration->defaultConnection()->generatePeekerId();
+}
+
+bool QXcbNativeInterface::removePeekerId(qint32 peekerId)
+{
+ QXcbIntegration *integration = QXcbIntegration::instance();
+ return integration->defaultConnection()->removePeekerId(peekerId);
+}
+
+bool QXcbNativeInterface::peekEventQueue(QXcbConnection::PeekerCallback peeker, void *peekerData,
+ QXcbConnection::PeekOptions option, qint32 peekerId)
+{
+ QXcbIntegration *integration = QXcbIntegration::instance();
+ return integration->defaultConnection()->peekEventQueue(peeker, peekerData, option, peekerId);
+}
+
void QXcbNativeInterface::setStartupId(const char *data)
{
QByteArray startupId(data);
diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.h b/src/plugins/platforms/xcb/qxcbnativeinterface.h
index b9f1ebcdc6..fb0db727aa 100644
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.h
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.h
@@ -46,12 +46,11 @@
#include <QtCore/QRect>
#include "qxcbexport.h"
+#include "qxcbconnection.h"
QT_BEGIN_NAMESPACE
-class QWidget;
class QXcbScreen;
-class QXcbConnection;
class QXcbNativeInterfaceHandler;
class Q_XCB_EXPORT QXcbNativeInterface : public QPlatformNativeInterface
@@ -74,7 +73,10 @@ public:
ScreenAntialiasingEnabled,
AtspiBus,
CompositingEnabled,
- VkSurface
+ VkSurface,
+ GeneratePeekerId,
+ RemovePeekerId,
+ PeekEventQueue
};
QXcbNativeInterface();
@@ -114,6 +116,12 @@ public:
static void setAppTime(QScreen *screen, xcb_timestamp_t time);
static void setAppUserTime(QScreen *screen, xcb_timestamp_t time);
+ static qint32 generatePeekerId();
+ static bool removePeekerId(qint32 peekerId);
+ static bool peekEventQueue(QXcbConnection::PeekerCallback peeker, void *peekerData = nullptr,
+ QXcbConnection::PeekOptions option = QXcbConnection::PeekDefault,
+ qint32 peekerId = -1);
+
Q_INVOKABLE bool systemTrayAvailable(const QScreen *screen) const;
Q_INVOKABLE void setParentRelativeBackPixmap(QWindow *window);
Q_INVOKABLE bool systrayVisualHasAlphaChannel();