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
18QT_BEGIN_NAMESPACE
19
20using namespace Qt::StringLiterals;
21
22Q_DECLARE_LOGGING_CATEGORY(qLcEvdevMouse)
23
24QEvdevMouseManager::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
72QEvdevMouseManager::~QEvdevMouseManager()
73{
74}
75
76void 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
92void 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
112void 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
118void 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
134void 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
142void QEvdevMouseManager::updateDeviceCount()
143{
144 QInputDeviceManagerPrivate::get(mgr: QGuiApplicationPrivate::inputDeviceManager())->setDeviceCount(
145 type: QInputDeviceManager::DeviceTypePointer, count: m_mice.count());
146}
147
148QT_END_NAMESPACE
149

source code of qtbase/src/platformsupport/input/evdevmouse/qevdevmousemanager.cpp