KWaylandServer

layershell_v1_interface.cpp
1 /*
2  SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <[email protected]>
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_interface.h"
8 #include "display.h"
9 #include "logging.h"
10 #include "surface_interface.h"
11 #include "surfacerole_p.h"
12 #include "xdgshell_interface_p.h"
13 
14 #include <QPointer>
15 #include <QQueue>
16 
17 #include "qwayland-server-wlr-layer-shell-unstable-v1.h"
18 
19 namespace KWaylandServer
20 {
21 static const int s_version = 3;
22 
23 class LayerShellV1InterfacePrivate : public QtWaylandServer::zwlr_layer_shell_v1
24 {
25 public:
26  LayerShellV1InterfacePrivate(LayerShellV1Interface *q, Display *display);
27 
28  LayerShellV1Interface *q;
29  Display *display;
30 
31 protected:
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 
41 class LayerSurfaceV1State
42 {
43 public:
44  LayerSurfaceV1Interface::Layer layer = LayerSurfaceV1Interface::BottomLayer;
45  Qt::Edges anchor;
46  QMargins margins;
47  QSize desiredSize = QSize(0, 0);
48  int exclusiveZone = 0;
49  quint32 acknowledgedConfigure;
50  bool acknowledgedConfigureIsSet = false;
51  bool acceptsFocus = false;
52 };
53 
54 class LayerSurfaceV1InterfacePrivate : public SurfaceRole, public QtWaylandServer::zwlr_layer_surface_v1
55 {
56 public:
57  LayerSurfaceV1InterfacePrivate(LayerSurfaceV1Interface *q, SurfaceInterface *surface);
58 
59  void commit() override;
60 
61  LayerSurfaceV1Interface *q;
62  LayerShellV1Interface *shell;
65  LayerSurfaceV1State current;
66  LayerSurfaceV1State pending;
67  QQueue<quint32> serials;
68  QString scope;
69  bool isClosed = false;
70  bool isConfigured = false;
71  bool isCommitted = false;
72 
73 protected:
74  void zwlr_layer_surface_v1_destroy_resource(Resource *resource) override;
75  void zwlr_layer_surface_v1_set_size(Resource *resource, uint32_t width, uint32_t height) override;
76  void zwlr_layer_surface_v1_set_anchor(Resource *resource, uint32_t anchor) override;
77  void zwlr_layer_surface_v1_set_exclusive_zone(Resource *resource, int32_t zone) override;
78  void zwlr_layer_surface_v1_set_margin(Resource *resource, int32_t top, int32_t right, int32_t bottom, int32_t left) override;
79  void zwlr_layer_surface_v1_set_keyboard_interactivity(Resource *resource, uint32_t keyboard_interactivity) override;
80  void zwlr_layer_surface_v1_get_popup(Resource *resource, struct ::wl_resource *popup) override;
81  void zwlr_layer_surface_v1_ack_configure(Resource *resource, uint32_t serial) override;
82  void zwlr_layer_surface_v1_destroy(Resource *resource) override;
83  void zwlr_layer_surface_v1_set_layer(Resource *resource, uint32_t layer) override;
84 };
85 
86 LayerShellV1InterfacePrivate::LayerShellV1InterfacePrivate(LayerShellV1Interface *q, Display *display)
87  : QtWaylandServer::zwlr_layer_shell_v1(*display, s_version)
88  , q(q)
89  , display(display)
90 {
91 }
92 
93 void LayerShellV1InterfacePrivate::zwlr_layer_shell_v1_get_layer_surface(Resource *resource,
94  uint32_t id,
95  wl_resource *surface_resource,
96  wl_resource *output_resource,
97  uint32_t layer,
98  const QString &scope)
99 {
100  SurfaceInterface *surface = SurfaceInterface::get(surface_resource);
101  OutputInterface *output = OutputInterface::get(output_resource);
102 
103  if (surface->buffer()) {
104  wl_resource_post_error(resource->handle, error_already_constructed, "the wl_surface already has a buffer attached");
105  return;
106  }
107 
108  if (layer > layer_overlay) {
109  wl_resource_post_error(resource->handle, error_invalid_layer, "invalid layer %d", layer);
110  return;
111  }
112 
113  SurfaceRole *surfaceRole = SurfaceRole::get(surface);
114  if (surfaceRole) {
115  wl_resource_post_error(resource->handle, error_role, "the wl_surface already has a role assigned %s", surfaceRole->name().constData());
116  return;
117  }
118 
119  wl_resource *layerSurfaceResource = wl_resource_create(resource->client(), &zwlr_layer_surface_v1_interface, resource->version(), id);
120  if (!layerSurfaceResource) {
121  wl_resource_post_no_memory(resource->handle);
122  return;
123  }
124 
125  auto layerSurface = new LayerSurfaceV1Interface(q, surface, output, LayerSurfaceV1Interface::Layer(layer), scope, layerSurfaceResource);
126  Q_EMIT q->surfaceCreated(layerSurface);
127 }
128 
129 void LayerShellV1InterfacePrivate::zwlr_layer_shell_v1_destroy(Resource *resource)
130 {
131  wl_resource_destroy(resource->handle);
132 }
133 
134 LayerShellV1Interface::LayerShellV1Interface(Display *display, QObject *parent)
135  : QObject(parent)
136  , d(new LayerShellV1InterfacePrivate(this, display))
137 {
138 }
139 
140 LayerShellV1Interface::~LayerShellV1Interface()
141 {
142 }
143 
145 {
146  return d->display;
147 }
148 
149 LayerSurfaceV1InterfacePrivate::LayerSurfaceV1InterfacePrivate(LayerSurfaceV1Interface *q, SurfaceInterface *surface)
150  : SurfaceRole(surface, QByteArrayLiteral("layer_surface_v1"))
151  , q(q)
152  , surface(surface)
153 {
154 }
155 
156 void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_destroy_resource(Resource *resource)
157 {
158  Q_UNUSED(resource)
159  Q_EMIT q->aboutToBeDestroyed();
160  delete q;
161 }
162 
163 void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_size(Resource *resource, uint32_t width, uint32_t height)
164 {
165  Q_UNUSED(resource)
166  pending.desiredSize = QSize(width, height);
167 }
168 
169 void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_anchor(Resource *resource, uint32_t anchor)
170 {
171  const uint32_t anchorMask = anchor_top | anchor_left | anchor_right | anchor_bottom;
172  if (anchor > anchorMask) {
173  wl_resource_post_error(resource->handle, error_invalid_anchor, "invalid anchor %d", anchor);
174  return;
175  }
176 
177  pending.anchor = Qt::Edges();
178 
179  if (anchor & anchor_top) {
180  pending.anchor |= Qt::TopEdge;
181  }
182 
183  if (anchor & anchor_right) {
184  pending.anchor |= Qt::RightEdge;
185  }
186 
187  if (anchor & anchor_bottom) {
188  pending.anchor |= Qt::BottomEdge;
189  }
190 
191  if (anchor & anchor_left) {
192  pending.anchor |= Qt::LeftEdge;
193  }
194 }
195 
196 void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_exclusive_zone(Resource *, int32_t zone)
197 {
198  pending.exclusiveZone = zone;
199 }
200 
201 void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_margin(Resource *, int32_t top, int32_t right, int32_t bottom, int32_t left)
202 {
203  pending.margins = QMargins(left, top, right, bottom);
204 }
205 
206 void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_keyboard_interactivity(Resource *resource, uint32_t keyboard_interactivity)
207 {
208  Q_UNUSED(resource)
209  pending.acceptsFocus = keyboard_interactivity;
210 }
211 
212 void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_get_popup(Resource *resource, struct ::wl_resource *popup_resource)
213 {
214  XdgPopupInterface *popup = XdgPopupInterface::get(popup_resource);
215  XdgPopupInterfacePrivate *popupPrivate = XdgPopupInterfacePrivate::get(popup);
216 
217  if (popup->isConfigured()) {
218  wl_resource_post_error(resource->handle, error_invalid_surface_state, "xdg_popup surface is already configured");
219  return;
220  }
221 
222  popupPrivate->parentSurface = surface;
223 }
224 
225 void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_ack_configure(Resource *resource, uint32_t serial)
226 {
227  if (!serials.contains(serial)) {
228  wl_resource_post_error(resource->handle, error_invalid_surface_state, "invalid configure serial %d", serial);
229  return;
230  }
231  while (!serials.isEmpty()) {
232  const quint32 head = serials.takeFirst();
233  if (head == serial) {
234  break;
235  }
236  }
237  if (!isClosed) {
238  pending.acknowledgedConfigure = serial;
239  pending.acknowledgedConfigureIsSet = true;
240  }
241 }
242 
243 void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_destroy(Resource *resource)
244 {
245  wl_resource_destroy(resource->handle);
246 }
247 
248 void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_layer(Resource *resource, uint32_t layer)
249 {
250  if (Q_UNLIKELY(layer > LayerShellV1InterfacePrivate::layer_overlay)) {
251  wl_resource_post_error(resource->handle, LayerShellV1InterfacePrivate::error_invalid_layer, "invalid layer %d", layer);
252  return;
253  }
254  pending.layer = LayerSurfaceV1Interface::Layer(layer);
255 }
256 
257 void LayerSurfaceV1InterfacePrivate::commit()
258 {
259  if (isClosed) {
260  return;
261  }
262 
263  if (pending.acknowledgedConfigureIsSet) {
264  current.acknowledgedConfigure = pending.acknowledgedConfigure;
265  pending.acknowledgedConfigureIsSet = false;
266  Q_EMIT q->configureAcknowledged(pending.acknowledgedConfigure);
267  }
268 
269  if (Q_UNLIKELY(surface->isMapped() && !isConfigured)) {
270  wl_resource_post_error(resource()->handle,
271  error_invalid_surface_state,
272  "a buffer has been attached to a layer surface prior "
273  "to the first layer_surface.configure event");
274  return;
275  }
276 
277  if (Q_UNLIKELY(pending.desiredSize.width() == 0 && (!(pending.anchor & Qt::LeftEdge) || !(pending.anchor & Qt::RightEdge)))) {
278  wl_resource_post_error(resource()->handle,
279  error_invalid_size,
280  "the layer surface has a width of 0 but its anchor "
281  "doesn't include the left and the right screen edge");
282  return;
283  }
284 
285  if (Q_UNLIKELY(pending.desiredSize.height() == 0 && (!(pending.anchor & Qt::TopEdge) || !(pending.anchor & Qt::BottomEdge)))) {
286  wl_resource_post_error(resource()->handle,
287  error_invalid_size,
288  "the layer surface has a height of 0 but its anchor "
289  "doesn't include the top and the bottom screen edge");
290  return;
291  }
292 
293  if (!surface->isMapped() && isCommitted) {
294  isCommitted = false;
295  isConfigured = false;
296 
297  current = LayerSurfaceV1State();
298  pending = LayerSurfaceV1State();
299 
300  return;
301  }
302 
303  const LayerSurfaceV1State previous = std::exchange(current, pending);
304 
305  isCommitted = true; // Must set the committed state before emitting any signals.
306 
307  if (previous.acceptsFocus != current.acceptsFocus) {
308  Q_EMIT q->acceptsFocusChanged();
309  }
310  if (previous.layer != current.layer) {
311  Q_EMIT q->layerChanged();
312  }
313  if (previous.anchor != current.anchor) {
314  Q_EMIT q->anchorChanged();
315  }
316  if (previous.desiredSize != current.desiredSize) {
317  Q_EMIT q->desiredSizeChanged();
318  }
319  if (previous.exclusiveZone != current.exclusiveZone) {
320  Q_EMIT q->exclusiveZoneChanged();
321  }
322  if (previous.margins != current.margins) {
323  Q_EMIT q->marginsChanged();
324  }
325 }
326 
327 LayerSurfaceV1Interface::LayerSurfaceV1Interface(LayerShellV1Interface *shell,
328  SurfaceInterface *surface,
329  OutputInterface *output,
330  Layer layer,
331  const QString &scope,
332  wl_resource *resource)
333  : d(new LayerSurfaceV1InterfacePrivate(this, surface))
334 {
335  d->current.layer = layer;
336  d->pending.layer = layer;
337 
338  d->shell = shell;
339  d->output = output;
340  d->scope = scope;
341 
342  d->init(resource);
343 }
344 
345 LayerSurfaceV1Interface::~LayerSurfaceV1Interface()
346 {
347 }
348 
350 {
351  return d->isCommitted;
352 }
353 
355 {
356  return d->surface;
357 }
358 
360 {
361  return d->current.anchor;
362 }
363 
365 {
366  return d->current.desiredSize;
367 }
368 
370 {
371  return d->current.acceptsFocus;
372 }
373 
374 LayerSurfaceV1Interface::Layer LayerSurfaceV1Interface::layer() const
375 {
376  return d->current.layer;
377 }
378 
380 {
381  return d->current.margins;
382 }
383 
385 {
386  return d->current.margins.left();
387 }
388 
390 {
391  return d->current.margins.top();
392 }
393 
395 {
396  return d->current.margins.right();
397 }
398 
400 {
401  return d->current.margins.bottom();
402 }
403 
405 {
406  return d->current.exclusiveZone;
407 }
408 
410 {
411  if (exclusiveZone() <= 0) {
412  return Qt::Edge();
413  }
414  if (anchor() == (Qt::LeftEdge | Qt::TopEdge | Qt::RightEdge) || anchor() == Qt::TopEdge) {
415  return Qt::TopEdge;
416  }
417  if (anchor() == (Qt::TopEdge | Qt::RightEdge | Qt::BottomEdge) || anchor() == Qt::RightEdge) {
418  return Qt::RightEdge;
419  }
420  if (anchor() == (Qt::RightEdge | Qt::BottomEdge | Qt::LeftEdge) || anchor() == Qt::BottomEdge) {
421  return Qt::BottomEdge;
422  }
423  if (anchor() == (Qt::BottomEdge | Qt::LeftEdge | Qt::TopEdge) || anchor() == Qt::LeftEdge) {
424  return Qt::LeftEdge;
425  }
426  return Qt::Edge();
427 }
428 
430 {
431  return d->output;
432 }
433 
435 {
436  return d->scope;
437 }
438 
440 {
441  if (d->isClosed) {
442  qCWarning(KWAYLAND_SERVER) << "Cannot configure a closed layer shell surface";
443  return 0;
444  }
445 
446  const uint32_t serial = d->shell->display()->nextSerial();
447  d->serials << serial;
448 
449  d->send_configure(serial, size.width(), size.height());
450  d->isConfigured = true;
451 
452  return serial;
453 }
454 
456 {
457  if (!d->isClosed) {
458  d->send_closed();
459  d->isClosed = true;
460  }
461 }
462 
463 } // namespace KWaylandServer
QSize desiredSize() const
Returns the desired size for this layer shell surface, in the surface-local coordinates.
The LayerSurfaceV1Interface class represents a desktop shell surface, e.g.
QMargins margins() const
Returns the margins object that indicates the distance between an anchor edge and the corresponding s...
int width() const const
The OutputInterface class represents a screen.
static XdgPopupInterface * get(::wl_resource *resource)
Returns the XdgPopupInterface for the specified wayland resource object resource. ...
quint32 sendConfigure(const QSize &size)
Sends a configure event to the client.
QString scope() const
Returns the scope of this layer surface.
typedef Edges
Class holding the Wayland server display loop.
Definition: display.h:47
The XdgPopupInterface class represents a surface that can be used to implement context menus...
int topMargin() const
Returns the value of the top margin.
bool acceptsFocus() const
Returns true if the surface accepts keyboard input; otherwise returns false.
Layer layer() const
Returns the stacking order layer where this layer surface has to be rendered.
Qt::Edges anchor() const
Returns the anchor point relative to which the surface will be positioned.
Qt::Edge exclusiveEdge() const
If the exclusive zone is positive, this function returns the corresponding exclusive anchor edge...
bool isCommitted() const
Returns true if the initial commit has been performed; otherwise returns false.
int rightMargin() const
Returns the value of the right margin.
Display * display() const
Returns the Wayland display for the layer shell compositor extension.
int leftMargin() const
Returns the value of the left margin.
static SurfaceInterface * get(wl_resource *native)
void sendClosed()
Sends a closed event to the client.
int bottomMargin() const
Returns the value of the bottom margin.
bool isConfigured() const
Returns true if the popup has been configured; otherwise returns false.
int exclusiveZone() const
Returns the distance from the anchor edge that should not be occluded.
bool isMapped() const
Whether the SurfaceInterface is currently considered to be mapped.
The LayerShellV1Interface compositor extension allows to create desktop shell surfaces.
SurfaceInterface * surface() const
Returns the underlying Wayland surface for this layer shell surface.
OutputInterface * output() const
Returns the output where the surface wants to be displayed.
int height() const const
Resource representing a wl_surface.
QObject * parent() const const
Q_EMITQ_EMIT
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Oct 23 2021 23:08:27 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.