1/*
2 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6#include "shell.h"
7#include "event_queue.h"
8#include "output.h"
9#include "seat.h"
10#include "surface.h"
11#include "wayland_pointer_p.h"
12// Qt
13#include <QGuiApplication>
14#include <QList>
15#include <qpa/qplatformnativeinterface.h>
16// Wayland
17#include <wayland-client-protocol.h>
18
19namespace KWayland
20{
21namespace Client
22{
23class Q_DECL_HIDDEN Shell::Private
24{
25public:
26 WaylandPointer<wl_shell, wl_shell_destroy> shell;
27 EventQueue *queue = nullptr;
28};
29
30Shell::Shell(QObject *parent)
31 : QObject(parent)
32 , d(new Private)
33{
34}
35
36Shell::~Shell()
37{
38 release();
39}
40
41void Shell::destroy()
42{
43 if (!d->shell) {
44 return;
45 }
46 Q_EMIT interfaceAboutToBeDestroyed();
47 d->shell.destroy();
48}
49
50void Shell::release()
51{
52 if (!d->shell) {
53 return;
54 }
55 Q_EMIT interfaceAboutToBeReleased();
56 d->shell.release();
57}
58
59void Shell::setup(wl_shell *shell)
60{
61 Q_ASSERT(!d->shell);
62 Q_ASSERT(shell);
63 d->shell.setup(pointer: shell);
64}
65
66void Shell::setEventQueue(EventQueue *queue)
67{
68 d->queue = queue;
69}
70
71EventQueue *Shell::eventQueue()
72{
73 return d->queue;
74}
75
76ShellSurface *Shell::createSurface(wl_surface *surface, QObject *parent)
77{
78 Q_ASSERT(isValid());
79 ShellSurface *s = new ShellSurface(parent);
80 connect(sender: this, signal: &Shell::interfaceAboutToBeReleased, context: s, slot: &ShellSurface::release);
81 connect(sender: this, signal: &Shell::interfaceAboutToBeDestroyed, context: s, slot: &ShellSurface::destroy);
82 auto w = wl_shell_get_shell_surface(wl_shell: d->shell, surface);
83 if (d->queue) {
84 d->queue->addProxy(proxy: w);
85 }
86 s->setup(w);
87 return s;
88}
89
90ShellSurface *Shell::createSurface(Surface *surface, QObject *parent)
91{
92 Q_ASSERT(surface);
93 return createSurface(surface: *surface, parent);
94}
95
96bool Shell::isValid() const
97{
98 return d->shell.isValid();
99}
100
101Shell::operator wl_shell *()
102{
103 return d->shell;
104}
105
106Shell::operator wl_shell *() const
107{
108 return d->shell;
109}
110
111class Q_DECL_HIDDEN ShellSurface::Private
112{
113public:
114 Private(ShellSurface *q);
115 void setup(wl_shell_surface *surface);
116
117 WaylandPointer<wl_shell_surface, wl_shell_surface_destroy> surface;
118 QSize size;
119 static QList<ShellSurface *> s_surfaces;
120
121private:
122 void ping(uint32_t serial);
123 static void pingCallback(void *data, struct wl_shell_surface *shellSurface, uint32_t serial);
124 static void configureCallback(void *data, struct wl_shell_surface *shellSurface, uint32_t edges, int32_t width, int32_t height);
125 static void popupDoneCallback(void *data, struct wl_shell_surface *shellSurface);
126
127 ShellSurface *q;
128 static const struct wl_shell_surface_listener s_listener;
129};
130
131QList<ShellSurface *> ShellSurface::Private::s_surfaces = QList<ShellSurface *>();
132
133ShellSurface::Private::Private(ShellSurface *q)
134 : q(q)
135{
136}
137
138void ShellSurface::Private::setup(wl_shell_surface *s)
139{
140 Q_ASSERT(s);
141 Q_ASSERT(!surface);
142 surface.setup(pointer: s);
143 wl_shell_surface_add_listener(wl_shell_surface: surface, listener: &s_listener, data: this);
144}
145
146ShellSurface *ShellSurface::fromWindow(QWindow *window)
147{
148 if (!window) {
149 return nullptr;
150 }
151 QPlatformNativeInterface *native = qApp->platformNativeInterface();
152 if (!native) {
153 return nullptr;
154 }
155 window->create();
156 wl_shell_surface *s = reinterpret_cast<wl_shell_surface *>(native->nativeResourceForWindow(QByteArrayLiteral("wl_shell_surface"), window));
157 if (!s) {
158 return nullptr;
159 }
160 if (auto surface = get(native: s)) {
161 return surface;
162 }
163 ShellSurface *surface = new ShellSurface(window);
164 surface->d->surface.setup(pointer: s, foreign: true);
165 return surface;
166}
167
168ShellSurface *ShellSurface::fromQtWinId(WId wid)
169{
170 QWindow *window = nullptr;
171
172 for (auto win : qApp->allWindows()) {
173 if (win->winId() == wid) {
174 window = win;
175 break;
176 }
177 }
178
179 if (!window) {
180 return nullptr;
181 }
182 return fromWindow(window);
183}
184
185ShellSurface *ShellSurface::get(wl_shell_surface *native)
186{
187 auto it = std::find_if(first: Private::s_surfaces.constBegin(), last: Private::s_surfaces.constEnd(), pred: [native](ShellSurface *s) {
188 return s->d->surface == native;
189 });
190 if (it != Private::s_surfaces.constEnd()) {
191 return *(it);
192 }
193 return nullptr;
194}
195
196ShellSurface::ShellSurface(QObject *parent)
197 : QObject(parent)
198 , d(new Private(this))
199{
200 Private::s_surfaces << this;
201}
202
203ShellSurface::~ShellSurface()
204{
205 Private::s_surfaces.removeOne(t: this);
206 release();
207}
208
209void ShellSurface::release()
210{
211 d->surface.release();
212}
213
214void ShellSurface::destroy()
215{
216 d->surface.destroy();
217}
218
219#ifndef K_DOXYGEN
220const struct wl_shell_surface_listener ShellSurface::Private::s_listener = {.ping: pingCallback, .configure: configureCallback, .popup_done: popupDoneCallback};
221#endif
222
223void ShellSurface::Private::configureCallback(void *data, wl_shell_surface *shellSurface, uint32_t edges, int32_t width, int32_t height)
224{
225 Q_UNUSED(edges)
226 auto s = reinterpret_cast<ShellSurface::Private *>(data);
227 Q_ASSERT(s->surface == shellSurface);
228 s->q->setSize(QSize(width, height));
229}
230
231void ShellSurface::Private::pingCallback(void *data, wl_shell_surface *shellSurface, uint32_t serial)
232{
233 auto s = reinterpret_cast<ShellSurface::Private *>(data);
234 Q_ASSERT(s->surface == shellSurface);
235 s->ping(serial);
236}
237
238void ShellSurface::Private::popupDoneCallback(void *data, wl_shell_surface *shellSurface)
239{
240 auto s = reinterpret_cast<ShellSurface::Private *>(data);
241 Q_ASSERT(s->surface == shellSurface);
242 Q_EMIT s->q->popupDone();
243}
244
245void ShellSurface::setup(wl_shell_surface *surface)
246{
247 d->setup(surface);
248}
249
250void ShellSurface::Private::ping(uint32_t serial)
251{
252 wl_shell_surface_pong(wl_shell_surface: surface, serial);
253 Q_EMIT q->pinged();
254}
255
256void ShellSurface::setSize(const QSize &size)
257{
258 if (d->size == size) {
259 return;
260 }
261 d->size = size;
262 Q_EMIT sizeChanged(size);
263}
264
265void ShellSurface::setFullscreen(Output *output)
266{
267 Q_ASSERT(isValid());
268 wl_shell_surface_set_fullscreen(wl_shell_surface: d->surface, method: WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, framerate: 0, output: output ? output->output() : nullptr);
269}
270
271void ShellSurface::setMaximized(Output *output)
272{
273 Q_ASSERT(isValid());
274 wl_shell_surface_set_maximized(wl_shell_surface: d->surface, output: output ? output->output() : nullptr);
275}
276
277void ShellSurface::setToplevel()
278{
279 Q_ASSERT(isValid());
280 wl_shell_surface_set_toplevel(wl_shell_surface: d->surface);
281}
282
283void ShellSurface::setTransient(Surface *parent, const QPoint &offset, TransientFlags flags)
284{
285 Q_ASSERT(isValid());
286 uint32_t wlFlags = 0;
287 if (flags.testFlag(flag: TransientFlag::NoFocus)) {
288 wlFlags |= WL_SHELL_SURFACE_TRANSIENT_INACTIVE;
289 }
290 wl_shell_surface_set_transient(wl_shell_surface: d->surface, parent: *parent, x: offset.x(), y: offset.y(), flags: wlFlags);
291}
292
293void ShellSurface::setTransientPopup(Surface *parent, Seat *grabbedSeat, quint32 grabSerial, const QPoint &offset, TransientFlags flags)
294{
295 Q_ASSERT(isValid());
296 Q_ASSERT(parent);
297 Q_ASSERT(grabbedSeat);
298 uint32_t wlFlags = 0;
299 if (flags.testFlag(flag: TransientFlag::NoFocus)) {
300 wlFlags |= WL_SHELL_SURFACE_TRANSIENT_INACTIVE;
301 }
302 wl_shell_surface_set_popup(wl_shell_surface: d->surface, seat: *grabbedSeat, serial: grabSerial, parent: *parent, x: offset.x(), y: offset.y(), flags: wlFlags);
303}
304
305void ShellSurface::requestMove(Seat *seat, quint32 serial)
306{
307 Q_ASSERT(isValid());
308 Q_ASSERT(seat);
309
310 wl_shell_surface_move(wl_shell_surface: d->surface, seat: *seat, serial);
311}
312
313void ShellSurface::requestResize(Seat *seat, quint32 serial, Qt::Edges edges)
314{
315 Q_ASSERT(isValid());
316 Q_ASSERT(seat);
317
318 uint wlEdge = WL_SHELL_SURFACE_RESIZE_NONE;
319 if (edges.testFlag(flag: Qt::TopEdge)) {
320 if (edges.testFlag(flag: Qt::LeftEdge) && ((edges & ~Qt::LeftEdge) == Qt::TopEdge)) {
321 wlEdge = WL_SHELL_SURFACE_RESIZE_TOP_LEFT;
322 } else if (edges.testFlag(flag: Qt::RightEdge) && ((edges & ~Qt::RightEdge) == Qt::TopEdge)) {
323 wlEdge = WL_SHELL_SURFACE_RESIZE_TOP_RIGHT;
324 } else if ((edges & ~Qt::TopEdge) == Qt::Edges()) {
325 wlEdge = WL_SHELL_SURFACE_RESIZE_TOP;
326 }
327 } else if (edges.testFlag(flag: Qt::BottomEdge)) {
328 if (edges.testFlag(flag: Qt::LeftEdge) && ((edges & ~Qt::LeftEdge) == Qt::BottomEdge)) {
329 wlEdge = WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT;
330 } else if (edges.testFlag(flag: Qt::RightEdge) && ((edges & ~Qt::RightEdge) == Qt::BottomEdge)) {
331 wlEdge = WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT;
332 } else if ((edges & ~Qt::BottomEdge) == Qt::Edges()) {
333 wlEdge = WL_SHELL_SURFACE_RESIZE_BOTTOM;
334 }
335 } else if (edges.testFlag(flag: Qt::RightEdge) && ((edges & ~Qt::RightEdge) == Qt::Edges())) {
336 wlEdge = WL_SHELL_SURFACE_RESIZE_RIGHT;
337 } else if (edges.testFlag(flag: Qt::LeftEdge) && ((edges & ~Qt::LeftEdge) == Qt::Edges())) {
338 wlEdge = WL_SHELL_SURFACE_RESIZE_LEFT;
339 }
340
341 wl_shell_surface_resize(wl_shell_surface: d->surface, seat: *seat, serial, edges: wlEdge);
342}
343
344void ShellSurface::setTitle(const QString &title)
345{
346 wl_shell_surface_set_title(wl_shell_surface: d->surface, title: title.toUtf8().constData());
347}
348
349void ShellSurface::setWindowClass(const QByteArray &windowClass)
350{
351 wl_shell_surface_set_class(wl_shell_surface: d->surface, class_: windowClass.constData());
352}
353
354QSize ShellSurface::size() const
355{
356 return d->size;
357}
358
359bool ShellSurface::isValid() const
360{
361 return d->surface.isValid();
362}
363
364ShellSurface::operator wl_shell_surface *()
365{
366 return d->surface;
367}
368
369ShellSurface::operator wl_shell_surface *() const
370{
371 return d->surface;
372}
373
374}
375}
376
377#include "moc_shell.cpp"
378

source code of kwayland/src/client/shell.cpp