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 | |
16 | namespace KWayland |
17 | { |
18 | namespace Client |
19 | { |
20 | class Q_DECL_HIDDEN Touch::Private |
21 | { |
22 | public: |
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 | |
30 | private: |
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 | |
44 | class TouchPoint::Private |
45 | { |
46 | public: |
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 | |
56 | TouchPoint::TouchPoint() |
57 | : d(new Private) |
58 | { |
59 | } |
60 | |
61 | TouchPoint::~TouchPoint() = default; |
62 | |
63 | QPointF TouchPoint::position() const |
64 | { |
65 | if (d->positions.isEmpty()) { |
66 | return QPointF(); |
67 | } |
68 | return d->positions.last(); |
69 | } |
70 | |
71 | QList<QPointF> TouchPoint::positions() const |
72 | { |
73 | return d->positions; |
74 | } |
75 | |
76 | quint32 TouchPoint::downSerial() const |
77 | { |
78 | return d->downSerial; |
79 | } |
80 | |
81 | quint32 TouchPoint::upSerial() const |
82 | { |
83 | return d->upSerial; |
84 | } |
85 | |
86 | QPointer<Surface> TouchPoint::surface() const |
87 | { |
88 | return d->surface; |
89 | } |
90 | |
91 | quint32 TouchPoint::time() const |
92 | { |
93 | if (d->timestamps.isEmpty()) { |
94 | return 0; |
95 | } |
96 | return d->timestamps.last(); |
97 | } |
98 | |
99 | QList<quint32> TouchPoint::timestamps() const |
100 | { |
101 | return d->timestamps; |
102 | } |
103 | |
104 | bool TouchPoint::isDown() const |
105 | { |
106 | return d->down; |
107 | } |
108 | |
109 | qint32 TouchPoint::id() const |
110 | { |
111 | return d->id; |
112 | } |
113 | |
114 | Touch::Private::Private(Touch *q) |
115 | : q(q) |
116 | { |
117 | } |
118 | |
119 | void 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 | |
127 | const wl_touch_listener Touch::Private::s_listener = {.down: downCallback, .up: upCallback, .motion: motionCallback, .frame: frameCallback, .cancel: cancelCallback}; |
128 | |
129 | void 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 | |
136 | void 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 | |
156 | TouchPoint *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 | |
167 | void 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 | |
174 | void 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 | |
195 | void 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 | |
202 | void 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 | |
213 | void 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 | |
220 | void 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 | |
228 | Touch::Touch(QObject *parent) |
229 | : QObject(parent) |
230 | , d(new Private(this)) |
231 | { |
232 | } |
233 | |
234 | Touch::~Touch() |
235 | { |
236 | release(); |
237 | } |
238 | |
239 | void Touch::destroy() |
240 | { |
241 | d->touch.destroy(); |
242 | } |
243 | |
244 | void Touch::release() |
245 | { |
246 | d->touch.release(); |
247 | } |
248 | |
249 | void Touch::setup(wl_touch *touch) |
250 | { |
251 | d->setup(touch); |
252 | } |
253 | |
254 | bool Touch::isValid() const |
255 | { |
256 | return d->touch.isValid(); |
257 | } |
258 | |
259 | Touch::operator wl_touch *() const |
260 | { |
261 | return d->touch; |
262 | } |
263 | |
264 | Touch::operator wl_touch *() |
265 | { |
266 | return d->touch; |
267 | } |
268 | |
269 | QList<TouchPoint *> Touch::sequence() const |
270 | { |
271 | return d->sequence; |
272 | } |
273 | |
274 | } |
275 | } |
276 | |
277 | #include "moc_touch.cpp" |
278 | |