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