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