From 00ae1e6b7bf6efa5f5e57d37844e44d521604fb6 Mon Sep 17 00:00:00 2001 From: Gatis Paeglis Date: Wed, 19 Sep 2018 11:40:31 +0200 Subject: xcb: respect QEventLoop::ExcludeUserInputEvents in native event handlers This was a regression from Qt 4. Before this patch, we supported filtering events only at QWindowSystemInterface level, but to properly support filtering in QAbstractEventDispatcher::filterNativeEvent, we have to filter the events earlier. Now it is possible to enable/disable this feature for platforms that support native event filtering. The mapping of which events are user input events were taken from QWindowSystemInterfacePrivate::EventType. Task-number: QTBUG-69687 Change-Id: I9a5fb9f999451c47abcdc83fdcc129b5eeb55447 Reviewed-by: Paul Wicking Reviewed-by: Allan Sandfeld Jensen --- src/gui/kernel/qwindowsysteminterface.cpp | 29 ++++++++++-- src/gui/kernel/qwindowsysteminterface.h | 1 + src/gui/kernel/qwindowsysteminterface_p.h | 1 + src/plugins/platforms/xcb/qxcbconnection.cpp | 54 ++++++++++++++++++++--- src/plugins/platforms/xcb/qxcbconnection.h | 4 +- src/plugins/platforms/xcb/qxcbeventdispatcher.cpp | 7 +-- src/plugins/platforms/xcb/qxcbeventqueue.cpp | 29 ++++++++++++ src/plugins/platforms/xcb/qxcbeventqueue.h | 5 +++ src/plugins/platforms/xcb/qxcbintegration.cpp | 2 + src/widgets/kernel/qwidget.cpp | 14 +++--- 10 files changed, 126 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/gui/kernel/qwindowsysteminterface.cpp b/src/gui/kernel/qwindowsysteminterface.cpp index 07ece5689e..67e1283462 100644 --- a/src/gui/kernel/qwindowsysteminterface.cpp +++ b/src/gui/kernel/qwindowsysteminterface.cpp @@ -57,6 +57,7 @@ QT_BEGIN_NAMESPACE QElapsedTimer QWindowSystemInterfacePrivate::eventTime; bool QWindowSystemInterfacePrivate::synchronousWindowSystemEvents = false; +bool QWindowSystemInterfacePrivate::platformFiltersEvents = false; bool QWindowSystemInterfacePrivate::TabletEvent::platformSynthesizesMouse = true; QWaitCondition QWindowSystemInterfacePrivate::eventsFlushed; QMutex QWindowSystemInterfacePrivate::flushEventMutex; @@ -1047,10 +1048,15 @@ bool QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::ProcessEventsFla int nevents = 0; while (QWindowSystemInterfacePrivate::windowSystemEventsQueued()) { - QWindowSystemInterfacePrivate::WindowSystemEvent *event = - (flags & QEventLoop::ExcludeUserInputEvents) ? - QWindowSystemInterfacePrivate::getNonUserInputWindowSystemEvent() : - QWindowSystemInterfacePrivate::getWindowSystemEvent(); + QWindowSystemInterfacePrivate::WindowSystemEvent *event = nullptr; + + if (QWindowSystemInterfacePrivate::platformFiltersEvents) { + event = QWindowSystemInterfacePrivate::getWindowSystemEvent(); + } else { + event = flags & QEventLoop::ExcludeUserInputEvents ? + QWindowSystemInterfacePrivate::getNonUserInputWindowSystemEvent() : + QWindowSystemInterfacePrivate::getWindowSystemEvent(); + } if (!event) break; @@ -1089,6 +1095,21 @@ bool QWindowSystemInterface::nonUserInputEventsQueued() return QWindowSystemInterfacePrivate::nonUserInputEventsQueued(); } +/*! + Platforms that implement UserInputEvent filtering at native event level must + set this property to \c true. The default is \c false, which means that event + filtering logic is handled by QWindowSystemInterface. Doing the filtering in + platform plugins is necessary when supporting AbstractEventDispatcher::filterNativeEvent(), + which should respect flags that were passed to event dispatcher's processEvents() + call. + + \since 5.12 +*/ +void QWindowSystemInterface::setPlatformFiltersEvents(bool enable) +{ + QWindowSystemInterfacePrivate::platformFiltersEvents = enable; +} + // --------------------- QtTestLib support --------------------- // The following functions are used by testlib, and need to be synchronous to avoid diff --git a/src/gui/kernel/qwindowsysteminterface.h b/src/gui/kernel/qwindowsysteminterface.h index 6bf6ee645c..1dde9130ac 100644 --- a/src/gui/kernel/qwindowsysteminterface.h +++ b/src/gui/kernel/qwindowsysteminterface.h @@ -292,6 +292,7 @@ public: static void deferredFlushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags); static int windowSystemEventsQueued(); static bool nonUserInputEventsQueued(); + static void setPlatformFiltersEvents(bool enable); }; #ifndef QT_NO_DEBUG_STREAM diff --git a/src/gui/kernel/qwindowsysteminterface_p.h b/src/gui/kernel/qwindowsysteminterface_p.h index 492f559f22..9cb4e191cc 100644 --- a/src/gui/kernel/qwindowsysteminterface_p.h +++ b/src/gui/kernel/qwindowsysteminterface_p.h @@ -525,6 +525,7 @@ public: public: static QElapsedTimer eventTime; static bool synchronousWindowSystemEvents; + static bool platformFiltersEvents; static QWaitCondition eventsFlushed; static QMutex flushEventMutex; diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index c1f0b71414..a4294956c1 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -1020,11 +1020,10 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) printXcbEvent(lcQpaEvents(), "Event", event); long result = 0; // Used only by MS Windows - QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance(); - bool handledByNativeEventFilter = dispatcher && dispatcher->filterNativeEvent( - m_nativeInterface->nativeEventType(), event, &result); - if (handledByNativeEventFilter) - return; + if (QAbstractEventDispatcher *dispatcher = QAbstractEventDispatcher::instance()) { + if (dispatcher->filterNativeEvent(m_nativeInterface->nativeEventType(), event, &result)) + return; + } uint response_type = event->response_type & ~0x80; @@ -1451,7 +1450,47 @@ bool QXcbConnection::compressEvent(xcb_generic_event_t *event) const return false; } -void QXcbConnection::processXcbEvents() +bool QXcbConnection::isUserInputEvent(xcb_generic_event_t *event) const +{ + auto eventType = event->response_type & ~0x80; + bool isInputEvent = eventType == XCB_BUTTON_PRESS || + eventType == XCB_BUTTON_RELEASE || + eventType == XCB_KEY_PRESS || + eventType == XCB_KEY_RELEASE || + eventType == XCB_MOTION_NOTIFY || + eventType == XCB_ENTER_NOTIFY || + eventType == XCB_LEAVE_NOTIFY; + if (isInputEvent) + return true; + +#if QT_CONFIG(xcb_xinput) + if (connection()->hasXInput2()) { + isInputEvent = isXIType(event, m_xiOpCode, XCB_INPUT_BUTTON_PRESS) || + isXIType(event, m_xiOpCode, XCB_INPUT_BUTTON_RELEASE) || + isXIType(event, m_xiOpCode, XCB_INPUT_MOTION) || + isXIType(event, m_xiOpCode, XCB_INPUT_TOUCH_BEGIN) || + isXIType(event, m_xiOpCode, XCB_INPUT_TOUCH_UPDATE) || + isXIType(event, m_xiOpCode, XCB_INPUT_TOUCH_END) || + isXIType(event, m_xiOpCode, XCB_INPUT_ENTER) || + isXIType(event, m_xiOpCode, XCB_INPUT_LEAVE) || + // wacom driver's way of reporting tool proximity + isXIType(event, m_xiOpCode, XCB_INPUT_PROPERTY); + } + if (isInputEvent) + return true; +#endif + + if (eventType == XCB_CLIENT_MESSAGE) { + auto clientMessage = reinterpret_cast(event); + if (clientMessage->format == 32 && clientMessage->type == atom(QXcbAtom::WM_PROTOCOLS)) + if (clientMessage->data.data32[0] == atom(QXcbAtom::WM_DELETE_WINDOW)) + isInputEvent = true; + } + + return isInputEvent; +} + +void QXcbConnection::processXcbEvents(QEventLoop::ProcessEventsFlags flags) { int connection_error = xcb_connection_has_error(xcb_connection()); if (connection_error) { @@ -1461,13 +1500,14 @@ void QXcbConnection::processXcbEvents() m_eventQueue->flushBufferedEvents(); - while (xcb_generic_event_t *event = m_eventQueue->takeFirst()) { + while (xcb_generic_event_t *event = m_eventQueue->takeFirst(flags)) { QScopedPointer eventGuard(event); if (!(event->response_type & ~0x80)) { handleXcbError(reinterpret_cast(event)); continue; } + if (compressEvent(event)) continue; diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 3fb64f01a4..96591c7994 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -475,6 +475,8 @@ public: Qt::MouseButtons queryMouseButtons() const; Qt::KeyboardModifiers queryKeyboardModifiers() const; + bool isUserInputEvent(xcb_generic_event_t *event) const; + #if QT_CONFIG(xcb_xinput) void xi2SelectStateEvents(); void xi2SelectDeviceEvents(xcb_window_t window); @@ -495,7 +497,7 @@ public: QXcbGlIntegration *glIntegration() const; void flush() { xcb_flush(m_connection); } - void processXcbEvents(); + void processXcbEvents(QEventLoop::ProcessEventsFlags flags); protected: bool event(QEvent *e) override; diff --git a/src/plugins/platforms/xcb/qxcbeventdispatcher.cpp b/src/plugins/platforms/xcb/qxcbeventdispatcher.cpp index 252c1c62e3..3cb2a5b5ef 100644 --- a/src/plugins/platforms/xcb/qxcbeventdispatcher.cpp +++ b/src/plugins/platforms/xcb/qxcbeventdispatcher.cpp @@ -58,7 +58,7 @@ QXcbUnixEventDispatcher::~QXcbUnixEventDispatcher() bool QXcbUnixEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) { const bool didSendEvents = QEventDispatcherUNIX::processEvents(flags); - m_connection->processXcbEvents(); + m_connection->processXcbEvents(flags); // The following line should not be necessary after QTBUG-70095 return QWindowSystemInterface::sendWindowSystemEvents(flags) || didSendEvents; } @@ -99,9 +99,10 @@ static gboolean xcbSourceCheck(GSource *source) static gboolean xcbSourceDispatch(GSource *source, GSourceFunc, gpointer) { auto xcbEventSource = reinterpret_cast(source); - xcbEventSource->connection->processXcbEvents(); + QEventLoop::ProcessEventsFlags flags = xcbEventSource->dispatcher->flags(); + xcbEventSource->connection->processXcbEvents(flags); // The following line should not be necessary after QTBUG-70095 - QWindowSystemInterface::sendWindowSystemEvents(xcbEventSource->dispatcher->flags()); + QWindowSystemInterface::sendWindowSystemEvents(flags); return true; } diff --git a/src/plugins/platforms/xcb/qxcbeventqueue.cpp b/src/plugins/platforms/xcb/qxcbeventqueue.cpp index d7d70536a0..20589fc550 100644 --- a/src/plugins/platforms/xcb/qxcbeventqueue.cpp +++ b/src/plugins/platforms/xcb/qxcbeventqueue.cpp @@ -114,6 +114,35 @@ QXcbEventQueue::~QXcbEventQueue() qCDebug(lcQpaEventReader) << "nodes on heap:" << m_nodesOnHeap; } +xcb_generic_event_t *QXcbEventQueue::takeFirst(QEventLoop::ProcessEventsFlags flags) +{ + // This is the level at which we were moving excluded user input events into + // separate queue in Qt 4 (see qeventdispatcher_x11.cpp). In this case + // QXcbEventQueue represents Xlib's internal event queue. In Qt 4, Xlib's + // event queue peeking APIs would not see these events anymore, the same way + // our peeking functions do not consider m_inputEvents. This design is + // intentional to keep the same behavior. We could do filtering directly on + // QXcbEventQueue, without the m_inputEvents, but it is not clear if it is + // needed by anyone who peeks at the native event queue. + + bool excludeUserInputEvents = flags.testFlag(QEventLoop::ExcludeUserInputEvents); + if (excludeUserInputEvents) { + xcb_generic_event_t *event = nullptr; + while ((event = takeFirst())) { + if (m_connection->isUserInputEvent(event)) { + m_inputEvents << event; + continue; + } + break; + } + return event; + } + + if (!m_inputEvents.isEmpty()) + return m_inputEvents.takeFirst(); + return takeFirst(); +} + xcb_generic_event_t *QXcbEventQueue::takeFirst() { if (isEmpty()) diff --git a/src/plugins/platforms/xcb/qxcbeventqueue.h b/src/plugins/platforms/xcb/qxcbeventqueue.h index 0096437dba..235f2824be 100644 --- a/src/plugins/platforms/xcb/qxcbeventqueue.h +++ b/src/plugins/platforms/xcb/qxcbeventqueue.h @@ -41,6 +41,8 @@ #include #include +#include +#include #include @@ -81,6 +83,7 @@ public: void run() override; bool isEmpty() const { return m_head == m_flushedTail && !m_head->event; } + xcb_generic_event_t *takeFirst(QEventLoop::ProcessEventsFlags flags); xcb_generic_event_t *takeFirst(); void flushBufferedEvents(); void wakeUpDispatcher(); @@ -124,6 +127,8 @@ private: bool m_peekerIndexCacheDirty = false; QHash m_peekerToNode; + QVector m_inputEvents; + // debug stats quint64 m_nodesOnHeap = 0; }; diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index d030b22fc8..9a7d193767 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -137,6 +137,8 @@ QXcbIntegration::QXcbIntegration(const QStringList ¶meters, int &argc, char m_instance = this; qApp->setAttribute(Qt::AA_CompressHighFrequencyEvents, true); + QWindowSystemInterface::setPlatformFiltersEvents(true); + qRegisterMetaType(); #if QT_CONFIG(xcb_xlib) XInitThreads(); diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 6753c35cfc..30e34c419e 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -10096,20 +10096,24 @@ void QWidget::hideEvent(QHideEvent *) which are passed in the \a message parameter. In your reimplementation of this function, if you want to stop the - event being handled by Qt, return true and set \a result. - If you return false, this native event is passed back to Qt, - which translates the event into a Qt event and sends it to the widget. + event being handled by Qt, return true and set \a result. The \a result + parameter has meaning only on Windows. If you return false, this native + event is passed back to Qt, which translates the event into a Qt event + and sends it to the widget. - \note Events are only delivered to this event handler if the widget is - has a native Window handle. + \note Events are only delivered to this event handler if the widget + has a native window handle. \note This function superseedes the event filter functions x11Event(), winEvent() and macEvent() of Qt 4. + \sa QAbstractNativeEventFilter + \table \header \li Platform \li Event Type Identifier \li Message Type \li Result Type \row \li Windows \li "windows_generic_MSG" \li MSG * \li LRESULT \row \li macOS \li "NSEvent" \li NSEvent * \li + \row \li XCB \li "xcb_generic_event_t" \li xcb_generic_event_t * \li \endtable */ -- cgit v1.2.3