1 /****************************************************************************
2 Copyright © 2020 Roman Gilg <subdiff@gmail.com>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) version 3, or any
8 later version accepted by the membership of KDE e.V. (or its
9 successor approved by the membership of KDE e.V.), which shall
10 act as a proxy defined in Section 6 of version 3 of the license.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 ****************************************************************************/
20 #include "pointer_constraints_v1.h"
21 #include "pointer_constraints_v1_p.h"
22
23 #include "display.h"
24 #include "pointer_p.h"
25 #include "region.h"
26 #include "surface.h"
27 #include "surface_p.h"
28
29 #include "wayland/client.h"
30 #include "wayland/display.h"
31 #include "wayland/global.h"
32 #include "wayland/resource.h"
33
34 #include <functional>
35
36 #include <wayland-pointer-constraints-unstable-v1-server-protocol.h>
37
38 namespace Wrapland::Server
39 {
40
Private(PointerConstraintsV1 * q,Display * display)41 PointerConstraintsV1::Private::Private(PointerConstraintsV1* q, Display* display)
42 : Wayland::Global<PointerConstraintsV1>(q,
43 display,
44 &zwp_pointer_constraints_v1_interface,
45 &s_interface)
46 , q_ptr(q)
47 {
48 }
49
50 const struct zwp_pointer_constraints_v1_interface PointerConstraintsV1::Private::s_interface = {
51 resourceDestroyCallback,
52 cb<lockPointerCallback>,
53 cb<confinePointerCallback>,
54 };
55
lockPointerCallback(PointerConstraintsV1Bind * bind,uint32_t id,wl_resource * wlSurface,wl_resource * wlPointer,wl_resource * wlRegion,uint32_t lifetime)56 void PointerConstraintsV1::Private::lockPointerCallback(PointerConstraintsV1Bind* bind,
57 uint32_t id,
58 wl_resource* wlSurface,
59 wl_resource* wlPointer,
60 wl_resource* wlRegion,
61 uint32_t lifetime)
62 {
63 auto priv = bind->global()->handle()->d_ptr.get();
64 priv->createConstraint<LockedPointerV1>(
65 bind->resource(), id, wlSurface, wlPointer, wlRegion, lifetime);
66 }
67
confinePointerCallback(PointerConstraintsV1Bind * bind,uint32_t id,wl_resource * wlSurface,wl_resource * wlPointer,wl_resource * wlRegion,uint32_t lifetime)68 void PointerConstraintsV1::Private::confinePointerCallback(PointerConstraintsV1Bind* bind,
69 uint32_t id,
70 wl_resource* wlSurface,
71 wl_resource* wlPointer,
72 wl_resource* wlRegion,
73 uint32_t lifetime)
74 {
75 auto priv = bind->global()->handle()->d_ptr.get();
76 priv->createConstraint<ConfinedPointerV1>(
77 bind->resource(), id, wlSurface, wlPointer, wlRegion, lifetime);
78 }
79
80 template<class Constraint>
createConstraint(wl_resource * wlResource,uint32_t id,wl_resource * wlSurface,wl_resource * wlPointer,wl_resource * wlRegion,uint32_t lifetime)81 void PointerConstraintsV1::Private::createConstraint(wl_resource* wlResource,
82 uint32_t id,
83 wl_resource* wlSurface,
84 wl_resource* wlPointer,
85 wl_resource* wlRegion,
86 uint32_t lifetime)
87 {
88 auto priv = handle(wlResource)->d_ptr.get();
89 auto bind = priv->getBind(wlResource);
90
91 if (!wlSurface || !wlPointer) {
92 // send error?
93 return;
94 }
95
96 auto surface = Wayland::Resource<Surface>::handle(wlSurface);
97 // auto pointer = Wayland::Resource<Pointer>::handle(wlPointer);
98
99 if (!surface->lockedPointer().isNull() || !surface->confinedPointer().isNull()) {
100 surface->d_ptr->postError(ZWP_POINTER_CONSTRAINTS_V1_ERROR_ALREADY_CONSTRAINED,
101 "Surface already constrained");
102 return;
103 }
104
105 auto constraint = new Constraint(bind->client()->handle(), bind->version(), id, priv->handle());
106
107 switch (lifetime) {
108 case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT:
109 constraint->d_ptr->lifeTime = Constraint::LifeTime::Persistent;
110 break;
111 case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT:
112 default:
113 constraint->d_ptr->lifeTime = Constraint::LifeTime::OneShot;
114 break;
115 }
116
117 auto region = wlRegion ? Wayland::Resource<Region>::handle(wlRegion) : nullptr;
118 constraint->d_ptr->region = region ? region->region() : QRegion();
119
120 surface->d_ptr->installPointerConstraint(constraint);
121 }
122
PointerConstraintsV1(Display * display,QObject * parent)123 PointerConstraintsV1::PointerConstraintsV1(Display* display, QObject* parent)
124 : QObject(parent)
125 , d_ptr(new Private(this, display))
126 {
127 d_ptr->create();
128 }
129
130 PointerConstraintsV1::~PointerConstraintsV1() = default;
131
Private(Client * client,uint32_t version,uint32_t id,LockedPointerV1 * q)132 LockedPointerV1::Private::Private(Client* client, uint32_t version, uint32_t id, LockedPointerV1* q)
133 : Wayland::Resource<LockedPointerV1>(client,
134 version,
135 id,
136 &zwp_locked_pointer_v1_interface,
137 &s_interface,
138 q)
139 , q_ptr(q)
140 {
141 }
142
143 const struct zwp_locked_pointer_v1_interface LockedPointerV1::Private::s_interface = {
144 destroyCallback,
145 setCursorPositionHintCallback,
146 setRegionCallback,
147 };
148
setCursorPositionHintCallback(wl_client * client,wl_resource * wlResource,wl_fixed_t surface_x,wl_fixed_t surface_y)149 void LockedPointerV1::Private::setCursorPositionHintCallback([[maybe_unused]] wl_client* client,
150 wl_resource* wlResource,
151 wl_fixed_t surface_x,
152 wl_fixed_t surface_y)
153 {
154 auto priv = handle(wlResource)->d_ptr;
155
156 priv->pendingHint = QPointF(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y));
157 priv->hintIsSet = true;
158 }
159
setRegionCallback(wl_client * wlClient,wl_resource * wlResource,wl_resource * wlRegion)160 void LockedPointerV1::Private::setRegionCallback([[maybe_unused]] wl_client* wlClient,
161 wl_resource* wlResource,
162 wl_resource* wlRegion)
163 {
164 auto priv = handle(wlResource)->d_ptr;
165 auto region = wlRegion ? Wayland::Resource<Region>::handle(wlRegion) : nullptr;
166
167 priv->pendingRegion = region ? region->region() : QRegion();
168 priv->regionIsSet = true;
169 }
170
commit()171 void LockedPointerV1::Private::commit()
172 {
173 if (regionIsSet) {
174 region = pendingRegion;
175 pendingRegion = QRegion();
176 regionIsSet = false;
177 Q_EMIT q_ptr->regionChanged();
178 }
179 if (hintIsSet) {
180 hint = pendingHint;
181 hintIsSet = false;
182 Q_EMIT q_ptr->cursorPositionHintChanged();
183 }
184 }
185
update()186 void LockedPointerV1::Private::update()
187 {
188 if (locked) {
189 send<zwp_locked_pointer_v1_send_locked>();
190 } else {
191 send<zwp_locked_pointer_v1_send_unlocked>();
192 }
193 }
194
LockedPointerV1(Client * client,uint32_t version,uint32_t id,PointerConstraintsV1 * constraints)195 LockedPointerV1::LockedPointerV1(Client* client,
196 uint32_t version,
197 uint32_t id,
198 PointerConstraintsV1* constraints)
199 : QObject(constraints)
200 , d_ptr(new Private(client, version, id, this))
201 {
202 connect(this, &LockedPointerV1::resourceDestroyed, this, [this]() { setLocked(false); });
203 }
204
lifeTime() const205 LockedPointerV1::LifeTime LockedPointerV1::lifeTime() const
206 {
207 return d_ptr->lifeTime;
208 }
209
region() const210 QRegion LockedPointerV1::region() const
211 {
212 return d_ptr->region;
213 }
214
cursorPositionHint() const215 QPointF LockedPointerV1::cursorPositionHint() const
216 {
217 return d_ptr->hint;
218 }
219
isLocked() const220 bool LockedPointerV1::isLocked() const
221 {
222 return d_ptr->locked;
223 }
224
setLocked(bool locked)225 void LockedPointerV1::setLocked(bool locked)
226 {
227 if (locked == d_ptr->locked) {
228 return;
229 }
230 if (!locked) {
231 d_ptr->hint = QPointF(-1., -1.);
232 }
233 d_ptr->locked = locked;
234 d_ptr->update();
235 Q_EMIT lockedChanged();
236 }
237
Private(Client * client,uint32_t version,uint32_t id,ConfinedPointerV1 * q)238 ConfinedPointerV1::Private::Private(Client* client,
239 uint32_t version,
240 uint32_t id,
241 ConfinedPointerV1* q)
242 : Wayland::Resource<ConfinedPointerV1>(client,
243 version,
244 id,
245 &zwp_confined_pointer_v1_interface,
246 &s_interface,
247 q)
248 , q_ptr(q)
249 {
250 }
251
252 const struct zwp_confined_pointer_v1_interface ConfinedPointerV1::Private::s_interface = {
253 destroyCallback,
254 setRegionCallback,
255 };
256
setRegionCallback(wl_client * wlClient,wl_resource * wlResource,wl_resource * wlRegion)257 void ConfinedPointerV1::Private::setRegionCallback([[maybe_unused]] wl_client* wlClient,
258 wl_resource* wlResource,
259 wl_resource* wlRegion)
260 {
261 auto priv = handle(wlResource)->d_ptr;
262 auto region = wlRegion ? Wayland::Resource<Region>::handle(wlRegion) : nullptr;
263
264 priv->handle()->d_ptr->pendingRegion = region ? region->region() : QRegion();
265 priv->handle()->d_ptr->regionIsSet = true;
266 }
267
update()268 void ConfinedPointerV1::Private::update()
269 {
270 if (confined) {
271 send<zwp_confined_pointer_v1_send_confined>();
272 } else {
273 send<zwp_confined_pointer_v1_send_unconfined>();
274 }
275 }
276
commit()277 void ConfinedPointerV1::Private::commit()
278 {
279 if (!regionIsSet) {
280 return;
281 }
282
283 region = pendingRegion;
284 pendingRegion = QRegion();
285 regionIsSet = false;
286
287 Q_EMIT q_ptr->regionChanged();
288 }
289
ConfinedPointerV1(Client * client,uint32_t version,uint32_t id,PointerConstraintsV1 * constraints)290 ConfinedPointerV1::ConfinedPointerV1(Client* client,
291 uint32_t version,
292 uint32_t id,
293 PointerConstraintsV1* constraints)
294 : QObject(constraints)
295 , d_ptr(new Private(client, version, id, this))
296 {
297 connect(this, &ConfinedPointerV1::resourceDestroyed, this, [this]() { setConfined(false); });
298 }
299
lifeTime() const300 ConfinedPointerV1::LifeTime ConfinedPointerV1::lifeTime() const
301 {
302 return d_ptr->lifeTime;
303 }
304
region() const305 QRegion ConfinedPointerV1::region() const
306 {
307 return d_ptr->region;
308 }
309
isConfined() const310 bool ConfinedPointerV1::isConfined() const
311 {
312 return d_ptr->confined;
313 }
314
setConfined(bool confined)315 void ConfinedPointerV1::setConfined(bool confined)
316 {
317 if (confined == d_ptr->confined) {
318 return;
319 }
320 d_ptr->confined = confined;
321 d_ptr->update();
322 Q_EMIT confinedChanged();
323 }
324
325 }
326