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:
parent
dd8a66daa4
commit
00ae1e6b7b
@ -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
|
||||
|
@ -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
|
||||
|
@ -525,6 +525,7 @@ public:
|
||||
public:
|
||||
static QElapsedTimer eventTime;
|
||||
static bool synchronousWindowSystemEvents;
|
||||
static bool platformFiltersEvents;
|
||||
|
||||
static QWaitCondition eventsFlushed;
|
||||
static QMutex flushEventMutex;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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<QXcbWindow*>();
|
||||
#if QT_CONFIG(xcb_xlib)
|
||||
XInitThreads();
|
||||
|
@ -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
|
||||
*/
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user