summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@digia.com>2014-01-02 10:32:52 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-01-09 19:27:32 +0100
commit097b0a531642f79d8b240f89ca1eacca2ee59a5e (patch)
tree4beeffbb2ccf330e12c0028dbcaaf724ec4cc29e
parent8ca19dde556d4ec5a04d15af6d9985f2203ee4d5 (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.cpp42
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.h18
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_xi2.cpp130
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.cpp2
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;
}