1 /*
2     SPDX-FileCopyrightText: 2021 Roman Gilg <subdiff@gmail.com>
3 
4     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only
5 */
6 #include "layer_shell_v1_p.h"
7 
8 #include "display.h"
9 #include "surface_p.h"
10 #include "wl_output_p.h"
11 #include "xdg_shell_popup.h"
12 
13 namespace Wrapland::Server
14 {
15 
16 const struct zwlr_layer_shell_v1_interface LayerShellV1::Private::s_interface = {
17     cb<getCallback>,
18     cb<destroyCallback>,
19 };
20 
Private(Display * display,LayerShellV1 * qptr)21 LayerShellV1::Private::Private(Display* display, LayerShellV1* qptr)
22     : LayerShellV1Global(qptr, display, &zwlr_layer_shell_v1_interface, &s_interface)
23 {
24     create();
25 }
26 
27 LayerShellV1::Private::~Private() = default;
28 
get_layer(uint32_t layer)29 LayerSurfaceV1::Layer get_layer(uint32_t layer)
30 {
31     switch (layer) {
32     case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM:
33         return Layer::Bottom;
34     case ZWLR_LAYER_SHELL_V1_LAYER_TOP:
35         return Layer::Top;
36     case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY:
37         return Layer::Overlay;
38     case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND:
39     default:
40         return Layer::Background;
41     }
42 }
43 
getCallback(LayerShellV1Bind * bind,uint32_t id,wl_resource * wlSurface,wl_resource * wlOutput,uint32_t wlLayer,char const * nspace)44 void LayerShellV1::Private::getCallback(LayerShellV1Bind* bind,
45                                         uint32_t id,
46                                         wl_resource* wlSurface,
47                                         wl_resource* wlOutput,
48                                         uint32_t wlLayer,
49                                         char const* nspace)
50 {
51     auto surface = Wayland::Resource<Surface>::handle(wlSurface);
52     auto output = wlOutput ? WlOutputGlobal::handle(wlOutput)->output() : nullptr;
53     auto layer = get_layer(wlLayer);
54 
55     if (surface->d_ptr->has_role()) {
56         bind->post_error(ZWLR_LAYER_SHELL_V1_ERROR_ROLE, "Surface already has a role.");
57         return;
58     }
59     if (surface->d_ptr->had_buffer_attached) {
60         bind->post_error(ZWLR_LAYER_SHELL_V1_ERROR_ALREADY_CONSTRUCTED,
61                          "Creation after a buffer was already attached.");
62         return;
63     }
64     if (wlLayer != ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND && layer == Layer::Background) {
65         bind->post_error(ZWLR_LAYER_SHELL_V1_ERROR_INVALID_LAYER, "Invalid layer set.");
66         return;
67     }
68     auto layer_surface = new LayerSurfaceV1(
69         bind->client()->handle(), bind->version(), id, surface, output, layer, std::string(nspace));
70     if (!layer_surface->d_ptr->resource()) {
71         bind->post_no_memory();
72         delete layer_surface;
73         return;
74     }
75     Q_EMIT bind->global()->handle()->surface_created(layer_surface);
76 }
77 
destroyCallback(LayerShellV1Bind * bind)78 void LayerShellV1::Private::destroyCallback([[maybe_unused]] LayerShellV1Bind* bind)
79 {
80     // TODO(romangg): unregister the client?
81 }
82 
LayerShellV1(Display * display,QObject * parent)83 LayerShellV1::LayerShellV1(Display* display, QObject* parent)
84     : QObject(parent)
85     , d_ptr(new Private(display, this))
86 {
87 }
88 
89 LayerShellV1::~LayerShellV1() = default;
90 
91 const struct zwlr_layer_surface_v1_interface LayerSurfaceV1::Private::s_interface = {
92     setSizeCallback,
93     setAnchorCallback,
94     setExclusiveZoneCallback,
95     setMarginCallback,
96     setKeyboardInteractivityCallback,
97     getPopupCallback,
98     ackConfigureCallback,
99     destroyCallback,
100     setLayerCallback,
101 };
102 
Private(Client * client,uint32_t version,uint32_t id,Surface * surface,Output * output,Layer layer,std::string domain,LayerSurfaceV1 * qptr)103 LayerSurfaceV1::Private::Private(Client* client,
104                                  uint32_t version,
105                                  uint32_t id,
106                                  Surface* surface,
107                                  Output* output,
108                                  Layer layer,
109                                  std::string domain,
110                                  LayerSurfaceV1* qptr)
111     : Wayland::Resource<LayerSurfaceV1>(client,
112                                         version,
113                                         id,
114                                         &zwlr_layer_surface_v1_interface,
115                                         &s_interface,
116                                         qptr)
117     , surface{surface}
118     , domain{std::move(domain)}
119 {
120     if (output) {
121         set_output(output);
122     }
123     current.layer = layer;
124     pending.layer = layer;
125 
126     // First commit must be processed.
127     current.set = true;
128     pending.set = true;
129 
130     surface->d_ptr->layer_surface = qptr;
__anona9a30e0c0102null131     QObject::connect(surface, &Surface::resourceDestroyed, qptr, [this] {
132         this->surface = nullptr;
133 
134         // TODO(romangg): do not use that but an extra signal or automatic with surface.
135         Q_EMIT handle()->resourceDestroyed();
136     });
137 }
138 
~Private()139 LayerSurfaceV1::Private::~Private()
140 {
141     if (surface) {
142         surface->d_ptr->layer_surface = nullptr;
143     }
144 }
145 
setSizeCallback(wl_client * wlClient,wl_resource * wlResource,uint32_t width,uint32_t height)146 void LayerSurfaceV1::Private::setSizeCallback([[maybe_unused]] wl_client* wlClient,
147                                               wl_resource* wlResource,
148                                               uint32_t width,
149                                               uint32_t height)
150 {
151     auto priv = handle(wlResource)->d_ptr;
152     priv->pending.size = QSize(static_cast<int>(width), static_cast<int>(height));
153     priv->pending.set = true;
154 }
155 
setAnchorCallback(wl_client * wlClient,wl_resource * wlResource,uint32_t anchor)156 void LayerSurfaceV1::Private::setAnchorCallback([[maybe_unused]] wl_client* wlClient,
157                                                 wl_resource* wlResource,
158                                                 uint32_t anchor)
159 {
160     auto get_anchor = [](auto anchor) {
161         Qt::Edges ret;
162         if (anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) {
163             ret |= Qt::TopEdge;
164         }
165         if (anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) {
166             ret |= Qt::BottomEdge;
167         }
168         if (anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) {
169             ret |= Qt::LeftEdge;
170         }
171         if (anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) {
172             ret |= Qt::RightEdge;
173         }
174         return ret;
175     };
176 
177     auto priv = handle(wlResource)->d_ptr;
178     priv->pending.anchor = get_anchor(anchor);
179     priv->pending.set = true;
180 }
181 
setExclusiveZoneCallback(wl_client * wlClient,wl_resource * wlResource,int zone)182 void LayerSurfaceV1::Private::setExclusiveZoneCallback([[maybe_unused]] wl_client* wlClient,
183                                                        wl_resource* wlResource,
184                                                        int zone)
185 {
186     auto priv = handle(wlResource)->d_ptr;
187     priv->pending.exclusive_zone = zone;
188     priv->pending.set = true;
189 }
190 
setMarginCallback(wl_client * wlClient,wl_resource * wlResource,int top,int right,int bottom,int left)191 void LayerSurfaceV1::Private::setMarginCallback([[maybe_unused]] wl_client* wlClient,
192                                                 wl_resource* wlResource,
193                                                 int top,
194                                                 int right,
195                                                 int bottom,
196                                                 int left)
197 {
198     auto priv = handle(wlResource)->d_ptr;
199     priv->pending.margins = QMargins(left, top, right, bottom);
200     priv->pending.set = true;
201 }
202 
setKeyboardInteractivityCallback(wl_client * wlClient,wl_resource * wlResource,uint32_t interactivity)203 void LayerSurfaceV1::Private::setKeyboardInteractivityCallback([[maybe_unused]] wl_client* wlClient,
204                                                                wl_resource* wlResource,
205                                                                uint32_t interactivity)
206 {
207     auto priv = handle(wlResource)->d_ptr;
208     if (interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) {
209         priv->pending.keyboard_interactivity = KeyboardInteractivity::Exclusive;
210     } else if (interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND) {
211         priv->pending.keyboard_interactivity = KeyboardInteractivity::OnDemand;
212     } else {
213         priv->pending.keyboard_interactivity = KeyboardInteractivity::None;
214     }
215     priv->pending.set = true;
216 }
217 
getPopupCallback(wl_client * wlClient,wl_resource * wlResource,wl_resource * wlPopup)218 void LayerSurfaceV1::Private::getPopupCallback([[maybe_unused]] wl_client* wlClient,
219                                                wl_resource* wlResource,
220                                                wl_resource* wlPopup)
221 {
222     auto priv = handle(wlResource)->d_ptr;
223     auto popup = Wayland::Resource<XdgShellPopup>::handle(wlPopup);
224 
225     if (popup->transientFor()) {
226         // TODO(romangg): * transientFor() does not cover the case where popup was assigned through
227         //                  another request like this get_popup call. On the other side the protocol
228         //                  only talks about it in regards to the creation time anyways.
229         //                * Should we send an error? Protocol doesn't say so.
230         return;
231     }
232     if (popup->surface()->surface()->d_ptr->had_buffer_attached) {
233         // TODO(romangg): Should we send an error? Protocol doesn't say so.
234         return;
235     }
236 
237     // TODO(romangg): check that the popup has a null parent and initial state already set.
238     Q_EMIT priv->handle()->got_popup(popup);
239 }
240 
ackConfigureCallback(wl_client * wlClient,wl_resource * wlResource,uint32_t serial)241 void LayerSurfaceV1::Private::ackConfigureCallback([[maybe_unused]] wl_client* wlClient,
242                                                    wl_resource* wlResource,
243                                                    uint32_t serial)
244 {
245     auto priv = handle(wlResource)->d_ptr;
246 
247     auto& serials = priv->configure_serials;
248 
249     if (std::count(serials.cbegin(), serials.cend(), serial) == 0) {
250         return;
251     }
252 
253     while (!serials.empty()) {
254         auto next = serials.front();
255         serials.pop_front();
256 
257         if (next == serial) {
258             break;
259         }
260     }
261     Q_EMIT priv->handle()->configure_acknowledged(serial);
262 }
263 
setLayerCallback(wl_client * wlClient,wl_resource * wlResource,uint32_t layer)264 void LayerSurfaceV1::Private::setLayerCallback([[maybe_unused]] wl_client* wlClient,
265                                                wl_resource* wlResource,
266                                                uint32_t layer)
267 {
268     auto priv = handle(wlResource)->d_ptr;
269     priv->pending.layer = get_layer(layer);
270     priv->pending.set = true;
271 }
272 
set_output(Output * output)273 void LayerSurfaceV1::Private::set_output(Output* output)
274 {
275     assert(output);
276     this->output = output;
277     connect(output->wayland_output(), &WlOutput::removed, handle(), [this]() {
278         this->output = nullptr;
279         handle()->close();
280         Q_EMIT handle()->resourceDestroyed();
281     });
282 }
283 
commit()284 bool LayerSurfaceV1::Private::commit()
285 {
286     if (closed) {
287         return false;
288     }
289     if (!pending.set) {
290         current.set = false;
291         return true;
292     }
293 
294     if (pending.size.width() == 0) {
295         if (!(pending.anchor & Qt::LeftEdge) || !(pending.anchor & Qt::RightEdge)) {
296             postError(ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SIZE,
297                       "Width zero while not anchoring to both vertical edges.");
298             return false;
299         }
300     }
301     if (pending.size.height() == 0) {
302         if (!(pending.anchor & Qt::TopEdge) || !(pending.anchor & Qt::BottomEdge)) {
303             postError(ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SIZE,
304                       "Height zero while not anchoring to both horizontal edges.");
305             return false;
306         }
307     }
308 
309     current = pending;
310     pending.set = false;
311     return true;
312 }
313 
LayerSurfaceV1(Client * client,uint32_t version,uint32_t id,Surface * surface,Output * output,Layer layer,std::string domain)314 LayerSurfaceV1::LayerSurfaceV1(Client* client,
315                                uint32_t version,
316                                uint32_t id,
317                                Surface* surface,
318                                Output* output,
319                                Layer layer,
320                                std::string domain)
321     : d_ptr(new Private(client, version, id, surface, output, layer, std::move(domain), this))
322 {
323 }
324 
surface() const325 Surface* LayerSurfaceV1::surface() const
326 {
327     return d_ptr->surface;
328 }
329 
output() const330 Output* LayerSurfaceV1::output() const
331 {
332     return d_ptr->output;
333 }
334 
set_output(Output * output)335 void LayerSurfaceV1::set_output(Output* output)
336 {
337     assert(output);
338     assert(!d_ptr->output);
339     d_ptr->set_output(output);
340 }
341 
domain() const342 std::string LayerSurfaceV1::domain() const
343 {
344     return d_ptr->domain;
345 }
346 
size() const347 QSize LayerSurfaceV1::size() const
348 {
349     return d_ptr->current.size;
350 }
351 
anchor() const352 Qt::Edges LayerSurfaceV1::anchor() const
353 {
354     return d_ptr->current.anchor;
355 }
356 
margins() const357 QMargins LayerSurfaceV1::margins() const
358 {
359     auto const anchor = d_ptr->current.anchor;
360     auto margins = d_ptr->current.margins;
361 
362     if (!(anchor & Qt::LeftEdge)) {
363         margins.setLeft(0);
364     }
365     if (!(anchor & Qt::TopEdge)) {
366         margins.setTop(0);
367     }
368     if (!(anchor & Qt::RightEdge)) {
369         margins.setRight(0);
370     }
371     if (!(anchor & Qt::BottomEdge)) {
372         margins.setBottom(0);
373     }
374 
375     return margins;
376 }
377 
layer() const378 LayerSurfaceV1::Layer LayerSurfaceV1::layer() const
379 {
380     return d_ptr->current.layer;
381 }
382 
keyboard_interactivity() const383 LayerSurfaceV1::KeyboardInteractivity LayerSurfaceV1::keyboard_interactivity() const
384 {
385     return d_ptr->current.keyboard_interactivity;
386 }
387 
exclusive_zone() const388 int LayerSurfaceV1::exclusive_zone() const
389 {
390     auto zone = d_ptr->current.exclusive_zone;
391 
392     if (zone <= 0) {
393         return zone;
394     }
395     if (exclusive_edge() == 0) {
396         // By protocol when no exclusive edge is well defined, a positive zone is set to 0.
397         return 0;
398     }
399 
400     return zone;
401 }
402 
exclusive_edge() const403 Qt::Edge LayerSurfaceV1::exclusive_edge() const
404 {
405     auto const& state = d_ptr->current;
406 
407     if (state.exclusive_zone <= 0) {
408         return Qt::Edge();
409     }
410 
411     auto anchor = state.anchor;
412 
413     if (anchor & Qt::TopEdge) {
414         if (anchor & Qt::BottomEdge) {
415             return Qt::Edge();
416         }
417         if (anchor == Qt::TopEdge) {
418             return Qt::TopEdge;
419         }
420         if ((anchor & Qt::LeftEdge) && (anchor & Qt::RightEdge)) {
421             return Qt::TopEdge;
422         }
423         return Qt::Edge();
424     }
425     if (anchor & Qt::BottomEdge) {
426         if (anchor == Qt::BottomEdge) {
427             return Qt::BottomEdge;
428         }
429         if ((anchor & Qt::LeftEdge) && (anchor & Qt::RightEdge)) {
430             return Qt::BottomEdge;
431         }
432     }
433     if (anchor == Qt::LeftEdge) {
434         return Qt::LeftEdge;
435     }
436     if (anchor == Qt::RightEdge) {
437         return Qt::RightEdge;
438     }
439     return Qt::Edge();
440 }
441 
configure(QSize const & size)442 uint32_t LayerSurfaceV1::configure(QSize const& size)
443 {
444     auto serial = d_ptr->client()->display()->handle()->nextSerial();
445     d_ptr->configure_serials.push_back(serial);
446     d_ptr->send<zwlr_layer_surface_v1_send_configure>(serial, size.width(), size.height());
447     return serial;
448 }
449 
close()450 void LayerSurfaceV1::close()
451 {
452     d_ptr->closed = true;
453     d_ptr->send<zwlr_layer_surface_v1_send_closed>();
454 }
455 
change_pending() const456 bool LayerSurfaceV1::change_pending() const
457 {
458     return d_ptr->current.set;
459 }
460 
461 }
462