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(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))
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user