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 "qlibinputtouch_p.h"
41#include "qtouchoutputmapping_p.h"
42#include <libinput.h>
43#include <QtGui/QGuiApplication>
44#include <QtGui/QScreen>
45#include <QtGui/private/qhighdpiscaling_p.h>
46
47QT_BEGIN_NAMESPACE
48
49Q_DECLARE_LOGGING_CATEGORY(qLcLibInput)
50
51QWindowSystemInterface::TouchPoint *QLibInputTouch::DeviceState::point(int32_t slot)
52{
53 const int id = qMax(a: 0, b: slot);
54
55 for (int i = 0; i < m_points.count(); ++i)
56 if (m_points.at(i).id == id)
57 return &m_points[i];
58
59 return nullptr;
60}
61
62QLibInputTouch::DeviceState *QLibInputTouch::deviceState(libinput_event_touch *e)
63{
64 libinput_device *dev = libinput_event_get_device(event: libinput_event_touch_get_base_event(event: e));
65 return &m_devState[dev];
66}
67
68QPointF QLibInputTouch::getPos(libinput_event_touch *e)
69{
70 DeviceState *state = deviceState(e);
71 QScreen *screen = QGuiApplication::primaryScreen();
72 if (!state->m_screenName.isEmpty()) {
73 if (!m_screen) {
74 const QList<QScreen *> screens = QGuiApplication::screens();
75 for (QScreen *s : screens) {
76 if (s->name() == state->m_screenName) {
77 m_screen = s;
78 break;
79 }
80 }
81 }
82 if (m_screen)
83 screen = m_screen;
84 }
85 const QRect geom = screen ? QHighDpi::toNativePixels(value: screen->geometry(), context: screen) : QRect();
86 const double x = libinput_event_touch_get_x_transformed(event: e, width: geom.width());
87 const double y = libinput_event_touch_get_y_transformed(event: e, height: geom.height());
88 return geom.topLeft() + QPointF(x, y);
89}
90
91void QLibInputTouch::registerDevice(libinput_device *dev)
92{
93 struct udev_device *udev_device;
94 udev_device = libinput_device_get_udev_device(device: dev);
95 QString devNode = QString::fromUtf8(str: udev_device_get_devnode(udev_device));
96 QString devName = QString::fromUtf8(str: libinput_device_get_name(device: dev));
97
98 qCDebug(qLcLibInput, "libinput: registerDevice %s - %s",
99 qPrintable(devNode), qPrintable(devName));
100
101 QTouchOutputMapping mapping;
102 if (mapping.load()) {
103 m_devState[dev].m_screenName = mapping.screenNameForDeviceNode(deviceNode: devNode);
104 if (!m_devState[dev].m_screenName.isEmpty())
105 qCDebug(qLcLibInput, "libinput: Mapping device %s to screen %s",
106 qPrintable(devNode), qPrintable(m_devState[dev].m_screenName));
107 }
108
109 QTouchDevice *&td = m_devState[dev].m_touchDevice;
110 td = new QTouchDevice;
111 td->setName(devName);
112 td->setType(QTouchDevice::TouchScreen);
113 td->setCapabilities(QTouchDevice::Position | QTouchDevice::Area);
114 QWindowSystemInterface::registerTouchDevice(device: td);
115}
116
117void QLibInputTouch::unregisterDevice(libinput_device *dev)
118{
119 Q_UNUSED(dev);
120 // There is no way to remove a QTouchDevice.
121}
122
123void QLibInputTouch::processTouchDown(libinput_event_touch *e)
124{
125 int slot = libinput_event_touch_get_slot(event: e);
126 DeviceState *state = deviceState(e);
127 QWindowSystemInterface::TouchPoint *tp = state->point(slot);
128 if (tp) {
129 qWarning(msg: "Incorrect touch state");
130 } else {
131 QWindowSystemInterface::TouchPoint newTp;
132 newTp.id = qMax(a: 0, b: slot);
133 newTp.state = Qt::TouchPointPressed;
134 newTp.area = QRect(0, 0, 8, 8);
135 newTp.area.moveCenter(p: getPos(e));
136 state->m_points.append(t: newTp);
137 }
138}
139
140void QLibInputTouch::processTouchMotion(libinput_event_touch *e)
141{
142 int slot = libinput_event_touch_get_slot(event: e);
143 DeviceState *state = deviceState(e);
144 QWindowSystemInterface::TouchPoint *tp = state->point(slot);
145 if (tp) {
146 Qt::TouchPointState tmpState = Qt::TouchPointMoved;
147 const QPointF p = getPos(e);
148 if (tp->area.center() == p)
149 tmpState = Qt::TouchPointStationary;
150 else
151 tp->area.moveCenter(p);
152 // 'down' may be followed by 'motion' within the same "frame".
153 // Handle this by compressing and keeping the Pressed state until the 'frame'.
154 if (tp->state != Qt::TouchPointPressed && tp->state != Qt::TouchPointReleased)
155 tp->state = tmpState;
156 } else {
157 qWarning(msg: "Inconsistent touch state (got 'motion' without 'down')");
158 }
159}
160
161void QLibInputTouch::processTouchUp(libinput_event_touch *e)
162{
163 int slot = libinput_event_touch_get_slot(event: e);
164 DeviceState *state = deviceState(e);
165 QWindowSystemInterface::TouchPoint *tp = state->point(slot);
166 if (tp) {
167 tp->state = Qt::TouchPointReleased;
168 // There may not be a Frame event after the last Up. Work this around.
169 Qt::TouchPointStates s;
170 for (int i = 0; i < state->m_points.count(); ++i)
171 s |= state->m_points.at(i).state;
172 if (s == Qt::TouchPointReleased)
173 processTouchFrame(e);
174 } else {
175 qWarning(msg: "Inconsistent touch state (got 'up' without 'down')");
176 }
177}
178
179void QLibInputTouch::processTouchCancel(libinput_event_touch *e)
180{
181 DeviceState *state = deviceState(e);
182 if (state->m_touchDevice)
183 QWindowSystemInterface::handleTouchCancelEvent(window: nullptr, device: state->m_touchDevice, mods: QGuiApplication::keyboardModifiers());
184 else
185 qWarning(msg: "TouchCancel without registered device");
186}
187
188void QLibInputTouch::processTouchFrame(libinput_event_touch *e)
189{
190 DeviceState *state = deviceState(e);
191 if (!state->m_touchDevice) {
192 qWarning(msg: "TouchFrame without registered device");
193 return;
194 }
195 if (state->m_points.isEmpty())
196 return;
197
198 QWindowSystemInterface::handleTouchEvent(window: nullptr, device: state->m_touchDevice, points: state->m_points,
199 mods: QGuiApplication::keyboardModifiers());
200
201 for (int i = 0; i < state->m_points.count(); ++i) {
202 QWindowSystemInterface::TouchPoint &tp(state->m_points[i]);
203 if (tp.state == Qt::TouchPointReleased)
204 state->m_points.removeAt(i: i--);
205 else if (tp.state == Qt::TouchPointPressed)
206 tp.state = Qt::TouchPointStationary;
207 }
208}
209
210QT_END_NAMESPACE
211

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