1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins 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 The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qlibinputhandler_p.h"
41 #include "qlibinputpointer_p.h"
42 #include "qlibinputkeyboard_p.h"
43 #include "qlibinputtouch_p.h"
44 
45 #include <libudev.h>
46 #include <libinput.h>
47 #include <QtCore/QLoggingCategory>
48 #include <QtCore/QSocketNotifier>
49 #include <QtCore/private/qcore_unix_p.h>
50 #include <private/qguiapplication_p.h>
51 #include <private/qinputdevicemanager_p_p.h>
52 
53 QT_BEGIN_NAMESPACE
54 
55 Q_LOGGING_CATEGORY(qLcLibInput, "qt.qpa.input")
56 
liOpen(const char * path,int flags,void * user_data)57 static int liOpen(const char *path, int flags, void *user_data)
58 {
59     Q_UNUSED(user_data);
60     return qt_safe_open(path, flags);
61 }
62 
liClose(int fd,void * user_data)63 static void liClose(int fd, void *user_data)
64 {
65     Q_UNUSED(user_data);
66     qt_safe_close(fd);
67 }
68 
69 static const struct libinput_interface liInterface = {
70     liOpen,
71     liClose
72 };
73 
liLogHandler(libinput * libinput,libinput_log_priority priority,const char * format,va_list args)74 static void liLogHandler(libinput *libinput, libinput_log_priority priority, const char *format, va_list args)
75 {
76     Q_UNUSED(libinput);
77     Q_UNUSED(priority);
78 
79     char buf[512];
80     int n = vsnprintf(buf, sizeof(buf), format, args);
81     if (n > 0) {
82         if (buf[n - 1] == '\n')
83             buf[n - 1] = '\0';
84         qCDebug(qLcLibInput, "libinput: %s", buf);
85     }
86 }
87 
QLibInputHandler(const QString & key,const QString & spec)88 QLibInputHandler::QLibInputHandler(const QString &key, const QString &spec)
89 {
90     Q_UNUSED(key);
91     Q_UNUSED(spec);
92 
93     m_udev = udev_new();
94     if (Q_UNLIKELY(!m_udev))
95         qFatal("Failed to get udev context for libinput");
96 
97     m_li = libinput_udev_create_context(&liInterface, nullptr, m_udev);
98     if (Q_UNLIKELY(!m_li))
99         qFatal("Failed to get libinput context");
100 
101     libinput_log_set_handler(m_li, liLogHandler);
102     if (qLcLibInput().isDebugEnabled())
103         libinput_log_set_priority(m_li, LIBINPUT_LOG_PRIORITY_DEBUG);
104 
105     if (Q_UNLIKELY(libinput_udev_assign_seat(m_li, "seat0")))
106         qFatal("Failed to assign seat");
107 
108     m_liFd = libinput_get_fd(m_li);
109     m_notifier.reset(new QSocketNotifier(m_liFd, QSocketNotifier::Read));
110 
111     connect(m_notifier.data(), &QSocketNotifier::activated, this, &QLibInputHandler::onReadyRead);
112 
113     m_pointer.reset(new QLibInputPointer);
114     m_keyboard.reset(new QLibInputKeyboard);
115     m_touch.reset(new QLibInputTouch);
116 
117     QInputDeviceManager *manager = QGuiApplicationPrivate::inputDeviceManager();
118     connect(manager, &QInputDeviceManager::cursorPositionChangeRequested, [this](const QPoint &pos) {
119         m_pointer->setPos(pos);
120     });
121 
122     // Process the initial burst of DEVICE_ADDED events.
123     onReadyRead();
124 }
125 
~QLibInputHandler()126 QLibInputHandler::~QLibInputHandler()
127 {
128     if (m_li)
129         libinput_unref(m_li);
130 
131     if (m_udev)
132         udev_unref(m_udev);
133 }
134 
onReadyRead()135 void QLibInputHandler::onReadyRead()
136 {
137     if (libinput_dispatch(m_li)) {
138         qWarning("libinput_dispatch failed");
139         return;
140     }
141 
142     libinput_event *ev;
143     while ((ev = libinput_get_event(m_li)) != nullptr) {
144         processEvent(ev);
145         libinput_event_destroy(ev);
146     }
147 }
148 
processEvent(libinput_event * ev)149 void QLibInputHandler::processEvent(libinput_event *ev)
150 {
151     libinput_event_type type = libinput_event_get_type(ev);
152     libinput_device *dev = libinput_event_get_device(ev);
153 
154     switch (type) {
155     case LIBINPUT_EVENT_DEVICE_ADDED:
156     {
157         // This is not just for hotplugging, it is also called for each input
158         // device libinput reads from on startup. Hence it is suitable for doing
159         // touch device registration.
160         QInputDeviceManagerPrivate *inputManagerPriv = QInputDeviceManagerPrivate::get(
161             QGuiApplicationPrivate::inputDeviceManager());
162         if (libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TOUCH)) {
163             m_touch->registerDevice(dev);
164             int &count(m_devCount[QInputDeviceManager::DeviceTypeTouch]);
165             ++count;
166             inputManagerPriv->setDeviceCount(QInputDeviceManager::DeviceTypeTouch, count);
167         }
168         if (libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_POINTER)) {
169             int &count(m_devCount[QInputDeviceManager::DeviceTypePointer]);
170             ++count;
171             inputManagerPriv->setDeviceCount(QInputDeviceManager::DeviceTypePointer, count);
172         }
173         if (libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_KEYBOARD)) {
174             int &count(m_devCount[QInputDeviceManager::DeviceTypeKeyboard]);
175             ++count;
176             inputManagerPriv->setDeviceCount(QInputDeviceManager::DeviceTypeKeyboard, count);
177         }
178         break;
179     }
180     case LIBINPUT_EVENT_DEVICE_REMOVED:
181     {
182         QInputDeviceManagerPrivate *inputManagerPriv = QInputDeviceManagerPrivate::get(
183             QGuiApplicationPrivate::inputDeviceManager());
184         if (libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_TOUCH)) {
185             m_touch->unregisterDevice(dev);
186             int &count(m_devCount[QInputDeviceManager::DeviceTypeTouch]);
187             --count;
188             inputManagerPriv->setDeviceCount(QInputDeviceManager::DeviceTypeTouch, count);
189         }
190         if (libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_POINTER)) {
191             int &count(m_devCount[QInputDeviceManager::DeviceTypePointer]);
192             --count;
193             inputManagerPriv->setDeviceCount(QInputDeviceManager::DeviceTypePointer, count);
194         }
195         if (libinput_device_has_capability(dev, LIBINPUT_DEVICE_CAP_KEYBOARD)) {
196             int &count(m_devCount[QInputDeviceManager::DeviceTypeKeyboard]);
197             --count;
198             inputManagerPriv->setDeviceCount(QInputDeviceManager::DeviceTypeKeyboard, count);
199         }
200         break;
201     }
202     case LIBINPUT_EVENT_POINTER_BUTTON:
203         m_pointer->processButton(libinput_event_get_pointer_event(ev));
204         break;
205     case LIBINPUT_EVENT_POINTER_MOTION:
206         m_pointer->processMotion(libinput_event_get_pointer_event(ev));
207         break;
208     case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
209         m_pointer->processAbsMotion(libinput_event_get_pointer_event(ev));
210         break;
211     case LIBINPUT_EVENT_POINTER_AXIS:
212         m_pointer->processAxis(libinput_event_get_pointer_event(ev));
213         break;
214     case LIBINPUT_EVENT_KEYBOARD_KEY:
215         m_keyboard->processKey(libinput_event_get_keyboard_event(ev));
216         break;
217     case LIBINPUT_EVENT_TOUCH_DOWN:
218         m_touch->processTouchDown(libinput_event_get_touch_event(ev));
219         break;
220     case LIBINPUT_EVENT_TOUCH_MOTION:
221         m_touch->processTouchMotion(libinput_event_get_touch_event(ev));
222         break;
223     case LIBINPUT_EVENT_TOUCH_UP:
224         m_touch->processTouchUp(libinput_event_get_touch_event(ev));
225         break;
226     case LIBINPUT_EVENT_TOUCH_CANCEL:
227         m_touch->processTouchCancel(libinput_event_get_touch_event(ev));
228         break;
229     case LIBINPUT_EVENT_TOUCH_FRAME:
230         m_touch->processTouchFrame(libinput_event_get_touch_event(ev));
231         break;
232     default:
233         break;
234     }
235 }
236 
237 QT_END_NAMESPACE
238