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

KDE's Doxygen guidelines are available online.