KWin
Loading...
Searching...
No Matches
layershell_v1.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
7#include "layershell_v1.h"
8#include "display.h"
9#include "output.h"
10#include "surface.h"
11#include "utils/common.h"
12#include "xdgshell_p.h"
13
14#include <QPointer>
15#include <QQueue>
16
17#include "qwayland-server-wlr-layer-shell-unstable-v1.h"
18
19namespace KWin
20{
21static const int s_version = 5;
22
23class LayerShellV1InterfacePrivate : public QtWaylandServer::zwlr_layer_shell_v1
24{
25public:
27
30
31protected:
32 void zwlr_layer_shell_v1_get_layer_surface(Resource *resource,
33 uint32_t id,
34 struct ::wl_resource *surface_resource,
35 struct ::wl_resource *output_resource,
36 uint32_t layer,
37 const QString &scope) override;
38 void zwlr_layer_shell_v1_destroy(Resource *resource) override;
39};
40
42{
43 std::optional<LayerSurfaceV1Interface::Layer> layer;
44 std::optional<Qt::Edges> anchor;
45 std::optional<QMargins> margins;
46 std::optional<QSize> desiredSize;
47 std::optional<int> exclusiveZone;
48 std::optional<Qt::Edge> exclusiveEdge;
49 std::optional<quint32> acknowledgedConfigure;
50 std::optional<bool> acceptsFocus;
51};
52
54{
55 QQueue<quint32> serials;
57 Qt::Edges anchor;
58 QMargins margins;
59 QSize desiredSize = QSize(0, 0);
61 Qt::Edge exclusiveEdge = Qt::Edge();
62 bool acceptsFocus = false;
63 bool configured = false;
64 bool closed = false;
65 bool committed = false;
66 bool firstBufferAttached = false;
67};
68
69class LayerSurfaceV1InterfacePrivate : public SurfaceExtension<LayerSurfaceV1Commit>, public QtWaylandServer::zwlr_layer_surface_v1
70{
71public:
73
74 void apply(LayerSurfaceV1Commit *commit) override;
75
78 QPointer<SurfaceInterface> surface;
79 QPointer<OutputInterface> output;
80 QString scope;
82
83protected:
84 void zwlr_layer_surface_v1_destroy_resource(Resource *resource) override;
85 void zwlr_layer_surface_v1_set_size(Resource *resource, uint32_t width, uint32_t height) override;
86 void zwlr_layer_surface_v1_set_anchor(Resource *resource, uint32_t anchor) override;
87 void zwlr_layer_surface_v1_set_exclusive_edge(Resource *resource, uint32_t edge) override;
88 void zwlr_layer_surface_v1_set_exclusive_zone(Resource *resource, int32_t zone) override;
89 void zwlr_layer_surface_v1_set_margin(Resource *resource, int32_t top, int32_t right, int32_t bottom, int32_t left) override;
90 void zwlr_layer_surface_v1_set_keyboard_interactivity(Resource *resource, uint32_t keyboard_interactivity) override;
91 void zwlr_layer_surface_v1_get_popup(Resource *resource, struct ::wl_resource *popup) override;
92 void zwlr_layer_surface_v1_ack_configure(Resource *resource, uint32_t serial) override;
93 void zwlr_layer_surface_v1_destroy(Resource *resource) override;
94 void zwlr_layer_surface_v1_set_layer(Resource *resource, uint32_t layer) override;
95};
96
98 : QtWaylandServer::zwlr_layer_shell_v1(*display, s_version)
99 , q(q)
100 , display(display)
101{
102}
103
105 uint32_t id,
106 wl_resource *surface_resource,
107 wl_resource *output_resource,
108 uint32_t layer,
109 const QString &scope)
110{
111 SurfaceInterface *surface = SurfaceInterface::get(surface_resource);
112 OutputInterface *output = OutputInterface::get(output_resource);
113
114 if (surface->buffer()) {
115 wl_resource_post_error(resource->handle, error_already_constructed, "the wl_surface already has a buffer attached");
116 return;
117 }
118
119 if (layer > layer_overlay) {
120 wl_resource_post_error(resource->handle, error_invalid_layer, "invalid layer %d", layer);
121 return;
122 }
123
124 if (const SurfaceRole *role = surface->role()) {
125 if (role != LayerSurfaceV1Interface::role()) {
126 wl_resource_post_error(resource->handle, error_role,
127 "the wl_surface already has a role assigned %s", role->name().constData());
128 return;
129 }
130 } else {
132 }
133
134 wl_resource *layerSurfaceResource = wl_resource_create(resource->client(), &zwlr_layer_surface_v1_interface, resource->version(), id);
135 if (!layerSurfaceResource) {
136 wl_resource_post_no_memory(resource->handle);
137 return;
138 }
139
140 auto layerSurface = new LayerSurfaceV1Interface(q, surface, output, LayerSurfaceV1Interface::Layer(layer), scope, layerSurfaceResource);
141 Q_EMIT q->surfaceCreated(layerSurface);
142}
143
145{
146 wl_resource_destroy(resource->handle);
147}
148
150 : QObject(parent)
151 , d(new LayerShellV1InterfacePrivate(this, display))
152{
153}
154
158
160{
161 return d->display;
162}
163
170
172{
173 Q_EMIT q->aboutToBeDestroyed();
174 delete q;
175}
176
177void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_size(Resource *resource, uint32_t width, uint32_t height)
178{
179 pending.desiredSize = QSize(width, height);
180}
181
183{
184 const uint32_t anchorMask = anchor_top | anchor_left | anchor_right | anchor_bottom;
185 if (anchor > anchorMask) {
186 wl_resource_post_error(resource->handle, error_invalid_anchor, "invalid anchor %d", anchor);
187 return;
188 }
189
190 pending.anchor = Qt::Edges();
191
192 if (anchor & anchor_top) {
193 *pending.anchor |= Qt::TopEdge;
194 }
195
196 if (anchor & anchor_right) {
197 *pending.anchor |= Qt::RightEdge;
198 }
199
200 if (anchor & anchor_bottom) {
201 *pending.anchor |= Qt::BottomEdge;
202 }
203
204 if (anchor & anchor_left) {
205 *pending.anchor |= Qt::LeftEdge;
206 }
207}
208
210{
211 if (!edge) {
212 pending.exclusiveEdge = Qt::Edge();
213 } else if (edge == anchor_top) {
214 pending.exclusiveEdge = Qt::TopEdge;
215 } else if (edge == anchor_right) {
216 pending.exclusiveEdge = Qt::RightEdge;
217 } else if (edge == anchor_bottom) {
218 pending.exclusiveEdge = Qt::BottomEdge;
219 } else if (edge == anchor_left) {
220 pending.exclusiveEdge = Qt::LeftEdge;
221 } else {
222 wl_resource_post_error(resource->handle, error_invalid_exclusive_edge, "Invalid exclusive edge: %d", edge);
223 }
224}
225
230
231void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_margin(Resource *, int32_t top, int32_t right, int32_t bottom, int32_t left)
232{
233 pending.margins = QMargins(left, top, right, bottom);
234}
235
236void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_keyboard_interactivity(Resource *resource, uint32_t keyboard_interactivity)
237{
238 pending.acceptsFocus = keyboard_interactivity;
239}
240
241void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_get_popup(Resource *resource, struct ::wl_resource *popup_resource)
242{
243 XdgPopupInterface *popup = XdgPopupInterface::get(popup_resource);
245
246 if (popup->isConfigured()) {
247 wl_resource_post_error(resource->handle, error_invalid_surface_state, "xdg_popup surface is already configured");
248 return;
249 }
250
251 popupPrivate->parentSurface = surface;
252}
253
255{
256 if (!state.serials.contains(serial)) {
257 wl_resource_post_error(resource->handle, error_invalid_surface_state, "invalid configure serial %d", serial);
258 return;
259 }
260 while (!state.serials.isEmpty()) {
261 const quint32 head = state.serials.takeFirst();
262 if (head == serial) {
263 break;
264 }
265 }
266 if (!state.closed) {
268 }
269}
270
272{
273 wl_resource_destroy(resource->handle);
274}
275
277{
278 if (Q_UNLIKELY(layer > LayerShellV1InterfacePrivate::layer_overlay)) {
279 wl_resource_post_error(resource->handle, LayerShellV1InterfacePrivate::error_invalid_layer, "invalid layer %d", layer);
280 return;
281 }
283}
284
286{
287 if (state.closed) {
288 return;
289 }
290
291 if (commit->acknowledgedConfigure.has_value()) {
292 Q_EMIT q->configureAcknowledged(commit->acknowledgedConfigure.value());
293 }
294
295 if (Q_UNLIKELY(surface->isMapped() && !state.configured)) {
296 wl_resource_post_error(resource()->handle,
297 error_invalid_surface_state,
298 "a buffer has been attached to a layer surface prior "
299 "to the first layer_surface.configure event");
300 return;
301 }
302
303 if (commit->desiredSize && commit->desiredSize->width() == 0) {
304 const Qt::Edges anchor = commit->anchor.value_or(state.anchor);
305 if (!(anchor & Qt::LeftEdge) || !(anchor & Qt::RightEdge)) {
306 wl_resource_post_error(resource()->handle,
307 error_invalid_size,
308 "the layer surface has a width of 0 but its anchor "
309 "doesn't include the left and the right screen edge");
310 return;
311 }
312 }
313
314 if (commit->desiredSize && commit->desiredSize->height() == 0) {
315 const Qt::Edges anchor = commit->anchor.value_or(state.anchor);
316 if (!(anchor & Qt::TopEdge) || !(anchor & Qt::BottomEdge)) {
317 wl_resource_post_error(resource()->handle,
318 error_invalid_size,
319 "the layer surface has a height of 0 but its anchor "
320 "doesn't include the top and the bottom screen edge");
321 return;
322 }
323 }
324
325 if (commit->exclusiveEdge.has_value() || commit->anchor.has_value()) {
326 const quint32 exclusiveEdge = commit->exclusiveEdge.value_or(state.exclusiveEdge);
327 const quint32 anchor = commit->anchor.value_or(state.anchor);
328 if (exclusiveEdge && !(exclusiveEdge & anchor)) {
329 wl_resource_post_error(resource()->handle, error_invalid_exclusive_edge, "Exclusive edge is not of the anchors");
330 return;
331 }
332 }
333
334 // detect reset
335 if (!surface->isMapped() && state.firstBufferAttached) {
338 stashed.clear();
339
340 return;
341 }
342
343 const LayerSurfaceV1State previous = state;
344
345 state.committed = true; // Must set the committed state before emitting any signals.
346 if (surface->isMapped()) {
348 }
349
350 if (commit->layer.has_value()) {
351 state.layer = commit->layer.value();
352 }
353 if (commit->anchor.has_value()) {
354 state.anchor = commit->anchor.value();
355 }
356 if (commit->margins.has_value()) {
357 state.margins = commit->margins.value();
358 }
359 if (commit->desiredSize.has_value()) {
360 state.desiredSize = commit->desiredSize.value();
361 }
362 if (commit->exclusiveZone.has_value()) {
363 state.exclusiveZone = commit->exclusiveZone.value();
364 }
365 if (commit->exclusiveEdge.has_value()) {
366 state.exclusiveEdge = commit->exclusiveEdge.value();
367 }
368
369 if (commit->acceptsFocus.has_value()) {
370 state.acceptsFocus = commit->acceptsFocus.value();
371 }
372
373 if (previous.acceptsFocus != state.acceptsFocus) {
374 Q_EMIT q->acceptsFocusChanged();
375 }
376 if (previous.layer != state.layer) {
377 Q_EMIT q->layerChanged();
378 }
379 if (previous.anchor != state.anchor) {
380 Q_EMIT q->anchorChanged();
381 }
382 if (previous.desiredSize != state.desiredSize) {
383 Q_EMIT q->desiredSizeChanged();
384 }
385 if (previous.exclusiveZone != state.exclusiveZone) {
386 Q_EMIT q->exclusiveZoneChanged();
387 }
388 if (previous.margins != state.margins) {
389 Q_EMIT q->marginsChanged();
390 }
391}
392
394 SurfaceInterface *surface,
395 OutputInterface *output,
396 Layer layer,
397 const QString &scope,
398 wl_resource *resource)
399 : d(new LayerSurfaceV1InterfacePrivate(this, surface))
400{
401 d->state.layer = layer;
402
403 d->shell = shell;
404 d->output = output;
405 d->scope = scope;
406
407 d->init(resource);
408}
409
413
415{
416 static SurfaceRole role(QByteArrayLiteral("layer_surface_v1"));
417 return &role;
418}
419
421{
422 return d->state.committed;
423}
424
426{
427 return d->surface;
428}
429
431{
432 return d->state.anchor;
433}
434
436{
437 return d->state.desiredSize;
438}
439
441{
442 return d->state.acceptsFocus;
443}
444
446{
447 return d->state.layer;
448}
449
451{
452 return d->state.margins;
453}
454
456{
457 return d->state.margins.left();
458}
459
461{
462 return d->state.margins.top();
463}
464
466{
467 return d->state.margins.right();
468}
469
471{
472 return d->state.margins.bottom();
473}
474
476{
477 return d->state.exclusiveZone;
478}
479
481{
482 if (exclusiveZone() <= 0) {
483 return Qt::Edge();
484 }
485
486 if (d->state.exclusiveEdge) {
487 return d->state.exclusiveEdge;
488 }
489
490 if (anchor() == (Qt::LeftEdge | Qt::TopEdge | Qt::RightEdge) || anchor() == Qt::TopEdge) {
491 return Qt::TopEdge;
492 }
493 if (anchor() == (Qt::TopEdge | Qt::RightEdge | Qt::BottomEdge) || anchor() == Qt::RightEdge) {
494 return Qt::RightEdge;
495 }
496 if (anchor() == (Qt::RightEdge | Qt::BottomEdge | Qt::LeftEdge) || anchor() == Qt::BottomEdge) {
497 return Qt::BottomEdge;
498 }
499 if (anchor() == (Qt::BottomEdge | Qt::LeftEdge | Qt::TopEdge) || anchor() == Qt::LeftEdge) {
500 return Qt::LeftEdge;
501 }
502 return Qt::Edge();
503}
504
506{
507 return d->output;
508}
509
511{
512 return d->scope;
513}
514
516{
517 if (d->state.closed) {
518 qCWarning(KWIN_CORE) << "Cannot configure a closed layer shell surface";
519 return 0;
520 }
521
522 const uint32_t serial = d->shell->display()->nextSerial();
523 d->state.serials << serial;
524
525 d->send_configure(serial, size.width(), size.height());
526 d->state.configured = true;
527
528 return serial;
529}
530
532{
533 if (!d->state.closed) {
534 d->send_closed();
535 d->state.closed = true;
536 }
537}
538
539} // namespace KWin
540
541#include "moc_layershell_v1.cpp"
Class holding the Wayland server display loop.
Definition display.h:34
LayerShellV1Interface(Display *display, QObject *parent=nullptr)
void surfaceCreated(LayerSurfaceV1Interface *surface)
LayerShellV1InterfacePrivate(LayerShellV1Interface *q, Display *display)
void zwlr_layer_shell_v1_get_layer_surface(Resource *resource, uint32_t id, struct ::wl_resource *surface_resource, struct ::wl_resource *output_resource, uint32_t layer, const QString &scope) override
void zwlr_layer_shell_v1_destroy(Resource *resource) override
LayerSurfaceV1Interface(LayerShellV1Interface *shell, SurfaceInterface *surface, OutputInterface *output, Layer layer, const QString &scope, wl_resource *resource)
void configureAcknowledged(quint32 serial)
static SurfaceRole * role()
SurfaceInterface * surface() const
OutputInterface * output() const
quint32 sendConfigure(const QSize &size)
void zwlr_layer_surface_v1_set_exclusive_edge(Resource *resource, uint32_t edge) override
void zwlr_layer_surface_v1_set_layer(Resource *resource, uint32_t layer) override
void apply(LayerSurfaceV1Commit *commit) override
LayerSurfaceV1InterfacePrivate(LayerSurfaceV1Interface *q, SurfaceInterface *surface)
void zwlr_layer_surface_v1_ack_configure(Resource *resource, uint32_t serial) override
void zwlr_layer_surface_v1_set_keyboard_interactivity(Resource *resource, uint32_t keyboard_interactivity) override
void zwlr_layer_surface_v1_set_margin(Resource *resource, int32_t top, int32_t right, int32_t bottom, int32_t left) override
void zwlr_layer_surface_v1_destroy(Resource *resource) override
void zwlr_layer_surface_v1_set_size(Resource *resource, uint32_t width, uint32_t height) override
void zwlr_layer_surface_v1_set_anchor(Resource *resource, uint32_t anchor) override
void zwlr_layer_surface_v1_set_exclusive_zone(Resource *resource, int32_t zone) override
void zwlr_layer_surface_v1_destroy_resource(Resource *resource) override
QPointer< SurfaceInterface > surface
QPointer< OutputInterface > output
void zwlr_layer_surface_v1_get_popup(Resource *resource, struct ::wl_resource *popup) override
static OutputInterface * get(wl_resource *native)
Definition output.cpp:320
QMap< quint32, LayerSurfaceV1Commit > stashed
Definition surface.h:472
Resource representing a wl_surface.
Definition surface.h:80
static SurfaceInterface * get(wl_resource *native)
Definition surface.cpp:819
GraphicsBuffer * buffer() const
Definition surface.cpp:809
SurfaceRole * role() const
Definition surface.cpp:429
void setRole(SurfaceRole *role)
Definition surface.cpp:434
static XdgPopupInterface * get(::wl_resource *resource)
Definition xdgshell.cpp:835
bool isConfigured() const
Definition xdgshell.cpp:801
static XdgPopupInterfacePrivate * get(XdgPopupInterface *popup)
Definition xdgshell.cpp:686
SurfaceInterface * parentSurface
Definition xdgshell_p.h:183
std::optional< QMargins > margins
std::optional< int > exclusiveZone
std::optional< QSize > desiredSize
std::optional< Qt::Edges > anchor
std::optional< bool > acceptsFocus
std::optional< LayerSurfaceV1Interface::Layer > layer
std::optional< quint32 > acknowledgedConfigure
std::optional< Qt::Edge > exclusiveEdge
QQueue< quint32 > serials
LayerSurfaceV1Interface::Layer layer