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 | |
57 | static int liOpen(const char *path, int flags, void *user_data) |
58 | { |
59 | Q_UNUSED(user_data); |
60 | return qt_safe_open(pathname: path, flags); |
61 | } |
62 | |
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 | .open_restricted: liOpen, |
71 | .close_restricted: liClose |
72 | }; |
73 | |
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(s: buf, maxlen: sizeof(buf), format: format, arg: 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 | |
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(msg: "Failed to get udev context for libinput" ); |
96 | |
97 | m_li = libinput_udev_create_context(interface: &liInterface, user_data: nullptr, udev: m_udev); |
98 | if (Q_UNLIKELY(!m_li)) |
99 | qFatal(msg: "Failed to get libinput context" ); |
100 | |
101 | libinput_log_set_handler(libinput: m_li, log_handler: liLogHandler); |
102 | if (qLcLibInput().isDebugEnabled()) |
103 | libinput_log_set_priority(libinput: m_li, priority: LIBINPUT_LOG_PRIORITY_DEBUG); |
104 | |
105 | if (Q_UNLIKELY(libinput_udev_assign_seat(m_li, "seat0" ))) |
106 | qFatal(msg: "Failed to assign seat" ); |
107 | |
108 | m_liFd = libinput_get_fd(libinput: m_li); |
109 | m_notifier.reset(other: new QSocketNotifier(m_liFd, QSocketNotifier::Read)); |
110 | |
111 | connect(sender: m_notifier.data(), signal: &QSocketNotifier::activated, receiver: this, slot: &QLibInputHandler::onReadyRead); |
112 | |
113 | m_pointer.reset(other: new QLibInputPointer); |
114 | m_keyboard.reset(other: new QLibInputKeyboard); |
115 | m_touch.reset(other: new QLibInputTouch); |
116 | |
117 | QInputDeviceManager *manager = QGuiApplicationPrivate::inputDeviceManager(); |
118 | connect(sender: manager, signal: &QInputDeviceManager::cursorPositionChangeRequested, slot: [this](const QPoint &pos) { |
119 | m_pointer->setPos(pos); |
120 | }); |
121 | |
122 | // Process the initial burst of DEVICE_ADDED events. |
123 | onReadyRead(); |
124 | } |
125 | |
126 | QLibInputHandler::~QLibInputHandler() |
127 | { |
128 | if (m_li) |
129 | libinput_unref(libinput: m_li); |
130 | |
131 | if (m_udev) |
132 | udev_unref(udev: m_udev); |
133 | } |
134 | |
135 | void QLibInputHandler::onReadyRead() |
136 | { |
137 | if (libinput_dispatch(libinput: m_li)) { |
138 | qWarning(msg: "libinput_dispatch failed" ); |
139 | return; |
140 | } |
141 | |
142 | libinput_event *ev; |
143 | while ((ev = libinput_get_event(libinput: m_li)) != nullptr) { |
144 | processEvent(ev); |
145 | libinput_event_destroy(event: ev); |
146 | } |
147 | } |
148 | |
149 | void QLibInputHandler::processEvent(libinput_event *ev) |
150 | { |
151 | libinput_event_type type = libinput_event_get_type(event: ev); |
152 | libinput_device *dev = libinput_event_get_device(event: 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 | mgr: QGuiApplicationPrivate::inputDeviceManager()); |
162 | if (libinput_device_has_capability(device: dev, capability: LIBINPUT_DEVICE_CAP_TOUCH)) { |
163 | m_touch->registerDevice(dev); |
164 | int &count(m_devCount[QInputDeviceManager::DeviceTypeTouch]); |
165 | ++count; |
166 | inputManagerPriv->setDeviceCount(type: QInputDeviceManager::DeviceTypeTouch, count); |
167 | } |
168 | if (libinput_device_has_capability(device: dev, capability: LIBINPUT_DEVICE_CAP_POINTER)) { |
169 | int &count(m_devCount[QInputDeviceManager::DeviceTypePointer]); |
170 | ++count; |
171 | inputManagerPriv->setDeviceCount(type: QInputDeviceManager::DeviceTypePointer, count); |
172 | } |
173 | if (libinput_device_has_capability(device: dev, capability: LIBINPUT_DEVICE_CAP_KEYBOARD)) { |
174 | int &count(m_devCount[QInputDeviceManager::DeviceTypeKeyboard]); |
175 | ++count; |
176 | inputManagerPriv->setDeviceCount(type: QInputDeviceManager::DeviceTypeKeyboard, count); |
177 | } |
178 | break; |
179 | } |
180 | case LIBINPUT_EVENT_DEVICE_REMOVED: |
181 | { |
182 | QInputDeviceManagerPrivate *inputManagerPriv = QInputDeviceManagerPrivate::get( |
183 | mgr: QGuiApplicationPrivate::inputDeviceManager()); |
184 | if (libinput_device_has_capability(device: dev, capability: LIBINPUT_DEVICE_CAP_TOUCH)) { |
185 | m_touch->unregisterDevice(dev); |
186 | int &count(m_devCount[QInputDeviceManager::DeviceTypeTouch]); |
187 | --count; |
188 | inputManagerPriv->setDeviceCount(type: QInputDeviceManager::DeviceTypeTouch, count); |
189 | } |
190 | if (libinput_device_has_capability(device: dev, capability: LIBINPUT_DEVICE_CAP_POINTER)) { |
191 | int &count(m_devCount[QInputDeviceManager::DeviceTypePointer]); |
192 | --count; |
193 | inputManagerPriv->setDeviceCount(type: QInputDeviceManager::DeviceTypePointer, count); |
194 | } |
195 | if (libinput_device_has_capability(device: dev, capability: LIBINPUT_DEVICE_CAP_KEYBOARD)) { |
196 | int &count(m_devCount[QInputDeviceManager::DeviceTypeKeyboard]); |
197 | --count; |
198 | inputManagerPriv->setDeviceCount(type: QInputDeviceManager::DeviceTypeKeyboard, count); |
199 | } |
200 | break; |
201 | } |
202 | case LIBINPUT_EVENT_POINTER_BUTTON: |
203 | m_pointer->processButton(e: libinput_event_get_pointer_event(event: ev)); |
204 | break; |
205 | case LIBINPUT_EVENT_POINTER_MOTION: |
206 | m_pointer->processMotion(e: libinput_event_get_pointer_event(event: ev)); |
207 | break; |
208 | case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: |
209 | m_pointer->processAbsMotion(e: libinput_event_get_pointer_event(event: ev)); |
210 | break; |
211 | case LIBINPUT_EVENT_POINTER_AXIS: |
212 | m_pointer->processAxis(e: libinput_event_get_pointer_event(event: ev)); |
213 | break; |
214 | case LIBINPUT_EVENT_KEYBOARD_KEY: |
215 | m_keyboard->processKey(e: libinput_event_get_keyboard_event(event: ev)); |
216 | break; |
217 | case LIBINPUT_EVENT_TOUCH_DOWN: |
218 | m_touch->processTouchDown(e: libinput_event_get_touch_event(event: ev)); |
219 | break; |
220 | case LIBINPUT_EVENT_TOUCH_MOTION: |
221 | m_touch->processTouchMotion(e: libinput_event_get_touch_event(event: ev)); |
222 | break; |
223 | case LIBINPUT_EVENT_TOUCH_UP: |
224 | m_touch->processTouchUp(e: libinput_event_get_touch_event(event: ev)); |
225 | break; |
226 | case LIBINPUT_EVENT_TOUCH_CANCEL: |
227 | m_touch->processTouchCancel(e: libinput_event_get_touch_event(event: ev)); |
228 | break; |
229 | case LIBINPUT_EVENT_TOUCH_FRAME: |
230 | m_touch->processTouchFrame(e: libinput_event_get_touch_event(event: ev)); |
231 | break; |
232 | default: |
233 | break; |
234 | } |
235 | } |
236 | |
237 | QT_END_NAMESPACE |
238 | |