1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qlibinputhandler_p.h"
5#include "qlibinputpointer_p.h"
6#include "qlibinputkeyboard_p.h"
7#include "qlibinputtouch_p.h"
8
9#include <libudev.h>
10#include <libinput.h>
11#include <QtCore/QLoggingCategory>
12#include <QtCore/QSocketNotifier>
13#include <QtCore/private/qcore_unix_p.h>
14#include <private/qguiapplication_p.h>
15#include <private/qinputdevicemanager_p_p.h>
16
17QT_BEGIN_NAMESPACE
18
19Q_LOGGING_CATEGORY(qLcLibInput, "qt.qpa.input")
20
21static int liOpen(const char *path, int flags, void *user_data)
22{
23 Q_UNUSED(user_data);
24 return qt_safe_open(pathname: path, flags);
25}
26
27static void liClose(int fd, void *user_data)
28{
29 Q_UNUSED(user_data);
30 qt_safe_close(fd);
31}
32
33static const struct libinput_interface liInterface = {
34 .open_restricted: liOpen,
35 .close_restricted: liClose
36};
37
38static void liLogHandler(libinput *libinput, libinput_log_priority priority, const char *format, va_list args)
39{
40 Q_UNUSED(libinput);
41 Q_UNUSED(priority);
42
43 char buf[512];
44 int n = vsnprintf(s: buf, maxlen: sizeof(buf), format: format, arg: args);
45 if (n > 0) {
46 if (buf[n - 1] == '\n')
47 buf[n - 1] = '\0';
48 qCDebug(qLcLibInput, "libinput: %s", buf);
49 }
50}
51
52QLibInputHandler::QLibInputHandler(const QString &key, const QString &spec)
53{
54 Q_UNUSED(key);
55 Q_UNUSED(spec);
56
57 m_udev = udev_new();
58 if (Q_UNLIKELY(!m_udev))
59 qFatal(msg: "Failed to get udev context for libinput");
60
61 m_li = libinput_udev_create_context(interface: &liInterface, user_data: nullptr, udev: m_udev);
62 if (Q_UNLIKELY(!m_li))
63 qFatal(msg: "Failed to get libinput context");
64
65 libinput_log_set_handler(libinput: m_li, log_handler: liLogHandler);
66 if (qLcLibInput().isDebugEnabled())
67 libinput_log_set_priority(libinput: m_li, priority: LIBINPUT_LOG_PRIORITY_DEBUG);
68
69 if (Q_UNLIKELY(libinput_udev_assign_seat(m_li, "seat0")))
70 qFatal(msg: "Failed to assign seat");
71
72 m_liFd = libinput_get_fd(libinput: m_li);
73 m_notifier.reset(other: new QSocketNotifier(m_liFd, QSocketNotifier::Read));
74
75 connect(sender: m_notifier.data(), signal: &QSocketNotifier::activated, context: this, slot: &QLibInputHandler::onReadyRead);
76
77 m_pointer.reset(other: new QLibInputPointer);
78 m_keyboard.reset(other: new QLibInputKeyboard);
79 m_touch.reset(other: new QLibInputTouch);
80
81 QInputDeviceManager *manager = QGuiApplicationPrivate::inputDeviceManager();
82 connect(sender: manager, signal: &QInputDeviceManager::cursorPositionChangeRequested, slot: [this](const QPoint &pos) {
83 m_pointer->setPos(pos);
84 });
85
86 // Process the initial burst of DEVICE_ADDED events.
87 onReadyRead();
88}
89
90QLibInputHandler::~QLibInputHandler()
91{
92 if (m_li)
93 libinput_unref(libinput: m_li);
94
95 if (m_udev)
96 udev_unref(udev: m_udev);
97}
98
99void QLibInputHandler::onReadyRead()
100{
101 if (libinput_dispatch(libinput: m_li)) {
102 qWarning(msg: "libinput_dispatch failed");
103 return;
104 }
105
106 libinput_event *ev;
107 while ((ev = libinput_get_event(libinput: m_li)) != nullptr) {
108 processEvent(ev);
109 libinput_event_destroy(event: ev);
110 }
111}
112
113void QLibInputHandler::processEvent(libinput_event *ev)
114{
115 libinput_event_type type = libinput_event_get_type(event: ev);
116 libinput_device *dev = libinput_event_get_device(event: ev);
117
118 switch (type) {
119 case LIBINPUT_EVENT_DEVICE_ADDED:
120 {
121 // This is not just for hotplugging, it is also called for each input
122 // device libinput reads from on startup. Hence it is suitable for doing
123 // touch device registration.
124 QInputDeviceManagerPrivate *inputManagerPriv = QInputDeviceManagerPrivate::get(
125 mgr: QGuiApplicationPrivate::inputDeviceManager());
126 if (libinput_device_has_capability(device: dev, capability: LIBINPUT_DEVICE_CAP_TOUCH)) {
127 m_touch->registerDevice(dev);
128 int &count(m_devCount[QInputDeviceManager::DeviceTypeTouch]);
129 ++count;
130 inputManagerPriv->setDeviceCount(type: QInputDeviceManager::DeviceTypeTouch, count);
131 }
132 if (libinput_device_has_capability(device: dev, capability: LIBINPUT_DEVICE_CAP_POINTER)) {
133 int &count(m_devCount[QInputDeviceManager::DeviceTypePointer]);
134 ++count;
135 inputManagerPriv->setDeviceCount(type: QInputDeviceManager::DeviceTypePointer, count);
136 }
137 if (libinput_device_has_capability(device: dev, capability: LIBINPUT_DEVICE_CAP_KEYBOARD)) {
138 int &count(m_devCount[QInputDeviceManager::DeviceTypeKeyboard]);
139 ++count;
140 inputManagerPriv->setDeviceCount(type: QInputDeviceManager::DeviceTypeKeyboard, count);
141 }
142 break;
143 }
144 case LIBINPUT_EVENT_DEVICE_REMOVED:
145 {
146 QInputDeviceManagerPrivate *inputManagerPriv = QInputDeviceManagerPrivate::get(
147 mgr: QGuiApplicationPrivate::inputDeviceManager());
148 if (libinput_device_has_capability(device: dev, capability: LIBINPUT_DEVICE_CAP_TOUCH)) {
149 m_touch->unregisterDevice(dev);
150 int &count(m_devCount[QInputDeviceManager::DeviceTypeTouch]);
151 --count;
152 inputManagerPriv->setDeviceCount(type: QInputDeviceManager::DeviceTypeTouch, count);
153 }
154 if (libinput_device_has_capability(device: dev, capability: LIBINPUT_DEVICE_CAP_POINTER)) {
155 int &count(m_devCount[QInputDeviceManager::DeviceTypePointer]);
156 --count;
157 inputManagerPriv->setDeviceCount(type: QInputDeviceManager::DeviceTypePointer, count);
158 }
159 if (libinput_device_has_capability(device: dev, capability: LIBINPUT_DEVICE_CAP_KEYBOARD)) {
160 int &count(m_devCount[QInputDeviceManager::DeviceTypeKeyboard]);
161 --count;
162 inputManagerPriv->setDeviceCount(type: QInputDeviceManager::DeviceTypeKeyboard, count);
163 }
164 break;
165 }
166 case LIBINPUT_EVENT_POINTER_BUTTON:
167 m_pointer->processButton(e: libinput_event_get_pointer_event(event: ev));
168 break;
169 case LIBINPUT_EVENT_POINTER_MOTION:
170 m_pointer->processMotion(e: libinput_event_get_pointer_event(event: ev));
171 break;
172 case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE:
173 m_pointer->processAbsMotion(e: libinput_event_get_pointer_event(event: ev));
174 break;
175#if QT_CONFIG(libinput_hires_wheel_support)
176 case LIBINPUT_EVENT_POINTER_SCROLL_WHEEL:
177#else
178 case LIBINPUT_EVENT_POINTER_AXIS:
179#endif
180 m_pointer->processAxis(e: libinput_event_get_pointer_event(event: ev));
181 break;
182 case LIBINPUT_EVENT_KEYBOARD_KEY:
183 m_keyboard->processKey(e: libinput_event_get_keyboard_event(event: ev));
184 break;
185 case LIBINPUT_EVENT_TOUCH_DOWN:
186 m_touch->processTouchDown(e: libinput_event_get_touch_event(event: ev));
187 break;
188 case LIBINPUT_EVENT_TOUCH_MOTION:
189 m_touch->processTouchMotion(e: libinput_event_get_touch_event(event: ev));
190 break;
191 case LIBINPUT_EVENT_TOUCH_UP:
192 m_touch->processTouchUp(e: libinput_event_get_touch_event(event: ev));
193 break;
194 case LIBINPUT_EVENT_TOUCH_CANCEL:
195 m_touch->processTouchCancel(e: libinput_event_get_touch_event(event: ev));
196 break;
197 case LIBINPUT_EVENT_TOUCH_FRAME:
198 m_touch->processTouchFrame(e: libinput_event_get_touch_event(event: ev));
199 break;
200 default:
201 break;
202 }
203}
204
205QT_END_NAMESPACE
206

source code of qtbase/src/platformsupport/input/libinput/qlibinputhandler.cpp