summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/xcb/qxcbconnection_xi2.cpp')
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_xi2.cpp94
1 files changed, 93 insertions, 1 deletions
diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
index 7ed61bff4e..01b1b37bb8 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
@@ -113,7 +113,7 @@ void QXcbConnection::xi2SelectDeviceEvents(xcb_window_t window)
}
qt_xcb_input_event_mask_t mask;
- mask.header.deviceid = XCB_INPUT_DEVICE_ALL_MASTER;
+ mask.header.deviceid = XCB_INPUT_DEVICE_ALL;
mask.header.mask_len = 1;
mask.mask = bitMask;
xcb_void_cookie_t cookie =
@@ -309,6 +309,7 @@ void QXcbConnection::xi2SetupDevices()
m_scrollingDevices.clear();
m_touchDevices.clear();
m_xiMasterPointerIds.clear();
+ m_floatingSlaveDevices.clear();
auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, m_connection, XCB_INPUT_DEVICE_ALL);
if (!reply) {
@@ -319,6 +320,10 @@ void QXcbConnection::xi2SetupDevices()
auto it = xcb_input_xi_query_device_infos_iterator(reply.get());
for (; it.rem; xcb_input_xi_device_info_next(&it)) {
xcb_input_xi_device_info_t *deviceInfo = it.data;
+ if (deviceInfo->type == XCB_INPUT_DEVICE_TYPE_FLOATING_SLAVE) {
+ m_floatingSlaveDevices.append(deviceInfo->deviceid);
+ continue;
+ }
if (deviceInfo->type == XCB_INPUT_DEVICE_TYPE_MASTER_POINTER) {
m_xiMasterPointerIds.append(deviceInfo->deviceid);
continue;
@@ -542,6 +547,72 @@ static inline qreal fixed1616ToReal(xcb_input_fp1616_t val)
}
#endif // QT_CONFIG(tabletevent)
+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:
+
+ - 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.
+
+ - 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.
+*/
+bool isDuplicateEvent(xcb_ge_event_t *event)
+{
+ struct qXIEvent {
+ bool isValid = false;
+ uint16_t sourceid;
+ uint8_t event_type;
+ uint32_t detail;
+ int32_t root_x;
+ int32_t root_y;
+ };
+ static qXIEvent lastSeenEvent;
+
+ bool isDuplicate = false;
+ auto xiDeviceEvent = reinterpret_cast<qt_xcb_input_device_event_t *>(event);
+ if (lastSeenEvent.isValid) {
+ isDuplicate = lastSeenEvent.sourceid == xiDeviceEvent->sourceid &&
+ lastSeenEvent.event_type == 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.event_type = xiDeviceEvent->event_type;
+ lastSeenEvent.detail = xiDeviceEvent->detail;
+ lastSeenEvent.root_x = xiDeviceEvent->root_x;
+ lastSeenEvent.root_y = xiDeviceEvent->root_y;
+
+ if (isDuplicate)
+ // 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<qt_xcb_input_device_event_t *>(event);
@@ -550,15 +621,31 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
xcb_input_enter_event_t *xiEnterEvent = nullptr;
QXcbWindowEventListener *eventListener = 0;
+ bool isTouchEvent = true;
switch (xiEvent->event_type) {
case XCB_INPUT_BUTTON_PRESS:
case XCB_INPUT_BUTTON_RELEASE:
case XCB_INPUT_MOTION:
+ isTouchEvent = false;
+ if (!xi2MouseEventsDisabled() && isDuplicateEvent(event))
+ return;
case XCB_INPUT_TOUCH_BEGIN:
case XCB_INPUT_TOUCH_UPDATE:
case XCB_INPUT_TOUCH_END:
{
xiDeviceEvent = xiEvent;
+
+ if (m_floatingSlaveDevices.contains(xiDeviceEvent->sourceid))
+ return; // Not interested in floating slave device events, only in attached slaves.
+
+ bool isSlaveEvent = xiDeviceEvent->deviceid == xiDeviceEvent->sourceid;
+ if (!xi2MouseEventsDisabled() && isTouchEvent && isSlaveEvent) {
+ // For touch events we want events only from master devices, at least
+ // currently there is no apparent reason why we would need to consider
+ // events from slave devices.
+ return;
+ }
+
eventListener = windowEventListenerFromId(xiDeviceEvent->event);
sourceDeviceId = xiDeviceEvent->sourceid; // use the actual device id instead of the master
break;
@@ -830,6 +917,11 @@ bool QXcbConnection::startSystemMoveResizeForTouchBegin(xcb_window_t window, con
return false;
}
+void QXcbConnection::abortSystemMoveResizeForTouch()
+{
+ m_startSystemMoveResizeInfo.window = XCB_NONE;
+}
+
bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
{
bool ok = false;