Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qvxmousehandler.cpp
Go to the documentation of this file.
1
2// Copyright (C) 2025 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
6
7#include <QSocketNotifier>
8#include <QStringList>
9#include <QPoint>
10#include <QGuiApplication>
11#include <QScreen>
12#include <QLoggingCategory>
13#include <qpa/qwindowsysteminterface.h>
14
15#include <qplatformdefs.h>
16#include <private/qcore_unix_p.h> // overrides QT_OPEN
17#include <private/qhighdpiscaling_p.h>
18
19#include <errno.h>
20#include <evdevLib.h>
21
22QT_BEGIN_NAMESPACE
23
24using namespace Qt::StringLiterals;
25
26Q_LOGGING_CATEGORY(qLcVxMouse, "qt.qpa.input")
27
28std::unique_ptr<QVxMouseHandler> QVxMouseHandler::create(const QString &device, const QString &specification)
29{
30 qCDebug(qLcVxMouse) << "create mouse handler for" << device << specification;
31
32 bool compression = true;
33 int jitterLimit = 0;
34 int grab = 0;
35 bool abs = false;
36
37 const auto args = QStringView{specification}.split(u':');
38 for (const auto &arg : args) {
39 if (arg == "nocompress"_L1)
40 compression = false;
41 else if (arg.startsWith("dejitter="_L1))
42 jitterLimit = arg.mid(9).toInt();
43 else if (arg.startsWith("grab="_L1))
44 grab = arg.mid(5).toInt();
45 }
46
47 int fd = qt_safe_open(device.toLocal8Bit().constData(), O_RDONLY | O_NONBLOCK, 0);
48 if (fd >= 0) {
49 return std::unique_ptr<QVxMouseHandler>(new QVxMouseHandler(device, fd, compression, jitterLimit));
50 } else {
51 qCWarning(qLcVxMouse) << "Cannot open mouse input device" << qPrintable(device) << ":" << strerror(errno);
52 return nullptr;
53 }
54}
55
56QVxMouseHandler::QVxMouseHandler(const QString &device, int fd, bool compression, int jitterLimit)
57 : m_device(device), m_fd(fd), m_compression(compression)
58{
59 setObjectName("Evdev Mouse Handler"_L1);
60
61 m_jitterLimitSquared = jitterLimit * jitterLimit;
62
63 // socket notifier for events on the mouse device
64 m_notify = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);
65 connect(m_notify, &QSocketNotifier::activated,
66 this, &QVxMouseHandler::readMouseData);
67}
68
70{
71 if (m_fd >= 0)
72 qt_safe_close(m_fd);
73}
74
75void QVxMouseHandler::sendMouseEvent()
76{
77 int x;
78 int y;
79
80 x = m_x - m_prevx;
81 y = m_y - m_prevy;
82 if (m_prevInvalid) {
83 x = y = 0;
84 m_prevInvalid = false;
85 }
86
87 if (m_eventType == QEvent::MouseMove)
88 emit handleMouseEvent(x, y, m_buttons, Qt::NoButton, m_eventType);
89 else
90 emit handleMouseEvent(x, y, m_buttons, m_button, m_eventType);
91
92 m_prevx = m_x;
93 m_prevy = m_y;
94}
95
97{
98 EV_DEV_EVENT buffer[32];
99 int n = 0;
100 bool posChanged = false, btnChanged = false;
101 bool pendingMouseEvent = false;
102 forever {
103 int result = QT_READ(m_fd, reinterpret_cast<char *>(buffer) + n, sizeof(buffer) - n);
104
105 if (result == 0) {
106 qCWarning(qLcVxMouse)<<"evdevmouse: Got EOF from the input device";
107 return;
108 } else if (result < 0) {
109 if (errno != EINTR && errno != EAGAIN) {
110 qCWarning(qLcVxMouse)<<"evdevmouse: Could not read from input device"<<errno;
111 // If the device got disconnected, stop reading, otherwise we get flooded
112 // by the above error over and over again.
113 if (errno == ENODEV) {
114 delete m_notify;
115 m_notify = nullptr;
116 qt_safe_close(m_fd);
117 m_fd = -1;
118 }
119 return;
120 }
121 } else {
122 n += result;
123 if (n % sizeof(buffer[0]) == 0)
124 break;
125 }
126 }
127
128 n /= sizeof(buffer[0]);
129
130 for (int i = 0; i < n; ++i) {
131 EV_DEV_EVENT *data = &buffer[i];
132 if (data->type == EV_DEV_REL) {
133 if (data->code == EV_DEV_PTR_REL_X) {
134 m_x += data->value;
135 posChanged = true;
136 } else if (data->code == EV_DEV_PTR_REL_Y) {
137 m_y += data->value;
138 posChanged = true;
139 }
140 } else if (data->type == EV_DEV_KEY && data->code >= EV_DEV_PTR_BTN_LEFT) {
141 Qt::MouseButton button = Qt::NoButton;
142 // BTN_LEFT == 0x110 in kernel's input.h
143 // The range of possible mouse buttons ends just before BTN_JOYSTICK, value 0x120.
144 switch (data->code) {
145 case 0x110: button = Qt::LeftButton; break; // BTN_LEFT
146 case 0x111: button = Qt::RightButton; break;
147 case 0x112: button = Qt::MiddleButton; break;
148 case 0x113: button = Qt::ExtraButton1; break; // AKA Qt::BackButton
149 case 0x114: button = Qt::ExtraButton2; break; // AKA Qt::ForwardButton
150 case 0x115: button = Qt::ExtraButton3; break; // AKA Qt::TaskButton
151 case 0x116: button = Qt::ExtraButton4; break;
152 case 0x117: button = Qt::ExtraButton5; break;
153 case 0x118: button = Qt::ExtraButton6; break;
154 case 0x119: button = Qt::ExtraButton7; break;
155 case 0x11a: button = Qt::ExtraButton8; break;
156 case 0x11b: button = Qt::ExtraButton9; break;
157 case 0x11c: button = Qt::ExtraButton10; break;
158 case 0x11d: button = Qt::ExtraButton11; break;
159 case 0x11e: button = Qt::ExtraButton12; break;
160 case 0x11f: button = Qt::ExtraButton13; break;
161 }
162 m_buttons.setFlag(button, data->value);
163 m_button = button;
164 m_eventType = data->value == 0 ? QEvent::MouseButtonRelease : QEvent::MouseButtonPress;
165 btnChanged = true;
166 } else if (data->type == EV_DEV_SYN) {
167 if (btnChanged) {
168 btnChanged = posChanged = false;
169 sendMouseEvent();
170 pendingMouseEvent = false;
171 } else if (posChanged) {
172 m_eventType = QEvent::MouseMove;
173 posChanged = false;
174 if (m_compression) {
175 pendingMouseEvent = true;
176 } else {
177 sendMouseEvent();
178 }
179 }
180 }
181 }
182 if (m_compression && pendingMouseEvent) {
183 int distanceSquared = (m_x - m_prevx)*(m_x - m_prevx) + (m_y - m_prevy)*(m_y - m_prevy);
184 if (distanceSquared > m_jitterLimitSquared)
185 sendMouseEvent();
186 }
187}
188
189QT_END_NAMESPACE
190
191#include "moc_qvxmousehandler_p.cpp"
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")