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>
This commit is contained in:
parent
8ca19dde55
commit
097b0a5316
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user