1// Copyright (C) 2017 The Qt Company Ltd.
2// Copyright (C) 2017 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
4
5#include "qwaylandwlshell.h"
6#include "qwaylandwlshell_p.h"
7
8#if QT_CONFIG(wayland_compositor_quick)
9#include "qwaylandwlshellintegration_p.h"
10#endif
11#include <QtWaylandCompositor/private/qwaylandutils_p.h>
12
13#include <QtWaylandCompositor/QWaylandCompositor>
14#include <QtWaylandCompositor/QWaylandView>
15#include <QtWaylandCompositor/QWaylandOutput>
16#include <QtWaylandCompositor/QWaylandClient>
17
18#include <QtCore/QObject>
19#include <QtCore/QDebug>
20
21QT_BEGIN_NAMESPACE
22
23QWaylandSurfaceRole QWaylandWlShellSurfacePrivate::s_role("wl_shell_surface");
24
25QWaylandWlShellPrivate::QWaylandWlShellPrivate()
26{
27}
28
29void QWaylandWlShellPrivate::shell_get_shell_surface(Resource *resource, uint32_t id, struct ::wl_resource *surface_res)
30{
31 Q_Q(QWaylandWlShell);
32 QWaylandSurface *surface = QWaylandSurface::fromResource(resource: surface_res);
33
34 QWaylandResource shellSurfaceResource(wl_resource_create(resource->client(), &wl_shell_surface_interface,
35 wl_resource_get_version(resource->handle), id));
36
37 // XXX FIXME
38 // The role concept was formalized in wayland 1.7, so that release adds one error
39 // code for each interface that implements a role, and we are supposed to pass here
40 // the newly constructed resource and the correct error code so that if setting the
41 // role fails, a proper error can be sent to the client.
42 // However we're still using wayland 1.4, which doesn't have interface specific role
43 // errors, so the best we can do is to use wl_display's object_id error.
44 wl_resource *displayRes = wl_client_get_object(resource->client(), 1);
45 if (!surface->setRole(role: QWaylandWlShellSurface::role(), errorResource: displayRes, errorCode: WL_DISPLAY_ERROR_INVALID_OBJECT))
46 return;
47
48 emit q->wlShellSurfaceRequested(surface, resource: shellSurfaceResource);
49
50 QWaylandWlShellSurface *shellSurface = QWaylandWlShellSurface::fromResource(res: shellSurfaceResource.resource());
51 if (!shellSurface) {
52 // A QWaylandWlShellSurface was not created in response to the wlShellSurfaceRequested
53 // signal, so we create one as fallback here instead.
54 shellSurface = new QWaylandWlShellSurface(q, surface, shellSurfaceResource);
55 }
56
57 m_shellSurfaces.append(shellSurface);
58 emit q->wlShellSurfaceCreated(shellSurface);
59}
60
61void QWaylandWlShellPrivate::unregisterShellSurface(QWaylandWlShellSurface *shellSurface)
62{
63 if (!m_shellSurfaces.removeOne(shellSurface))
64 qWarning(msg: "Unexpected state. Can't find registered shell surface.");
65}
66
67QWaylandWlShellSurfacePrivate::QWaylandWlShellSurfacePrivate()
68{
69}
70
71QWaylandWlShellSurfacePrivate::~QWaylandWlShellSurfacePrivate()
72{
73}
74
75void QWaylandWlShellSurfacePrivate::ping(uint32_t serial)
76{
77 m_pings.insert(value: serial);
78 send_ping(serial);
79}
80
81void QWaylandWlShellSurfacePrivate::setWindowType(Qt::WindowType windowType)
82{
83 if (m_windowType == windowType)
84 return;
85 m_windowType = windowType;
86
87 Q_Q(QWaylandWlShellSurface);
88 emit q->windowTypeChanged();
89}
90
91void QWaylandWlShellSurfacePrivate::shell_surface_destroy_resource(Resource *)
92{
93 Q_Q(QWaylandWlShellSurface);
94
95 delete q;
96}
97
98void QWaylandWlShellSurfacePrivate::shell_surface_move(Resource *resource,
99 struct wl_resource *input_device_super,
100 uint32_t serial)
101{
102 Q_UNUSED(resource);
103 Q_UNUSED(serial);
104
105 Q_Q(QWaylandWlShellSurface);
106 QWaylandSeat *input_device = QWaylandSeat::fromSeatResource(resource: input_device_super);
107 emit q->startMove(seat: input_device);
108}
109
110void QWaylandWlShellSurfacePrivate::shell_surface_resize(Resource *resource,
111 struct wl_resource *input_device_super,
112 uint32_t serial,
113 uint32_t edges)
114{
115 Q_UNUSED(resource);
116 Q_UNUSED(serial);
117 Q_Q(QWaylandWlShellSurface);
118
119 QWaylandSeat *input_device = QWaylandSeat::fromSeatResource(resource: input_device_super);
120 emit q->startResize(seat: input_device, edges: QWaylandWlShellSurface::ResizeEdge(edges));
121}
122
123void QWaylandWlShellSurfacePrivate::shell_surface_set_toplevel(Resource *resource)
124{
125 Q_UNUSED(resource);
126 Q_Q(QWaylandWlShellSurface);
127 setWindowType(Qt::WindowType::Window);
128 emit q->setDefaultToplevel();
129}
130
131void QWaylandWlShellSurfacePrivate::shell_surface_set_transient(Resource *resource,
132 struct wl_resource *parent_surface_resource,
133 int x,
134 int y,
135 uint32_t flags)
136{
137
138 Q_UNUSED(resource);
139 Q_Q(QWaylandWlShellSurface);
140 QWaylandSurface *parent_surface = QWaylandSurface::fromResource(resource: parent_surface_resource);
141 setWindowType(Qt::WindowType::SubWindow);
142 emit q->setTransient(parentSurface: parent_surface, relativeToParent: QPoint(x,y), inactive: flags & WL_SHELL_SURFACE_TRANSIENT_INACTIVE);
143}
144
145void QWaylandWlShellSurfacePrivate::shell_surface_set_fullscreen(Resource *resource,
146 uint32_t method,
147 uint32_t framerate,
148 struct wl_resource *output_resource)
149{
150 Q_UNUSED(resource);
151 Q_UNUSED(method);
152 Q_UNUSED(framerate);
153 Q_Q(QWaylandWlShellSurface);
154 QWaylandOutput *output = output_resource
155 ? QWaylandOutput::fromResource(resource: output_resource)
156 : nullptr;
157 setWindowType(Qt::WindowType::Window);
158 emit q->setFullScreen(method: QWaylandWlShellSurface::FullScreenMethod(method), framerate, output);
159}
160
161void QWaylandWlShellSurfacePrivate::shell_surface_set_popup(Resource *resource, wl_resource *input_device, uint32_t serial, wl_resource *parent, int32_t x, int32_t y, uint32_t flags)
162{
163 Q_UNUSED(resource);
164 Q_UNUSED(serial);
165 Q_UNUSED(flags);
166 Q_Q(QWaylandWlShellSurface);
167 QWaylandSeat *input = QWaylandSeat::fromSeatResource(resource: input_device);
168 QWaylandSurface *parentSurface = QWaylandSurface::fromResource(resource: parent);
169 setWindowType(Qt::WindowType::Popup);
170 emit q->setPopup(seat: input, parentSurface, relativeToParent: QPoint(x,y));
171
172}
173
174void QWaylandWlShellSurfacePrivate::shell_surface_set_maximized(Resource *resource,
175 struct wl_resource *output_resource)
176{
177 Q_UNUSED(resource);
178 Q_Q(QWaylandWlShellSurface);
179 QWaylandOutput *output = output_resource
180 ? QWaylandOutput::fromResource(resource: output_resource)
181 : nullptr;
182 setWindowType(Qt::WindowType::Window);
183 emit q->setMaximized(output);
184}
185
186void QWaylandWlShellSurfacePrivate::shell_surface_pong(Resource *resource,
187 uint32_t serial)
188{
189 Q_UNUSED(resource);
190 Q_Q(QWaylandWlShellSurface);
191 if (m_pings.remove(value: serial))
192 emit q->pong();
193 else
194 qWarning(msg: "Received an unexpected pong!");
195}
196
197void QWaylandWlShellSurfacePrivate::shell_surface_set_title(Resource *resource,
198 const QString &title)
199{
200 Q_UNUSED(resource);
201 if (title == m_title)
202 return;
203 Q_Q(QWaylandWlShellSurface);
204 m_title = title;
205 emit q->titleChanged();
206}
207
208void QWaylandWlShellSurfacePrivate::shell_surface_set_class(Resource *resource,
209 const QString &className)
210{
211 Q_UNUSED(resource);
212 if (className == m_className)
213 return;
214 Q_Q(QWaylandWlShellSurface);
215 m_className = className;
216 emit q->classNameChanged();
217}
218
219/*!
220 * \qmltype WlShell
221 * \instantiates QWaylandWlShell
222 * \inqmlmodule QtWayland.Compositor.WlShell
223 * \since 5.8
224 * \brief Provides an extension for desktop-style user interfaces.
225 *
226 * The WlShell extension provides a way to associate a ShellSurface
227 * with a regular Wayland surface. Using the shell surface interface, the client
228 * can request that the surface is resized, moved, and so on.
229 *
230 * WlShell corresponds to the Wayland interface \c wl_shell.
231 *
232 * To provide the functionality of the shell extension in a compositor, create
233 * an instance of the WlShell component and add it to the list of extensions
234 * supported by the compositor:
235 *
236 * \qml
237 * import QtWayland.Compositor.WlShell
238 *
239 * WaylandCompositor {
240 * WlShell {
241 * // ...
242 * }
243 * }
244 * \endqml
245 */
246
247/*!
248 * \class QWaylandWlShell
249 * \inmodule QtWaylandCompositor
250 * \since 5.8
251 * \brief The QWaylandWlShell class is an extension for desktop-style user interfaces.
252 *
253 * The QWaylandWlShell extension provides a way to associate a QWaylandWlShellSurface with
254 * a regular Wayland surface. Using the shell surface interface, the client
255 * can request that the surface is resized, moved, and so on.
256 *
257 * WlShell corresponds to the Wayland interface \c wl_shell.
258 */
259
260/*!
261 * Constructs a QWaylandWlShell object.
262 */
263QWaylandWlShell::QWaylandWlShell()
264 : QWaylandShellTemplate<QWaylandWlShell>(*new QWaylandWlShellPrivate())
265{ }
266
267/*!
268 * Constructs a QWaylandWlShell object for the provided \a compositor.
269 */
270QWaylandWlShell::QWaylandWlShell(QWaylandCompositor *compositor)
271 : QWaylandShellTemplate<QWaylandWlShell>(compositor, *new QWaylandWlShellPrivate())
272{ }
273
274
275/*!
276 * Initializes the WlShell extension.
277 */
278void QWaylandWlShell::initialize()
279{
280 Q_D(QWaylandWlShell);
281 QWaylandShellTemplate::initialize();
282 QWaylandCompositor *compositor = qobject_cast<QWaylandCompositor *>(object: extensionContainer());
283 if (!compositor) {
284 qWarning() << "Failed to find QWaylandCompositor when initializing QWaylandWlShell";
285 return;
286 }
287 d->init(compositor->display(), 1);
288}
289
290QList<QWaylandWlShellSurface *> QWaylandWlShell::shellSurfaces() const
291{
292 Q_D(const QWaylandWlShell);
293 return d->m_shellSurfaces;
294}
295
296QList<QWaylandWlShellSurface *> QWaylandWlShell::shellSurfacesForClient(QWaylandClient *client) const
297{
298 Q_D(const QWaylandWlShell);
299 QList<QWaylandWlShellSurface *> surfsForClient;
300 for (QWaylandWlShellSurface *shellSurface : d->m_shellSurfaces) {
301 if (shellSurface->surface() && shellSurface->surface()->client() == client)
302 surfsForClient.append(shellSurface);
303 }
304 return surfsForClient;
305}
306
307QList<QWaylandWlShellSurface *> QWaylandWlShell::mappedPopups() const
308{
309 Q_D(const QWaylandWlShell);
310 QList<QWaylandWlShellSurface *> popupSurfaces;
311 for (QWaylandWlShellSurface *shellSurface : d->m_shellSurfaces) {
312 if (shellSurface->windowType() == Qt::WindowType::Popup
313 && shellSurface->surface() && shellSurface->surface()->hasContent()) {
314 popupSurfaces.append(shellSurface);
315 }
316 }
317 return popupSurfaces;
318}
319
320QWaylandClient *QWaylandWlShell::popupClient() const
321{
322 Q_D(const QWaylandWlShell);
323 for (QWaylandWlShellSurface *shellSurface : d->m_shellSurfaces) {
324 if (shellSurface->windowType() == Qt::WindowType::Popup
325 && shellSurface->surface() && shellSurface->surface()->hasContent()) {
326 return shellSurface->surface()->client();
327 }
328 }
329 return nullptr;
330}
331
332void QWaylandWlShell::closeAllPopups()
333{
334 const auto mapped = mappedPopups();
335 for (QWaylandWlShellSurface *shellSurface : mapped)
336 shellSurface->sendPopupDone();
337}
338
339/*!
340 * Returns the Wayland interface for the QWaylandWlShell.
341 */
342const struct wl_interface *QWaylandWlShell::interface()
343{
344 return QWaylandWlShellPrivate::interface();
345}
346
347/*!
348 * \qmlsignal void WlShell::wlShellSurfaceRequested(WaylandSurface surface, WaylandResource resource)
349 *
350 * This signal is emitted when the client has requested a \c wl_shell_surface to be associated with
351 * \a surface. The handler for this signal may create a shell surface for \a resource and initialize
352 * it within the scope of the signal emission. Otherwise a WlShellSurface will be created
353 * automatically.
354 */
355
356/*!
357 * \fn void QWaylandWlShell::wlShellSurfaceRequested(QWaylandSurface *surface, const QWaylandResource &resource)
358 *
359 * This signal is emitted when the client has requested a \c wl_shell_surface to be associated with
360 * \a surface. The handler for this signal may create a shell surface for \a resource and initialize
361 * it within the scope of the signal emission. Otherwise a QWaylandWlShellSurface will be created
362 * automatically.
363 */
364
365/*!
366 * \qmlsignal void WlShell::wlShellSurfaceCreated(WlShellSurface shellSurface)
367 *
368 * This signal is emitted when the client has created a \c wl_shell_surface.
369 * A common use case is to let the handler of this signal instantiate a ShellSurfaceItem or
370 * WaylandQuickItem for displaying \a shellSurface in a QtQuick scene.
371 */
372
373/*!
374 * \fn void QWaylandWlShell::wlShellSurfaceCreated(QWaylandWlShellSurface *shellSurface)
375 *
376 * This signal is emitted when the client has created a \c wl_shell_surface.
377 * A common use case is to let the handler of this signal instantiate a QWaylandShellSurfaceItem or
378 * QWaylandQuickItem for displaying \a shellSurface in a QtQuick scene.
379 */
380
381/*!
382 * \internal
383 */
384QByteArray QWaylandWlShell::interfaceName()
385{
386 return QWaylandWlShellPrivate::interfaceName();
387}
388
389/*!
390 * \qmltype WlShellSurface
391 * \instantiates QWaylandWlShellSurface
392 * \inqmlmodule QtWayland.Compositor.WlShell
393 * \since 5.8
394 * \brief Provides a \c wl_shell_surface that offers desktop-style compositor-specific features to a surface.
395 *
396 * This type is part of the \l{WlShell} extension and provides a way to extend
397 * the functionality of an existing WaylandSurface with features specific to desktop-style
398 * compositors, such as resizing and moving the surface.
399 *
400 * It corresponds to the Wayland interface \c wl_shell_surface.
401 */
402
403/*!
404 * \class QWaylandWlShellSurface
405 * \inmodule QtWaylandCompositor
406 * \since 5.8
407 * \brief The QWaylandWlShellSurface class provides desktop-style compositor-specific features to a surface.
408 *
409 * This class is part of the QWaylandWlShell extension and provides a way to extend
410 * the functionality of an existing QWaylandSurface with features specific to desktop-style
411 * compositors, such as resizing and moving the surface.
412 *
413 * It corresponds to the Wayland interface \c wl_shell_surface.
414 */
415
416/*!
417 * Constructs a QWaylandWlShellSurface.
418 */
419QWaylandWlShellSurface::QWaylandWlShellSurface()
420 : QWaylandShellSurfaceTemplate<QWaylandWlShellSurface>(*new QWaylandWlShellSurfacePrivate)
421{
422}
423
424/*!
425 * Constructs a QWaylandWlShellSurface for \a surface and initializes it with the given \a shell and resource \a res.
426 */
427QWaylandWlShellSurface::QWaylandWlShellSurface(QWaylandWlShell *shell, QWaylandSurface *surface, const QWaylandResource &res)
428 : QWaylandShellSurfaceTemplate<QWaylandWlShellSurface>(*new QWaylandWlShellSurfacePrivate)
429{
430 initialize(shell, surface, resource: res);
431}
432
433QWaylandWlShellSurface::~QWaylandWlShellSurface()
434{
435 Q_D(QWaylandWlShellSurface);
436 if (d->m_shell)
437 QWaylandWlShellPrivate::get(shell: d->m_shell)->unregisterShellSurface(shellSurface: this);
438}
439
440/*!
441 * \qmlmethod void WlShellSurface::initialize(WlShell shell, WaylandSurface surface, WaylandResource resource)
442 *
443 * Initializes the WlShellSurface and associates it with the given \a shell, \a surface, and \a resource.
444 */
445
446/*!
447 * Initializes the QWaylandWlShellSurface and associates it with the given \a shell, \a surface, and \a resource.
448 */
449void QWaylandWlShellSurface::initialize(QWaylandWlShell *shell, QWaylandSurface *surface, const QWaylandResource &resource)
450{
451 Q_D(QWaylandWlShellSurface);
452 d->m_shell = shell;
453 d->m_surface = surface;
454 d->init(resource.resource());
455 setExtensionContainer(surface);
456 emit surfaceChanged();
457 emit shellChanged();
458 QWaylandCompositorExtension::initialize();
459}
460
461/*!
462 * \internal
463 */
464void QWaylandWlShellSurface::initialize()
465{
466 QWaylandCompositorExtension::initialize();
467}
468
469const struct wl_interface *QWaylandWlShellSurface::interface()
470{
471 return QWaylandWlShellSurfacePrivate::interface();
472}
473
474/*!
475 * \internal
476 */
477QByteArray QWaylandWlShellSurface::interfaceName()
478{
479 return QWaylandWlShellSurfacePrivate::interfaceName();
480}
481
482QSize QWaylandWlShellSurface::sizeForResize(const QSizeF &size, const QPointF &delta, QWaylandWlShellSurface::ResizeEdge edge)
483{
484 qreal width = size.width();
485 qreal height = size.height();
486 if (edge & LeftEdge)
487 width -= delta.x();
488 else if (edge & RightEdge)
489 width += delta.x();
490
491 if (edge & TopEdge)
492 height -= delta.y();
493 else if (edge & BottomEdge)
494 height += delta.y();
495
496 QSizeF newSize(qMax(a: width, b: 1.0), qMax(a: height, b: 1.0));
497 return newSize.toSize();
498}
499
500/*!
501 * \enum QWaylandWlShellSurface::ResizeEdge
502 *
503 * This enum type provides a way to specify an edge or corner of
504 * the surface.
505 *
506 * \value NoneEdge No edge.
507 * \value TopEdge The top edge.
508 * \value BottomEdge The bottom edge.
509 * \value LeftEdge The left edge.
510 * \value TopLeftEdge The top left corner.
511 * \value BottomLeftEdge The bottom left corner.
512 * \value RightEdge The right edge.
513 * \value TopRightEdge The top right corner.
514 * \value BottomRightEdge The bottom right corner.
515 */
516
517/*!
518 * \qmlmethod void WlShellSurface::sendConfigure(size s, enum edges)
519 *
520 * Sends a configure event to the client, suggesting that it resize its surface to
521 * the provided size \a s. The \a edges provide a hint about how the surface
522 * was resized.
523 */
524
525/*!
526 * Sends a configure event to the client, suggesting that it resize its surface to
527 * the provided \a size. The \a edges provide a hint about how the surface
528 * was resized.
529 */
530void QWaylandWlShellSurface::sendConfigure(const QSize &size, ResizeEdge edges)
531{
532 Q_D(QWaylandWlShellSurface);
533 if (!size.isValid()) {
534 qWarning() << "Can't configure wl_shell_surface with an invalid size" << size;
535 return;
536 }
537 d->send_configure(edges, size.width(), size.height());
538}
539
540/*!
541 * \qmlmethod void WlShellSurface::sendPopupDone()
542 *
543 * Sends a popup_done event to the client to indicate that the user has clicked
544 * somewhere outside the client's surfaces.
545 */
546
547/*!
548 * Sends a popup_done event to the client to indicate that the user has clicked
549 * somewhere outside the client's surfaces.
550 */
551void QWaylandWlShellSurface::sendPopupDone()
552{
553 Q_D(QWaylandWlShellSurface);
554 d->send_popup_done();
555}
556
557#if QT_CONFIG(wayland_compositor_quick)
558QWaylandQuickShellIntegration *QWaylandWlShellSurface::createIntegration(QWaylandQuickShellSurfaceItem *item)
559{
560 return new QtWayland::WlShellIntegration(item);
561}
562#endif
563
564/*!
565 * \qmlproperty WaylandSurface WlShellSurface::surface
566 *
567 * This property holds the \c wl_surface associated with this WlShellSurface.
568 */
569
570/*!
571 * \property QWaylandWlShellSurface::surface
572 *
573 * This property holds the surface associated with this QWaylandWlShellSurface.
574 */
575QWaylandSurface *QWaylandWlShellSurface::surface() const
576{
577 Q_D(const QWaylandWlShellSurface);
578 return d->m_surface;
579}
580
581/*!
582 * \qmlproperty WlShell WlShellSurface::shell
583 *
584 * This property holds the shell associated with this WlShellSurface.
585 */
586
587/*!
588 * \property QWaylandWlShellSurface::shell
589 *
590 * This property holds the shell associated with this QWaylandWlShellSurface.
591 */
592QWaylandWlShell *QWaylandWlShellSurface::shell() const
593{
594 Q_D(const QWaylandWlShellSurface);
595 return d->m_shell;
596}
597
598/*!
599 * \qmlproperty enum WlShellSurface::windowType
600 *
601 * This property holds the window type of the WlShellSurface.
602 */
603
604Qt::WindowType QWaylandWlShellSurface::windowType() const
605{
606 Q_D(const QWaylandWlShellSurface);
607 return d->m_windowType;
608}
609
610/*!
611 * \qmlproperty string WlShellSurface::title
612 *
613 * This property holds the title of the WlShellSurface.
614 */
615
616/*!
617 * \property QWaylandWlShellSurface::title
618 *
619 * This property holds the title of the QWaylandWlShellSurface.
620 */
621QString QWaylandWlShellSurface::title() const
622{
623 Q_D(const QWaylandWlShellSurface);
624 return d->m_title;
625}
626
627/*!
628 * \qmlproperty string WlShellSurface::className
629 *
630 * This property holds the class name of the WlShellSurface.
631 */
632
633/*!
634 * \property QWaylandWlShellSurface::className
635 *
636 * This property holds the class name of the QWaylandWlShellSurface.
637 */
638QString QWaylandWlShellSurface::className() const
639{
640 Q_D(const QWaylandWlShellSurface);
641 return d->m_className;
642}
643
644QWaylandSurfaceRole *QWaylandWlShellSurface::role()
645{
646 return &QWaylandWlShellSurfacePrivate::s_role;
647}
648
649/*!
650 * \qmlmethod void WlShellSurface::ping()
651 *
652 * Sends a ping event to the client. If the client replies to the event the pong
653 * signal will be emitted.
654 */
655
656/*!
657 * Sends a ping event to the client. If the client replies to the event the pong
658 * signal will be emitted.
659 */
660void QWaylandWlShellSurface::ping()
661{
662 Q_D(QWaylandWlShellSurface);
663 uint32_t serial = d->m_surface->compositor()->nextSerial();
664 d->ping(serial);
665}
666
667/*!
668 * Returns the QWaylandWlShellSurface object associated with the given \a resource, or null if no such object exists.
669 */
670QWaylandWlShellSurface *QWaylandWlShellSurface::fromResource(wl_resource *resource)
671{
672 if (auto p = QtWayland::fromResource<QWaylandWlShellSurfacePrivate *>(resource))
673 return p->q_func();
674 return nullptr;
675}
676
677QT_END_NAMESPACE
678
679#include "moc_qwaylandwlshell.cpp"
680

source code of qtwayland/src/compositor/extensions/qwaylandwlshell.cpp