Revision control
++ // Copyright (C) 2018 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++ // Qt-Security score:critical reason:network-protocol
++
++ #include "qwaylandxdgshell.h"
++ #include "qwaylandxdgshell_p.h"
++
++ #if QT_CONFIG(wayland_compositor_quick)
++ #include "qwaylandxdgshellintegration_p.h"
++ #endif
++ #include <QtWaylandCompositor/private/qwaylandutils_p.h>
++
++ #include "qwaylandxdgdialogv1_p.h"
++
++ #include <QtWaylandCompositor/QWaylandCompositor>
++ #include <QtWaylandCompositor/QWaylandSeat>
++ #include <QtWaylandCompositor/QWaylandSurface>
++ #include <QtWaylandCompositor/QWaylandSurfaceRole>
++ #include <QtWaylandCompositor/QWaylandResource>
++
++ #include <QtCore/QObject>
++
++ #include <algorithm>
++
++ QT_BEGIN_NAMESPACE
++
++ QWaylandXdgShellPrivate::QWaylandXdgShellPrivate()
++ {
++ }
++
++ void QWaylandXdgShellPrivate::ping(QtWaylandServer::xdg_wm_base::Resource *resource, uint32_t serial)
++ {
++ m_pings.insert(serial);
++ send_ping(resource->handle, serial);
++ }
++
++ void QWaylandXdgShellPrivate::registerXdgSurface(QWaylandXdgSurface *xdgSurface)
++ {
++ m_xdgSurfaces.insert(xdgSurface->surface()->client()->client(), xdgSurface);
++ }
++
++ void QWaylandXdgShellPrivate::unregisterXdgSurface(QWaylandXdgSurface *xdgSurface)
++ {
++ auto xdgSurfacePrivate = QWaylandXdgSurfacePrivate::get(xdgSurface);
++ if (!m_xdgSurfaces.remove(xdgSurfacePrivate->resource()->client(), xdgSurface))
++ qWarning("%s Unexpected state. Can't find registered xdg surface\n", Q_FUNC_INFO);
++ }
++
++ QWaylandXdgSurface *QWaylandXdgShellPrivate::xdgSurfaceFromSurface(QWaylandSurface *surface)
++ {
++ for (QWaylandXdgSurface *xdgSurface : std::as_const(m_xdgSurfaces)) {
++ if (surface == xdgSurface->surface())
++ return xdgSurface;
++ }
++ return nullptr;
++ }
++
++ void QWaylandXdgShellPrivate::xdg_wm_base_destroy(Resource *resource)
++ {
++ if (!m_xdgSurfaces.values(resource->client()).empty())
++ wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_DEFUNCT_SURFACES,
++ "xdg_shell was destroyed before children");
++
++ wl_resource_destroy(resource->handle);
++ }
++
++ void QWaylandXdgShellPrivate::xdg_wm_base_create_positioner(QtWaylandServer::xdg_wm_base::Resource *resource, uint32_t id)
++ {
++ QWaylandResource positionerResource(wl_resource_create(resource->client(), &xdg_positioner_interface,
++ wl_resource_get_version(resource->handle), id));
++
++ new QWaylandXdgPositioner(positionerResource);
++ }
++
++ void QWaylandXdgShellPrivate::xdg_wm_base_get_xdg_surface(Resource *resource, uint32_t id, wl_resource *surfaceResource)
++ {
++ Q_Q(QWaylandXdgShell);
++ QWaylandSurface *surface = QWaylandSurface::fromResource(surfaceResource);
++
++ if (surface->hasContent()) {
++ //TODO: According to the spec, this is a client error, but there's no appropriate error code
++ qWarning() << "get_xdg_surface requested on a xdg_surface with content";
++ }
++
++ QWaylandResource xdgSurfaceResource(wl_resource_create(resource->client(), &xdg_surface_interface,
++ wl_resource_get_version(resource->handle), id));
++
++ QWaylandXdgSurface *xdgSurface = new QWaylandXdgSurface(q, surface, xdgSurfaceResource);
++
++ registerXdgSurface(xdgSurface);
++ emit q->xdgSurfaceCreated(xdgSurface);
++ }
++
++ void QWaylandXdgShellPrivate::xdg_wm_base_pong(Resource *resource, uint32_t serial)
++ {
++ Q_UNUSED(resource);
++ Q_Q(QWaylandXdgShell);
++ if (m_pings.remove(serial))
++ emit q->pong(serial);
++ else
++ qWarning("Received an unexpected pong!");
++ }
++
++ /*!
++ * \qmltype XdgShell
++ * \nativetype QWaylandXdgShell
++ * \inqmlmodule QtWayland.Compositor.XdgShell
++ * \since 5.12
++ * \brief Provides an extension for desktop-style user interfaces.
++ *
++ * The XdgShell extension provides a way to associate a XdgToplevel or XdgPopup
++ * with a regular Wayland surface. Using the XdgToplevel interface, the client
++ * can request that the surface is resized, moved, and so on.
++ *
++ * XdgShell corresponds to the Wayland interface, \c xdg_shell.
++ *
++ * To provide the functionality of the shell extension in a compositor, create
++ * an instance of the XdgShell component and add it to the list of extensions
++ * supported by the compositor:
++ *
++ * \qml
++ * import QtWayland.Compositor.XdgShell
++ *
++ * WaylandCompositor {
++ * XdgShell {
++ * // ...
++ * }
++ * }
++ * \endqml
++ */
++
++ /*!
++ * \class QWaylandXdgShell
++ * \inmodule QtWaylandCompositor
++ * \since 5.12
++ * \brief The QWaylandXdgShell class is an extension for desktop-style user interfaces.
++ *
++ * The QWaylandXdgShell extension provides a way to associate a QWaylandXdgToplevel or
++ * QWaylandXdgPopup with a regular Wayland surface. Using the QWaylandXdgToplevel interface,
++ * the client can request that the surface is resized, moved, and so on.
++ *
++ * QWaylandXdgShell corresponds to the Wayland interface, \c xdg_shell.
++ */
++
++ /*!
++ * Constructs a QWaylandXdgShell object.
++ */
++ QWaylandXdgShell::QWaylandXdgShell()
++ : QWaylandShellTemplate<QWaylandXdgShell>(*new QWaylandXdgShellPrivate())
++ {
++ }
++
++ /*!
++ * Constructs a QWaylandXdgShell object for the provided \a compositor.
++ */
++ QWaylandXdgShell::QWaylandXdgShell(QWaylandCompositor *compositor)
++ : QWaylandShellTemplate<QWaylandXdgShell>(compositor, *new QWaylandXdgShellPrivate())
++ {
++ }
++
++ /*!
++ * Initializes the shell extension.
++ */
++ void QWaylandXdgShell::initialize()
++ {
++ Q_D(QWaylandXdgShell);
++ QWaylandShellTemplate::initialize();
++ QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer());
++ if (!compositor) {
++ qWarning() << "Failed to find QWaylandCompositor when initializing QWaylandXdgShell";
++ return;
++ }
++ d->init(compositor->display(), 1);
++
++ handleSeatChanged(compositor->defaultSeat(), nullptr);
++
++ connect(compositor, &QWaylandCompositor::defaultSeatChanged,
++ this, &QWaylandXdgShell::handleSeatChanged);
++
++ // Support the dialog extension unconditionally.
++ QObject *dialogExtension = new QWaylandXdgDialogV1Global(compositor);
++ dialogExtension->setParent(this);
++ }
++
++ /*!
++ * Returns the Wayland interface for the QWaylandXdgShell.
++ */
++ const struct wl_interface *QWaylandXdgShell::interface()
++ {
++ return QWaylandXdgShellPrivate::interface();
++ }
++
++ QByteArray QWaylandXdgShell::interfaceName()
++ {
++ return QWaylandXdgShellPrivate::interfaceName();
++ }
++
++ /*!
++ * \qmlmethod void XdgShell::ping(WaylandClient client)
++ *
++ * Sends a ping event to \a client. If the client replies to the event the
++ * \l pong signal will be emitted.
++ */
++
++ /*!
++ * Sends a ping event to \a client. If the client replies to the event the
++ * \l pong signal will be emitted.
++ */
++ uint QWaylandXdgShell::ping(QWaylandClient *client)
++ {
++ Q_D(QWaylandXdgShell);
++
++ QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer());
++ Q_ASSERT(compositor);
++
++ uint32_t serial = compositor->nextSerial();
++
++ QWaylandXdgShellPrivate::Resource *clientResource = d->resourceMap().value(client->client(), nullptr);
++ Q_ASSERT(clientResource);
++
++ d->ping(clientResource, serial);
++ return serial;
++ }
++
++ void QWaylandXdgShell::handleSeatChanged(QWaylandSeat *newSeat, QWaylandSeat *oldSeat)
++ {
++ if (oldSeat != nullptr) {
++ disconnect(oldSeat, &QWaylandSeat::keyboardFocusChanged,
++ this, &QWaylandXdgShell::handleFocusChanged);
++ }
++
++ if (newSeat != nullptr) {
++ connect(newSeat, &QWaylandSeat::keyboardFocusChanged,
++ this, &QWaylandXdgShell::handleFocusChanged);
++ }
++ }
++
++ void QWaylandXdgShell::handleFocusChanged(QWaylandSurface *newSurface, QWaylandSurface *oldSurface)
++ {
++ Q_D(QWaylandXdgShell);
++
++ QWaylandXdgSurface *newXdgSurface = d->xdgSurfaceFromSurface(newSurface);
++ QWaylandXdgSurface *oldXdgSurface = d->xdgSurfaceFromSurface(oldSurface);
++
++ if (newXdgSurface)
++ QWaylandXdgSurfacePrivate::get(newXdgSurface)->handleFocusReceived();
++
++ if (oldXdgSurface)
++ QWaylandXdgSurfacePrivate::get(oldXdgSurface)->handleFocusLost();
++ }
++
++ QWaylandXdgSurfacePrivate::QWaylandXdgSurfacePrivate()
++ {
++ }
++
++ void QWaylandXdgSurfacePrivate::setWindowType(Qt::WindowType windowType)
++ {
++ if (m_windowType == windowType)
++ return;
++
++ m_windowType = windowType;
++
++ Q_Q(QWaylandXdgSurface);
++ emit q->windowTypeChanged();
++ }
++
++ void QWaylandXdgSurfacePrivate::handleFocusLost()
++ {
++ if (m_toplevel)
++ QWaylandXdgToplevelPrivate::get(m_toplevel)->handleFocusLost();
++ }
++
++ void QWaylandXdgSurfacePrivate::handleFocusReceived()
++ {
++ if (m_toplevel)
++ QWaylandXdgToplevelPrivate::get(m_toplevel)->handleFocusReceived();
++ }
++
++ QRect QWaylandXdgSurfacePrivate::calculateFallbackWindowGeometry() const
++ {
++ // TODO: The unset window geometry should include subsurfaces as well, so this solution
++ // won't work too well on those kinds of clients.
++ return QRect(QPoint(), m_surface->destinationSize());
++ }
++
++ void QWaylandXdgSurfacePrivate::updateFallbackWindowGeometry()
++ {
++ Q_Q(QWaylandXdgSurface);
++ if (!m_unsetWindowGeometry)
++ return;
++
++ const QRect unsetGeometry = calculateFallbackWindowGeometry();
++ if (unsetGeometry == m_windowGeometry)
++ return;
++
++ m_windowGeometry = unsetGeometry;
++ emit q->windowGeometryChanged();
++ }
++
++ void QWaylandXdgSurfacePrivate::xdg_surface_destroy_resource(QtWaylandServer::xdg_surface::Resource *resource)
++ {
++ Q_UNUSED(resource);
++ Q_Q(QWaylandXdgSurface);
++ QWaylandXdgShellPrivate::get(m_xdgShell)->unregisterXdgSurface(q);
++ delete q;
++ }
++
++ void QWaylandXdgSurfacePrivate::xdg_surface_destroy(QtWaylandServer::xdg_surface::Resource *resource)
++ {
++ wl_resource_destroy(resource->handle);
++ }
++
++ void QWaylandXdgSurfacePrivate::xdg_surface_get_toplevel(QtWaylandServer::xdg_surface::Resource *resource, uint32_t id)
++ {
++ Q_Q(QWaylandXdgSurface);
++
++ if (m_toplevel || m_popup) {
++ wl_resource_post_error(resource->handle, XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED,
++ "xdg_surface already has a role object");
++ return;
++ }
++
++ if (!m_surface->setRole(QWaylandXdgToplevel::role(), resource->handle, XDG_WM_BASE_ERROR_ROLE))
++ return;
++
++ QWaylandResource topLevelResource(wl_resource_create(resource->client(), &xdg_toplevel_interface,
++ wl_resource_get_version(resource->handle), id));
++
++ m_toplevel = new QWaylandXdgToplevel(q, topLevelResource);
++ emit q->toplevelCreated();
++ emit m_xdgShell->toplevelCreated(m_toplevel, q);
++ q->connect(m_toplevel, &QWaylandXdgToplevel::modalChanged, q, [q, this](){
++ q->setModal(m_toplevel->isModal());
++ });
++ }
++
++ void QWaylandXdgSurfacePrivate::xdg_surface_get_popup(QtWaylandServer::xdg_surface::Resource *resource, uint32_t id, wl_resource *parentResource, wl_resource *positionerResource)
++ {
++ Q_Q(QWaylandXdgSurface);
++
++ if (m_toplevel || m_popup) {
++ wl_resource_post_error(resource->handle, XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED,
++ "xdg_surface already has a role object");
++ return;
++ }
++
++ QWaylandXdgSurface *parent = QWaylandXdgSurface::fromResource(parentResource);
++ if (!parent) {
++ wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT,
++ "xdg_surface.get_popup with invalid popup parent");
++ return;
++ }
++
++ QWaylandXdgPositioner *positioner = QWaylandXdgPositioner::fromResource(positionerResource);
++ if (!positioner) {
++ wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_INVALID_POSITIONER,
++ "xdg_surface.get_popup without positioner");
++ return;
++ }
++
++ if (!positioner->m_data.isComplete()) {
++ QWaylandXdgPositionerData p = positioner->m_data;
++ wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_INVALID_POSITIONER,
++ "xdg_surface.get_popup with invalid positioner (size: %dx%d, anchorRect: %dx%d)",
++ p.size.width(), p.size.height(), p.anchorRect.width(), p.anchorRect.height());
++ return;
++ }
++
++ QRect anchorBounds(QPoint(0, 0), parent->windowGeometry().size());
++ if (!anchorBounds.contains(positioner->m_data.anchorRect)) {
++ // TODO: this is a protocol error and should ideally be handled like this:
++ //wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_INVALID_POSITIONER,
++ // "xdg_positioner anchor rect extends beyound its parent's window geometry");
++ //return;
++ // However, our own clients currently do this, so we'll settle for a gentle warning instead.
++ qCWarning(qLcWaylandCompositor) << "Ignoring client protocol error: xdg_positioner anchor"
++ << "rect extends beyond its parent's window geometry";
++ }
++
++ if (!m_surface->setRole(QWaylandXdgPopup::role(), resource->handle, XDG_WM_BASE_ERROR_ROLE))
++ return;
++
++ QWaylandResource popupResource(wl_resource_create(resource->client(), &xdg_popup_interface,
++ wl_resource_get_version(resource->handle), id));
++
++ m_popup = new QWaylandXdgPopup(q, parent, positioner, popupResource);
++ emit q->popupCreated();
++ emit m_xdgShell->popupCreated(m_popup, q);
++ }
++
++ void QWaylandXdgSurfacePrivate::xdg_surface_ack_configure(QtWaylandServer::xdg_surface::Resource *resource, uint32_t serial)
++ {
++ if (m_toplevel) {
++ QWaylandXdgToplevelPrivate::get(m_toplevel)->handleAckConfigure(serial);
++ } else if (m_popup) {
++ QWaylandXdgPopupPrivate::get(m_popup)->handleAckConfigure(serial);
++ } else {
++ wl_resource_post_error(resource->handle, XDG_SURFACE_ERROR_NOT_CONSTRUCTED,
++ "ack_configure requested on an unconstructed xdg_surface");
++ }
++ }
++
++ void QWaylandXdgSurfacePrivate::xdg_surface_set_window_geometry(QtWaylandServer::xdg_surface::Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
++ {
++ Q_Q(QWaylandXdgSurface);
++
++ if (!q->surface()->role()) {
++ wl_resource_post_error(resource->handle, XDG_SURFACE_ERROR_NOT_CONSTRUCTED,
++ "set_window_geometry requested on an unconstructed xdg_surface");
++ return;
++ }
++
++ if (width <= 0 || height <= 0) {
++ // The protocol spec says "setting an invalid size will raise an error". But doesn't tell
++ // which error to raise, and there's no fitting error in the xdg_surface_error enum.
++ // So until this is fixed, just output a warning and return.
++ qWarning() << "Invalid (non-positive) dimensions received in set_window_geometry";
++ return;
++ }
++
++ m_unsetWindowGeometry = false;
++
++ QRect geometry(x, y, width, height);
++
++ if (m_windowGeometry == geometry)
++ return;
++
++ m_windowGeometry = geometry;
++ emit q->windowGeometryChanged();
++ }
++
++ /*!
++ * \qmltype XdgSurface
++ * \nativetype QWaylandXdgSurface
++ * \inqmlmodule QtWayland.Compositor.XdgShell
++ * \since 5.12
++ * \brief XdgSurface provides desktop-style compositor-specific features to an xdg surface.
++ *
++ * This type is part of the \l{XdgShell} extension and provides a way to
++ * extend the functionality of an existing \l{WaylandSurface} with features
++ * specific to desktop-style compositors, such as resizing and moving the
++ * surface.
++ *
++ * It corresponds to the Wayland interface \c xdg_surface.
++ */
++
++ /*!
++ * \class QWaylandXdgSurface
++ * \inmodule QtWaylandCompositor
++ * \since 5.12
++ * \brief The QWaylandXdgSurface class provides desktop-style compositor-specific features to an xdg surface.
++ *
++ * This class is part of the QWaylandXdgShell extension and provides a way to
++ * extend the functionality of an existing QWaylandSurface with features
++ * specific to desktop-style compositors, such as resizing and moving the
++ * surface.
++ *
++ * It corresponds to the Wayland interface \c xdg_surface.
++ */
++
++ /*!
++ * Constructs a QWaylandXdgSurface.
++ */
++ QWaylandXdgSurface::QWaylandXdgSurface()
++ : QWaylandShellSurfaceTemplate<QWaylandXdgSurface>(*new QWaylandXdgSurfacePrivate)
++ {
++ }
++
++ /*!
++ * Constructs a QWaylandXdgSurface for \a surface and initializes it with the
++ * given \a xdgShell, \a surface, and resource \a res.
++ */
++ QWaylandXdgSurface::QWaylandXdgSurface(QWaylandXdgShell *xdgShell, QWaylandSurface *surface, const QWaylandResource &res)
++ : QWaylandShellSurfaceTemplate<QWaylandXdgSurface>(*new QWaylandXdgSurfacePrivate)
++ {
++ initialize(xdgShell, surface, res);
++ }
++
++ /*!
++ * \qmlmethod void XdgSurface::initialize(object xdgShell, object surface, object client, int id)
++ *
++ * Initializes the XdgSurface, associating it with the given \a xdgShell, \a surface,
++ * \a client, and \a id.
++ */
++
++ /*!
++ * Initializes the QWaylandXdgSurface, associating it with the given \a xdgShell, \a surface
++ * and \a resource.
++ */
++ void QWaylandXdgSurface::initialize(QWaylandXdgShell *xdgShell, QWaylandSurface *surface, const QWaylandResource &resource)
++ {
++ Q_D(QWaylandXdgSurface);
++ d->m_xdgShell = xdgShell;
++ d->m_surface = surface;
++ d->init(resource.resource());
++ setExtensionContainer(surface);
++ d->m_windowGeometry = d->calculateFallbackWindowGeometry();
++ connect(surface, &QWaylandSurface::destinationSizeChanged, this, &QWaylandXdgSurface::handleSurfaceSizeChanged);
++ connect(surface, &QWaylandSurface::bufferScaleChanged, this, &QWaylandXdgSurface::handleBufferScaleChanged);
++ emit shellChanged();
++ emit surfaceChanged();
++ QWaylandCompositorExtension::initialize();
++ }
++
++ /*!
++ * \qmlproperty enum XdgSurface::windowType
++ *
++ * This property holds the window type of the XdgSurface.
++ */
++ Qt::WindowType QWaylandXdgSurface::windowType() const
++ {
++ Q_D(const QWaylandXdgSurface);
++ return d->m_windowType;
++ }
++
++ /*!
++ * \qmlproperty rect XdgSurface::windowGeometry
++ *
++ * This property holds the window geometry of this XdgSurface. The window
++ * geometry describes the window's visible bounds from the user's perspective.
++ * The geometry includes title bars and borders if drawn by the client, but
++ * excludes drop shadows. It is meant to be used for aligning and tiling
++ * windows.
++ */
++
++ /*!
++ * \property QWaylandXdgSurface::windowGeometry
++ *
++ * This property holds the window geometry of the QWaylandXdgSurface. The window
++ * geometry describes the window's visible bounds from the user's perspective.
++ * The geometry includes title bars and borders if drawn by the client, but
++ * excludes drop shadows. It is meant to be used for aligning and tiling
++ * windows.
++ */
++ QRect QWaylandXdgSurface::windowGeometry() const
++ {
++ Q_D(const QWaylandXdgSurface);
++ return d->m_windowGeometry;
++ }
++
++ /*!
++ * \internal
++ */
++ void QWaylandXdgSurface::initialize()
++ {
++ QWaylandCompositorExtension::initialize();
++ }
++
++ void QWaylandXdgSurface::handleSurfaceSizeChanged()
++ {
++ Q_D(QWaylandXdgSurface);
++ d->updateFallbackWindowGeometry();
++ }
++
++ void QWaylandXdgSurface::handleBufferScaleChanged()
++ {
++ Q_D(QWaylandXdgSurface);
++ d->updateFallbackWindowGeometry();
++ }
++
++ /*!
++ * \qmlproperty XdgShell XdgSurface::shell
++ *
++ * This property holds the shell associated with this XdgSurface.
++ */
++
++ /*!
++ * \property QWaylandXdgSurface::shell
++ *
++ * This property holds the shell associated with this QWaylandXdgSurface.
++ */
++ QWaylandXdgShell *QWaylandXdgSurface::shell() const
++ {
++ Q_D(const QWaylandXdgSurface);
++ return d->m_xdgShell;
++ }
++
++ /*!
++ * \qmlproperty WaylandSurface XdgSurface::surface
++ *
++ * This property holds the surface associated with this XdgSurface.
++ */
++
++ /*!
++ * \property QWaylandXdgSurface::surface
++ *
++ * This property holds the surface associated with this QWaylandXdgSurface.
++ */
++ QWaylandSurface *QWaylandXdgSurface::surface() const
++ {
++ Q_D(const QWaylandXdgSurface);
++ return d->m_surface;
++ }
++
++ /*!
++ * \qmlproperty XdgToplevel XdgSurface::toplevel
++ *
++ * This property holds the properties and methods that are specific to the
++ * toplevel XdgSurface.
++ *
++ * \sa popup, XdgShell::toplevelCreated
++ */
++
++ /*!
++ * \property QWaylandXdgSurface::toplevel
++ *
++ * This property holds the properties and methods that are specific to the
++ * toplevel QWaylandXdgSurface.
++ *
++ * \sa QWaylandXdgSurface::popup, QWaylandXdgShell::toplevelCreated
++ */
++ QWaylandXdgToplevel *QWaylandXdgSurface::toplevel() const
++ {
++ Q_D(const QWaylandXdgSurface);
++ return d->m_toplevel;
++ }
++
++ /*!
++ * \qmlproperty XdgPopup XdgSurface::popup
++ *
++ * This property holds the properties and methods that are specific to the
++ * popup XdgSurface.
++ *
++ * \sa toplevel, XdgShell::popupCreated
++ */
++
++ /*!
++ * \property QWaylandXdgSurface::popup
++ *
++ * This property holds the properties and methods that are specific to the
++ * popup QWaylandXdgSurface.
++ *
++ * \sa QWaylandXdgSurface::toplevel, QWaylandXdgShell::popupCreated
++ */
++ QWaylandXdgPopup *QWaylandXdgSurface::popup() const
++ {
++ Q_D(const QWaylandXdgSurface);
++ return d->m_popup;
++ }
++
++ /*!
++ * Returns the Wayland interface for the QWaylandXdgSurface.
++ */
++ const wl_interface *QWaylandXdgSurface::interface()
++ {
++ return QWaylandXdgSurfacePrivate::interface();
++ }
++
++ /*!
++ * \internal
++ */
++ QByteArray QWaylandXdgSurface::interfaceName()
++ {
++ return QWaylandXdgSurfacePrivate::interfaceName();
++ }
++
++ /*!
++ * Returns the QWaylandXdgSurface corresponding to the \a resource.
++ */
++ QWaylandXdgSurface *QWaylandXdgSurface::fromResource(wl_resource *resource)
++ {
++ if (auto p = QtWayland::fromResource<QWaylandXdgSurfacePrivate *>(resource))
++ return p->q_func();
++ return nullptr;
++ }
++
++ #if QT_CONFIG(wayland_compositor_quick)
++ QWaylandQuickShellIntegration *QWaylandXdgSurface::createIntegration(QWaylandQuickShellSurfaceItem *item)
++ {
++ Q_D(const QWaylandXdgSurface);
++
++ if (d->m_toplevel)
++ return new QtWayland::XdgToplevelIntegration(item);
++
++ if (d->m_popup)
++ return new QtWayland::XdgPopupIntegration(item);
++
++ return nullptr;
++ }
++ #endif
++
++ /*!
++ * \qmltype XdgToplevel
++ * \nativetype QWaylandXdgToplevel
++ * \inqmlmodule QtWayland.Compositor.XdgShell
++ * \since 5.12
++ * \brief XdgToplevel represents the toplevel window specific parts of an xdg surface.
++ *
++ * This type is part of the \l{XdgShell} extension and provides a way to
++ * extend the functionality of an XdgSurface with features
++ * specific to desktop-style windows.
++ *
++ * It corresponds to the Wayland interface \c xdg_toplevel.
++ */
++
++ /*!
++ * \class QWaylandXdgToplevel
++ * \inmodule QtWaylandCompositor
++ * \since 5.12
++ * \brief The QWaylandXdgToplevel class represents the toplevel window specific parts of an xdg surface.
++ *
++ * This class is part of the QWaylandXdgShell extension and provides a way to
++ * extend the functionality of an QWaylandXdgSurface with features
++ * specific to desktop-style windows.
++ *
++ * It corresponds to the Wayland interface \c xdg_toplevel.
++ */
++
++ /*!
++ * Constructs a QWaylandXdgToplevel for the given \a xdgSurface and \a resource.
++ */
++ QWaylandXdgToplevel::QWaylandXdgToplevel(QWaylandXdgSurface *xdgSurface, QWaylandResource &resource)
++ : QObject(*new QWaylandXdgToplevelPrivate(xdgSurface, resource))
++ {
++ QList<QWaylandXdgToplevel::State> states;
++ sendConfigure({0, 0}, states);
++ }
++
++ QWaylandXdgToplevel::~QWaylandXdgToplevel()
++ {
++ Q_D(QWaylandXdgToplevel);
++ // Usually, the decoration is destroyed by the client (according to the protocol),
++ // but if the client misbehaves, or is shut down, we need to clean up here.
++ if (Q_UNLIKELY(d->m_decoration))
++ wl_resource_destroy(d->m_decoration->resource()->handle);
++ Q_ASSERT(!d->m_decoration);
++ }
++
++ /*!
++ * \qmlproperty XdgSurface XdgToplevel::xdgSurface
++ *
++ * This property holds the XdgSurface for this XdgToplevel.
++ */
++
++ /*!
++ * \property QWaylandXdgToplevel::xdgSurface
++ *
++ * This property holds the QWaylandXdgSurface for this QWaylandXdgToplevel.
++ */
++ QWaylandXdgSurface *QWaylandXdgToplevel::xdgSurface() const
++ {
++ Q_D(const QWaylandXdgToplevel);
++ return d->m_xdgSurface;
++ }
++
++ /*!
++ * \qmlproperty XdgToplevel XdgToplevel::parentToplevel
++ *
++ * This property holds the XdgToplevel parent of this XdgToplevel.
++ */
++
++ /*!
++ * \property QWaylandXdgToplevel::parentToplevel
++ *
++ * This property holds the XdgToplevel parent of this XdgToplevel.
++ *
++ */
++ QWaylandXdgToplevel *QWaylandXdgToplevel::parentToplevel() const
++ {
++ Q_D(const QWaylandXdgToplevel);
++ return d->m_parentToplevel;
++ }
++
++ /*!
++ * \qmlproperty string XdgToplevel::title
++ *
++ * This property holds the title of the XdgToplevel.
++ */
++
++ /*!
++ * \property QWaylandXdgToplevel::title
++ *
++ * This property holds the title of the QWaylandXdgToplevel.
++ */
++ QString QWaylandXdgToplevel::title() const
++ {
++ Q_D(const QWaylandXdgToplevel);
++ return d->m_title;
++ }
++
++ /*!
++ * \qmlproperty string XdgToplevel::appId
++ *
++ * This property holds the app id of the XdgToplevel. It corresponds to the
++ * \l {QGuiApplication::desktopFileName}{desktopFileName} in Qt.
++ */
++
++ /*!
++ * \property QWaylandXdgToplevel::appId
++ *
++ * This property holds the app id of the QWaylandXdgToplevel. It corresponds to
++ * the \l {QGuiApplication::desktopFileName}{desktopFileName} in Qt.
++ */
++ QString QWaylandXdgToplevel::appId() const
++ {
++ Q_D(const QWaylandXdgToplevel);
++ return d->m_appId;
++ }
++
++ /*!
++ * \qmlproperty size XdgToplevel::maxSize
++ *
++ * This property holds the maximum size of the XdgToplevel as requested by the client.
++ *
++ * The compositor is free to ignore this value and request a larger size.
++ */
++
++ /*!
++ * \property QWaylandXdgToplevel::maxSize
++ *
++ * This property holds the maximum size of the QWaylandXdgToplevel.
++ *
++ * The compositor is free to ignore this value and request a larger size.
++ */
++ QSize QWaylandXdgToplevel::maxSize() const
++ {
++ Q_D(const QWaylandXdgToplevel);
++ return d->m_maxSize;
++ }
++
++ /*!
++ * \qmlproperty size XdgToplevel::minSize
++ *
++ * This property holds the minimum size of the XdgToplevel as requested by the client.
++ *
++ * The compositor is free to ignore this value and request a smaller size.
++ */
++
++ /*!
++ * \property QWaylandXdgToplevel::minSize
++ *
++ * This property holds the minimum size of the QWaylandXdgToplevel.
++ *
++ * The compositor is free to ignore this value and request a smaller size.
++ */
++ QSize QWaylandXdgToplevel::minSize() const
++ {
++ Q_D(const QWaylandXdgToplevel);
++ return d->m_minSize;
++ }
++
++ /*!
++ * \property QWaylandXdgToplevel::states
++ *
++ * This property holds the last states the client acknowledged for this QWaylandToplevel.
++ */
++ QList<QWaylandXdgToplevel::State> QWaylandXdgToplevel::states() const
++ {
++ Q_D(const QWaylandXdgToplevel);
++ return d->m_lastAckedConfigure.states;
++ }
++
++ /*!
++ * \qmlproperty bool XdgToplevel::maximized
++ *
++ * This property holds whether the client has acknowledged that it should be maximized.
++ */
++
++ /*!
++ * \property QWaylandXdgToplevel::maximized
++ *
++ * This property holds whether the client has acknowledged that it should be maximized.
++ */
++ bool QWaylandXdgToplevel::maximized() const
++ {
++ Q_D(const QWaylandXdgToplevel);
++ return d->m_lastAckedConfigure.states.contains(QWaylandXdgToplevel::State::MaximizedState);
++ }
++
++ /*!
++ * \qmlproperty bool XdgToplevel::fullscreen
++ *
++ * This property holds whether the client has acknowledged that it should be fullscreen.
++ */
++
++ /*!
++ * \property QWaylandXdgToplevel::fullscreen
++ *
++ * This property holds whether the client has acknowledged that it should be fullscreen.
++ */
++ bool QWaylandXdgToplevel::fullscreen() const
++ {
++ Q_D(const QWaylandXdgToplevel);
++ return d->m_lastAckedConfigure.states.contains(QWaylandXdgToplevel::State::FullscreenState);
++ }
++
++ /*!
++ * \qmlproperty bool XdgToplevel::resizing
++ *
++ * This property holds whether the client has acknowledged that it is being resized.
++ */
++
++ /*!
++ * \property QWaylandXdgToplevel::resizing
++ *
++ * This property holds whether the client has acknowledged that it is being resized.
++ */
++ bool QWaylandXdgToplevel::resizing() const
++ {
++ Q_D(const QWaylandXdgToplevel);
++ return d->m_lastAckedConfigure.states.contains(QWaylandXdgToplevel::State::ResizingState);
++ }
++
++ /*!
++ * \qmlproperty bool XdgToplevel::activated
++ *
++ * This property holds whether toplevel is drawing itself as having input focus.
++ */
++
++ /*!
++ * \property QWaylandXdgToplevel::activated
++ *
++ * This property holds whether toplevel is drawing itself as having input focus.
++ */
++ bool QWaylandXdgToplevel::activated() const
++ {
++ Q_D(const QWaylandXdgToplevel);
++ return d->m_lastAckedConfigure.states.contains(QWaylandXdgToplevel::State::ActivatedState);
++ }
++
++ /*!
++ * \qmlproperty bool XdgToplevel::modal
++ *
++ * This property holds whether toplevel blocks other windows from receiving input.
++ * \since 6.8
++ */
++
++ /*!
++ * \property QWaylandXdgToplevel::modal
++ *
++ * This property holds whether toplevel blocks other windows from receiving input.
++ * \since 6.8
++ */
++ bool QWaylandXdgToplevel::isModal() const
++ {
++ Q_D(const QWaylandXdgToplevel);
++ return d->m_modal;
++ }
++
++ void QWaylandXdgToplevel::setModal(bool newModal)
++ {
++ Q_D(QWaylandXdgToplevel);
++ if (d->m_modal == newModal)
++ return;
++ d->m_modal = newModal;
++ emit modalChanged();
++ }
++
++ /*!
++ * \enum QWaylandXdgToplevel::DecorationMode
++ *
++ * This enum type is used to specify the window decoration mode for toplevel windows.
++ *
++ * \value ServerSideDecoration The compositor should draw window decorations.
++ * \value ClientSideDecoration The client should draw window decorations.
++ */
++
++ /*!
++ * \qmlproperty enumeration XdgToplevel::decorationMode
++ *
++ * This property holds the current window decoration mode for this toplevel.
++ *
++ * The possible values are:
++ * \value XdgToplevel.ServerSideDecoration The compositor should draw window decorations.
++ * \value XdgToplevel.ClientSideDecoration The client should draw window decorations.
++ *
++ * \sa XdgDecorationManagerV1
++ */
++
++ /*!
++ * \property QWaylandXdgToplevel::decorationMode
++ *
++ * This property holds the current window decoration mode for this toplevel.
++ *
++ * \sa QWaylandXdgDecorationManagerV1
++ */
++ QWaylandXdgToplevel::DecorationMode QWaylandXdgToplevel::decorationMode() const
++ {
++ Q_D(const QWaylandXdgToplevel);
++ return d->m_decoration ? d->m_decoration->configuredMode() : DecorationMode::ClientSideDecoration;
++ }
++
++ /*!
++ * \qmlmethod size XdgToplevel::sizeForResize(size size, point delta, uint edges)
++ *
++ * Convenience for computing the new size given the current \a size, a \a delta, and
++ * the \a edges active in the drag.
++ */
++
++ /*!
++ * Convenience for computing the new size given the current \a size, a \a delta, and
++ * the \a edges active in the drag.
++ */
++ QSize QWaylandXdgToplevel::sizeForResize(const QSizeF &size, const QPointF &delta, Qt::Edges edges) const
++ {
++ qreal width = size.width();
++ qreal height = size.height();
++ if (edges & Qt::LeftEdge)
++ width -= delta.x();
++ else if (edges & Qt::RightEdge)
++ width += delta.x();
++
++ if (edges & Qt::TopEdge)
++ height -= delta.y();
++ else if (edges & Qt::BottomEdge)
++ height += delta.y();
++
++ QSize newSize = QSize(width, height)
++ .expandedTo(minSize())
++ .expandedTo({1, 1}); // We don't want to send a size of (0,0) as that means that the client decides
++
++ if (maxSize().isValid())
++ newSize = newSize.boundedTo(maxSize());
++
++ return newSize;
++ }
++
++ /*!
++ * Sends a configure event to the client. Parameter \a size contains the pixel size
++ * of the surface. A size of zero means the client is free to decide the size.
++ * Known \a states are enumerated in QWaylandXdgToplevel::State.
++ */
++ uint QWaylandXdgToplevel::sendConfigure(const QSize &size, const QList<QWaylandXdgToplevel::State> &states)
++ {
++ if (!size.isValid()) {
++ qWarning() << "Can't configure xdg_toplevel with an invalid size" << size;
++ return 0;
++ }
++ Q_D(QWaylandXdgToplevel);
++ auto statesBytes = QByteArray::fromRawData(reinterpret_cast<const char *>(states.data()),
++ states.size() * static_cast<int>(sizeof(State)));
++ uint32_t serial = d->m_xdgSurface->surface()->compositor()->nextSerial();
++ d->m_pendingConfigures.append(QWaylandXdgToplevelPrivate::ConfigureEvent{states, size, serial});
++ d->send_configure(size.width(), size.height(), statesBytes);
++ QWaylandXdgSurfacePrivate::get(d->m_xdgSurface)->send_configure(serial);
++ return serial;
++ }
++
++ /*!
++ * \qmlmethod int XdgToplevel::sendConfigure(size size, list<int> states)
++ *
++ * Sends a configure event to the client. \a size contains the pixel size of the surface.
++ * A size of zero means the client is free to decide the size.
++ *
++ * Possible values for \a states are:
++ * \value XdgToplevel.MaximizedState The surface is maximized
++ * \value XdgToplevel.FullscreenState The surface is fullscreen
++ * \value XdgToplevel.ResizingState The surface is being resized
++ * \value XdgToplevel.ActivatedState The surface is now active
++ */
++ uint QWaylandXdgToplevel::sendConfigure(const QSize &size, const QList<int> &states)
++ {
++ QList<State> s;
++ for (auto state : states)
++ s << State(state);
++ return sendConfigure(size, s);
++ }
++
++ /*!
++ * \qmlmethod void XdgToplevel::sendClose()
++ *
++ * Sends a close event to the client. The client may choose to ignore the event.
++ */
++
++ /*!
++ * Sends a close event to the client. The client may choose to ignore the event.
++ */
++ void QWaylandXdgToplevel::sendClose()
++ {
++ Q_D(QWaylandXdgToplevel);
++ d->send_close();
++ }
++
++ /*!
++ * \qmlmethod void XdgToplevel::sendMaximized(size size)
++ *
++ * Convenience for sending a configure event with the maximized state set, and
++ * fullscreen and resizing removed. The activated state is left in its current state.
++ *
++ * \a size is the new size of the window.
++ */
++
++ /*!
++ * Convenience for sending a configure event with the maximized state set, and
++ * fullscreen and resizing removed. The activated state is left in its current state.
++ *
++ * \a size is the new size of the window.
++ */
++ uint QWaylandXdgToplevel::sendMaximized(const QSize &size)
++ {
++ Q_D(QWaylandXdgToplevel);
++ QWaylandXdgToplevelPrivate::ConfigureEvent conf = d->lastSentConfigure();
++
++ if (!conf.states.contains(QWaylandXdgToplevel::State::MaximizedState))
++ conf.states.append(QWaylandXdgToplevel::State::MaximizedState);
++ conf.states.removeOne(QWaylandXdgToplevel::State::FullscreenState);
++ conf.states.removeOne(QWaylandXdgToplevel::State::ResizingState);
++
++ return sendConfigure(size, conf.states);
++ }
++
++ /*!
++ * \qmlmethod void XdgToplevel::sendUnmaximized(size size)
++ *
++ * Convenience for sending a configure event with the maximized, fullscreen and
++ * resizing states removed, and fullscreen and resizing removed. The activated
++ * state is left in its current state.
++ *
++ * \a size is the new size of the window. If \a size is zero, the client decides the size.
++ */
++
++ /*!
++ * Convenience for sending a configure event with the maximized, fullscreen and
++ * resizing states removed, and fullscreen and resizing removed. The activated
++ * state is left in its current state.
++ *
++ * \a size is the new size of the window. If \a size is zero, the client decides the size.
++ */
++ uint QWaylandXdgToplevel::sendUnmaximized(const QSize &size)
++ {
++ Q_D(QWaylandXdgToplevel);
++ QWaylandXdgToplevelPrivate::ConfigureEvent conf = d->lastSentConfigure();
++
++ conf.states.removeOne(QWaylandXdgToplevel::State::MaximizedState);
++ conf.states.removeOne(QWaylandXdgToplevel::State::FullscreenState);
++ conf.states.removeOne(QWaylandXdgToplevel::State::ResizingState);
++
++ return sendConfigure(size, conf.states);
++
++ }
++
++ /*!
++ * \qmlmethod void XdgToplevel::sendFullscreen(size size)
++ *
++ * Convenience for sending a configure event with the fullscreen state set, and
++ * maximized and resizing removed. The activated state is left in its current state.
++ *
++ * \sa sendUnmaximized
++ *
++ * \a size is the new size of the window.
++ */
++
++ /*!
++ * Convenience for sending a configure event with the fullscreen state set, and
++ * maximized and resizing removed. The activated state is left in its current state.
++ *
++ * \sa sendUnmaximized
++ *
++ * \a size is the new size of the window.
++ */
++ uint QWaylandXdgToplevel::sendFullscreen(const QSize &size)
++ {
++ Q_D(QWaylandXdgToplevel);
++ QWaylandXdgToplevelPrivate::ConfigureEvent conf = d->lastSentConfigure();
++
++ if (!conf.states.contains(QWaylandXdgToplevel::State::FullscreenState))
++ conf.states.append(QWaylandXdgToplevel::State::FullscreenState);
++ conf.states.removeOne(QWaylandXdgToplevel::State::MaximizedState);
++ conf.states.removeOne(QWaylandXdgToplevel::State::ResizingState);
++
++ return sendConfigure(size, conf.states);
++ }
++
++ /*!
++ * \qmlmethod void XdgToplevel::sendResizing(size size)
++ *
++ * Convenience for sending a configure event with the resizing state set, and
++ * maximized and fullscreen removed. The activated state is left in its current state.
++ *
++ * \a size is the new size of the window.
++ */
++
++ /*!
++ * Convenience for sending a configure event with the resizing state set, and
++ * maximized and fullscreen removed. The activated state is left in its current state.
++ *
++ * \a size is the new size of the window.
++ */
++ uint QWaylandXdgToplevel::sendResizing(const QSize &size)
++ {
++ Q_D(QWaylandXdgToplevel);
++ QWaylandXdgToplevelPrivate::ConfigureEvent conf = d->lastSentConfigure();
++
++ if (!conf.states.contains(QWaylandXdgToplevel::State::ResizingState))
++ conf.states.append(QWaylandXdgToplevel::State::ResizingState);
++ conf.states.removeOne(QWaylandXdgToplevel::State::MaximizedState);
++ conf.states.removeOne(QWaylandXdgToplevel::State::FullscreenState);
++
++ return sendConfigure(size, conf.states);
++ }
++
++ /*!
++ * Returns the surface role for the QWaylandToplevel.
++ */
++ QWaylandSurfaceRole *QWaylandXdgToplevel::role()
++ {
++ return &QWaylandXdgToplevelPrivate::s_role;
++ }
++
++ /*!
++ * Returns the QWaylandXdgToplevel corresponding to the \a resource.
++ */
++ QWaylandXdgToplevel *QWaylandXdgToplevel::fromResource(wl_resource *resource)
++ {
++ if (auto p = QtWayland::fromResource<QWaylandXdgToplevelPrivate *>(resource))
++ return p->q_func();
++ return nullptr;
++ }
++
++ /*!
++ * \qmlsignal XdgShell::xdgSurfaceCreated(XdgSurface xdgSurface)
++ *
++ * This signal is emitted when the client has created a \c xdg_surface.
++ * Note that \a xdgSurface is not mapped, i.e. according to the \c xdg-shell
++ * protocol it should not be displayed, until it has received a role object.
++ *
++ * \sa toplevelCreated(), popupCreated()
++ */
++
++ /*!
++ * \fn void QWaylandXdgShell::xdgSurfaceCreated(QWaylandXdgSurface *xdgSurface)
++ *
++ * This signal is emitted when the client has created a \c xdg_surface.
++ * Note that \a xdgSurface is not mapped, i.e. according to the \c xdg-shell
++ * protocol it should not be displayed, until it has received a role object.
++ *
++ * \sa toplevelCreated(), popupCreated()
++ */
++
++ /*!
++ * \qmlsignal XdgShell::toplevelCreated(XdgToplevel toplevel, XdgSurface xdgSurface)
++ *
++ * This signal is emitted when the client has created a \c xdg_toplevel.
++ * A common use case is to let the handler of this signal instantiate a ShellSurfaceItem or
++ * WaylandQuickItem for displaying \a toplevel in a QtQuick scene.
++ *
++ * \a xdgSurface is the XdgSurface \a toplevel is the role object for.
++ */
++
++ /*!
++ * \fn void QWaylandXdgShell::toplevelCreated(QWaylandXdgToplevel *toplevel, QWaylandXdgSurface *xdgSurface)
++ *
++ * This signal is emitted when the client has created a \c xdg_toplevel.
++ * A common use case is to let the handler of this signal instantiate a QWaylandShellSurfaceItem or
++ * QWaylandQuickItem for displaying \a toplevel in a QtQuick scene.
++ *
++ * \a xdgSurface is the XdgSurface \a toplevel is the role object for.
++ */
++
++ /*!
++ * \qmlsignal XdgShell::popupCreated(XdgPopup popup, XdgSurface xdgSurface)
++ *
++ * This signal is emitted when the client has created a \c xdg_popup.
++ * A common use case is to let the handler of this signal instantiate a ShellSurfaceItem or
++ * WaylandQuickItem for displaying \a popup in a QtQuick scene.
++ *
++ * \a xdgSurface is the XdgSurface \a popup is the role object for.
++ */
++
++ /*!
++ * \fn void QWaylandXdgShell::popupCreated(QWaylandXdgPopup *popup, QWaylandXdgSurface *xdgSurface)
++ *
++ * This signal is emitted when the client has created a \c xdg_popup.
++ * A common use case is to let the handler of this signal instantiate a QWaylandShellSurfaceItem or
++ * QWaylandQuickItem for displaying \a popup in a QtQuick scene.
++ *
++ * \a xdgSurface is the XdgSurface \a popup is the role object for.
++ */
++
++ /*!
++ * \qmlsignal XdgShell::pong(int serial)
++ *
++ * This signal is emitted when the client has responded to a ping event with serial, \a serial.
++ *
++ * \sa ping()
++ */
++
++ /*!
++ * \fn void QWaylandXdgShell::pong(uint serial)
++ *
++ * This signal is emitted when the client has responded to a ping event with serial, \a serial.
++ *
++ * \sa QWaylandXdgShell::ping()
++ */
++
++ QList<int> QWaylandXdgToplevel::statesAsInts() const
++ {
++ QList<int> list;
++ const auto s = states();
++ list.reserve(s.size());
++ for (auto state : s) {
++ list << static_cast<int>(state);
++ }
++ return list;
++ }
++
++ QWaylandSurfaceRole QWaylandXdgToplevelPrivate::s_role("xdg_toplevel");
++
++ QWaylandXdgToplevelPrivate::QWaylandXdgToplevelPrivate(QWaylandXdgSurface *xdgSurface, const QWaylandResource &resource)
++ : m_xdgSurface(xdgSurface)
++ {
++ init(resource.resource());
++ }
++
++ void QWaylandXdgToplevelPrivate::handleAckConfigure(uint serial)
++ {
++ Q_Q(QWaylandXdgToplevel);
++ ConfigureEvent config;
++ Q_FOREVER {
++ if (m_pendingConfigures.empty()) {
++ qWarning("Toplevel received an unexpected ack_configure!");
++ return;
++ }
++
++ // This won't work unless there always is a toplevel.configure for each xdgsurface.configure
++ config = m_pendingConfigures.takeFirst();
++
++ if (config.serial == serial)
++ break;
++ }
++
++ QList<uint> changedStates;
++ std::set_symmetric_difference(
++ m_lastAckedConfigure.states.begin(), m_lastAckedConfigure.states.end(),
++ config.states.begin(), config.states.end(),
++ std::back_inserter(changedStates));
++
++ m_lastAckedConfigure = config;
++
++ for (uint state : std::as_const(changedStates)) {
++ switch (state) {
++ case state_maximized:
++ emit q->maximizedChanged();
++ break;
++ case state_fullscreen:
++ emit q->fullscreenChanged();
++ break;
++ case state_resizing:
++ emit q->resizingChanged();
++ break;
++ case state_activated:
++ emit q->activatedChanged();
++ break;
++ }
++ }
++
++ if (!changedStates.empty())
++ emit q->statesChanged();
++ }
++
++ void QWaylandXdgToplevelPrivate::handleFocusLost()
++ {
++ Q_Q(QWaylandXdgToplevel);
++ QWaylandXdgToplevelPrivate::ConfigureEvent current = lastSentConfigure();
++ current.states.removeOne(QWaylandXdgToplevel::State::ActivatedState);
++ q->sendConfigure(current.size, current.states);
++ }
++
++ void QWaylandXdgToplevelPrivate::handleFocusReceived()
++ {
++ Q_Q(QWaylandXdgToplevel);
++ QWaylandXdgToplevelPrivate::ConfigureEvent current = lastSentConfigure();
++ if (!current.states.contains(QWaylandXdgToplevel::State::ActivatedState)) {
++ current.states.push_back(QWaylandXdgToplevel::State::ActivatedState);
++ q->sendConfigure(current.size, current.states);
++ }
++ }
++
++ Qt::Edges QWaylandXdgToplevelPrivate::convertToEdges(resize_edge edge)
++ {
++ return Qt::Edges(((edge & 0b1100) >> 1) | ((edge & 0b0010) << 2) | (edge & 0b0001));
++ }
++
++ void QWaylandXdgToplevelPrivate::xdg_toplevel_destroy_resource(QtWaylandServer::xdg_toplevel::Resource *resource)
++ {
++ Q_UNUSED(resource);
++ Q_Q(QWaylandXdgToplevel);
++ delete q;
++ }
++
++ void QWaylandXdgToplevelPrivate::xdg_toplevel_destroy(QtWaylandServer::xdg_toplevel::Resource *resource)
++ {
++ if (Q_UNLIKELY(m_decoration))
++ qWarning() << "Client error: xdg_toplevel destroyed before its decoration object";
++
++ wl_resource_destroy(resource->handle);
++ //TODO: Should the xdg surface be desroyed as well? Or is it allowed to recreate a new toplevel for it?
++ }
++
++ void QWaylandXdgToplevelPrivate::xdg_toplevel_set_parent(QtWaylandServer::xdg_toplevel::Resource *resource, wl_resource *parent)
++ {
++ Q_UNUSED(resource);
++ QWaylandXdgToplevel *parentToplevel = QWaylandXdgToplevel::fromResource(parent);
++
++ Q_Q(QWaylandXdgToplevel);
++
++ if (m_parentToplevel != parentToplevel) {
++ m_parentToplevel = parentToplevel;
++ emit q->parentToplevelChanged();
++ }
++
++ if (m_parentToplevel && m_xdgSurface->windowType() != Qt::WindowType::SubWindow) {
++ // There's a parent now, which means the surface is transient
++ QWaylandXdgSurfacePrivate::get(m_xdgSurface)->setWindowType(Qt::WindowType::SubWindow);
++ } else if (!m_parentToplevel && m_xdgSurface->windowType() != Qt::WindowType::Window) {
++ // When the surface has no parent it is toplevel
++ QWaylandXdgSurfacePrivate::get(m_xdgSurface)->setWindowType(Qt::WindowType::Window);
++ }
++ }
++
++ void QWaylandXdgToplevelPrivate::xdg_toplevel_set_title(QtWaylandServer::xdg_toplevel::Resource *resource, const QString &title)
++ {
++ Q_UNUSED(resource);
++ if (title == m_title)
++ return;
++ Q_Q(QWaylandXdgToplevel);
++ m_title = title;
++ emit q->titleChanged();
++ }
++
++ void QWaylandXdgToplevelPrivate::xdg_toplevel_set_app_id(QtWaylandServer::xdg_toplevel::Resource *resource, const QString &app_id)
++ {
++ Q_UNUSED(resource);
++ if (app_id == m_appId)
++ return;
++ Q_Q(QWaylandXdgToplevel);
++ m_appId = app_id;
++ emit q->appIdChanged();
++ }
++
++ void QWaylandXdgToplevelPrivate::xdg_toplevel_show_window_menu(QtWaylandServer::xdg_toplevel::Resource *resource, wl_resource *seatResource, uint32_t serial, int32_t x, int32_t y)
++ {
++ Q_UNUSED(resource);
++ Q_UNUSED(serial);
++ QPoint position(x, y);
++ auto seat = QWaylandSeat::fromSeatResource(seatResource);
++ Q_Q(QWaylandXdgToplevel);
++ emit q->showWindowMenu(seat, position);
++ }
++
++ void QWaylandXdgToplevelPrivate::xdg_toplevel_move(Resource *resource, wl_resource *seatResource, uint32_t serial)
++ {
++ Q_UNUSED(resource);
++ Q_UNUSED(serial);
++ Q_Q(QWaylandXdgToplevel);
++ QWaylandSeat *seat = QWaylandSeat::fromSeatResource(seatResource);
++ emit q->startMove(seat);
++ }
++
++ void QWaylandXdgToplevelPrivate::xdg_toplevel_resize(QtWaylandServer::xdg_toplevel::Resource *resource, wl_resource *seatResource, uint32_t serial, uint32_t edges)
++ {
++ Q_UNUSED(resource);
++ Q_UNUSED(serial);
++ Q_Q(QWaylandXdgToplevel);
++ QWaylandSeat *seat = QWaylandSeat::fromSeatResource(seatResource);
++ emit q->startResize(seat, convertToEdges(resize_edge(edges)));
++ }
++
++ void QWaylandXdgToplevelPrivate::xdg_toplevel_set_max_size(QtWaylandServer::xdg_toplevel::Resource *resource, int32_t width, int32_t height)
++ {
++ Q_UNUSED(resource);
++
++ QSize maxSize(width, height);
++ if (width == 0 && height == 0)
++ maxSize = QSize(); // Wayland size of zero means unspecified which best translates to invalid
++
++ if (m_maxSize == maxSize)
++ return;
++
++ if (width < 0 || height < 0) {
++ // The spec says raise a protocol error, but there's no matching error defined
++ qWarning() << "Received a xdg_toplevel.set_max_size request with a negative size";
++ return;
++ }
++
++ if (m_minSize.isValid() && maxSize.isValid() &&
++ (maxSize.width() < m_minSize.width() || maxSize.height() < m_minSize.height())) {
++ // The spec says raise a protocol error, but there's no matching error defined
++ qWarning() << "Received a xdg_toplevel.set_max_size request with a size smaller than the minimium size";
++ return;
++ }
++
++ m_maxSize = maxSize;
++
++ Q_Q(QWaylandXdgToplevel);
++ emit q->maxSizeChanged();
++ }
++
++ void QWaylandXdgToplevelPrivate::xdg_toplevel_set_min_size(QtWaylandServer::xdg_toplevel::Resource *resource, int32_t width, int32_t height)
++ {
++ Q_UNUSED(resource);
++
++ QSize minSize(width, height);
++ if (width == 0 && height == 0)
++ minSize = QSize(); // Wayland size of zero means unspecified
++
++ if (m_minSize == minSize)
++ return;
++
++ if (width < 0 || height < 0) {
++ // The spec says raise a protocol error, but there's no matching error defined
++ qWarning() << "Received a xdg_toplevel.set_min_size request with a negative size";
++ return;
++ }
++
++ if (m_maxSize.isValid() && minSize.isValid() &&
++ (minSize.width() > m_maxSize.width() || minSize.height() > m_maxSize.height())) {
++ // The spec says raise a protocol error, but there's no matching error defined
++ qWarning() << "Received a xdg_toplevel.set_min_size request with a size larger than the maximum size";
++ return;
++ }
++
++ m_minSize = minSize;
++
++ Q_Q(QWaylandXdgToplevel);
++ emit q->minSizeChanged();
++ }
++
++ void QWaylandXdgToplevelPrivate::xdg_toplevel_set_maximized(QtWaylandServer::xdg_toplevel::Resource *resource)
++ {
++ Q_UNUSED(resource);
++ Q_Q(QWaylandXdgToplevel);
++ emit q->setMaximized();
++ }
++
++ void QWaylandXdgToplevelPrivate::xdg_toplevel_unset_maximized(QtWaylandServer::xdg_toplevel::Resource *resource)
++ {
++ Q_UNUSED(resource);
++ Q_Q(QWaylandXdgToplevel);
++ emit q->unsetMaximized();
++ }
++
++ void QWaylandXdgToplevelPrivate::xdg_toplevel_set_fullscreen(QtWaylandServer::xdg_toplevel::Resource *resource, wl_resource *output_res)
++ {
++ Q_UNUSED(resource);
++ Q_Q(QWaylandXdgToplevel);
++ QWaylandOutput *output = output_res ? QWaylandOutput::fromResource(output_res) : nullptr;
++ emit q->setFullscreen(output);
++ }
++
++ void QWaylandXdgToplevelPrivate::xdg_toplevel_unset_fullscreen(QtWaylandServer::xdg_toplevel::Resource *resource)
++ {
++ Q_UNUSED(resource);
++ Q_Q(QWaylandXdgToplevel);
++ emit q->unsetFullscreen();
++ }
++
++ void QWaylandXdgToplevelPrivate::xdg_toplevel_set_minimized(QtWaylandServer::xdg_toplevel::Resource *resource)
++ {
++ Q_UNUSED(resource);
++ Q_Q(QWaylandXdgToplevel);
++ emit q->setMinimized();
++ }
++
++ /*!
++ * \qmltype XdgPopup
++ * \nativetype QWaylandXdgPopup
++ * \inqmlmodule QtWayland.Compositor.XdgShell
++ * \since 5.12
++ * \brief XdgPopup represents the popup specific parts of and xdg surface.
++ *
++ * This type is part of the \l{XdgShell} extension and provides a way to extend
++ * the functionality of an \l{XdgSurface} with features
++ * specific to desktop-style menus for an xdg surface.
++ *
++ * It corresponds to the Wayland interface \c xdg_popup.
++ */
++
++ /*!
++ * \class QWaylandXdgPopup
++ * \inmodule QtWaylandCompositor
++ * \since 5.12
++ * \brief The QWaylandXdgPopup class represents the popup specific parts of an xdg surface.
++ *
++ * This class is part of the QWaylandXdgShell extension and provides a way to
++ * extend the functionality of a QWaylandXdgSurface with features
++ * specific to desktop-style menus for an xdg surface.
++ *
++ * It corresponds to the Wayland interface \c xdg_popup.
++ */
++
++ /*!
++ * Constructs a QWaylandXdgPopup.
++ */
++ QWaylandXdgPopup::QWaylandXdgPopup(QWaylandXdgSurface *xdgSurface, QWaylandXdgSurface *parentXdgSurface,
++ QWaylandXdgPositioner *positioner, QWaylandResource &resource)
++ : QObject(*new QWaylandXdgPopupPrivate(xdgSurface, parentXdgSurface, positioner, resource))
++ {
++ }
++
++ /*!
++ * \qmlproperty XdgSurface XdgPopup::xdgSurface
++ *
++ * This property holds the XdgSurface associated with this XdgPopup.
++ */
++
++ /*!
++ * \property QWaylandXdgPopup::xdgSurface
++ *
++ * This property holds the QWaylandXdgSurface associated with this QWaylandXdgPopup.
++ */
++ QWaylandXdgSurface *QWaylandXdgPopup::xdgSurface() const
++ {
++ Q_D(const QWaylandXdgPopup);
++ return d->m_xdgSurface;
++ }
++
++ /*!
++ * \qmlproperty XdgSurface XdgPopup::parentXdgSurface
++ *
++ * This property holds the XdgSurface associated with the parent of this XdgPopup.
++ */
++
++ /*!
++ * \property QWaylandXdgPopup::parentXdgSurface
++ *
++ * This property holds the QWaylandXdgSurface associated with the parent of this
++ * QWaylandXdgPopup.
++ */
++ QWaylandXdgSurface *QWaylandXdgPopup::parentXdgSurface() const
++ {
++ Q_D(const QWaylandXdgPopup);
++ return d->m_parentXdgSurface;
++ }
++
++ /*!
++ * \qmlproperty rect XdgPopup::configuredGeometry
++ *
++ * The window geometry the popup received in the configure event. Relative to the
++ * upper left corner of the parent surface.
++ */
++
++ /*!
++ * \property QWaylandXdgPopup::configuredGeometry
++ *
++ * The window geometry the popup received in the configure event. Relative to the
++ * upper left corner of the parent surface.
++ */
++ QRect QWaylandXdgPopup::configuredGeometry() const
++ {
++ Q_D(const QWaylandXdgPopup);
++ return d->m_geometry;
++ }
++
++ /*!
++ * \qmlproperty rect XdgPopup::anchorRect
++ *
++ * The anchor rectangle relative to the parent window geometry that the child
++ * surface should be placed relative to.
++ */
++
++ /*!
++ * \property QWaylandXdgPopup::anchorRect
++ *
++ * Returns the anchor rectangle relative to the parent window geometry that the child
++ * surface should be placed relative to.
++ */
++ QRect QWaylandXdgPopup::anchorRect() const
++ {
++ Q_D(const QWaylandXdgPopup);
++ return d->m_positionerData.anchorRect;
++ }
++
++ /*!
++ * \qmlproperty enumeration XdgPopup::anchorEdges
++ *
++ * This property holds the set of edges on the anchor rect that the child surface should be placed
++ * relative to. If no edges are specified in a direction, the anchor point should be
++ * centered between the edges.
++ *
++ * The possible values are:
++ * \value Qt.TopEdge The top edge of the rectangle.
++ * \value Qt.LeftEdge The left edge of the rectangle.
++ * \value Qt.RightEdge The right edge of the rectangle.
++ * \value Qt.BottomEdge The bottom edge of the rectangle.
++ */
++
++ /*!
++ * \property QWaylandXdgPopup::anchorEdges
++ *
++ * Returns the set of edges on the anchor rect that the child surface should be placed
++ * relative to. If no edges are specified in a direction, the anchor point should be
++ * centered between the edges.
++ */
++ Qt::Edges QWaylandXdgPopup::anchorEdges() const
++ {
++ Q_D(const QWaylandXdgPopup);
++ return d->m_positionerData.anchorEdges;
++ }
++
++ /*!
++ * \qmlproperty rect XdgPopup::gravityEdges
++ *
++ * Specifies in what direction the surface should be positioned, relative to the anchor
++ * point.
++ *
++ * The possible values are:
++ * \value Qt.TopEdge The surface should slide towards the top of the screen.
++ * \value Qt.LeftEdge The surface should slide towards the left of the screen.
++ * \value Qt.RightEdge The surface should slide towards the right of the screen.
++ * \value Qt.BottomEdge The surface should slide towards the bottom of the screen.
++ */
++
++ /*!
++ * \property QWaylandXdgPopup::gravityEdges
++ *
++ * Specifies in what direction the surface should be positioned, relative to the anchor
++ * point.
++ */
++ Qt::Edges QWaylandXdgPopup::gravityEdges() const
++ {
++ Q_D(const QWaylandXdgPopup);
++ return d->m_positionerData.gravityEdges;
++ }
++
++ /*!
++ * \qmlproperty enumeration XdgPopup::slideConstraints
++ *
++ * This property holds the orientations in which the child should slide to fit within the screen.
++ *
++ * Possible values:
++ * \value Qt.Horizontal Horizontal
++ * \value Qt.Vertical Vertical
++ */
++
++ /*!
++ * \property QWaylandXdgPopup::slideConstraints
++ *
++ * This property holds the orientations in which the child should slide to fit within the screen.
++ */
++ Qt::Orientations QWaylandXdgPopup::slideConstraints() const
++ {
++ Q_D(const QWaylandXdgPopup);
++ const uint flags = d->m_positionerData.constraintAdjustments;
++
++ Qt::Orientations constraints = {};
++
++ if (flags & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X)
++ constraints |= Qt::Horizontal;
++ if (flags & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y)
++ constraints |= Qt::Vertical;
++
++ return constraints;
++ }
++
++ /*!
++ * \qmlproperty enumeration XdgPopup::flipConstraints
++ *
++ * This property holds the orientations in which the child should flip to fit within the screen.
++ *
++ * Possible values:
++ * \value Qt.Horizontal Horizontal
++ * \value Qt.Vertical Vertical
++ */
++
++ /*!
++ * \property QWaylandXdgPopup::flipConstraints
++ *
++ * This property holds the orientations in which the child should flip to fit within the screen.
++ */
++ Qt::Orientations QWaylandXdgPopup::flipConstraints() const
++ {
++ Q_D(const QWaylandXdgPopup);
++ const uint flags = d->m_positionerData.constraintAdjustments;
++
++ Qt::Orientations constraints = {};
++
++ if (flags & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X)
++ constraints |= Qt::Horizontal;
++ if (flags & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y)
++ constraints |= Qt::Vertical;
++
++ return constraints;
++ }
++
++ /*!
++ * \qmlproperty enumeration XdgPopup::resizeConstraints
++ *
++ * This property holds the orientations in which the child should resize to fit within the screen.
++ *
++ * Possible values:
++ * \value Qt.Horizontal Horizontal
++ * \value Qt.Vertical Vertical
++ */
++
++ /*!
++ * \property QWaylandXdgPopup::resizeConstraints
++ *
++ * This property holds the orientations in which the child should resize to fit within the screen.
++ */
++ Qt::Orientations QWaylandXdgPopup::resizeConstraints() const
++ {
++ Q_D(const QWaylandXdgPopup);
++ const uint flags = d->m_positionerData.constraintAdjustments;
++
++ Qt::Orientations constraints = {};
++
++ if (flags & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X)
++ constraints |= Qt::Horizontal;
++ if (flags & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y)
++ constraints |= Qt::Vertical;
++
++ return constraints;
++ }
++
++ /*!
++ * \qmlproperty point XdgPopup::offset
++ *
++ * The position relative to the position of the anchor on the anchor rectangle and
++ * the anchor on the surface.
++ */
++
++ /*!
++ * \property QWaylandXdgPopup::offset
++ *
++ * Returns the surface position relative to the position of the anchor on the anchor
++ * rectangle and the anchor on the surface.
++ */
++ QPoint QWaylandXdgPopup::offset() const
++ {
++ Q_D(const QWaylandXdgPopup);
++ return d->m_positionerData.offset;
++ }
++
++ /*!
++ * \qmlproperty size XdgPopup::positionerSize
++ *
++ * The size requested for the window geometry by the positioner object.
++ */
++
++ /*!
++ * \property QWaylandXdgPopup::positionerSize
++ *
++ * Returns the size requested for the window geometry by the positioner object.
++ */
++ QSize QWaylandXdgPopup::positionerSize() const
++ {
++ Q_D(const QWaylandXdgPopup);
++ return d->m_positionerData.size;
++ }
++
++ /*!
++ * \qmlproperty point XdgPopup::unconstrainedPosition
++ *
++ * The position of the surface relative to the parent window geometry if the surface
++ * is not constrained. I.e. when not moved to fit inside the screen or similar.
++ */
++
++ /*!
++ * \property QWaylandXdgPopup::unconstrainedPosition
++ *
++ * The position of the surface relative to the parent window geometry if the surface
++ * is not constrained. I.e. when not moved to fit inside the screen or similar.
++ */
++ QPoint QWaylandXdgPopup::unconstrainedPosition() const
++ {
++ Q_D(const QWaylandXdgPopup);
++ return d->m_positionerData.unconstrainedPosition();
++ }
++
++ /*!
++ * \qmlmethod int XdgPopup::sendConfigure(rect geometry)
++ *
++ * Sends a configure event to the client. \a geometry contains the window geometry
++ * relative to the upper left corner of the window geometry of the parent surface.
++ *
++ * This implicitly sends a configure event to the corresponding XdgSurface as well.
++ */
++
++ /*!
++ * Sends a configure event to the client. \a geometry contains the window geometry
++ * relative to the upper left corner of the window geometry of the parent surface.
++ *
++ * This implicitly sends a configure event to the corresponding QWaylandXdgSurface
++ * as well.
++ */
++ uint QWaylandXdgPopup::sendConfigure(const QRect &geometry)
++ {
++ Q_D(QWaylandXdgPopup);
++ return d->sendConfigure(geometry);
++ }
++
++ /*!
++ * \qmlmethod void XdgPopup::sendPopupDone()
++ * \since 5.14
++ *
++ * Dismiss the popup. According to the \c xdg-shell protocol this should make the
++ * client destroy the popup.
++ */
++
++ /*!
++ * \since 5.14
++ *
++ * Dismiss the popup. According to the \c xdg-shell protocol this should make the
++ * client destroy the popup.
++ */
++ void QWaylandXdgPopup::sendPopupDone()
++ {
++ Q_D(QWaylandXdgPopup);
++ d->send_popup_done();
++ }
++
++ /*!
++ * Returns the surface role for the QWaylandPopup.
++ */
++ QWaylandSurfaceRole *QWaylandXdgPopup::role()
++ {
++ return &QWaylandXdgPopupPrivate::s_role;
++ }
++
++ QWaylandXdgPopupPrivate::QWaylandXdgPopupPrivate(QWaylandXdgSurface *xdgSurface, QWaylandXdgSurface *parentXdgSurface,
++ QWaylandXdgPositioner *positioner, const QWaylandResource &resource)
++ : m_xdgSurface(xdgSurface)
++ , m_parentXdgSurface(parentXdgSurface)
++ , m_positionerData(positioner->m_data)
++ {
++ Q_ASSERT(m_positionerData.isComplete());
++ init(resource.resource());
++
++ QWaylandXdgSurfacePrivate::get(m_xdgSurface)->setWindowType(Qt::WindowType::Popup);
++
++ //TODO: Need an API for sending a different initial configure
++ sendConfigure(QRect(m_positionerData.unconstrainedPosition(), m_positionerData.size));
++ }
++
++ void QWaylandXdgPopupPrivate::handleAckConfigure(uint serial)
++ {
++ Q_Q(QWaylandXdgPopup);
++ ConfigureEvent config;
++ Q_FOREVER {
++ if (m_pendingConfigures.empty()) {
++ qWarning("Popup received an unexpected ack_configure!");
++ return;
++ }
++
++ // This won't work unless there always is a popup.configure for each xdgsurface.configure
++ config = m_pendingConfigures.takeFirst();
++
++ if (config.serial == serial)
++ break;
++ }
++
++ if (m_geometry == config.geometry)
++ return;
++
++ m_geometry = config.geometry;
++ emit q->configuredGeometryChanged();
++ }
++
++ uint QWaylandXdgPopupPrivate::sendConfigure(const QRect &geometry)
++ {
++ uint32_t serial = m_xdgSurface->surface()->compositor()->nextSerial();
++ m_pendingConfigures.append(QWaylandXdgPopupPrivate::ConfigureEvent{geometry, serial});
++ send_configure(geometry.x(), geometry.y(), geometry.width(), geometry.height());
++ QWaylandXdgSurfacePrivate::get(m_xdgSurface)->send_configure(serial);
++ return serial;
++ }
++
++ void QWaylandXdgPopupPrivate::xdg_popup_destroy_resource(QtWaylandServer::xdg_popup::Resource *resource)
++ {
++ Q_UNUSED(resource);
++ Q_Q(QWaylandXdgPopup);
++ delete q;
++ }
++
++ void QWaylandXdgPopupPrivate::xdg_popup_destroy(QtWaylandServer::xdg_popup::Resource *resource)
++ {
++ wl_resource_destroy(resource->handle);
++ }
++
++ void QWaylandXdgPopupPrivate::xdg_popup_grab(QtWaylandServer::xdg_popup::Resource *resource, wl_resource *seat, uint32_t serial)
++ {
++ Q_UNUSED(resource);
++ Q_UNUSED(serial);
++ Q_UNUSED(seat);
++ qWarning() << Q_FUNC_INFO << "Not implemented"; //TODO
++ //switch keyboard focus
++ //eventually send configure with activated.
++ }
++
++ QWaylandSurfaceRole QWaylandXdgPopupPrivate::s_role("xdg_popup");
++
++ QWaylandXdgPositionerData::QWaylandXdgPositionerData()
++ : offset(0, 0)
++ {}
++
++ bool QWaylandXdgPositionerData::isComplete() const
++ {
++ return size.width() > 0 && size.height() > 0 && anchorRect.size().width() > 0 && anchorRect.size().height() > 0;
++ }
++
++ QPoint QWaylandXdgPositionerData::anchorPoint() const
++ {
++ int yPosition = 0;
++ if (anchorEdges & Qt::TopEdge)
++ yPosition = anchorRect.top();
++ else if (anchorEdges & Qt::BottomEdge)
++ yPosition = anchorRect.bottom() + 1;
++ else
++ yPosition = anchorRect.top() + anchorRect.height() / 2;
++
++ int xPosition = 0;
++ if (anchorEdges & Qt::LeftEdge)
++ xPosition = anchorRect.left();
++ else if (anchorEdges & Qt::RightEdge)
++ xPosition = anchorRect.right() + 1;
++ else
++ xPosition = anchorRect.left() + anchorRect.width() / 2;
++
++ return QPoint(xPosition, yPosition);
++ }
++
++ QPoint QWaylandXdgPositionerData::unconstrainedPosition() const
++ {
++ int gravityOffsetY = 0;
++ if (gravityEdges & Qt::TopEdge)
++ gravityOffsetY = -size.height();
++ else if (!(gravityEdges & Qt::BottomEdge))
++ gravityOffsetY = -size.height() / 2;
++
++ int gravityOffsetX = 0;
++ if (gravityEdges & Qt::LeftEdge)
++ gravityOffsetX = -size.width();
++ else if (!(gravityEdges & Qt::RightEdge))
++ gravityOffsetX = -size.width() / 2;
++
++ QPoint gravityOffset(gravityOffsetX, gravityOffsetY);
++ return anchorPoint() + gravityOffset + offset;
++ }
++
++ QWaylandXdgPositioner::QWaylandXdgPositioner(const QWaylandResource &resource)
++ {
++ init(resource.resource());
++ }
++
++ void QWaylandXdgPositioner::xdg_positioner_destroy_resource(QtWaylandServer::xdg_positioner::Resource *resource)
++ {
++ Q_UNUSED(resource);
++ delete this;
++ }
++
++ void QWaylandXdgPositioner::xdg_positioner_destroy(QtWaylandServer::xdg_positioner::Resource *resource)
++ {
++ wl_resource_destroy(resource->handle);
++ }
++
++ void QWaylandXdgPositioner::xdg_positioner_set_size(QtWaylandServer::xdg_positioner::Resource *resource, int32_t width, int32_t height)
++ {
++ if (width <= 0 || height <= 0) {
++ wl_resource_post_error(resource->handle, XDG_POSITIONER_ERROR_INVALID_INPUT,
++ "xdg_positioner.set_size requested with non-positive dimensions");
++ return;
++ }
++
++ QSize size(width, height);
++ m_data.size = size;
++ }
++
++ void QWaylandXdgPositioner::xdg_positioner_set_anchor_rect(QtWaylandServer::xdg_positioner::Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
++ {
++ if (width <= 0 || height <= 0) {
++ wl_resource_post_error(resource->handle, XDG_POSITIONER_ERROR_INVALID_INPUT,
++ "xdg_positioner.set_anchor_rect requested with non-positive dimensions");
++ return;
++ }
++
++ QRect anchorRect(x, y, width, height);
++ m_data.anchorRect = anchorRect;
++ }
++
++ void QWaylandXdgPositioner::xdg_positioner_set_anchor(QtWaylandServer::xdg_positioner::Resource *resource, uint32_t anchor)
++ {
++ Qt::Edges anchorEdges = convertToEdges(xdg_positioner::anchor(anchor));
++
++ if ((anchorEdges & Qt::BottomEdge && anchorEdges & Qt::TopEdge) ||
++ (anchorEdges & Qt::LeftEdge && anchorEdges & Qt::RightEdge)) {
++ wl_resource_post_error(resource->handle, XDG_POSITIONER_ERROR_INVALID_INPUT,
++ "xdg_positioner.set_anchor requested with parallel edges");
++ return;
++ }
++
++ m_data.anchorEdges = anchorEdges;
++ }
++
++ void QWaylandXdgPositioner::xdg_positioner_set_gravity(QtWaylandServer::xdg_positioner::Resource *resource, uint32_t gravity)
++ {
++ Qt::Edges gravityEdges = convertToEdges(xdg_positioner::gravity(gravity));
++
++ if ((gravityEdges & Qt::BottomEdge && gravityEdges & Qt::TopEdge) ||
++ (gravityEdges & Qt::LeftEdge && gravityEdges & Qt::RightEdge)) {
++ wl_resource_post_error(resource->handle, XDG_POSITIONER_ERROR_INVALID_INPUT,
++ "xdg_positioner.set_gravity requested with parallel edges");
++ return;
++ }
++
++ m_data.gravityEdges = gravityEdges;
++ }
++
++ void QWaylandXdgPositioner::xdg_positioner_set_constraint_adjustment(QtWaylandServer::xdg_positioner::Resource *resource, uint32_t constraint_adjustment)
++ {
++ Q_UNUSED(resource);
++ m_data.constraintAdjustments = constraint_adjustment;
++ }
++
++ void QWaylandXdgPositioner::xdg_positioner_set_offset(QtWaylandServer::xdg_positioner::Resource *resource, int32_t x, int32_t y)
++ {
++ Q_UNUSED(resource);
++ m_data.offset = QPoint(x, y);
++ }
++
++ QWaylandXdgPositioner *QWaylandXdgPositioner::fromResource(wl_resource *resource)
++ {
++ return QtWayland::fromResource<QWaylandXdgPositioner *>(resource);
++ }
++
++ Qt::Edges QWaylandXdgPositioner::convertToEdges(anchor anchor)
++ {
++ switch (anchor) {
++ case anchor_none:
++ return Qt::Edges();
++ case anchor_top:
++ return Qt::TopEdge;
++ case anchor_bottom:
++ return Qt::BottomEdge;
++ case anchor_left:
++ return Qt::LeftEdge;
++ case anchor_right:
++ return Qt::RightEdge;
++ case anchor_top_left:
++ return Qt::TopEdge | Qt::LeftEdge;
++ case anchor_bottom_left:
++ return Qt::BottomEdge | Qt::LeftEdge;
++ case anchor_top_right:
++ return Qt::TopEdge | Qt::RightEdge;
++ case anchor_bottom_right:
++ return Qt::BottomEdge | Qt::RightEdge;
++ default:
++ qWarning() << "Unknown Wayland xdg edge" << anchor;
++ return Qt::Edges();
++ }
++ }
++
++ Qt::Edges QWaylandXdgPositioner::convertToEdges(QWaylandXdgPositioner::gravity gravity)
++ {
++ return convertToEdges(anchor(gravity));
++ }
++
++
++ QT_END_NAMESPACE
++
++ #include "moc_qwaylandxdgshell.cpp"