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 "qlibinputtouch_p.h"
5#include "qoutputmapping_p.h"
6#include <libinput.h>
7#include <QtGui/QGuiApplication>
8#include <QtGui/QPointingDevice>
9#include <QtGui/QScreen>
10#include <QtGui/QPointingDevice>
11#include <QtGui/private/qhighdpiscaling_p.h>
12#include <QtGui/private/qpointingdevice_p.h>
13
14QT_BEGIN_NAMESPACE
15
16Q_DECLARE_LOGGING_CATEGORY(qLcLibInput)
17Q_LOGGING_CATEGORY(qLcLibInputEvents, "qt.qpa.input.events")
18
19QWindowSystemInterface::TouchPoint *QLibInputTouch::DeviceState::point(int32_t slot)
20{
21 const int id = qMax(a: 0, b: slot);
22
23 for (int i = 0; i < m_points.size(); ++i)
24 if (m_points.at(i).id == id)
25 return &m_points[i];
26
27 return nullptr;
28}
29
30QLibInputTouch::DeviceState *QLibInputTouch::deviceState(libinput_event_touch *e)
31{
32 libinput_device *dev = libinput_event_get_device(event: libinput_event_touch_get_base_event(event: e));
33 return &m_devState[dev];
34}
35
36QRect QLibInputTouch::screenGeometry(DeviceState *state)
37{
38 QScreen *screen = QGuiApplication::primaryScreen();
39 if (!state->m_screenName.isEmpty()) {
40 if (!m_screen) {
41 const QList<QScreen *> screens = QGuiApplication::screens();
42 for (QScreen *s : screens) {
43 if (s->name() == state->m_screenName) {
44 m_screen = s;
45 break;
46 }
47 }
48 }
49 if (m_screen)
50 screen = m_screen;
51 }
52 return screen ? QHighDpi::toNativePixels(value: screen->geometry(), context: screen) : QRect();
53}
54
55QPointF QLibInputTouch::getPos(libinput_event_touch *e)
56{
57 DeviceState *state = deviceState(e);
58 QRect geom = screenGeometry(state);
59 const double x = libinput_event_touch_get_x_transformed(event: e, width: geom.width());
60 const double y = libinput_event_touch_get_y_transformed(event: e, height: geom.height());
61 return geom.topLeft() + QPointF(x, y);
62}
63
64static void setMatrix(libinput_device *dev)
65{
66 if (libinput_device_config_calibration_has_matrix(device: dev)) {
67 QByteArray env = qgetenv(varName: "QT_QPA_LIBINPUT_TOUCH_MATRIX");
68 env = env.simplified();
69 if (env.size()) {
70 float matrix[6];
71 QList<QByteArray> list = env.split(sep: ' ');
72 if (list.length() != 6) {
73 qCWarning(qLcLibInput, "matrix length %" PRIdQSIZETYPE " wrong, should be 6",
74 list.length());
75 return;
76 }
77 for (int i = 0; i < 6; i++) {
78 bool ok = true;
79 matrix[i] = list[i].toFloat(ok: &ok);
80 if (!ok) {
81 qCWarning(qLcLibInput, "Invalid matrix entry %d %s ", i, list[i].constData());
82 return;
83 }
84 }
85 if (libinput_device_config_calibration_set_matrix(device: dev, matrix) != LIBINPUT_CONFIG_STATUS_SUCCESS)
86 qCWarning(qLcLibInput, "Failed to set libinput calibration matrix ");
87 }
88 } else {
89 qCWarning(qLcLibInput, "Touch device doesn't support matrix");
90 }
91}
92void QLibInputTouch::registerDevice(libinput_device *dev)
93{
94 struct udev_device *udev_device;
95 udev_device = libinput_device_get_udev_device(device: dev);
96 QString devNode = QString::fromUtf8(utf8: udev_device_get_devnode(udev_device));
97 QString devName = QString::fromUtf8(utf8: libinput_device_get_name(device: dev));
98
99 qCDebug(qLcLibInput, "libinput: registerDevice %s - %s",
100 qPrintable(devNode), qPrintable(devName));
101
102 QOutputMapping *mapping = QOutputMapping::get();
103 QRect geom;
104 if (mapping->load()) {
105 m_devState[dev].m_screenName = mapping->screenNameForDeviceNode(deviceNode: devNode);
106 if (!m_devState[dev].m_screenName.isEmpty()) {
107 geom = screenGeometry(state: &m_devState[dev]);
108 qCDebug(qLcLibInput) << "libinput: Mapping device" << devNode
109 << "to screen" << m_devState[dev].m_screenName
110 << "with geometry" << geom;
111 }
112 }
113
114 QPointingDevice *&td = m_devState[dev].m_touchDevice;
115 td = new QPointingDevice(devName, udev_device_get_devnum(udev_device),
116 QInputDevice::DeviceType::TouchScreen, QPointingDevice::PointerType::Finger,
117 QPointingDevice::Capability::Position | QPointingDevice::Capability::Area, 16, 0);
118 auto devPriv = QPointingDevicePrivate::get(q: td);
119 devPriv->busId = QString::fromLocal8Bit(ba: udev_device_get_syspath(udev_device)); // TODO is that the best to choose?
120 if (!geom.isNull())
121 devPriv->setAvailableVirtualGeometry(geom);
122 QWindowSystemInterface::registerInputDevice(device: td);
123 setMatrix(dev);
124}
125
126void QLibInputTouch::unregisterDevice(libinput_device *dev)
127{
128 Q_UNUSED(dev);
129 // There is no way to remove a QPointingDevice.
130}
131
132void QLibInputTouch::processTouchDown(libinput_event_touch *e)
133{
134 int slot = libinput_event_touch_get_slot(event: e);
135 DeviceState *state = deviceState(e);
136 QWindowSystemInterface::TouchPoint *tp = state->point(slot);
137 if (tp) {
138 qWarning(msg: "Incorrect touch state");
139 } else {
140 QWindowSystemInterface::TouchPoint newTp;
141 newTp.id = qMax(a: 0, b: slot);
142 newTp.state = QEventPoint::State::Pressed;
143 newTp.area = QRect(0, 0, 8, 8);
144 newTp.area.moveCenter(p: getPos(e));
145 state->m_points.append(t: newTp);
146 qCDebug(qLcLibInputEvents) << "touch down" << newTp;
147 }
148}
149
150void QLibInputTouch::processTouchMotion(libinput_event_touch *e)
151{
152 int slot = libinput_event_touch_get_slot(event: e);
153 DeviceState *state = deviceState(e);
154 QWindowSystemInterface::TouchPoint *tp = state->point(slot);
155 if (tp) {
156 QEventPoint::State tmpState = QEventPoint::State::Updated;
157 const QPointF p = getPos(e);
158 if (tp->area.center() == p)
159 tmpState = QEventPoint::State::Stationary;
160 else
161 tp->area.moveCenter(p);
162 // 'down' may be followed by 'motion' within the same "frame".
163 // Handle this by compressing and keeping the Pressed state until the 'frame'.
164 if (tp->state != QEventPoint::State::Pressed && tp->state != QEventPoint::State::Released)
165 tp->state = tmpState;
166 qCDebug(qLcLibInputEvents) << "touch move" << tp;
167 } else {
168 qWarning(msg: "Inconsistent touch state (got 'motion' without 'down')");
169 }
170}
171
172void QLibInputTouch::processTouchUp(libinput_event_touch *e)
173{
174 int slot = libinput_event_touch_get_slot(event: e);
175 DeviceState *state = deviceState(e);
176 QWindowSystemInterface::TouchPoint *tp = state->point(slot);
177 if (tp) {
178 tp->state = QEventPoint::State::Released;
179 // There may not be a Frame event after the last Up. Work this around.
180 QEventPoint::States s;
181 for (int i = 0; i < state->m_points.size(); ++i)
182 s |= state->m_points.at(i).state;
183 qCDebug(qLcLibInputEvents) << "touch up" << s << tp;
184 if (s == QEventPoint::State::Released)
185 processTouchFrame(e);
186 else
187 qCDebug(qLcLibInputEvents, "waiting for all points to be released");
188 } else {
189 qWarning(msg: "Inconsistent touch state (got 'up' without 'down')");
190 }
191}
192
193void QLibInputTouch::processTouchCancel(libinput_event_touch *e)
194{
195 DeviceState *state = deviceState(e);
196 qCDebug(qLcLibInputEvents) << "touch cancel" << state->m_points;
197 if (state->m_touchDevice)
198 QWindowSystemInterface::handleTouchCancelEvent(window: nullptr, device: state->m_touchDevice, mods: QGuiApplication::keyboardModifiers());
199 else
200 qWarning(msg: "TouchCancel without registered device");
201}
202
203void QLibInputTouch::processTouchFrame(libinput_event_touch *e)
204{
205 DeviceState *state = deviceState(e);
206 if (!state->m_touchDevice) {
207 qWarning(msg: "TouchFrame without registered device");
208 return;
209 }
210 qCDebug(qLcLibInputEvents) << "touch frame" << state->m_points;
211 if (state->m_points.isEmpty())
212 return;
213
214 QWindowSystemInterface::handleTouchEvent(window: nullptr, device: state->m_touchDevice, points: state->m_points,
215 mods: QGuiApplication::keyboardModifiers());
216
217 for (int i = 0; i < state->m_points.size(); ++i) {
218 QWindowSystemInterface::TouchPoint &tp(state->m_points[i]);
219 if (tp.state == QEventPoint::State::Released)
220 state->m_points.removeAt(i: i--);
221 else if (tp.state == QEventPoint::State::Pressed)
222 tp.state = QEventPoint::State::Stationary;
223 }
224}
225
226QT_END_NAMESPACE
227

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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