summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/xcb/qxcbconnection_xi2.cpp')
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_xi2.cpp1012
1 files changed, 552 insertions, 460 deletions
diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
index 26a9ba8d26..f90f189146 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
@@ -50,19 +50,6 @@
#include <X11/extensions/XInput2.h>
#include <X11/extensions/XI2proto.h>
-struct XInput2TouchDeviceData {
- XIDeviceInfo *xiDeviceInfo = nullptr;
- QTouchDevice *qtTouchDevice = nullptr;
- QHash<int, QWindowSystemInterface::TouchPoint> touchPoints;
- QHash<int, QPointF> pointPressedPosition; // in screen coordinates where each point was pressed
-
- // Stuff that is relevant only for touchpads
- QPointF firstPressedPosition; // in screen coordinates where the first point was pressed
- QPointF firstPressedNormalPosition; // device coordinates (0 to 1, 0 to 1) where the first point was pressed
- QSizeF size; // device size in mm
- bool providesTouchOrientation = false;
-};
-
void QXcbConnection::initializeXInput2()
{
// TODO Qt 6 (or perhaps earlier): remove these redundant env variables
@@ -70,30 +57,37 @@ void QXcbConnection::initializeXInput2()
const_cast<QLoggingCategory&>(lcQpaXInput()).setEnabled(QtDebugMsg, true);
if (qEnvironmentVariableIsSet("QT_XCB_DEBUG_XINPUT_DEVICES"))
const_cast<QLoggingCategory&>(lcQpaXInputDevices()).setEnabled(QtDebugMsg, true);
+
Display *xDisplay = static_cast<Display *>(m_xlib_display);
if (XQueryExtension(xDisplay, "XInputExtension", &m_xiOpCode, &m_xiEventBase, &m_xiErrorBase)) {
int xiMajor = 2;
- // try 2.2 first, needed for TouchBegin/Update/End
- if (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor) == BadRequest) {
- 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) {
-#ifdef XCB_USE_XINPUT22
- qCDebug(lcQpaXInputDevices, "XInput version %d.%d is available and Qt supports 2.2 or greater", xiMajor, m_xi2Minor);
- m_startSystemResizeInfo.window = XCB_NONE;
+#if defined(XCB_USE_XINPUT22)
+ m_xi2Minor = 2; // for touch support 2.2 is enough
+#elif defined(XCB_USE_XINPUT21)
+ m_xi2Minor = 1; // for smooth scrolling 2.1 is enough
#else
- qCDebug(lcQpaXInputDevices, "XInput version %d.%d is available and Qt supports 2.0", xiMajor, m_xi2Minor);
+ m_xi2Minor = 0; // for tablet support 2.0 is enough
#endif
+ qCDebug(lcQpaXInput, "Plugin build with support for XInput 2 version up "
+ "to %d.%d", xiMajor, m_xi2Minor);
+
+ switch (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor)) {
+ case Success:
+ // Server's supported version can be lower than the version we have
+ // announced to support. In this case Qt client will be limited by
+ // X server's supported version.
+ qCDebug(lcQpaXInput, "Using XInput version %d.%d", xiMajor, m_xi2Minor);
+ m_xi2Enabled = true;
+ xi2SetupDevices();
xi2SelectStateEvents();
+ break;
+ case BadRequest: // Must be an X server with XInput 1
+ qCDebug(lcQpaXInput, "X server does not support XInput 2");
+ break;
+ default: // BadValue
+ qCDebug(lcQpaXInput, "Internal error");
+ break;
}
-
- xi2SetupDevices();
}
}
@@ -113,377 +107,434 @@ void QXcbConnection::xi2SelectStateEvents()
XISelectEvents(dpy, DefaultRootWindow(dpy), &xiEventMask, 1);
}
-void QXcbConnection::xi2SetupDevices()
+void QXcbConnection::xi2SelectDeviceEvents(xcb_window_t window)
{
-#if QT_CONFIG(tabletevent)
- m_tabletData.clear();
+ if (window == rootWindow())
+ return;
+
+ unsigned int bitMask = 0;
+ unsigned char *xiBitMask = reinterpret_cast<unsigned char *>(&bitMask);
+ bitMask |= XI_ButtonPressMask;
+ bitMask |= XI_ButtonReleaseMask;
+ bitMask |= XI_MotionMask;
+ // There is a check for enter/leave events in plain xcb enter/leave event handler,
+ // core enter/leave events will be ignored in this case.
+ bitMask |= XI_EnterMask;
+ bitMask |= XI_LeaveMask;
+ bitMask |= XI_PropertyEventMask;
+#ifdef XCB_USE_XINPUT22
+ if (isAtLeastXI22()) {
+ bitMask |= XI_TouchBeginMask;
+ bitMask |= XI_TouchUpdateMask;
+ bitMask |= XI_TouchEndMask;
+ }
#endif
- m_scrollingDevices.clear();
- if (!m_xi2Enabled)
- return;
+ XIEventMask mask;
+ mask.mask_len = sizeof(bitMask);
+ mask.mask = xiBitMask;
+ mask.deviceid = XIAllMasterDevices;
+ Display *dpy = static_cast<Display *>(m_xlib_display);
+ Status result = XISelectEvents(dpy, window, &mask, 1);
+ if (result == Success)
+ QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
+ else
+ qCDebug(lcQpaXInput, "failed to select events, window %x, result %d", window, result);
+}
- Display *xDisplay = static_cast<Display *>(m_xlib_display);
- int deviceCount = 0;
- XIDeviceInfo *devices = XIQueryDevice(xDisplay, XIAllDevices, &deviceCount);
- for (int i = 0; i < deviceCount; ++i) {
- // Only non-master pointing devices are relevant here.
- if (devices[i].use != XISlavePointer)
- continue;
- qCDebug(lcQpaXInputDevices) << "input device " << devices[i].name << "ID" << devices[i].deviceid;
+void QXcbConnection::xi2SetupDevice(void *info, bool removeExisting)
+{
+ XIDeviceInfo *deviceInfo = reinterpret_cast<XIDeviceInfo *>(info);
+ if (removeExisting) {
#if QT_CONFIG(tabletevent)
- TabletData tabletData;
+ for (int i = 0; i < m_tabletData.count(); ++i) {
+ if (m_tabletData.at(i).deviceId == deviceInfo->deviceid) {
+ m_tabletData.remove(i);
+ break;
+ }
+ }
#endif
- ScrollingDevice scrollingDevice;
- for (int c = 0; c < devices[i].num_classes; ++c) {
- switch (devices[i].classes[c]->type) {
- case XIValuatorClass: {
- XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(devices[i].classes[c]);
- const int valuatorAtom = qatom(vci->label);
- qCDebug(lcQpaXInputDevices) << " has valuator" << atomName(vci->label) << "recognized?" << (valuatorAtom < QXcbAtom::NAtoms);
+ m_scrollingDevices.remove(deviceInfo->deviceid);
+ m_touchDevices.remove(deviceInfo->deviceid);
+ }
+
+ qCDebug(lcQpaXInputDevices) << "input device " << deviceInfo->name << "ID" << deviceInfo->deviceid;
#if QT_CONFIG(tabletevent)
- if (valuatorAtom < QXcbAtom::NAtoms) {
- TabletData::ValuatorClassInfo info;
- info.minVal = vci->min;
- info.maxVal = vci->max;
- info.number = vci->number;
- tabletData.valuatorInfo[valuatorAtom] = info;
- }
-#endif // QT_CONFIG(tabletevent)
- 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;
+ TabletData tabletData;
+#endif
+ ScrollingDevice scrollingDevice;
+ for (int c = 0; c < deviceInfo->num_classes; ++c) {
+ XIAnyClassInfo *classinfo = deviceInfo->classes[c];
+ switch (classinfo->type) {
+ case XIValuatorClass: {
+ XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
+ const int valuatorAtom = qatom(vci->label);
+ qCDebug(lcQpaXInputDevices) << " has valuator" << atomName(vci->label) << "recognized?" << (valuatorAtom < QXcbAtom::NAtoms);
+#if QT_CONFIG(tabletevent)
+ if (valuatorAtom < QXcbAtom::NAtoms) {
+ TabletData::ValuatorClassInfo info;
+ info.minVal = vci->min;
+ info.maxVal = vci->max;
+ info.number = vci->number;
+ tabletData.valuatorInfo[valuatorAtom] = info;
}
+#endif // QT_CONFIG(tabletevent)
+ 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]);
- 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;
+ case XIScrollClass: {
+ XIScrollClassInfo *sci = reinterpret_cast<XIScrollClassInfo *>(classinfo);
+ if (sci->scroll_type == XIScrollTypeVertical) {
+ scrollingDevice.orientations |= Qt::Vertical;
+ scrollingDevice.verticalIndex = sci->number;
+ scrollingDevice.verticalIncrement = sci->increment;
}
- case XIButtonClass: {
- XIButtonClassInfo *bci = reinterpret_cast<XIButtonClassInfo *>(devices[i].classes[c]);
- if (bci->num_buttons >= 5) {
- Atom label4 = bci->labels[3];
- Atom label5 = bci->labels[4];
- // Some drivers have no labels on the wheel buttons, some have no label on just one and some have no label on
- // button 4 and the wrong one on button 5. So we just check that they are not labelled with unrelated buttons.
- if ((!label4 || qatom(label4) == QXcbAtom::ButtonWheelUp || qatom(label4) == QXcbAtom::ButtonWheelDown) &&
- (!label5 || qatom(label5) == QXcbAtom::ButtonWheelUp || qatom(label5) == QXcbAtom::ButtonWheelDown))
- scrollingDevice.legacyOrientations |= Qt::Vertical;
- }
- if (bci->num_buttons >= 7) {
- Atom label6 = bci->labels[5];
- Atom label7 = bci->labels[6];
- if ((!label6 || qatom(label6) == QXcbAtom::ButtonHorizWheelLeft) && (!label7 || qatom(label7) == QXcbAtom::ButtonHorizWheelRight))
- scrollingDevice.legacyOrientations |= Qt::Horizontal;
- }
- qCDebug(lcQpaXInputDevices, " has %d buttons", bci->num_buttons);
- break;
+ else if (sci->scroll_type == XIScrollTypeHorizontal) {
+ scrollingDevice.orientations |= Qt::Horizontal;
+ scrollingDevice.horizontalIndex = sci->number;
+ scrollingDevice.horizontalIncrement = sci->increment;
+ }
+ break;
+ }
+ case XIButtonClass: {
+ XIButtonClassInfo *bci = reinterpret_cast<XIButtonClassInfo *>(classinfo);
+ if (bci->num_buttons >= 5) {
+ Atom label4 = bci->labels[3];
+ Atom label5 = bci->labels[4];
+ // Some drivers have no labels on the wheel buttons, some have no label on just one and some have no label on
+ // button 4 and the wrong one on button 5. So we just check that they are not labelled with unrelated buttons.
+ if ((!label4 || qatom(label4) == QXcbAtom::ButtonWheelUp || qatom(label4) == QXcbAtom::ButtonWheelDown) &&
+ (!label5 || qatom(label5) == QXcbAtom::ButtonWheelUp || qatom(label5) == QXcbAtom::ButtonWheelDown))
+ scrollingDevice.legacyOrientations |= Qt::Vertical;
}
+ if (bci->num_buttons >= 7) {
+ Atom label6 = bci->labels[5];
+ Atom label7 = bci->labels[6];
+ if ((!label6 || qatom(label6) == QXcbAtom::ButtonHorizWheelLeft) && (!label7 || qatom(label7) == QXcbAtom::ButtonHorizWheelRight))
+ scrollingDevice.legacyOrientations |= Qt::Horizontal;
+ }
+ qCDebug(lcQpaXInputDevices, " has %d buttons", bci->num_buttons);
+ break;
+ }
#endif
- case XIKeyClass:
- qCDebug(lcQpaXInputDevices) << " it's a keyboard";
- break;
+ case XIKeyClass:
+ qCDebug(lcQpaXInputDevices) << " it's a keyboard";
+ break;
#ifdef XCB_USE_XINPUT22
- case XITouchClass:
- // will be handled in deviceForId()
- break;
+ case XITouchClass:
+ // will be handled in populateTouchDevices()
+ break;
#endif
- default:
- qCDebug(lcQpaXInputDevices) << " has class" << devices[i].classes[c]->type;
- break;
- }
+ default:
+ qCDebug(lcQpaXInputDevices) << " has class" << classinfo->type;
+ break;
}
- bool isTablet = false;
+ }
+ bool isTablet = false;
#if QT_CONFIG(tabletevent)
- // If we have found the valuators which we expect a tablet to have, it might be a tablet.
- if (tabletData.valuatorInfo.contains(QXcbAtom::AbsX) &&
- tabletData.valuatorInfo.contains(QXcbAtom::AbsY) &&
- tabletData.valuatorInfo.contains(QXcbAtom::AbsPressure))
- isTablet = true;
-
- // But we need to be careful not to take the touch and tablet-button devices as tablets.
- QByteArray name = QByteArray(devices[i].name).toLower();
- QString dbgType = QLatin1String("UNKNOWN");
- if (name.contains("eraser")) {
- isTablet = true;
- tabletData.pointerType = QTabletEvent::Eraser;
- dbgType = QLatin1String("eraser");
- } else if (name.contains("cursor") && !(name.contains("cursor controls") && name.contains("trackball"))) {
- isTablet = true;
- tabletData.pointerType = QTabletEvent::Cursor;
- dbgType = QLatin1String("cursor");
- } else if (name.contains("wacom") && name.contains("finger touch")) {
- isTablet = false;
- } else if ((name.contains("pen") || name.contains("stylus")) && isTablet) {
- tabletData.pointerType = QTabletEvent::Pen;
- dbgType = QLatin1String("pen");
- } else if (name.contains("wacom") && isTablet && !name.contains("touch")) {
- // combined device (evdev) rather than separate pen/eraser (wacom driver)
- tabletData.pointerType = QTabletEvent::Pen;
- dbgType = QLatin1String("pen");
- } else if (name.contains("aiptek") /* && device == QXcbAtom::KEYBOARD */) {
- // some "Genius" tablets
- isTablet = true;
- tabletData.pointerType = QTabletEvent::Pen;
- dbgType = QLatin1String("pen");
- } else if (name.contains("waltop") && name.contains("tablet")) {
- // other "Genius" tablets
- // WALTOP International Corp. Slim Tablet
- isTablet = true;
- tabletData.pointerType = QTabletEvent::Pen;
- dbgType = QLatin1String("pen");
- } else if (name.contains("uc-logic") && isTablet) {
- tabletData.pointerType = QTabletEvent::Pen;
- dbgType = QLatin1String("pen");
- } else {
- isTablet = false;
- }
+ // If we have found the valuators which we expect a tablet to have, it might be a tablet.
+ if (tabletData.valuatorInfo.contains(QXcbAtom::AbsX) &&
+ tabletData.valuatorInfo.contains(QXcbAtom::AbsY) &&
+ tabletData.valuatorInfo.contains(QXcbAtom::AbsPressure))
+ isTablet = true;
+
+ // But we need to be careful not to take the touch and tablet-button devices as tablets.
+ QByteArray name = QByteArray(deviceInfo->name).toLower();
+ QString dbgType = QLatin1String("UNKNOWN");
+ if (name.contains("eraser")) {
+ isTablet = true;
+ tabletData.pointerType = QTabletEvent::Eraser;
+ dbgType = QLatin1String("eraser");
+ } else if (name.contains("cursor") && !(name.contains("cursor controls") && name.contains("trackball"))) {
+ isTablet = true;
+ tabletData.pointerType = QTabletEvent::Cursor;
+ dbgType = QLatin1String("cursor");
+ } else if (name.contains("wacom") && name.contains("finger touch")) {
+ isTablet = false;
+ } else if ((name.contains("pen") || name.contains("stylus")) && isTablet) {
+ tabletData.pointerType = QTabletEvent::Pen;
+ dbgType = QLatin1String("pen");
+ } else if (name.contains("wacom") && isTablet && !name.contains("touch")) {
+ // combined device (evdev) rather than separate pen/eraser (wacom driver)
+ tabletData.pointerType = QTabletEvent::Pen;
+ dbgType = QLatin1String("pen");
+ } else if (name.contains("aiptek") /* && device == QXcbAtom::KEYBOARD */) {
+ // some "Genius" tablets
+ isTablet = true;
+ tabletData.pointerType = QTabletEvent::Pen;
+ dbgType = QLatin1String("pen");
+ } else if (name.contains("waltop") && name.contains("tablet")) {
+ // other "Genius" tablets
+ // WALTOP International Corp. Slim Tablet
+ isTablet = true;
+ tabletData.pointerType = QTabletEvent::Pen;
+ dbgType = QLatin1String("pen");
+ } else if (name.contains("uc-logic") && isTablet) {
+ tabletData.pointerType = QTabletEvent::Pen;
+ dbgType = QLatin1String("pen");
+ } else {
+ isTablet = false;
+ }
- if (isTablet) {
- tabletData.deviceId = devices[i].deviceid;
- m_tabletData.append(tabletData);
- qCDebug(lcQpaXInputDevices) << " it's a tablet with pointer type" << dbgType;
- }
+ if (isTablet) {
+ tabletData.deviceId = deviceInfo->deviceid;
+ m_tabletData.append(tabletData);
+ qCDebug(lcQpaXInputDevices) << " it's a tablet with pointer type" << dbgType;
+ }
#endif // QT_CONFIG(tabletevent)
#ifdef XCB_USE_XINPUT21
- if (scrollingDevice.orientations || scrollingDevice.legacyOrientations) {
- scrollingDevice.deviceId = devices[i].deviceid;
- // Only use legacy wheel button events when we don't have real scroll valuators.
- scrollingDevice.legacyOrientations &= ~scrollingDevice.orientations;
- m_scrollingDevices.insert(scrollingDevice.deviceId, scrollingDevice);
- qCDebug(lcQpaXInputDevices) << " it's a scrolling device";
- }
+ if (scrollingDevice.orientations || scrollingDevice.legacyOrientations) {
+ scrollingDevice.deviceId = deviceInfo->deviceid;
+ // Only use legacy wheel button events when we don't have real scroll valuators.
+ scrollingDevice.legacyOrientations &= ~scrollingDevice.orientations;
+ m_scrollingDevices.insert(scrollingDevice.deviceId, scrollingDevice);
+ qCDebug(lcQpaXInputDevices) << " it's a scrolling device";
+ }
#endif
- if (!isTablet) {
- // touchDeviceForId populates XInput2DeviceData the first time it is called
- // with a new deviceId. On subsequent calls it will return the cached object.
- XInput2TouchDeviceData *dev = touchDeviceForId(devices[i].deviceid);
- if (dev && lcQpaXInputDevices().isDebugEnabled()) {
- if (dev->qtTouchDevice->type() == QTouchDevice::TouchScreen)
- qCDebug(lcQpaXInputDevices, " it's a touchscreen with type %d capabilities 0x%X max touch points %d",
- dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(),
- dev->qtTouchDevice->maximumTouchPoints());
- else if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad)
- qCDebug(lcQpaXInputDevices, " it's a touchpad with type %d capabilities 0x%X max touch points %d size %f x %f",
- dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(),
- dev->qtTouchDevice->maximumTouchPoints(),
- dev->size.width(), dev->size.height());
- }
+ if (!isTablet) {
+ TouchDeviceData *dev = populateTouchDevices(deviceInfo);
+ if (dev && lcQpaXInputDevices().isDebugEnabled()) {
+ if (dev->qtTouchDevice->type() == QTouchDevice::TouchScreen)
+ qCDebug(lcQpaXInputDevices, " it's a touchscreen with type %d capabilities 0x%X max touch points %d",
+ dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(),
+ dev->qtTouchDevice->maximumTouchPoints());
+ else if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad)
+ qCDebug(lcQpaXInputDevices, " it's a touchpad with type %d capabilities 0x%X max touch points %d size %f x %f",
+ dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(),
+ dev->qtTouchDevice->maximumTouchPoints(),
+ dev->size.width(), dev->size.height());
}
}
- XIFreeDeviceInfo(devices);
+
}
-void QXcbConnection::finalizeXInput2()
+void QXcbConnection::xi2SetupDevices()
{
- for (XInput2TouchDeviceData *dev : qAsConst(m_touchDevices)) {
- if (dev->xiDeviceInfo)
- XIFreeDeviceInfo(dev->xiDeviceInfo);
- delete dev;
+#if QT_CONFIG(tabletevent)
+ m_tabletData.clear();
+#endif
+ m_scrollingDevices.clear();
+ m_touchDevices.clear();
+
+ Display *xDisplay = static_cast<Display *>(m_xlib_display);
+ int deviceCount = 0;
+ XIDeviceInfo *devices = XIQueryDevice(xDisplay, XIAllDevices, &deviceCount);
+ for (int i = 0; i < deviceCount; ++i) {
+ // Only non-master pointing devices are relevant here.
+ if (devices[i].use != XISlavePointer)
+ continue;
+ xi2SetupDevice(&devices[i], false);
}
+ XIFreeDeviceInfo(devices);
}
-void QXcbConnection::xi2Select(xcb_window_t window)
+/*! \internal
+
+ Notes on QT_XCB_NO_XI2_MOUSE Handling:
+
+ Here we don't select pointer button press/release and motion events on master devices, instead
+ we select these events directly on slave devices. This means that a master device will fallback
+ to sending core events for every XI_* event that is sent directly by a slave device. For more
+ details see "Event processing for attached slave devices" in XInput2 specification. To prevent
+ handling of the same event twice, we have checks for xi2MouseEventsDisabled() in XI2 event
+ handlers (but this is somewhat inconsistent in some situations). If the purpose for
+ QT_XCB_NO_XI2_MOUSE was so that an application using QAbstractNativeEventFilter would see core
+ mouse events before they are handled by Qt then QT_XCB_NO_XI2_MOUSE won't always work as
+ expected (e.g. we handle scroll event directly from a slave device event, before an application
+ has seen the fallback core event from a master device).
+
+ The commit introducing QT_XCB_NO_XI2_MOUSE also states that setting this envvar "restores the
+ old behavior with broken grabbing". It did not elaborate why grabbing was not fixed for this
+ code path. The issue that this envvar tries to solve seem to be less important than broken
+ grabbing (broken apparently only for touch events). Thus, if you really want core mouse events
+ in your application and do not care about broken touch, then use QT_XCB_NO_XI2 (more on this
+ below) to disable the extension all together. The reason why grabbing might have not been fixed
+ is that calling XIGrabDevice with this code path for some reason always returns AlreadyGrabbed
+ (by debugging X server's code it appears that when we call XIGrabDevice, an X server first grabs
+ pointer via core pointer and then fails to do XI2 grab with AlreadyGrabbed; disclaimer - I did
+ not debug this in great detail). When we try supporting odd setups like QT_XCB_NO_XI2_MOUSE, we
+ are asking for trouble anyways.
+
+ In conclusion, introduction of QT_XCB_NO_XI2_MOUSE causes more issues than solves - the above
+ mentioned inconsistencies, maintenance of this code path and that QT_XCB_NO_XI2_MOUSE replaces
+ less important issue with somewhat more important issue. It also makes us to use less optimal
+ code paths in certain situations (see xi2HandleHierarchyEvent). Using of QT_XCB_NO_XI2 has its
+ drawbacks too - no tablet and touch events. So the only real fix in this case is at an
+ application side (teach the application about xcb_ge_event_t events). Based on this,
+ QT_XCB_NO_XI2_MOUSE will be removed in ### Qt 6. It should not have existed in the first place,
+ native events seen by QAbstractNativeEventFilter is not really a public API, applications should
+ expect changes at this level and do ifdefs if something changes between Qt version.
+*/
+void QXcbConnection::xi2SelectDeviceEventsCompatibility(xcb_window_t window)
{
- if (!m_xi2Enabled || window == rootWindow())
+ if (window == rootWindow())
return;
- Display *xDisplay = static_cast<Display *>(m_xlib_display);
- unsigned int bitMask = 0;
- unsigned char *xiBitMask = reinterpret_cast<unsigned char *>(&bitMask);
-
+ unsigned int mask = 0;
+ unsigned char *bitMask = reinterpret_cast<unsigned char *>(&mask);
+ mask |= XI_PropertyEventMask;
#ifdef XCB_USE_XINPUT22
if (isAtLeastXI22()) {
- bitMask |= XI_TouchBeginMask;
- bitMask |= XI_TouchUpdateMask;
- bitMask |= XI_TouchEndMask;
- bitMask |= XI_PropertyEventMask; // for tablets
- if (xi2MouseEvents()) {
- // We want both mouse and touch through XI2 if touch is supported (>= 2.2).
- // The plain xcb press and motion events will not be delivered after this.
- bitMask |= XI_ButtonPressMask;
- bitMask |= XI_ButtonReleaseMask;
- bitMask |= XI_MotionMask;
-
- // There is a check for enter/leave events in plain xcb enter/leave event handler
- bitMask |= XI_EnterMask;
- bitMask |= XI_LeaveMask;
-
- qCDebug(lcQpaXInput, "XInput 2.2: Selecting press/release/motion events in addition to touch");
- }
- XIEventMask mask;
- mask.mask_len = sizeof(bitMask);
- mask.mask = xiBitMask;
- // When xi2MouseEvents() is true (the default), pointer emulation for touch and tablet
- // events will get disabled. This is preferable, as Qt Quick handles touch events
- // directly, while for other applications QtGui synthesizes mouse events.
- mask.deviceid = XIAllMasterDevices;
- Status result = XISelectEvents(xDisplay, window, &mask, 1);
- if (result == Success)
- QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
- else
- qCDebug(lcQpaXInput, "XInput 2.2: failed to select pointer/touch events, window %x, result %d", window, result);
+ mask |= XI_TouchBeginMask;
+ mask |= XI_TouchUpdateMask;
+ mask |= XI_TouchEndMask;
}
+#endif
+ XIEventMask xiMask;
+ xiMask.mask_len = sizeof(mask);
+ xiMask.mask = bitMask;
+ xiMask.deviceid = XIAllMasterDevices;
+ Display *dpy = static_cast<Display *>(m_xlib_display);
+ Status result = XISelectEvents(dpy, window, &xiMask, 1);
+ if (result == Success)
+ QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
+ else
+ qCDebug(lcQpaXInput, "failed to select events, window %x, result %d", window, result);
- const bool pointerSelected = isAtLeastXI22() && xi2MouseEvents();
-#else
- const bool pointerSelected = false;
-#endif // XCB_USE_XINPUT22
+ mask = XI_ButtonPressMask;
+ mask |= XI_ButtonReleaseMask;
+ mask |= XI_MotionMask;
- QSet<int> tabletDevices;
#if QT_CONFIG(tabletevent)
+ QSet<int> tabletDevices;
if (!m_tabletData.isEmpty()) {
- unsigned int tabletBitMask;
- unsigned char *xiTabletBitMask = reinterpret_cast<unsigned char *>(&tabletBitMask);
- QVector<XIEventMask> xiEventMask(m_tabletData.count());
- tabletBitMask = XI_PropertyEventMask;
- if (!pointerSelected)
- tabletBitMask |= XI_ButtonPressMask | XI_ButtonReleaseMask | XI_MotionMask;
- for (int i = 0; i < m_tabletData.count(); ++i) {
+ const int nrTablets = m_tabletData.count();
+ QVector<XIEventMask> xiEventMask(nrTablets);
+ for (int i = 0; i < nrTablets; ++i) {
int deviceId = m_tabletData.at(i).deviceId;
tabletDevices.insert(deviceId);
xiEventMask[i].deviceid = deviceId;
- xiEventMask[i].mask_len = sizeof(tabletBitMask);
- xiEventMask[i].mask = xiTabletBitMask;
+ xiEventMask[i].mask_len = sizeof(mask);
+ xiEventMask[i].mask = bitMask;
}
- XISelectEvents(xDisplay, window, xiEventMask.data(), m_tabletData.count());
+ XISelectEvents(dpy, window, xiEventMask.data(), nrTablets);
}
-#endif // QT_CONFIG(tabletevent)
+#endif
#ifdef XCB_USE_XINPUT21
- // Enable each scroll device
- if (!m_scrollingDevices.isEmpty() && !pointerSelected) {
- // Only when XI2 mouse events are not enabled, otherwise motion and release are selected already.
+ if (!m_scrollingDevices.isEmpty()) {
QVector<XIEventMask> xiEventMask(m_scrollingDevices.size());
- unsigned int scrollBitMask;
- unsigned char *xiScrollBitMask = reinterpret_cast<unsigned char *>(&scrollBitMask);
-
- scrollBitMask = XI_MotionMask;
- scrollBitMask |= XI_ButtonReleaseMask;
- int i=0;
+ int i = 0;
for (const ScrollingDevice& scrollingDevice : qAsConst(m_scrollingDevices)) {
+#if QT_CONFIG(tabletevent)
if (tabletDevices.contains(scrollingDevice.deviceId))
continue; // All necessary events are already captured.
+#endif
xiEventMask[i].deviceid = scrollingDevice.deviceId;
- xiEventMask[i].mask_len = sizeof(scrollBitMask);
- xiEventMask[i].mask = xiScrollBitMask;
+ xiEventMask[i].mask_len = sizeof(mask);
+ xiEventMask[i].mask = bitMask;
i++;
}
- XISelectEvents(xDisplay, window, xiEventMask.data(), i);
+ XISelectEvents(dpy, window, xiEventMask.data(), i);
}
-#else
- Q_UNUSED(xiBitMask);
#endif
}
-XInput2TouchDeviceData *QXcbConnection::touchDeviceForId(int id)
+QXcbConnection::TouchDeviceData *QXcbConnection::touchDeviceForId(int id)
{
- XInput2TouchDeviceData *dev = Q_NULLPTR;
- QHash<int, XInput2TouchDeviceData*>::const_iterator devIt = m_touchDevices.constFind(id);
- if (devIt != m_touchDevices.cend()) {
- dev = devIt.value();
- } else {
- int nrDevices = 0;
- QTouchDevice::Capabilities caps = 0;
- dev = new XInput2TouchDeviceData;
- dev->xiDeviceInfo = XIQueryDevice(static_cast<Display *>(m_xlib_display), id, &nrDevices);
- if (nrDevices <= 0) {
- delete dev;
- return 0;
- }
- int type = -1;
- int maxTouchPoints = 1;
- bool hasRelativeCoords = false;
- for (int i = 0; i < dev->xiDeviceInfo->num_classes; ++i) {
- XIAnyClassInfo *classinfo = dev->xiDeviceInfo->classes[i];
- switch (classinfo->type) {
+ TouchDeviceData *dev = nullptr;
+ if (m_touchDevices.contains(id))
+ dev = &m_touchDevices[id];
+ return dev;
+}
+
+QXcbConnection::TouchDeviceData *QXcbConnection::populateTouchDevices(void *info)
+{
+ XIDeviceInfo *deviceinfo = reinterpret_cast<XIDeviceInfo *>(info);
+ QTouchDevice::Capabilities caps = 0;
+ int type = -1;
+ int maxTouchPoints = 1;
+ bool isTouchDevice = false;
+ bool hasRelativeCoords = false;
+ TouchDeviceData dev;
+ for (int i = 0; i < deviceinfo->num_classes; ++i) {
+ XIAnyClassInfo *classinfo = deviceinfo->classes[i];
+ switch (classinfo->type) {
#ifdef XCB_USE_XINPUT22
- case XITouchClass: {
- XITouchClassInfo *tci = reinterpret_cast<XITouchClassInfo *>(classinfo);
- maxTouchPoints = tci->num_touches;
- qCDebug(lcQpaXInputDevices, " has touch class with mode %d", tci->mode);
- switch (tci->mode) {
- case XIDependentTouch:
- type = QTouchDevice::TouchPad;
- break;
- case XIDirectTouch:
- type = QTouchDevice::TouchScreen;
- break;
- }
+ case XITouchClass: {
+ XITouchClassInfo *tci = reinterpret_cast<XITouchClassInfo *>(classinfo);
+ maxTouchPoints = tci->num_touches;
+ qCDebug(lcQpaXInputDevices, " has touch class with mode %d", tci->mode);
+ switch (tci->mode) {
+ case XIDependentTouch:
+ type = QTouchDevice::TouchPad;
+ break;
+ case XIDirectTouch:
+ type = QTouchDevice::TouchScreen;
break;
}
+ break;
+ }
#endif // XCB_USE_XINPUT22
- case XIValuatorClass: {
- XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
- // Some devices (mice) report a resolution of 0; they will be excluded later,
- // for now just prevent a division by zero
- const int vciResolution = vci->resolution ? vci->resolution : 1;
- if (vci->label == atom(QXcbAtom::AbsMTPositionX))
- caps |= QTouchDevice::Position | QTouchDevice::NormalizedPosition;
- else if (vci->label == atom(QXcbAtom::AbsMTTouchMajor))
- caps |= QTouchDevice::Area;
- else if (vci->label == atom(QXcbAtom::AbsMTOrientation))
- dev->providesTouchOrientation = true;
- else if (vci->label == atom(QXcbAtom::AbsMTPressure) || vci->label == atom(QXcbAtom::AbsPressure))
- caps |= QTouchDevice::Pressure;
- else if (vci->label == atom(QXcbAtom::RelX)) {
- hasRelativeCoords = true;
- dev->size.setWidth((vci->max - vci->min) * 1000.0 / vciResolution);
- } else if (vci->label == atom(QXcbAtom::RelY)) {
- hasRelativeCoords = true;
- dev->size.setHeight((vci->max - vci->min) * 1000.0 / vciResolution);
- } else if (vci->label == atom(QXcbAtom::AbsX)) {
- caps |= QTouchDevice::Position;
- dev->size.setWidth((vci->max - vci->min) * 1000.0 / vciResolution);
- } else if (vci->label == atom(QXcbAtom::AbsY)) {
- caps |= QTouchDevice::Position;
- dev->size.setHeight((vci->max - vci->min) * 1000.0 / vciResolution);
- }
- break;
+ case XIValuatorClass: {
+ XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
+ const QXcbAtom::Atom valuatorAtom = qatom(vci->label);
+ if (valuatorAtom < QXcbAtom::NAtoms) {
+ TouchDeviceData::ValuatorClassInfo info;
+ info.min = vci->min;
+ info.max = vci->max;
+ info.number = vci->number;
+ info.label = valuatorAtom;
+ dev.valuatorInfo.append(info);
}
- default:
- break;
+ // Some devices (mice) report a resolution of 0; they will be excluded later,
+ // for now just prevent a division by zero
+ const int vciResolution = vci->resolution ? vci->resolution : 1;
+ if (valuatorAtom == QXcbAtom::AbsMTPositionX)
+ caps |= QTouchDevice::Position | QTouchDevice::NormalizedPosition;
+ else if (valuatorAtom == QXcbAtom::AbsMTTouchMajor)
+ caps |= QTouchDevice::Area;
+ else if (valuatorAtom == QXcbAtom::AbsMTOrientation)
+ dev.providesTouchOrientation = true;
+ else if (valuatorAtom == QXcbAtom::AbsMTPressure || valuatorAtom == QXcbAtom::AbsPressure)
+ caps |= QTouchDevice::Pressure;
+ else if (valuatorAtom == QXcbAtom::RelX) {
+ hasRelativeCoords = true;
+ dev.size.setWidth((vci->max - vci->min) * 1000.0 / vciResolution);
+ } else if (valuatorAtom == QXcbAtom::RelY) {
+ hasRelativeCoords = true;
+ dev.size.setHeight((vci->max - vci->min) * 1000.0 / vciResolution);
+ } else if (valuatorAtom == QXcbAtom::AbsX) {
+ caps |= QTouchDevice::Position;
+ dev.size.setWidth((vci->max - vci->min) * 1000.0 / vciResolution);
+ } else if (valuatorAtom == QXcbAtom::AbsY) {
+ caps |= QTouchDevice::Position;
+ dev.size.setHeight((vci->max - vci->min) * 1000.0 / vciResolution);
}
+ break;
}
- if (type < 0 && caps && hasRelativeCoords) {
- type = QTouchDevice::TouchPad;
- if (dev->size.width() < 10 || dev->size.height() < 10 ||
- dev->size.width() > 10000 || dev->size.height() > 10000)
- dev->size = QSizeF(130, 110);
- }
- if (!isAtLeastXI22() || type == QTouchDevice::TouchPad)
- caps |= QTouchDevice::MouseEmulation;
-
- if (type >= QTouchDevice::TouchScreen && type <= QTouchDevice::TouchPad) {
- dev->qtTouchDevice = new QTouchDevice;
- dev->qtTouchDevice->setName(QString::fromUtf8(dev->xiDeviceInfo->name));
- dev->qtTouchDevice->setType((QTouchDevice::DeviceType)type);
- dev->qtTouchDevice->setCapabilities(caps);
- dev->qtTouchDevice->setMaximumTouchPoints(maxTouchPoints);
- if (caps != 0)
- QWindowSystemInterface::registerTouchDevice(dev->qtTouchDevice);
- m_touchDevices[id] = dev;
- } else {
- XIFreeDeviceInfo(dev->xiDeviceInfo);
- delete dev;
- dev = 0;
+ default:
+ break;
}
}
- return dev;
+ if (type < 0 && caps && hasRelativeCoords) {
+ type = QTouchDevice::TouchPad;
+ if (dev.size.width() < 10 || dev.size.height() < 10 ||
+ dev.size.width() > 10000 || dev.size.height() > 10000)
+ dev.size = QSizeF(130, 110);
+ }
+ if (!isAtLeastXI22() || type == QTouchDevice::TouchPad)
+ caps |= QTouchDevice::MouseEmulation;
+
+ if (type >= QTouchDevice::TouchScreen && type <= QTouchDevice::TouchPad) {
+ dev.qtTouchDevice = new QTouchDevice;
+ dev.qtTouchDevice->setName(QString::fromUtf8(deviceinfo->name));
+ dev.qtTouchDevice->setType((QTouchDevice::DeviceType)type);
+ dev.qtTouchDevice->setCapabilities(caps);
+ dev.qtTouchDevice->setMaximumTouchPoints(maxTouchPoints);
+ if (caps != 0)
+ QWindowSystemInterface::registerTouchDevice(dev.qtTouchDevice);
+ m_touchDevices[deviceinfo->deviceid] = dev;
+ isTouchDevice = true;
+ }
+
+ return isTouchDevice ? &m_touchDevices[deviceinfo->deviceid] : nullptr;
}
#if defined(XCB_USE_XINPUT21) || QT_CONFIG(tabletevent)
@@ -525,7 +576,7 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
break;
}
case XI_HierarchyChanged:
- xi2HandleHierachyEvent(xiEvent);
+ xi2HandleHierarchyEvent(xiEvent);
return;
case XI_DeviceChanged:
xi2HandleDeviceChangedEvent(xiEvent);
@@ -549,9 +600,8 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
#endif // QT_CONFIG(tabletevent)
#ifdef XCB_USE_XINPUT21
- QHash<int, ScrollingDevice>::iterator device = m_scrollingDevices.find(sourceDeviceId);
- if (device != m_scrollingDevices.end())
- xi2HandleScrollEvent(xiEvent, device.value());
+ if (ScrollingDevice *device = scrollingDeviceForId(sourceDeviceId))
+ xi2HandleScrollEvent(xiEvent, *device);
#endif // XCB_USE_XINPUT21
#ifdef XCB_USE_XINPUT22
@@ -560,7 +610,7 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
case XI_ButtonPress:
case XI_ButtonRelease:
case XI_Motion:
- if (xi2MouseEvents() && eventListener && !(xiDeviceEvent->flags & XIPointerEmulated))
+ if (!xi2MouseEventsDisabled() && eventListener && !(xiDeviceEvent->flags & XIPointerEmulated))
eventListener->handleXIMouseEvent(event);
break;
@@ -576,7 +626,7 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
xi2ProcessTouch(xiDeviceEvent, platformWindow);
break;
}
- } else if (xiEnterEvent && xi2MouseEvents() && eventListener) {
+ } else if (xiEnterEvent && !xi2MouseEventsDisabled() && eventListener) {
switch (xiEnterEvent->evtype) {
case XI_Enter:
case XI_Leave:
@@ -587,20 +637,25 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
#endif // XCB_USE_XINPUT22
}
+bool QXcbConnection::xi2MouseEventsDisabled() const
+{
+ static bool xi2MouseDisabled = qEnvironmentVariableIsSet("QT_XCB_NO_XI2_MOUSE");
+ // FIXME: Don't use XInput2 mouse events when Xinerama extension
+ // is enabled, because it causes problems with multi-monitor setup.
+ return xi2MouseDisabled || has_xinerama_extension;
+}
+
#ifdef XCB_USE_XINPUT22
-static qreal valuatorNormalized(double value, XIValuatorClassInfo *vci)
+bool QXcbConnection::isTouchScreen(int id)
{
- if (value > vci->max)
- value = vci->max;
- if (value < vci->min)
- value = vci->min;
- return (value - vci->min) / (vci->max - vci->min);
+ auto device = touchDeviceForId(id);
+ return device && device->qtTouchDevice->type() == QTouchDevice::TouchScreen;
}
void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindow)
{
xXIDeviceEvent *xiDeviceEvent = static_cast<xXIDeviceEvent *>(xiDevEvent);
- XInput2TouchDeviceData *dev = touchDeviceForId(xiDeviceEvent->sourceid);
+ TouchDeviceData *dev = touchDeviceForId(xiDeviceEvent->sourceid);
Q_ASSERT(dev);
const bool firstTouch = dev->touchPoints.isEmpty();
if (xiDeviceEvent->evtype == XI_TouchBegin) {
@@ -617,53 +672,53 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
qreal nx = -1.0, ny = -1.0;
qreal w = 0.0, h = 0.0;
bool majorAxisIsY = touchPoint.area.height() > touchPoint.area.width();
- for (int i = 0; i < dev->xiDeviceInfo->num_classes; ++i) {
- XIAnyClassInfo *classinfo = dev->xiDeviceInfo->classes[i];
- if (classinfo->type == XIValuatorClass) {
- XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
- int n = vci->number;
- double value;
- if (!xi2GetValuatorValueIfSet(xiDeviceEvent, n, &value))
- continue;
- if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
- qCDebug(lcQpaXInputEvents, " valuator %20s value %lf from range %lf -> %lf",
- atomName(vci->label).constData(), value, vci->min, vci->max );
- if (vci->label == atom(QXcbAtom::RelX)) {
- nx = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::RelY)) {
- ny = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::AbsX)) {
- nx = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::AbsY)) {
- ny = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::AbsMTPositionX)) {
- nx = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::AbsMTPositionY)) {
- ny = valuatorNormalized(value, vci);
- } else if (vci->label == atom(QXcbAtom::AbsMTTouchMajor)) {
- const qreal sw = screen->geometry().width();
- const qreal sh = screen->geometry().height();
- w = valuatorNormalized(value, vci) * std::sqrt(sw * sw + sh * sh);
- } else if (vci->label == atom(QXcbAtom::AbsMTTouchMinor)) {
- const qreal sw = screen->geometry().width();
- const qreal sh = screen->geometry().height();
- h = valuatorNormalized(value, vci) * std::sqrt(sw * sw + sh * sh);
- } else if (vci->label == atom(QXcbAtom::AbsMTOrientation)) {
- // Find the closest axis.
- // 0 corresponds to the Y axis, vci->max to the X axis.
- // Flipping over the Y axis and rotating by 180 degrees
- // don't change the result, so normalize value to range
- // [0, vci->max] first.
- value = qAbs(value);
- while (value > vci->max)
- value -= 2 * vci->max;
- value = qAbs(value);
- majorAxisIsY = value < vci->max - value;
- } else if (vci->label == atom(QXcbAtom::AbsMTPressure) ||
- vci->label == atom(QXcbAtom::AbsPressure)) {
- touchPoint.pressure = valuatorNormalized(value, vci);
- }
+ for (const TouchDeviceData::ValuatorClassInfo vci : dev->valuatorInfo) {
+ double value;
+ if (!xi2GetValuatorValueIfSet(xiDeviceEvent, vci.number, &value))
+ continue;
+ if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
+ qCDebug(lcQpaXInputEvents, " valuator %20s value %lf from range %lf -> %lf",
+ atomName(vci.label).constData(), value, vci.min, vci.max);
+ if (value > vci.max)
+ value = vci.max;
+ if (value < vci.min)
+ value = vci.min;
+ qreal valuatorNormalized = (value - vci.min) / (vci.max - vci.min);
+ if (vci.label == QXcbAtom::RelX) {
+ nx = valuatorNormalized;
+ } else if (vci.label == QXcbAtom::RelY) {
+ ny = valuatorNormalized;
+ } else if (vci.label == QXcbAtom::AbsX) {
+ nx = valuatorNormalized;
+ } else if (vci.label == QXcbAtom::AbsY) {
+ ny = valuatorNormalized;
+ } else if (vci.label == QXcbAtom::AbsMTPositionX) {
+ nx = valuatorNormalized;
+ } else if (vci.label == QXcbAtom::AbsMTPositionY) {
+ ny = valuatorNormalized;
+ } else if (vci.label == QXcbAtom::AbsMTTouchMajor) {
+ const qreal sw = screen->geometry().width();
+ const qreal sh = screen->geometry().height();
+ w = valuatorNormalized * std::sqrt(sw * sw + sh * sh);
+ } else if (vci.label == QXcbAtom::AbsMTTouchMinor) {
+ const qreal sw = screen->geometry().width();
+ const qreal sh = screen->geometry().height();
+ h = valuatorNormalized * std::sqrt(sw * sw + sh * sh);
+ } else if (vci.label == QXcbAtom::AbsMTOrientation) {
+ // Find the closest axis.
+ // 0 corresponds to the Y axis, vci.max to the X axis.
+ // Flipping over the Y axis and rotating by 180 degrees
+ // don't change the result, so normalize value to range
+ // [0, vci.max] first.
+ value = qAbs(value);
+ while (value > vci.max)
+ value -= 2 * vci.max;
+ value = qAbs(value);
+ majorAxisIsY = value < vci.max - value;
+ } else if (vci.label == QXcbAtom::AbsMTPressure || vci.label == QXcbAtom::AbsPressure) {
+ touchPoint.pressure = valuatorNormalized;
}
+
}
// If any value was not updated, use the last-known value.
if (nx == -1.0) {
@@ -765,12 +820,12 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
bool QXcbConnection::startSystemResizeForTouchBegin(xcb_window_t window, const QPoint &point, Qt::Corner corner)
{
- QHash<int, XInput2TouchDeviceData*>::const_iterator devIt = m_touchDevices.constBegin();
+ QHash<int, TouchDeviceData>::const_iterator devIt = m_touchDevices.constBegin();
for (; devIt != m_touchDevices.constEnd(); ++devIt) {
- XInput2TouchDeviceData *deviceData = devIt.value();
- if (deviceData->qtTouchDevice->type() == QTouchDevice::TouchScreen) {
- QHash<int, QPointF>::const_iterator pointIt = deviceData->pointPressedPosition.constBegin();
- for (; pointIt != deviceData->pointPressedPosition.constEnd(); ++pointIt) {
+ TouchDeviceData deviceData = devIt.value();
+ if (deviceData.qtTouchDevice->type() == QTouchDevice::TouchScreen) {
+ QHash<int, QPointF>::const_iterator pointIt = deviceData.pointPressedPosition.constBegin();
+ for (; pointIt != deviceData.pointPressedPosition.constEnd(); ++pointIt) {
if (pointIt.value().toPoint() == point) {
m_startSystemResizeInfo.window = window;
m_startSystemResizeInfo.deviceid = devIt.key();
@@ -783,6 +838,7 @@ bool QXcbConnection::startSystemResizeForTouchBegin(xcb_window_t window, const Q
}
return false;
}
+#endif // XCB_USE_XINPUT22
bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
{
@@ -843,59 +899,70 @@ bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
return grabbed;
}
-#endif // XCB_USE_XINPUT22
-void QXcbConnection::xi2HandleHierachyEvent(void *event)
+void QXcbConnection::xi2HandleHierarchyEvent(void *event)
{
xXIHierarchyEvent *xiEvent = reinterpret_cast<xXIHierarchyEvent *>(event);
// We only care about hotplugged devices
if (!(xiEvent->flags & (XISlaveRemoved | XISlaveAdded)))
return;
+
xi2SetupDevices();
- // Reselect events for all event-listening windows.
- for (auto it = m_mapper.cbegin(), end = m_mapper.cend(); it != end; ++it)
- xi2Select(it.key());
+
+ if (xi2MouseEventsDisabled()) {
+ // In compatibility mode (a.k.a xi2MouseEventsDisabled() mode) we select events for
+ // each device separately. When a new device appears, we have to select events from
+ // this device on all event-listening windows. This is not needed when events are
+ // selected via XIAllDevices/XIAllMasterDevices (as in xi2SelectDeviceEvents()).
+ for (auto it = m_mapper.cbegin(), end = m_mapper.cend(); it != end; ++it)
+ xi2SelectDeviceEventsCompatibility(it.key());
+ }
}
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;
-
+ switch (xiEvent->reason) {
+ case XIDeviceChange: {
+ int nrDevices = 0;
+ Display *dpy = static_cast<Display *>(m_xlib_display);
+ XIDeviceInfo* deviceInfo = XIQueryDevice(dpy, xiEvent->sourceid, &nrDevices);
+ if (nrDevices <= 0)
+ return;
+ xi2SetupDevice(deviceInfo);
+ XIFreeDeviceInfo(deviceInfo);
+ break;
+ }
+ case XISlaveSwitch: {
#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;
+ if (ScrollingDevice *scrollingDevice = scrollingDeviceForId(xiEvent->sourceid))
+ xi2UpdateScrollingDevice(*scrollingDevice);
+#endif
+ break;
+ }
+ default:
+ qCDebug(lcQpaXInputEvents, "unknown device-changed-event (device %d)", xiEvent->sourceid);
+ break;
+ }
+}
+#ifdef XCB_USE_XINPUT21
+void QXcbConnection::xi2UpdateScrollingDevice(ScrollingDevice &scrollingDevice)
+{
int nrDevices = 0;
- XIDeviceInfo* xiDeviceInfo = XIQueryDevice(static_cast<Display *>(m_xlib_display), xiEvent->sourceid, &nrDevices);
+ Display *dpy = static_cast<Display *>(m_xlib_display);
+ XIDeviceInfo* deviceInfo = XIQueryDevice(dpy, scrollingDevice.deviceId, &nrDevices);
if (nrDevices <= 0) {
- qCDebug(lcQpaXInputDevices, "scrolling device %d no longer present", xiEvent->sourceid);
+ qCDebug(lcQpaXInputDevices, "scrolling device %d no longer present", scrollingDevice.deviceId);
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]);
+ for (int c = 0; c < deviceInfo->num_classes; ++c) {
+ XIAnyClassInfo *classInfo = deviceInfo->classes[c];
+ if (classInfo->type == XIValuatorClass) {
+ XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classInfo);
const int valuatorAtom = qatom(vci->label);
if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel)
scrollingDevice.lastScrollPosition.setX(vci->value);
@@ -908,37 +975,30 @@ void QXcbConnection::updateScrollingDevice(ScrollingDevice &scrollingDevice, int
lastScrollPosition.x(), lastScrollPosition.y(),
scrollingDevice.lastScrollPosition.x(),
scrollingDevice.lastScrollPosition.y());
-#else
- Q_UNUSED(scrollingDevice);
- Q_UNUSED(num_classes);
- Q_UNUSED(classInfo);
-#endif
+
+ XIFreeDeviceInfo(deviceInfo);
}
-#ifdef XCB_USE_XINPUT21
-void QXcbConnection::handleEnterEvent()
+void QXcbConnection::xi2UpdateScrollingDevices()
{
QHash<int, ScrollingDevice>::iterator it = m_scrollingDevices.begin();
const QHash<int, ScrollingDevice>::iterator end = m_scrollingDevices.end();
while (it != end) {
- ScrollingDevice& scrollingDevice = it.value();
- 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;
- }
- updateScrollingDevice(scrollingDevice, xiDeviceInfo->num_classes, xiDeviceInfo->classes);
- XIFreeDeviceInfo(xiDeviceInfo);
+ xi2UpdateScrollingDevice(it.value());
++it;
}
}
-#endif
+
+QXcbConnection::ScrollingDevice *QXcbConnection::scrollingDeviceForId(int id)
+{
+ ScrollingDevice *dev = nullptr;
+ if (m_scrollingDevices.contains(id))
+ dev = &m_scrollingDevices[id];
+ return dev;
+}
void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice)
{
-#ifdef XCB_USE_XINPUT21
xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event);
if (xiEvent->evtype == XI_Motion && scrollingDevice.orientations) {
@@ -1008,10 +1068,51 @@ void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollin
}
}
}
-#else
- Q_UNUSED(event);
- Q_UNUSED(scrollingDevice);
+}
#endif // XCB_USE_XINPUT21
+
+static int xi2ValuatorOffset(const unsigned char *maskPtr, int maskLen, int number)
+{
+ 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 -1;
+}
+
+bool QXcbConnection::xi2GetValuatorValueIfSet(const void *event, int valuatorNum, double *value)
+{
+ const xXIDeviceEvent *xideviceevent = static_cast<const xXIDeviceEvent *>(event);
+ const unsigned char *buttonsMaskAddr = (const unsigned char*)&xideviceevent[1];
+ const unsigned char *valuatorsMaskAddr = buttonsMaskAddr + xideviceevent->buttons_len * 4;
+ FP3232 *valuatorsValuesAddr = (FP3232*)(valuatorsMaskAddr + xideviceevent->valuators_len * 4);
+
+ int valuatorOffset = xi2ValuatorOffset(valuatorsMaskAddr, xideviceevent->valuators_len, valuatorNum);
+ if (valuatorOffset < 0)
+ return false;
+
+ *value = valuatorsValuesAddr[valuatorOffset].integral;
+ *value += ((double)valuatorsValuesAddr[valuatorOffset].frac / (1 << 16) / (1 << 16));
+ return true;
+}
+
+void QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event)
+{
+ // xcb event structs contain stuff that wasn't on the wire, the full_sequence field
+ // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes.
+ // Move this data back to have the same layout in memory as it was on the wire
+ // and allow casting, overwriting the full_sequence field.
+ memmove((char*) event + 32, (char*) event + 36, event->length * 4);
}
Qt::MouseButton QXcbConnection::xiToQtMouseButton(uint32_t b)
@@ -1028,15 +1129,6 @@ Qt::MouseButton QXcbConnection::xiToQtMouseButton(uint32_t b)
return Qt::NoButton;
}
-#ifdef XCB_USE_XINPUT22
-bool QXcbConnection::isTouchScreen(int id) const
-{
- auto device = m_touchDevices.value(id);
- return device && device->qtTouchDevice
- && device->qtTouchDevice->type() == QTouchDevice::TouchScreen;
-}
-#endif
-
#if QT_CONFIG(tabletevent)
static QTabletEvent::TabletDevice toolIdToTabletDevice(quint32 toolId) {
// keep in sync with wacom_intuos_inout() in Linux kernel driver wacom_wac.c