Directory: | ./ |
---|---|
File: | server/touch_pool.cpp |
Date: | 2024-01-22 17:25:27 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 98 | 121 | 81.0% |
Branches: | 49 | 78 | 62.8% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | SPDX-FileCopyrightText: 2020 Roman Gilg <subdiff@gmail.com> | ||
3 | SPDX-FileCopyrightText: 2021 Francesco Sorrentino <francesco.sorr@gmail.com> | ||
4 | |||
5 | SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only | ||
6 | */ | ||
7 | #include "touch_pool.h" | ||
8 | #include "data_device.h" | ||
9 | #include "display.h" | ||
10 | #include "pointer.h" | ||
11 | #include "pointer_p.h" | ||
12 | #include "seat.h" | ||
13 | #include "seat_p.h" | ||
14 | #include "touch.h" | ||
15 | #include "utils.h" | ||
16 | |||
17 | #include <config-wrapland.h> | ||
18 | |||
19 | #if HAVE_LINUX_INPUT_H | ||
20 | #include <linux/input-event-codes.h> | ||
21 | #endif | ||
22 | |||
23 | namespace Wrapland::Server | ||
24 | { | ||
25 | |||
26 | 112 | touch_pool::touch_pool(Seat* seat) | |
27 | 112 | : seat{seat} | |
28 | { | ||
29 | 112 | } | |
30 | |||
31 | 224 | touch_pool::~touch_pool() | |
32 | { | ||
33 |
1/2✓ Branch 0 taken 224 times.
✗ Branch 1 not taken.
|
224 | QObject::disconnect(focus.surface_lost_notifier); |
34 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 224 times.
|
224 | for (auto dev : devices) { |
35 | ✗ | QObject::disconnect(dev, nullptr, seat, nullptr); | |
36 | } | ||
37 | 224 | } | |
38 | |||
39 | 14 | touch_focus const& touch_pool::get_focus() const | |
40 | { | ||
41 | 14 | return focus; | |
42 | } | ||
43 | |||
44 | 1 | std::vector<Touch*> const& touch_pool::get_devices() const | |
45 | { | ||
46 | 1 | return devices; | |
47 | } | ||
48 | |||
49 | 12 | void touch_pool::create_device(Client* client, uint32_t version, uint32_t id) | |
50 | { | ||
51 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | auto touch = new Touch(client, version, id, seat); |
52 | 12 | devices.push_back(touch); | |
53 | |||
54 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
12 | if (focus.surface && focus.surface->client() == client) { |
55 | // this is a touch for the currently focused touch surface | ||
56 | 1 | focus.devices.push_back(touch); | |
57 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (!ids.empty()) { |
58 | // TODO(unknown author): send out all the points | ||
59 | } | ||
60 | 1 | } | |
61 | 24 | QObject::connect(touch, &Touch::resourceDestroyed, seat, [touch, this] { | |
62 | 12 | remove_one(devices, touch); | |
63 | 12 | remove_one(focus.devices, touch); | |
64 | |||
65 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | assert(!contains(devices, touch)); |
66 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | assert(!contains(focus.devices, touch)); |
67 | 12 | }); | |
68 | 12 | Q_EMIT seat->touchCreated(touch); | |
69 | 12 | } | |
70 | |||
71 | 3 | void touch_pool::set_focused_surface(Surface* surface, QPointF const& surfacePosition) | |
72 | { | ||
73 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (is_in_progress()) { |
74 | // changing surface not allowed during a touch sequence | ||
75 | ✗ | return; | |
76 | } | ||
77 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | Q_ASSERT(!seat->drags().is_touch_drag()); |
78 | |||
79 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | if (focus.surface) { |
80 | 1 | QObject::disconnect(focus.surface_lost_notifier); | |
81 | 1 | } | |
82 | 3 | focus = touch_focus(); | |
83 | 3 | focus.surface = surface; | |
84 | 3 | focus.offset = surfacePosition; | |
85 | 3 | focus.devices = interfacesForSurface(surface, devices); | |
86 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (focus.surface) { |
87 | 6 | focus.surface_lost_notifier | |
88 | 8 | = QObject::connect(surface, &Surface::resourceDestroyed, seat, [this] { | |
89 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (is_in_progress()) { |
90 | // Surface destroyed during touch sequence - send a cancel | ||
91 | ✗ | for (auto touch : focus.devices) { | |
92 | ✗ | touch->cancel(); | |
93 | } | ||
94 | } | ||
95 | 2 | focus = touch_focus(); | |
96 | 2 | }); | |
97 | 3 | } | |
98 | 3 | } | |
99 | |||
100 | 1 | void touch_pool::set_focused_surface_position(QPointF const& surfacePosition) | |
101 | { | ||
102 | 1 | focus.offset = surfacePosition; | |
103 | 1 | } | |
104 | |||
105 | 7 | int32_t touch_pool::touch_down(QPointF const& globalPosition) | |
106 | { | ||
107 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
|
7 | const int32_t id = ids.empty() ? 0 : ids.crbegin()->first + 1; |
108 | 7 | auto const serial = seat->d_ptr->display()->handle->nextSerial(); | |
109 | 7 | auto const pos = globalPosition - focus.offset; | |
110 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 7 times.
|
12 | for (auto touch : focus.devices) { |
111 | 5 | touch->down(id, serial, pos); | |
112 | } | ||
113 | |||
114 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
|
7 | if (id == 0) { |
115 | 4 | focus.first_touch_position = globalPosition; | |
116 | 4 | } | |
117 | |||
118 | #if HAVE_LINUX_INPUT_H | ||
119 |
5/6✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
7 | if (id == 0 && focus.devices.empty() && seat->hasPointer()) { |
120 | // If the client did not bind the touch interface fall back | ||
121 | // to at least emulating touch through pointer events. | ||
122 | ✗ | forEachInterface( | |
123 | ✗ | focus.surface, seat->pointers().get_devices(), [this, pos, serial](auto pointer) { | |
124 | ✗ | pointer->d_ptr->sendEnter(serial, focus.surface, pos); | |
125 | ✗ | pointer->d_ptr->sendMotion(pos); | |
126 | ✗ | pointer->buttonPressed(serial, BTN_LEFT); | |
127 | ✗ | pointer->d_ptr->sendFrame(); | |
128 | ✗ | }); | |
129 | } | ||
130 | #endif | ||
131 | |||
132 | 7 | ids[id] = serial; | |
133 | 7 | return id; | |
134 | } | ||
135 | |||
136 | 4 | void touch_pool::touch_up(int32_t id) | |
137 | { | ||
138 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | Q_ASSERT(ids.count(id)); |
139 | 4 | auto const serial = seat->d_ptr->display()->handle->nextSerial(); | |
140 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
4 | if (seat->drags().is_touch_drag() && seat->drags().get_source().serial == ids[id]) { |
141 | // the implicitly grabbing touch point has been upped | ||
142 | 1 | seat->drags().drop(); | |
143 | 1 | } | |
144 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
|
8 | for (auto touch : focus.devices) { |
145 | 4 | touch->up(id, serial); | |
146 | } | ||
147 | |||
148 | #if HAVE_LINUX_INPUT_H | ||
149 |
3/6✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
4 | if (id == 0 && focus.devices.empty() && seat->hasPointer()) { |
150 | // Client did not bind touch, fall back to emulating with pointer events. | ||
151 | ✗ | const uint32_t serial = seat->d_ptr->display()->handle->nextSerial(); | |
152 | ✗ | forEachInterface(focus.surface, seat->pointers().get_devices(), [serial](auto pointer) { | |
153 | ✗ | pointer->buttonReleased(serial, BTN_LEFT); | |
154 | ✗ | }); | |
155 | } | ||
156 | #endif | ||
157 | |||
158 | 4 | ids.erase(id); | |
159 | 4 | } | |
160 | |||
161 | 3 | void touch_pool::touch_move(int32_t id, QPointF const& globalPosition) | |
162 | { | ||
163 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | Q_ASSERT(ids.count(id)); |
164 | 3 | auto const pos = globalPosition - focus.offset; | |
165 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
|
5 | for (auto touch : focus.devices) { |
166 | 2 | touch->move(id, pos); | |
167 | } | ||
168 | |||
169 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (id == 0) { |
170 | 3 | focus.first_touch_position = globalPosition; | |
171 | 3 | } | |
172 | |||
173 |
4/6✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
3 | if (id == 0 && focus.devices.empty() && seat->hasPointer()) { |
174 | // Client did not bind touch, fall back to emulating with pointer events. | ||
175 | ✗ | forEachInterface(focus.surface, seat->pointers().get_devices(), [pos](auto pointer) { | |
176 | ✗ | pointer->d_ptr->sendMotion(pos); | |
177 | ✗ | }); | |
178 | } | ||
179 | 3 | Q_EMIT seat->touchMoved(id, ids[id], globalPosition); | |
180 | 3 | } | |
181 | |||
182 | ✗ | void touch_pool::touch_move_any(QPointF const& pos) | |
183 | { | ||
184 | ✗ | assert(!ids.empty()); | |
185 | ✗ | touch_move(ids.cbegin()->first, pos); | |
186 | } | ||
187 | |||
188 | 8 | void touch_pool::touch_frame() const | |
189 | { | ||
190 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 8 times.
|
15 | for (auto touch : focus.devices) { |
191 | 7 | touch->frame(); | |
192 | } | ||
193 | 8 | } | |
194 | |||
195 | 2 | void touch_pool::cancel_sequence() | |
196 | { | ||
197 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | for (auto touch : focus.devices) { |
198 | 1 | touch->cancel(); | |
199 | } | ||
200 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (seat->drags().is_touch_drag()) { |
201 | // cancel the drag, don't drop. | ||
202 | ✗ | seat->drags().cancel(); | |
203 | } | ||
204 | 2 | ids.clear(); | |
205 | 2 | } | |
206 | |||
207 | 6 | bool touch_pool::is_in_progress() const | |
208 | { | ||
209 | 6 | return !ids.empty(); | |
210 | } | ||
211 | |||
212 | 2 | bool touch_pool::has_implicit_grab(uint32_t serial) const | |
213 | { | ||
214 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (!focus.surface) { |
215 | // origin surface has been destroyed | ||
216 | ✗ | return false; | |
217 | } | ||
218 | 4 | auto check = [serial](auto const& pair) { return pair.second == serial; }; | |
219 | 2 | return std::find_if(ids.begin(), ids.end(), check) != ids.end(); | |
220 | 2 | } | |
221 | |||
222 | } | ||
223 |