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

KDE's Doxygen guidelines are available online.