xcb: handle XI2 input button and motion events from slave devices
On Ubuntu 24.04, when the SuperKey is pressed, Qt only receives XI2 input events sent from the slave pointer devices on a mouse click or a mouse move, but does not handle those XI2 input events. Fixing this by allowing Qt to handle such an XI2 event and generate a QMouseEvent for it if it is not handled previously. Upon receipt of an aforementioned event, check if the event is a duplicate XI2 event. A duplicate XI2 event means that the XI2 event was sent from another device previously. - If it is not a duplicate event, the XI2 event is passed to QXcbWindow to be handled. Eventually, a QMouseEvent is generated and sent from the GUI for the XI2 event. - If it is a duplicate event, ignore it. Note, part of commit:3bc0f1724ae49c2fd7e6d7bcb650350d20d12246 is re-done with the fix. Fixes: QTBUG-110841 Pick-to: 6.7 6.5 6.2 5.15 Change-Id: I93beddef7261c37ddbe8bfdb6ae08c95f8f155c5 Reviewed-by: Liang Qi <liang.qi@qt.io> Reviewed-by: Axel Spoerl <axel.spoerl@qt.io> (cherry picked from commit b71be292780b858f2c55ce92601452e2ea946de2) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
432b7187e1
commit
65b6de2a20
@ -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<qt_xcb_input_device_event_t *>(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<qt_xcb_input_device_event_t *>(event);
|
||||
setTime(xiEvent->time);
|
||||
if (m_xiSlavePointerIds.contains(xiEvent->deviceid) && xiEvent->event_type != XCB_INPUT_PROPERTY) {
|
||||
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_BUTTON_RELEASE
|
||||
&& xiEvent->detail == XCB_BUTTON_INDEX_1 ) {
|
||||
if (xiEvent->event_type == XCB_INPUT_TOUCH_END)
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user