From e67ebe8ba4f59a4554a47fa26e9c374b1022d36f Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Thu, 21 Aug 2014 10:45:27 +0200 Subject: [PATCH] Listen to XI2 deviceChanged events to reset last position Some drivers appears to reset their scroll positions when make the primary point. This patch listens for the device changed event and uses the information in it to update the position. Task-number: QTBUG-40829 Change-Id: If354dfde6300ceb2b81be14ea24594ab0658a6aa Reviewed-by: Alex Blasche --- src/plugins/platforms/xcb/qxcbconnection.h | 2 + .../platforms/xcb/qxcbconnection_xi2.cpp | 76 ++++++++++++++++--- 2 files changed, 68 insertions(+), 10 deletions(-) diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 7286b6b89b..4d0596550c 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -506,6 +506,7 @@ private: XInput2TouchDeviceData *touchDeviceForId(int id); void xi2HandleEvent(xcb_ge_event_t *event); void xi2HandleHierachyEvent(void *event); + void xi2HandleDeviceChangedEvent(void *event); int m_xiOpCode, m_xiEventBase, m_xiErrorBase; #ifndef QT_NO_TABLETEVENT struct TabletData { @@ -539,6 +540,7 @@ private: Qt::Orientations legacyOrientations; QPointF lastScrollPosition; }; + void updateScrollingDevice(ScrollingDevice& scrollingDevice, int num_classes, void *classes); void xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice); QHash m_scrollingDevices; #endif // XCB_USE_XINPUT2 diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp index 90d323ed34..60ae062586 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -346,6 +346,7 @@ void QXcbConnection::xi2Select(xcb_window_t window) // Listen for hotplug events XIEventMask xiEventMask; bitMask = XI_HierarchyChangedMask; + bitMask |= XI_DeviceChangedMask; xiEventMask.deviceid = XIAllDevices; xiEventMask.mask_len = sizeof(bitMask); xiEventMask.mask = xiBitMask; @@ -464,6 +465,11 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) xi2HandleHierachyEvent(xiEvent); return; } + if (xiEvent->evtype == XI_DeviceChanged) { + xi2HandleDeviceChangedEvent(xiEvent); + return; + } + #ifndef QT_NO_TABLETEVENT for (int i = 0; i < m_tabletData.count(); ++i) { if (m_tabletData.at(i).deviceId == xiEvent->deviceid) { @@ -624,6 +630,64 @@ void QXcbConnection::xi2HandleHierachyEvent(void *event) } } +void QXcbConnection::xi2HandleDeviceChangedEvent(void *event) +{ + xXIDeviceChangedEvent *xiEvent = reinterpret_cast(event); + + // ### If a slave device changes (XIDeviceChange), we should probably run setup on it again. + if (xiEvent->reason != XISlaveSwitch) + return; + +#ifdef XCB_USE_XINPUT21 + // This code handles broken scrolling device drivers that reset absolute positions + // when they are made active. Whenever a new slave device is made active the + // primary pointer sends a DeviceChanged event with XISlaveSwitch, and the new + // active slave in sourceid. + + QHash::iterator device = m_scrollingDevices.find(xiEvent->sourceid); + if (device == m_scrollingDevices.end()) + return; + + int nrDevices = 0; + XIDeviceInfo* xiDeviceInfo = XIQueryDevice(static_cast(m_xlib_display), xiEvent->sourceid, &nrDevices); + if (nrDevices <= 0) { + qCDebug(lcQpaXInputDevices, "scrolling device %d no longer present", xiEvent->sourceid); + return; + } + updateScrollingDevice(*device, xiDeviceInfo->num_classes, xiDeviceInfo->classes); + XIFreeDeviceInfo(xiDeviceInfo); +#endif +} + +void QXcbConnection::updateScrollingDevice(ScrollingDevice &scrollingDevice, int num_classes, void *classInfo) +{ +#ifdef XCB_USE_XINPUT21 + XIAnyClassInfo **classes = reinterpret_cast(classInfo); + QPointF lastScrollPosition; + if (lcQpaXInput().isDebugEnabled()) + lastScrollPosition = scrollingDevice.lastScrollPosition; + for (int c = 0; c < num_classes; ++c) { + if (classes[c]->type == XIValuatorClass) { + XIValuatorClassInfo *vci = reinterpret_cast(classes[c]); + const int valuatorAtom = qatom(vci->label); + if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel) + scrollingDevice.lastScrollPosition.setX(vci->value); + else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel) + scrollingDevice.lastScrollPosition.setY(vci->value); + } + } + if (lcQpaXInput().isDebugEnabled() && lastScrollPosition != scrollingDevice.lastScrollPosition) + qCDebug(lcQpaXInput, "scrolling device %d moved from (%f, %f) to (%f, %f)", scrollingDevice.deviceId, + lastScrollPosition.x(), lastScrollPosition.y(), + scrollingDevice.lastScrollPosition.x(), + scrollingDevice.lastScrollPosition.y()); +#else + Q_UNUSED(scrollingDevice); + Q_UNUSED(num_classes); + Q_UNUSED(classInfo); +#endif +} + void QXcbConnection::handleEnterEvent(const xcb_enter_notify_event_t *) { #ifdef XCB_USE_XINPUT21 @@ -634,19 +698,11 @@ void QXcbConnection::handleEnterEvent(const xcb_enter_notify_event_t *) int nrDevices = 0; XIDeviceInfo* xiDeviceInfo = XIQueryDevice(static_cast(m_xlib_display), scrollingDevice.deviceId, &nrDevices); if (nrDevices <= 0) { + qCDebug(lcQpaXInputDevices, "scrolling device %d no longer present", scrollingDevice.deviceId); it = m_scrollingDevices.erase(it); continue; } - for (int c = 0; c < xiDeviceInfo->num_classes; ++c) { - if (xiDeviceInfo->classes[c]->type == XIValuatorClass) { - XIValuatorClassInfo *vci = reinterpret_cast(xiDeviceInfo->classes[c]); - const int valuatorAtom = qatom(vci->label); - if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel) - scrollingDevice.lastScrollPosition.setX(vci->value); - else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel) - scrollingDevice.lastScrollPosition.setY(vci->value); - } - } + updateScrollingDevice(scrollingDevice, xiDeviceInfo->num_classes, xiDeviceInfo->classes); XIFreeDeviceInfo(xiDeviceInfo); ++it; }