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