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>
This commit is contained in:
parent
a6c4d54eaf
commit
255abc1e5a
@ -110,6 +110,8 @@ Q_LOGGING_CATEGORY(lcQpaXInputDevices, "qt.qpa.input.devices")
|
|||||||
Q_LOGGING_CATEGORY(lcQpaXInputEvents, "qt.qpa.input.events")
|
Q_LOGGING_CATEGORY(lcQpaXInputEvents, "qt.qpa.input.events")
|
||||||
Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen")
|
Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen")
|
||||||
Q_LOGGING_CATEGORY(lcQpaEvents, "qt.qpa.events")
|
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,
|
// this event type was added in libxcb 1.10,
|
||||||
// but we support also older version
|
// but we support also older version
|
||||||
@ -1227,6 +1229,95 @@ void QXcbConnection::addPeekFunc(PeekFunc f)
|
|||||||
m_peekFuncs.append(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)
|
QXcbEventReader::QXcbEventReader(QXcbConnection *connection)
|
||||||
: m_connection(connection)
|
: m_connection(connection)
|
||||||
{
|
{
|
||||||
@ -1673,6 +1764,8 @@ void QXcbConnection::processXcbEvents()
|
|||||||
|
|
||||||
m_reader->unlock();
|
m_reader->unlock();
|
||||||
|
|
||||||
|
m_peekerIndexCacheDirty = m_mainEventLoopFlushedQueue = true;
|
||||||
|
|
||||||
// Indicate with a null event that the event the callbacks are waiting for
|
// Indicate with a null event that the event the callbacks are waiting for
|
||||||
// is not in the queue currently.
|
// is not in the queue currently.
|
||||||
for (PeekFunc f : qAsConst(m_peekFuncs))
|
for (PeekFunc f : qAsConst(m_peekFuncs))
|
||||||
|
@ -90,6 +90,8 @@ Q_DECLARE_LOGGING_CATEGORY(lcQpaXInputDevices)
|
|||||||
Q_DECLARE_LOGGING_CATEGORY(lcQpaXInputEvents)
|
Q_DECLARE_LOGGING_CATEGORY(lcQpaXInputEvents)
|
||||||
Q_DECLARE_LOGGING_CATEGORY(lcQpaScreen)
|
Q_DECLARE_LOGGING_CATEGORY(lcQpaScreen)
|
||||||
Q_DECLARE_LOGGING_CATEGORY(lcQpaEvents)
|
Q_DECLARE_LOGGING_CATEGORY(lcQpaEvents)
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(lcQpaXcb)
|
||||||
|
Q_DECLARE_LOGGING_CATEGORY(lcQpaPeeker)
|
||||||
|
|
||||||
class QXcbVirtualDesktop;
|
class QXcbVirtualDesktop;
|
||||||
class QXcbScreen;
|
class QXcbScreen;
|
||||||
@ -444,6 +446,15 @@ public:
|
|||||||
typedef bool (*PeekFunc)(QXcbConnection *, xcb_generic_event_t *);
|
typedef bool (*PeekFunc)(QXcbConnection *, xcb_generic_event_t *);
|
||||||
void addPeekFunc(PeekFunc f);
|
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 xcb_timestamp_t time() const { return m_time; }
|
||||||
inline void setTime(xcb_timestamp_t t) { if (t > m_time) m_time = t; }
|
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;
|
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;
|
friend class QXcbEventReader;
|
||||||
};
|
};
|
||||||
#if QT_CONFIG(xinput2)
|
#if QT_CONFIG(xinput2)
|
||||||
|
@ -83,7 +83,10 @@ static int resourceType(const QByteArray &key)
|
|||||||
QByteArrayLiteral("subpixeltype"), QByteArrayLiteral("antialiasingenabled"),
|
QByteArrayLiteral("subpixeltype"), QByteArrayLiteral("antialiasingenabled"),
|
||||||
QByteArrayLiteral("atspibus"),
|
QByteArrayLiteral("atspibus"),
|
||||||
QByteArrayLiteral("compositingenabled"),
|
QByteArrayLiteral("compositingenabled"),
|
||||||
QByteArrayLiteral("vksurface")
|
QByteArrayLiteral("vksurface"),
|
||||||
|
QByteArrayLiteral("generatepeekerid"),
|
||||||
|
QByteArrayLiteral("removepeekerid"),
|
||||||
|
QByteArrayLiteral("peekeventqueue")
|
||||||
};
|
};
|
||||||
const QByteArray *end = names + sizeof(names) / sizeof(names[0]);
|
const QByteArray *end = names + sizeof(names) / sizeof(names[0]);
|
||||||
const QByteArray *result = std::find(names, end, key);
|
const QByteArray *result = std::find(names, end, key);
|
||||||
@ -304,6 +307,13 @@ QPlatformNativeInterface::NativeResourceForIntegrationFunction QXcbNativeInterfa
|
|||||||
|
|
||||||
if (lowerCaseResource == "setstartupid")
|
if (lowerCaseResource == "setstartupid")
|
||||||
return NativeResourceForIntegrationFunction(setStartupId);
|
return NativeResourceForIntegrationFunction(setStartupId);
|
||||||
|
if (lowerCaseResource == "generatepeekerid")
|
||||||
|
return NativeResourceForIntegrationFunction(generatePeekerId);
|
||||||
|
if (lowerCaseResource == "removepeekerid")
|
||||||
|
return NativeResourceForIntegrationFunction(removePeekerId);
|
||||||
|
if (lowerCaseResource == "peekeventqueue")
|
||||||
|
return NativeResourceForIntegrationFunction(peekEventQueue);
|
||||||
|
|
||||||
return 0;
|
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)
|
void QXcbNativeInterface::setStartupId(const char *data)
|
||||||
{
|
{
|
||||||
QByteArray startupId(data);
|
QByteArray startupId(data);
|
||||||
|
@ -46,12 +46,11 @@
|
|||||||
#include <QtCore/QRect>
|
#include <QtCore/QRect>
|
||||||
|
|
||||||
#include "qxcbexport.h"
|
#include "qxcbexport.h"
|
||||||
|
#include "qxcbconnection.h"
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
class QWidget;
|
|
||||||
class QXcbScreen;
|
class QXcbScreen;
|
||||||
class QXcbConnection;
|
|
||||||
class QXcbNativeInterfaceHandler;
|
class QXcbNativeInterfaceHandler;
|
||||||
|
|
||||||
class Q_XCB_EXPORT QXcbNativeInterface : public QPlatformNativeInterface
|
class Q_XCB_EXPORT QXcbNativeInterface : public QPlatformNativeInterface
|
||||||
@ -74,7 +73,10 @@ public:
|
|||||||
ScreenAntialiasingEnabled,
|
ScreenAntialiasingEnabled,
|
||||||
AtspiBus,
|
AtspiBus,
|
||||||
CompositingEnabled,
|
CompositingEnabled,
|
||||||
VkSurface
|
VkSurface,
|
||||||
|
GeneratePeekerId,
|
||||||
|
RemovePeekerId,
|
||||||
|
PeekEventQueue
|
||||||
};
|
};
|
||||||
|
|
||||||
QXcbNativeInterface();
|
QXcbNativeInterface();
|
||||||
@ -114,6 +116,12 @@ public:
|
|||||||
static void setAppTime(QScreen *screen, xcb_timestamp_t time);
|
static void setAppTime(QScreen *screen, xcb_timestamp_t time);
|
||||||
static void setAppUserTime(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 bool systemTrayAvailable(const QScreen *screen) const;
|
||||||
Q_INVOKABLE void setParentRelativeBackPixmap(QWindow *window);
|
Q_INVOKABLE void setParentRelativeBackPixmap(QWindow *window);
|
||||||
Q_INVOKABLE bool systrayVisualHasAlphaChannel();
|
Q_INVOKABLE bool systrayVisualHasAlphaChannel();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user