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:
Gatis Paeglis 2017-08-04 12:10:12 +02:00
parent a6c4d54eaf
commit 255abc1e5a
4 changed files with 149 additions and 4 deletions

View File

@ -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))

View File

@ -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)

View File

@ -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);

View File

@ -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();