diff options
author | Allan Sandfeld Jensen <allan.jensen@digia.com> | 2014-01-02 10:32:52 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-01-09 19:27:32 +0100 |
commit | 097b0a531642f79d8b240f89ca1eacca2ee59a5e (patch) | |
tree | 4beeffbb2ccf330e12c0028dbcaaf724ec4cc29e /src/plugins/platforms/xcb/qxcbconnection_xi2.cpp | |
parent | 8ca19dde556d4ec5a04d15af6d9985f2203ee4d5 (diff) |
Use XInput2 scrolling events to enable smoother scrolling
With this patch all wheel events will be generated by XInput2 where
available. This enables higher precision and smoother scrolling
especially from input devices such as touchpads on laptops.
[ChangeLog][Platform Specific Changes][X11 / XCB]Support XInput2 smooth scrolling events
Change-Id: I3b069ed92ad5c53e08af64baaece32de82e9b5c4
Reviewed-by: Shawn Rutledge <shawn.rutledge@digia.com>
Diffstat (limited to 'src/plugins/platforms/xcb/qxcbconnection_xi2.cpp')
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection_xi2.cpp | 130 |
1 files changed, 121 insertions, 9 deletions
diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp index 9b6d234375..d80b49ccbb 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qxcbconnection.h" +#include "qxcbkeyboard.h" #include "qxcbscreen.h" #include "qxcbwindow.h" #include "qtouchdevice.h" @@ -75,16 +76,20 @@ void QXcbConnection::initializeXInput2() #ifndef QT_NO_TABLETEVENT m_tabletData.clear(); #endif + m_scrollingDevices.clear(); Display *xDisplay = static_cast<Display *>(m_xlib_display); if (XQueryExtension(xDisplay, "XInputExtension", &m_xiOpCode, &m_xiEventBase, &m_xiErrorBase)) { int xiMajor = 2; m_xi2Minor = 2; // try 2.2 first, needed for TouchBegin/Update/End if (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) == BadRequest) { - m_xi2Minor = 0; // for tablet support 2.0 is enough - m_xi2Enabled = XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) != BadRequest; - } else { + m_xi2Minor = 1; // for smooth scrolling 2.1 is enough + if (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) == BadRequest) { + m_xi2Minor = 0; // for tablet support 2.0 is enough + m_xi2Enabled = XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) != BadRequest; + } else + m_xi2Enabled = true; + } else m_xi2Enabled = true; - } if (m_xi2Enabled) { if (Q_UNLIKELY(debug_xinput_devices)) #ifdef XCB_USE_XINPUT22 @@ -103,6 +108,7 @@ void QXcbConnection::initializeXInput2() #ifndef QT_NO_TABLETEVENT TabletData tabletData; #endif + ScrollingDevice scrollingDevice; for (int c = 0; c < devices[i].num_classes; ++c) { switch (devices[i].classes[c]->type) { case XIValuatorClass: { @@ -119,7 +125,29 @@ void QXcbConnection::initializeXInput2() tabletData.valuatorInfo[valuatorAtom] = info; } #endif // QT_NO_TABLETEVENT - } break; + 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); + break; + } +#ifdef XCB_USE_XINPUT21 + case XIScrollClass: { + XIScrollClassInfo *sci = reinterpret_cast<XIScrollClassInfo *>(devices[i].classes[c]); + scrollingDevice.deviceId = devices[i].deviceid; + if (sci->scroll_type == XIScrollTypeVertical) { + scrollingDevice.orientations |= Qt::Vertical; + scrollingDevice.verticalIndex = sci->number; + scrollingDevice.verticalIncrement = sci->increment; + } + else if (sci->scroll_type == XIScrollTypeHorizontal) { + scrollingDevice.orientations |= Qt::Horizontal; + scrollingDevice.horizontalIndex = sci->number; + scrollingDevice.horizontalIncrement = sci->increment; + } + break; + } +#endif default: break; } @@ -140,6 +168,15 @@ void QXcbConnection::initializeXInput2() qDebug() << " it's a tablet with pointer type" << tabletData.pointerType; } #endif // QT_NO_TABLETEVENT + +#ifdef XCB_USE_XINPUT21 + if (scrollingDevice.orientations) { + m_scrollingDevices.insert(scrollingDevice.deviceId, scrollingDevice); + if (Q_UNLIKELY(debug_xinput_devices)) + qDebug() << " it's a scrolling device"; + } +#endif + if (!isTablet) { XInput2DeviceData *dev = deviceForId(devices[i].deviceid); if (Q_UNLIKELY(debug_xinput_devices)) { @@ -213,6 +250,22 @@ void QXcbConnection::xi2Select(xcb_window_t window) XISelectEvents(xDisplay, window, xiEventMask.data(), m_tabletData.count()); } #endif // QT_NO_TABLETEVENT + +#ifdef XCB_USE_XINPUT21 + // Enable each scroll device + if (!m_scrollingDevices.isEmpty()) { + QVector<XIEventMask> xiEventMask(m_scrollingDevices.size()); + bitMask = XI_MotionMask; + int i=0; + Q_FOREACH (const ScrollingDevice& scrollingDevice, m_scrollingDevices) { + xiEventMask[i].deviceid = scrollingDevice.deviceId; + xiEventMask[i].mask_len = sizeof(bitMask); + xiEventMask[i].mask = xiBitMask; + i++; + } + XISelectEvents(xDisplay, window, xiEventMask.data(), m_scrollingDevices.size()); + } +#endif } XInput2DeviceData *QXcbConnection::deviceForId(int id) @@ -243,7 +296,8 @@ XInput2DeviceData *QXcbConnection::deviceForId(int id) type = QTouchDevice::TouchScreen; break; } - } break; + break; + } #endif // XCB_USE_XINPUT22 case XIValuatorClass: { XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo); @@ -260,7 +314,8 @@ XInput2DeviceData *QXcbConnection::deviceForId(int id) hasRelativeCoords = true; dev->size.setHeight((vci->max - vci->min) * 1000.0 / vci->resolution); } - } break; + break; + } } } if (type < 0 && caps && hasRelativeCoords) { @@ -287,12 +342,14 @@ XInput2DeviceData *QXcbConnection::deviceForId(int id) return dev; } -#ifdef XCB_USE_XINPUT22 +#if defined(XCB_USE_XINPUT21) || !defined(QT_NO_TABLETEVENT) static qreal fixed1616ToReal(FP1616 val) { return (qreal(val >> 16)) + (val & 0xFF) / (qreal)0xFF; } +#endif // defined(XCB_USE_XINPUT21) || !defined(QT_NO_TABLETEVENT) +#if defined(XCB_USE_XINPUT21) static qreal valuatorNormalized(double value, XIValuatorClassInfo *vci) { if (value > vci->max) @@ -301,7 +358,7 @@ static qreal valuatorNormalized(double value, XIValuatorClassInfo *vci) value = vci->min; return (value - vci->min) / (vci->max - vci->min); } -#endif // XCB_USE_XINPUT22 +#endif // XCB_USE_XINPUT21 void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) { @@ -317,6 +374,12 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) } #endif // QT_NO_TABLETEVENT +#ifdef XCB_USE_XINPUT21 + QHash<int, ScrollingDevice>::iterator device = m_scrollingDevices.find(xiEvent->deviceid); + if (device != m_scrollingDevices.end()) + xi2HandleScrollEvent(xiEvent, device.value()); +#endif // XCB_USE_XINPUT21 + #ifdef XCB_USE_XINPUT22 if (xiEvent->evtype == XI_TouchBegin || xiEvent->evtype == XI_TouchUpdate || xiEvent->evtype == XI_TouchEnd) { xXIDeviceEvent* xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event); @@ -461,6 +524,55 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) } } +void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice) +{ +#ifdef XCB_USE_XINPUT21 + xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event); + + if (xiEvent->evtype == XI_Motion) { + xXIDeviceEvent* xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event); + if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event)) { + QPoint rawDelta; + QPoint angleDelta; + double value; + if (scrollingDevice.orientations & Qt::Vertical) { + if (xi2GetValuatorValueIfSet(xiDeviceEvent, scrollingDevice.verticalIndex, &value)) { + double delta = scrollingDevice.lastScrollPosition.y() - value; + scrollingDevice.lastScrollPosition.setY(value); + angleDelta.setY((delta / scrollingDevice.verticalIncrement) * 120); + // We do not set "pixel" delta if it is only measured in ticks. + if (scrollingDevice.verticalIncrement > 1) + rawDelta.setY(delta); + } + } + if (scrollingDevice.orientations & Qt::Horizontal) { + if (xi2GetValuatorValueIfSet(xiDeviceEvent, scrollingDevice.horizontalIndex, &value)) { + double delta = scrollingDevice.lastScrollPosition.x() - value; + scrollingDevice.lastScrollPosition.setX(value); + angleDelta.setX((delta / scrollingDevice.horizontalIncrement) * 120); + // We do not set "pixel" delta if it is only measured in ticks. + if (scrollingDevice.horizontalIncrement > 1) + rawDelta.setX(delta); + } + } + if (!angleDelta.isNull()) { + QPoint local(fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y)); + QPoint global(fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y)); + Qt::KeyboardModifiers modifiers = keyboard()->translateModifiers(xiDeviceEvent->mods.effective_mods); + if (modifiers & Qt::AltModifier) { + std::swap(angleDelta.rx(), angleDelta.ry()); + std::swap(rawDelta.rx(), rawDelta.ry()); + } + QWindowSystemInterface::handleWheelEvent(platformWindow->window(), xiEvent->time, local, global, rawDelta, angleDelta, modifiers); + } + } + } +#else + Q_UNUSED(event); + Q_UNUSED(scrollingDevice); +#endif // XCB_USE_XINPUT21 +} + #ifndef QT_NO_TABLETEVENT bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData) { |