Unity 8
qinputdeviceinfo_linux.cpp
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 ****************************************************************************/
41 
42 #include "qinputdeviceinfo_linux_p.h"
43 
44 #include <libudev.h>
45 #include <libevdev/libevdev.h>
46 #include <fcntl.h>
47 #include <unistd.h>
48 #include <QDebug>
49 #include <QSocketNotifier>
50 #include <QTimer>
51 #include <QDir>
52 
53 QInputDeviceManagerPrivate::QInputDeviceManagerPrivate(QObject *parent) :
54  QObject(parent),
55  currentFilter(QInputDevice::Unknown),
56  udevMonitor(0),
57  udevice(0)
58 {
59  QTimer::singleShot(250,this,SLOT(init()));
60 }
61 
62 QInputDeviceManagerPrivate::~QInputDeviceManagerPrivate()
63 {
64  udev_unref(udevice);
65  udev_monitor_unref(udevMonitor);
66 }
67 
68 void QInputDeviceManagerPrivate::init()
69 {
70  if (!udevice)
71  udevice = udev_new();
72 
73  udev_list_entry *devices;
74  udev_list_entry *dev_list_entry;
75  udev_device *dev;
76 
77  QString subsystem = QStringLiteral("input");
78  struct udev_enumerate *enumerate = 0;
79 
80  if (udevice) {
81 
82  udevMonitor = udev_monitor_new_from_netlink(udevice, "udev");
83  udev_monitor_filter_add_match_subsystem_devtype(udevMonitor, subsystem.toLatin1(), NULL);
84  enumerate = udev_enumerate_new(udevice);
85  udev_enumerate_add_match_subsystem(enumerate, subsystem.toLatin1());
86 
87  udev_monitor_enable_receiving(udevMonitor);
88  int notifierFd = udev_monitor_get_fd(udevMonitor);
89 
90  QSocketNotifier *notifier = new QSocketNotifier(notifierFd, QSocketNotifier::Read, this);
91  connect(notifier, SIGNAL(activated(int)), this, SLOT(onUDevChanges()));
92 
93  udev_enumerate_scan_devices(enumerate);
94  devices = udev_enumerate_get_list_entry(enumerate);
95 
96  udev_list_entry_foreach(dev_list_entry, devices) {
97  const char *path;
98  path = udev_list_entry_get_name(dev_list_entry);
99 
100  dev = udev_device_new_from_syspath(udevice, path);
101  if (qstrcmp(udev_device_get_subsystem(dev), "input") == 0 ) {
102  QInputDevice *iDevice = addDevice(dev);
103  if (iDevice && !iDevice->devicePath().isEmpty()) {
104  deviceMap.insert(iDevice->devicePath(),iDevice);
105  }
106  }
107  udev_device_unref(dev);
108  }
109  udev_enumerate_unref(enumerate);
110  }
111  // udev_unref(udevice);
112  Q_FOREACH (const QString &devicePath, deviceMap.keys()) {
113  Q_EMIT deviceAdded(devicePath);
114  }
115  Q_EMIT ready();
116 }
117 
118 QInputDevice::InputTypeFlags QInputDeviceManagerPrivate::getInputTypeFlags(struct udev_device *dev)
119 {
120  QInputDevice::InputTypeFlags flags = QInputDevice::Unknown;
121  if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_KEY"), "1") == 0 ) {
122  flags |= QInputDevice::Button;
123  }
124  if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_MOUSE"), "1") == 0) {
125  flags |= QInputDevice::Mouse;
126  }
127  if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_TOUCHPAD"), "1") == 0) {
128  flags |= QInputDevice::TouchPad;
129  }
130  if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_TOUCHSCREEN"), "1") == 0
131  || qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_TABLET"), "1") == 0) {
132  flags |= QInputDevice::TouchScreen;
133  }
134  if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD"), "1") == 0 ) {
135  flags |= QInputDevice::Keyboard;
136  }
137  if (!QString::fromLatin1(udev_device_get_property_value(dev, "SW")).isEmpty()) {
138  flags |= QInputDevice::Switch;
139  }
140 
141  return flags;
142 }
143 
144 QInputDevice *QInputDeviceManagerPrivate::addDevice(struct udev_device *udev)
145 {
146  QString eventPath = QString::fromLatin1(udev_device_get_sysname(udev));
147 
148  if (eventPath.contains(QStringLiteral("event")))
149  eventPath.prepend(QStringLiteral("/dev/input/"));
150 
151  if (deviceMap.contains(eventPath)) {
152  return Q_NULLPTR;
153  }
154  struct libevdev *dev = NULL;
155  int fd;
156  int rc = 1;
157  QInputDevice *inputDevice;
158  inputDevice = addUdevDevice(udev);
159  if (!inputDevice) {
160  return Q_NULLPTR;
161  }
162  eventPath = inputDevice->devicePath();
163 
164  qDebug() << "Input device added:" << inputDevice->name() << inputDevice->devicePath() << inputDevice->type();
165 
166  fd = open(eventPath.toLatin1(), O_RDONLY|O_NONBLOCK);
167  if (fd == -1) {
168  return inputDevice;
169  }
170  rc = libevdev_new_from_fd(fd, &dev);
171  if (rc < 0) {
172  qWarning() << "Failed to init libevdev ("<< strerror(-rc) << ")";
173  close(fd);
174  return Q_NULLPTR;
175  }
176 
177  for (int i = 0; i < EV_MAX; i++) {
178  if (i == EV_KEY || i == EV_SW || i == EV_REL
179  || i == EV_REL || i == EV_ABS) {
180  for (int j = 0; j < libevdev_event_type_get_max(i); j++) {
181  if (libevdev_has_event_code(dev, i, j)) {
182  switch (i) {
183  case EV_KEY:
184  inputDevice->addButton(j);
185  break;
186  case EV_SW:
187  inputDevice->addSwitch(j);
188  break;
189  case EV_REL:
190  inputDevice->addRelativeAxis(j);
191  break;
192  case EV_ABS:
193  inputDevice->addAbsoluteAxis(j);
194  break;
195  };
196  }
197  }
198  }
199  }
200 
201  libevdev_free(dev);
202  close(fd);
203  return inputDevice;
204 }
205 
206 void QInputDeviceManagerPrivate::addDetails(struct udev_device *)
207 {
208 }
209 
210 void QInputDeviceManagerPrivate::removeDevice(const QString &path)
211 {
212  // this path is not a full evdev path
213  Q_FOREACH (const QString devicePath, deviceMap.keys()) {
214  if (devicePath.contains(path)) {
215  qDebug() << "Input device removed:" << deviceMap.value(devicePath)->name() << devicePath << deviceMap.value(devicePath)->type();
216  deviceMap.remove(devicePath);
217  Q_EMIT deviceRemoved(devicePath);
218  }
219  }
220 }
221 
222 QInputDevice *QInputDeviceManagerPrivate::addUdevDevice(struct udev_device *udev)
223 {
224  QInputDevice *iDevice;
225 
226  struct udev_list_entry *list;
227  struct udev_list_entry *node;
228 
229  list = udev_device_get_properties_list_entry (udev);
230  QString syspath = QString::fromLatin1(udev_device_get_syspath(udev));
231  QDir sysdir(syspath);
232 
233  QStringList infoList = sysdir.entryList(QStringList() << QStringLiteral("event*"),QDir::Dirs);
234 
235  if (infoList.count() > 0) {
236  QString token = infoList.at(0);
237 
238  token.prepend(QStringLiteral("/dev/input/"));
239  iDevice = new QInputDevice(this);
240  iDevice->setDevicePath(token);
241  } else {
242  return Q_NULLPTR;
243  }
244  udev_list_entry_foreach (node, list) {
245 
246  QString key = QString::fromLatin1(udev_list_entry_get_name(node));
247  QString value = QString::fromLatin1(udev_list_entry_get_value(node));
248 
249  if (key == QStringLiteral("NAME")) {
250  iDevice->setName(value.remove(QStringLiteral("\"")));
251  }
252  }
253  iDevice->setType(getInputTypeFlags(udev));
254  return iDevice;
255 }
256 
257 void QInputDeviceManagerPrivate::onUDevChanges()
258 {
259  if (!udevMonitor)
260  return;
261 
262  udev_device *dev = udev_monitor_receive_device(udevMonitor);
263 
264  if (dev) {
265  if (qstrcmp(udev_device_get_subsystem(dev), "input") == 0 ) {
266  QString eventPath = QString::fromLatin1(udev_device_get_sysname(dev));
267 
268  QString action = QString::fromStdString(udev_device_get_action(dev));
269 
270  if (!eventPath.contains(QStringLiteral("/dev/input/")))
271  eventPath.prepend(QStringLiteral("/dev/input/"));
272 
273  if (action == QStringLiteral("add")) {
274  if (deviceMap.contains(eventPath)){
275  udev_device_unref(dev);
276  return;
277  }
278 
279  QInputDevice *iDevice = addDevice(dev);
280  if (!iDevice) {
281  delete iDevice;
282  return;
283  }
284  iDevice->setType(getInputTypeFlags(dev));
285  udev_device_unref(dev);
286 
287  deviceMap.insert(eventPath,iDevice);
288 
289  Q_EMIT deviceAdded(eventPath);
290 
291  } else if (action == QStringLiteral("remove")) {
292  removeDevice(eventPath);
293  }
294  }
295  }
296 }