1/*
2 SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6#include "touch.h"
7#include "surface.h"
8#include "wayland_pointer_p.h"
9// Qt
10#include <QList>
11#include <QPointF>
12#include <QPointer>
13// wayland
14#include <wayland-client-protocol.h>
15
16namespace KWayland
17{
18namespace Client
19{
20class Q_DECL_HIDDEN Touch::Private
21{
22public:
23 Private(Touch *q);
24 void setup(wl_touch *t);
25 WaylandPointer<wl_touch, wl_touch_release> touch;
26 bool active = false;
27 QList<TouchPoint *> sequence;
28 TouchPoint *getActivePoint(qint32 id) const;
29
30private:
31 static void downCallback(void *data, wl_touch *touch, uint32_t serial, uint32_t time, wl_surface *surface, int32_t id, wl_fixed_t x, wl_fixed_t y);
32 static void upCallback(void *data, wl_touch *touch, uint32_t serial, uint32_t time, int32_t id);
33 static void motionCallback(void *data, wl_touch *touch, uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y);
34 static void frameCallback(void *data, wl_touch *touch);
35 static void cancelCallback(void *data, wl_touch *touch);
36 void down(quint32 serial, quint32 time, qint32 id, const QPointF &position, const QPointer<Surface> &surface);
37 void up(quint32 serial, quint32 time, qint32 id);
38 void motion(quint32 time, qint32 id, const QPointF &position);
39
40 Touch *q;
41 static const wl_touch_listener s_listener;
42};
43
44class TouchPoint::Private
45{
46public:
47 qint32 id = 0;
48 quint32 downSerial = 0;
49 quint32 upSerial = 0;
50 QPointer<Surface> surface;
51 QList<QPointF> positions;
52 QList<quint32> timestamps;
53 bool down = true;
54};
55
56TouchPoint::TouchPoint()
57 : d(new Private)
58{
59}
60
61TouchPoint::~TouchPoint() = default;
62
63QPointF TouchPoint::position() const
64{
65 if (d->positions.isEmpty()) {
66 return QPointF();
67 }
68 return d->positions.last();
69}
70
71QList<QPointF> TouchPoint::positions() const
72{
73 return d->positions;
74}
75
76quint32 TouchPoint::downSerial() const
77{
78 return d->downSerial;
79}
80
81quint32 TouchPoint::upSerial() const
82{
83 return d->upSerial;
84}
85
86QPointer<Surface> TouchPoint::surface() const
87{
88 return d->surface;
89}
90
91quint32 TouchPoint::time() const
92{
93 if (d->timestamps.isEmpty()) {
94 return 0;
95 }
96 return d->timestamps.last();
97}
98
99QList<quint32> TouchPoint::timestamps() const
100{
101 return d->timestamps;
102}
103
104bool TouchPoint::isDown() const
105{
106 return d->down;
107}
108
109qint32 TouchPoint::id() const
110{
111 return d->id;
112}
113
114Touch::Private::Private(Touch *q)
115 : q(q)
116{
117}
118
119void Touch::Private::setup(wl_touch *t)
120{
121 Q_ASSERT(t);
122 Q_ASSERT(!touch);
123 touch.setup(pointer: t);
124 wl_touch_add_listener(wl_touch: touch, listener: &s_listener, data: this);
125}
126
127const wl_touch_listener Touch::Private::s_listener = {.down: downCallback, .up: upCallback, .motion: motionCallback, .frame: frameCallback, .cancel: cancelCallback};
128
129void Touch::Private::downCallback(void *data, wl_touch *touch, uint32_t serial, uint32_t time, wl_surface *surface, int32_t id, wl_fixed_t x, wl_fixed_t y)
130{
131 auto t = reinterpret_cast<Touch::Private *>(data);
132 Q_ASSERT(t->touch == touch);
133 t->down(serial, time, id, position: QPointF(wl_fixed_to_double(f: x), wl_fixed_to_double(f: y)), surface: QPointer<Surface>(Surface::get(native: surface)));
134}
135
136void Touch::Private::down(quint32 serial, quint32 time, qint32 id, const QPointF &position, const QPointer<Surface> &surface)
137{
138 TouchPoint *p = new TouchPoint;
139 p->d->downSerial = serial;
140 p->d->surface = surface;
141 p->d->id = id;
142 p->d->positions << position;
143 p->d->timestamps << time;
144 if (active) {
145 sequence << p;
146 Q_EMIT q->pointAdded(point: p);
147 } else {
148 qDeleteAll(c: sequence);
149 sequence.clear();
150 sequence << p;
151 active = true;
152 Q_EMIT q->sequenceStarted(startPoint: p);
153 }
154}
155
156TouchPoint *Touch::Private::getActivePoint(qint32 id) const
157{
158 auto it = std::find_if(first: sequence.constBegin(), last: sequence.constEnd(), pred: [id](TouchPoint *p) {
159 return p->id() == id && p->isDown();
160 });
161 if (it == sequence.constEnd()) {
162 return nullptr;
163 }
164 return *it;
165}
166
167void Touch::Private::upCallback(void *data, wl_touch *touch, uint32_t serial, uint32_t time, int32_t id)
168{
169 auto t = reinterpret_cast<Touch::Private *>(data);
170 Q_ASSERT(t->touch == touch);
171 t->up(serial, time, id);
172}
173
174void Touch::Private::up(quint32 serial, quint32 time, qint32 id)
175{
176 TouchPoint *p = getActivePoint(id);
177 if (!p) {
178 return;
179 }
180 p->d->timestamps << time;
181 p->d->upSerial = serial;
182 p->d->down = false;
183 Q_EMIT q->pointRemoved(point: p);
184 // check whether the sequence ended
185 for (auto it = sequence.constBegin(); it != sequence.constEnd(); ++it) {
186 if ((*it)->isDown()) {
187 return;
188 }
189 }
190 // no touch point is down
191 active = false;
192 Q_EMIT q->sequenceEnded();
193}
194
195void Touch::Private::motionCallback(void *data, wl_touch *touch, uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y)
196{
197 auto t = reinterpret_cast<Touch::Private *>(data);
198 Q_ASSERT(t->touch == touch);
199 t->motion(time, id, position: QPointF(wl_fixed_to_double(f: x), wl_fixed_to_double(f: y)));
200}
201
202void Touch::Private::motion(quint32 time, qint32 id, const QPointF &position)
203{
204 TouchPoint *p = getActivePoint(id);
205 if (!p) {
206 return;
207 }
208 p->d->positions << position;
209 p->d->timestamps << time;
210 Q_EMIT q->pointMoved(point: p);
211}
212
213void Touch::Private::frameCallback(void *data, wl_touch *touch)
214{
215 auto t = reinterpret_cast<Touch::Private *>(data);
216 Q_ASSERT(t->touch == touch);
217 Q_EMIT t->q->frameEnded();
218}
219
220void Touch::Private::cancelCallback(void *data, wl_touch *touch)
221{
222 auto t = reinterpret_cast<Touch::Private *>(data);
223 Q_ASSERT(t->touch == touch);
224 t->active = false;
225 Q_EMIT t->q->sequenceCanceled();
226}
227
228Touch::Touch(QObject *parent)
229 : QObject(parent)
230 , d(new Private(this))
231{
232}
233
234Touch::~Touch()
235{
236 release();
237}
238
239void Touch::destroy()
240{
241 d->touch.destroy();
242}
243
244void Touch::release()
245{
246 d->touch.release();
247}
248
249void Touch::setup(wl_touch *touch)
250{
251 d->setup(touch);
252}
253
254bool Touch::isValid() const
255{
256 return d->touch.isValid();
257}
258
259Touch::operator wl_touch *() const
260{
261 return d->touch;
262}
263
264Touch::operator wl_touch *()
265{
266 return d->touch;
267}
268
269QList<TouchPoint *> Touch::sequence() const
270{
271 return d->sequence;
272}
273
274}
275}
276
277#include "moc_touch.cpp"
278

source code of kwayland/src/client/touch.cpp