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 <QDebug>
48 #include <QSocketNotifier>
49 #include <QTimer>
50 #include <QDir>
51 
52 QInputDeviceManagerPrivate::QInputDeviceManagerPrivate(QObject *parent) :
53  QObject(parent),
54  currentFilter(QInputDevice::Unknown),
55  udevice(0)
56 {
57  QTimer::singleShot(250,this,SLOT(init()));
58 }
59 
60 QInputDeviceManagerPrivate::~QInputDeviceManagerPrivate()
61 {
62  udev_unref(udevice);
63  udev_monitor_unref(udevMonitor);
64 }
65 
66 void QInputDeviceManagerPrivate::init()
67 {
68  if (!udevice)
69  udevice = udev_new();
70 
71  udev_list_entry *devices;
72  udev_list_entry *dev_list_entry;
73  udev_device *dev;
74 
75  QString subsystem = QStringLiteral("input");
76  struct udev_enumerate *enumerate = 0;
77 
78  if (udevice) {
79 
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());
84 
85  udev_monitor_enable_receiving(udevMonitor);
86  notifierFd = udev_monitor_get_fd(udevMonitor);
87 
88  notifier = new QSocketNotifier(notifierFd, QSocketNotifier::Read, this);
89  connect(notifier, SIGNAL(activated(int)), this, SLOT(onUDevChanges()));
90 
91  udev_enumerate_scan_devices(enumerate);
92  devices = udev_enumerate_get_list_entry(enumerate);
93 
94  udev_list_entry_foreach(dev_list_entry, devices) {
95  const char *path;
96  path = udev_list_entry_get_name(dev_list_entry);
97 
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();
114 }
115 
116 QInputDevice::InputTypeFlags QInputDeviceManagerPrivate::getInputTypeFlags(struct udev_device *dev)
117 {
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  }
138 
139  return flags;
140 }
141 
142 QInputDevice *QInputDeviceManagerPrivate::addDevice(struct udev_device *udev)
143 {
144  QString eventPath = QString::fromLatin1(udev_device_get_sysname(udev));
145 
146  if (eventPath.contains(QStringLiteral("event")))
147  eventPath.prepend(QStringLiteral("/dev/input/"));
148 
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();
161 
162  qDebug() << "Input device added:" << inputDevice->name() << inputDevice->devicePath() << inputDevice->type();
163 
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  }
173 
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  }
197 
198  return inputDevice;
199 }
200 
201 void QInputDeviceManagerPrivate::addDetails(struct udev_device *)
202 {
203 }
204 
205 void QInputDeviceManagerPrivate::removeDevice(const QString &path)
206 {
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  }
215 }
216 
217 QInputDevice *QInputDeviceManagerPrivate::addUdevDevice(struct udev_device *udev)
218 {
219  QInputDevice *iDevice;
220 
221  struct udev_list_entry *list;
222  struct udev_list_entry *node;
223 
224  list = udev_device_get_properties_list_entry (udev);
225  QString syspath = QString::fromLatin1(udev_device_get_syspath(udev));
226  QDir sysdir(syspath);
227 
228  QStringList infoList = sysdir.entryList(QStringList() << QStringLiteral("event*"),QDir::Dirs);
229 
230  if (infoList.count() > 0) {
231  QString token = infoList.at(0);
232 
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) {
240 
241  QString key = QString::fromLatin1(udev_list_entry_get_name(node));
242  QString value = QString::fromLatin1(udev_list_entry_get_value(node));
243 
244  if (key == QStringLiteral("NAME")) {
245  iDevice->setName(value.remove(QStringLiteral("\"")));
246  }
247  }
248  iDevice->setType(getInputTypeFlags(udev));
249  return iDevice;
250 }
251 
252 void QInputDeviceManagerPrivate::onUDevChanges()
253 {
254  if (!udevMonitor)
255  return;
256 
257  udev_device *dev = udev_monitor_receive_device(udevMonitor);
258 
259  if (dev) {
260  if (qstrcmp(udev_device_get_subsystem(dev), "input") == 0 ) {
261  QString eventPath = QString::fromLatin1(udev_device_get_sysname(dev));
262 
263  QString action = QString::fromStdString(udev_device_get_action(dev));
264 
265  if (!eventPath.contains(QStringLiteral("/dev/input/")))
266  eventPath.prepend(QStringLiteral("/dev/input/"));
267 
268  if (action == QStringLiteral("add")) {
269  if (deviceMap.contains(eventPath)){
270  udev_device_unref(dev);
271  return;
272  }
273 
274  QInputDevice *iDevice = addDevice(dev);
275  if (!iDevice) {
276  delete iDevice;
277  return;
278  }
279  iDevice->setType(getInputTypeFlags(dev));
280  udev_device_unref(dev);
281 
282  deviceMap.insert(eventPath,iDevice);
283 
284  Q_EMIT deviceAdded(eventPath);
285 
286  } else if (action == QStringLiteral("remove")) {
287  removeDevice(eventPath);
288  }
289  }
290  }
291 }