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 <alexander.blasche@theqtcompany.com>
This commit is contained in:
Allan Sandfeld Jensen 2014-08-21 10:45:27 +02:00 committed by Allan Sandfeld Jensen
parent e558e71791
commit e67ebe8ba4
2 changed files with 68 additions and 10 deletions

View File

@ -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<int, ScrollingDevice> m_scrollingDevices;
#endif // XCB_USE_XINPUT2

View File

@ -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<xXIDeviceChangedEvent *>(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<int, ScrollingDevice>::iterator device = m_scrollingDevices.find(xiEvent->sourceid);
if (device == m_scrollingDevices.end())
return;
int nrDevices = 0;
XIDeviceInfo* xiDeviceInfo = XIQueryDevice(static_cast<Display *>(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<XIAnyClassInfo**>(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<XIValuatorClassInfo *>(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<Display *>(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<XIValuatorClassInfo *>(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;
}