1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qevdevmousemanager_p.h" |
5 | |
6 | #include <QtInputSupport/private/qevdevutil_p.h> |
7 | |
8 | #include <QStringList> |
9 | #include <QGuiApplication> |
10 | #include <QScreen> |
11 | #include <QLoggingCategory> |
12 | #include <qpa/qwindowsysteminterface.h> |
13 | #include <QtDeviceDiscoverySupport/private/qdevicediscovery_p.h> |
14 | #include <private/qguiapplication_p.h> |
15 | #include <private/qinputdevicemanager_p_p.h> |
16 | #include <private/qhighdpiscaling_p.h> |
17 | |
18 | QT_BEGIN_NAMESPACE |
19 | |
20 | using namespace Qt::StringLiterals; |
21 | |
22 | Q_DECLARE_LOGGING_CATEGORY(qLcEvdevMouse) |
23 | |
24 | QEvdevMouseManager::QEvdevMouseManager(const QString &key, const QString &specification, QObject *parent) |
25 | : QObject(parent), m_x(0), m_y(0), m_xoffset(0), m_yoffset(0) |
26 | { |
27 | Q_UNUSED(key); |
28 | |
29 | QString spec = QString::fromLocal8Bit(ba: qgetenv(varName: "QT_QPA_EVDEV_MOUSE_PARAMETERS" )); |
30 | |
31 | if (spec.isEmpty()) |
32 | spec = specification; |
33 | |
34 | auto parsed = QEvdevUtil::parseSpecification(specification: spec); |
35 | m_spec = std::move(parsed.spec); |
36 | |
37 | for (const auto &arg : std::as_const(t&: parsed.args)) { |
38 | if (arg.startsWith(s: "xoffset="_L1 )) { |
39 | m_xoffset = arg.mid(pos: 8).toInt(); |
40 | } else if (arg.startsWith(s: "yoffset="_L1 )) { |
41 | m_yoffset = arg.mid(pos: 8).toInt(); |
42 | } |
43 | } |
44 | |
45 | // add all mice for devices specified in the argument list |
46 | for (const QString &device : std::as_const(t&: parsed.devices)) |
47 | addMouse(deviceNode: device); |
48 | |
49 | if (parsed.devices.isEmpty()) { |
50 | qCDebug(qLcEvdevMouse, "evdevmouse: Using device discovery" ); |
51 | if (auto deviceDiscovery = QDeviceDiscovery::create(type: QDeviceDiscovery::Device_Mouse | QDeviceDiscovery::Device_Touchpad, parent: this)) { |
52 | // scan and add already connected keyboards |
53 | const QStringList devices = deviceDiscovery->scanConnectedDevices(); |
54 | for (const QString &device : devices) |
55 | addMouse(deviceNode: device); |
56 | |
57 | connect(sender: deviceDiscovery, signal: &QDeviceDiscovery::deviceDetected, |
58 | context: this, slot: &QEvdevMouseManager::addMouse); |
59 | connect(sender: deviceDiscovery, signal: &QDeviceDiscovery::deviceRemoved, |
60 | context: this, slot: &QEvdevMouseManager::removeMouse); |
61 | } |
62 | } |
63 | |
64 | QInputDeviceManager *manager = QGuiApplicationPrivate::inputDeviceManager(); |
65 | connect(sender: manager, signal: &QInputDeviceManager::cursorPositionChangeRequested, slot: [this](const QPoint &pos) { |
66 | m_x = pos.x(); |
67 | m_y = pos.y(); |
68 | clampPosition(); |
69 | }); |
70 | } |
71 | |
72 | QEvdevMouseManager::~QEvdevMouseManager() |
73 | { |
74 | } |
75 | |
76 | void QEvdevMouseManager::clampPosition() |
77 | { |
78 | // clamp to screen geometry |
79 | QScreen *primaryScreen = QGuiApplication::primaryScreen(); |
80 | QRect g = QHighDpi::toNativePixels(value: primaryScreen->virtualGeometry(), context: primaryScreen); |
81 | if (m_x + m_xoffset < g.left()) |
82 | m_x = g.left() - m_xoffset; |
83 | else if (m_x + m_xoffset > g.right()) |
84 | m_x = g.right() - m_xoffset; |
85 | |
86 | if (m_y + m_yoffset < g.top()) |
87 | m_y = g.top() - m_yoffset; |
88 | else if (m_y + m_yoffset > g.bottom()) |
89 | m_y = g.bottom() - m_yoffset; |
90 | } |
91 | |
92 | void QEvdevMouseManager::handleMouseEvent(int x, int y, bool abs, Qt::MouseButtons buttons, |
93 | Qt::MouseButton button, QEvent::Type type) |
94 | { |
95 | // update current absolute coordinates |
96 | if (!abs) { |
97 | m_x += x; |
98 | m_y += y; |
99 | } else { |
100 | m_x = x; |
101 | m_y = y; |
102 | } |
103 | |
104 | clampPosition(); |
105 | |
106 | QPoint pos(m_x + m_xoffset, m_y + m_yoffset); |
107 | // Cannot track the keyboard modifiers ourselves here. Instead, report the |
108 | // modifiers from the last key event that has been seen by QGuiApplication. |
109 | QWindowSystemInterface::handleMouseEvent(window: 0, local: pos, global: pos, state: buttons, button, type, mods: QGuiApplicationPrivate::inputDeviceManager()->keyboardModifiers()); |
110 | } |
111 | |
112 | void QEvdevMouseManager::handleWheelEvent(QPoint delta) |
113 | { |
114 | QPoint pos(m_x + m_xoffset, m_y + m_yoffset); |
115 | QWindowSystemInterface::handleWheelEvent(window: 0, local: pos, global: pos, pixelDelta: QPoint(), angleDelta: delta, mods: QGuiApplicationPrivate::inputDeviceManager()->keyboardModifiers()); |
116 | } |
117 | |
118 | void QEvdevMouseManager::addMouse(const QString &deviceNode) |
119 | { |
120 | qCDebug(qLcEvdevMouse, "Adding mouse at %ls" , qUtf16Printable(deviceNode)); |
121 | auto handler = QEvdevMouseHandler::create(device: deviceNode, specification: m_spec); |
122 | if (handler) { |
123 | connect(sender: handler.get(), signal: &QEvdevMouseHandler::handleMouseEvent, |
124 | context: this, slot: &QEvdevMouseManager::handleMouseEvent); |
125 | connect(sender: handler.get(), signal: &QEvdevMouseHandler::handleWheelEvent, |
126 | context: this, slot: &QEvdevMouseManager::handleWheelEvent); |
127 | m_mice.add(deviceNode, handler: std::move(handler)); |
128 | updateDeviceCount(); |
129 | } else { |
130 | qWarning(msg: "evdevmouse: Failed to open mouse device %ls" , qUtf16Printable(deviceNode)); |
131 | } |
132 | } |
133 | |
134 | void QEvdevMouseManager::removeMouse(const QString &deviceNode) |
135 | { |
136 | if (m_mice.remove(deviceNode)) { |
137 | qCDebug(qLcEvdevMouse, "Removing mouse at %ls" , qUtf16Printable(deviceNode)); |
138 | updateDeviceCount(); |
139 | } |
140 | } |
141 | |
142 | void QEvdevMouseManager::updateDeviceCount() |
143 | { |
144 | QInputDeviceManagerPrivate::get(mgr: QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount( |
145 | type: QInputDeviceManager::DeviceTypePointer, count: m_mice.count()); |
146 | } |
147 | |
148 | QT_END_NAMESPACE |
149 | |