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

View File

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

View File

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

View File

@ -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<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());
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<xcb_generic_event_t, QScopedPointerPodDeleter> eventGuard(event);
if (!(event->response_type & ~0x80)) {
handleXcbError(reinterpret_cast<xcb_generic_error_t *>(event));
continue;
}
if (compressEvent(event))
continue;

View File

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

View File

@ -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<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
QWindowSystemInterface::sendWindowSystemEvents(xcbEventSource->dispatcher->flags());
QWindowSystemInterface::sendWindowSystemEvents(flags);
return true;
}

View File

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

View File

@ -41,6 +41,8 @@
#include <QtCore/QThread>
#include <QtCore/QHash>
#include <QtCore/QEventLoop>
#include <QtCore/QVector>
#include <xcb/xcb.h>
@ -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<qint32, QXcbEventNode *> m_peekerToNode;
QVector<xcb_generic_event_t *> m_inputEvents;
// debug stats
quint64 m_nodesOnHeap = 0;
};

View File

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

View File

@ -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
*/