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
47 QT_BEGIN_NAMESPACE
48
Q_DECLARE_LOGGING_CATEGORY(qLcLibInput)49 Q_DECLARE_LOGGING_CATEGORY(qLcLibInput)
50
51 QWindowSystemInterface::TouchPoint *QLibInputTouch::DeviceState::point(int32_t slot)
52 {
53 const int id = qMax(0, 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
deviceState(libinput_event_touch * e)62 QLibInputTouch::DeviceState *QLibInputTouch::deviceState(libinput_event_touch *e)
63 {
64 libinput_device *dev = libinput_event_get_device(libinput_event_touch_get_base_event(e));
65 return &m_devState[dev];
66 }
67
getPos(libinput_event_touch * e)68 QPointF 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 = QHighDpi::toNativePixels(screen->geometry(), screen);
86 const double x = libinput_event_touch_get_x_transformed(e, geom.width());
87 const double y = libinput_event_touch_get_y_transformed(e, geom.height());
88 return geom.topLeft() + QPointF(x, y);
89 }
90
registerDevice(libinput_device * dev)91 void QLibInputTouch::registerDevice(libinput_device *dev)
92 {
93 struct udev_device *udev_device;
94 udev_device = libinput_device_get_udev_device(dev);
95 QString devNode = QString::fromUtf8(udev_device_get_devnode(udev_device));
96 QString devName = QString::fromUtf8(libinput_device_get_name(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(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(td);
115 }
116
unregisterDevice(libinput_device * dev)117 void QLibInputTouch::unregisterDevice(libinput_device *dev)
118 {
119 Q_UNUSED(dev);
120 // There is no way to remove a QTouchDevice.
121 }
122
processTouchDown(libinput_event_touch * e)123 void QLibInputTouch::processTouchDown(libinput_event_touch *e)
124 {
125 int slot = libinput_event_touch_get_slot(e);
126 DeviceState *state = deviceState(e);
127 QWindowSystemInterface::TouchPoint *tp = state->point(slot);
128 if (tp) {
129 qWarning("Incorrect touch state");
130 } else {
131 QWindowSystemInterface::TouchPoint newTp;
132 newTp.id = qMax(0, slot);
133 newTp.state = Qt::TouchPointPressed;
134 newTp.area = QRect(0, 0, 8, 8);
135 newTp.area.moveCenter(getPos(e));
136 state->m_points.append(newTp);
137 }
138 }
139
processTouchMotion(libinput_event_touch * e)140 void QLibInputTouch::processTouchMotion(libinput_event_touch *e)
141 {
142 int slot = libinput_event_touch_get_slot(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("Inconsistent touch state (got 'motion' without 'down')");
158 }
159 }
160
processTouchUp(libinput_event_touch * e)161 void QLibInputTouch::processTouchUp(libinput_event_touch *e)
162 {
163 int slot = libinput_event_touch_get_slot(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("Inconsistent touch state (got 'up' without 'down')");
176 }
177 }
178
processTouchCancel(libinput_event_touch * e)179 void QLibInputTouch::processTouchCancel(libinput_event_touch *e)
180 {
181 DeviceState *state = deviceState(e);
182 if (state->m_touchDevice)
183 QWindowSystemInterface::handleTouchCancelEvent(nullptr, state->m_touchDevice, QGuiApplication::keyboardModifiers());
184 else
185 qWarning("TouchCancel without registered device");
186 }
187
processTouchFrame(libinput_event_touch * e)188 void QLibInputTouch::processTouchFrame(libinput_event_touch *e)
189 {
190 DeviceState *state = deviceState(e);
191 if (!state->m_touchDevice) {
192 qWarning("TouchFrame without registered device");
193 return;
194 }
195 if (state->m_points.isEmpty())
196 return;
197
198 QWindowSystemInterface::handleTouchEvent(nullptr, state->m_touchDevice, state->m_points,
199 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--);
205 else if (tp.state == Qt::TouchPointPressed)
206 tp.state = Qt::TouchPointStationary;
207 }
208 }
209
210 QT_END_NAMESPACE
211