KWayland

shell_interface.cpp
1 /*
2  SPDX-FileCopyrightText: 2014 Martin Gräßlin <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5 */
6 #include "shell_interface.h"
7 #include "display.h"
8 #include "generic_shell_surface_p.h"
9 #include "global_p.h"
10 #include "resource_p.h"
11 #include "surface_interface.h"
12 
13 #include <QTimer>
14 
15 #include <wayland-server.h>
16 
17 namespace KWayland
18 {
19 namespace Server
20 {
21 class ShellInterface::Private : public Global::Private
22 {
23 public:
24  Private(ShellInterface *q, Display *d);
25 
27 
28 private:
29  static void createSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface);
30  void bind(wl_client *client, uint32_t version, uint32_t id) override;
31  void createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource);
32 
33  ShellInterface *q;
34  static const struct wl_shell_interface s_interface;
35  static const quint32 s_version;
36 };
37 
38 const quint32 ShellInterface::Private::s_version = 1;
39 
40 ShellInterface::Private::Private(ShellInterface *q, Display *d)
41  : Global::Private(d, &wl_shell_interface, s_version)
42  , q(q)
43 {
44 }
45 
46 #ifndef K_DOXYGEN
47 const struct wl_shell_interface ShellInterface::Private::s_interface = {createSurfaceCallback};
48 #endif
49 
50 class ShellSurfaceInterface::Private : public Resource::Private, public GenericShellSurface<ShellSurfaceInterface>
51 {
52 public:
53  Private(ShellSurfaceInterface *q, ShellInterface *shell, SurfaceInterface *surface, wl_resource *parentResource);
54  void ping();
55 
56  void commit() override;
57 
58  QScopedPointer<QTimer> pingTimer;
59  quint32 pingSerial = 0;
60  enum class WindowMode {
61  Fullscreen,
62  Toplevel,
63  Maximized,
64  Popup,
65  };
66  WindowMode windowMode = WindowMode::Toplevel;
67  QPoint transientOffset;
68  QPointer<SurfaceInterface> transientFor;
69  bool acceptsKeyboardFocus = true;
70  void setWindowMode(WindowMode newWindowMode);
71 
72  ShellSurfaceInterface *q_func()
73  {
74  return reinterpret_cast<ShellSurfaceInterface *>(q);
75  }
76 
77 private:
78  // interface callbacks
79  static void pongCallback(wl_client *client, wl_resource *resource, uint32_t serial);
80  static void setToplevelCallback(wl_client *client, wl_resource *resource);
81  static void setTransientCallback(wl_client *client, wl_resource *resource, wl_resource *parent, int32_t x, int32_t y, uint32_t flags);
82  static void setFullscreenCallback(wl_client *client, wl_resource *resource, uint32_t method, uint32_t framerate, wl_resource *output);
83  static void
84  setPopupCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial, wl_resource *parent, int32_t x, int32_t y, uint32_t flags);
85  static void setMaximizedCallback(wl_client *client, wl_resource *resource, wl_resource *output);
86 
87  void pong(quint32 serial);
88  void setAcceptsFocus(quint32 flags);
89 
90  static const struct wl_shell_surface_interface s_interface;
91 };
92 
93 ShellInterface::ShellInterface(Display *display, QObject *parent)
94  : Global(new Private(this, display), parent)
95 {
96 }
97 
98 ShellInterface::~ShellInterface() = default;
99 
100 void ShellInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id)
101 {
102  auto c = display->getConnection(client);
103  wl_resource *shell = c->createResource(&wl_shell_interface, qMin(version, s_version), id);
104  if (!shell) {
105  wl_client_post_no_memory(client);
106  return;
107  }
108  wl_resource_set_implementation(shell, &s_interface, this, nullptr);
109 }
110 
111 void ShellInterface::Private::createSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface)
112 {
113  auto s = reinterpret_cast<ShellInterface::Private *>(wl_resource_get_user_data(resource));
114  s->createSurface(client, wl_resource_get_version(resource), id, SurfaceInterface::get(surface), resource);
115 }
116 
117 void ShellInterface::Private::createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource)
118 {
119  auto it = std::find_if(surfaces.constBegin(), surfaces.constEnd(), [surface](ShellSurfaceInterface *s) {
120  return surface == s->surface();
121  });
122  if (it != surfaces.constEnd()) {
123  wl_resource_post_error(surface->resource(), WL_SHELL_ERROR_ROLE, "ShellSurface already created");
124  return;
125  }
126  ShellSurfaceInterface *shellSurface = new ShellSurfaceInterface(q, surface, parentResource);
127  surfaces << shellSurface;
128  QObject::connect(shellSurface, &ShellSurfaceInterface::destroyed, q, [this, shellSurface] {
129  surfaces.removeAll(shellSurface);
130  });
131  shellSurface->d->create(display->getConnection(client), version, id);
132  Q_EMIT q->surfaceCreated(shellSurface);
133 }
134 
135 /*********************************
136  * ShellSurfaceInterface
137  *********************************/
138 ShellSurfaceInterface::Private::Private(ShellSurfaceInterface *q, ShellInterface *shell, SurfaceInterface *surface, wl_resource *parentResource)
139  : Resource::Private(q, shell, parentResource, &wl_shell_surface_interface, &s_interface)
140  , GenericShellSurface<KWayland::Server::ShellSurfaceInterface>(q, surface)
141  , pingTimer(new QTimer)
142 {
143  pingTimer->setSingleShot(true);
144  pingTimer->setInterval(1000);
145 }
146 
147 #ifndef K_DOXYGEN
148 const struct wl_shell_surface_interface ShellSurfaceInterface::Private::s_interface = {pongCallback,
149  moveCallback,
150  resizeCallback<wl_shell_surface_resize>,
151  setToplevelCallback,
152  setTransientCallback,
153  setFullscreenCallback,
154  setPopupCallback,
155  setMaximizedCallback,
156  setTitleCallback,
157  setAppIdCallback};
158 #endif
159 
160 ShellSurfaceInterface::ShellSurfaceInterface(ShellInterface *shell, SurfaceInterface *parent, wl_resource *parentResource)
161  : Resource(new Private(this, shell, parent, parentResource))
162 {
163  Q_D();
164  connect(d->pingTimer.data(), &QTimer::timeout, this, &ShellSurfaceInterface::pingTimeout);
165  auto unsetSurface = [this] {
166  Q_D();
167  d->surface = nullptr;
168  };
169  connect(parent, &Resource::unbound, this, unsetSurface);
170  connect(parent, &QObject::destroyed, this, unsetSurface);
171 }
172 
173 ShellSurfaceInterface::~ShellSurfaceInterface() = default;
174 
175 void ShellSurfaceInterface::Private::commit()
176 {
177 }
178 
179 void ShellSurfaceInterface::Private::pongCallback(wl_client *client, wl_resource *resource, uint32_t serial)
180 {
181  auto s = cast<Private>(resource);
182  Q_ASSERT(client == *s->client);
183  s->pong(serial);
184 }
185 
186 void ShellSurfaceInterface::Private::pong(quint32 serial)
187 {
188  if (pingTimer->isActive() && serial == pingSerial) {
189  pingTimer->stop();
190  Q_Q(ShellSurfaceInterface);
191  Q_EMIT q->pongReceived();
192  }
193 }
194 
195 void ShellSurfaceInterface::ping()
196 {
197  Q_D();
198  if (!d->resource) {
199  return;
200  }
201  d->ping();
202 }
203 
204 void ShellSurfaceInterface::Private::ping()
205 {
206  if (pingTimer->isActive()) {
207  return;
208  }
209  pingSerial = global->display()->nextSerial();
210  wl_shell_surface_send_ping(resource, pingSerial);
211  client->flush();
212  pingTimer->start();
213 }
214 
215 void ShellSurfaceInterface::setPingTimeout(uint msec)
216 {
217  Q_D();
218  d->pingTimer->setInterval(msec);
219 }
220 
221 bool ShellSurfaceInterface::isPinged() const
222 {
223  Q_D();
224  return d->pingTimer->isActive();
225 }
226 
227 void ShellSurfaceInterface::requestSize(const QSize &size)
228 {
229  Q_D();
230  if (!d->resource) {
231  return;
232  }
233  // TODO: what about the edges?
234  wl_shell_surface_send_configure(d->resource, 0, size.width(), size.height());
235  d->client->flush();
236 }
237 
238 namespace
239 {
240 template<>
241 Qt::Edges edgesToQtEdges(wl_shell_surface_resize edges)
242 {
243  Qt::Edges qtEdges;
244  switch (edges) {
245  case WL_SHELL_SURFACE_RESIZE_TOP:
246  qtEdges = Qt::TopEdge;
247  break;
248  case WL_SHELL_SURFACE_RESIZE_BOTTOM:
249  qtEdges = Qt::BottomEdge;
250  break;
251  case WL_SHELL_SURFACE_RESIZE_LEFT:
252  qtEdges = Qt::LeftEdge;
253  break;
254  case WL_SHELL_SURFACE_RESIZE_TOP_LEFT:
255  qtEdges = Qt::TopEdge | Qt::LeftEdge;
256  break;
257  case WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT:
258  qtEdges = Qt::BottomEdge | Qt::LeftEdge;
259  break;
260  case WL_SHELL_SURFACE_RESIZE_RIGHT:
261  qtEdges = Qt::RightEdge;
262  break;
263  case WL_SHELL_SURFACE_RESIZE_TOP_RIGHT:
264  qtEdges = Qt::TopEdge | Qt::RightEdge;
265  break;
266  case WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT:
267  qtEdges = Qt::BottomEdge | Qt::RightEdge;
268  break;
269  case WL_SHELL_SURFACE_RESIZE_NONE:
270  break;
271  default:
272  Q_UNREACHABLE();
273  break;
274  }
275  return qtEdges;
276 }
277 }
278 
279 void ShellSurfaceInterface::Private::setToplevelCallback(wl_client *client, wl_resource *resource)
280 {
281  auto s = cast<Private>(resource);
282  Q_ASSERT(client == *s->client);
283  s->setWindowMode(WindowMode::Toplevel);
284 }
285 
286 void ShellSurfaceInterface::Private::setTransientCallback(wl_client *client, wl_resource *resource, wl_resource *parent, int32_t x, int32_t y, uint32_t flags)
287 {
288  Q_UNUSED(flags)
289  auto s = cast<Private>(resource);
290  Q_ASSERT(client == *s->client);
291  auto surface = SurfaceInterface::get(parent);
292  if (surface && s->surface == surface) {
293  wl_resource_post_error(surface->resource(), WL_SHELL_ERROR_ROLE, "Cannot be a transient to itself");
294  return;
295  }
296  s->transientFor = QPointer<SurfaceInterface>(surface);
297  s->transientOffset = QPoint(x, y);
298  Q_EMIT s->q_func()->transientChanged(!s->transientFor.isNull());
299  Q_EMIT s->q_func()->transientOffsetChanged(s->transientOffset);
300  Q_EMIT s->q_func()->transientForChanged();
301  s->setAcceptsFocus(flags);
302 }
303 
304 void ShellSurfaceInterface::Private::setAcceptsFocus(quint32 flags)
305 {
306  const bool acceptsFocus = !(flags & WL_SHELL_SURFACE_TRANSIENT_INACTIVE);
307  if (acceptsFocus != acceptsKeyboardFocus) {
308  acceptsKeyboardFocus = acceptsFocus;
309  Q_Q(ShellSurfaceInterface);
310  Q_EMIT q->acceptsKeyboardFocusChanged();
311  }
312 }
313 
314 void ShellSurfaceInterface::Private::setFullscreenCallback(wl_client *client, wl_resource *resource, uint32_t method, uint32_t framerate, wl_resource *output)
315 {
316  Q_UNUSED(method)
317  Q_UNUSED(framerate)
318  Q_UNUSED(output)
319  auto s = cast<Private>(resource);
320  Q_ASSERT(client == *s->client);
321  // TODO: add method, framerate and output
322  s->setWindowMode(WindowMode::Fullscreen);
323 }
324 
325 void ShellSurfaceInterface::Private::setWindowMode(WindowMode newWindowMode)
326 {
327  if (windowMode == newWindowMode) {
328  return;
329  }
330  const WindowMode oldWindowMode = windowMode;
331  windowMode = newWindowMode;
332  Q_Q(ShellSurfaceInterface);
333  if (oldWindowMode == WindowMode::Fullscreen || newWindowMode == WindowMode::Fullscreen) {
334  Q_EMIT q->fullscreenChanged(windowMode == WindowMode::Fullscreen);
335  }
336  if (oldWindowMode == WindowMode::Toplevel || newWindowMode == WindowMode::Toplevel) {
337  Q_EMIT q->toplevelChanged(windowMode == WindowMode::Toplevel);
338  }
339  if (oldWindowMode == WindowMode::Maximized || newWindowMode == WindowMode::Maximized) {
340  Q_EMIT q->maximizedChanged(windowMode == WindowMode::Maximized);
341  }
342  if (oldWindowMode == WindowMode::Popup || newWindowMode == WindowMode::Popup) {
343  Q_EMIT q->popupChanged(windowMode == WindowMode::Popup);
344  }
345 }
346 
347 void ShellSurfaceInterface::Private::setPopupCallback(wl_client *client,
348  wl_resource *resource,
349  wl_resource *seat,
350  uint32_t serial,
351  wl_resource *parent,
352  int32_t x,
353  int32_t y,
354  uint32_t flags)
355 {
356  Q_UNUSED(seat)
357  Q_UNUSED(serial)
358  Q_UNUSED(flags)
359  auto s = cast<Private>(resource);
360  Q_ASSERT(client == *s->client);
361  // TODO: what about seat and serial?
362  s->transientFor = QPointer<SurfaceInterface>(SurfaceInterface::get(parent));
363  s->transientOffset = QPoint(x, y);
364  s->setWindowMode(WindowMode::Popup);
365  Q_EMIT s->q_func()->transientChanged(!s->transientFor.isNull());
366  Q_EMIT s->q_func()->transientOffsetChanged(s->transientOffset);
367  Q_EMIT s->q_func()->transientForChanged();
368  // we ignore the flags as Qt requests keyboard focus for popups
369  // if we would honor the flag this could break compositors
370  // compare QtWayland (5.6), file qwaylandwlshellsurface.cpp:208
371  s->setAcceptsFocus(WL_SHELL_SURFACE_TRANSIENT_INACTIVE);
372 }
373 
374 void ShellSurfaceInterface::Private::setMaximizedCallback(wl_client *client, wl_resource *resource, wl_resource *output)
375 {
376  Q_UNUSED(output)
377  auto s = cast<Private>(resource);
378  Q_ASSERT(client == *s->client);
379  s->setWindowMode(WindowMode::Maximized);
380 }
381 
382 SurfaceInterface *ShellSurfaceInterface::surface() const
383 {
384  Q_D();
385  return d->surface;
386 }
387 
388 ShellInterface *ShellSurfaceInterface::shell() const
389 {
390  Q_D();
391  return reinterpret_cast<ShellInterface *>(d->global);
392 }
393 
394 QString ShellSurfaceInterface::title() const
395 {
396  Q_D();
397  return d->title;
398 }
399 
400 QByteArray ShellSurfaceInterface::windowClass() const
401 {
402  Q_D();
403  return d->windowClass;
404 }
405 
406 bool ShellSurfaceInterface::isFullscreen() const
407 {
408  Q_D();
409  return d->windowMode == Private::WindowMode::Fullscreen;
410 }
411 
412 bool ShellSurfaceInterface::isToplevel() const
413 {
414  Q_D();
415  return d->windowMode == Private::WindowMode::Toplevel;
416 }
417 
418 bool ShellSurfaceInterface::isMaximized() const
419 {
420  Q_D();
421  return d->windowMode == Private::WindowMode::Maximized;
422 }
423 
424 bool ShellSurfaceInterface::isPopup() const
425 {
426  Q_D();
427  return d->windowMode == Private::WindowMode::Popup;
428 }
429 
430 bool ShellSurfaceInterface::isTransient() const
431 {
432  Q_D();
433  return !d->transientFor.isNull();
434 }
435 
436 QPoint ShellSurfaceInterface::transientOffset() const
437 {
438  Q_D();
439  return d->transientOffset;
440 }
441 
442 bool ShellSurfaceInterface::acceptsKeyboardFocus() const
443 {
444  Q_D();
445  return d->acceptsKeyboardFocus;
446 }
447 
448 void ShellSurfaceInterface::popupDone()
449 {
450  Q_D();
451  if (isPopup() && d->resource) {
452  wl_shell_surface_send_popup_done(d->resource);
453  }
454 }
455 
456 QPointer<SurfaceInterface> ShellSurfaceInterface::transientFor() const
457 {
458  Q_D();
459  return d->transientFor;
460 }
461 
462 ShellSurfaceInterface::Private *ShellSurfaceInterface::d_func() const
463 {
464  return reinterpret_cast<ShellSurfaceInterface::Private *>(d.data());
465 }
466 
467 }
468 }
Global for the wl_shell interface.
int width() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void destroyed(QObject *obj)
T * data() const const
int height() const const
void timeout()
typedef Edges
QObject * parent() const const
Resource representing a wl_surface.
Q_D(Todo)
virtual QVariant get(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Feb 7 2023 03:56:22 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.