diff options
Diffstat (limited to 'src/plugins/platforms/xcb/qxcbconnection_xi2.cpp')
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection_xi2.cpp | 117 |
1 files changed, 84 insertions, 33 deletions
diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp index c995a27d33..4f62a1880b 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -15,7 +15,9 @@ #include <xcb/xinput.h> +#if QT_CONFIG(gestures) #define QT_XCB_HAS_TOUCHPAD_GESTURES (XCB_INPUT_MINOR_VERSION >= 4) +#endif using namespace Qt::StringLiterals; @@ -239,6 +241,7 @@ void QXcbConnection::xi2SetupSlavePointerDevice(void *info, bool removeExisting, const QByteArray nameRaw = QByteArray(xcb_input_xi_device_info_name(deviceInfo), xcb_input_xi_device_info_name_length(deviceInfo)); const QString name = QString::fromUtf8(nameRaw); + m_xiSlavePointerIds.append(deviceInfo->deviceid); qCDebug(lcQpaXInputDevices) << "input device " << name << "ID" << deviceInfo->deviceid; #if QT_CONFIG(tabletevent) TabletData tabletData; @@ -442,12 +445,15 @@ void QXcbConnection::xi2SetupSlavePointerDevice(void *info, bool removeExisting, } } +/*! + Find all X11 input devices at startup, or react to a device hierarchy event, + and create/delete the corresponding QInputDevice instances as necessary. + Afterwards, we expect QInputDevice::devices() to contain only the + Qt-relevant devices that \c {xinput list} reports. The parent of each master + device is this QXcbConnection object; the parent of each slave is its master. +*/ void QXcbConnection::xi2SetupDevices() { -#if QT_CONFIG(tabletevent) - m_tabletData.clear(); -#endif - m_touchDevices.clear(); m_xiMasterPointerIds.clear(); auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, xcb_connection(), XCB_INPUT_DEVICE_ALL); @@ -456,6 +462,18 @@ void QXcbConnection::xi2SetupDevices() return; } + // Start with all known devices; remove the ones that still exist. + // Afterwards, previousDevices will be the list of those that we should delete. + QList<const QInputDevice *> previousDevices = QInputDevice::devices(); + // Return true if the device with the given systemId is new; + // otherwise remove it from previousDevices and return false. + auto newOrKeep = [&previousDevices](qint64 systemId) { + // if nothing is removed from previousDevices, it's a new device + return !previousDevices.removeIf([systemId](const QInputDevice *dev) { + return dev->systemId() == systemId; + }); + }; + // XInput doesn't provide a way to identify "seats"; but each device has an attachment to another device. // So we make up a seatId: master-keyboard-id << 16 | master-pointer-id. @@ -464,17 +482,21 @@ void QXcbConnection::xi2SetupDevices() xcb_input_xi_device_info_t *deviceInfo = it.data; switch (deviceInfo->type) { case XCB_INPUT_DEVICE_TYPE_MASTER_KEYBOARD: { - auto dev = new QInputDevice(QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo)), - deviceInfo->deviceid, QInputDevice::DeviceType::Keyboard, - QString::number(deviceInfo->deviceid << 16 | deviceInfo->attachment, 16), this); - QWindowSystemInterface::registerInputDevice(dev); + if (newOrKeep(deviceInfo->deviceid)) { + auto dev = new QInputDevice(QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo)), + deviceInfo->deviceid, QInputDevice::DeviceType::Keyboard, + QString::number(deviceInfo->deviceid << 16 | deviceInfo->attachment, 16), this); + QWindowSystemInterface::registerInputDevice(dev); + } } break; case XCB_INPUT_DEVICE_TYPE_MASTER_POINTER: { m_xiMasterPointerIds.append(deviceInfo->deviceid); - auto dev = new QXcbScrollingDevice(QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo)), deviceInfo->deviceid, - QInputDevice::Capability::Position | QInputDevice::Capability::Scroll | QInputDevice::Capability::Hover, - 32, QString::number(deviceInfo->attachment << 16 | deviceInfo->deviceid, 16), this); - QWindowSystemInterface::registerInputDevice(dev); + if (newOrKeep(deviceInfo->deviceid)) { + auto dev = new QXcbScrollingDevice(QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo)), deviceInfo->deviceid, + QInputDevice::Capability::Position | QInputDevice::Capability::Scroll | QInputDevice::Capability::Hover, + 32, QString::number(deviceInfo->attachment << 16 | deviceInfo->deviceid, 16), this); + QWindowSystemInterface::registerInputDevice(dev); + } continue; } break; default: @@ -491,23 +513,36 @@ void QXcbConnection::xi2SetupDevices() // already registered break; case XCB_INPUT_DEVICE_TYPE_SLAVE_POINTER: { - m_xiSlavePointerIds.append(deviceInfo->deviceid); - QInputDevice *master = const_cast<QInputDevice *>(QInputDevicePrivate::fromId(deviceInfo->attachment)); - Q_ASSERT(master); - xi2SetupSlavePointerDevice(deviceInfo, false, qobject_cast<QPointingDevice *>(master)); + if (newOrKeep(deviceInfo->deviceid)) { + m_xiSlavePointerIds.append(deviceInfo->deviceid); + QInputDevice *master = const_cast<QInputDevice *>(QInputDevicePrivate::fromId(deviceInfo->attachment)); + Q_ASSERT(master); + xi2SetupSlavePointerDevice(deviceInfo, false, qobject_cast<QPointingDevice *>(master)); + } } break; case XCB_INPUT_DEVICE_TYPE_SLAVE_KEYBOARD: { - QInputDevice *master = const_cast<QInputDevice *>(QInputDevicePrivate::fromId(deviceInfo->attachment)); - Q_ASSERT(master); - QWindowSystemInterface::registerInputDevice(new QInputDevice( - QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo)), deviceInfo->deviceid, - QInputDevice::DeviceType::Keyboard, master->seatName(), master)); + if (newOrKeep(deviceInfo->deviceid)) { + QInputDevice *master = const_cast<QInputDevice *>(QInputDevicePrivate::fromId(deviceInfo->attachment)); + Q_ASSERT(master); + QWindowSystemInterface::registerInputDevice(new QInputDevice( + QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo)), deviceInfo->deviceid, + QInputDevice::DeviceType::Keyboard, master->seatName(), master)); + } } break; case XCB_INPUT_DEVICE_TYPE_FLOATING_SLAVE: break; } } + // previousDevices is now the list of those that are no longer found + qCDebug(lcQpaXInputDevices) << "removed" << previousDevices; + for (auto it = previousDevices.constBegin(); it != previousDevices.constEnd(); ++it) { + const auto id = (*it)->systemId(); + m_xiSlavePointerIds.removeAll(id); + m_touchDevices.remove(id); + } + qDeleteAll(previousDevices); + if (m_xiMasterPointerIds.size() > 1) qCDebug(lcQpaXInputDevices) << "multi-pointer X detected"; } @@ -730,6 +765,8 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) if (auto device = QPointingDevicePrivate::pointingDeviceById(sourceDeviceId)) xi2HandleScrollEvent(event, device); + else + qCDebug(lcQpaXInputEvents) << "scroll event from unregistered device" << sourceDeviceId; if (xiDeviceEvent) { switch (xiDeviceEvent->event_type) { @@ -776,7 +813,16 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo { auto *xiDeviceEvent = reinterpret_cast<xcb_input_touch_begin_event_t *>(xiDevEvent); TouchDeviceData *dev = touchDeviceForId(xiDeviceEvent->sourceid); - Q_ASSERT(dev); + if (!dev) { + qCDebug(lcQpaXInputEvents) << "didn't find the dev for given sourceid - " << xiDeviceEvent->sourceid + << ", try to repopulate xi2 devices"; + xi2SetupDevices(); + dev = touchDeviceForId(xiDeviceEvent->sourceid); + if (!dev) { + qCDebug(lcQpaXInputEvents) << "still can't find the dev for it, give up."; + return; + } + } const bool firstTouch = dev->touchPoints.isEmpty(); if (xiDeviceEvent->event_type == XCB_INPUT_TOUCH_BEGIN) { QWindowSystemInterface::TouchPoint tp; @@ -1065,11 +1111,15 @@ bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab) void QXcbConnection::xi2HandleHierarchyEvent(void *event) { auto *xiEvent = reinterpret_cast<xcb_input_hierarchy_event_t *>(event); - // We only care about hotplugged devices - if (!(xiEvent->flags & (XCB_INPUT_HIERARCHY_MASK_SLAVE_REMOVED | XCB_INPUT_HIERARCHY_MASK_SLAVE_ADDED))) - return; - - xi2SetupDevices(); + // We care about hotplugged devices (slaves) and master devices. + // We don't report anything for DEVICE_ENABLED or DEVICE_DISABLED + // (but often that goes with adding or removal anyway). + // We don't react to SLAVE_ATTACHED or SLAVE_DETACHED either. + if (xiEvent->flags & (XCB_INPUT_HIERARCHY_MASK_MASTER_ADDED | + XCB_INPUT_HIERARCHY_MASK_MASTER_REMOVED | + XCB_INPUT_HIERARCHY_MASK_SLAVE_REMOVED | + XCB_INPUT_HIERARCHY_MASK_SLAVE_ADDED)) + xi2SetupDevices(); } #if QT_XCB_HAS_TOUCHPAD_GESTURES @@ -1234,6 +1284,9 @@ void QXcbConnection::xi2HandleDeviceChangedEvent(void *event) auto *xiEvent = reinterpret_cast<xcb_input_device_changed_event_t *>(event); switch (xiEvent->reason) { case XCB_INPUT_CHANGE_REASON_DEVICE_CHANGE: { + // Don't call xi2SetupSlavePointerDevice() again for an already-known device, and never for a master. + if (m_xiMasterPointerIds.contains(xiEvent->deviceid) || m_xiSlavePointerIds.contains(xiEvent->deviceid)) + return; auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, xcb_connection(), xiEvent->sourceid); if (!reply || reply->num_infos <= 0) return; @@ -1485,6 +1538,7 @@ bool QXcbConnection::xi2HandleTabletEvent(const void *event, TabletData *tabletD if (!tool && ptr[_WACSER_TOOL_SERIAL]) tool = ptr[_WACSER_TOOL_SERIAL]; + QWindow *win = nullptr; // TODO QTBUG-111400 get the position somehow, then the window // The property change event informs us which tool is in proximity or which one left proximity. if (tool) { const QPointingDevice *dev = tabletToolInstance(nullptr, tabletData->name, @@ -1493,22 +1547,19 @@ bool QXcbConnection::xi2HandleTabletEvent(const void *event, TabletData *tabletD tabletData->inProximity = true; tabletData->tool = dev->type(); tabletData->serialId = qint64(ptr[_WACSER_TOOL_SERIAL]); - QWindowSystemInterface::handleTabletEnterProximityEvent(ev->time, - int(tabletData->tool), int(tabletData->pointerType), tabletData->serialId); + QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(win, ev->time, dev, true); // enter } else { tool = ptr[_WACSER_LAST_TOOL_ID]; // Workaround for http://sourceforge.net/p/linuxwacom/bugs/246/ // e.g. on Thinkpad Helix, tool ID will be 0 and serial will be 1 if (!tool) tool = ptr[_WACSER_LAST_TOOL_SERIAL]; - const QInputDevice *dev = QInputDevicePrivate::fromId(tabletData->deviceId); + auto *dev = qobject_cast<const QPointingDevice *>(QInputDevicePrivate::fromId(tabletData->deviceId)); Q_ASSERT(dev); tabletData->tool = dev->type(); tabletData->inProximity = false; tabletData->serialId = qint64(ptr[_WACSER_LAST_TOOL_SERIAL]); - // TODO why doesn't it just take QPointingDevice* - QWindowSystemInterface::handleTabletLeaveProximityEvent(ev->time, - int(tabletData->tool), int(tabletData->pointerType), tabletData->serialId); + QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(win, ev->time, dev, false); // leave } // TODO maybe have a hash of tabletData->deviceId to device data so we can // look up the tablet name here, and distinguish multiple tablets |