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 | |
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>
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.cpp | 42 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.h | 18 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection_xi2.cpp | 130 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbwindow.cpp | 2 |
4 files changed, 164 insertions, 28 deletions
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 5e632bff7f..0cff92dacc 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -1441,6 +1441,10 @@ static const char * xcb_atomnames = { "Abs Distance\0" "Wacom Serial IDs\0" "INTEGER\0" + "Rel Horiz Wheel\0" + "Rel Vert Wheel\0" + "Rel Horiz Scroll\0" + "Rel Vert Scroll\0" #if XCB_USE_MAEMO_WINDOW_PROPERTIES "_MEEGOTOUCH_ORIENTATION_ANGLE\0" #endif @@ -1722,21 +1726,23 @@ bool QXcbConnection::hasEgl() const #endif // defined(XCB_USE_EGL) #if defined(XCB_USE_XINPUT2) || defined(XCB_USE_XINPUT2_MAEMO) -// Borrowed from libXi. -int QXcbConnection::xi2CountBits(unsigned char *ptr, int len) +static int xi2ValuatorOffset(unsigned char *maskPtr, int maskLen, int number) { - int bits = 0; - int i; - unsigned char x; - - for (i = 0; i < len; i++) { - x = ptr[i]; - while (x > 0) { - bits += (x & 0x1); - x >>= 1; + int offset = 0; + for (int i = 0; i < maskLen; i++) { + if (number < 8) { + if ((maskPtr[i] & (1 << number)) == 0) + return -1; } + for (int j = 0; j < 8; j++) { + if (j == number) + return offset; + if (maskPtr[i] & (1 << j)) + offset++; + } + number -= 8; } - return bits; + return -1; } bool QXcbConnection::xi2GetValuatorValueIfSet(void *event, int valuatorNum, double *value) @@ -1745,13 +1751,13 @@ bool QXcbConnection::xi2GetValuatorValueIfSet(void *event, int valuatorNum, doub unsigned char *buttonsMaskAddr = (unsigned char*)&xideviceevent[1]; unsigned char *valuatorsMaskAddr = buttonsMaskAddr + xideviceevent->buttons_len * 4; FP3232 *valuatorsValuesAddr = (FP3232*)(valuatorsMaskAddr + xideviceevent->valuators_len * 4); - int numValuatorValues = xi2CountBits(valuatorsMaskAddr, xideviceevent->valuators_len * 4); - // This relies on all bit being set until a certain number i.e. it doesn't support only bit 0 and 5 being set in the mask. - // Just like the original code, works for now. - if (valuatorNum >= numValuatorValues) + + int valuatorOffset = xi2ValuatorOffset(valuatorsMaskAddr, xideviceevent->valuators_len, valuatorNum); + if (valuatorOffset < 0) return false; - *value = valuatorsValuesAddr[valuatorNum].integral; - *value += ((double)valuatorsValuesAddr[valuatorNum].frac / (1 << 16) / (1 << 16)); + + *value = valuatorsValuesAddr[valuatorOffset].integral; + *value += ((double)valuatorsValuesAddr[valuatorOffset].frac / (1 << 16) / (1 << 16)); return true; } diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 9e64c37b94..71f5ce13fb 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -69,9 +69,12 @@ struct XInput2MaemoData; #elif XCB_USE_XINPUT2 #include <X11/extensions/XI2.h> +#ifdef XIScrollClass +#define XCB_USE_XINPUT21 // XI 2.1 adds smooth scrolling support #ifdef XI_TouchBeginMask #define XCB_USE_XINPUT22 // XI 2.2 adds multi-point touch support #endif +#endif struct XInput2DeviceData; #endif struct xcb_randr_get_output_info_reply_t; @@ -271,6 +274,10 @@ namespace QXcbAtom { AbsDistance, WacomSerialIDs, INTEGER, + RelHorizWheel, + RelVertWheel, + RelHorizScroll, + RelVertScroll, #if XCB_USE_MAEMO_WINDOW_PROPERTIES MeegoTouchOrientationAngle, @@ -499,10 +506,19 @@ private: void xi2ReportTabletEvent(const TabletData &tabletData, void *event); QVector<TabletData> m_tabletData; #endif + struct ScrollingDevice { + ScrollingDevice() : deviceId(0), verticalIndex(0), horizontalIndex(0), orientations(0) { } + int deviceId; + int verticalIndex, horizontalIndex; + double verticalIncrement, horizontalIncrement; + Qt::Orientations orientations; + QPointF lastScrollPosition; + }; + void xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice); + QHash<int, ScrollingDevice> m_scrollingDevices; #endif // XCB_USE_XINPUT2 #if defined(XCB_USE_XINPUT2) || defined(XCB_USE_XINPUT2_MAEMO) - static int xi2CountBits(unsigned char *ptr, int len); static bool xi2GetValuatorValueIfSet(void *event, int valuatorNum, double *value); static bool xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event, int opCode); #endif 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) { diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 81134ced49..407adc29ee 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -1689,6 +1689,7 @@ void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event) Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state); if (isWheel) { +#ifndef XCB_USE_XINPUT21 // Logic borrowed from qapplication_x11.cpp int delta = 120 * ((event->detail == 4 || event->detail == 6) ? 1 : -1); bool hor = (((event->detail == 4 || event->detail == 5) @@ -1697,6 +1698,7 @@ void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event) QWindowSystemInterface::handleWheelEvent(window(), event->time, local, global, delta, hor ? Qt::Horizontal : Qt::Vertical, modifiers); +#endif return; } |