KWayland

xdgshell_v5_interface.cpp
1 /*
2  SPDX-FileCopyrightText: 2016 Martin Gräßlin <[email protected]>
3  SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <[email protected]>
4 
5  SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6 */
7 #include "display.h"
8 #include "generic_shell_surface_p.h"
9 #include "global_p.h"
10 #include "output_interface.h"
11 #include "resource_p.h"
12 #include "seat_interface.h"
13 #include "surface_interface.h"
14 #include "xdgshell_interface_p.h"
15 #include "xdgshell_v5_interface_p.h"
16 
17 #include "../compat/wayland-xdg-shell-v5-server-protocol.h"
18 
19 namespace KWayland
20 {
21 namespace Server
22 {
23 class XdgShellV5Interface::Private : public XdgShellInterface::Private
24 {
25 public:
26  Private(XdgShellV5Interface *q, Display *d);
27 
29 
30 private:
31  void createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource);
32  void createPopup(wl_client *client,
33  uint32_t version,
34  uint32_t id,
35  SurfaceInterface *surface,
36  SurfaceInterface *parent,
37  SeatInterface *seat,
38  quint32 serial,
39  const QPoint &pos,
40  wl_resource *parentResource);
41  void bind(wl_client *client, uint32_t version, uint32_t id) override;
42  quint32 ping(XdgShellSurfaceInterface *surface) override;
43 
44  static void unbind(wl_resource *resource);
45  static Private *cast(wl_resource *r)
46  {
47  return reinterpret_cast<Private *>(wl_resource_get_user_data(r));
48  }
49 
51 
52  static void destroyCallback(wl_client *client, wl_resource *resource);
53  static void useUnstableVersionCallback(wl_client *client, wl_resource *resource, int32_t version);
54  static void getXdgSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface);
55  static void getXdgPopupCallback(wl_client *client,
56  wl_resource *resource,
57  uint32_t id,
58  wl_resource *surface,
59  wl_resource *parent,
60  wl_resource *seat,
61  uint32_t serial,
62  int32_t x,
63  int32_t y);
64  static void pongCallback(wl_client *client, wl_resource *resource, uint32_t serial);
65 
66  XdgShellV5Interface *q;
67  static const struct zxdg_shell_v5_interface s_interface;
68  static const quint32 s_version;
69 };
70 
71 class XdgPopupV5Interface::Private : public XdgShellPopupInterface::Private
72 {
73 public:
74  Private(XdgPopupV5Interface *q, XdgShellV5Interface *c, SurfaceInterface *surface, wl_resource *parentResource);
75  ~Private() override;
76 
77  QRect windowGeometry() const override;
78  void commit() override;
79  void popupDone() override;
80 
81  XdgPopupV5Interface *q_func()
82  {
83  return reinterpret_cast<XdgPopupV5Interface *>(q);
84  }
85 
86 private:
87  static const struct zxdg_popup_v5_interface s_interface;
88 };
89 
90 const quint32 XdgShellV5Interface::Private::s_version = 1;
91 
92 #ifndef K_DOXYGEN
93 const struct zxdg_shell_v5_interface XdgShellV5Interface::Private::s_interface = {destroyCallback,
94  useUnstableVersionCallback,
95  getXdgSurfaceCallback,
96  getXdgPopupCallback,
97  pongCallback};
98 #endif
99 
100 void XdgShellV5Interface::Private::destroyCallback(wl_client *client, wl_resource *resource)
101 {
102  Q_UNUSED(client)
103  // TODO: send protocol error if there are still surfaces mapped
104  wl_resource_destroy(resource);
105 }
106 
107 void XdgShellV5Interface::Private::useUnstableVersionCallback(wl_client *client, wl_resource *resource, int32_t version)
108 {
109  Q_UNUSED(client)
110  Q_UNUSED(resource)
111  Q_UNUSED(version)
112  // TODO: implement
113 }
114 
115 void XdgShellV5Interface::Private::getXdgSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface)
116 {
117  auto s = cast(resource);
118  s->createSurface(client, wl_resource_get_version(resource), id, SurfaceInterface::get(surface), resource);
119 }
120 
121 void XdgShellV5Interface::Private::createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource)
122 {
123  auto it = std::find_if(surfaces.constBegin(), surfaces.constEnd(), [surface](XdgSurfaceV5Interface *s) {
124  return surface == s->surface();
125  });
126  if (it != surfaces.constEnd()) {
127  wl_resource_post_error(surface->resource(), ZXDG_SHELL_V5_ERROR_ROLE, "ShellSurface already created");
128  return;
129  }
130  XdgSurfaceV5Interface *shellSurface = new XdgSurfaceV5Interface(q, surface, parentResource);
131  surfaces << shellSurface;
132  QObject::connect(shellSurface, &XdgSurfaceV5Interface::destroyed, q, [this, shellSurface] {
133  surfaces.removeAll(shellSurface);
134  });
135  shellSurface->d->create(display->getConnection(client), version, id);
136  Q_EMIT q->surfaceCreated(shellSurface);
137 }
138 
139 void XdgShellV5Interface::Private::getXdgPopupCallback(wl_client *client,
140  wl_resource *resource,
141  uint32_t id,
142  wl_resource *surface,
143  wl_resource *parent,
144  wl_resource *seat,
145  uint32_t serial,
146  int32_t x,
147  int32_t y)
148 {
149  auto s = cast(resource);
150  s->createPopup(client,
151  wl_resource_get_version(resource),
152  id,
153  SurfaceInterface::get(surface),
154  SurfaceInterface::get(parent),
155  SeatInterface::get(seat),
156  serial,
157  QPoint(x, y),
158  resource);
159 }
160 
161 void XdgShellV5Interface::Private::createPopup(wl_client *client,
162  uint32_t version,
163  uint32_t id,
164  SurfaceInterface *surface,
165  SurfaceInterface *parent,
166  SeatInterface *seat,
167  quint32 serial,
168  const QPoint &pos,
169  wl_resource *parentResource)
170 {
171  XdgPopupV5Interface *popupSurface = new XdgPopupV5Interface(q, surface, parentResource);
172  auto d = popupSurface->d_func();
173  d->parent = QPointer<SurfaceInterface>(parent);
174  d->anchorRect = QRect(pos, QSize(0, 0));
175  // default open like a normal popup
176  d->anchorEdge = Qt::BottomEdge;
177  d->gravity = Qt::TopEdge;
178  d->create(display->getConnection(client), version, id);
179 
180  // compat
181  Q_EMIT q->popupCreated(popupSurface, seat, serial);
182 
183  // new system
184  Q_EMIT q->xdgPopupCreated(popupSurface);
185  Q_EMIT popupSurface->grabRequested(seat, serial);
186 }
187 
188 void XdgShellV5Interface::Private::pongCallback(wl_client *client, wl_resource *resource, uint32_t serial)
189 {
190  Q_UNUSED(client)
191  auto s = cast(resource);
192  auto timerIt = s->pingTimers.find(serial);
193  if (timerIt != s->pingTimers.end() && timerIt.value()->isActive()) {
194  delete timerIt.value();
195  s->pingTimers.erase(timerIt);
196  Q_EMIT s->q->pongReceived(serial);
197  }
198 }
199 
200 XdgShellV5Interface::Private::Private(XdgShellV5Interface *q, Display *d)
201  : XdgShellInterface::Private(XdgShellInterfaceVersion::UnstableV5, q, d, &zxdg_shell_v5_interface, s_version)
202  , q(q)
203 {
204 }
205 
206 void XdgShellV5Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id)
207 {
208  auto c = display->getConnection(client);
209  auto resource = c->createResource(&zxdg_shell_v5_interface, qMin(version, s_version), id);
210  if (!resource) {
211  wl_client_post_no_memory(client);
212  return;
213  }
214  resources[client] = resource;
215  wl_resource_set_implementation(resource, &s_interface, this, unbind);
216 }
217 
218 void XdgShellV5Interface::Private::unbind(wl_resource *resource)
219 {
220  auto s = cast(resource);
221  auto client = wl_resource_get_client(resource);
222  s->resources.remove(client);
223 }
224 
225 XdgSurfaceV5Interface *XdgShellV5Interface::getSurface(wl_resource *resource)
226 {
227  if (!resource) {
228  return nullptr;
229  }
230  Q_D();
231  auto it = std::find_if(d->surfaces.constBegin(), d->surfaces.constEnd(), [resource](XdgSurfaceV5Interface *surface) {
232  return surface->resource() == resource;
233  });
234  if (it != d->surfaces.constEnd()) {
235  return *it;
236  }
237  return nullptr;
238 }
239 
240 quint32 XdgShellV5Interface::Private::ping(XdgShellSurfaceInterface *surface)
241 {
242  auto client = surface->client()->client();
243  // from here we can get the resource bound to our global.
244 
245  auto clientXdgShellResource = resources.value(client);
246  if (!clientXdgShellResource) {
247  return 0;
248  }
249  const quint32 pingSerial = display->nextSerial();
250  zxdg_shell_v5_send_ping(clientXdgShellResource, pingSerial);
251 
252  setupTimer(pingSerial);
253  return pingSerial;
254 }
255 
256 XdgShellV5Interface::Private *XdgShellV5Interface::d_func() const
257 {
258  return reinterpret_cast<Private *>(d.data());
259 }
260 
261 class XdgSurfaceV5Interface::Private : public XdgShellSurfaceInterface::Private
262 {
263 public:
264  Private(XdgSurfaceV5Interface *q, XdgShellV5Interface *c, SurfaceInterface *surface, wl_resource *parentResource);
265  ~Private() override;
266 
267  QRect windowGeometry() const override;
268  QSize minimumSize() const override;
269  QSize maximumSize() const override;
270  void close() override;
271  void commit() override;
272  quint32 configure(States states, const QSize &size) override;
273 
274  XdgSurfaceV5Interface *q_func()
275  {
276  return reinterpret_cast<XdgSurfaceV5Interface *>(q);
277  }
278 
279 private:
280  static void setParentCallback(wl_client *client, wl_resource *resource, wl_resource *parent);
281  static void showWindowMenuCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial, int32_t x, int32_t y);
282  static void ackConfigureCallback(wl_client *client, wl_resource *resource, uint32_t serial);
283  static void setWindowGeometryCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height);
284  static void setMaximizedCallback(wl_client *client, wl_resource *resource);
285  static void unsetMaximizedCallback(wl_client *client, wl_resource *resource);
286  static void setFullscreenCallback(wl_client *client, wl_resource *resource, wl_resource *output);
287  static void unsetFullscreenCallback(wl_client *client, wl_resource *resource);
288  static void setMinimizedCallback(wl_client *client, wl_resource *resource);
289 
290  static const struct zxdg_surface_v5_interface s_interface;
291 
292  struct ShellSurfaceState {
293  QRect windowGeometry;
294 
295  bool windowGeometryIsSet = false;
296  };
297 
298  ShellSurfaceState m_currentState;
299  ShellSurfaceState m_pendingState;
300 };
301 
302 namespace
303 {
304 template<>
305 Qt::Edges edgesToQtEdges(zxdg_surface_v5_resize_edge edges)
306 {
307  Qt::Edges qtEdges;
308  switch (edges) {
309  case ZXDG_SURFACE_V5_RESIZE_EDGE_TOP:
310  qtEdges = Qt::TopEdge;
311  break;
312  case ZXDG_SURFACE_V5_RESIZE_EDGE_BOTTOM:
313  qtEdges = Qt::BottomEdge;
314  break;
315  case ZXDG_SURFACE_V5_RESIZE_EDGE_LEFT:
316  qtEdges = Qt::LeftEdge;
317  break;
318  case ZXDG_SURFACE_V5_RESIZE_EDGE_TOP_LEFT:
319  qtEdges = Qt::TopEdge | Qt::LeftEdge;
320  break;
321  case ZXDG_SURFACE_V5_RESIZE_EDGE_BOTTOM_LEFT:
322  qtEdges = Qt::BottomEdge | Qt::LeftEdge;
323  break;
324  case ZXDG_SURFACE_V5_RESIZE_EDGE_RIGHT:
325  qtEdges = Qt::RightEdge;
326  break;
327  case ZXDG_SURFACE_V5_RESIZE_EDGE_TOP_RIGHT:
328  qtEdges = Qt::TopEdge | Qt::RightEdge;
329  break;
330  case ZXDG_SURFACE_V5_RESIZE_EDGE_BOTTOM_RIGHT:
331  qtEdges = Qt::BottomEdge | Qt::RightEdge;
332  break;
333  case ZXDG_SURFACE_V5_RESIZE_EDGE_NONE:
334  break;
335  default:
336  Q_UNREACHABLE();
337  break;
338  }
339  return qtEdges;
340 }
341 }
342 
343 #ifndef K_DOXYGEN
344 const struct zxdg_surface_v5_interface XdgSurfaceV5Interface::Private::s_interface = {resourceDestroyedCallback,
345  setParentCallback,
346  setTitleCallback,
347  setAppIdCallback,
348  showWindowMenuCallback,
349  moveCallback,
350  resizeCallback<zxdg_surface_v5_resize_edge>,
351  ackConfigureCallback,
352  setWindowGeometryCallback,
353  setMaximizedCallback,
354  unsetMaximizedCallback,
355  setFullscreenCallback,
356  unsetFullscreenCallback,
357  setMinimizedCallback};
358 #endif
359 
360 void XdgSurfaceV5Interface::Private::setParentCallback(wl_client *client, wl_resource *resource, wl_resource *parent)
361 {
362  auto s = cast<Private>(resource);
363  Q_ASSERT(client == *s->client);
364  auto parentSurface = static_cast<XdgShellV5Interface *>(s->q->global())->getSurface(parent);
365  if (s->parent.data() != parentSurface) {
366  s->parent = QPointer<XdgSurfaceV5Interface>(parentSurface);
367  Q_EMIT s->q_func()->transientForChanged();
368  }
369 }
370 
371 void XdgSurfaceV5Interface::Private::showWindowMenuCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial, int32_t x, int32_t y)
372 {
373  auto s = cast<Private>(resource);
374  Q_ASSERT(client == *s->client);
375  Q_EMIT s->q_func()->windowMenuRequested(SeatInterface::get(seat), serial, QPoint(x, y));
376 }
377 
378 void XdgSurfaceV5Interface::Private::ackConfigureCallback(wl_client *client, wl_resource *resource, uint32_t serial)
379 {
380  auto s = cast<Private>(resource);
381  Q_ASSERT(client == *s->client);
382  if (!s->configureSerials.contains(serial)) {
383  // TODO: send error?
384  return;
385  }
386  while (!s->configureSerials.isEmpty()) {
387  quint32 i = s->configureSerials.takeFirst();
388  Q_EMIT s->q_func()->configureAcknowledged(i);
389  if (i == serial) {
390  break;
391  }
392  }
393 }
394 
395 void XdgSurfaceV5Interface::Private::setWindowGeometryCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
396 {
397  if (width < 0 || height < 0) {
398  wl_resource_post_error(resource, -1, "Tried to set invalid xdg-surface geometry");
399  return;
400  }
401  auto s = cast<Private>(resource);
402  Q_ASSERT(client == *s->client);
403  s->m_pendingState.windowGeometry = QRect(x, y, width, height);
404  s->m_pendingState.windowGeometryIsSet = true;
405 }
406 
407 void XdgSurfaceV5Interface::Private::setMaximizedCallback(wl_client *client, wl_resource *resource)
408 {
409  auto s = cast<Private>(resource);
410  Q_ASSERT(client == *s->client);
411  s->q_func()->maximizedChanged(true);
412 }
413 
414 void XdgSurfaceV5Interface::Private::unsetMaximizedCallback(wl_client *client, wl_resource *resource)
415 {
416  auto s = cast<Private>(resource);
417  Q_ASSERT(client == *s->client);
418  s->q_func()->maximizedChanged(false);
419 }
420 
421 void XdgSurfaceV5Interface::Private::setFullscreenCallback(wl_client *client, wl_resource *resource, wl_resource *output)
422 {
423  auto s = cast<Private>(resource);
424  Q_ASSERT(client == *s->client);
425  OutputInterface *o = nullptr;
426  if (output) {
427  o = OutputInterface::get(output);
428  }
429  s->q_func()->fullscreenChanged(true, o);
430 }
431 
432 void XdgSurfaceV5Interface::Private::unsetFullscreenCallback(wl_client *client, wl_resource *resource)
433 {
434  auto s = cast<Private>(resource);
435  Q_ASSERT(client == *s->client);
436  s->q_func()->fullscreenChanged(false, nullptr);
437 }
438 
439 void XdgSurfaceV5Interface::Private::setMinimizedCallback(wl_client *client, wl_resource *resource)
440 {
441  auto s = cast<Private>(resource);
442  Q_ASSERT(client == *s->client);
443  s->q_func()->minimizeRequested();
444 }
445 
446 XdgSurfaceV5Interface::Private::Private(XdgSurfaceV5Interface *q, XdgShellV5Interface *c, SurfaceInterface *surface, wl_resource *parentResource)
447  : XdgShellSurfaceInterface::Private(XdgShellInterfaceVersion::UnstableV5, q, c, surface, parentResource, &zxdg_surface_v5_interface, &s_interface)
448 {
449 }
450 
451 XdgSurfaceV5Interface::Private::~Private() = default;
452 
453 QRect XdgSurfaceV5Interface::Private::windowGeometry() const
454 {
455  return m_currentState.windowGeometry;
456 }
457 
458 QSize XdgSurfaceV5Interface::Private::minimumSize() const
459 {
460  return QSize(0, 0);
461 }
462 
463 QSize XdgSurfaceV5Interface::Private::maximumSize() const
464 {
465  return QSize(INT_MAX, INT_MAX);
466 }
467 
468 void XdgSurfaceV5Interface::Private::close()
469 {
470  zxdg_surface_v5_send_close(resource);
471  client->flush();
472 }
473 
474 void XdgSurfaceV5Interface::Private::commit()
475 {
476  const bool windowGeometryChanged = m_pendingState.windowGeometryIsSet;
477 
478  if (windowGeometryChanged) {
479  m_currentState.windowGeometry = m_pendingState.windowGeometry;
480  }
481 
482  m_pendingState = ShellSurfaceState{};
483 
484  if (windowGeometryChanged) {
485  Q_EMIT q_func()->windowGeometryChanged(m_currentState.windowGeometry);
486  }
487 }
488 
489 quint32 XdgSurfaceV5Interface::Private::configure(States states, const QSize &size)
490 {
491  if (!resource) {
492  return 0;
493  }
494  const quint32 serial = global->display()->nextSerial();
495  wl_array state;
496  wl_array_init(&state);
497  if (states.testFlag(State::Maximized)) {
498  uint32_t *s = reinterpret_cast<uint32_t *>(wl_array_add(&state, sizeof(uint32_t)));
500  }
501  if (states.testFlag(State::Fullscreen)) {
502  uint32_t *s = reinterpret_cast<uint32_t *>(wl_array_add(&state, sizeof(uint32_t)));
504  }
505  if (states.testFlag(State::Resizing)) {
506  uint32_t *s = reinterpret_cast<uint32_t *>(wl_array_add(&state, sizeof(uint32_t)));
508  }
509  if (states.testFlag(State::Activated)) {
510  uint32_t *s = reinterpret_cast<uint32_t *>(wl_array_add(&state, sizeof(uint32_t)));
512  }
513  configureSerials << serial;
514  zxdg_surface_v5_send_configure(resource, size.width(), size.height(), &state, serial);
515  client->flush();
516  wl_array_release(&state);
517 
518  return serial;
519 }
520 
521 #ifndef K_DOXYGEN
522 const struct zxdg_popup_v5_interface XdgPopupV5Interface::Private::s_interface = {resourceDestroyedCallback};
523 #endif
524 
525 XdgPopupV5Interface::Private::Private(XdgPopupV5Interface *q, XdgShellV5Interface *c, SurfaceInterface *surface, wl_resource *parentResource)
526  : XdgShellPopupInterface::Private(XdgShellInterfaceVersion::UnstableV5, q, c, surface, parentResource, &zxdg_popup_v5_interface, &s_interface)
527 {
528 }
529 
530 XdgPopupV5Interface::Private::~Private() = default;
531 
532 QRect XdgPopupV5Interface::Private::windowGeometry() const
533 {
534  return QRect();
535 }
536 
537 void XdgPopupV5Interface::Private::commit()
538 {
539 }
540 
541 void XdgPopupV5Interface::Private::popupDone()
542 {
543  if (!resource) {
544  return;
545  }
546  // TODO: dismiss all child popups
548  client->flush();
549 }
550 
551 XdgShellV5Interface::XdgShellV5Interface(Display *display, QObject *parent)
552  : XdgShellInterface(new Private(this, display), parent)
553 {
554 }
555 
556 XdgShellV5Interface::~XdgShellV5Interface() = default;
557 
558 XdgSurfaceV5Interface::XdgSurfaceV5Interface(XdgShellV5Interface *parent, SurfaceInterface *surface, wl_resource *parentResource)
559  : KWayland::Server::XdgShellSurfaceInterface(new Private(this, parent, surface, parentResource))
560 {
561 }
562 
563 XdgSurfaceV5Interface::~XdgSurfaceV5Interface() = default;
564 
565 XdgPopupV5Interface::XdgPopupV5Interface(XdgShellV5Interface *parent, SurfaceInterface *surface, wl_resource *parentResource)
566  : XdgShellPopupInterface(new Private(this, parent, surface, parentResource))
567 {
568 }
569 
570 XdgPopupV5Interface::~XdgPopupV5Interface() = default;
571 
572 XdgPopupV5Interface::Private *XdgPopupV5Interface::d_func() const
573 {
574  return reinterpret_cast<Private *>(d.data());
575 }
576 
577 }
578 }
static void zxdg_popup_v5_send_popup_done(struct wl_resource *resource_)
XdgShellInterfaceVersion
Enum describing the different InterfaceVersion encapsulated in this implementation.
KGuiItem configure()
int width() const const
const QList< QKeySequence > & close()
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
static SurfaceInterface * get(wl_resource *native)
@ ZXDG_SURFACE_V5_STATE_MAXIMIZED
the surface is maximized
static void zxdg_surface_v5_send_configure(struct wl_resource *resource_, int32_t width, int32_t height, struct wl_array *states, uint32_t serial)
int height() const const
static void zxdg_shell_v5_send_ping(struct wl_resource *resource_, uint32_t serial)
@ ZXDG_SURFACE_V5_STATE_RESIZING
the surface is being resized
@ ZXDG_SURFACE_V5_STATE_FULLSCREEN
the surface is fullscreen
BottomEdge
Q_D(Todo)
static void zxdg_surface_v5_send_close(struct wl_resource *resource_)
@ ZXDG_SURFACE_V5_STATE_ACTIVATED
the surface is now activated
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Jan 29 2023 03:54:09 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.