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.cpp955
1 files changed, 586 insertions, 369 deletions
diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
index dc8a533388..4f62a1880b 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
@@ -1,62 +1,43 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qxcbconnection.h"
#include "qxcbkeyboard.h"
+#include "qxcbscrollingdevice_p.h"
#include "qxcbscreen.h"
#include "qxcbwindow.h"
#include "QtCore/qmetaobject.h"
+#include "QtCore/qmath.h"
#include <QtGui/qpointingdevice.h>
#include <QtGui/private/qpointingdevice_p.h>
#include <qpa/qwindowsysteminterface_p.h>
#include <QDebug>
-#include <cmath>
#include <xcb/xinput.h>
+#if QT_CONFIG(gestures)
+#define QT_XCB_HAS_TOUCHPAD_GESTURES (XCB_INPUT_MINOR_VERSION >= 4)
+#endif
+
+using namespace Qt::StringLiterals;
+
using qt_xcb_input_device_event_t = xcb_input_button_press_event_t;
+#if QT_XCB_HAS_TOUCHPAD_GESTURES
+using qt_xcb_input_pinch_event_t = xcb_input_gesture_pinch_begin_event_t;
+using qt_xcb_input_swipe_event_t = xcb_input_gesture_swipe_begin_event_t;
+#endif
struct qt_xcb_input_event_mask_t {
xcb_input_event_mask_t header;
- uint32_t mask;
+ alignas(4) uint8_t mask[8] = {}; // up to 2 units of 4 bytes
};
+static inline void setXcbMask(uint8_t* mask, int bit)
+{
+ // note that XI protocol always uses little endian for masks over the wire
+ mask[bit >> 3] |= 1 << (bit & 7);
+}
+
void QXcbConnection::xi2SelectStateEvents()
{
// These state events do not depend on a specific X window, but are global
@@ -64,9 +45,9 @@ void QXcbConnection::xi2SelectStateEvents()
qt_xcb_input_event_mask_t xiEventMask;
xiEventMask.header.deviceid = XCB_INPUT_DEVICE_ALL;
xiEventMask.header.mask_len = 1;
- xiEventMask.mask = XCB_INPUT_XI_EVENT_MASK_HIERARCHY;
- xiEventMask.mask |= XCB_INPUT_XI_EVENT_MASK_DEVICE_CHANGED;
- xiEventMask.mask |= XCB_INPUT_XI_EVENT_MASK_PROPERTY;
+ setXcbMask(xiEventMask.mask, XCB_INPUT_HIERARCHY);
+ setXcbMask(xiEventMask.mask, XCB_INPUT_DEVICE_CHANGED);
+ setXcbMask(xiEventMask.mask, XCB_INPUT_PROPERTY);
xcb_input_xi_select_events(xcb_connection(), rootWindow(), 1, &xiEventMask.header);
}
@@ -75,23 +56,33 @@ void QXcbConnection::xi2SelectDeviceEvents(xcb_window_t window)
if (window == rootWindow())
return;
- uint32_t bitMask = XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS;
- bitMask |= XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE;
- bitMask |= XCB_INPUT_XI_EVENT_MASK_MOTION;
+ qt_xcb_input_event_mask_t mask;
+
+ setXcbMask(mask.mask, XCB_INPUT_BUTTON_PRESS);
+ setXcbMask(mask.mask, XCB_INPUT_BUTTON_RELEASE);
+ setXcbMask(mask.mask, XCB_INPUT_MOTION);
// 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 |= XCB_INPUT_XI_EVENT_MASK_ENTER;
- bitMask |= XCB_INPUT_XI_EVENT_MASK_LEAVE;
+ setXcbMask(mask.mask, XCB_INPUT_ENTER);
+ setXcbMask(mask.mask, XCB_INPUT_LEAVE);
if (isAtLeastXI22()) {
- bitMask |= XCB_INPUT_XI_EVENT_MASK_TOUCH_BEGIN;
- bitMask |= XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE;
- bitMask |= XCB_INPUT_XI_EVENT_MASK_TOUCH_END;
+ setXcbMask(mask.mask, XCB_INPUT_TOUCH_BEGIN);
+ setXcbMask(mask.mask, XCB_INPUT_TOUCH_UPDATE);
+ setXcbMask(mask.mask, XCB_INPUT_TOUCH_END);
+ }
+#if QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
+ if (isAtLeastXI24()) {
+ setXcbMask(mask.mask, XCB_INPUT_GESTURE_PINCH_BEGIN);
+ setXcbMask(mask.mask, XCB_INPUT_GESTURE_PINCH_UPDATE);
+ setXcbMask(mask.mask, XCB_INPUT_GESTURE_PINCH_END);
+ setXcbMask(mask.mask, XCB_INPUT_GESTURE_SWIPE_BEGIN);
+ setXcbMask(mask.mask, XCB_INPUT_GESTURE_SWIPE_UPDATE);
+ setXcbMask(mask.mask, XCB_INPUT_GESTURE_SWIPE_END);
}
+#endif // QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
- qt_xcb_input_event_mask_t mask;
- mask.header.deviceid = XCB_INPUT_DEVICE_ALL_MASTER;
- mask.header.mask_len = 1;
- mask.mask = bitMask;
+ mask.header.deviceid = XCB_INPUT_DEVICE_ALL;
+ mask.header.mask_len = 2;
xcb_void_cookie_t cookie =
xcb_input_xi_select_events_checked(xcb_connection(), window, 1, &mask.header);
xcb_generic_error_t *error = xcb_request_check(xcb_connection(), cookie);
@@ -203,7 +194,8 @@ static const QPointingDevice *tabletToolInstance(QPointingDevice *master, const
if (pointerTypeOverride != QPointingDevice::PointerType::Unknown)
pointerType = pointerTypeOverride;
const QPointingDevice *ret = QPointingDevicePrivate::queryTabletDevice(devType, pointerType,
- QPointingDeviceUniqueId::fromNumericId(uniqueId), id);
+ QPointingDeviceUniqueId::fromNumericId(uniqueId),
+ caps, id);
if (!ret) {
ret = new QPointingDevice(tabletName, id, devType, pointerType, caps, 1, buttonCount,
master ? master->seatName() : QString(),
@@ -236,22 +228,33 @@ void QXcbConnection::xi2SetupSlavePointerDevice(void *info, bool removeExisting,
auto *deviceInfo = reinterpret_cast<xcb_input_xi_device_info_t *>(info);
if (removeExisting) {
#if QT_CONFIG(tabletevent)
- for (int i = 0; i < m_tabletData.count(); ++i) {
+ for (int i = 0; i < m_tabletData.size(); ++i) {
if (m_tabletData.at(i).deviceId == deviceInfo->deviceid) {
m_tabletData.remove(i);
break;
}
}
#endif
- m_scrollingDevices.remove(deviceInfo->deviceid);
m_touchDevices.remove(deviceInfo->deviceid);
}
- qCDebug(lcQpaXInputDevices) << "input device " << xcb_input_xi_device_info_name(deviceInfo) << "ID" << deviceInfo->deviceid;
+ const QByteArray nameRaw = QByteArray(xcb_input_xi_device_info_name(deviceInfo),
+ xcb_input_xi_device_info_name_length(deviceInfo));
+ const QString name = QString::fromUtf8(nameRaw);
+ m_xiSlavePointerIds.append(deviceInfo->deviceid);
+ qCDebug(lcQpaXInputDevices) << "input device " << name << "ID" << deviceInfo->deviceid;
#if QT_CONFIG(tabletevent)
TabletData tabletData;
#endif
- ScrollingDevice scrollingDevice;
+ QXcbScrollingDevicePrivate *scrollingDeviceP = nullptr;
+ bool used = false;
+ auto scrollingDevice = [&]() {
+ if (!scrollingDeviceP)
+ scrollingDeviceP = new QXcbScrollingDevicePrivate(name, deviceInfo->deviceid,
+ QInputDevice::Capability::Scroll);
+ return scrollingDeviceP;
+ };
+
int buttonCount = 32;
auto classes_it = xcb_input_xi_device_info_classes_iterator(deviceInfo);
for (; classes_it.rem; xcb_input_device_class_next(&classes_it)) {
@@ -270,22 +273,24 @@ void QXcbConnection::xi2SetupSlavePointerDevice(void *info, bool removeExisting,
tabletData.valuatorInfo[valuatorAtom] = info;
}
#endif // QT_CONFIG(tabletevent)
- if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel)
- scrollingDevice.lastScrollPosition.setX(fixed3232ToReal(vci->value));
- else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel)
- scrollingDevice.lastScrollPosition.setY(fixed3232ToReal(vci->value));
+ if (valuatorAtom == QXcbAtom::AtomRelHorizScroll || valuatorAtom == QXcbAtom::AtomRelHorizWheel)
+ scrollingDevice()->lastScrollPosition.setX(fixed3232ToReal(vci->value));
+ else if (valuatorAtom == QXcbAtom::AtomRelVertScroll || valuatorAtom == QXcbAtom::AtomRelVertWheel)
+ scrollingDevice()->lastScrollPosition.setY(fixed3232ToReal(vci->value));
break;
}
case XCB_INPUT_DEVICE_CLASS_TYPE_SCROLL: {
auto *sci = reinterpret_cast<xcb_input_scroll_class_t *>(classinfo);
if (sci->scroll_type == XCB_INPUT_SCROLL_TYPE_VERTICAL) {
- scrollingDevice.orientations |= Qt::Vertical;
- scrollingDevice.verticalIndex = sci->number;
- scrollingDevice.verticalIncrement = fixed3232ToReal(sci->increment);
+ auto dev = scrollingDevice();
+ dev->orientations.setFlag(Qt::Vertical);
+ dev->verticalIndex = sci->number;
+ dev->verticalIncrement = fixed3232ToReal(sci->increment);
} else if (sci->scroll_type == XCB_INPUT_SCROLL_TYPE_HORIZONTAL) {
- scrollingDevice.orientations |= Qt::Horizontal;
- scrollingDevice.horizontalIndex = sci->number;
- scrollingDevice.horizontalIncrement = fixed3232ToReal(sci->increment);
+ auto dev = scrollingDevice();
+ dev->orientations.setFlag(Qt::Horizontal);
+ dev->horizontalIndex = sci->number;
+ dev->horizontalIncrement = fixed3232ToReal(sci->increment);
}
break;
}
@@ -298,15 +303,15 @@ void QXcbConnection::xi2SetupSlavePointerDevice(void *info, bool removeExisting,
xcb_atom_t label5 = 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 ((!label4 || qatom(label4) == QXcbAtom::AtomButtonWheelUp || qatom(label4) == QXcbAtom::AtomButtonWheelDown) &&
+ (!label5 || qatom(label5) == QXcbAtom::AtomButtonWheelUp || qatom(label5) == QXcbAtom::AtomButtonWheelDown))
+ scrollingDevice()->legacyOrientations |= Qt::Vertical;
}
if (bci->num_buttons >= 7) {
xcb_atom_t label6 = labels[5];
xcb_atom_t label7 = labels[6];
- if ((!label6 || qatom(label6) == QXcbAtom::ButtonHorizWheelLeft) && (!label7 || qatom(label7) == QXcbAtom::ButtonHorizWheelRight))
- scrollingDevice.legacyOrientations |= Qt::Horizontal;
+ if ((!label6 || qatom(label6) == QXcbAtom::AtomButtonHorizWheelLeft) && (!label7 || qatom(label7) == QXcbAtom::AtomButtonHorizWheelRight))
+ scrollingDevice()->legacyOrientations |= Qt::Horizontal;
}
buttonCount = bci->num_buttons;
qCDebug(lcQpaXInputDevices, " has %d buttons", bci->num_buttons);
@@ -316,6 +321,9 @@ void QXcbConnection::xi2SetupSlavePointerDevice(void *info, bool removeExisting,
qCDebug(lcQpaXInputDevices) << " it's a keyboard";
break;
case XCB_INPUT_DEVICE_CLASS_TYPE_TOUCH:
+#if QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
+ case XCB_INPUT_DEVICE_CLASS_TYPE_GESTURE:
+#endif // QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
// will be handled in populateTouchDevices()
break;
default:
@@ -326,64 +334,62 @@ void QXcbConnection::xi2SetupSlavePointerDevice(void *info, bool removeExisting,
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))
+ if (tabletData.valuatorInfo.contains(QXcbAtom::AtomAbsX) &&
+ tabletData.valuatorInfo.contains(QXcbAtom::AtomAbsY) &&
+ tabletData.valuatorInfo.contains(QXcbAtom::AtomAbsPressure))
isTablet = true;
// But we need to be careful not to take the touch and tablet-button devices as tablets.
- QByteArray name = QByteArray(xcb_input_xi_device_info_name(deviceInfo),
- xcb_input_xi_device_info_name_length(deviceInfo));
- QByteArray nameLower = name.toLower();
- QString dbgType = QLatin1String("UNKNOWN");
+ QByteArray nameLower = nameRaw.toLower();
+ QString dbgType = "UNKNOWN"_L1;
if (nameLower.contains("eraser")) {
isTablet = true;
tabletData.pointerType = QPointingDevice::PointerType::Eraser;
- dbgType = QLatin1String("eraser");
+ dbgType = "eraser"_L1;
} else if (nameLower.contains("cursor") && !(nameLower.contains("cursor controls") && nameLower.contains("trackball"))) {
isTablet = true;
tabletData.pointerType = QPointingDevice::PointerType::Cursor;
- dbgType = QLatin1String("cursor");
+ dbgType = "cursor"_L1;
} else if (nameLower.contains("wacom") && nameLower.contains("finger touch")) {
isTablet = false;
} else if ((nameLower.contains("pen") || nameLower.contains("stylus")) && isTablet) {
tabletData.pointerType = QPointingDevice::PointerType::Pen;
- dbgType = QLatin1String("pen");
+ dbgType = "pen"_L1;
} else if (nameLower.contains("wacom") && isTablet && !nameLower.contains("touch")) {
// combined device (evdev) rather than separate pen/eraser (wacom driver)
tabletData.pointerType = QPointingDevice::PointerType::Pen;
- dbgType = QLatin1String("pen");
- } else if (nameLower.contains("aiptek") /* && device == QXcbAtom::KEYBOARD */) {
+ dbgType = "pen"_L1;
+ } else if (nameLower.contains("aiptek") /* && device == QXcbAtom::AtomKEYBOARD */) {
// some "Genius" tablets
isTablet = true;
tabletData.pointerType = QPointingDevice::PointerType::Pen;
- dbgType = QLatin1String("pen");
+ dbgType = "pen"_L1;
} else if (nameLower.contains("waltop") && nameLower.contains("tablet")) {
// other "Genius" tablets
// WALTOP International Corp. Slim Tablet
isTablet = true;
tabletData.pointerType = QPointingDevice::PointerType::Pen;
- dbgType = QLatin1String("pen");
+ dbgType = "pen"_L1;
} else if (nameLower.contains("uc-logic") && isTablet) {
tabletData.pointerType = QPointingDevice::PointerType::Pen;
- dbgType = QLatin1String("pen");
+ dbgType = "pen"_L1;
} else if (nameLower.contains("ugee")) {
isTablet = true;
tabletData.pointerType = QPointingDevice::PointerType::Pen;
- dbgType = QLatin1String("pen");
+ dbgType = "pen"_L1;
} else {
isTablet = false;
}
if (isTablet) {
tabletData.deviceId = deviceInfo->deviceid;
- tabletData.name = QLatin1String(name);
+ tabletData.name = name;
m_tabletData.append(tabletData);
qCDebug(lcQpaXInputDevices) << " it's a tablet with pointer type" << dbgType;
QPointingDevice::Capabilities capsOverride = QInputDevice::Capability::None;
- if (tabletData.valuatorInfo.contains(QXcbAtom::AbsTiltX))
+ if (tabletData.valuatorInfo.contains(QXcbAtom::AtomAbsTiltX))
capsOverride.setFlag(QInputDevice::Capability::XTilt);
- if (tabletData.valuatorInfo.contains(QXcbAtom::AbsTiltY))
+ if (tabletData.valuatorInfo.contains(QXcbAtom::AtomAbsTiltY))
capsOverride.setFlag(QInputDevice::Capability::YTilt);
// TODO can we get USB ID?
Q_ASSERT(deviceInfo->deviceid == tabletData.deviceId);
@@ -394,16 +400,14 @@ void QXcbConnection::xi2SetupSlavePointerDevice(void *info, bool removeExisting,
}
#endif // QT_CONFIG(tabletevent)
- if (scrollingDevice.orientations || scrollingDevice.legacyOrientations) {
- scrollingDevice.deviceId = deviceInfo->deviceid;
+ if (scrollingDeviceP) {
// Only use legacy wheel button events when we don't have real scroll valuators.
- scrollingDevice.legacyOrientations &= ~scrollingDevice.orientations;
- m_scrollingDevices.insert(scrollingDevice.deviceId, scrollingDevice);
+ scrollingDeviceP->legacyOrientations &= ~scrollingDeviceP->orientations;
qCDebug(lcQpaXInputDevices) << " it's a scrolling device";
}
if (!isTablet) {
- TouchDeviceData *dev = populateTouchDevices(deviceInfo);
+ TouchDeviceData *dev = populateTouchDevices(deviceInfo, scrollingDeviceP, &used);
if (dev && lcQpaXInputDevices().isDebugEnabled()) {
if (dev->qtTouchDevice->type() == QInputDevice::DeviceType::TouchScreen)
qCDebug(lcQpaXInputDevices, " it's a touchscreen with type %d capabilities 0x%X max touch points %d",
@@ -420,22 +424,36 @@ void QXcbConnection::xi2SetupSlavePointerDevice(void *info, bool removeExisting,
if (!QInputDevicePrivate::fromId(deviceInfo->deviceid)) {
qCDebug(lcQpaXInputDevices) << " it's a mouse";
QInputDevice::Capabilities caps = QInputDevice::Capability::Position | QInputDevice::Capability::Hover;
- if (scrollingDevice.orientations || scrollingDevice.legacyOrientations)
- caps.setFlag(QInputDevice::Capability::Scroll);
- QWindowSystemInterface::registerInputDevice(new QPointingDevice(
- QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo)), deviceInfo->deviceid,
- QInputDevice::DeviceType::Mouse, QPointingDevice::PointerType::Generic,
- caps, 1, buttonCount, (master ? master->seatName() : QString()), QPointingDeviceUniqueId(), master));
+ if (scrollingDeviceP) {
+ scrollingDeviceP->capabilities |= caps;
+ scrollingDeviceP->buttonCount = buttonCount;
+ if (master)
+ scrollingDeviceP->seatName = master->seatName();
+ QWindowSystemInterface::registerInputDevice(new QXcbScrollingDevice(*scrollingDeviceP, master));
+ used = true;
+ } else {
+ QWindowSystemInterface::registerInputDevice(new QPointingDevice(
+ name, deviceInfo->deviceid,
+ QInputDevice::DeviceType::Mouse, QPointingDevice::PointerType::Generic,
+ caps, 1, buttonCount, (master ? master->seatName() : QString()), QPointingDeviceUniqueId(), master));
+ }
+ }
+
+ if (!used && scrollingDeviceP) {
+ QXcbScrollingDevice *holder = new QXcbScrollingDevice(*scrollingDeviceP, master);
+ holder->deleteLater();
}
}
+/*!
+ Find all X11 input devices at startup, or react to a device hierarchy event,
+ and create/delete the corresponding QInputDevice instances as necessary.
+ Afterwards, we expect QInputDevice::devices() to contain only the
+ Qt-relevant devices that \c {xinput list} reports. The parent of each master
+ device is this QXcbConnection object; the parent of each slave is its master.
+*/
void QXcbConnection::xi2SetupDevices()
{
-#if QT_CONFIG(tabletevent)
- m_tabletData.clear();
-#endif
- m_scrollingDevices.clear();
- m_touchDevices.clear();
m_xiMasterPointerIds.clear();
auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, xcb_connection(), XCB_INPUT_DEVICE_ALL);
@@ -444,6 +462,18 @@ void QXcbConnection::xi2SetupDevices()
return;
}
+ // Start with all known devices; remove the ones that still exist.
+ // Afterwards, previousDevices will be the list of those that we should delete.
+ QList<const QInputDevice *> previousDevices = QInputDevice::devices();
+ // Return true if the device with the given systemId is new;
+ // otherwise remove it from previousDevices and return false.
+ auto newOrKeep = [&previousDevices](qint64 systemId) {
+ // if nothing is removed from previousDevices, it's a new device
+ return !previousDevices.removeIf([systemId](const QInputDevice *dev) {
+ return dev->systemId() == systemId;
+ });
+ };
+
// XInput doesn't provide a way to identify "seats"; but each device has an attachment to another device.
// So we make up a seatId: master-keyboard-id << 16 | master-pointer-id.
@@ -452,18 +482,21 @@ void QXcbConnection::xi2SetupDevices()
xcb_input_xi_device_info_t *deviceInfo = it.data;
switch (deviceInfo->type) {
case XCB_INPUT_DEVICE_TYPE_MASTER_KEYBOARD: {
- auto dev = new QInputDevice(QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo)),
- deviceInfo->deviceid, QInputDevice::DeviceType::Keyboard,
- QString::number(deviceInfo->deviceid << 16 | deviceInfo->attachment, 16), this);
- QWindowSystemInterface::registerInputDevice(dev);
+ if (newOrKeep(deviceInfo->deviceid)) {
+ auto dev = new QInputDevice(QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo)),
+ deviceInfo->deviceid, QInputDevice::DeviceType::Keyboard,
+ QString::number(deviceInfo->deviceid << 16 | deviceInfo->attachment, 16), this);
+ QWindowSystemInterface::registerInputDevice(dev);
+ }
} break;
case XCB_INPUT_DEVICE_TYPE_MASTER_POINTER: {
m_xiMasterPointerIds.append(deviceInfo->deviceid);
- auto dev = new QPointingDevice(QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo)), deviceInfo->deviceid,
- QInputDevice::DeviceType::Mouse, QPointingDevice::PointerType::Generic,
- QInputDevice::Capability::Position | QInputDevice::Capability::Scroll | QInputDevice::Capability::Hover,
- 1, 32, QString::number(deviceInfo->attachment << 16 | deviceInfo->deviceid, 16), QPointingDeviceUniqueId(), this);
- QWindowSystemInterface::registerInputDevice(dev);
+ if (newOrKeep(deviceInfo->deviceid)) {
+ auto dev = new QXcbScrollingDevice(QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo)), deviceInfo->deviceid,
+ QInputDevice::Capability::Position | QInputDevice::Capability::Scroll | QInputDevice::Capability::Hover,
+ 32, QString::number(deviceInfo->attachment << 16 | deviceInfo->deviceid, 16), this);
+ QWindowSystemInterface::registerInputDevice(dev);
+ }
continue;
} break;
default:
@@ -480,126 +513,38 @@ void QXcbConnection::xi2SetupDevices()
// already registered
break;
case XCB_INPUT_DEVICE_TYPE_SLAVE_POINTER: {
- QInputDevice *master = const_cast<QInputDevice *>(QInputDevicePrivate::fromId(deviceInfo->attachment));
- Q_ASSERT(master);
- xi2SetupSlavePointerDevice(deviceInfo, false, qobject_cast<QPointingDevice *>(master));
+ if (newOrKeep(deviceInfo->deviceid)) {
+ m_xiSlavePointerIds.append(deviceInfo->deviceid);
+ QInputDevice *master = const_cast<QInputDevice *>(QInputDevicePrivate::fromId(deviceInfo->attachment));
+ Q_ASSERT(master);
+ xi2SetupSlavePointerDevice(deviceInfo, false, qobject_cast<QPointingDevice *>(master));
+ }
} break;
case XCB_INPUT_DEVICE_TYPE_SLAVE_KEYBOARD: {
- QInputDevice *master = const_cast<QInputDevice *>(QInputDevicePrivate::fromId(deviceInfo->attachment));
- Q_ASSERT(master);
- QWindowSystemInterface::registerInputDevice(new QInputDevice(
- QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo)), deviceInfo->deviceid,
- QInputDevice::DeviceType::Keyboard, master->seatName(), master));
+ if (newOrKeep(deviceInfo->deviceid)) {
+ QInputDevice *master = const_cast<QInputDevice *>(QInputDevicePrivate::fromId(deviceInfo->attachment));
+ Q_ASSERT(master);
+ QWindowSystemInterface::registerInputDevice(new QInputDevice(
+ QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo)), deviceInfo->deviceid,
+ QInputDevice::DeviceType::Keyboard, master->seatName(), master));
+ }
} break;
case XCB_INPUT_DEVICE_TYPE_FLOATING_SLAVE:
break;
}
}
- if (m_xiMasterPointerIds.size() > 1)
- qCDebug(lcQpaXInputDevices) << "multi-pointer X detected";
-}
-
-/*! \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 (window == rootWindow())
- return;
-
- uint32_t mask = 0;
-
- if (isAtLeastXI22()) {
- mask |= XCB_INPUT_XI_EVENT_MASK_TOUCH_BEGIN;
- mask |= XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE;
- mask |= XCB_INPUT_XI_EVENT_MASK_TOUCH_END;
-
- qt_xcb_input_event_mask_t xiMask;
- xiMask.header.deviceid = XCB_INPUT_DEVICE_ALL_MASTER;
- xiMask.header.mask_len = 1;
- xiMask.mask = mask;
-
- xcb_void_cookie_t cookie =
- xcb_input_xi_select_events_checked(xcb_connection(), window, 1, &xiMask.header);
- xcb_generic_error_t *error = xcb_request_check(xcb_connection(), cookie);
- if (error) {
- qCDebug(lcQpaXInput, "failed to select events, window %x, error code %d", window, error->error_code);
- free(error);
- } else {
- QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
- }
- }
-
- mask = XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS;
- mask |= XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE;
- mask |= XCB_INPUT_XI_EVENT_MASK_MOTION;
-
-#if QT_CONFIG(tabletevent)
- QSet<int> tabletDevices;
- if (!m_tabletData.isEmpty()) {
- const int nrTablets = m_tabletData.count();
- QList<qt_xcb_input_event_mask_t> xiEventMask(nrTablets);
- for (int i = 0; i < nrTablets; ++i) {
- int deviceId = m_tabletData.at(i).deviceId;
- tabletDevices.insert(deviceId);
- xiEventMask[i].header.deviceid = deviceId;
- xiEventMask[i].header.mask_len = 1;
- xiEventMask[i].mask = mask;
- }
- xcb_input_xi_select_events(xcb_connection(), window, nrTablets, &(xiEventMask.data()->header));
+ // previousDevices is now the list of those that are no longer found
+ qCDebug(lcQpaXInputDevices) << "removed" << previousDevices;
+ for (auto it = previousDevices.constBegin(); it != previousDevices.constEnd(); ++it) {
+ const auto id = (*it)->systemId();
+ m_xiSlavePointerIds.removeAll(id);
+ m_touchDevices.remove(id);
}
-#endif
+ qDeleteAll(previousDevices);
- if (!m_scrollingDevices.isEmpty()) {
- QList<qt_xcb_input_event_mask_t> xiEventMask(m_scrollingDevices.size());
- 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].header.deviceid = scrollingDevice.deviceId;
- xiEventMask[i].header.mask_len = 1;
- xiEventMask[i].mask = mask;
- i++;
- }
- xcb_input_xi_select_events(xcb_connection(), window, i, &(xiEventMask.data()->header));
- }
+ if (m_xiMasterPointerIds.size() > 1)
+ qCDebug(lcQpaXInputDevices) << "multi-pointer X detected";
}
QXcbConnection::TouchDeviceData *QXcbConnection::touchDeviceForId(int id)
@@ -610,7 +555,7 @@ QXcbConnection::TouchDeviceData *QXcbConnection::touchDeviceForId(int id)
return dev;
}
-QXcbConnection::TouchDeviceData *QXcbConnection::populateTouchDevices(void *info)
+QXcbConnection::TouchDeviceData *QXcbConnection::populateTouchDevices(void *info, QXcbScrollingDevicePrivate *scrollingDeviceP, bool *used)
{
auto *deviceInfo = reinterpret_cast<xcb_input_xi_device_info_t *>(info);
QPointingDevice::Capabilities caps;
@@ -637,6 +582,18 @@ QXcbConnection::TouchDeviceData *QXcbConnection::populateTouchDevices(void *info
}
break;
}
+#if QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
+ case XCB_INPUT_DEVICE_CLASS_TYPE_GESTURE: {
+ // Note that gesture devices can only be touchpads (i.e. dependent devices in XInput
+ // naming convention). According to XI 2.4, the same device can't have touch and
+ // gesture device classes.
+ auto *gci = reinterpret_cast<xcb_input_gesture_class_t *>(classinfo);
+ maxTouchPoints = gci->num_touches;
+ qCDebug(lcQpaXInputDevices, " has gesture class");
+ type = QInputDevice::DeviceType::TouchPad;
+ break;
+ }
+#endif // QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
case XCB_INPUT_DEVICE_CLASS_TYPE_VALUATOR: {
auto *vci = reinterpret_cast<xcb_input_valuator_class_t *>(classinfo);
const QXcbAtom::Atom valuatorAtom = qatom(vci->label);
@@ -651,26 +608,28 @@ QXcbConnection::TouchDeviceData *QXcbConnection::populateTouchDevices(void *info
// 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)
+ if (valuatorAtom == QXcbAtom::AtomAbsMTPositionX)
caps |= QInputDevice::Capability::Position | QInputDevice::Capability::NormalizedPosition;
- else if (valuatorAtom == QXcbAtom::AbsMTTouchMajor)
+ else if (valuatorAtom == QXcbAtom::AtomAbsMTTouchMajor)
caps |= QInputDevice::Capability::Area;
- else if (valuatorAtom == QXcbAtom::AbsMTOrientation)
+ else if (valuatorAtom == QXcbAtom::AtomAbsMTOrientation)
dev.providesTouchOrientation = true;
- else if (valuatorAtom == QXcbAtom::AbsMTPressure || valuatorAtom == QXcbAtom::AbsPressure)
+ else if (valuatorAtom == QXcbAtom::AtomAbsMTPressure || valuatorAtom == QXcbAtom::AtomAbsPressure)
caps |= QInputDevice::Capability::Pressure;
- else if (valuatorAtom == QXcbAtom::RelX) {
+ else if (valuatorAtom == QXcbAtom::AtomRelX) {
hasRelativeCoords = true;
dev.size.setWidth((fixed3232ToReal(vci->max) - fixed3232ToReal(vci->min)) * 1000.0 / vciResolution);
- } else if (valuatorAtom == QXcbAtom::RelY) {
+ } else if (valuatorAtom == QXcbAtom::AtomRelY) {
hasRelativeCoords = true;
dev.size.setHeight((fixed3232ToReal(vci->max) - fixed3232ToReal(vci->min)) * 1000.0 / vciResolution);
- } else if (valuatorAtom == QXcbAtom::AbsX) {
+ } else if (valuatorAtom == QXcbAtom::AtomAbsX) {
caps |= QInputDevice::Capability::Position;
dev.size.setWidth((fixed3232ToReal(vci->max) - fixed3232ToReal(vci->min)) * 1000.0 / vciResolution);
- } else if (valuatorAtom == QXcbAtom::AbsY) {
+ } else if (valuatorAtom == QXcbAtom::AtomAbsY) {
caps |= QInputDevice::Capability::Position;
dev.size.setHeight((fixed3232ToReal(vci->max) - fixed3232ToReal(vci->min)) * 1000.0 / vciResolution);
+ } else if (valuatorAtom == QXcbAtom::AtomRelVertWheel || valuatorAtom == QXcbAtom::AtomRelHorizWheel) {
+ caps |= QInputDevice::Capability::Scroll;
}
break;
}
@@ -690,11 +649,25 @@ QXcbConnection::TouchDeviceData *QXcbConnection::populateTouchDevices(void *info
if (type == QInputDevice::DeviceType::TouchScreen || type == QInputDevice::DeviceType::TouchPad) {
QInputDevice *master = const_cast<QInputDevice *>(QInputDevicePrivate::fromId(deviceInfo->attachment));
Q_ASSERT(master);
- dev.qtTouchDevice = new QPointingDevice(QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo),
- xcb_input_xi_device_info_name_length(deviceInfo)),
- deviceInfo->deviceid,
- type, QPointingDevice::PointerType::Finger, caps, maxTouchPoints, 0,
- master->seatName(), QPointingDeviceUniqueId(), master);
+ if (scrollingDeviceP) {
+ // valuators were already discovered in QXcbConnection::xi2SetupSlavePointerDevice, so just finish initialization
+ scrollingDeviceP->deviceType = type;
+ scrollingDeviceP->pointerType = QPointingDevice::PointerType::Finger;
+ scrollingDeviceP->capabilities |= caps;
+ scrollingDeviceP->maximumTouchPoints = maxTouchPoints;
+ scrollingDeviceP->buttonCount = 3;
+ scrollingDeviceP->seatName = master->seatName();
+ dev.qtTouchDevice = new QXcbScrollingDevice(*scrollingDeviceP, master);
+ if (Q_UNLIKELY(!caps.testFlag(QInputDevice::Capability::Scroll)))
+ qCDebug(lcQpaXInputDevices) << "unexpectedly missing RelVert/HorizWheel atoms for touchpad with scroll capability" << dev.qtTouchDevice;
+ *used = true;
+ } else {
+ dev.qtTouchDevice = new QPointingDevice(QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo),
+ xcb_input_xi_device_info_name_length(deviceInfo)),
+ deviceInfo->deviceid,
+ type, QPointingDevice::PointerType::Finger, caps, maxTouchPoints, 0,
+ master->seatName(), QPointingDeviceUniqueId(), master);
+ }
if (caps != 0)
QWindowSystemInterface::registerInputDevice(dev.qtTouchDevice);
m_touchDevices[deviceInfo->deviceid] = dev;
@@ -712,6 +685,23 @@ static inline qreal fixed1616ToReal(xcb_input_fp1616_t val)
void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
{
auto *xiEvent = reinterpret_cast<qt_xcb_input_device_event_t *>(event);
+ setTime(xiEvent->time);
+ if (m_xiSlavePointerIds.contains(xiEvent->deviceid) && xiEvent->event_type != XCB_INPUT_PROPERTY) {
+ if (!m_duringSystemMoveResize)
+ return;
+ if (xiEvent->event == XCB_NONE)
+ return;
+
+ if (xiEvent->event_type == XCB_INPUT_BUTTON_RELEASE
+ && xiEvent->detail == XCB_BUTTON_INDEX_1 ) {
+ abortSystemMoveResize(xiEvent->event);
+ } else if (xiEvent->event_type == XCB_INPUT_TOUCH_END) {
+ abortSystemMoveResize(xiEvent->event);
+ return;
+ } else {
+ return;
+ }
+ }
int sourceDeviceId = xiEvent->deviceid; // may be the master id
qt_xcb_input_device_event_t *xiDeviceEvent = nullptr;
xcb_input_enter_event_t *xiEnterEvent = nullptr;
@@ -730,6 +720,18 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
sourceDeviceId = xiDeviceEvent->sourceid; // use the actual device id instead of the master
break;
}
+#if QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
+ case XCB_INPUT_GESTURE_PINCH_BEGIN:
+ case XCB_INPUT_GESTURE_PINCH_UPDATE:
+ case XCB_INPUT_GESTURE_PINCH_END:
+ xi2HandleGesturePinchEvent(event);
+ return;
+ case XCB_INPUT_GESTURE_SWIPE_BEGIN:
+ case XCB_INPUT_GESTURE_SWIPE_UPDATE:
+ case XCB_INPUT_GESTURE_SWIPE_END:
+ xi2HandleGestureSwipeEvent(event);
+ return;
+#endif // QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
case XCB_INPUT_ENTER:
case XCB_INPUT_LEAVE: {
xiEnterEvent = reinterpret_cast<xcb_input_enter_event_t *>(event);
@@ -761,16 +763,17 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
}
#endif // QT_CONFIG(tabletevent)
- if (ScrollingDevice *device = scrollingDeviceForId(sourceDeviceId))
- xi2HandleScrollEvent(event, *device);
+ if (auto device = QPointingDevicePrivate::pointingDeviceById(sourceDeviceId))
+ xi2HandleScrollEvent(event, device);
+ else
+ qCDebug(lcQpaXInputEvents) << "scroll event from unregistered device" << sourceDeviceId;
if (xiDeviceEvent) {
switch (xiDeviceEvent->event_type) {
case XCB_INPUT_BUTTON_PRESS:
case XCB_INPUT_BUTTON_RELEASE:
case XCB_INPUT_MOTION:
- if (!xi2MouseEventsDisabled() && eventListener &&
- !(xiDeviceEvent->flags & XCB_INPUT_POINTER_EVENT_FLAGS_POINTER_EMULATED))
+ if (eventListener && !(xiDeviceEvent->flags & XCB_INPUT_POINTER_EVENT_FLAGS_POINTER_EMULATED))
eventListener->handleXIMouseEvent(event);
break;
@@ -782,11 +785,15 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
event->event_type, xiDeviceEvent->sequence, xiDeviceEvent->detail,
fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y),
fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y),xiDeviceEvent->event);
- if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event))
+ if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event)) {
xi2ProcessTouch(xiDeviceEvent, platformWindow);
+ } else { // When the window cannot be matched, delete it from touchPoints
+ if (TouchDeviceData *dev = touchDeviceForId(xiDeviceEvent->sourceid))
+ dev->touchPoints.remove((xiDeviceEvent->detail % INT_MAX));
+ }
break;
}
- } else if (xiEnterEvent && !xi2MouseEventsDisabled() && eventListener) {
+ } else if (xiEnterEvent && eventListener) {
switch (xiEnterEvent->event_type) {
case XCB_INPUT_ENTER:
case XCB_INPUT_LEAVE:
@@ -796,14 +803,6 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
}
}
-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 || hasXinerama();
-}
-
bool QXcbConnection::isTouchScreen(int id)
{
auto device = touchDeviceForId(id);
@@ -814,7 +813,16 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
{
auto *xiDeviceEvent = reinterpret_cast<xcb_input_touch_begin_event_t *>(xiDevEvent);
TouchDeviceData *dev = touchDeviceForId(xiDeviceEvent->sourceid);
- Q_ASSERT(dev);
+ if (!dev) {
+ qCDebug(lcQpaXInputEvents) << "didn't find the dev for given sourceid - " << xiDeviceEvent->sourceid
+ << ", try to repopulate xi2 devices";
+ xi2SetupDevices();
+ dev = touchDeviceForId(xiDeviceEvent->sourceid);
+ if (!dev) {
+ qCDebug(lcQpaXInputEvents) << "still can't find the dev for it, give up.";
+ return;
+ }
+ }
const bool firstTouch = dev->touchPoints.isEmpty();
if (xiDeviceEvent->event_type == XCB_INPUT_TOUCH_BEGIN) {
QWindowSystemInterface::TouchPoint tp;
@@ -830,39 +838,39 @@ 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 (const TouchDeviceData::ValuatorClassInfo &vci : qAsConst(dev->valuatorInfo)) {
+ for (const TouchDeviceData::ValuatorClassInfo &vci : std::as_const(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);
+ atomName(atom(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) {
+ if (vci.label == QXcbAtom::AtomRelX) {
nx = valuatorNormalized;
- } else if (vci.label == QXcbAtom::RelY) {
+ } else if (vci.label == QXcbAtom::AtomRelY) {
ny = valuatorNormalized;
- } else if (vci.label == QXcbAtom::AbsX) {
+ } else if (vci.label == QXcbAtom::AtomAbsX) {
nx = valuatorNormalized;
- } else if (vci.label == QXcbAtom::AbsY) {
+ } else if (vci.label == QXcbAtom::AtomAbsY) {
ny = valuatorNormalized;
- } else if (vci.label == QXcbAtom::AbsMTPositionX) {
+ } else if (vci.label == QXcbAtom::AtomAbsMTPositionX) {
nx = valuatorNormalized;
- } else if (vci.label == QXcbAtom::AbsMTPositionY) {
+ } else if (vci.label == QXcbAtom::AtomAbsMTPositionY) {
ny = valuatorNormalized;
- } else if (vci.label == QXcbAtom::AbsMTTouchMajor) {
+ } else if (vci.label == QXcbAtom::AtomAbsMTTouchMajor) {
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) {
+ w = valuatorNormalized * qHypot(sw, sh);
+ } else if (vci.label == QXcbAtom::AtomAbsMTTouchMinor) {
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) {
+ h = valuatorNormalized * qHypot(sw, sh);
+ } else if (vci.label == QXcbAtom::AtomAbsMTOrientation) {
// 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
@@ -873,7 +881,7 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
value -= 2 * vci.max;
value = qAbs(value);
majorAxisIsY = value < vci.max - value;
- } else if (vci.label == QXcbAtom::AbsMTPressure || vci.label == QXcbAtom::AbsPressure) {
+ } else if (vci.label == QXcbAtom::AtomAbsMTPressure || vci.label == QXcbAtom::AtomAbsPressure) {
touchPoint.pressure = valuatorNormalized;
}
@@ -990,6 +998,8 @@ bool QXcbConnection::startSystemMoveResizeForTouch(xcb_window_t window, int edge
m_startSystemMoveResizeInfo.deviceid = devIt.key();
m_startSystemMoveResizeInfo.pointid = pointIt.key();
m_startSystemMoveResizeInfo.edges = edges;
+ setDuringSystemMoveResize(true);
+ qCDebug(lcQpaXInputDevices) << "triggered system move or resize from touch";
return true;
}
}
@@ -998,9 +1008,38 @@ bool QXcbConnection::startSystemMoveResizeForTouch(xcb_window_t window, int edge
return false;
}
-void QXcbConnection::abortSystemMoveResizeForTouch()
+void QXcbConnection::abortSystemMoveResize(xcb_window_t window)
{
+ qCDebug(lcQpaXInputDevices) << "sending client message NET_WM_MOVERESIZE_CANCEL to window: " << window;
m_startSystemMoveResizeInfo.window = XCB_NONE;
+
+ const xcb_atom_t moveResize = connection()->atom(QXcbAtom::Atom_NET_WM_MOVERESIZE);
+ xcb_client_message_event_t xev;
+ xev.response_type = XCB_CLIENT_MESSAGE;
+ xev.type = moveResize;
+ xev.sequence = 0;
+ xev.window = window;
+ xev.format = 32;
+ xev.data.data32[0] = 0;
+ xev.data.data32[1] = 0;
+ xev.data.data32[2] = 11; // _NET_WM_MOVERESIZE_CANCEL
+ xev.data.data32[3] = 0;
+ xev.data.data32[4] = 0;
+ xcb_send_event(xcb_connection(), false, primaryScreen()->root(),
+ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY,
+ (const char *)&xev);
+
+ m_duringSystemMoveResize = false;
+}
+
+bool QXcbConnection::isDuringSystemMoveResize() const
+{
+ return m_duringSystemMoveResize;
+}
+
+void QXcbConnection::setDuringSystemMoveResize(bool during)
+{
+ m_duringSystemMoveResize = during;
}
bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
@@ -1008,20 +1047,33 @@ bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
bool ok = false;
if (grab) { // grab
- uint32_t mask = XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS
- | XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE
- | XCB_INPUT_XI_EVENT_MASK_MOTION
- | XCB_INPUT_XI_EVENT_MASK_ENTER
- | XCB_INPUT_XI_EVENT_MASK_LEAVE
- | XCB_INPUT_XI_EVENT_MASK_TOUCH_BEGIN
- | XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE
- | XCB_INPUT_XI_EVENT_MASK_TOUCH_END;
-
- for (int id : qAsConst(m_xiMasterPointerIds)) {
+ uint8_t mask[8] = {};
+ setXcbMask(mask, XCB_INPUT_BUTTON_PRESS);
+ setXcbMask(mask, XCB_INPUT_BUTTON_RELEASE);
+ setXcbMask(mask, XCB_INPUT_MOTION);
+ setXcbMask(mask, XCB_INPUT_ENTER);
+ setXcbMask(mask, XCB_INPUT_LEAVE);
+ if (isAtLeastXI22()) {
+ setXcbMask(mask, XCB_INPUT_TOUCH_BEGIN);
+ setXcbMask(mask, XCB_INPUT_TOUCH_UPDATE);
+ setXcbMask(mask, XCB_INPUT_TOUCH_END);
+ }
+#if QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
+ if (isAtLeastXI24()) {
+ setXcbMask(mask, XCB_INPUT_GESTURE_PINCH_BEGIN);
+ setXcbMask(mask, XCB_INPUT_GESTURE_PINCH_UPDATE);
+ setXcbMask(mask, XCB_INPUT_GESTURE_PINCH_END);
+ setXcbMask(mask, XCB_INPUT_GESTURE_SWIPE_BEGIN);
+ setXcbMask(mask, XCB_INPUT_GESTURE_SWIPE_UPDATE);
+ setXcbMask(mask, XCB_INPUT_GESTURE_SWIPE_END);
+ }
+#endif // QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
+
+ for (int id : std::as_const(m_xiMasterPointerIds)) {
xcb_generic_error_t *error = nullptr;
auto cookie = xcb_input_xi_grab_device(xcb_connection(), w, XCB_CURRENT_TIME, XCB_CURSOR_NONE, id,
XCB_INPUT_GRAB_MODE_22_ASYNC, XCB_INPUT_GRAB_MODE_22_ASYNC,
- false, 1, &mask);
+ false, 2, reinterpret_cast<uint32_t *>(mask));
auto *reply = xcb_input_xi_grab_device_reply(xcb_connection(), cookie, &error);
if (error) {
qCDebug(lcQpaXInput, "failed to grab events for device %d on window %x"
@@ -1035,7 +1087,7 @@ bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
free(reply);
}
} else { // ungrab
- for (int id : qAsConst(m_xiMasterPointerIds)) {
+ for (int id : std::as_const(m_xiMasterPointerIds)) {
auto cookie = xcb_input_xi_ungrab_device_checked(xcb_connection(), XCB_CURRENT_TIME, id);
xcb_generic_error_t *error = xcb_request_check(xcb_connection(), cookie);
if (error) {
@@ -1059,27 +1111,182 @@ bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
void QXcbConnection::xi2HandleHierarchyEvent(void *event)
{
auto *xiEvent = reinterpret_cast<xcb_input_hierarchy_event_t *>(event);
- // We only care about hotplugged devices
- if (!(xiEvent->flags & (XCB_INPUT_HIERARCHY_MASK_SLAVE_REMOVED | XCB_INPUT_HIERARCHY_MASK_SLAVE_ADDED)))
+ // We care about hotplugged devices (slaves) and master devices.
+ // We don't report anything for DEVICE_ENABLED or DEVICE_DISABLED
+ // (but often that goes with adding or removal anyway).
+ // We don't react to SLAVE_ATTACHED or SLAVE_DETACHED either.
+ if (xiEvent->flags & (XCB_INPUT_HIERARCHY_MASK_MASTER_ADDED |
+ XCB_INPUT_HIERARCHY_MASK_MASTER_REMOVED |
+ XCB_INPUT_HIERARCHY_MASK_SLAVE_REMOVED |
+ XCB_INPUT_HIERARCHY_MASK_SLAVE_ADDED))
+ xi2SetupDevices();
+}
+
+#if QT_XCB_HAS_TOUCHPAD_GESTURES
+void QXcbConnection::xi2HandleGesturePinchEvent(void *event)
+{
+ auto *xiEvent = reinterpret_cast<qt_xcb_input_pinch_event_t *>(event);
+
+ if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) {
+ qCDebug(lcQpaXInputEvents, "XI2 gesture event type %d seq %d fingers %d pos %6.1f, "
+ "%6.1f root pos %6.1f, %6.1f delta_angle %6.1f scale %6.1f on window %x",
+ xiEvent->event_type, xiEvent->sequence, xiEvent->detail,
+ fixed1616ToReal(xiEvent->event_x), fixed1616ToReal(xiEvent->event_y),
+ fixed1616ToReal(xiEvent->root_x), fixed1616ToReal(xiEvent->root_y),
+ fixed1616ToReal(xiEvent->delta_angle), fixed1616ToReal(xiEvent->scale),
+ xiEvent->event);
+ }
+ QXcbWindow *platformWindow = platformWindowFromId(xiEvent->event);
+ if (!platformWindow)
+ return;
+
+ setTime(xiEvent->time);
+
+ TouchDeviceData *dev = touchDeviceForId(xiEvent->sourceid);
+ Q_ASSERT(dev);
+
+ uint32_t fingerCount = xiEvent->detail;
+
+ switch (xiEvent->event_type) {
+ case XCB_INPUT_GESTURE_PINCH_BEGIN:
+ // Gestures must be accepted when we are grabbing gesture events. Otherwise the entire
+ // sequence will get replayed when the grab ends.
+ if (m_xiGrab) {
+ xcb_input_xi_allow_events(xcb_connection(), XCB_CURRENT_TIME, xiEvent->deviceid,
+ XCB_INPUT_EVENT_MODE_ASYNC_DEVICE, 0, xiEvent->event);
+ }
+ m_lastPinchScale = 1.0;
+ QWindowSystemInterface::handleGestureEvent(platformWindow->window(), xiEvent->time,
+ dev->qtTouchDevice,
+ Qt::BeginNativeGesture,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ break;
+
+ case XCB_INPUT_GESTURE_PINCH_UPDATE: {
+ qreal rotationDelta = fixed1616ToReal(xiEvent->delta_angle);
+ qreal scale = fixed1616ToReal(xiEvent->scale);
+ qreal scaleDelta = scale - m_lastPinchScale;
+ m_lastPinchScale = scale;
+
+ QPointF delta = QPointF(fixed1616ToReal(xiEvent->delta_x),
+ fixed1616ToReal(xiEvent->delta_y));
+
+ if (!delta.isNull()) {
+ QWindowSystemInterface::handleGestureEventWithValueAndDelta(
+ platformWindow->window(), xiEvent->time, dev->qtTouchDevice,
+ Qt::PanNativeGesture, 0, delta,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ }
+ if (rotationDelta != 0) {
+ QWindowSystemInterface::handleGestureEventWithRealValue(
+ platformWindow->window(), xiEvent->time, dev->qtTouchDevice,
+ Qt::RotateNativeGesture,
+ rotationDelta,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ }
+ if (scaleDelta != 0) {
+ QWindowSystemInterface::handleGestureEventWithRealValue(
+ platformWindow->window(), xiEvent->time, dev->qtTouchDevice,
+ Qt::ZoomNativeGesture,
+ scaleDelta,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ }
+ break;
+ }
+ case XCB_INPUT_GESTURE_PINCH_END:
+ QWindowSystemInterface::handleGestureEvent(platformWindow->window(), xiEvent->time,
+ dev->qtTouchDevice,
+ Qt::EndNativeGesture,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ break;
+ }
+}
+
+void QXcbConnection::xi2HandleGestureSwipeEvent(void *event)
+{
+ auto *xiEvent = reinterpret_cast<qt_xcb_input_swipe_event_t *>(event);
+
+ if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) {
+ qCDebug(lcQpaXInputEvents, "XI2 gesture event type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f on window %x",
+ xiEvent->event_type, xiEvent->sequence, xiEvent->detail,
+ fixed1616ToReal(xiEvent->event_x), fixed1616ToReal(xiEvent->event_y),
+ fixed1616ToReal(xiEvent->root_x), fixed1616ToReal(xiEvent->root_y),
+ xiEvent->event);
+ }
+ QXcbWindow *platformWindow = platformWindowFromId(xiEvent->event);
+ if (!platformWindow)
return;
- xi2SetupDevices();
+ setTime(xiEvent->time);
+
+ TouchDeviceData *dev = touchDeviceForId(xiEvent->sourceid);
+ Q_ASSERT(dev);
+
+ uint32_t fingerCount = xiEvent->detail;
- 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());
+ switch (xiEvent->event_type) {
+ case XCB_INPUT_GESTURE_SWIPE_BEGIN:
+ // Gestures must be accepted when we are grabbing gesture events. Otherwise the entire
+ // sequence will get replayed when the grab ends.
+ if (m_xiGrab) {
+ xcb_input_xi_allow_events(xcb_connection(), XCB_CURRENT_TIME, xiEvent->deviceid,
+ XCB_INPUT_EVENT_MODE_ASYNC_DEVICE, 0, xiEvent->event);
+ }
+ QWindowSystemInterface::handleGestureEvent(platformWindow->window(), xiEvent->time,
+ dev->qtTouchDevice,
+ Qt::BeginNativeGesture,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ break;
+ case XCB_INPUT_GESTURE_SWIPE_UPDATE: {
+ QPointF delta = QPointF(fixed1616ToReal(xiEvent->delta_x),
+ fixed1616ToReal(xiEvent->delta_y));
+
+ if (xiEvent->delta_x != 0 || xiEvent->delta_y != 0) {
+ QWindowSystemInterface::handleGestureEventWithValueAndDelta(
+ platformWindow->window(), xiEvent->time, dev->qtTouchDevice,
+ Qt::PanNativeGesture, 0, delta,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ }
+ break;
+ }
+ case XCB_INPUT_GESTURE_SWIPE_END:
+ QWindowSystemInterface::handleGestureEvent(platformWindow->window(), xiEvent->time,
+ dev->qtTouchDevice,
+ Qt::EndNativeGesture,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ break;
}
}
+#else // QT_XCB_HAS_TOUCHPAD_GESTURES
+void QXcbConnection::xi2HandleGesturePinchEvent(void*) {}
+void QXcbConnection::xi2HandleGestureSwipeEvent(void*) {}
+#endif
+
void QXcbConnection::xi2HandleDeviceChangedEvent(void *event)
{
auto *xiEvent = reinterpret_cast<xcb_input_device_changed_event_t *>(event);
switch (xiEvent->reason) {
case XCB_INPUT_CHANGE_REASON_DEVICE_CHANGE: {
+ // Don't call xi2SetupSlavePointerDevice() again for an already-known device, and never for a master.
+ if (m_xiMasterPointerIds.contains(xiEvent->deviceid) || m_xiSlavePointerIds.contains(xiEvent->deviceid))
+ return;
auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, xcb_connection(), xiEvent->sourceid);
if (!reply || reply->num_infos <= 0)
return;
@@ -1088,8 +1295,8 @@ void QXcbConnection::xi2HandleDeviceChangedEvent(void *event)
break;
}
case XCB_INPUT_CHANGE_REASON_SLAVE_SWITCH: {
- if (ScrollingDevice *scrollingDevice = scrollingDeviceForId(xiEvent->sourceid))
- xi2UpdateScrollingDevice(*scrollingDevice);
+ if (auto *scrollingDevice = scrollingDeviceForId(xiEvent->sourceid))
+ xi2UpdateScrollingDevice(scrollingDevice);
break;
}
default:
@@ -1098,16 +1305,21 @@ void QXcbConnection::xi2HandleDeviceChangedEvent(void *event)
}
}
-void QXcbConnection::xi2UpdateScrollingDevice(ScrollingDevice &scrollingDevice)
+void QXcbConnection::xi2UpdateScrollingDevice(QInputDevice *dev)
{
- auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, xcb_connection(), scrollingDevice.deviceId);
+ QXcbScrollingDevice *scrollDev = qobject_cast<QXcbScrollingDevice *>(dev);
+ if (!scrollDev || !scrollDev->capabilities().testFlag(QInputDevice::Capability::Scroll))
+ return;
+ QXcbScrollingDevicePrivate *scrollingDevice = QXcbScrollingDevice::get(scrollDev);
+
+ auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, xcb_connection(), scrollingDevice->systemId);
if (!reply || reply->num_infos <= 0) {
- qCDebug(lcQpaXInputDevices, "scrolling device %d no longer present", scrollingDevice.deviceId);
+ qCDebug(lcQpaXInputDevices, "scrolling device %lld no longer present", scrollingDevice->systemId);
return;
}
QPointF lastScrollPosition;
if (lcQpaXInputEvents().isDebugEnabled())
- lastScrollPosition = scrollingDevice.lastScrollPosition;
+ lastScrollPosition = scrollingDevice->lastScrollPosition;
xcb_input_xi_device_info_t *deviceInfo = xcb_input_xi_query_device_infos_iterator(reply.get()).data;
auto classes_it = xcb_input_xi_device_info_classes_iterator(deviceInfo);
@@ -1116,69 +1328,73 @@ void QXcbConnection::xi2UpdateScrollingDevice(ScrollingDevice &scrollingDevice)
if (classInfo->type == XCB_INPUT_DEVICE_CLASS_TYPE_VALUATOR) {
auto *vci = reinterpret_cast<xcb_input_valuator_class_t *>(classInfo);
const int valuatorAtom = qatom(vci->label);
- if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel)
- scrollingDevice.lastScrollPosition.setX(fixed3232ToReal(vci->value));
- else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel)
- scrollingDevice.lastScrollPosition.setY(fixed3232ToReal(vci->value));
+ if (valuatorAtom == QXcbAtom::AtomRelHorizScroll || valuatorAtom == QXcbAtom::AtomRelHorizWheel)
+ scrollingDevice->lastScrollPosition.setX(fixed3232ToReal(vci->value));
+ else if (valuatorAtom == QXcbAtom::AtomRelVertScroll || valuatorAtom == QXcbAtom::AtomRelVertWheel)
+ scrollingDevice->lastScrollPosition.setY(fixed3232ToReal(vci->value));
}
}
- if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled() && lastScrollPosition != scrollingDevice.lastScrollPosition))
- qCDebug(lcQpaXInputEvents, "scrolling device %d moved from (%f, %f) to (%f, %f)", scrollingDevice.deviceId,
+ if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled() && lastScrollPosition != scrollingDevice->lastScrollPosition))
+ qCDebug(lcQpaXInputEvents, "scrolling device %lld moved from (%f, %f) to (%f, %f)", scrollingDevice->systemId,
lastScrollPosition.x(), lastScrollPosition.y(),
- scrollingDevice.lastScrollPosition.x(),
- scrollingDevice.lastScrollPosition.y());
+ scrollingDevice->lastScrollPosition.x(),
+ scrollingDevice->lastScrollPosition.y());
}
void QXcbConnection::xi2UpdateScrollingDevices()
{
- QHash<int, ScrollingDevice>::iterator it = m_scrollingDevices.begin();
- const QHash<int, ScrollingDevice>::iterator end = m_scrollingDevices.end();
- while (it != end) {
- xi2UpdateScrollingDevice(it.value());
- ++it;
+ const auto &devices = QInputDevice::devices();
+ for (const QInputDevice *dev : devices) {
+ if (dev->capabilities().testFlag(QInputDevice::Capability::Scroll))
+ xi2UpdateScrollingDevice(const_cast<QInputDevice *>(dev));
}
}
-QXcbConnection::ScrollingDevice *QXcbConnection::scrollingDeviceForId(int id)
+QXcbScrollingDevice *QXcbConnection::scrollingDeviceForId(int id)
{
- ScrollingDevice *dev = nullptr;
- if (m_scrollingDevices.contains(id))
- dev = &m_scrollingDevices[id];
- return dev;
+ const QPointingDevice *dev = QPointingDevicePrivate::pointingDeviceById(id);
+ if (!dev|| !dev->capabilities().testFlag(QInputDevice::Capability::Scroll))
+ return nullptr;
+ return qobject_cast<QXcbScrollingDevice *>(const_cast<QPointingDevice *>(dev));
}
-void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice)
+void QXcbConnection::xi2HandleScrollEvent(void *event, const QPointingDevice *dev)
{
auto *xiDeviceEvent = reinterpret_cast<qt_xcb_input_device_event_t *>(event);
- if (xiDeviceEvent->event_type == XCB_INPUT_MOTION && scrollingDevice.orientations) {
+ const QXcbScrollingDevice *scrollDev = qobject_cast<const QXcbScrollingDevice *>(dev);
+ if (!scrollDev || !scrollDev->capabilities().testFlag(QInputDevice::Capability::Scroll))
+ return;
+ const QXcbScrollingDevicePrivate *scrollingDevice = QXcbScrollingDevice::get(scrollDev);
+
+ if (xiDeviceEvent->event_type == XCB_INPUT_MOTION && scrollingDevice->orientations) {
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);
+ 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);
// With most drivers the increment is 1 for wheels.
// For libinput it is hardcoded to a useless 15.
// For a proper touchpad driver it should be in the same order of magnitude as 120
- if (scrollingDevice.verticalIncrement > 15)
+ if (scrollingDevice->verticalIncrement > 15)
rawDelta.setY(delta);
- else if (scrollingDevice.verticalIncrement < -15)
+ else if (scrollingDevice->verticalIncrement < -15)
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);
+ 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);
// See comment under vertical
- if (scrollingDevice.horizontalIncrement > 15)
+ if (scrollingDevice->horizontalIncrement > 15)
rawDelta.setX(delta);
- else if (scrollingDevice.horizontalIncrement < -15)
+ else if (scrollingDevice->horizontalIncrement < -15)
rawDelta.setX(-delta);
}
}
@@ -1190,20 +1406,22 @@ void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollin
angleDelta = angleDelta.transposed();
rawDelta = rawDelta.transposed();
}
- qCDebug(lcQpaXInputEvents) << "scroll wheel @ window pos" << local << "delta px" << rawDelta << "angle" << angleDelta;
- QWindowSystemInterface::handleWheelEvent(platformWindow->window(), xiDeviceEvent->time, local, global, rawDelta, angleDelta, modifiers);
+ qCDebug(lcQpaXInputEvents) << "scroll wheel from device" << scrollingDevice->systemId
+ << "@ window pos" << local << "delta px" << rawDelta << "angle" << angleDelta;
+ QWindowSystemInterface::handleWheelEvent(platformWindow->window(), xiDeviceEvent->time, dev,
+ local, global, rawDelta, angleDelta, modifiers);
}
}
- } else if (xiDeviceEvent->event_type == XCB_INPUT_BUTTON_RELEASE && scrollingDevice.legacyOrientations) {
+ } else if (xiDeviceEvent->event_type == XCB_INPUT_BUTTON_RELEASE && scrollingDevice->legacyOrientations) {
if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event)) {
QPoint angleDelta;
- if (scrollingDevice.legacyOrientations & Qt::Vertical) {
+ if (scrollingDevice->legacyOrientations & Qt::Vertical) {
if (xiDeviceEvent->detail == 4)
angleDelta.setY(120);
else if (xiDeviceEvent->detail == 5)
angleDelta.setY(-120);
}
- if (scrollingDevice.legacyOrientations & Qt::Horizontal) {
+ if (scrollingDevice->legacyOrientations & Qt::Horizontal) {
if (xiDeviceEvent->detail == 6)
angleDelta.setX(120);
else if (xiDeviceEvent->detail == 7)
@@ -1216,7 +1434,8 @@ void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollin
if (modifiers & Qt::AltModifier)
angleDelta = angleDelta.transposed();
qCDebug(lcQpaXInputEvents) << "scroll wheel (button" << xiDeviceEvent->detail << ") @ window pos" << local << "delta angle" << angleDelta;
- QWindowSystemInterface::handleWheelEvent(platformWindow->window(), xiDeviceEvent->time, local, global, QPoint(), angleDelta, modifiers);
+ QWindowSystemInterface::handleWheelEvent(platformWindow->window(), xiDeviceEvent->time, dev,
+ local, global, QPoint(), angleDelta, modifiers);
}
}
}
@@ -1298,7 +1517,7 @@ bool QXcbConnection::xi2HandleTabletEvent(const void *event, TabletData *tabletD
// The evdev driver doesn't do it this way.
const auto *ev = reinterpret_cast<const xcb_input_property_event_t *>(event);
if (ev->what == XCB_INPUT_PROPERTY_FLAG_MODIFIED) {
- if (ev->property == atom(QXcbAtom::WacomSerialIDs)) {
+ if (ev->property == atom(QXcbAtom::AtomWacomSerialIDs)) {
enum WacomSerialIndex {
_WACSER_USB_ID = 0,
_WACSER_LAST_TOOL_SERIAL,
@@ -1311,7 +1530,7 @@ bool QXcbConnection::xi2HandleTabletEvent(const void *event, TabletData *tabletD
auto reply = Q_XCB_REPLY(xcb_input_xi_get_property, xcb_connection(), tabletData->deviceId, 0,
ev->property, XCB_GET_PROPERTY_TYPE_ANY, 0, 100);
if (reply) {
- if (reply->type == atom(QXcbAtom::INTEGER) && reply->format == 32 && reply->num_items == _WACSER_COUNT) {
+ if (reply->type == atom(QXcbAtom::AtomINTEGER) && reply->format == 32 && reply->num_items == _WACSER_COUNT) {
quint32 *ptr = reinterpret_cast<quint32 *>(xcb_input_xi_get_property_items(reply.get()));
quint32 tool = ptr[_WACSER_TOOL_ID];
// Workaround for http://sourceforge.net/p/linuxwacom/bugs/246/
@@ -1319,6 +1538,7 @@ bool QXcbConnection::xi2HandleTabletEvent(const void *event, TabletData *tabletD
if (!tool && ptr[_WACSER_TOOL_SERIAL])
tool = ptr[_WACSER_TOOL_SERIAL];
+ QWindow *win = nullptr; // TODO QTBUG-111400 get the position somehow, then the window
// The property change event informs us which tool is in proximity or which one left proximity.
if (tool) {
const QPointingDevice *dev = tabletToolInstance(nullptr, tabletData->name,
@@ -1327,30 +1547,26 @@ bool QXcbConnection::xi2HandleTabletEvent(const void *event, TabletData *tabletD
tabletData->inProximity = true;
tabletData->tool = dev->type();
tabletData->serialId = qint64(ptr[_WACSER_TOOL_SERIAL]);
- QWindowSystemInterface::handleTabletEnterProximityEvent(ev->time,
- int(tabletData->tool), int(tabletData->pointerType), tabletData->serialId);
+ QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(win, ev->time, dev, true); // enter
} else {
tool = ptr[_WACSER_LAST_TOOL_ID];
// Workaround for http://sourceforge.net/p/linuxwacom/bugs/246/
// e.g. on Thinkpad Helix, tool ID will be 0 and serial will be 1
if (!tool)
tool = ptr[_WACSER_LAST_TOOL_SERIAL];
- const QInputDevice *dev = QInputDevicePrivate::fromId(tabletData->deviceId);
+ auto *dev = qobject_cast<const QPointingDevice *>(QInputDevicePrivate::fromId(tabletData->deviceId));
Q_ASSERT(dev);
tabletData->tool = dev->type();
tabletData->inProximity = false;
tabletData->serialId = qint64(ptr[_WACSER_LAST_TOOL_SERIAL]);
- // TODO why doesn't it just take QPointingDevice*
- QWindowSystemInterface::handleTabletLeaveProximityEvent(ev->time,
- int(tabletData->tool), int(tabletData->pointerType), tabletData->serialId);
+ QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(win, ev->time, dev, false); // leave
}
// TODO maybe have a hash of tabletData->deviceId to device data so we can
// look up the tablet name here, and distinguish multiple tablets
- if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
- qCDebug(lcQpaXInputDevices, "XI2 proximity change on tablet %d %s (USB %x): last tool: %x id %x current tool: %x id %x %s",
- tabletData->deviceId, qPrintable(tabletData->name), ptr[_WACSER_USB_ID],
- ptr[_WACSER_LAST_TOOL_SERIAL], ptr[_WACSER_LAST_TOOL_ID],
- ptr[_WACSER_TOOL_SERIAL], ptr[_WACSER_TOOL_ID], toolName(tabletData->tool));
+ qCDebug(lcQpaXInputDevices, "XI2 proximity change on tablet %d %s (USB %x): last tool: %x id %x current tool: %x id %x %s",
+ tabletData->deviceId, qPrintable(tabletData->name), ptr[_WACSER_USB_ID],
+ ptr[_WACSER_LAST_TOOL_SERIAL], ptr[_WACSER_LAST_TOOL_ID],
+ ptr[_WACSER_TOOL_SERIAL], ptr[_WACSER_TOOL_ID], toolName(tabletData->tool));
}
}
}
@@ -1384,6 +1600,9 @@ void QXcbConnection::xi2ReportTabletEvent(const void *event, TabletData *tabletD
double pressure = 0, rotation = 0, tangentialPressure = 0;
int xTilt = 0, yTilt = 0;
static const bool useValuators = !qEnvironmentVariableIsSet("QT_XCB_TABLET_LEGACY_COORDINATES");
+ const QPointingDevice *dev = QPointingDevicePrivate::tabletDevice(QInputDevice::DeviceType(tabletData->tool),
+ QPointingDevice::PointerType(tabletData->pointerType),
+ QPointingDeviceUniqueId::fromNumericId(tabletData->serialId));
// Valuators' values are relative to the physical size of the current virtual
// screen. Therefore we cannot use QScreen/QWindow geometry and should use
@@ -1402,38 +1621,37 @@ void QXcbConnection::xi2ReportTabletEvent(const void *event, TabletData *tabletD
xi2GetValuatorValueIfSet(event, classInfo.number, &classInfo.curVal);
double normalizedValue = (classInfo.curVal - classInfo.minVal) / (classInfo.maxVal - classInfo.minVal);
switch (valuator) {
- case QXcbAtom::AbsX:
+ case QXcbAtom::AtomAbsX:
if (Q_LIKELY(useValuators)) {
const qreal value = scaleOneValuator(normalizedValue, physicalScreenArea.x(), physicalScreenArea.width());
global.setX(value);
- // mapFromGlobal is ok for nested/embedded windows, but works only with whole-number QPoint;
- // so map it first, then add back the sub-pixel position
- local.setX(window->mapFromGlobal(QPoint(int(value), 0)).x() + (value - int(value)));
+ local.setX(xcbWindow->mapFromGlobalF(global).x());
}
break;
- case QXcbAtom::AbsY:
+ case QXcbAtom::AtomAbsY:
if (Q_LIKELY(useValuators)) {
qreal value = scaleOneValuator(normalizedValue, physicalScreenArea.y(), physicalScreenArea.height());
global.setY(value);
- local.setY(window->mapFromGlobal(QPoint(0, int(value))).y() + (value - int(value)));
+ local.setY(xcbWindow->mapFromGlobalF(global).y());
}
break;
- case QXcbAtom::AbsPressure:
+ case QXcbAtom::AtomAbsPressure:
pressure = normalizedValue;
break;
- case QXcbAtom::AbsTiltX:
+ case QXcbAtom::AtomAbsTiltX:
xTilt = classInfo.curVal;
break;
- case QXcbAtom::AbsTiltY:
+ case QXcbAtom::AtomAbsTiltY:
yTilt = classInfo.curVal;
break;
- case QXcbAtom::AbsWheel:
+ case QXcbAtom::AtomAbsWheel:
switch (tabletData->tool) {
case QInputDevice::DeviceType::Airbrush:
tangentialPressure = normalizedValue * 2.0 - 1.0; // Convert 0..1 range to -1..+1 range
break;
case QInputDevice::DeviceType::Stylus:
- rotation = normalizedValue * 360.0 - 180.0; // Convert 0..1 range to -180..+180 degrees
+ if (dev->capabilities().testFlag(QInputDevice::Capability::Rotation))
+ rotation = normalizedValue * 360.0 - 180.0; // Convert 0..1 range to -180..+180 degrees
break;
default: // Other types of styli do not use this valuator
break;
@@ -1452,16 +1670,15 @@ void QXcbConnection::xi2ReportTabletEvent(const void *event, TabletData *tabletD
local.x(), local.y(), global.x(), global.y(),
(int)tabletData->buttons, pressure, xTilt, yTilt, rotation, (int)modifiers);
- QWindowSystemInterface::handleTabletEvent(window, ev->time, local, global,
- int(tabletData->tool), int(tabletData->pointerType),
+ QWindowSystemInterface::handleTabletEvent(window, ev->time, dev, local, global,
tabletData->buttons, pressure,
xTilt, yTilt, tangentialPressure,
- rotation, 0, tabletData->serialId, modifiers);
+ rotation, 0, modifiers);
}
QXcbConnection::TabletData *QXcbConnection::tabletDataForDevice(int id)
{
- for (int i = 0; i < m_tabletData.count(); ++i) {
+ for (int i = 0; i < m_tabletData.size(); ++i) {
if (m_tabletData.at(i).deviceId == id)
return &m_tabletData[i];
}