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
53QT_BEGIN_NAMESPACE
54
55Q_LOGGING_CATEGORY(qLcLibInput, "qt.qpa.input")
56
57static 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
63static void liClose(int fd, void *user_data)
64{
65 Q_UNUSED(user_data);
66 qt_safe_close(fd);
67}
68
69static const struct libinput_interface liInterface = {
70 liOpen,
71 liClose
72};
73
74static 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
88QLibInputHandler::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
126QLibInputHandler::~QLibInputHandler()
127{
128 if (m_li)
129 libinput_unref(m_li);
130
131 if (m_udev)
132 udev_unref(m_udev);
133}
134
135void 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
149void 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
237QT_END_NAMESPACE
238