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 <paul.wicking@qt.io>
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
This commit is contained in:
Gatis Paeglis 2018-09-19 11:40:31 +02:00
parent dd8a66daa4
commit 00ae1e6b7b
10 changed files with 126 additions and 20 deletions

View File

@ -57,6 +57,7 @@ QT_BEGIN_NAMESPACE
QElapsedTimer QWindowSystemInterfacePrivate::eventTime; QElapsedTimer QWindowSystemInterfacePrivate::eventTime;
bool QWindowSystemInterfacePrivate::synchronousWindowSystemEvents = false; bool QWindowSystemInterfacePrivate::synchronousWindowSystemEvents = false;
bool QWindowSystemInterfacePrivate::platformFiltersEvents = false;
bool QWindowSystemInterfacePrivate::TabletEvent::platformSynthesizesMouse = true; bool QWindowSystemInterfacePrivate::TabletEvent::platformSynthesizesMouse = true;
QWaitCondition QWindowSystemInterfacePrivate::eventsFlushed; QWaitCondition QWindowSystemInterfacePrivate::eventsFlushed;
QMutex QWindowSystemInterfacePrivate::flushEventMutex; QMutex QWindowSystemInterfacePrivate::flushEventMutex;
@ -1047,10 +1048,15 @@ bool QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::ProcessEventsFla
int nevents = 0; int nevents = 0;
while (QWindowSystemInterfacePrivate::windowSystemEventsQueued()) { while (QWindowSystemInterfacePrivate::windowSystemEventsQueued()) {
QWindowSystemInterfacePrivate::WindowSystemEvent *event = QWindowSystemInterfacePrivate::WindowSystemEvent *event = nullptr;
(flags & QEventLoop::ExcludeUserInputEvents) ?
QWindowSystemInterfacePrivate::getNonUserInputWindowSystemEvent() : if (QWindowSystemInterfacePrivate::platformFiltersEvents) {
QWindowSystemInterfacePrivate::getWindowSystemEvent(); event = QWindowSystemInterfacePrivate::getWindowSystemEvent();
} else {
event = flags & QEventLoop::ExcludeUserInputEvents ?
QWindowSystemInterfacePrivate::getNonUserInputWindowSystemEvent() :
QWindowSystemInterfacePrivate::getWindowSystemEvent();
}
if (!event) if (!event)
break; break;
@ -1089,6 +1095,21 @@ bool QWindowSystemInterface::nonUserInputEventsQueued()
return QWindowSystemInterfacePrivate::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 --------------------- // --------------------- QtTestLib support ---------------------
// The following functions are used by testlib, and need to be synchronous to avoid // The following functions are used by testlib, and need to be synchronous to avoid

View File

@ -292,6 +292,7 @@ public:
static void deferredFlushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags); static void deferredFlushWindowSystemEvents(QEventLoop::ProcessEventsFlags flags);
static int windowSystemEventsQueued(); static int windowSystemEventsQueued();
static bool nonUserInputEventsQueued(); static bool nonUserInputEventsQueued();
static void setPlatformFiltersEvents(bool enable);
}; };
#ifndef QT_NO_DEBUG_STREAM #ifndef QT_NO_DEBUG_STREAM

View File

@ -525,6 +525,7 @@ public:
public: public:
static QElapsedTimer eventTime; static QElapsedTimer eventTime;
static bool synchronousWindowSystemEvents; static bool synchronousWindowSystemEvents;
static bool platformFiltersEvents;
static QWaitCondition eventsFlushed; static QWaitCondition eventsFlushed;
static QMutex flushEventMutex; static QMutex flushEventMutex;

View File

