on e804c19 help sign in
unity8/plugins/Unity/InputInfo/linux/qinputdeviceinfo_linux.cpp
291 lines 10 kB history raw
1/****************************************************************************
2**
3** Copyright (C) 2014 Canonical, Ltd. and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the QtSystems module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Digia. For licensing terms and
14** conditions see http://qt.digia.com/licensing. For further information
15** use the contact form at http://qt.digia.com/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Digia gives you certain additional
26** rights. These rights are described in the Digia Qt LGPL Exception
27** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28**
29** GNU General Public License Usage
30** Alternatively, this file may be used under the terms of the GNU
31** General Public License version 3.0 as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL included in the
33** packaging of this file. Please review the following information to
34** ensure the GNU General Public License version 3.0 requirements will be
35** met: http://www.gnu.org/copyleft/gpl.html.
36**
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
42#include "qinputdeviceinfo_linux_p.h"
44#include <libudev.h>
45#include <libevdev/libevdev.h>
46#include <fcntl.h>
47#include <QDebug>
48#include <QSocketNotifier>
49#include <QTimer>
50#include <QDir>
52QInputDeviceManagerPrivate::QInputDeviceManagerPrivate(QObject *parent) :
53 QObject(parent),
54 currentFilter(QInputDevice::Unknown),
55 udevice(0)
57 QTimer::singleShot(250,this,SLOT(init()));
60QInputDeviceManagerPrivate::~QInputDeviceManagerPrivate()
62 udev_unref(udevice);
63 udev_monitor_unref(udevMonitor);
66void QInputDeviceManagerPrivate::init()
68 if (!udevice)
69 udevice = udev_new();
71 udev_list_entry *devices;
72 udev_list_entry *dev_list_entry;
73 udev_device *dev;
75 QString subsystem = QStringLiteral("input");
76 struct udev_enumerate *enumerate = 0;
78 if (udevice) {
80 udevMonitor = udev_monitor_new_from_netlink(udevice, "udev");
81 udev_monitor_filter_add_match_subsystem_devtype(udevMonitor, subsystem.toLatin1(), NULL);
82 enumerate = udev_enumerate_new(udevice);
83 udev_enumerate_add_match_subsystem(enumerate, subsystem.toLatin1());
85 udev_monitor_enable_receiving(udevMonitor);
86 notifierFd = udev_monitor_get_fd(udevMonitor);
88 notifier = new QSocketNotifier(notifierFd, QSocketNotifier::Read, this);
89 connect(notifier, SIGNAL(activated(int)), this, SLOT(onUDevChanges()));
91 udev_enumerate_scan_devices(enumerate);
92 devices = udev_enumerate_get_list_entry(enumerate);
94 udev_list_entry_foreach(dev_list_entry, devices) {
95 const char *path;
96 path = udev_list_entry_get_name(dev_list_entry);
98 dev = udev_device_new_from_syspath(udevice, path);
99 if (qstrcmp(udev_device_get_subsystem(dev), "input") == 0 ) {
100 QInputDevice *iDevice = addDevice(dev);
101 if (iDevice && !iDevice->devicePath().isEmpty()) {
102 deviceMap.insert(iDevice->devicePath(),iDevice);
103 }
104 }
105 udev_device_unref(dev);
106 }
107 udev_enumerate_unref(enumerate);
108 }
109 // udev_unref(udevice);
110 Q_FOREACH (const QString &devicePath, deviceMap.keys()) {
111 Q_EMIT deviceAdded(devicePath);
112 }
113 Q_EMIT ready();
116QInputDevice::InputTypeFlags QInputDeviceManagerPrivate::getInputTypeFlags(struct udev_device *dev)
118 QInputDevice::InputTypeFlags flags = QInputDevice::Unknown;
119 if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_KEY"), "1") == 0 ) {
120 flags |= QInputDevice::Button;
121 }
122 if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_MOUSE"), "1") == 0) {
123 flags |= QInputDevice::Mouse;
124 }
125 if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_TOUCHPAD"), "1") == 0) {
126 flags |= QInputDevice::TouchPad;
127 }
128 if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_TOUCHSCREEN"), "1") == 0
129 || qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_TABLET"), "1") == 0) {
130 flags |= QInputDevice::TouchScreen;
131 }
132 if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD"), "1") == 0 ) {
133 flags |= QInputDevice::Keyboard;
134 }
135 if (!QString::fromLatin1(udev_device_get_property_value(dev, "SW")).isEmpty()) {
136 flags |= QInputDevice::Switch;
137 }
139 return flags;
142QInputDevice *QInputDeviceManagerPrivate::addDevice(struct udev_device *udev)
144 QString eventPath = QString::fromLatin1(udev_device_get_sysname(udev));
146 if (eventPath.contains(QStringLiteral("event")))
147 eventPath.prepend(QStringLiteral("/dev/input/"));
149 if (deviceMap.contains(eventPath)) {
150 return Q_NULLPTR;
151 }
152 struct libevdev *dev = NULL;
153 int fd;
154 int rc = 1;
155 QInputDevice *inputDevice;
156 inputDevice = addUdevDevice(udev);
157 if (!inputDevice) {
158 return Q_NULLPTR;
159 }
160 eventPath = inputDevice->devicePath();
162 qDebug() << "Input device added:" << inputDevice->name() << inputDevice->devicePath() << inputDevice->type();
164 fd = open(eventPath.toLatin1(), O_RDONLY|O_NONBLOCK);
165 if (fd == -1) {
166 return inputDevice;
167 }
168 rc = libevdev_new_from_fd(fd, &dev);
169 if (rc < 0) {
170 qWarning() << "Failed to init libevdev ("<< strerror(-rc) << ")";
171 return Q_NULLPTR;
172 }
174 for (int i = 0; i < EV_MAX; i++) {
175 if (i == EV_KEY || i == EV_SW || i == EV_REL
176 || i == EV_REL || i == EV_ABS) {
177 for (int j = 0; j < libevdev_event_type_get_max(i); j++) {
178 if (libevdev_has_event_code(dev, i, j)) {
179 switch (i) {
180 case EV_KEY:
181 inputDevice->addButton(j);
182 break;
183 case EV_SW:
184 inputDevice->addSwitch(j);
185 break;
186 case EV_REL:
187 inputDevice->addRelativeAxis(j);
188 break;
189 case EV_ABS:
190 inputDevice->addAbsoluteAxis(j);
191 break;
192 };
193 }
194 }
195 }
196 }
198 return inputDevice;
201void QInputDeviceManagerPrivate::addDetails(struct udev_device *)
205void QInputDeviceManagerPrivate::removeDevice(const QString &path)
207 // this path is not a full evdev path
208 Q_FOREACH (const QString devicePath, deviceMap.keys()) {
209 if (devicePath.contains(path)) {
210 qDebug() << "Input device removed:" << deviceMap.value(devicePath)->name() << devicePath << deviceMap.value(devicePath)->type();
211 deviceMap.remove(devicePath);
212 Q_EMIT deviceRemoved(devicePath);
213 }
214 }
217QInputDevice *QInputDeviceManagerPrivate::addUdevDevice(struct udev_device *udev)
219 QInputDevice *iDevice;
221 struct udev_list_entry *list;
222 struct udev_list_entry *node;
224 list = udev_device_get_properties_list_entry (udev);
225 QString syspath = QString::fromLatin1(udev_device_get_syspath(udev));
226 QDir sysdir(syspath);
228 QStringList infoList = sysdir.entryList(QStringList() << QStringLiteral("event*"),QDir::Dirs);
230 if (infoList.count() > 0) {
231 QString token = infoList.at(0);
233 token.prepend(QStringLiteral("/dev/input/"));
234 iDevice = new QInputDevice(this);
235 iDevice->setDevicePath(token);
236 } else {
237 return Q_NULLPTR;
238 }
239 udev_list_entry_foreach (node, list) {
241 QString key = QString::fromLatin1(udev_list_entry_get_name(node));
242 QString value = QString::fromLatin1(udev_list_entry_get_value(node));
244 if (key == QStringLiteral("NAME")) {
245 iDevice->setName(value.remove(QStringLiteral("\"")));
246 }
247 }
248 iDevice->setType(getInputTypeFlags(udev));
249 return iDevice;
252void QInputDeviceManagerPrivate::onUDevChanges()
254 if (!udevMonitor)
255 return;
257 udev_device *dev = udev_monitor_receive_device(udevMonitor);
259 if (dev) {
260 if (qstrcmp(udev_device_get_subsystem(dev), "input") == 0 ) {
261 QString eventPath = QString::fromLatin1(udev_device_get_sysname(dev));
263 QString action = QString::fromStdString(udev_device_get_action(dev));
265 if (!eventPath.contains(QStringLiteral("/dev/input/")))
266 eventPath.prepend(QStringLiteral("/dev/input/"));
268 if (action == QStringLiteral("add")) {
269 if (deviceMap.contains(eventPath)){
270 udev_device_unref(dev);
271 return;
272 }
274 QInputDevice *iDevice = addDevice(dev);
275 if (!iDevice) {
276 delete iDevice;
277 return;
278 }
279 iDevice->setType(getInputTypeFlags(dev));
280 udev_device_unref(dev);
282 deviceMap.insert(eventPath,iDevice);
284 Q_EMIT deviceAdded(eventPath);
286 } else if (action == QStringLiteral("remove")) {
287 removeDevice(eventPath);
288 }
289 }
290 }