diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp index 4f62a1880b8..55c226d3e69 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -682,23 +682,96 @@ static inline qreal fixed1616ToReal(xcb_input_fp1616_t val) return qreal(val) / 0x10000; } +//implementation is ported from https://codereview.qt-project.org/c/qt/qtbase/+/231552/12/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp#558 +namespace { + +/*! \internal + + Qt listens for XIAllDevices to avoid losing mouse events. This function + ensures that we don't process the same event twice: from a slave device and + then again from a master device. + + In a normal use case (e.g. mouse press and release inside a window), we will + drop events from master devices as duplicates. Other advantage of processing + events from slave devices is that they don't share button state. All buttons + on a master device share the state. + + Examples of special cases: + +\list + +\li During system move/resize, window manager (_NET_WM_MOVERESIZE) grabs the + master pointer, in this case we process the matching release from the slave + device. A master device event is not sent by the server, hence no duplicate + event to drop. If we listened for XIAllMasterDevices instead, we would never + see a release event in this case. + +\li If we dismiss a context menu by clicking somewhere outside a Qt application, + we will process the mouse press from the master pointer as that is the + device we are grabbing. We are not grabbing slave devices (grabbing on the + slave device is buggy according to 19d289ab1b5bde3e136765e5432b5c7d004df3a4). + And since the event occurs outside our window, the slave device event is + not sent to us by the server, hence no duplicate event to drop. + +\endlist +*/ +bool isDuplicateEvent(xcb_ge_event_t *event) +{ + Q_ASSERT(event); + + struct qXIEvent { + bool isValid = false; + uint16_t sourceid; + uint8_t evtype; + uint32_t detail; + int32_t root_x; + int32_t root_y; + }; + static qXIEvent lastSeenEvent; + + bool isDuplicate = false; + auto *xiDeviceEvent = reinterpret_cast(event); + if (lastSeenEvent.isValid) { + isDuplicate = lastSeenEvent.sourceid == xiDeviceEvent->sourceid && + lastSeenEvent.evtype == xiDeviceEvent->event_type && + lastSeenEvent.detail == xiDeviceEvent->detail && + lastSeenEvent.root_x == xiDeviceEvent->root_x && + lastSeenEvent.root_y == xiDeviceEvent->root_y; + } else { + lastSeenEvent.isValid = true; + } + lastSeenEvent.sourceid = xiDeviceEvent->sourceid; + lastSeenEvent.evtype = xiDeviceEvent->event_type; + lastSeenEvent.detail = xiDeviceEvent->detail; + lastSeenEvent.root_x = xiDeviceEvent->root_x; + lastSeenEvent.root_y = xiDeviceEvent->root_y; + + if (isDuplicate) { + qCDebug(lcQpaXInputEvents, "Duplicate XI2 event %d", event->event_type); + // This sanity check ensures that special cases like QTBUG-59277 keep working. + lastSeenEvent.isValid = false; // An event can be a duplicate only once. + } + + return isDuplicate; +} + +} // namespace + void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) { auto *xiEvent = reinterpret_cast(event); - setTime(xiEvent->time); - if (m_xiSlavePointerIds.contains(xiEvent->deviceid) && xiEvent->event_type != XCB_INPUT_PROPERTY) { - if (!m_duringSystemMoveResize) - return; - if (xiEvent->event == XCB_NONE) - return; + if (m_xiSlavePointerIds.contains(xiEvent->deviceid)) { + if (!(xiEvent->event_type == XCB_INPUT_BUTTON_PRESS + || xiEvent->event_type == XCB_INPUT_BUTTON_RELEASE + || xiEvent->event_type == XCB_INPUT_MOTION)) { + if (!m_duringSystemMoveResize) + return; + if (xiEvent->event == XCB_NONE) + return; + + if (xiEvent->event_type == XCB_INPUT_TOUCH_END) + abortSystemMoveResize(xiEvent->event); - if (xiEvent->event_type == XCB_INPUT_BUTTON_RELEASE - && xiEvent->detail == XCB_BUTTON_INDEX_1 ) { - abortSystemMoveResize(xiEvent->event); - } else if (xiEvent->event_type == XCB_INPUT_TOUCH_END) { - abortSystemMoveResize(xiEvent->event); - return; - } else { return; } } @@ -710,11 +783,27 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) switch (xiEvent->event_type) { case XCB_INPUT_BUTTON_PRESS: case XCB_INPUT_BUTTON_RELEASE: - case XCB_INPUT_MOTION: + case XCB_INPUT_MOTION: { + if (isDuplicateEvent(event)) + return; + if (m_xiSlavePointerIds.contains(xiEvent->deviceid)) { + if (m_duringSystemMoveResize) { + if (xiEvent->event_type == XCB_INPUT_BUTTON_RELEASE + && xiEvent->detail == XCB_BUTTON_INDEX_1 ) { + abortSystemMoveResize(xiEvent->event); + } else { + return; + } + } + } + xiDeviceEvent = xiEvent; + eventListener = windowEventListenerFromId(xiDeviceEvent->event); + sourceDeviceId = xiDeviceEvent->sourceid; // use the actual device id instead of the master + break; + } case XCB_INPUT_TOUCH_BEGIN: case XCB_INPUT_TOUCH_UPDATE: - case XCB_INPUT_TOUCH_END: - { + case XCB_INPUT_TOUCH_END: { xiDeviceEvent = xiEvent; eventListener = windowEventListenerFromId(xiDeviceEvent->event); sourceDeviceId = xiDeviceEvent->sourceid; // use the actual device id instead of the master