summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohan Klokkhammer Helsing <johan.helsing@qt.io>2018-08-07 13:53:34 +0200
committerJohan Helsing <johan.helsing@qt.io>2018-08-15 08:47:50 +0000
commit992cde48d72435052514845b8add8d0dc880ccb2 (patch)
treef5768d4621f2286aea5e10b088cc337cfe6aacd9
parent4d0ae358dd646c369829bb38c099eb47bd04db4a (diff)
downloadqtwayland-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.qml5
-rw-r--r--examples/wayland/minimal-qml/main.qml8
-rw-r--r--examples/wayland/multi-screen/qml/main.qml6
-rw-r--r--examples/wayland/overview-compositor/main.qml4
-rw-r--r--examples/wayland/pure-qml/qml/main.qml7
-rw-r--r--examples/wayland/spanning-screens/main.qml4
-rw-r--r--src/compositor/extensions/extensions.pri6
-rw-r--r--src/compositor/extensions/qwaylandxdgshell.cpp2020
-rw-r--r--src/compositor/extensions/qwaylandxdgshell.h261
-rw-r--r--src/compositor/extensions/qwaylandxdgshell_p.h239
-rw-r--r--src/compositor/extensions/qwaylandxdgshellintegration.cpp300
-rw-r--r--src/compositor/extensions/qwaylandxdgshellintegration_p.h144
-rw-r--r--src/imports/compositor/qwaylandquickcompositorplugin.cpp8
-rw-r--r--sync.profile2
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",
],