@ -1020,11 +1020,10 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
printXcbEvent(lcQpaEvents(), "Event", event); printXcbEvent(lcQpaEvents(), "Event", event);
long result = 0; // Used only by MS Windows long result = 0; // Used only by MS Windows
QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance(); if (QAbstractEventDispatcher *dispatcher = QAbstractEventDispatcher::instance()) {
bool handledByNativeEventFilter = dispatcher && dispatcher->filterNativeEvent( if (dispatcher->filterNativeEvent(m_nativeInterface->nativeEventType(), event, &result))
m_nativeInterface->nativeEventType(), event, &result); return;
if (handledByNativeEventFilter) }
return;
uint response_type = event->response_type & ~0x80; uint response_type = event->response_type & ~0x80;
@ -1451,7 +1450,47 @@ bool QXcbConnection::compressEvent(xcb_generic_event_t *event) const
return false; 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<const xcb_client_message_event_t *>(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()); int connection_error = xcb_connection_has_error(xcb_connection());
if (connection_error) { if (connection_error) {
@ -1461,13 +1500,14 @@ void QXcbConnection::processXcbEvents()
m_eventQueue->flushBufferedEvents(); m_eventQueue->flushBufferedEvents();
while (xcb_generic_event_t *event = m_eventQueue->takeFirst()) { while (xcb_generic_event_t *event = m_eventQueue->takeFirst(flags)) {
QScopedPointer<xcb_generic_event_t, QScopedPointerPodDeleter> eventGuard(event); QScopedPointer<xcb_generic_event_t, QScopedPointerPodDeleter> eventGuard(event);
if (!(event->response_type & ~0x80)) { if (!(event->response_type & ~0x80)) {
handleXcbError(reinterpret_cast<xcb_generic_error_t *>(event)); handleXcbError(reinterpret_cast<xcb_generic_error_t *>(event));
continue; continue;
} }
if (compressEvent(event)) if (compressEvent(event))
continue; continue;

View File

@ -475,6 +475,8 @@ public:
Qt::MouseButtons queryMouseButtons() const; Qt::MouseButtons queryMouseButtons() const;
Qt::KeyboardModifiers queryKeyboardModifiers() const; Qt::KeyboardModifiers queryKeyboardModifiers() const;
bool isUserInputEvent(xcb_generic_event_t *event) const;
#if QT_CONFIG(xcb_xinput) #if QT_CONFIG(xcb_xinput)
void xi2SelectStateEvents(); void xi2SelectStateEvents();
void xi2SelectDeviceEvents(xcb_window_t window); void xi2SelectDeviceEvents(xcb_window_t window);
@ -495,7 +497,7 @@ public:
QXcbGlIntegration *glIntegration() const; QXcbGlIntegration *glIntegration() const;
void flush() { xcb_flush(m_connection); } void flush() { xcb_flush(m_connection); }
void processXcbEvents(); void processXcbEvents(QEventLoop::ProcessEventsFlags flags);
protected: protected:
bool event(QEvent *e) override; bool event(QEvent *e) override;

View File

@ -58,7 +58,7 @@ QXcbUnixEventDispatcher::~QXcbUnixEventDispatcher()
bool QXcbUnixEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) bool QXcbUnixEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
{ {
const bool didSendEvents = QEventDispatcherUNIX::processEvents(flags); const bool didSendEvents = QEventDispatcherUNIX::processEvents(flags);
m_connection->processXcbEvents(); m_connection->processXcbEvents(flags);
// The following line should not be necessary after QTBUG-70095 // The following line should not be necessary after QTBUG-70095
return QWindowSystemInterface::sendWindowSystemEvents(flags) || didSendEvents; return QWindowSystemInterface::sendWindowSystemEvents(flags) || didSendEvents;
} }
@ -99,9 +99,10 @@ static gboolean xcbSourceCheck(GSource *source)
static gboolean xcbSourceDispatch(GSource *source, GSourceFunc, gpointer) static gboolean xcbSourceDispatch(GSource *source, GSourceFunc, gpointer)
{ {
auto xcbEventSource = reinterpret_cast<XcbEventSource *>(source); auto xcbEventSource = reinterpret_cast<XcbEventSource *>(source);
xcbEventSource->connection->processXcbEvents(); QEventLoop::ProcessEventsFlags flags = xcbEventSource->dispatcher->flags();
xcbEventSource->connection->processXcbEvents(flags);
// The following line should not be necessary after QTBUG-70095 // The following line should not be necessary after QTBUG-70095
QWindowSystemInterface::sendWindowSystemEvents(xcbEventSource->dispatcher->flags()); QWindowSystemInterface::sendWindowSystemEvents(flags);
return true; return true;
} }

View File

@ -114,6 +114,35 @@ QXcbEventQueue::~QXcbEventQueue()
qCDebug(lcQpaEventReader) << "nodes on heap:" << m_nodesOnHeap; 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() xcb_generic_event_t *QXcbEventQueue::takeFirst()
{ {
if (isEmpty()) if (isEmpty())

View File

@ -41,6 +41,8 @@
#include <QtCore/QThread> #include <QtCore/QThread>
#include <QtCore/QHash> #include <QtCore/QHash>
#include <QtCore/QEventLoop>
#include <QtCore/QVector>
#include <xcb/xcb.h> #include <xcb/xcb.h>
@ -81,6 +83,7 @@ public:
void run() override; void run() override;
bool isEmpty() const { return m_head == m_flushedTail && !m_head->event; } bool isEmpty() const { return m_head == m_flushedTail && !m_head->event; }
xcb_generic_event_t *takeFirst(QEventLoop::ProcessEventsFlags flags);
xcb_generic_event_t *takeFirst(); xcb_generic_event_t *takeFirst();
void flushBufferedEvents(); void flushBufferedEvents();
void wakeUpDispatcher(); void wakeUpDispatcher();
@ -124,6 +127,8 @@ private:
bool m_peekerIndexCacheDirty = false; bool m_peekerIndexCacheDirty = false;
QHash<qint32, QXcbEventNode *> m_peekerToNode; QHash<qint32, QXcbEventNode *> m_peekerToNode;
QVector<xcb_generic_event_t *> m_inputEvents;
// debug stats // debug stats
quint64 m_nodesOnHeap = 0; quint64 m_nodesOnHeap = 0;
}; };

View File

@ -137,6 +137,8 @@ QXcbIntegration::QXcbIntegration(const QStringList &parameters, int &argc, char
m_instance = this; m_instance = this;
qApp->setAttribute(Qt::AA_CompressHighFrequencyEvents, true); qApp->setAttribute(Qt::AA_CompressHighFrequencyEvents, true);
QWindowSystemInterface::setPlatformFiltersEvents(true);
qRegisterMetaType<QXcbWindow*>(); qRegisterMetaType<QXcbWindow*>();
#if QT_CONFIG(xcb_xlib) #if QT_CONFIG(xcb_xlib)
XInitThreads(); XInitThreads();

View File

@ -10096,20 +10096,24 @@ void QWidget::hideEvent(QHideEvent *)
which are passed in the \a message parameter. which are passed in the \a message parameter.
In your reimplementation of this function, if you want to stop the In your reimplementation of this function, if you want to stop the
event being handled by Qt, return true and set \a result. event being handled by Qt, return true and set \a result. The \a result
If you return false, this native event is passed back to Qt, parameter has meaning only on Windows. If you return false, this native
which translates the event into a Qt event and sends it to the widget. 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 \note Events are only delivered to this event handler if the widget
has a native Window handle. has a native window handle.
\note This function superseedes the event filter functions \note This function superseedes the event filter functions
x11Event(), winEvent() and macEvent() of Qt 4. x11Event(), winEvent() and macEvent() of Qt 4.
\sa QAbstractNativeEventFilter
\table \table
\header \li Platform \li Event Type Identifier \li Message Type \li Result Type \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 Windows \li "windows_generic_MSG" \li MSG * \li LRESULT
\row \li macOS \li "NSEvent" \li NSEvent * \li \row \li macOS \li "NSEvent" \li NSEvent * \li
\row \li XCB \li "xcb_generic_event_t" \li xcb_generic_event_t * \li
\endtable \endtable
*/ */