diff options
author | Johan Klokkhammer Helsing <johan.helsing@qt.io> | 2018-08-07 13:53:34 +0200 |
---|---|---|
committer | Johan Helsing <johan.helsing@qt.io> | 2018-08-15 08:47:50 +0000 |
commit | 992cde48d72435052514845b8add8d0dc880ccb2 (patch) | |
tree | f5768d4621f2286aea5e10b088cc337cfe6aacd9 | |
parent | 4d0ae358dd646c369829bb38c099eb47bd04db4a (diff) | |
download | qtwayland-992cde48d72435052514845b8add8d0dc880ccb2.tar.gz |
Compositor API: Add xdg-shell stable
[ChangeLog][Compositor API] Added APIs for xdg-shell stable.
Code copied from the V6 implementation with suffixes and prefixes removed.
Otherwise a few minor edits, such as renaming xdg_shell to xdg_wm_base, and
handling the anchor and gravity edges no longer being bitfields.
Examples that used v6 have been updated to use stable instead, or, in some
cases, to support both.
Task-number: QTBUG-66784
Change-Id: Ia619b478a938fdcd9b47af8a8df2d7fcc4406204
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
-rw-r--r-- | examples/wayland/hwlayer-compositor/main.qml | 5 | ||||
-rw-r--r-- | examples/wayland/minimal-qml/main.qml | 8 | ||||
-rw-r--r-- | examples/wayland/multi-screen/qml/main.qml | 6 | ||||
-rw-r--r-- | examples/wayland/overview-compositor/main.qml | 4 | ||||
-rw-r--r-- | examples/wayland/pure-qml/qml/main.qml | 7 | ||||
-rw-r--r-- | examples/wayland/spanning-screens/main.qml | 4 | ||||
-rw-r--r-- | src/compositor/extensions/extensions.pri | 6 | ||||
-rw-r--r-- | src/compositor/extensions/qwaylandxdgshell.cpp | 2020 | ||||
-rw-r--r-- | src/compositor/extensions/qwaylandxdgshell.h | 261 | ||||
-rw-r--r-- | src/compositor/extensions/qwaylandxdgshell_p.h | 239 | ||||
-rw-r--r-- | src/compositor/extensions/qwaylandxdgshellintegration.cpp | 300 | ||||
-rw-r--r-- | src/compositor/extensions/qwaylandxdgshellintegration_p.h | 144 | ||||
-rw-r--r-- | src/imports/compositor/qwaylandquickcompositorplugin.cpp | 8 | ||||
-rw-r--r-- | sync.profile | 2 |
14 files changed, 3004 insertions, 10 deletions
diff --git a/examples/wayland/hwlayer-compositor/main.qml b/examples/wayland/hwlayer-compositor/main.qml index 2f6a5f0d..b4f088c9 100644 --- a/examples/wayland/hwlayer-compositor/main.qml +++ b/examples/wayland/hwlayer-compositor/main.qml @@ -51,7 +51,7 @@ import QtQuick 2.6 import QtQuick.Window 2.2 import QtQuick.Controls 2.2 -import QtWayland.Compositor 1.2 +import QtWayland.Compositor 1.3 WaylandCompositor { WaylandOutput { @@ -155,6 +155,7 @@ WaylandCompositor { function addShellSurface(shellSurface) { shellSurfaces.append({shSurface: shellSurface, animatePosition: false, animateOpacity: false, level: 0}); } - WlShell { onWlShellSurfaceCreated: addShellSurface(shellSurface) } + XdgShell { onToplevelCreated: addShellSurface(xdgSurface) } XdgShellV6 { onToplevelCreated: addShellSurface(xdgSurface) } + WlShell { onWlShellSurfaceCreated: addShellSurface(shellSurface) } } diff --git a/examples/wayland/minimal-qml/main.qml b/examples/wayland/minimal-qml/main.qml index 85c8c578..63ed9da5 100644 --- a/examples/wayland/minimal-qml/main.qml +++ b/examples/wayland/minimal-qml/main.qml @@ -50,7 +50,7 @@ import QtQuick 2.6 import QtQuick.Window 2.2 -import QtWayland.Compositor 1.1 +import QtWayland.Compositor 1.3 WaylandCompositor { // The output defines the screen. @@ -75,7 +75,7 @@ WaylandCompositor { } } // Extensions are additions to the core Wayland - // protocol. We choose to support two different + // protocol. We choose to support three different // shells (window management protocols). When the // client creates a new shell surface (i.e. a window) // we append it to our list of shellSurfaces. @@ -87,5 +87,9 @@ WaylandCompositor { onToplevelCreated: shellSurfaces.append({shellSurface: xdgSurface}); } + XdgShell { + onToplevelCreated: + shellSurfaces.append({shellSurface: xdgSurface}); + } ListModel { id: shellSurfaces } } diff --git a/examples/wayland/multi-screen/qml/main.qml b/examples/wayland/multi-screen/qml/main.qml index 354dd1c9..ecd32739 100644 --- a/examples/wayland/multi-screen/qml/main.qml +++ b/examples/wayland/multi-screen/qml/main.qml @@ -52,7 +52,7 @@ import QtQml 2.2 import QtQuick 2.0 import QtQuick.Window 2.3 as Window -import QtWayland.Compositor 1.1 +import QtWayland.Compositor 1.3 import QtQml.Models 2.1 WaylandCompositor { @@ -104,6 +104,10 @@ WaylandCompositor { onToplevelCreated: handleShellSurfaceCreated(xdgSurface) } + XdgShell { + onToplevelCreated: handleShellSurfaceCreated(xdgSurface) + } + function createShellSurfaceItem(shellSurface, moveItem, output) { var parentSurfaceItem = output.viewsBySurface[shellSurface.parentSurface]; var parent = parentSurfaceItem || output.surfaceArea; diff --git a/examples/wayland/overview-compositor/main.qml b/examples/wayland/overview-compositor/main.qml index 18589e50..d0482a90 100644 --- a/examples/wayland/overview-compositor/main.qml +++ b/examples/wayland/overview-compositor/main.qml @@ -49,7 +49,7 @@ ****************************************************************************/ import QtQuick 2.7 -import QtWayland.Compositor 1.1 +import QtWayland.Compositor 1.3 import QtQuick.Window 2.3 import QtQuick.Controls 2.0 @@ -132,7 +132,7 @@ WaylandCompositor { ListModel { id: toplevels } - XdgShellV6 { + XdgShell { onToplevelCreated: { toplevels.append({xdgSurface}); toplevel.sendFullscreen(Qt.size(win.pixelWidth, win.pixelHeight)); diff --git a/examples/wayland/pure-qml/qml/main.qml b/examples/wayland/pure-qml/qml/main.qml index b188ebbb..69be7cf1 100644 --- a/examples/wayland/pure-qml/qml/main.qml +++ b/examples/wayland/pure-qml/qml/main.qml @@ -49,7 +49,7 @@ ****************************************************************************/ import QtQuick 2.0 -import QtWayland.Compositor 1.1 +import QtWayland.Compositor 1.3 WaylandCompositor { id: waylandCompositor @@ -58,6 +58,11 @@ WaylandCompositor { // Shell surface extension. Needed to provide a window concept for Wayland clients. // I.e. requests and events for maximization, minimization, resizing, closing etc. + XdgShell { + onToplevelCreated: screen.handleShellSurface(xdgSurface) + } + + // Unstable version of xdg-shell still used by some clients XdgShellV6 { onToplevelCreated: screen.handleShellSurface(xdgSurface) } diff --git a/examples/wayland/spanning-screens/main.qml b/examples/wayland/spanning-screens/main.qml index a3230806..ab57306b 100644 --- a/examples/wayland/spanning-screens/main.qml +++ b/examples/wayland/spanning-screens/main.qml @@ -50,7 +50,7 @@ import QtQuick 2.6 import QtQuick.Window 2.3 -import QtWayland.Compositor 1.1 +import QtWayland.Compositor 1.3 WaylandCompositor { WaylandOutput { @@ -105,7 +105,7 @@ WaylandCompositor { } } - XdgShellV6 { + XdgShell { onToplevelCreated: { const shellSurface = xdgSurface; diff --git a/src/compositor/extensions/extensions.pri b/src/compositor/extensions/extensions.pri index 577001a2..b2c55635 100644 --- a/src/compositor/extensions/extensions.pri +++ b/src/compositor/extensions/extensions.pri @@ -10,6 +10,7 @@ WAYLANDSERVERSOURCES += \ ../extensions/qt-windowmanager.xml \ ../3rdparty/protocol/text-input-unstable-v2.xml \ ../3rdparty/protocol/xdg-shell-unstable-v6.xml \ + ../3rdparty/protocol/xdg-shell.xml \ ../3rdparty/protocol/ivi-application.xml \ HEADERS += \ @@ -29,6 +30,8 @@ HEADERS += \ extensions/qwaylandxdgshellv5_p.h \ extensions/qwaylandxdgshellv6.h \ extensions/qwaylandxdgshellv6_p.h \ + extensions/qwaylandxdgshell.h \ + extensions/qwaylandxdgshell_p.h \ extensions/qwaylandshellsurface.h \ extensions/qwaylandiviapplication.h \ extensions/qwaylandiviapplication_p.h \ @@ -45,6 +48,7 @@ SOURCES += \ extensions/qwaylandqtwindowmanager.cpp \ extensions/qwaylandxdgshellv5.cpp \ extensions/qwaylandxdgshellv6.cpp \ + extensions/qwaylandxdgshell.cpp \ extensions/qwaylandshellsurface.cpp \ extensions/qwaylandiviapplication.cpp \ extensions/qwaylandivisurface.cpp \ @@ -57,6 +61,7 @@ qtHaveModule(quick):contains(QT_CONFIG, opengl) { extensions/qwaylandwlshellintegration_p.h \ extensions/qwaylandxdgshellv5integration_p.h \ extensions/qwaylandxdgshellv6integration_p.h \ + extensions/qwaylandxdgshellintegration_p.h SOURCES += \ extensions/qwaylandquickshellsurfaceitem.cpp \ @@ -64,6 +69,7 @@ qtHaveModule(quick):contains(QT_CONFIG, opengl) { extensions/qwaylandwlshellintegration.cpp \ extensions/qwaylandxdgshellv5integration.cpp \ extensions/qwaylandxdgshellv6integration.cpp \ + extensions/qwaylandxdgshellintegration.cpp } diff --git a/src/compositor/extensions/qwaylandxdgshell.cpp b/src/compositor/extensions/qwaylandxdgshell.cpp new file mode 100644 index 00000000..a502cdf3 --- /dev/null +++ b/src/compositor/extensions/qwaylandxdgshell.cpp @@ -0,0 +1,2020 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwaylandxdgshell.h" +#include "qwaylandxdgshell_p.h" + +#ifdef QT_WAYLAND_COMPOSITOR_QUICK +#include "qwaylandxdgshellintegration_p.h" +#endif + +#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); +} + +Qt::Edges QWaylandXdgShellPrivate::convertToEdges(uint xdgEdges) +{ + switch (xdgEdges) { + case XDG_POSITIONER_ANCHOR_NONE: + return Qt::Edges(); + case XDG_POSITIONER_ANCHOR_TOP: + return Qt::TopEdge; + case XDG_POSITIONER_ANCHOR_BOTTOM: + return Qt::BottomEdge; + case XDG_POSITIONER_ANCHOR_LEFT: + return Qt::LeftEdge; + case XDG_POSITIONER_ANCHOR_RIGHT: + return Qt::RightEdge; + case XDG_POSITIONER_ANCHOR_TOP_LEFT: + return Qt::TopEdge | Qt::LeftEdge; + case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: + return Qt::BottomEdge | Qt::LeftEdge; + case XDG_POSITIONER_ANCHOR_TOP_RIGHT: + return Qt::TopEdge | Qt::RightEdge; + case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT: + return Qt::BottomEdge | Qt::RightEdge; + default: + qWarning() << "Unknown Wayland xdg edge" << xdgEdges; + return Qt::Edges(); + } +} + +QWaylandXdgSurface *QWaylandXdgShellPrivate::xdgSurfaceFromSurface(QWaylandSurface *surface) +{ + for (QWaylandXdgSurface *xdgSurface : qAsConst(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->role() != nullptr) { + wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_ROLE, + "wl_surface@%d, already has role %s\n", + wl_resource_get_id(surface->resource()), + surface->role()->name().constData()); + return; + } + + 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 + * \inqmlmodule QtWayland.Compositor + * \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: + * \code + * import QtWayland.Compositor 1.3 + * + * WaylandCompositor { + * XdgShell { + * // ... + * } + * } + * \endcode + */ + +/*! + * \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); +} + +/*! + * Returns the Wayland interface for the QWaylandXdgShell. + */ +const struct wl_interface *QWaylandXdgShell::interface() +{ + return QWaylandXdgShellPrivate::interface(); +} + +QByteArray QWaylandXdgShell::interfaceName() +{ + return QWaylandXdgShellPrivate::interfaceName(); +} + +/*! + * \qmlmethod void QtWaylandCompositor::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(0, 0), m_surface->size() / m_surface->bufferScale()); +} + +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); +} + +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; + } + + 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 + * \inqmlmodule QtWayland.Compositor + * \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 QtWaylandCompositor::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::sizeChanged, this, &QWaylandXdgSurface::handleSurfaceSizeChanged); + connect(surface, &QWaylandSurface::bufferScaleChanged, this, &QWaylandXdgSurface::handleBufferScaleChanged); + emit shellChanged(); + emit surfaceChanged(); + QWaylandCompositorExtension::initialize(); +} + +/*! + * \qmlproperty enum QtWaylandCompositor::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 QtWaylandCompositor::XdgSurface::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. + */ + +/*! + * \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 QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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) +{ + auto xsResource = QWaylandXdgSurfacePrivate::Resource::fromResource(resource); + if (!xsResource) + return nullptr; + return static_cast<QWaylandXdgSurfacePrivate *>(xsResource->xdg_surface_object)->q_func(); +} + +#ifdef QT_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 + * \inqmlmodule QtWayland.Compositor + * \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)) +{ + QVector<QWaylandXdgToplevel::State> states; + sendConfigure({0, 0}, states); +} + +/*! + * \qmlproperty XdgToplevel QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::XdgToplevel::appId + * + * This property holds the app id of the XdgToplevel. + */ + +/*! + * \property QWaylandXdgToplevel::appId + * + * This property holds the app id of the QWaylandXdgToplevel. + */ +QString QWaylandXdgToplevel::appId() const +{ + Q_D(const QWaylandXdgToplevel); + return d->m_appId; +} + +/*! + * \qmlproperty size QtWaylandCompositor::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 QtWaylandCompositor::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. + */ +QVector<QWaylandXdgToplevel::State> QWaylandXdgToplevel::states() const +{ + Q_D(const QWaylandXdgToplevel); + return d->m_lastAckedConfigure.states; +} + +/*! + * \qmlproperty bool QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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); +} + +/*! + * \qmlmethod size QtWaylandCompositor::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(); + + //TODO: use minSize given by the client here instead + QSizeF newSize(qMax(width, 1.0), qMax(height, 1.0)); + return newSize.toSize(); +} + +/*! + * 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 QVector<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 QtWaylandCompositor::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. + * Known \a states are enumerated in XdgToplevel::State. + */ +uint QWaylandXdgToplevel::sendConfigure(const QSize &size, const QVector<int> &states) +{ + QVector<State> s; + for (auto state : states) + s << State(state); + return sendConfigure(size, s); +} + +/*! + * \qmlmethod void QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::XdgToplevel::sendResizing(size maxSize) + * + * 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 maxSize 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 maxSize is the new size of the window. + */ +uint QWaylandXdgToplevel::sendResizing(const QSize &maxSize) +{ + 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(maxSize, conf.states); +} + +/*! + * Returns the surface role for the QWaylandToplevel. + */ +QWaylandSurfaceRole *QWaylandXdgToplevel::role() +{ + return &QWaylandXdgToplevelPrivate::s_role; +} + + +/*! + * \qmlsignal QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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; + Q_FOREACH (uint state, states()) { + 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; + } + + QVector<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 : 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); + } +} + +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) +{ + 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 = nullptr; + if (parent) { + parentToplevel = static_cast<QWaylandXdgToplevelPrivate *>( + QWaylandXdgToplevelPrivate::Resource::fromResource(parent)->xdg_toplevel_object)->q_func(); + } + + 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, QWaylandXdgShellPrivate::convertToEdges(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; + } + + else + 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; + } + + else + 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 + * \inqmlmodule QtWayland.Compositor + * \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 + * 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 QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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 = 0; + + 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 QtWaylandCompositor::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 = 0; + + 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 QtWaylandCompositor::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 = 0; + + 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 QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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 QtWaylandCompositor::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); +} + +/*! + * 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) +{ + init(resource.resource()); + m_positionerData = positioner->m_data; + + if (!m_positionerData.isComplete()) + qWarning() << "Trying to create xdg popup with incomplete positioner"; + + QWaylandXdgSurfacePrivate::get(m_xdgSurface)->setWindowType(Qt::WindowType::Popup); + + //TODO: positioner rect may not extend parent's window geometry, enforce this? + //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(QtWaylandServer::xdg_popup::Resource *resource) +{ + Q_UNUSED(resource); + qWarning() << Q_FUNC_INFO << "Not implemented"; //TODO +} + +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 = QWaylandXdgShellPrivate::convertToEdges(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 = QWaylandXdgShellPrivate::convertToEdges(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 static_cast<QWaylandXdgPositioner *>(Resource::fromResource(resource)->xdg_positioner_object); +} + +QT_END_NAMESPACE diff --git a/src/compositor/extensions/qwaylandxdgshell.h b/src/compositor/extensions/qwaylandxdgshell.h new file mode 100644 index 00000000..6f660eb1 --- /dev/null +++ b/src/compositor/extensions/qwaylandxdgshell.h @@ -0,0 +1,261 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDXDGSHELL_H +#define QWAYLANDXDGSHELL_H + +#include <QtWaylandCompositor/QWaylandCompositorExtension> +#include <QtWaylandCompositor/QWaylandResource> +#include <QtWaylandCompositor/QWaylandShell> +#include <QtWaylandCompositor/QWaylandShellSurface> + +#include <QtCore/QRect> + +struct wl_resource; + +QT_BEGIN_NAMESPACE + +class QWaylandClient; +class QWaylandOutput; +class QWaylandSeat; +class QWaylandSurface; +class QWaylandSurfaceRole; +class QWaylandXdgShellPrivate; +class QWaylandXdgSurface; +class QWaylandXdgSurfacePrivate; +class QWaylandXdgToplevel; +class QWaylandXdgToplevelPrivate; +class QWaylandXdgPopup; +class QWaylandXdgPopupPrivate; +class QWaylandXdgPositioner; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgShell : public QWaylandShellTemplate<QWaylandXdgShell> +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QWaylandXdgShell) +public: + QWaylandXdgShell(); + QWaylandXdgShell(QWaylandCompositor *compositor); + + void initialize() override; + + static const struct wl_interface *interface(); + static QByteArray interfaceName(); + +public Q_SLOTS: + uint ping(QWaylandClient *client); + +Q_SIGNALS: + void xdgSurfaceCreated(QWaylandXdgSurface *xdgSurface); + void toplevelCreated(QWaylandXdgToplevel *toplevel, QWaylandXdgSurface *xdgSurface); + void popupCreated(QWaylandXdgPopup *popup, QWaylandXdgSurface *xdgSurface); + void pong(uint serial); + +private Q_SLOTS: + void handleSeatChanged(QWaylandSeat *newSeat, QWaylandSeat *oldSeat); + void handleFocusChanged(QWaylandSurface *newSurface, QWaylandSurface *oldSurface); +}; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgSurface : public QWaylandShellSurfaceTemplate<QWaylandXdgSurface> +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QWaylandXdgSurface) + Q_PROPERTY(QWaylandXdgShell *shell READ shell NOTIFY shellChanged) + Q_PROPERTY(QWaylandSurface *surface READ surface NOTIFY surfaceChanged) + Q_PROPERTY(QWaylandXdgToplevel *toplevel READ toplevel NOTIFY toplevelCreated) + Q_PROPERTY(QWaylandXdgPopup *popup READ popup NOTIFY popupCreated) + Q_PROPERTY(QRect windowGeometry READ windowGeometry NOTIFY windowGeometryChanged) + +public: + QWaylandXdgSurface(); + QWaylandXdgSurface(QWaylandXdgShell* xdgShell, QWaylandSurface *surface, const QWaylandResource &resource); + + Q_INVOKABLE void initialize(QWaylandXdgShell* xdgShell, QWaylandSurface *surface, const QWaylandResource &resource); + + Qt::WindowType windowType() const override; + + QWaylandXdgShell *shell() const; + QWaylandSurface *surface() const; + QWaylandXdgToplevel *toplevel() const; + QWaylandXdgPopup *popup() const; + QRect windowGeometry() const; + + static const struct wl_interface *interface(); + static QByteArray interfaceName(); + static QWaylandXdgSurface *fromResource(::wl_resource *resource); + +#ifdef QT_WAYLAND_COMPOSITOR_QUICK + QWaylandQuickShellIntegration *createIntegration(QWaylandQuickShellSurfaceItem *item) override; +#endif + +Q_SIGNALS: + void shellChanged(); + void surfaceChanged(); + void toplevelCreated(); + void popupCreated(); + void windowGeometryChanged(); + +private: + void initialize() override; + +private Q_SLOTS: + void handleSurfaceSizeChanged(); + void handleBufferScaleChanged(); +}; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgToplevel : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QWaylandXdgToplevel) + Q_PROPERTY(QWaylandXdgToplevel *parentToplevel READ parentToplevel NOTIFY parentToplevelChanged) + Q_PROPERTY(QString title READ title NOTIFY titleChanged) + Q_PROPERTY(QString appId READ appId NOTIFY appIdChanged) + Q_PROPERTY(QSize maxSize READ maxSize NOTIFY maxSizeChanged) + Q_PROPERTY(QSize minSize READ minSize NOTIFY minSizeChanged) + Q_PROPERTY(QList<int> states READ statesAsInts NOTIFY statesChanged) + Q_PROPERTY(bool maximized READ maximized NOTIFY maximizedChanged) + Q_PROPERTY(bool fullscreen READ fullscreen NOTIFY fullscreenChanged) + Q_PROPERTY(bool resizing READ resizing NOTIFY resizingChanged) + Q_PROPERTY(bool activated READ activated NOTIFY activatedChanged) +public: + enum State : uint { + MaximizedState = 1, + FullscreenState = 2, + ResizingState = 3, + ActivatedState = 4 + }; + Q_ENUM(State) + + QWaylandXdgToplevel(QWaylandXdgSurface *xdgSurface, QWaylandResource &resource); + + QWaylandXdgToplevel *parentToplevel() const; + + QString title() const; + QString appId() const; + QSize maxSize() const; + QSize minSize() const; + QVector<QWaylandXdgToplevel::State> states() const; + bool maximized() const; + bool fullscreen() const; + bool resizing() const; + bool activated() const; + + Q_INVOKABLE QSize sizeForResize(const QSizeF &size, const QPointF &delta, Qt::Edges edges) const; + uint sendConfigure(const QSize &size, const QVector<State> &states); + Q_INVOKABLE uint sendConfigure(const QSize &size, const QVector<int> &states); + Q_INVOKABLE void sendClose(); + Q_INVOKABLE uint sendMaximized(const QSize &size); + Q_INVOKABLE uint sendUnmaximized(const QSize &size = QSize(0, 0)); + Q_INVOKABLE uint sendFullscreen(const QSize &size); + Q_INVOKABLE uint sendResizing(const QSize &maxSize); + + static QWaylandSurfaceRole *role(); + +Q_SIGNALS: + void parentToplevelChanged(); + void titleChanged(); + void appIdChanged(); + void maxSizeChanged(); + void minSizeChanged(); + void startMove(QWaylandSeat *seat); + void startResize(QWaylandSeat *seat, Qt::Edges edges); + void statesChanged(); + void maximizedChanged(); + void fullscreenChanged(); + void resizingChanged(); + void activatedChanged(); + + void showWindowMenu(QWaylandSeat *seat, const QPoint &localSurfacePosition); + void setMaximized(); + void unsetMaximized(); + void setFullscreen(QWaylandOutput *output); + void unsetFullscreen(); + void setMinimized(); + +private: + QList<int> statesAsInts() const; +}; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgPopup : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QWaylandXdgPopup) + Q_PROPERTY(QWaylandXdgSurface *xdgSurface READ xdgSurface) + Q_PROPERTY(QWaylandXdgSurface *parentXdgSurface READ parentXdgSurface) + Q_PROPERTY(QRect configuredGeometry READ configuredGeometry NOTIFY configuredGeometryChanged) + + // Positioner properties + Q_PROPERTY(QRect anchorRect READ anchorRect CONSTANT) + Q_PROPERTY(Qt::Edges anchorEdges READ anchorEdges CONSTANT) + Q_PROPERTY(Qt::Edges gravityEdges READ gravityEdges CONSTANT) + Q_PROPERTY(Qt::Orientations slideConstraints READ slideConstraints CONSTANT) + Q_PROPERTY(Qt::Orientations flipConstraints READ flipConstraints CONSTANT) + Q_PROPERTY(Qt::Orientations resizeConstraints READ resizeConstraints CONSTANT) + Q_PROPERTY(QPoint offset READ offset CONSTANT) + Q_PROPERTY(QSize positionerSize READ positionerSize CONSTANT) + Q_PROPERTY(QPoint unconstrainedPosition READ unconstrainedPosition CONSTANT) +public: + QWaylandXdgSurface *xdgSurface() const; + QWaylandXdgSurface *parentXdgSurface() const; + QRect configuredGeometry() const; + + // Positioner properties + QRect anchorRect() const; + Qt::Edges anchorEdges() const ; + Qt::Edges gravityEdges() const ; + Qt::Orientations slideConstraints() const; + Qt::Orientations flipConstraints() const; + Qt::Orientations resizeConstraints() const; + QPoint offset() const; + QSize positionerSize() const; + QPoint unconstrainedPosition() const; + + Q_INVOKABLE uint sendConfigure(const QRect &geometry); + + static QWaylandSurfaceRole *role(); + +Q_SIGNALS: + void configuredGeometryChanged(); + +private: + QWaylandXdgPopup(QWaylandXdgSurface *xdgSurface, QWaylandXdgSurface *parentXdgSurface, + QWaylandXdgPositioner *positioner, QWaylandResource &resource); + friend class QWaylandXdgSurfacePrivate; +}; + +QT_END_NAMESPACE + +#endif // QWAYLANDXDGSHELL_H diff --git a/src/compositor/extensions/qwaylandxdgshell_p.h b/src/compositor/extensions/qwaylandxdgshell_p.h new file mode 100644 index 00000000..f45cb7f4 --- /dev/null +++ b/src/compositor/extensions/qwaylandxdgshell_p.h @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDXDGSHELL_P_H +#define QWAYLANDXDGSHELL_P_H + +#include <QtWaylandCompositor/private/qwaylandcompositorextension_p.h> +#include <QtWaylandCompositor/private/qwayland-server-xdg-shell.h> + +#include <QtWaylandCompositor/QWaylandXdgShell> + +#include <QtCore/QSet> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +struct Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgPositionerData { + QSize size; + QRect anchorRect; + Qt::Edges anchorEdges = 0; + Qt::Edges gravityEdges = 0; + uint constraintAdjustments = XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE; + QPoint offset; + QWaylandXdgPositionerData(); + bool isComplete() const; + QPoint anchorPoint() const; + QPoint unconstrainedPosition() const; +}; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgShellPrivate + : public QWaylandCompositorExtensionPrivate + , public QtWaylandServer::xdg_wm_base +{ + Q_DECLARE_PUBLIC(QWaylandXdgShell) +public: + QWaylandXdgShellPrivate(); + void ping(Resource *resource, uint32_t serial); + void registerXdgSurface(QWaylandXdgSurface *xdgSurface); + void unregisterXdgSurface(QWaylandXdgSurface *xdgSurface); + static QWaylandXdgShellPrivate *get(QWaylandXdgShell *xdgShell) { return xdgShell->d_func(); } + static Qt::Edges convertToEdges(uint xdgEdges); + + QSet<uint32_t> m_pings; + QMultiMap<struct wl_client *, QWaylandXdgSurface *> m_xdgSurfaces; + + QWaylandXdgSurface *xdgSurfaceFromSurface(QWaylandSurface *surface); + +protected: + void xdg_wm_base_destroy(Resource *resource) override; + void xdg_wm_base_create_positioner(Resource *resource, uint32_t id) override; + void xdg_wm_base_get_xdg_surface(Resource *resource, uint32_t id, + struct ::wl_resource *surface) override; + void xdg_wm_base_pong(Resource *resource, uint32_t serial) override; +}; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgSurfacePrivate + : public QWaylandCompositorExtensionPrivate + , public QtWaylandServer::xdg_surface +{ + Q_DECLARE_PUBLIC(QWaylandXdgSurface) +public: + QWaylandXdgSurfacePrivate(); + void setWindowType(Qt::WindowType windowType); + void handleFocusLost(); + void handleFocusReceived(); + static QWaylandXdgSurfacePrivate *get(QWaylandXdgSurface *xdgSurface) { return xdgSurface->d_func(); } + + QRect calculateFallbackWindowGeometry() const; + void updateFallbackWindowGeometry(); + +private: + QWaylandXdgShell *m_xdgShell = nullptr; + QWaylandSurface *m_surface = nullptr; + + QWaylandXdgToplevel *m_toplevel = nullptr; + QWaylandXdgPopup *m_popup = nullptr; + QRect m_windowGeometry; + bool m_unsetWindowGeometry = true; + QMargins m_windowMargins; + Qt::WindowType m_windowType = Qt::WindowType::Window; + + void xdg_surface_destroy_resource(Resource *resource) override; + void xdg_surface_destroy(Resource *resource) override; + void xdg_surface_get_toplevel(Resource *resource, uint32_t id) override; + void xdg_surface_get_popup(Resource *resource, uint32_t id, struct ::wl_resource *parent, struct ::wl_resource *positioner) override; + void xdg_surface_ack_configure(Resource *resource, uint32_t serial) override; + void xdg_surface_set_window_geometry(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) override; +}; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgToplevelPrivate : public QObjectPrivate, public QtWaylandServer::xdg_toplevel +{ + Q_DECLARE_PUBLIC(QWaylandXdgToplevel) +public: + struct ConfigureEvent { + QVector<QWaylandXdgToplevel::State> states; + QSize size = {0, 0}; + uint serial = 0; + }; + + QWaylandXdgToplevelPrivate(QWaylandXdgSurface *xdgSurface, const QWaylandResource& resource); + ConfigureEvent lastSentConfigure() const { return m_pendingConfigures.empty() ? m_lastAckedConfigure : m_pendingConfigures.last(); } + void handleAckConfigure(uint serial); //TODO: move? + void handleFocusLost(); + void handleFocusReceived(); + + static QWaylandXdgToplevelPrivate *get(QWaylandXdgToplevel *toplevel) { return toplevel->d_func(); } + +protected: + + void xdg_toplevel_destroy_resource(Resource *resource) override; + + void xdg_toplevel_destroy(Resource *resource) override; + void xdg_toplevel_set_parent(Resource *resource, struct ::wl_resource *parent) override; + void xdg_toplevel_set_title(Resource *resource, const QString &title) override; + void xdg_toplevel_set_app_id(Resource *resource, const QString &app_id) override; + void xdg_toplevel_show_window_menu(Resource *resource, struct ::wl_resource *seat, uint32_t serial, int32_t x, int32_t y) override; + void xdg_toplevel_move(Resource *resource, struct ::wl_resource *seatResource, uint32_t serial) override; + void xdg_toplevel_resize(Resource *resource, struct ::wl_resource *seat, uint32_t serial, uint32_t edges) override; + void xdg_toplevel_set_max_size(Resource *resource, int32_t width, int32_t height) override; + void xdg_toplevel_set_min_size(Resource *resource, int32_t width, int32_t height) override; + void xdg_toplevel_set_maximized(Resource *resource) override; + void xdg_toplevel_unset_maximized(Resource *resource) override; + void xdg_toplevel_set_fullscreen(Resource *resource, struct ::wl_resource *output) override; + void xdg_toplevel_unset_fullscreen(Resource *resource) override; + void xdg_toplevel_set_minimized(Resource *resource) override; + +public: + QWaylandXdgSurface *m_xdgSurface = nullptr; + QWaylandXdgToplevel *m_parentToplevel = nullptr; + QList<ConfigureEvent> m_pendingConfigures; + ConfigureEvent m_lastAckedConfigure; + QString m_title; + QString m_appId; + QSize m_maxSize; + QSize m_minSize; + + static QWaylandSurfaceRole s_role; +}; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgPopupPrivate : public QObjectPrivate, public QtWaylandServer::xdg_popup +{ + Q_DECLARE_PUBLIC(QWaylandXdgPopup) +public: + struct ConfigureEvent { + QRect geometry; + uint serial; + }; + + QWaylandXdgPopupPrivate(QWaylandXdgSurface *xdgSurface, QWaylandXdgSurface *parentXdgSurface, + QWaylandXdgPositioner *positioner, const QWaylandResource& resource); + + void handleAckConfigure(uint serial); + + static QWaylandXdgPopupPrivate *get(QWaylandXdgPopup *popup) { return popup->d_func(); } + + static QWaylandSurfaceRole s_role; + +private: + uint sendConfigure(const QRect &geometry); + +protected: + void xdg_popup_destroy(Resource *resource) override; + void xdg_popup_grab(Resource *resource, struct ::wl_resource *seat, uint32_t serial) override; + +private: + QWaylandXdgSurface *m_xdgSurface = nullptr; + QWaylandXdgSurface *m_parentXdgSurface = nullptr; + QWaylandXdgPositionerData m_positionerData; + QRect m_geometry; + QList<ConfigureEvent> m_pendingConfigures; +}; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandXdgPositioner : public QtWaylandServer::xdg_positioner +{ +public: + QWaylandXdgPositioner(const QWaylandResource& resource); + static QWaylandXdgPositioner *fromResource(wl_resource *resource); + +protected: + void xdg_positioner_destroy_resource(Resource *resource) override; //TODO: do something special here? + + void xdg_positioner_destroy(Resource *resource) override; + void xdg_positioner_set_size(Resource *resource, int32_t width, int32_t height) override; + void xdg_positioner_set_anchor_rect(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) override; + void xdg_positioner_set_anchor(Resource *resource, uint32_t anchor) override; + void xdg_positioner_set_gravity(Resource *resource, uint32_t gravity) override; + void xdg_positioner_set_constraint_adjustment(Resource *resource, uint32_t constraint_adjustment) override; + void xdg_positioner_set_offset(Resource *resource, int32_t x, int32_t y) override; + +public: + QWaylandXdgPositionerData m_data; +}; + +QT_END_NAMESPACE + +#endif // QWAYLANDXDGSHELL_P_H diff --git a/src/compositor/extensions/qwaylandxdgshellintegration.cpp b/src/compositor/extensions/qwaylandxdgshellintegration.cpp new file mode 100644 index 00000000..7701c05a --- /dev/null +++ b/src/compositor/extensions/qwaylandxdgshellintegration.cpp @@ -0,0 +1,300 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwaylandxdgshellintegration_p.h" + +#include <QtWaylandCompositor/QWaylandXdgSurface> +#include <QtWaylandCompositor/QWaylandCompositor> +#include <QtWaylandCompositor/QWaylandSeat> + +QT_BEGIN_NAMESPACE + +namespace QtWayland { + +static void handlePopupCreated(QWaylandQuickShellSurfaceItem *parentItem, QWaylandXdgPopup *popup) +{ + if (parentItem->shellSurface() == popup->parentXdgSurface()) + QWaylandQuickShellSurfaceItemPrivate::get(parentItem)->maybeCreateAutoPopup(popup->xdgSurface()); +} + +XdgToplevelIntegration::XdgToplevelIntegration(QWaylandQuickShellSurfaceItem *item) + : QWaylandQuickShellIntegration(item) + , m_item(item) + , m_xdgSurface(qobject_cast<QWaylandXdgSurface *>(item->shellSurface())) + , m_toplevel(m_xdgSurface->toplevel()) + , grabberState(GrabberState::Default) +{ + Q_ASSERT(m_toplevel); + + m_item->setSurface(m_xdgSurface->surface()); + + connect(m_toplevel, &QWaylandXdgToplevel::startMove, this, &XdgToplevelIntegration::handleStartMove); + connect(m_toplevel, &QWaylandXdgToplevel::startResize, this, &XdgToplevelIntegration::handleStartResize); + connect(m_toplevel, &QWaylandXdgToplevel::setMaximized, this, &XdgToplevelIntegration::handleSetMaximized); + connect(m_toplevel, &QWaylandXdgToplevel::unsetMaximized, this, &XdgToplevelIntegration::handleUnsetMaximized); + connect(m_toplevel, &QWaylandXdgToplevel::maximizedChanged, this, &XdgToplevelIntegration::handleMaximizedChanged); + connect(m_toplevel, &QWaylandXdgToplevel::setFullscreen, this, &XdgToplevelIntegration::handleSetFullscreen); + connect(m_toplevel, &QWaylandXdgToplevel::unsetFullscreen, this, &XdgToplevelIntegration::handleUnsetFullscreen); + connect(m_toplevel, &QWaylandXdgToplevel::fullscreenChanged, this, &XdgToplevelIntegration::handleFullscreenChanged); + connect(m_toplevel, &QWaylandXdgToplevel::activatedChanged, this, &XdgToplevelIntegration::handleActivatedChanged); + connect(m_xdgSurface->shell(), &QWaylandXdgShell::popupCreated, this, [item](QWaylandXdgPopup *popup, QWaylandXdgSurface *){ + handlePopupCreated(item, popup); + }); + connect(m_xdgSurface->surface(), &QWaylandSurface::sizeChanged, this, &XdgToplevelIntegration::handleSurfaceSizeChanged); + connect(m_toplevel, &QObject::destroyed, this, &XdgToplevelIntegration::handleToplevelDestroyed); +} + +bool XdgToplevelIntegration::mouseMoveEvent(QMouseEvent *event) +{ + if (grabberState == GrabberState::Resize) { + Q_ASSERT(resizeState.seat == m_item->compositor()->seatFor(event)); + if (!resizeState.initialized) { + resizeState.initialMousePos = event->windowPos(); + resizeState.initialized = true; + return true; + } + QPointF delta = m_item->mapToSurface(event->windowPos() - resizeState.initialMousePos); + QSize newSize = m_toplevel->sizeForResize(resizeState.initialWindowSize, delta, resizeState.resizeEdges); + m_toplevel->sendResizing(newSize); + } else if (grabberState == GrabberState::Move) { + Q_ASSERT(moveState.seat == m_item->compositor()->seatFor(event)); + QQuickItem *moveItem = m_item->moveItem(); + if (!moveState.initialized) { + moveState.initialOffset = moveItem->mapFromItem(nullptr, event->windowPos()); + moveState.initialized = true; + return true; + } + if (!moveItem->parentItem()) + return true; + QPointF parentPos = moveItem->parentItem()->mapFromItem(nullptr, event->windowPos()); + moveItem->setPosition(parentPos - moveState.initialOffset); + } + return false; +} + +bool XdgToplevelIntegration::mouseReleaseEvent(QMouseEvent *event) +{ + Q_UNUSED(event); + + if (grabberState == GrabberState::Move) { + grabberState = GrabberState::Default; + return true; + } + return false; +} + +void XdgToplevelIntegration::handleStartMove(QWaylandSeat *seat) +{ + grabberState = GrabberState::Move; + moveState.seat = seat; + moveState.initialized = false; +} + +void XdgToplevelIntegration::handleStartResize(QWaylandSeat *seat, Qt::Edges edges) +{ + grabberState = GrabberState::Resize; + resizeState.seat = seat; + resizeState.resizeEdges = edges; + resizeState.initialWindowSize = m_xdgSurface->windowGeometry().size(); + resizeState.initialPosition = m_item->moveItem()->position(); + resizeState.initialSurfaceSize = m_item->surface()->size(); + resizeState.initialized = false; +} + +void XdgToplevelIntegration::handleSetMaximized() +{ + if (!m_item->view()->isPrimary()) + return; + + QVector<QWaylandXdgToplevel::State> states = m_toplevel->states(); + + if (!states.contains(QWaylandXdgToplevel::State::FullscreenState) && !states.contains(QWaylandXdgToplevel::State::MaximizedState)) { + windowedGeometry.initialWindowSize = m_xdgSurface->windowGeometry().size(); + windowedGeometry.initialPosition = m_item->moveItem()->position(); + } + + // Any prior output-resize handlers are irrelevant at this point. + disconnect(nonwindowedState.sizeChangedConnection); + nonwindowedState.output = m_item->view()->output(); + nonwindowedState.sizeChangedConnection = connect(nonwindowedState.output, &QWaylandOutput::availableGeometryChanged, this, &XdgToplevelIntegration::handleMaximizedSizeChanged); + handleMaximizedSizeChanged(); +} + +void XdgToplevelIntegration::handleMaximizedSizeChanged() +{ + // Insurance against handleToplevelDestroyed() not managing to disconnect this + // handler in time. + if (m_toplevel == nullptr) + return; + + m_toplevel->sendMaximized(nonwindowedState.output->availableGeometry().size() / nonwindowedState.output->scaleFactor()); +} + +void XdgToplevelIntegration::handleUnsetMaximized() +{ + if (!m_item->view()->isPrimary()) + return; + + // If no prior windowed size was recorded, send a 0x0 configure event + // to allow the client to choose its preferred size. + if (windowedGeometry.initialWindowSize.isValid()) + m_toplevel->sendUnmaximized(windowedGeometry.initialWindowSize); + else + m_toplevel->sendUnmaximized(); +} + +void XdgToplevelIntegration::handleMaximizedChanged() +{ + if (m_toplevel->maximized()) { + QWaylandOutput *output = m_item->view()->output(); + m_item->moveItem()->setPosition(output->position() + output->availableGeometry().topLeft()); + } else { + m_item->moveItem()->setPosition(windowedGeometry.initialPosition); + } +} + +void XdgToplevelIntegration::handleSetFullscreen() +{ + if (!m_item->view()->isPrimary()) + return; + + QVector<QWaylandXdgToplevel::State> states = m_toplevel->states(); + + if (!states.contains(QWaylandXdgToplevel::State::FullscreenState) && !states.contains(QWaylandXdgToplevel::State::MaximizedState)) { + windowedGeometry.initialWindowSize = m_xdgSurface->windowGeometry().size(); + windowedGeometry.initialPosition = m_item->moveItem()->position(); + } + + // Any prior output-resize handlers are irrelevant at this point. + disconnect(nonwindowedState.sizeChangedConnection); + nonwindowedState.output = m_item->view()->output(); + nonwindowedState.sizeChangedConnection = connect(nonwindowedState.output, &QWaylandOutput::geometryChanged, this, &XdgToplevelIntegration::handleFullscreenSizeChanged); + handleFullscreenSizeChanged(); +} + +void XdgToplevelIntegration::handleFullscreenSizeChanged() +{ + // Insurance against handleToplevelDestroyed() not managing to disconnect this + // handler in time. + if (m_toplevel == nullptr) + return; + + m_toplevel->sendFullscreen(nonwindowedState.output->geometry().size() / nonwindowedState.output->scaleFactor()); +} + +void XdgToplevelIntegration::handleUnsetFullscreen() +{ + if (!m_item->view()->isPrimary()) + return; + + // If no prior windowed size was recorded, send a 0x0 configure event + // to allow the client to choose its preferred size. + if (windowedGeometry.initialWindowSize.isValid()) + m_toplevel->sendUnmaximized(windowedGeometry.initialWindowSize); + else + m_toplevel->sendUnmaximized(); +} + +void XdgToplevelIntegration::handleFullscreenChanged() +{ + if (m_toplevel->fullscreen()) { + QWaylandOutput *output = m_item->view()->output(); + m_item->moveItem()->setPosition(output->position() + output->geometry().topLeft()); + } else { + m_item->moveItem()->setPosition(windowedGeometry.initialPosition); + } +} + +void XdgToplevelIntegration::handleActivatedChanged() +{ + if (m_toplevel->activated()) + m_item->raise(); +} + +void XdgToplevelIntegration::handleSurfaceSizeChanged() +{ + if (grabberState == GrabberState::Resize) { + qreal x = resizeState.initialPosition.x(); + qreal y = resizeState.initialPosition.y(); + if (resizeState.resizeEdges & Qt::TopEdge) + y += resizeState.initialSurfaceSize.height() - m_item->surface()->size().height(); + + if (resizeState.resizeEdges & Qt::LeftEdge) + x += resizeState.initialSurfaceSize.width() - m_item->surface()->size().width(); + m_item->moveItem()->setPosition(QPointF(x, y)); + } +} + +void XdgToplevelIntegration::handleToplevelDestroyed() +{ + // Disarm any handlers that might fire on the now-stale toplevel pointer + nonwindowedState.output = nullptr; + disconnect(nonwindowedState.sizeChangedConnection); +} + +XdgPopupIntegration::XdgPopupIntegration(QWaylandQuickShellSurfaceItem *item) + : m_item(item) + , m_xdgSurface(qobject_cast<QWaylandXdgSurface *>(item->shellSurface())) + , m_popup(m_xdgSurface->popup()) +{ + Q_ASSERT(m_popup); + + m_item->setSurface(m_xdgSurface->surface()); + handleGeometryChanged(); + + connect(m_popup, &QWaylandXdgPopup::configuredGeometryChanged, this, &XdgPopupIntegration::handleGeometryChanged); + connect(m_xdgSurface->shell(), &QWaylandXdgShell::popupCreated, this, [item](QWaylandXdgPopup *popup, QWaylandXdgSurface *){ + handlePopupCreated(item, popup); + }); +} + +void XdgPopupIntegration::handleGeometryChanged() +{ + if (m_item->view()->output()) { + const QPoint windowOffset = m_popup->parentXdgSurface()->windowGeometry().topLeft(); + const QPoint position = m_popup->unconstrainedPosition() + windowOffset; + //TODO: positioner size or other size...? + const float scaleFactor = m_item->view()->output()->scaleFactor(); + //TODO check positioner constraints etc... sliding, flipping + m_item->moveItem()->setPosition(position * scaleFactor); + } else { + qWarning() << "XdgPopupIntegration popup item without output" << m_item; + } +} + +} + +QT_END_NAMESPACE diff --git a/src/compositor/extensions/qwaylandxdgshellintegration_p.h b/src/compositor/extensions/qwaylandxdgshellintegration_p.h new file mode 100644 index 00000000..34e3873d --- /dev/null +++ b/src/compositor/extensions/qwaylandxdgshellintegration_p.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDXDGSHELLINTEGRATION_H +#define QWAYLANDXDGSHELLINTEGRATION_H + +#include <QtWaylandCompositor/private/qwaylandquickshellsurfaceitem_p.h> +#include <QtWaylandCompositor/QWaylandQuickShellSurfaceItem> +#include <QtWaylandCompositor/QWaylandXdgToplevel> + +QT_BEGIN_NAMESPACE + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +class QWaylandXdgSurface; + +namespace QtWayland { + +class XdgToplevelIntegration : public QWaylandQuickShellIntegration +{ + Q_OBJECT +public: + XdgToplevelIntegration(QWaylandQuickShellSurfaceItem *item); + bool mouseMoveEvent(QMouseEvent *event) override; + bool mouseReleaseEvent(QMouseEvent *event) override; + +private Q_SLOTS: + void handleStartMove(QWaylandSeat *seat); + void handleStartResize(QWaylandSeat *seat, Qt::Edges edges); + void handleSetMaximized(); + void handleUnsetMaximized(); + void handleMaximizedChanged(); + void handleSetFullscreen(); + void handleUnsetFullscreen(); + void handleFullscreenChanged(); + void handleActivatedChanged(); + void handleSurfaceSizeChanged(); + void handleToplevelDestroyed(); + void handleMaximizedSizeChanged(); + void handleFullscreenSizeChanged(); + +private: + QWaylandQuickShellSurfaceItem *m_item = nullptr; + QWaylandXdgSurface *m_xdgSurface = nullptr; + QWaylandXdgToplevel *m_toplevel = nullptr; + + enum class GrabberState { + Default, + Resize, + Move + }; + GrabberState grabberState; + + struct { + QWaylandSeat *seat = nullptr; + QPointF initialOffset; + bool initialized; + } moveState; + + struct { + QWaylandSeat *seat = nullptr; + Qt::Edges resizeEdges; + QSizeF initialWindowSize; + QPointF initialMousePos; + QPointF initialPosition; + QSize initialSurfaceSize; + bool initialized; + } resizeState; + + struct { + QSize initialWindowSize; + QPointF initialPosition; + } windowedGeometry; + + struct { + QWaylandOutput *output = nullptr; + QMetaObject::Connection sizeChangedConnection; // Depending on whether maximized or fullscreen, + // will be hooked to geometry-changed or available- + // geometry-changed. + } nonwindowedState; +}; + +class XdgPopupIntegration : public QWaylandQuickShellIntegration +{ + Q_OBJECT +public: + XdgPopupIntegration(QWaylandQuickShellSurfaceItem *item); + +private Q_SLOTS: + void handleGeometryChanged(); + +private: + QWaylandQuickShellSurfaceItem *m_item = nullptr; + QWaylandXdgSurface *m_xdgSurface = nullptr; + QWaylandXdgPopup *m_popup = nullptr; +}; + +} + +QT_END_NAMESPACE + +#endif // QWAYLANDXDGSHELLINTEGRATION_H diff --git a/src/imports/compositor/qwaylandquickcompositorplugin.cpp b/src/imports/compositor/qwaylandquickcompositorplugin.cpp index 9f3bf5d4..ef2b5203 100644 --- a/src/imports/compositor/qwaylandquickcompositorplugin.cpp +++ b/src/imports/compositor/qwaylandquickcompositorplugin.cpp @@ -63,6 +63,7 @@ #include <QtWaylandCompositor/QWaylandTextInputManager> #include <QtWaylandCompositor/QWaylandXdgShellV5> #include <QtWaylandCompositor/QWaylandXdgShellV6> +#include <QtWaylandCompositor/QWaylandXdgShell> #include <QtWaylandCompositor/QWaylandIviApplication> #include <QtWaylandCompositor/QWaylandIviSurface> @@ -85,6 +86,8 @@ Q_COMPOSITOR_DECLARE_QUICK_PARENT_CLASS(QWaylandXdgSurfaceV5) Q_COMPOSITOR_DECLARE_QUICK_PARENT_CLASS(QWaylandXdgPopupV5) Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandXdgShellV6) Q_COMPOSITOR_DECLARE_QUICK_PARENT_CLASS(QWaylandXdgSurfaceV6) +Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandXdgShell) +Q_COMPOSITOR_DECLARE_QUICK_PARENT_CLASS(QWaylandXdgSurface) Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandTextInputManager) class QmlUrlResolver @@ -170,6 +173,11 @@ public: qmlRegisterType<QWaylandXdgSurfaceV6QuickParent>(uri, 1, 1, "XdgSurfaceV6"); qmlRegisterUncreatableType<QWaylandXdgToplevelV6>(uri, 1, 1, "XdgToplevelV6", QObject::tr("Cannot create instance of XdgShellToplevelV6")); qmlRegisterUncreatableType<QWaylandXdgPopupV6>(uri, 1, 1, "XdgPopupV6", QObject::tr("Cannot create instance of XdgShellPopupV6")); + + qmlRegisterType<QWaylandXdgShellQuickExtension>(uri, 1, 3, "XdgShell"); + qmlRegisterType<QWaylandXdgSurfaceQuickParent>(uri, 1, 3, "XdgSurface"); + qmlRegisterUncreatableType<QWaylandXdgToplevel>(uri, 1, 3, "XdgToplevel", QObject::tr("Cannot create instance of XdgShellToplevel")); + qmlRegisterUncreatableType<QWaylandXdgPopup>(uri, 1, 3, "XdgPopup", QObject::tr("Cannot create instance of XdgShellPopup")); } }; //![class decl] diff --git a/sync.profile b/sync.profile index aa7f1034..40a38745 100644 --- a/sync.profile +++ b/sync.profile @@ -63,6 +63,7 @@ "^qwayland-server-touch-extension.h", "^qwayland-server-xdg-shell-unstable-v5.h", "^qwayland-server-xdg-shell-unstable-v6.h", + "^qwayland-server-xdg-shell.h", "^wayland-hardware-integration-server-protocol.h", "^wayland-ivi-application-server-protocol.h", "^wayland-qt-windowmanager-server-protocol.h", @@ -71,6 +72,7 @@ "^wayland-text-input-unstable-v2-server-protocol.h", "^wayland-touch-extension-server-protocol.h", "^wayland-wayland-server-protocol.h", + "^wayland-xdg-shell-server-protocol.h", "^wayland-xdg-shell-unstable-v5-server-protocol.h", "^wayland-xdg-shell-unstable-v6-server-protocol.h", ], |