Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qdevicediscovery_udev.cpp
Go to the documentation of this file.
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
5
6#include <QStringList>
7#include <QCoreApplication>
8#include <QObject>
9#include <QHash>
10#include <QSocketNotifier>
11#include <QLoggingCategory>
12
13#ifdef Q_OS_FREEBSD
14#include <dev/evdev/input.h>
15#else
16#include <linux/input.h>
17#endif
18
20
21using namespace Qt::StringLiterals;
22
23Q_LOGGING_CATEGORY(lcDD, "qt.qpa.input")
24
26{
27 qCDebug(lcDD) << "udev device discovery for type" << types;
28
29 QDeviceDiscovery *helper = nullptr;
30 struct udev *udev;
31
32 udev = udev_new();
33 if (udev) {
34 helper = new QDeviceDiscoveryUDev(types, udev, parent);
35 } else {
36 qWarning("Failed to get udev library context");
37 }
38
39 return helper;
40}
41
42QDeviceDiscoveryUDev::QDeviceDiscoveryUDev(QDeviceTypes types, struct udev *udev, QObject *parent) :
43 QDeviceDiscovery(types, parent),
44 m_udev(udev)
45{
46 if (!m_udev)
47 return;
48
49 m_udevMonitor = udev_monitor_new_from_netlink(m_udev, "udev");
50 if (!m_udevMonitor) {
51 qWarning("Unable to create an udev monitor. No devices can be detected.");
52 return;
53 }
54
55 udev_monitor_filter_add_match_subsystem_devtype(m_udevMonitor, "input", 0);
56 udev_monitor_filter_add_match_subsystem_devtype(m_udevMonitor, "drm", 0);
57 udev_monitor_enable_receiving(m_udevMonitor);
58 m_udevMonitorFileDescriptor = udev_monitor_get_fd(m_udevMonitor);
59
60 m_udevSocketNotifier = new QSocketNotifier(m_udevMonitorFileDescriptor, QSocketNotifier::Read, this);
61 connect(m_udevSocketNotifier, SIGNAL(activated(QSocketDescriptor)), this, SLOT(handleUDevNotification()));
62}
63
65{
66 if (m_udevMonitor)
67 udev_monitor_unref(m_udevMonitor);
68
69 if (m_udev)
70 udev_unref(m_udev);
71}
72
74{
76
77 if (!m_udev)
78 return devices;
79
80 udev_enumerate *ue = udev_enumerate_new(m_udev);
81 udev_enumerate_add_match_subsystem(ue, "input");
82 udev_enumerate_add_match_subsystem(ue, "drm");
83
85 udev_enumerate_add_match_property(ue, "ID_INPUT_MOUSE", "1");
87 udev_enumerate_add_match_property(ue, "ID_INPUT_TOUCHPAD", "1");
89 udev_enumerate_add_match_property(ue, "ID_INPUT_TOUCHSCREEN", "1");
91 udev_enumerate_add_match_property(ue, "ID_INPUT_KEYBOARD", "1");
92 udev_enumerate_add_match_property(ue, "ID_INPUT_KEY", "1");
93 }
95 udev_enumerate_add_match_property(ue, "ID_INPUT_TABLET", "1");
97 udev_enumerate_add_match_property(ue, "ID_INPUT_JOYSTICK", "1");
98
99 if (udev_enumerate_scan_devices(ue) != 0) {
100 qWarning("Failed to scan devices");
101 return devices;
102 }
103
104 udev_list_entry *entry;
105 udev_list_entry_foreach (entry, udev_enumerate_get_list_entry(ue)) {
106 const char *syspath = udev_list_entry_get_name(entry);
107 udev_device *udevice = udev_device_new_from_syspath(m_udev, syspath);
108 QString candidate = QString::fromUtf8(udev_device_get_devnode(udevice));
109 if ((m_types & Device_InputMask) && candidate.startsWith(QT_EVDEV_DEVICE ""_L1))
110 devices << candidate;
111 if ((m_types & Device_VideoMask) && candidate.startsWith(QT_DRM_DEVICE ""_L1)) {
113 udev_device *pci = udev_device_get_parent_with_subsystem_devtype(udevice, "pci", 0);
114 if (pci) {
115 if (qstrcmp(udev_device_get_sysattr_value(pci, "boot_vga"), "1") == 0)
116 devices << candidate;
117 }
118 } else
119 devices << candidate;
120 }
121
122 udev_device_unref(udevice);
123 }
124 udev_enumerate_unref(ue);
125
126 qCDebug(lcDD) << "Found matching devices" << devices;
127
128 return devices;
129}
130
131void QDeviceDiscoveryUDev::handleUDevNotification()
132{
133 if (!m_udevMonitor)
134 return;
135
136 struct udev_device *dev;
137 QString devNode;
138
139 dev = udev_monitor_receive_device(m_udevMonitor);
140 if (!dev)
141 goto cleanup;
142
143 const char *action;
144 action = udev_device_get_action(dev);
145 if (!action)
146 goto cleanup;
147
148 const char *str;
149 str = udev_device_get_devnode(dev);
150 if (!str)
151 goto cleanup;
152
153 const char *subsystem;
154 devNode = QString::fromUtf8(str);
155 if (devNode.startsWith(QT_EVDEV_DEVICE ""_L1))
156 subsystem = "input";
157 else if (devNode.startsWith(QT_DRM_DEVICE ""_L1))
158 subsystem = "drm";
159 else goto cleanup;
160
161 // if we cannot determine a type, walk up the device tree
162 if (!checkDeviceType(dev)) {
163 // does not increase the refcount
164 struct udev_device *parent_dev = udev_device_get_parent_with_subsystem_devtype(dev, subsystem, 0);
165 if (!parent_dev)
166 goto cleanup;
167
168 if (!checkDeviceType(parent_dev))
169 goto cleanup;
170 }
171
172 if (qstrcmp(action, "add") == 0)
173 emit deviceDetected(devNode);
174
175 if (qstrcmp(action, "remove") == 0)
176 emit deviceRemoved(devNode);
177
178cleanup:
179 udev_device_unref(dev);
180}
181
182bool QDeviceDiscoveryUDev::checkDeviceType(udev_device *dev)
183{
184 if (!dev)
185 return false;
186
187 if ((m_types & Device_Keyboard) && (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD"), "1") == 0 )) {
188 const QString capabilities_key = QString::fromUtf8(udev_device_get_sysattr_value(dev, "capabilities/key"));
189 const auto val = QStringView{capabilities_key}.split(u' ', Qt::SkipEmptyParts);
190 if (!val.isEmpty()) {
191 bool ok;
192 unsigned long long keys = val.last().toULongLong(&ok, 16);
193 if (ok) {
194 // Tests if the letter Q is valid for the device. We may want to alter this test, but it seems mostly reliable.
195 bool test = (keys >> KEY_Q) & 1;
196 if (test)
197 return true;
198 }
199 }
200 }
201
202 if ((m_types & Device_Keyboard) && (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_KEY"), "1") == 0 ))
203 return true;
204
205 if ((m_types & Device_Mouse) && (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_MOUSE"), "1") == 0))
206 return true;
207
208 if ((m_types & Device_Touchpad) && (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_TOUCHPAD"), "1") == 0))
209 return true;
210
211 if ((m_types & Device_Touchscreen) && (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_TOUCHSCREEN"), "1") == 0))
212 return true;
213
214 if ((m_types & Device_Tablet) && (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_TABLET"), "1") == 0))
215 return true;
216
217 if ((m_types & Device_Joystick) && (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK"), "1") == 0))
218 return true;
219
220 if ((m_types & Device_DRM) && (qstrcmp(udev_device_get_subsystem(dev), "drm") == 0))
221 return true;
222
223 return false;
224}
225
227
228#include "moc_qdevicediscovery_udev_p.cpp"
QStringList scanConnectedDevices() override
QDeviceDiscoveryUDev(QDeviceTypes types, struct udev *udev, QObject *parent=nullptr)
void deviceDetected(const QString &deviceNode)
void deviceRemoved(const QString &deviceNode)
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
Q_CORE_EXPORT QList< QStringView > split(QStringView sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the view into substring views wherever sep occurs, and returns the list of those string views.
Definition qstring.cpp:8249
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5455
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
QString str
[2]
Combined button and popup list for selecting options.
@ SkipEmptyParts
Definition qnamespace.h:128
Q_CORE_EXPORT int qstrcmp(const char *str1, const char *str2)
#define QT_EVDEV_DEVICE
#define QT_DRM_DEVICE
EGLDeviceEXT * devices
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
#define SLOT(a)
Definition qobjectdefs.h:52
#define SIGNAL(a)
Definition qobjectdefs.h:53
GLsizei GLenum GLenum * types
GLuint GLfloat * val
GLuint entry
#define emit
QStringList keys
view create()