summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Olav Tvete <paul.tvete@qt.io>2021-10-18 16:41:12 +0200
committerPaul Olav Tvete <paul.tvete@qt.io>2021-12-02 09:08:40 +0100
commitd89c8920f3b82dd2098971b5a66c4b9c75da5af0 (patch)
treeb7f35e03485f1aa8725e30870f3066b34d24ea94
parent0204df32ee7417db3156ef3c70bcf762d7e9fb0a (diff)
downloadqtwayland-d89c8920f3b82dd2098971b5a66c4b9c75da5af0.tar.gz
Introduce new qt-shell and an API for custom shells
Adds a new API for writing custom shell extensions. This API is supported, but semi-public. Binary compatibility is not guaranteed. Also adds qt-shell, a new shell that maps directly to the QWindow API, and provides functionality that Qt provides on other window systems, such as absolute window positions and window activation. This shell is not intended for use on the desktop. This is a squashed commit of a development branch consisting of approximately 60 changes. Contributors: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io> Paul Olav Tvete <paul.tvete@qt.io> Task-number: QTBUG-94330 Task-number: QTBUG-91542 Change-Id: I419b6bd8179fe03e4da47d328c7ff4b4795b8a91 Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io> Reviewed-by: David Edmundson <davidedmundson@kde.org> Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
-rw-r--r--examples/wayland/CMakeLists.txt2
-rw-r--r--examples/wayland/custom-shell/CMakeLists.txt2
-rw-r--r--examples/wayland/custom-shell/client-plugin/CMakeLists.txt51
-rw-r--r--examples/wayland/custom-shell/client-plugin/client-plugin.pro33
-rw-r--r--examples/wayland/custom-shell/client-plugin/example-shell.json3
-rw-r--r--examples/wayland/custom-shell/client-plugin/exampleshellintegration.cpp69
-rw-r--r--examples/wayland/custom-shell/client-plugin/exampleshellintegration.h70
-rw-r--r--examples/wayland/custom-shell/client-plugin/examplesurface.cpp96
-rw-r--r--examples/wayland/custom-shell/client-plugin/examplesurface.h85
-rw-r--r--examples/wayland/custom-shell/client-plugin/main.cpp79
-rw-r--r--examples/wayland/custom-shell/compositor/CMakeLists.txt62
-rw-r--r--examples/wayland/custom-shell/compositor/compositor.pro29
-rw-r--r--examples/wayland/custom-shell/compositor/compositor.qrc6
-rw-r--r--examples/wayland/custom-shell/compositor/exampleshell.cpp166
-rw-r--r--examples/wayland/custom-shell/compositor/exampleshell.h135
-rw-r--r--examples/wayland/custom-shell/compositor/exampleshellintegration.cpp76
-rw-r--r--examples/wayland/custom-shell/compositor/exampleshellintegration.h73
-rw-r--r--examples/wayland/custom-shell/compositor/images/background.pngbin0 -> 2275 bytes
-rw-r--r--examples/wayland/custom-shell/compositor/main.cpp73
-rw-r--r--examples/wayland/custom-shell/compositor/qml/main.qml119
-rw-r--r--examples/wayland/custom-shell/custom-shell.pro5
-rw-r--r--examples/wayland/custom-shell/doc/src/custom-shell.qdoc242
-rw-r--r--examples/wayland/custom-shell/protocol/example-shell.xml67
-rw-r--r--examples/wayland/qtshell/CMakeLists.txt41
-rw-r--r--examples/wayland/qtshell/doc/src/qtshell.qdoc171
-rw-r--r--examples/wayland/qtshell/images/background.jpgbin0 -> 30730 bytes
-rw-r--r--examples/wayland/qtshell/main.cpp68
-rw-r--r--examples/wayland/qtshell/qml/Chrome.qml231
-rw-r--r--examples/wayland/qtshell/qml/CompositorScreen.qml155
-rw-r--r--examples/wayland/qtshell/qml/main.qml68
-rw-r--r--examples/wayland/qtshell/qtshell.pro16
-rw-r--r--examples/wayland/qtshell/qtshell.qrc8
-rw-r--r--examples/wayland/wayland.pro4
-rw-r--r--src/client/CMakeLists.txt3
-rw-r--r--src/client/configure.cmake5
-rw-r--r--src/client/qtwaylandclientglobal.h11
-rw-r--r--src/client/qwaylanddisplay_p.h1
-rw-r--r--src/client/qwaylandintegration.cpp16
-rw-r--r--src/client/qwaylandshellsurface.cpp44
-rw-r--r--src/client/qwaylandshellsurface_p.h25
-rw-r--r--src/client/qwaylandwindow.cpp60
-rw-r--r--src/client/qwaylandwindow_p.h3
-rw-r--r--src/client/shellintegration/qwaylandclientshellapi_p.h60
-rw-r--r--src/client/shellintegration/qwaylandshellintegration.cpp57
-rw-r--r--src/client/shellintegration/qwaylandshellintegration_p.h57
-rw-r--r--src/compositor/compositor_api/qwaylandquickitem.cpp30
-rw-r--r--src/compositor/compositor_api/qwaylandquickitem_p.h3
-rw-r--r--src/compositor/compositor_api/qwaylandresource.cpp25
-rw-r--r--src/compositor/compositor_api/qwaylandsurface.cpp63
-rw-r--r--src/compositor/doc/qtwaylandcompositor.qdocconf8
-rw-r--r--src/compositor/doc/src/qtwaylandcompositor-shellextensions.qdoc4
-rw-r--r--src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp123
-rw-r--r--src/compositor/extensions/qwaylandquickshellsurfaceitem.h9
-rw-r--r--src/compositor/extensions/qwaylandquickshellsurfaceitem_p.h5
-rw-r--r--src/compositor/extensions/qwaylandshellsurface.cpp41
-rw-r--r--src/compositor/extensions/qwaylandshellsurface.h2
-rw-r--r--src/compositor/global/qwaylandcompositorextension.cpp136
-rw-r--r--src/compositor/global/qwaylandquickextension.qdoc81
-rw-r--r--src/extensions/qt-shell-unstable-v1.xml276
-rw-r--r--src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp2
-rw-r--r--src/imports/compositor-extensions/CMakeLists.txt1
-rw-r--r--src/imports/compositor-extensions/qtshell/CMakeLists.txt32
-rw-r--r--src/imports/compositor-extensions/qtshell/plugins.qmltypes442
-rw-r--r--src/imports/compositor-extensions/qtshell/qwaylandqtshell.cpp872
-rw-r--r--src/imports/compositor-extensions/qtshell/qwaylandqtshell.h202
-rw-r--r--src/imports/compositor-extensions/qtshell/qwaylandqtshell_p.h134
-rw-r--r--src/imports/compositor-extensions/qtshell/qwaylandqtshellchrome.cpp1524
-rw-r--r--src/imports/compositor-extensions/qtshell/qwaylandqtshellchrome.h181
-rw-r--r--src/imports/compositor-extensions/qtshell/qwaylandqtshellchrome_p.h113
-rw-r--r--src/imports/compositor-extensions/qtshell/qwaylandqtshellintegration.cpp63
-rw-r--r--src/imports/compositor-extensions/qtshell/qwaylandqtshellintegration_p.h71
-rw-r--r--src/imports/compositor-extensions/qtshell/qwaylandqtshellplugin.cpp63
-rw-r--r--src/plugins/shellintegration/CMakeLists.txt4
-rw-r--r--src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.cpp21
-rw-r--r--src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.h3
-rw-r--r--src/plugins/shellintegration/ivi-shell/qwaylandivishellintegration.cpp25
-rw-r--r--src/plugins/shellintegration/ivi-shell/qwaylandivishellintegration.h3
-rw-r--r--src/plugins/shellintegration/qt-shell/CMakeLists.txt31
-rw-r--r--src/plugins/shellintegration/qt-shell/main.cpp67
-rw-r--r--src/plugins/shellintegration/qt-shell/qt-shell.json3
-rw-r--r--src/plugins/shellintegration/qt-shell/qwaylandqtshellintegration.cpp89
-rw-r--r--src/plugins/shellintegration/qt-shell/qwaylandqtshellintegration.h72
-rw-r--r--src/plugins/shellintegration/qt-shell/qwaylandqtsurface.cpp252
-rw-r--r--src/plugins/shellintegration/qt-shell/qwaylandqtsurface_p.h107
-rw-r--r--src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration.cpp19
-rw-r--r--src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration_p.h2
-rw-r--r--src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp20
-rw-r--r--src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h2
-rw-r--r--sync.profile2
-rw-r--r--tests/manual/qt-shell/CMakeLists.txt43
-rw-r--r--tests/manual/qt-shell/images/background.jpgbin0 -> 30730 bytes
-rw-r--r--tests/manual/qt-shell/main.cpp68
-rw-r--r--tests/manual/qt-shell/qml/Chrome.qml505
-rw-r--r--tests/manual/qt-shell/qml/CompositorScreen.qml191
-rw-r--r--tests/manual/qt-shell/qml/HandleHandler.qml133
-rw-r--r--tests/manual/qt-shell/qml/Keyboard.qml59
-rw-r--r--tests/manual/qt-shell/qml/main.qml69
-rw-r--r--tests/manual/qt-shell/qt-shell.pro13
-rw-r--r--tests/manual/qt-shell/qt-shell.qrc10
99 files changed, 8872 insertions, 94 deletions
diff --git a/examples/wayland/CMakeLists.txt b/examples/wayland/CMakeLists.txt
index 78132bb1..1bc36387 100644
--- a/examples/wayland/CMakeLists.txt
+++ b/examples/wayland/CMakeLists.txt
@@ -14,9 +14,11 @@ if(TARGET Qt::Quick)
add_subdirectory(ivi-compositor)
add_subdirectory(server-side-decoration)
add_subdirectory(hwlayer-compositor)
+ add_subdirectory(qtshell)
endif()
if(TARGET Qt::Quick AND TARGET Qt::WaylandClient)
add_subdirectory(custom-extension)
+ add_subdirectory(custom-shell)
endif()
if(QT_FEATURE_opengl AND TARGET Qt::Quick AND TARGET Qt::WaylandClient)
add_subdirectory(server-buffer)
diff --git a/examples/wayland/custom-shell/CMakeLists.txt b/examples/wayland/custom-shell/CMakeLists.txt
new file mode 100644
index 00000000..1d12b07b
--- /dev/null
+++ b/examples/wayland/custom-shell/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_subdirectory(client-plugin)
+add_subdirectory(compositor)
diff --git a/examples/wayland/custom-shell/client-plugin/CMakeLists.txt b/examples/wayland/custom-shell/client-plugin/CMakeLists.txt
new file mode 100644
index 00000000..8af9aeb3
--- /dev/null
+++ b/examples/wayland/custom-shell/client-plugin/CMakeLists.txt
@@ -0,0 +1,51 @@
+cmake_minimum_required(VERSION 3.14)
+project(exampleshellplugin LANGUAGES CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+set(CMAKE_AUTOUIC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/wayland/custom-shell/plugins")
+
+find_package(Qt6 COMPONENTS Core)
+find_package(Qt6 COMPONENTS Gui)
+find_package(Qt6 COMPONENTS WaylandClient)
+
+qt_add_plugin(exampleshellplugin)
+target_sources(exampleshellplugin PRIVATE
+ main.cpp
+ exampleshellintegration.cpp exampleshellintegration.h
+ examplesurface.cpp examplesurface.h
+)
+
+qt6_generate_wayland_protocol_client_sources(exampleshellplugin
+ FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/../protocol/example-shell.xml
+)
+
+set_target_properties(exampleshellplugin PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+ LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/../plugins/wayland-shell-integration"
+)
+
+target_link_libraries(exampleshellplugin PUBLIC
+ Qt::Core
+ Qt::Gui
+ Qt::GuiPrivate
+ Qt::WaylandClient
+ Qt::WaylandClientPrivate
+ Wayland::Client
+)
+
+install(TARGETS exampleshellplugin
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/wayland/custom-shell/client-plugin/client-plugin.pro b/examples/wayland/custom-shell/client-plugin/client-plugin.pro
new file mode 100644
index 00000000..96a01c23
--- /dev/null
+++ b/examples/wayland/custom-shell/client-plugin/client-plugin.pro
@@ -0,0 +1,33 @@
+QT += gui-private waylandclient-private
+CONFIG += plugin wayland-scanner
+TEMPLATE = lib
+
+QMAKE_USE += wayland-client
+
+qtConfig(xkbcommon): \
+ QMAKE_USE += xkbcommon
+
+WAYLANDCLIENTSOURCES += \
+ ../protocol/example-shell.xml
+
+HEADERS += \
+ exampleshellintegration.h \
+ examplesurface.h
+
+SOURCES += \
+ main.cpp \
+ exampleshellintegration.cpp \
+ examplesurface.cpp
+
+OTHER_FILES += \
+ example-shell.json
+
+DESTDIR = ../plugins/wayland-shell-integration
+TARGET = $$qtLibraryTarget(exampleshellplugin)
+
+### Everything below this line is just to make the example work inside the Qt source tree.
+### Do not include the following lines in your own code.
+
+target.path = $$[QT_INSTALL_EXAMPLES]/wayland/customshell/plugins/wayland-shell-integration
+INSTALLS += target
+CONFIG += install_ok
diff --git a/examples/wayland/custom-shell/client-plugin/example-shell.json b/examples/wayland/custom-shell/client-plugin/example-shell.json
new file mode 100644
index 00000000..c36490af
--- /dev/null
+++ b/examples/wayland/custom-shell/client-plugin/example-shell.json
@@ -0,0 +1,3 @@
+{
+ "Keys":[ "example-shell" ]
+}
diff --git a/examples/wayland/custom-shell/client-plugin/exampleshellintegration.cpp b/examples/wayland/custom-shell/client-plugin/exampleshellintegration.cpp
new file mode 100644
index 00000000..aefe4921
--- /dev/null
+++ b/examples/wayland/custom-shell/client-plugin/exampleshellintegration.cpp
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Wayland module
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "exampleshellintegration.h"
+#include "examplesurface.h"
+
+//! [constructor]
+ExampleShellIntegration::ExampleShellIntegration()
+ : QWaylandShellIntegrationTemplate(/* Supported protocol version */ 1)
+{
+}
+//! [constructor]
+
+//! [createShellSurface]
+QWaylandShellSurface *ExampleShellIntegration::createShellSurface(QWaylandWindow *window)
+{
+ if (!isActive())
+ return nullptr;
+ auto *surface = surface_create(wlSurfaceForWindow(window));
+ return new ExampleShellSurface(surface, window);
+}
+//! [createShellSurface]
diff --git a/examples/wayland/custom-shell/client-plugin/exampleshellintegration.h b/examples/wayland/custom-shell/client-plugin/exampleshellintegration.h
new file mode 100644
index 00000000..344483ea
--- /dev/null
+++ b/examples/wayland/custom-shell/client-plugin/exampleshellintegration.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Wayland module
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef EXAMPLESHELLINTEGRATION_H
+#define EXAMPLESHELLINTEGRATION_H
+#include <QtWaylandClient/private/qwaylandclientshellapi_p.h>
+#include "qwayland-example-shell.h"
+
+using namespace QtWaylandClient;
+
+//! [shell-integration]
+class Q_WAYLAND_CLIENT_EXPORT ExampleShellIntegration
+ : public QWaylandShellIntegrationTemplate<ExampleShellIntegration>
+ , public QtWayland::qt_example_shell
+{
+public:
+ ExampleShellIntegration();
+
+ QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override;
+};
+//! [shell-integration]
+
+#endif // EXAMPLESHELLINTEGRATION_H
diff --git a/examples/wayland/custom-shell/client-plugin/examplesurface.cpp b/examples/wayland/custom-shell/client-plugin/examplesurface.cpp
new file mode 100644
index 00000000..68fbaf4b
--- /dev/null
+++ b/examples/wayland/custom-shell/client-plugin/examplesurface.cpp
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Wayland module
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "examplesurface.h"
+#include <qpa/qwindowsysteminterface.h>
+#include <qpa/qplatformwindow.h>
+
+ExampleShellSurface::ExampleShellSurface(struct ::qt_example_shell_surface *shell_surface, QWaylandWindow *window)
+ : QWaylandShellSurface(window)
+ , QtWayland::qt_example_shell_surface(shell_surface)
+{
+}
+
+ExampleShellSurface::~ExampleShellSurface()
+{
+}
+
+bool ExampleShellSurface::wantsDecorations() const
+{
+ return true;
+}
+
+//! [setTitle]
+void ExampleShellSurface::setTitle(const QString &windowTitle)
+{
+ set_window_title(windowTitle);
+}
+//! [setTitle]
+
+void ExampleShellSurface::requestWindowStates(Qt::WindowStates states)
+{
+ set_minimized(states & Qt::WindowMinimized);
+}
+
+//! [applyConfigure]
+void ExampleShellSurface::applyConfigure()
+{
+ if (m_stateChanged)
+ QWindowSystemInterface::handleWindowStateChanged(platformWindow()->window(), m_pendingStates);
+ m_stateChanged = false;
+}
+//! [applyConfigure]
+
+void ExampleShellSurface::example_shell_surface_minimize(uint32_t minimized)
+{
+ m_pendingStates = minimized ? Qt::WindowMinimized : Qt::WindowNoState;
+ m_stateChanged = true;
+ applyConfigureWhenPossible();
+}
diff --git a/examples/wayland/custom-shell/client-plugin/examplesurface.h b/examples/wayland/custom-shell/client-plugin/examplesurface.h
new file mode 100644
index 00000000..a15bd52e
--- /dev/null
+++ b/examples/wayland/custom-shell/client-plugin/examplesurface.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Wayland module
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef EXAMPLESHELLSURFACE_H
+#define EXAMPLESHELLSURFACE_H
+
+#include <QtWaylandClient/private/qwaylandclientshellapi_p.h>
+#include "qwayland-example-shell.h"
+
+using namespace QtWaylandClient;
+
+//! [ExampleShellSurface]
+class ExampleShellSurface : public QWaylandShellSurface
+ , public QtWayland::qt_example_shell_surface
+//! [ExampleShellSurface]
+{
+public:
+ ExampleShellSurface(struct ::qt_example_shell_surface *shell_surface, QWaylandWindow *window);
+ ~ExampleShellSurface() override;
+
+//! [virtuals]
+ bool wantsDecorations() const override;
+ void setTitle(const QString &) override;
+ void requestWindowStates(Qt::WindowStates states) override;
+ void applyConfigure() override;
+//! [virtuals]
+
+protected:
+//! [events]
+ void example_shell_surface_minimize(uint32_t minimized) override;
+//! [events]
+
+private:
+ Qt::WindowStates m_pendingStates;
+ bool m_stateChanged = false;
+};
+
+#endif // EXAMPLESHELLSURFACE_H
diff --git a/examples/wayland/custom-shell/client-plugin/main.cpp b/examples/wayland/custom-shell/client-plugin/main.cpp
new file mode 100644
index 00000000..3a3ad822
--- /dev/null
+++ b/examples/wayland/custom-shell/client-plugin/main.cpp
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Wayland module
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "exampleshellintegration.h"
+
+//! [include]
+#include <QtWaylandClient/private/qwaylandclientshellapi_p.h>
+//! [include]
+
+#include "qwayland-example-shell.h"
+
+using namespace QtWaylandClient;
+
+//! [plugin]
+class QWaylandExampleShellIntegrationPlugin : public QWaylandShellIntegrationPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QWaylandShellIntegrationFactoryInterface_iid FILE "example-shell.json")
+
+public:
+ QWaylandShellIntegration *create(const QString &key, const QStringList &paramList) override;
+};
+
+QWaylandShellIntegration *QWaylandExampleShellIntegrationPlugin::create(const QString &key, const QStringList &paramList)
+{
+ Q_UNUSED(key);
+ Q_UNUSED(paramList);
+ return new ExampleShellIntegration();
+}
+//! [plugin]
+
+#include "main.moc"
diff --git a/examples/wayland/custom-shell/compositor/CMakeLists.txt b/examples/wayland/custom-shell/compositor/CMakeLists.txt
new file mode 100644
index 00000000..aab5a558
--- /dev/null
+++ b/examples/wayland/custom-shell/compositor/CMakeLists.txt
@@ -0,0 +1,62 @@
+# Generated from compositor.pro.
+
+cmake_minimum_required(VERSION 3.14)
+project(custom-shell-compositor) # special case
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+set(CMAKE_AUTOUIC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/wayland/custom-shell/compositor")
+
+find_package(Qt6 COMPONENTS Core)
+find_package(Qt6 COMPONENTS Gui)
+find_package(Qt6 COMPONENTS Qml)
+find_package(Qt6 COMPONENTS WaylandCompositor)
+
+qt_add_executable(custom-shell-compositor
+ exampleshell.cpp exampleshell.h
+ exampleshellintegration.cpp exampleshellintegration.h
+ main.cpp
+)
+
+qt6_generate_wayland_protocol_server_sources(custom-shell-compositor
+ FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/../protocol/example-shell.xml
+)
+set_target_properties(custom-shell-compositor PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+target_link_libraries(custom-shell-compositor PUBLIC
+ Qt::Core
+ Qt::Gui
+ Qt::Qml
+ Qt::WaylandCompositor
+)
+
+
+# Resources:
+set(compositor_resource_files
+ "images/background.png"
+ "qml/main.qml"
+)
+
+qt6_add_resources(custom-shell-compositor "compositor"
+ PREFIX
+ "/"
+ FILES
+ ${compositor_resource_files}
+)
+
+install(TARGETS custom-shell-compositor
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/wayland/custom-shell/compositor/compositor.pro b/examples/wayland/custom-shell/compositor/compositor.pro
new file mode 100644
index 00000000..d64ddac6
--- /dev/null
+++ b/examples/wayland/custom-shell/compositor/compositor.pro
@@ -0,0 +1,29 @@
+QT += core gui qml
+
+QT += waylandcompositor
+
+CONFIG += wayland-scanner
+CONFIG += c++11
+SOURCES += \
+ main.cpp \
+ exampleshell.cpp \
+ exampleshellintegration.cpp \
+
+HEADERS += \
+ exampleshell.h \
+ exampleshellintegration.h
+
+OTHER_FILES = \
+ qml/main.qml \
+ images/background.jpg
+
+WAYLANDSERVERSOURCES += \
+ ../protocol/example-shell.xml
+
+RESOURCES += compositor.qrc
+
+TARGET = custom-shell-compositor
+
+
+target.path = $$[QT_INSTALL_EXAMPLES]/wayland/custom-shell/compositor
+INSTALLS += target
diff --git a/examples/wayland/custom-shell/compositor/compositor.qrc b/examples/wayland/custom-shell/compositor/compositor.qrc
new file mode 100644
index 00000000..59c00f5f
--- /dev/null
+++ b/examples/wayland/custom-shell/compositor/compositor.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/background.png</file>
+ <file>qml/main.qml</file>
+ </qresource>
+</RCC>
diff --git a/examples/wayland/custom-shell/compositor/exampleshell.cpp b/examples/wayland/custom-shell/compositor/exampleshell.cpp
new file mode 100644
index 00000000..12e5c459
--- /dev/null
+++ b/examples/wayland/custom-shell/compositor/exampleshell.cpp
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Wayland module
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "exampleshell.h"
+#include "exampleshellintegration.h"
+
+#include <QtWaylandCompositor/QWaylandCompositor>
+#include <QtWaylandCompositor/QWaylandSurface>
+#include <QtWaylandCompositor/QWaylandResource>
+
+ExampleShell::ExampleShell()
+{
+}
+
+ExampleShell::ExampleShell(QWaylandCompositor *compositor)
+ : QWaylandCompositorExtensionTemplate<ExampleShell>(compositor)
+{
+ this->compositor = compositor;
+}
+
+//! [initialize]
+void ExampleShell::initialize()
+{
+ QWaylandCompositorExtensionTemplate::initialize();
+
+ QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer());
+ if (!compositor) {
+ qWarning() << "Failed to find QWaylandCompositor when initializing ExampleShell";
+ return;
+ }
+
+ init(compositor->display(), 1);
+}
+//! [initialize]
+
+//! [surface_create]
+void ExampleShell::example_shell_surface_create(Resource *resource, wl_resource *surfaceResource, uint32_t id)
+{
+ QWaylandSurface *surface = QWaylandSurface::fromResource(surfaceResource);
+
+ if (!surface->setRole(ExampleShellSurface::role(), resource->handle, QT_EXAMPLE_SHELL_ERROR_ROLE))
+ return;
+
+ QWaylandResource shellSurfaceResource(wl_resource_create(resource->client(), &::qt_example_shell_surface_interface,
+ wl_resource_get_version(resource->handle), id));
+
+ auto *shellSurface = new ExampleShellSurface(this, surface, shellSurfaceResource);
+ emit shellSurfaceCreated(shellSurface);
+}
+//! [surface_create]
+
+ExampleShellSurface::ExampleShellSurface(ExampleShell *shell, QWaylandSurface *surface, const QWaylandResource &resource)
+{
+ m_shell = shell;
+ m_surface = surface;
+ init(resource.resource());
+ setExtensionContainer(surface);
+ QWaylandCompositorExtension::initialize();
+}
+
+QWaylandSurfaceRole ExampleShellSurface::s_role("qt_example_shell_surface");
+
+/*!
+ * Returns the surface role for the ExampleShellSurface.
+ */
+QWaylandSurfaceRole *ExampleShellSurface::role()
+{
+ return &s_role;
+}
+
+/*!
+ * Returns the ExampleShellSurface corresponding to the \a resource.
+ */
+ExampleShellSurface *ExampleShellSurface::fromResource(wl_resource *resource)
+{
+ auto *res = Resource::fromResource(resource);
+ return static_cast<ExampleShellSurface *>(res->object());
+}
+
+QWaylandQuickShellIntegration *ExampleShellSurface::createIntegration(QWaylandQuickShellSurfaceItem *item)
+{
+ return new ExampleShellIntegration(item);
+}
+
+void ExampleShellSurface::example_shell_surface_destroy_resource(Resource *resource)
+{
+ Q_UNUSED(resource);
+ delete this;
+}
+
+void ExampleShellSurface::example_shell_surface_destroy(Resource *resource)
+{
+ wl_resource_destroy(resource->handle);
+}
+
+void ExampleShellSurface::example_shell_surface_set_window_title(Resource *resource, const QString &window_title)
+{
+ Q_UNUSED(resource);
+ m_windowTitle = window_title;
+ emit windowTitleChanged();
+}
+
+void ExampleShellSurface::example_shell_surface_set_minimized(Resource *resource, uint32_t minimized)
+{
+ Q_UNUSED(resource);
+ if (m_minimized != minimized) {
+ m_minimized = minimized;
+ emit minimizedChanged();
+ }
+}
+
+void ExampleShellSurface::setMinimized(bool newMinimized)
+{
+ if (m_minimized == newMinimized)
+ return;
+ m_minimized = newMinimized;
+ send_minimize(newMinimized);
+ emit minimizedChanged();
+}
diff --git a/examples/wayland/custom-shell/compositor/exampleshell.h b/examples/wayland/custom-shell/compositor/exampleshell.h
new file mode 100644
index 00000000..05e23555
--- /dev/null
+++ b/examples/wayland/custom-shell/compositor/exampleshell.h
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Wayland module
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef EXAMPLESHELL_H
+#define EXAMPLESHELL_H
+
+#include <QtWaylandCompositor/QWaylandCompositorExtension>
+#include <QtWaylandCompositor/QWaylandQuickExtension>
+#include <QtWaylandCompositor/QWaylandSurface>
+#include <QtWaylandCompositor/QWaylandResource>
+#include <QtCore/QSize>
+
+#include <QtWaylandCompositor/QWaylandShellSurface>
+#include "qwayland-server-example-shell.h"
+
+class ExampleShellSurface;
+
+//! [ExampleShell]
+class ExampleShell
+ : public QWaylandCompositorExtensionTemplate<ExampleShell>
+ , QtWaylandServer::qt_example_shell
+//! [ExampleShell]
+{
+ Q_OBJECT
+public:
+ ExampleShell();
+ ExampleShell(QWaylandCompositor *compositor);
+
+ void initialize() override;
+
+ using QtWaylandServer::qt_example_shell::interface;
+ using QtWaylandServer::qt_example_shell::interfaceName;
+
+protected:
+ void example_shell_surface_create(Resource *resource, wl_resource *surfaceResource, uint32_t id) override;
+
+signals:
+ void shellSurfaceCreated(ExampleShellSurface *shellSurface);
+
+private:
+ QWaylandCompositor *compositor;
+};
+
+class ExampleShellSurface :
+ public QWaylandShellSurfaceTemplate<ExampleShellSurface>
+ , public QtWaylandServer::qt_example_shell_surface
+{
+ Q_OBJECT
+ Q_PROPERTY(QString windowTitle READ windowTitle NOTIFY windowTitleChanged)
+ Q_PROPERTY(bool minimized READ minimized WRITE setMinimized NOTIFY minimizedChanged)
+public:
+ ExampleShellSurface(ExampleShell *shell, QWaylandSurface *surface, const QWaylandResource &resource);
+
+ using QtWaylandServer::qt_example_shell_surface::interface;
+ using QtWaylandServer::qt_example_shell_surface::interfaceName;
+ static QWaylandSurfaceRole *role();
+ static ExampleShellSurface *fromResource(::wl_resource *resource);
+
+ QWaylandQuickShellIntegration *createIntegration(QWaylandQuickShellSurfaceItem *item) override;
+
+ QWaylandSurface *surface() const { return m_surface; }
+ const QString &windowTitle() const { return m_windowTitle; }
+ bool minimized() const { return m_minimized; }
+ void setMinimized(bool newMinimized);
+
+signals:
+ void windowTitleChanged();
+ void minimizedChanged();
+
+protected:
+ void example_shell_surface_destroy_resource(Resource *resource) override;
+ void example_shell_surface_destroy(Resource *resource) override;
+ void example_shell_surface_set_window_title(Resource *resource, const QString &window_title) override;
+ void example_shell_surface_set_minimized(Resource *resource, uint32_t minimized) override;
+
+private:
+ static QWaylandSurfaceRole s_role;
+ QWaylandSurface *m_surface = nullptr;
+ ExampleShell *m_shell = nullptr;
+ QString m_windowTitle;
+ bool m_minimized = false;
+};
+
+//! [declare_extension]
+Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(ExampleShell)
+//! [declare_extension]
+
+#endif // EXAMPLESHELL_H
diff --git a/examples/wayland/custom-shell/compositor/exampleshellintegration.cpp b/examples/wayland/custom-shell/compositor/exampleshellintegration.cpp
new file mode 100644
index 00000000..2c796692
--- /dev/null
+++ b/examples/wayland/custom-shell/compositor/exampleshellintegration.cpp
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Wayland module
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "exampleshellintegration.h"
+
+#include <QtWaylandCompositor/QWaylandCompositor>
+#include <QtWaylandCompositor/QWaylandQuickShellSurfaceItem>
+#include <QtWaylandCompositor/QWaylandSeat>
+#include "exampleshell.h"
+
+
+ExampleShellIntegration::ExampleShellIntegration(QWaylandQuickShellSurfaceItem *item)
+ : QWaylandQuickShellIntegration(item)
+ , m_item(item)
+ , m_shellSurface(qobject_cast<ExampleShellSurface *>(item->shellSurface()))
+{
+ m_item->setSurface(m_shellSurface->surface());
+ connect(m_shellSurface, &ExampleShellSurface::destroyed, this, &ExampleShellIntegration::handleExampleShellSurfaceDestroyed);
+}
+
+ExampleShellIntegration::~ExampleShellIntegration()
+{
+ m_item->setSurface(nullptr);
+}
+
+void ExampleShellIntegration::handleExampleShellSurfaceDestroyed()
+{
+ m_shellSurface = nullptr;
+}
diff --git a/examples/wayland/custom-shell/compositor/exampleshellintegration.h b/examples/wayland/custom-shell/compositor/exampleshellintegration.h
new file mode 100644
index 00000000..0e0d5c1f
--- /dev/null
+++ b/examples/wayland/custom-shell/compositor/exampleshellintegration.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Wayland module
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef EXAMPLESHELLINTEGRATION_H
+#define EXAMPLESHELLINTEGRATION_H
+
+#include "exampleshell.h"
+#include <QtWaylandCompositor/QWaylandQuickShellIntegration>
+#include <QtWaylandCompositor/QWaylandQuickShellSurfaceItem>
+
+class ExampleShellIntegration : public QWaylandQuickShellIntegration
+{
+ Q_OBJECT
+public:
+ ExampleShellIntegration(QWaylandQuickShellSurfaceItem *item);
+ ~ExampleShellIntegration() override;
+
+private slots:
+ void handleExampleShellSurfaceDestroyed();
+
+private:
+ QWaylandQuickShellSurfaceItem *m_item = nullptr;
+ ExampleShellSurface *m_shellSurface = nullptr;
+};
+
+#endif // EXAMPLESHELLINTEGRATION_H
diff --git a/examples/wayland/custom-shell/compositor/images/background.png b/examples/wayland/custom-shell/compositor/images/background.png
new file mode 100644
index 00000000..cf264746
--- /dev/null
+++ b/examples/wayland/custom-shell/compositor/images/background.png
Binary files differ
diff --git a/examples/wayland/custom-shell/compositor/main.cpp b/examples/wayland/custom-shell/compositor/main.cpp
new file mode 100644
index 00000000..391ed694
--- /dev/null
+++ b/examples/wayland/custom-shell/compositor/main.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Wayland module
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QUrl>
+#include <QtCore/QDebug>
+#include <QtGui/QGuiApplication>
+#include <QtQml/QQmlApplicationEngine>
+
+#include <QtQml/qqml.h>
+#include <QtQml/QQmlEngine>
+
+#include "exampleshell.h"
+
+static void registerTypes()
+{
+ qmlRegisterType<ExampleShellQuickExtension>("io.qt.examples", 1, 0, "ExampleShell");
+}
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+ registerTypes();
+ QQmlApplicationEngine appEngine(QUrl("qrc:///qml/main.qml"));
+
+ return app.exec();
+}
diff --git a/examples/wayland/custom-shell/compositor/qml/main.qml b/examples/wayland/custom-shell/compositor/qml/main.qml
new file mode 100644
index 00000000..e4f23b73
--- /dev/null
+++ b/examples/wayland/custom-shell/compositor/qml/main.qml
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Wayland module
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtWayland.Compositor
+import QtQuick.Layouts
+import QtQuick.Controls
+
+import io.qt.examples 1.0
+
+WaylandCompositor {
+ id: comp
+
+ ListModel { id: shellSurfaces }
+
+
+ WaylandOutput {
+ sizeFollowsWindow: true
+ window: Window {
+ width: 1024
+ height: 768
+ visible: true
+
+ ColumnLayout {
+ anchors.fill: parent
+ spacing: 0
+ Image {
+ fillMode: Image.Tile
+ source: "qrc:/images/background.png"
+ smooth: false
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ GridLayout {
+ columns: 3
+ Repeater {
+ model: shellSurfaces
+ ShellSurfaceItem {
+ id: chrome
+ shellSurface: modelData
+ visible: !shellSurface.minimized
+ onSurfaceDestroyed: shellSurfaces.remove(index)
+ }
+ }
+ }
+ }
+ Row {
+ id: taskbar
+ Layout.fillWidth: true
+ Repeater {
+ model: shellSurfaces
+ Button {
+ id: minimizeButton
+ checkable: true
+ text: modelData.windowTitle
+ onCheckedChanged: modelData.minimized = checked
+ checked: modelData.minimized
+ }
+ }
+ }
+ }
+ }
+ }
+
+ //! [ExampleShell]
+ ExampleShell {
+ id: shell
+ onShellSurfaceCreated: (shellSurface) => {
+ shellSurfaces.append({shellSurface: shellSurface});
+ }
+ }
+ //! [ExampleShell]
+}
diff --git a/examples/wayland/custom-shell/custom-shell.pro b/examples/wayland/custom-shell/custom-shell.pro
new file mode 100644
index 00000000..246115d3
--- /dev/null
+++ b/examples/wayland/custom-shell/custom-shell.pro
@@ -0,0 +1,5 @@
+TEMPLATE=subdirs
+
+SUBDIRS += compositor client-plugin
+
+OTHER_FILES += protocol/example-shell.xml
diff --git a/examples/wayland/custom-shell/doc/src/custom-shell.qdoc b/examples/wayland/custom-shell/doc/src/custom-shell.qdoc
new file mode 100644
index 00000000..7ed9305a
--- /dev/null
+++ b/examples/wayland/custom-shell/doc/src/custom-shell.qdoc
@@ -0,0 +1,242 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ * \title Qt Wayland Compositor Examples - Custom Shell
+ * \example custom-shell
+ * \brief Custom Shell shows how to implement a custom shell extension.
+ * \ingroup qtwaylandcompositor-examples
+ *
+ * \l{Shell Extensions - Qt Wayland Compositor}{Shell extensions} to Wayland are protocols that
+ * manage window state, position and size. Most compositors will support one or more of built-in
+ * extensions, but in some circumstances it can be useful to be able to write a custom one which
+ * contains the exact features your applications need.
+ *
+ * This requires that you implement the shell extension on both the server-side and client-side
+ * of the Wayland connection, so it is mainly useful when you are building a platform and are in
+ * control of both the compositor and its client applications.
+ *
+ * The Custom Shell example shows the implementation of a simple shell extension. It is divided into
+ * three parts:
+ * \list
+ * \li A protocol description for a custom shell interface.
+ * \li A plugin for connecting to the interface in a client application.
+ * \li An example compositor with a server-side implementation of the interface.
+ * \endlist
+ *
+ * The protocol description follows the standard XML format read by \c{wayland-scanner}. It will
+ * not be covered in detail here, but it covers the following features:
+ *
+ * \list
+ * \li An interface for creating a shell surfaces for a \c{wl_surface}. This allows the protocol
+ * to add functionality on top of the existing \c{wl_surface} APIs.
+ * \li A request to set a window title on the shell surface.
+ * \li A request to minimize/de-minimize the shell surface.
+ * \li An event informing the client of the shell surface's current minimized state.
+ * \endlist
+ *
+ * \section1 The Client Plugin
+ *
+ * In order for the shell integration to be discovered by a Qt client, we must reimplement
+ * the QWaylandShellIntegrationPlugin.
+ *
+ * \snippet custom-shell/client-plugin/main.cpp plugin
+ *
+ * This attaches the "example-shell" key to the shell integration and provides a way for the
+ * \c ExampleShellIntegration class to be instantiated when a client connects to the interface.
+ *
+ * The APIs for creating shell extensions are available in the header \c qwaylandclientshellapi_p.h.
+ *
+ * \snippet custom-shell/client-plugin/main.cpp include
+ *
+ * This header requires including private API because unlike public Qt APIs, it does not come with
+ * binary compatibility guarantees. The APIs are still considered stable and will remain source
+ * compatible, and are similar in this respect to other plugin APIs in Qt.
+ *
+ * The \c ExampleShellIntegration is the client-side entry point for creating shell surfaces as
+ * described above. It extends the QWaylandShellIntegrationTemplate class, using the
+ * \l{https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern}{Curiously Recurring Template Pattern}.
+ *
+ * \snippet custom-shell/client-plugin/exampleshellintegration.h shell-integration
+ *
+ * It also inherits from the \c QtWayland::qt_example_shell class, which is generated by
+ * \c qtwaylandscanner based on the XML description of the protocol.
+ *
+ * The constructor specifies the version of the protocol that we support:
+ *
+ * \snippet custom-shell/client-plugin/exampleshellintegration.cpp constructor
+ *
+ * The example_shell protocol is currently at version one, so we pass a \c{1} to the parent
+ * class. This is used in protocol negotiation, and makes sure that older clients will continue
+ * working if the compositor uses a newer version of the protocol.
+ *
+ * When the \c ExampleShellIntegration is initialized, the application is connected to the server,
+ * and has received the broadcast of global interfaces the compositor supports.
+ * If successful, it can issue requests for the interface. In this
+ * case, there is only one request to support: Creating a shell surface. It uses the built-in
+ * function \c wlSurfaceForWindow() to convert the QWaylandWindow to a \c{wl_surface}, then it issues the
+ * request. It then extends the returned surface with a \c ExampleShellSurface object which will
+ * handle the requests and events on the \c qt_example_shell_surface interface.
+ *
+ * \snippet custom-shell/client-plugin/exampleshellintegration.cpp createShellSurface
+ *
+ * The \c ExampleShellSurface extends two classes.
+ *
+ * \snippet custom-shell/client-plugin/examplesurface.h ExampleShellSurface
+ *
+ * The first is the \c QtWayland::qt_example_shell_surface class which is generated based on the XML
+ * description of the protocol. This provides virtual functions for events and ordinary member
+ * functions for the requests in the protocol.
+ *
+ * The \c QtWayland::qt_example_shell_surface class only has a single event.
+ *
+ * \snippet custom-shell/client-plugin/examplesurface.h events
+ *
+ * The \c ExampleShellSurface reimplements this to update its internal window state. When the
+ * window state is change, it stores the pending state until later and calls
+ * \c{applyConfigureWhenPossible()} in QWaylandShellSurface. State, size and position changes should
+ * be organized like this. That way, we ensure that changes do not interfere with rendering to the
+ * surface, and multiple related changes can easily be applied as one.
+ *
+ * When it is safe to reconfigure the surface, the virtual \c applyConfigure() function is called.
+ *
+ * \snippet custom-shell/client-plugin/examplesurface.cpp applyConfigure
+ *
+ * This is where we actually commit the new (minimized or de-minimized) state to the window.
+ *
+ * The second super class is QWaylandShellSurface. This is the interface used by Wayland's QPA
+ * plugin and QWaylandWindow to communicate with the shell. The \c ExampleShellSurface reimplements
+ * a few virtual functions from this interface as well.
+ *
+ * \snippet custom-shell/client-plugin/examplesurface.h virtuals
+ *
+ * For example, when the Qt applications sets the title of a window, this translates into a call to
+ * the virtual \c setTitle() function.
+ *
+ * \snippet custom-shell/client-plugin/examplesurface.cpp setTitle
+ *
+ * In the \c ExampleShellSurface this in turn translates to a request on our custom shell surface
+ * interface.
+ *
+ * \section1 The Compositor
+ *
+ * The final part of the example is the compositor itself. This has the same general structure as
+ * the other compositor examples. See the
+ * \l{Qt Wayland Compositor Examples - Minimal QML}{Minimal QML example} for more details on
+ * the building blocks of a \l{Qt Wayland Compositor}.
+ *
+ * One notable difference in the Custom Shell compositor is the instantiation of the shell
+ * extension. Where the \l{Qt Wayland Compositor Examples - Minimal QML}{the Minimal QML example}
+ * instantiates the shell extensions \l{IviApplication}, \l{XdgShell} and \l{WlShell}, the
+ * Custom Shell example only creates an instance of the \c ExampleShell extension.
+ *
+ * \snippet custom-shell/compositor/qml/main.qml ExampleShell
+ *
+ * We create the instance of the shell extension as a direct child of the WaylandCompositor in
+ * order to have it registered as a global interface. This will be broadcasted to clients as they
+ * connect, and they will be able to attach to the interface as outlined in the previous section.
+ *
+ * The \c ExampleShell is a subclass of the generated \c QtWaylandServer::qt_example_shell
+ * interface, which contains the API defined in the protocol XML. It is also a subclass of
+ * \l{QWaylandCompositorExtensionTemplate}, which ensures the objects are recognized by
+ * QWaylandCompositor as extensions.
+ *
+ * \snippet custom-shell/compositor/exampleshell.h ExampleShell
+ *
+ * This dual inheritance is a typical pattern in Qt Wayland Compositor when building extensions.
+ * The QWaylandCompositorExtensionTemplate class creates the connection between
+ * QWaylandCompositorExtension and the \c qt_example_shell class generated by \c qtwaylandscanner.
+ *
+ * Equivalently, the \c ExampleShellSurface class extends the generated
+ * \c QtWaylandServer::qt_example_shell_surface class as well as \l{QWaylandShellSurfaceTemplate},
+ * which makes it a subclass of the ShellSurface class and establishes the connection between
+ * Qt Wayland Compositor and the generated protocol code.
+ *
+ * To make the type available to Qt Quick, we use the \l{Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS}
+ * preprocessor macro for the convenience. Among other things, this handles automatically
+ * initializing the extension when it has been added to the Qt Quick graph.
+ *
+ * \snippet custom-shell/compositor/exampleshell.cpp initialize
+ *
+ * The default implementation of the \c initialize() function register the extension with the
+ * compositor. In addition to this, we initialize the protocol extension itself. We do this by
+ * calling the generated \c init() function in the \c QtWaylandServer::qt_example_shell_surface
+ * class.
+ *
+ * We also reimplement the virtual function generated for the \c surface_create request.
+ *
+ * \snippet custom-shell/compositor/exampleshell.cpp surface_create
+ *
+ * This virtual function is called whenever a client issues the request on the connection.
+ *
+ * Our shell extension only supports a single QWaylandSurfaceRole, but it is still important that
+ * we assign it to the QWaylandSurface when we create a shell surface for it. The primary reason
+ * for this is that assigning conflicting roles to the same surface is considered a protocol error,
+ * and it is the compositor's responsibility to issue this error if it happens. Setting a role on
+ * the surface when we adopt it, ensures that the protocol error will be issued if the surface is
+ * reused with a different role later.
+ *
+ * We use built-in functions to convert between Wayland and Qt types, and create an
+ * \c ExampleShellSurface object. When everything is prepared, we emit the \c shellSurfaceCreated()
+ * signal, which in turn is intercepted in the QML code and added to the list of shell surfaces.
+ *
+ * \snippet custom-shell/compositor/qml/main.qml ExampleShell
+ *
+ * In \c{ExampleShellSurface}, we equivalently enable the shell surface part of the protocol
+ * extension.
+ *
+ * \section1 Running the example
+ *
+ * In order to have a client successfully connect to the new shell extension, there is a couple
+ * of configuration details to be handled.
+ *
+ * First of all, the client has to be able to find the shell extension's plugin. One simple way
+ * of doing this is to set the \c QT_PLUGIN_PATH to point to the plugin install directory. Since
+ * Qt will look up plugins by category, the plugin path should point to the parent directory that
+ * contains the directory for category \c{wayland-shell-integration}. So if the installed file is
+ * \c{/path/to/build/plugins/wayland-shell-integration/libexampleshellplugin.so}, then you should
+ * set \c QT_PLUGIN_PATH as follows:
+ *
+ * \badcode
+ * export QT_PLUGIN_PATH=/path/to/build/plugins
+ * \endcode
+ *
+ * For other ways to configure the plugin directory, see the
+ * \l{Deploying Plugins}{plugin documentation}.
+ *
+ * The final step is to make sure the client actually attaches to the correct shell extension.
+ * Qt clients will automatically try to attach to the built-in shell extensions, but this can be
+ * overridden by setting the \c QT_WAYLAND_SHELL_INTEGRATION environment variable to the name of
+ * the extension to load.
+ *
+ * \badcode
+ * export QT_WAYLAND_SHELL_INTEGRATION=example-shell
+ * \endcode
+ *
+ * And that is all there is to it. The Custom Shell example is a limited shell extension with only
+ * a very few features, but it can be used as a starting point for building specialized extensions.
+ */
diff --git a/examples/wayland/custom-shell/protocol/example-shell.xml b/examples/wayland/custom-shell/protocol/example-shell.xml
new file mode 100644
index 00000000..f3bfea13
--- /dev/null
+++ b/examples/wayland/custom-shell/protocol/example-shell.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="example_shell">
+ <copyright>
+ Copyright (C) 2021 The Qt Company Ltd.
+ Contact: http://www.qt.io/licensing/
+
+ This file is part of the examples of the Qt Wayland module.
+
+ $QT_BEGIN_LICENSE:BSD$
+ You may use this file under the terms of the BSD license as follows:
+
+ "Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of The Qt Company Ltd nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+
+ $QT_END_LICENSE$
+ </copyright>
+
+ <interface name="qt_example_shell_surface" version="1">
+ <request name="destroy" type="destructor">
+ </request>
+
+ <request name="set_window_title">
+ <arg name="window_title" type="string" />
+ </request>
+
+ <request name="set_minimized">
+ <arg name="minimized" type="uint" />
+ </request>
+ <event name="minimize">
+ <arg name="minimized" type="uint"/>
+ </event>
+ </interface>
+
+ <interface name="qt_example_shell" version="1">
+ <request name="surface_create">
+ <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="id" type="new_id" interface="qt_example_shell_surface"/>
+ </request>
+ <enum name="error">
+ <entry name="role" value="0" summary="wl_surface already has a different role"/>
+ </enum>
+ </interface>
+
+</protocol>
diff --git a/examples/wayland/qtshell/CMakeLists.txt b/examples/wayland/qtshell/CMakeLists.txt
new file mode 100644
index 00000000..c6d3b379
--- /dev/null
+++ b/examples/wayland/qtshell/CMakeLists.txt
@@ -0,0 +1,41 @@
+cmake_minimum_required(VERSION 3.14)
+project(qtshell LANGUAGES CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+set(CMAKE_AUTOUIC ON)
+
+find_package(Qt6 COMPONENTS Core)
+find_package(Qt6 COMPONENTS Gui)
+find_package(Qt6 COMPONENTS Qml)
+
+qt_add_executable(qtshell
+ main.cpp
+)
+set_target_properties(qtshell PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+target_link_libraries(qtshell PUBLIC
+ Qt::Core
+ Qt::Gui
+ Qt::Qml
+)
+
+
+# Resources:
+set(qtshell_resource_files
+ "images/background.jpg"
+ "qml/Chrome.qml"
+ "qml/CompositorScreen.qml"
+ "qml/main.qml"
+)
+
+qt6_add_resources(qtshell "qtshell"
+ PREFIX
+ "/"
+ FILES
+ ${qtshell_resource_files}
+)
diff --git a/examples/wayland/qtshell/doc/src/qtshell.qdoc b/examples/wayland/qtshell/doc/src/qtshell.qdoc
new file mode 100644
index 00000000..6990d082
--- /dev/null
+++ b/examples/wayland/qtshell/doc/src/qtshell.qdoc
@@ -0,0 +1,171 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ * \title Qt Wayland Compositor Examples - QtShell Compositor
+ * \example qtshell
+ * \brief QtShell Compositor shows how to use the QtShell shell extension.
+ * \ingroup qtwaylandcompositor-examples
+ *
+ * QtShell Compositor is a desktop-style Wayland compositor example implementing a
+ * complete Qt Wayland Compositor which uses the specialized
+ * \l{Shell Extensions - Qt Wayland Compositor}{shell extension protocol} called \l{QtShell}.
+ *
+ * The compositor is implemented with Qt Quick and QML.
+ *
+ * \section1 Making the Connection
+ *
+ * The example lists QtShell as the only extension to the WaylandCompositor object. This means that
+ * any client connecting to the server must also support this extension, thus they should be Qt
+ * applications running against the same version of Qt as the compositor.
+ *
+ * \snippet qtshell/qml/main.qml shell
+ *
+ * When a client connects to the QtShell interface, it creates a \l{QtShellSurface}. The compositor
+ * is notified of this by the emission of the
+ * \l{QtWaylandCompositor::QtShell::qtShellSurfaceCreated()}{qtShellSurfaceCreated} signal. The
+ * example then adds the shell surface to a ListModel for easy access later.
+ *
+ * \snippet qtshell/qml/CompositorScreen.qml handleShellSurface
+ *
+ * The ListModel is used as the model for a \l{Repeater} which creates the Qt Quick items required
+ * to display the client contents on screen.
+ *
+ * \snippet qtshell/qml/CompositorScreen.qml repeater
+ *
+ * It uses the local \c Chrome type, which handles window states and decorations.
+ *
+ * \section1 Chrome
+ *
+ * The \c Chrome is the type that ensures the client contents are visible and also handles window
+ * state, position, size, and so on. It uses the built-in QtShellChrome as a basis, which
+ * automatically handles window state (maximized, minimized, fullscreen) and window activation
+ * (ensuring that only a single window is active at the time).
+ *
+ * Its behavior can be customized to some extent, but it is also possible to write the \c Chrome
+ * functionality from scratch, building from a basic \l{Item} type instead. QtShellChrome is a
+ * convenience class which provides typical compositor behavior, and saves us the time of
+ * implementing this logic in the example.
+ *
+ * However the \c Chrome is written, it should have a ShellSurfaceItem to hold the client contents.
+ *
+ * \snippet qtshell/qml/Chrome.qml shellsurfaceitem
+ *
+ * The ShellSurfaceItem is the visual representation of the client's contents in the Qt Quick scene.
+ * Its size should usually match the size of the client's buffer, otherwise it may look stretched or
+ * squeezed. QtShellChrome will automatically be sized to the \l{QtShellSurface}'s
+ * \l{QtShellSurface::windowGeometry}{windowGeometry}, which is size of the
+ * client's buffer plus the size of the frame margins. The frame margins are reserved areas on the
+ * sides of the \c Chrome which can be used to contain window decorations.
+ *
+ * The ShellSurfaceItem is therefore anchored to the window decorations to fill the area reserved
+ * for the client buffer.
+ *
+ * \section1 Window Decorations
+ *
+ * The window decoration is usually a frame around a client's contents which adds information
+ * (such as a window title) and the possibility of user interaction (such as resizing, closing,
+ * moving the window, and so on.)
+ *
+ * With \l{QtShell}, window decorations are always drawn by the compositor and not by the client.
+ * In order for sizes and positions to be communicated correctly, QtShell also needs to know how
+ * much of the window is reserved for these decorations. This can be handled automatically by
+ * QtShellChrome, or manually, by setting
+ * \l{QtShellChrome::frameMarginLeft}{frameMarginLeft},
+ * \l{QtShellChrome::frameMarginRight}{frameMarginRight},
+ * \l{QtShellChrome::frameMarginTop}{frameMarginTop} and
+ * \l{QtShellChrome::frameMarginBottom}{frameMarginBottom}.
+ *
+ * For typical cases where there are resize handles around the window and a title bar at the top,
+ * it is more convenient to rely on the default frame margins. The QtShell Compositor example
+ * does this.
+ *
+ * First, we create Qt Quick items to represent the different parts of the window's decorations.
+ * On the left side, for example, there should be a resize handle that the user can grab and drag in
+ * order to resize the window.
+ *
+ * \snippet qtshell/qml/Chrome.qml leftResizeHandle
+ *
+ * We simply make this a five-pixel wide rectangle in the example, anchored to the top, bottom and
+ * left side of the \c Chrome.
+ *
+ * Similarly, we add Qt Quick items that represent the right, top, bottom, top-left, top-right,
+ * bottom-left and bottom-right resize handles. We also add a title bar. When the decorations have
+ * been created and anchored correctly to the sides of the \c{Chrome}, we set corresponding
+ * properties in QtShellChrome.
+ *
+ * \snippet qtshell/qml/Chrome.qml decorations
+ *
+ * When the decoration properties are set, the default resizing and repositioning behavior will be
+ * added automatically. The user will be able to interact with the resize handles in order to resize
+ * the window, and drag the title bar to reposition it. The frame margins of the QtShellSurface will
+ * also be set automatically to account for the size of the decorations (as long as none of the
+ * frame margins properties have been set explicitly.)
+ *
+ * The visibility of the decorations will be handled automatically by the QtShellChrome based on
+ * the window flags of the QtShellSurface.
+ *
+ * \section1 Window Management
+ *
+ * As part of the decorations, it is common to have tool buttons which manage the window state
+ * and life span. In the example, these are added to the title bar.
+ *
+ * \snippet qtshell/qml/Chrome.qml buttons
+ *
+ * The visibility of each button is conditional on the window flag for that button, and when each
+ * of them is clicked, we simply call the corresponding method in QtShellChrome. The exception is
+ * the "close" button, which calls the
+ * \l{QtWaylandCompositor::QtShellSurface::sendClose()}{sendClose()} method in QtShellSurface.
+ * This instructs the client to close itself, and ensures a graceful shutdown of the application.
+ *
+ * \snippet qtshell/qml/CompositorScreen.qml taskbar
+ *
+ * As an additional window management tool, the example has a "task bar". This is just a row of
+ * tool buttons at the bottom with the window titles. The buttons can be clicked to de-minimize
+ * applications and bring them to the front if they are obscured by other windows. Similarly
+ * to the \c{Chrome}, we use a \l{Repeater} for creating the tool buttons and use the shell surface
+ * list as model for this. For simplicity, the example does not have any handling of overflow (when
+ * there are too many applications for the task bar), but in a proper compositor, this is also
+ * something that should be considered.
+ *
+ * Finally, to avoid maximized applications expanding to fill the area covered by the task bar, we
+ * create a special item to manage the parts of the WaylandOutput real estate that is available to
+ * client windows.
+ *
+ * \snippet qtshell/qml/CompositorScreen.qml usableArea
+ *
+ * It is simply anchored to the sides of the WaylandOutput, but its bottom anchor is at the top
+ * of the task bar.
+ *
+ * In the \c{Chrome}, we use this area to define the \l{QtShellChrome::maximizedRect}{maximizedRect}
+ * of the window.
+ *
+ * \snippet qtshell/qml/Chrome.qml maximizedRect
+ *
+ * By default, this property will match the full WaylandOutput. In our case, however, we do not want
+ * to include the task bar in the available area, so we override the default.
+ */
diff --git a/examples/wayland/qtshell/images/background.jpg b/examples/wayland/qtshell/images/background.jpg
new file mode 100644
index 00000000..445567fb
--- /dev/null
+++ b/examples/wayland/qtshell/images/background.jpg
Binary files differ
diff --git a/examples/wayland/qtshell/main.cpp b/examples/wayland/qtshell/main.cpp
new file mode 100644
index 00000000..1b129474
--- /dev/null
+++ b/examples/wayland/qtshell/main.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Wayland module
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QUrl>
+#include <QtCore/QDebug>
+
+#include <QtGui/QGuiApplication>
+
+#include <QtQml/QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+ // ShareOpenGLContexts is needed for using the threaded renderer
+ // on Nvidia EGLStreams
+ QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine appEngine(QUrl("qrc:///qml/main.qml"));
+
+ return app.exec();
+}
diff --git a/examples/wayland/qtshell/qml/Chrome.qml b/examples/wayland/qtshell/qml/Chrome.qml
new file mode 100644
index 00000000..ec50da27
--- /dev/null
+++ b/examples/wayland/qtshell/qml/Chrome.qml
@@ -0,0 +1,231 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Wayland module
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtWayland.Compositor
+import QtWayland.Compositor.QtShell
+
+QtShellChrome {
+ id: chrome
+
+ property alias shellSurface: shellSurfaceItemId.shellSurface
+
+ //! [maximizedRect]
+ maximizedRect: Qt.rect(usableArea.x,
+ usableArea.y,
+ usableArea.width,
+ usableArea.height)
+ //! [maximizedRect]
+
+ //! [leftResizeHandle]
+ Rectangle {
+ id: leftResizeHandle
+ color: "gray"
+ width: visible ? 5 : 0
+ anchors.topMargin: 5
+ anchors.bottomMargin: 5
+ anchors.left: parent.left
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ }
+ //! [leftResizeHandle]
+
+ Rectangle {
+ id: rightResizeHandle
+ color: "gray"
+ width: visible ? 5 : 0
+ anchors.topMargin: 5
+ anchors.bottomMargin: 5
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ }
+
+ Rectangle {
+ id: topResizeHandle
+ color: "gray"
+ height: visible ? 5 : 0
+ anchors.leftMargin: 5
+ anchors.rightMargin: 5
+ anchors.left: parent.left
+ anchors.top: parent.top
+ anchors.right: parent.right
+ }
+
+ Rectangle {
+ id: bottomResizeHandle
+ color: "gray"
+ height: visible ? 5 : 0
+ anchors.leftMargin: 5
+ anchors.rightMargin: 5
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+ }
+
+ Rectangle {
+ id: titleBar
+ anchors.top: topResizeHandle.bottom
+ anchors.left: leftResizeHandle.right
+ anchors.right: rightResizeHandle.left
+ height: visible ? xButton.height + 10 : 0
+ color: shellSurface.active ? "cornflowerblue" : "lightgray"
+
+ Text {
+ anchors.left: parent.left
+ anchors.right: rowLayout.left
+ anchors.verticalCenter: parent.verticalCenter
+
+ font.pixelSize: xButton.height
+ text: shellSurface.windowTitle
+ fontSizeMode: Text.Fit
+ }
+
+ //! [buttons]
+ RowLayout {
+ id: rowLayout
+ anchors.right: parent.right
+ anchors.rightMargin: 5
+
+ ToolButton {
+ text: "-"
+ Layout.margins: 5
+ visible: (chrome.windowFlags & Qt.WindowMinimizeButtonHint) != 0
+ onClicked: {
+ chrome.toggleMinimized()
+ }
+ }
+
+ ToolButton {
+ text: "+"
+ Layout.margins: 5
+ visible: (chrome.windowFlags & Qt.WindowMaximizeButtonHint) != 0
+ onClicked: {
+ chrome.toggleMaximized()
+ }
+ }
+
+ ToolButton {
+ id: xButton
+ text: "X"
+ Layout.margins: 5
+ visible: (chrome.windowFlags & Qt.WindowCloseButtonHint) != 0
+ onClicked: shellSurface.sendClose()
+ }
+ }
+ //! [buttons]
+ }
+
+ Rectangle {
+ id: topLeftResizeHandle
+ color: "gray"
+ height: 5
+ width: 5
+ anchors.left: parent.left
+ anchors.top: parent.top
+ }
+
+ Rectangle {
+ id: topRightResizeHandle
+ color: "gray"
+ height: 5
+ width: 5
+ anchors.right: parent.right
+ anchors.top: parent.top
+ }
+
+ Rectangle {
+ id: bottomLeftResizeHandle
+ color: "gray"
+ height: 5
+ width: 5
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ }
+
+ Rectangle {
+ id: bottomRightResizeHandle
+ color: "gray"
+ height: 5
+ width: 5
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ }
+
+ //! [decorations]
+ leftResizeHandle: leftResizeHandle
+ rightResizeHandle: rightResizeHandle
+ topResizeHandle: topResizeHandle
+ bottomResizeHandle: bottomResizeHandle
+ bottomLeftResizeHandle: bottomLeftResizeHandle
+ bottomRightResizeHandle: bottomRightResizeHandle
+ topLeftResizeHandle: topLeftResizeHandle
+ topRightResizeHandle: topRightResizeHandle
+ titleBar: titleBar
+ //! [decorations]
+
+ //! [shellsurfaceitem]
+ ShellSurfaceItem {
+ id: shellSurfaceItemId
+ anchors.top: titleBar.bottom
+ anchors.bottom: bottomResizeHandle.top
+ anchors.left: leftResizeHandle.right
+ anchors.right: rightResizeHandle.left
+
+ moveItem: chrome
+
+ staysOnBottom: shellSurface.windowFlags & Qt.WindowStaysOnBottomHint
+ staysOnTop: !staysOnBottom && shellSurface.windowFlags & Qt.WindowStaysOnTopHint
+ }
+ shellSurfaceItem: shellSurfaceItemId
+ //! [shellsurfaceitem]
+}
diff --git a/examples/wayland/qtshell/qml/CompositorScreen.qml b/examples/wayland/qtshell/qml/CompositorScreen.qml
new file mode 100644
index 00000000..f6597a41
--- /dev/null
+++ b/examples/wayland/qtshell/qml/CompositorScreen.qml
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Wayland module
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Window
+import QtQuick.Controls
+import QtWayland.Compositor
+
+WaylandOutput {
+ id: output
+
+ property bool isNestedCompositor: Qt.platform.pluginName.startsWith("wayland") || Qt.platform.pluginName === "xcb"
+
+ //! [handleShellSurface]
+ property ListModel shellSurfaces: ListModel {}
+ function handleShellSurface(shellSurface) {
+ shellSurfaces.append({shellSurface: shellSurface});
+ }
+ //! [handleShellSurface]
+
+ // During development, it can be useful to start the compositor inside X11 or
+ // another Wayland compositor. In such cases, set sizeFollowsWindow to true to
+ // enable resizing of the compositor window to be forwarded to the Wayland clients
+ // as the output (screen) changing resolution. Consider setting it to false if you
+ // are running the compositor using eglfs, linuxfb or similar QPA backends.
+ sizeFollowsWindow: output.isNestedCompositor
+
+ window: Window {
+ width: 1920
+ height: 1080
+ visible: true
+
+ WaylandMouseTracker {
+ id: mouseTracker
+
+ anchors.fill: parent
+
+ // Set this to false to disable the outer mouse cursor when running nested
+ // compositors. Otherwise you would see two mouse cursors, one for each compositor.
+ windowSystemCursorEnabled: output.isNestedCompositor
+
+ Image {
+ id: background
+
+ anchors.fill: parent
+ fillMode: Image.Tile
+ source: "qrc:/images/background.jpg"
+ smooth: true
+
+ //! [repeater]
+ Repeater {
+ id: chromeRepeater
+ model: output.shellSurfaces
+ // Chrome displays a shell surface on the screen (See Chrome.qml)
+ Chrome {
+ shellSurface: modelData
+ onClientDestroyed:
+ {
+ output.shellSurfaces.remove(index)
+ }
+ }
+ }
+ //! [repeater]
+ }
+
+ Rectangle {
+ anchors.fill: taskbar
+ color: "lavenderblush"
+ }
+
+ //! [taskbar]
+ Row {
+ id: taskbar
+ height: 40
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+
+ Repeater {
+ anchors.fill: parent
+ model: output.shellSurfaces
+
+ ToolButton {
+ anchors.verticalCenter: parent.verticalCenter
+ text: modelData.windowTitle
+ onClicked: {
+ var item = chromeRepeater.itemAt(index)
+ if ((item.windowState & Qt.WindowMinimized) != 0)
+ item.toggleMinimized()
+ chromeRepeater.itemAt(index).activate()
+ }
+ }
+ }
+ }
+ //! [taskbar]
+
+ //! [usableArea]
+ Item {
+ id: usableArea
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: taskbar.top
+ }
+ //! [usableArea]
+ }
+ }
+}
diff --git a/examples/wayland/qtshell/qml/main.qml b/examples/wayland/qtshell/qml/main.qml
new file mode 100644
index 00000000..10f02ede
--- /dev/null
+++ b/examples/wayland/qtshell/qml/main.qml
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Wayland module
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtWayland.Compositor
+import QtWayland.Compositor.QtShell
+
+WaylandCompositor {
+ id: waylandCompositor
+
+ CompositorScreen { id: screen; compositor: waylandCompositor }
+
+ // Shell surface extension. Needed to provide a window concept for Wayland clients.
+ // I.e. requests and events for maximization, minimization, resizing, closing etc.
+
+ //! [shell]
+ QtShell {
+ onQtShellSurfaceCreated: screen.handleShellSurface(qtShellSurface)
+ }
+ //! [shell]
+}
diff --git a/examples/wayland/qtshell/qtshell.pro b/examples/wayland/qtshell/qtshell.pro
new file mode 100644
index 00000000..c3f927b6
--- /dev/null
+++ b/examples/wayland/qtshell/qtshell.pro
@@ -0,0 +1,16 @@
+QT += gui qml quick quick-private
+
+SOURCES += \
+ main.cpp
+
+OTHER_FILES = \
+ qml/main.qml \
+ qml/CompositorScreen.qml \
+ qml/Chrome.qml \
+ images/background.jpg \
+
+RESOURCES += qtshell.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/wayland/qtshell
+sources.path = $$[QT_INSTALL_EXAMPLES]/wayland/qtshell
+INSTALLS += target sources
diff --git a/examples/wayland/qtshell/qtshell.qrc b/examples/wayland/qtshell/qtshell.qrc
new file mode 100644
index 00000000..7523ebaf
--- /dev/null
+++ b/examples/wayland/qtshell/qtshell.qrc
@@ -0,0 +1,8 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/background.jpg</file>
+ <file>qml/main.qml</file>
+ <file>qml/CompositorScreen.qml</file>
+ <file>qml/Chrome.qml</file>
+ </qresource>
+</RCC>
diff --git a/examples/wayland/wayland.pro b/examples/wayland/wayland.pro
index c6103623..4a4aaf81 100644
--- a/examples/wayland/wayland.pro
+++ b/examples/wayland/wayland.pro
@@ -17,9 +17,11 @@ qtHaveModule(quick) {
SUBDIRS += overview-compositor
SUBDIRS += ivi-compositor
SUBDIRS += server-side-decoration
+ SUBDIRS += qtshell
qtHaveModule(waylandclient) {
SUBDIRS += \
- custom-extension
+ custom-extension \
+ custom-shell
qtConfig(opengl) {
SUBDIRS += \
diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt
index c7264d67..696a8b3d 100644
--- a/src/client/CMakeLists.txt
+++ b/src/client/CMakeLists.txt
@@ -48,7 +48,8 @@ qt_internal_add_module(WaylandClient
qwaylandtouch.cpp qwaylandtouch_p.h
qwaylandwindow.cpp qwaylandwindow_p.h
qwaylandwindowmanagerintegration.cpp qwaylandwindowmanagerintegration_p.h
- shellintegration/qwaylandshellintegration_p.h
+ shellintegration/qwaylandclientshellapi_p.h
+ shellintegration/qwaylandshellintegration_p.h shellintegration/qwaylandshellintegration.cpp
shellintegration/qwaylandshellintegrationfactory.cpp shellintegration/qwaylandshellintegrationfactory_p.h
shellintegration/qwaylandshellintegrationplugin.cpp shellintegration/qwaylandshellintegrationplugin_p.h
INCLUDE_DIRECTORIES
diff --git a/src/client/configure.cmake b/src/client/configure.cmake
index e0d0205b..a8184fc1 100644
--- a/src/client/configure.cmake
+++ b/src/client/configure.cmake
@@ -37,6 +37,10 @@ qt_feature("wayland-client-xdg-shell" PRIVATE
LABEL "xdg-shell"
CONDITION QT_FEATURE_wayland_client
)
+qt_feature("wayland-client-qt-shell" PRIVATE
+ LABEL "qt-shell"
+ CONDITION QT_FEATURE_wayland_client
+)
qt_feature("egl-extension-platform-wayland" PRIVATE
LABEL "EGL wayland platform extension"
CONDITION QT_FEATURE_wayland_client AND QT_FEATURE_opengl AND QT_FEATURE_egl AND TEST_egl_1_5_wayland
@@ -45,4 +49,5 @@ qt_configure_add_summary_section(NAME "Qt Wayland Client Shell Integrations")
qt_configure_add_summary_entry(ARGS "wayland-client-xdg-shell")
qt_configure_add_summary_entry(ARGS "wayland-client-ivi-shell")
qt_configure_add_summary_entry(ARGS "wayland-client-wl-shell")
+qt_configure_add_summary_entry(ARGS "wayland-client-qt-shell")
qt_configure_end_summary_section() # end of "Qt Wayland Client Shell Integrations" section
diff --git a/src/client/qtwaylandclientglobal.h b/src/client/qtwaylandclientglobal.h
index 5f474f37..42f8ec8a 100644
--- a/src/client/qtwaylandclientglobal.h
+++ b/src/client/qtwaylandclientglobal.h
@@ -40,17 +40,6 @@
#ifndef QWAYLANDCLIENTGLOBAL_H
#define QWAYLANDCLIENTGLOBAL_H
-//
-// 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.
-//
-
#include <QtGui/qtguiglobal.h>
#include <QtWaylandClient/qtwaylandclient-config.h>
diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h
index 8be91118..cb345d7a 100644
--- a/src/client/qwaylanddisplay_p.h
+++ b/src/client/qwaylanddisplay_p.h
@@ -217,6 +217,7 @@ public:
bool isKeyboardAvailable() const;
void initEventThread();
+
public slots:
void blockingReadEvents();
void flushRequests();
diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp
index 73e4222f..274ae9a9 100644
--- a/src/client/qwaylandintegration.cpp
+++ b/src/client/qwaylandintegration.cpp
@@ -83,12 +83,14 @@
#include "qwaylandserverbufferintegration_p.h"
#include "qwaylandserverbufferintegrationfactory_p.h"
+#include "qwaylandshellsurface_p.h"
#include "qwaylandshellintegration_p.h"
#include "qwaylandshellintegrationfactory_p.h"
#include "qwaylandinputdeviceintegration_p.h"
#include "qwaylandinputdeviceintegrationfactory_p.h"
+#include "qwaylandwindow_p.h"
#if QT_CONFIG(accessibility_atspi_bridge)
#include <QtGui/private/qspiaccessiblebridge_p.h>
@@ -119,6 +121,19 @@ QWaylandIntegration::QWaylandIntegration()
mFailed = true;
return;
}
+
+ // ### Not ideal...
+ // We don't want to use QPlatformWindow::requestActivate here, since that gives a warning
+ // for most shells. Also, we don't want to put this into the specific shells that can use
+ // it, since we want to support more than one shell in one client.
+ // In addition, this will send a new requestActivate when the focus object changes, even if
+ // the focus window stays the same.
+ QObject::connect(qApp, &QGuiApplication::focusObjectChanged, qApp, [](){
+ QWindow *fw = QGuiApplication::focusWindow();
+ auto *w = fw ? static_cast<QWaylandWindow*>(fw->handle()) : nullptr;
+ if (w && w->shellSurface())
+ w->shellSurface()->requestActivate();
+ });
}
QWaylandIntegration::~QWaylandIntegration()
@@ -435,6 +450,7 @@ void QWaylandIntegration::initializeShellIntegration()
} else {
preferredShells << QLatin1String("xdg-shell");
preferredShells << QLatin1String("wl-shell") << QLatin1String("ivi-shell");
+ preferredShells << QLatin1String("qt-shell");
}
for (const QString &preferredShell : qAsConst(preferredShells)) {
diff --git a/src/client/qwaylandshellsurface.cpp b/src/client/qwaylandshellsurface.cpp
index 1dfdfd5e..81e05a44 100644
--- a/src/client/qwaylandshellsurface.cpp
+++ b/src/client/qwaylandshellsurface.cpp
@@ -40,6 +40,7 @@
#include "qwaylandshellsurface_p.h"
#include "qwaylandwindow_p.h"
#include "qwaylandextendedsurface_p.h"
+#include "qwaylandinputdevice_p.h"
QT_BEGIN_NAMESPACE
@@ -61,6 +62,49 @@ void QWaylandShellSurface::sendProperty(const QString &name, const QVariant &val
Q_UNUSED(value);
}
+QPlatformWindow *QWaylandShellSurface::platformWindow()
+{
+ return m_window;
+}
+
+wl_surface *QWaylandShellSurface::wlSurface()
+{
+ return m_window ? m_window->wlSurface() : nullptr;
+}
+
+void QWaylandShellSurface::resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset)
+{
+ m_window->resizeFromApplyConfigure(sizeWithMargins, offset);
+}
+
+void QWaylandShellSurface::repositionFromApplyConfigure(const QPoint &position)
+{
+ m_window->repositionFromApplyConfigure(position);
+}
+
+void QWaylandShellSurface::setGeometryFromApplyConfigure(const QPoint &globalPosition, const QSize &sizeWithMargins)
+{
+ m_window->setGeometryFromApplyConfigure(globalPosition, sizeWithMargins);
+}
+
+void QWaylandShellSurface::applyConfigureWhenPossible()
+{
+ m_window->applyConfigureWhenPossible();
+}
+
+void QWaylandShellSurface::handleActivationChanged(bool activated)
+{
+ if (activated)
+ m_window->display()->handleWindowActivated(m_window);
+ else
+ m_window->display()->handleWindowDeactivated(m_window);
+}
+
+uint32_t QWaylandShellSurface::getSerial(QWaylandInputDevice *inputDevice)
+{
+ return inputDevice->serial();
+}
+
}
QT_END_NAMESPACE
diff --git a/src/client/qwaylandshellsurface_p.h b/src/client/qwaylandshellsurface_p.h
index 080d1385..cdf1abdb 100644
--- a/src/client/qwaylandshellsurface_p.h
+++ b/src/client/qwaylandshellsurface_p.h
@@ -53,14 +53,16 @@
#include <QtCore/QSize>
#include <QObject>
-
-#include <QtWaylandClient/private/qwayland-wayland.h>
+#include <QPoint>
#include <QtWaylandClient/qtwaylandclientglobal.h>
+struct wl_surface;
+
QT_BEGIN_NAMESPACE
class QVariant;
class QWindow;
+class QPlatformWindow;
namespace QtWaylandClient {
@@ -90,15 +92,30 @@ public:
virtual void sendProperty(const QString &name, const QVariant &value);
- inline QWaylandWindow *window() { return m_window; }
-
virtual void applyConfigure() {}
virtual void requestWindowStates(Qt::WindowStates states) {Q_UNUSED(states);}
virtual bool wantsDecorations() const { return false; }
+ virtual QMargins serverSideFrameMargins() const { return QMargins(); }
virtual void propagateSizeHints() {}
virtual void setWindowGeometry(const QRect &rect) { Q_UNUSED(rect); }
+ virtual void setWindowPosition(const QPoint &position) { Q_UNUSED(position); }
+
+ virtual bool requestActivate() { return false; }
+
+ inline QWaylandWindow *window() { return m_window; }
+ QPlatformWindow *platformWindow();
+ struct wl_surface *wlSurface();
+
+protected:
+ void resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset = {0, 0});
+ void repositionFromApplyConfigure(const QPoint &position);
+ void setGeometryFromApplyConfigure(const QPoint &globalPosition, const QSize &sizeWithMargins);
+ void applyConfigureWhenPossible();
+ void handleActivationChanged(bool activated);
+
+ static uint32_t getSerial(QWaylandInputDevice *inputDevice);
private:
QWaylandWindow *m_window = nullptr;
diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp
index 0cc18552..635c92a0 100644
--- a/src/client/qwaylandwindow.cpp
+++ b/src/client/qwaylandwindow.cpp
@@ -342,7 +342,7 @@ void QWaylandWindow::setGeometry_helper(const QRect &rect)
qBound(minimum.height(), rect.height(), maximum.height())));
if (mSubSurfaceWindow) {
- QMargins m = QPlatformWindow::parent()->frameMargins();
+ QMargins m = static_cast<QWaylandWindow *>(QPlatformWindow::parent())->clientSideMargins();
mSubSurfaceWindow->set_position(rect.x() + m.left(), rect.y() + m.top());
QWaylandWindow *parentWindow = mSubSurfaceWindow->parent();
@@ -372,16 +372,45 @@ void QWaylandWindow::setGeometry(const QRect &rect)
if (isExposed() && !mInResizeFromApplyConfigure && exposeGeometry != mLastExposeGeometry)
sendExposeEvent(exposeGeometry);
- if (mShellSurface && isExposed())
+ if (mShellSurface && isExposed()) {
mShellSurface->setWindowGeometry(windowContentGeometry());
+ if (!qt_window_private(window())->positionAutomatic)
+ mShellSurface->setWindowPosition(windowGeometry().topLeft());
+ }
if (isOpaque() && mMask.isEmpty())
setOpaqueArea(rect);
}
+void QWaylandWindow::setGeometryFromApplyConfigure(const QPoint &globalPosition, const QSize &sizeWithMargins)
+{
+ QMargins margins = clientSideMargins();
+
+ QPoint positionWithoutMargins = globalPosition + QPoint(margins.left(), margins.top());
+ int widthWithoutMargins = qMax(sizeWithMargins.width() - (margins.left() + margins.right()), 1);
+ int heightWithoutMargins = qMax(sizeWithMargins.height() - (margins.top() + margins.bottom()), 1);
+
+ QRect geometry(positionWithoutMargins, QSize(widthWithoutMargins, heightWithoutMargins));
+
+ mInResizeFromApplyConfigure = true;
+ setGeometry(geometry);
+ mInResizeFromApplyConfigure = false;
+}
+
+void QWaylandWindow::repositionFromApplyConfigure(const QPoint &globalPosition)
+{
+ QMargins margins = clientSideMargins();
+ QPoint positionWithoutMargins = globalPosition + QPoint(margins.left(), margins.top());
+
+ QRect geometry(positionWithoutMargins, windowGeometry().size());
+ mInResizeFromApplyConfigure = true;
+ setGeometry(geometry);
+ mInResizeFromApplyConfigure = false;
+}
+
void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset)
{
- QMargins margins = frameMargins();
+ QMargins margins = clientSideMargins();
// Exclude shadows from margins once they are excluded from window geometry
// 1) First resizeFromApplyConfigure() call will have sizeWithMargins equal to surfaceSize()
@@ -394,7 +423,8 @@ void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, cons
int widthWithoutMargins = qMax(sizeWithMargins.width() - (margins.left() + margins.right()), 1);
int heightWithoutMargins = qMax(sizeWithMargins.height() - (margins.top() + margins.bottom()), 1);
- QRect geometry(windowGeometry().topLeft(), QSize(widthWithoutMargins, heightWithoutMargins));
+ QRect geometry(windowGeometry().topLeft() + QPoint(margins.left(), margins.top()),
+ QSize(widthWithoutMargins, heightWithoutMargins));
mOffset += offset;
mInResizeFromApplyConfigure = true;
@@ -411,7 +441,6 @@ void QWaylandWindow::sendExposeEvent(const QRect &rect)
mLastExposeGeometry = rect;
}
-
static QList<QPointer<QWaylandWindow>> activePopups;
void QWaylandWindow::closePopups(QWaylandWindow *parent)
@@ -719,7 +748,15 @@ QMargins QWaylandWindow::frameMargins() const
{
if (mWindowDecoration)
return mWindowDecoration->margins();
- return QPlatformWindow::frameMargins();
+ else if (mShellSurface)
+ return mShellSurface->serverSideFrameMargins();
+ else
+ return QPlatformWindow::frameMargins();
+}
+
+QMargins QWaylandWindow::clientSideMargins() const
+{
+ return mWindowDecoration ? mWindowDecoration->margins() : QMargins{};
}
/*!
@@ -727,7 +764,7 @@ QMargins QWaylandWindow::frameMargins() const
*/
QSize QWaylandWindow::surfaceSize() const
{
- return geometry().marginsAdded(frameMargins()).size();
+ return geometry().marginsAdded(clientSideMargins()).size();
}
/*!
@@ -753,7 +790,7 @@ QRect QWaylandWindow::windowContentGeometry() const
*/
QPointF QWaylandWindow::mapFromWlSurface(const QPointF &surfacePosition) const
{
- const QMargins margins = frameMargins();
+ const QMargins margins = clientSideMargins();
return QPointF(surfacePosition.x() - margins.left(), surfacePosition.y() - margins.top());
}
@@ -980,7 +1017,7 @@ void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylan
#if QT_CONFIG(cursor)
if (e.type == QEvent::Enter) {
- QRect contentGeometry = windowContentGeometry().marginsRemoved(frameMargins());
+ QRect contentGeometry = windowContentGeometry().marginsRemoved(clientSideMargins());
if (contentGeometry.contains(e.local.toPoint()))
restoreMouseCursor(inputDevice);
}
@@ -1213,7 +1250,8 @@ void QWaylandWindow::restoreMouseCursor(QWaylandInputDevice *device)
void QWaylandWindow::requestActivateWindow()
{
- qCWarning(lcQpaWayland) << "Wayland does not support QWindow::requestActivate()";
+ if (mShellSurface == nullptr || !mShellSurface->requestActivate())
+ qCWarning(lcQpaWayland) << "Wayland does not support QWindow::requestActivate()";
}
bool QWaylandWindow::isExposed() const
@@ -1448,7 +1486,7 @@ bool QWaylandWindow::isOpaque() const
void QWaylandWindow::setOpaqueArea(const QRegion &opaqueArea)
{
- const QRegion translatedOpaqueArea = opaqueArea.translated(frameMargins().left(), frameMargins().top());
+ const QRegion translatedOpaqueArea = opaqueArea.translated(clientSideMargins().left(), clientSideMargins().top());
if (translatedOpaqueArea == mOpaqueArea || !mSurface)
return;
diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h
index 17ed9fdc..f2dbe337 100644
--- a/src/client/qwaylandwindow_p.h
+++ b/src/client/qwaylandwindow_p.h
@@ -123,6 +123,8 @@ public:
void setGeometry(const QRect &rect) override;
void resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset = {0, 0});
+ void repositionFromApplyConfigure(const QPoint &position);
+ void setGeometryFromApplyConfigure(const QPoint &globalPosition, const QSize &sizeWithMargins);
void applyConfigureWhenPossible(); //rename to possible?
@@ -239,6 +241,7 @@ protected:
virtual void doHandleFrameCallback();
virtual QRect defaultGeometry() const;
void sendExposeEvent(const QRect &rect);
+ QMargins clientSideMargins() const;
QWaylandDisplay *mDisplay = nullptr;
QScopedPointer<QWaylandSurface> mSurface;
diff --git a/src/client/shellintegration/qwaylandclientshellapi_p.h b/src/client/shellintegration/qwaylandclientshellapi_p.h
new file mode 100644
index 00000000..a8863e7d
--- /dev/null
+++ b/src/client/shellintegration/qwaylandclientshellapi_p.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandClient module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDCLIENTSHELLAPI_P_H
+#define QWAYLANDCLIENTSHELLAPI_P_H
+
+//
+// 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.
+//
+
+// N O T E
+// -------
+// This file provides a supported API for creating client-side shell
+// extensions. Source compatibility will be preserved, but we may break
+// forward and backward binary compatibility, even in patch releases.
+//
+// The supported API contains these classes:
+//
+// QtWaylandClient::QWaylandShellSurface
+// QtWaylandClient::QWaylandShellIntegration
+// QtWaylandClient::QWaylandShellIntegrationPlugin
+
+#include "QtWaylandClient/private/qwaylandshellsurface_p.h"
+#include "QtWaylandClient/private/qwaylandshellintegration_p.h"
+#include "QtWaylandClient/private/qwaylandshellintegrationplugin_p.h"
+
+#endif // QWAYLANDCLIENTSHELLAPI_P_H
diff --git a/src/client/shellintegration/qwaylandshellintegration.cpp b/src/client/shellintegration/qwaylandshellintegration.cpp
new file mode 100644
index 00000000..7bc37792
--- /dev/null
+++ b/src/client/shellintegration/qwaylandshellintegration.cpp
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandClient module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qwaylandshellintegration_p.h"
+#include <QtWaylandClient/private/qwaylanddisplay_p.h>
+#include <QtWaylandClient/private/qwaylandwindow_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+wl_surface *QWaylandShellIntegration::wlSurfaceForWindow(QWaylandWindow *window)
+{
+ return window->wlSurface();
+}
+
+bool QWaylandShellIntegration::findGlobal(const QString &interface, wl_registry **registry, uint32_t *id, uint32_t *version)
+{
+ for (QWaylandDisplay::RegistryGlobal &global : m_display->globals()) {
+ if (global.interface == interface) {
+ *registry = m_display->wl_registry();
+ *id = global.id;
+ *version = global.version;
+ return true;
+ }
+ }
+ return false;
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/client/shellintegration/qwaylandshellintegration_p.h b/src/client/shellintegration/qwaylandshellintegration_p.h
index b308ffe2..4de19e78 100644
--- a/src/client/shellintegration/qwaylandshellintegration_p.h
+++ b/src/client/shellintegration/qwaylandshellintegration_p.h
@@ -52,10 +52,19 @@
//
#include <QtWaylandClient/qtwaylandclientglobal.h>
-#include <QtWaylandClient/private/qwaylanddisplay_p.h>
+#include <QtWaylandClient/qwaylandclientextension.h>
+
+
+
+#include <QDebug>
+
+struct wl_surface;
+struct wl_registry;
QT_BEGIN_NAMESPACE
+class QWindow;
+
namespace QtWaylandClient {
class QWaylandWindow;
@@ -68,9 +77,12 @@ public:
QWaylandShellIntegration() {}
virtual ~QWaylandShellIntegration() {}
- virtual bool initialize(QWaylandDisplay *display) {
+ bool initialize(QWaylandDisplay *display) {
m_display = display;
- return true;
+ return initialize();
+ }
+ virtual bool initialize() {
+ return false;
}
virtual QWaylandShellSurface *createShellSurface(QWaylandWindow *window) = 0;
virtual void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) {
@@ -79,10 +91,49 @@ public:
return nullptr;
}
+ static wl_surface *wlSurfaceForWindow(QWaylandWindow *window);
+ bool findGlobal(const QString &interface, wl_registry **registry, uint32_t *id, uint32_t *version);
+
protected:
QWaylandDisplay *m_display = nullptr;
};
+template <typename T>
+class Q_WAYLAND_CLIENT_EXPORT QWaylandShellIntegrationTemplate : public QWaylandShellIntegration, public QWaylandClientExtension
+{
+public:
+ QWaylandShellIntegrationTemplate(const int ver) :
+ QWaylandClientExtension(ver)
+ {
+ }
+
+ bool initialize() override
+ {
+ QWaylandClientExtension::initialize();
+ return isActive();
+ }
+
+ const struct wl_interface *extensionInterface() const override
+ {
+ return T::interface();
+ }
+
+ void bind(struct ::wl_registry *registry, int id, int ver) override
+ {
+ T* instance = static_cast<T *>(this);
+ // Make sure lowest version is used of the supplied version from the
+ // developer and the version specified in the protocol and also the
+ // compositor version.
+ if (this->version() > T::interface()->version) {
+ qWarning("Supplied protocol version to QWaylandClientExtensionTemplate is higher than the version of the protocol, using protocol version instead.");
+ }
+ int minVersion = qMin(ver, qMin(T::interface()->version, this->version()));
+ setVersion(minVersion);
+ instance->init(registry, id, minVersion);
+ }
+};
+
+
}
QT_END_NAMESPACE
diff --git a/src/compositor/compositor_api/qwaylandquickitem.cpp b/src/compositor/compositor_api/qwaylandquickitem.cpp
index 9796b72a..095c6e3b 100644
--- a/src/compositor/compositor_api/qwaylandquickitem.cpp
+++ b/src/compositor/compositor_api/qwaylandquickitem.cpp
@@ -1606,20 +1606,34 @@ void QWaylandQuickItem::setInputEventsEnabled(bool enabled)
void QWaylandQuickItem::lower()
{
- QQuickItem *parent = parentItem();
+ Q_D(QWaylandQuickItem);
+ d->lower();
+}
+
+void QWaylandQuickItemPrivate::lower()
+{
+ Q_Q(QWaylandQuickItem);
+ QQuickItem *parent = q->parentItem();
Q_ASSERT(parent);
- QQuickItem *bottom = parent->childItems().first();
- if (this != bottom)
- stackBefore(bottom);
+ QQuickItem *bottom = parent->childItems().constFirst();
+ if (q != bottom)
+ q->stackBefore(bottom);
}
void QWaylandQuickItem::raise()
{
- QQuickItem *parent = parentItem();
+ Q_D(QWaylandQuickItem);
+ d->raise();
+}
+
+void QWaylandQuickItemPrivate::raise()
+{
+ Q_Q(QWaylandQuickItem);
+ QQuickItem *parent = q->parentItem();
Q_ASSERT(parent);
- QQuickItem *top = parent->childItems().last();
- if (this != top)
- stackAfter(top);
+ QQuickItem *top = parent->childItems().constLast();
+ if (q != top)
+ q->stackAfter(top);
}
void QWaylandQuickItem::sendMouseMoveEvent(const QPointF &position, QWaylandSeat *seat)
diff --git a/src/compositor/compositor_api/qwaylandquickitem_p.h b/src/compositor/compositor_api/qwaylandquickitem_p.h
index 09b9f678..13892204 100644
--- a/src/compositor/compositor_api/qwaylandquickitem_p.h
+++ b/src/compositor/compositor_api/qwaylandquickitem_p.h
@@ -153,6 +153,9 @@ public:
void placeAboveParent();
void placeBelowParent();
+ virtual void raise();
+ virtual void lower();
+
static QMutex *mutex;
QScopedPointer<QWaylandView> view;
diff --git a/src/compositor/compositor_api/qwaylandresource.cpp b/src/compositor/compositor_api/qwaylandresource.cpp
index 585b238c..612589eb 100644
--- a/src/compositor/compositor_api/qwaylandresource.cpp
+++ b/src/compositor/compositor_api/qwaylandresource.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2017 Klarälvdalens Datakonsult AB (KDAB).
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
@@ -31,13 +32,37 @@
QT_BEGIN_NAMESPACE
+/*!
+ * \class QWaylandResource
+ * \inmodule QtWaylandCompositor
+ * \since 5.8
+ * \brief QWaylandResource is a container for a \c wl_resource.
+ *
+ * The QWaylandResource is a simple wrapper around the Wayland type \c wl_resource, and makes it
+ * possible to use wl_resource pointers in Qt Quick APIs.
+ *
+ * \sa {Qt Wayland Compositor Examples - Custom Shell}
+ */
+
+/*!
+ * Constructs an invalid QWaylandResource. The \l{resource()} accessor will return null.
+ */
QWaylandResource::QWaylandResource()
{
}
+/*!
+ * Constructs a QWaylandResource which contains \a resource.
+ */
QWaylandResource::QWaylandResource(wl_resource *resource)
: m_resource(resource)
{
}
+/*!
+ * \fn wl_resource *QWaylandResource::resource() const
+ *
+ * \return the wl_resource pointer held by this QWaylandResource.
+ */
+
QT_END_NAMESPACE
diff --git a/src/compositor/compositor_api/qwaylandsurface.cpp b/src/compositor/compositor_api/qwaylandsurface.cpp
index fc10be11..b07a93e9 100644
--- a/src/compositor/compositor_api/qwaylandsurface.cpp
+++ b/src/compositor/compositor_api/qwaylandsurface.cpp
@@ -362,6 +362,52 @@ QtWayland::ClientBuffer *QWaylandSurfacePrivate::getBuffer(struct ::wl_resource
}
/*!
+ * \class QWaylandSurfaceRole
+ * \inmodule QtWaylandCompositor
+ * \since 5.8
+ * \brief The QWaylandSurfaceRole class represents the role of the surface in context of wl_surface.
+ *
+ * QWaylandSurfaceRole is used to represent the role of a QWaylandSurface. According to the protocol
+ * specification, the role of a surface is permanent once set, and if the same surface is later
+ * reused for a different role, this constitutes a protocol error. Setting the surface to the same
+ * role multiple times is not an error.
+ *
+ * As an example, the QWaylandXdgShell can assign either "popup" or "toplevel" roles to surfaces.
+ * If \c get_toplevel is requested on a surface which has previously received a \c get_popup
+ * request, then the compositor will issue a protocol error.
+ *
+ * Roles are compared by pointer value, so any two objects of QWaylandSurfaceRole will be considered
+ * different roles, regardless of what their \l{name()}{names} are. A typical way of assigning a
+ * role is to have a static QWaylandSurfaceRole object to represent it.
+ *
+ * \code
+ * class MyShellSurfaceSubType
+ * {
+ * static QWaylandSurfaceRole s_role;
+ * // ...
+ * };
+ *
+ * // ...
+ *
+ * surface->setRole(&MyShellSurfaceSubType::s_role, resource->handle, MY_ERROR_CODE);
+ * \endcode
+ */
+
+/*!
+ * \fn QWaylandSurfaceRole::QWaylandSurfaceRole(const QByteArray &name)
+ *
+ * Creates a QWaylandSurfaceRole and assigns it \a name. The name is used in error messages
+ * involving this QWaylandSurfaceRole.
+ */
+
+/*!
+ * \fn const QByteArray QWaylandSurfaceRole::name()
+ *
+ * Returns the name of the QWaylandSurfaceRole. The name is used in error messages involving this
+ * QWaylandSurfaceRole, for example if an attempt is made to change the role of a surface.
+ */
+
+/*!
* \qmltype WaylandSurface
* \instantiates QWaylandSurface
* \inqmlmodule QtWayland.Compositor
@@ -912,10 +958,19 @@ struct wl_resource *QWaylandSurface::resource() const
}
/*!
- * Sets a \a role on the surface. A role defines how a surface will be mapped on screen; without a role
- * a surface is supposed to be hidden. Only one role can be set on a surface, at all times. Although
- * setting the same role many times is allowed, attempting to change the role of a surface will trigger
- * a protocol error to the \a errorResource and send an \a errorCode to the client.
+ * Sets a \a role on the surface. A role defines how a surface will be mapped on screen; without a
+ * role a surface is supposed to be hidden. Once a role is assigned to a surface, this becomes its
+ * permanent role. Any subsequent call to \c setRole() with a different role will trigger a
+ * protocol error to the \a errorResource and send an \a errorCode to the client. Enforcing this
+ * requirement is the main purpose of the surface role.
+ *
+ * The \a role is compared by pointer value. Any two objects of QWaylandSurfaceRole will be
+ * considered different roles, regardless of their names.
+ *
+ * The surface role is set internally by protocol implementations when a surface is adopted for a
+ * specific purpose, for example in a \l{Shell Extensions - Qt Wayland Compositor}{shell extension}.
+ * Unless you are developing extensions which use surfaces in this way, you should not call this
+ * function.
*
* Returns true if a role can be assigned; false otherwise.
*/
diff --git a/src/compositor/doc/qtwaylandcompositor.qdocconf b/src/compositor/doc/qtwaylandcompositor.qdocconf
index 9e49887e..817846d3 100644
--- a/src/compositor/doc/qtwaylandcompositor.qdocconf
+++ b/src/compositor/doc/qtwaylandcompositor.qdocconf
@@ -31,8 +31,12 @@ qhp.QtWaylandCompositor.subprojects.examples.sortPages = true
depends += qtcore qtqml qtquick qtdoc qtquickcontrols qmake qtgui qtqmlmodels qtwidgets qtvirtualkeyboard
exampledirs += ../../../examples/wayland
-headerdirs += ..
-sourcedirs += ..
+headerdirs += \
+ ../ \
+ ../../imports/
+sourcedirs += \
+ ../ \
+ ../../imports/
imagedirs += images
examplesinstallpath = wayland
diff --git a/src/compositor/doc/src/qtwaylandcompositor-shellextensions.qdoc b/src/compositor/doc/src/qtwaylandcompositor-shellextensions.qdoc
index 418df9f9..5feee55d 100644
--- a/src/compositor/doc/src/qtwaylandcompositor-shellextensions.qdoc
+++ b/src/compositor/doc/src/qtwaylandcompositor-shellextensions.qdoc
@@ -57,6 +57,10 @@
applications can run, often with pre-assigned screen real estate. For some more details
on this protocol, see the
\l{Qt Wayland Compositor Examples - IVI Compositor}{IVI Compositor example}.
+ \li \l QtShell is a specialized shell for Qt applications which supports the window management
+ features available in Qt. It may be suitable on a platform where both the compositor and
+ client applications are written with Qt, and where applications are trusted not to abuse
+ features such as manual window positioning and "bring-to-front".
\li \l WlShell is mostly useful for compatibility with third-party applications. This is also
a desktop-style shell, but it has been deprecated and replaced by \l XdgShell.
\endlist
diff --git a/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp b/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp
index 8928d803..4b481d0d 100644
--- a/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp
+++ b/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp
@@ -313,4 +313,127 @@ void QWaylandQuickShellEventFilter::timerEvent(QTimerEvent *event)
}
}
+static QWaylandQuickShellSurfaceItem *findSurfaceItemFromMoveItem(QQuickItem *moveItem)
+{
+ if (Q_UNLIKELY(!moveItem))
+ return nullptr;
+ if (auto *surf = qobject_cast<QWaylandQuickShellSurfaceItem *>(moveItem))
+ return surf;
+ for (auto *item : moveItem->childItems()) {
+ if (auto *surf = findSurfaceItemFromMoveItem(item))
+ return surf;
+ }
+ return nullptr;
+}
+
+/*
+ To raise a surface, find the topmost suitable surface and place above that.
+ We start from the top and:
+ If we don't have staysOnTop, skip all surfaces with staysOnTop
+ If we have staysOnBottom, skip all surfaces that don't have staysOnBottom
+ */
+void QWaylandQuickShellSurfaceItemPrivate::raise()
+{
+ Q_Q(QWaylandQuickShellSurfaceItem);
+ auto *moveItem = q->moveItem();
+ QQuickItem *parent = moveItem->parentItem();
+ if (!parent)
+ return;
+ auto it = parent->childItems().crbegin();
+ auto skip = [this](QQuickItem *item) {
+ if (auto *surf = findSurfaceItemFromMoveItem(item))
+ return (!staysOnTop && surf->staysOnTop()) || (staysOnBottom && !surf->staysOnBottom());
+ return true; // ignore any other Quick items that may be there
+ };
+ while (skip(*it))
+ ++it;
+ QQuickItem *top = *it;
+ if (moveItem != top)
+ moveItem->stackAfter(top);
+}
+
+/*
+ To lower a surface, find the lowest suitable surface and place below that.
+ We start from the bottom and:
+ If we don't have staysOnBottom, skip all surfaces with staysOnBottom
+ If we have staysOnTop, skip all surfaces that don't have staysOnTop
+ */
+void QWaylandQuickShellSurfaceItemPrivate::lower()
+{
+ Q_Q(QWaylandQuickShellSurfaceItem);
+ auto *moveItem = q->moveItem();
+ QQuickItem *parent = moveItem->parentItem();
+ if (!parent)
+ return;
+ auto it = parent->childItems().cbegin();
+
+ auto skip = [this](QQuickItem *item) {
+ if (auto *surf = findSurfaceItemFromMoveItem(item))
+ return (!staysOnBottom && surf->staysOnBottom()) || (staysOnTop && !surf->staysOnTop());
+ return true; // ignore any other Quick items that may be there
+ };
+ while (skip(*it))
+ ++it;
+
+ QQuickItem *bottom = *it;
+ if (moveItem != bottom)
+ moveItem->stackBefore(bottom);
+}
+
+/*!
+ * \property QWaylandQuickShellSurfaceItem::staysOnTop
+ *
+ * Keep this item above other Wayland surfaces
+ */
+bool QWaylandQuickShellSurfaceItem::staysOnTop() const
+{
+ Q_D(const QWaylandQuickShellSurfaceItem);
+ return d->staysOnTop;
+}
+
+void QWaylandQuickShellSurfaceItem::setStaysOnTop(bool onTop)
+{
+ Q_D(QWaylandQuickShellSurfaceItem);
+ if (d->staysOnTop == onTop)
+ return;
+ d->staysOnTop = onTop;
+ if (d->staysOnBottom) {
+ d->staysOnBottom = false;
+ emit staysOnBottomChanged();
+ }
+ // We need to call raise() even if onTop is false, since we need to stack under any other
+ // staysOnTop surfaces in that case
+ raise();
+ emit staysOnTopChanged();
+ Q_ASSERT(!(d->staysOnTop && d->staysOnBottom));
+}
+
+/*!
+ * \property QWaylandQuickShellSurfaceItem::staysOnBottom
+ *
+ * Keep this item above other Wayland surfaces
+ */
+bool QWaylandQuickShellSurfaceItem::staysOnBottom() const
+{
+ Q_D(const QWaylandQuickShellSurfaceItem);
+ return d->staysOnBottom;
+}
+
+void QWaylandQuickShellSurfaceItem::setStaysOnBottom(bool onBottom)
+{
+ Q_D(QWaylandQuickShellSurfaceItem);
+ if (d->staysOnBottom == onBottom)
+ return;
+ d->staysOnBottom = onBottom;
+ if (d->staysOnTop) {
+ d->staysOnTop = false;
+ emit staysOnTopChanged();
+ }
+ // We need to call lower() even if onBottom is false, since we need to stack over any other
+ // staysOnBottom surfaces in that case
+ lower();
+ emit staysOnBottomChanged();
+ Q_ASSERT(!(d->staysOnTop && d->staysOnBottom));
+}
+
QT_END_NAMESPACE
diff --git a/src/compositor/extensions/qwaylandquickshellsurfaceitem.h b/src/compositor/extensions/qwaylandquickshellsurfaceitem.h
index 9307909e..d2cc311a 100644
--- a/src/compositor/extensions/qwaylandquickshellsurfaceitem.h
+++ b/src/compositor/extensions/qwaylandquickshellsurfaceitem.h
@@ -45,6 +45,8 @@ class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandQuickShellSurfaceItem : public QWaylan
Q_PROPERTY(QWaylandShellSurface *shellSurface READ shellSurface WRITE setShellSurface NOTIFY shellSurfaceChanged)
Q_PROPERTY(QQuickItem *moveItem READ moveItem WRITE setMoveItem NOTIFY moveItemChanged)
Q_PROPERTY(bool autoCreatePopupItems READ autoCreatePopupItems WRITE setAutoCreatePopupItems NOTIFY autoCreatePopupItemsChanged)
+ Q_PROPERTY(bool staysOnTop READ staysOnTop WRITE setStaysOnTop NOTIFY staysOnTopChanged)
+ Q_PROPERTY(bool staysOnBottom READ staysOnBottom WRITE setStaysOnBottom NOTIFY staysOnBottomChanged)
Q_MOC_INCLUDE("qwaylandshellsurface.h")
QML_NAMED_ELEMENT(ShellSurfaceItem)
QML_ADDED_IN_VERSION(1, 0)
@@ -61,10 +63,17 @@ public:
bool autoCreatePopupItems();
void setAutoCreatePopupItems(bool enabled);
+ bool staysOnTop() const;
+ void setStaysOnTop(bool on);
+ bool staysOnBottom() const;
+ void setStaysOnBottom(bool on);
+
Q_SIGNALS:
void shellSurfaceChanged();
void moveItemChanged();
void autoCreatePopupItemsChanged();
+ void staysOnTopChanged();
+ void staysOnBottomChanged();
protected:
QWaylandQuickShellSurfaceItem(QWaylandQuickShellSurfaceItemPrivate &dd, QQuickItem *parent);
diff --git a/src/compositor/extensions/qwaylandquickshellsurfaceitem_p.h b/src/compositor/extensions/qwaylandquickshellsurfaceitem_p.h
index 24f38160..6fd96d16 100644
--- a/src/compositor/extensions/qwaylandquickshellsurfaceitem_p.h
+++ b/src/compositor/extensions/qwaylandquickshellsurfaceitem_p.h
@@ -61,10 +61,15 @@ public:
QWaylandQuickShellSurfaceItem *maybeCreateAutoPopup(QWaylandShellSurface* shellSurface);
static QWaylandQuickShellSurfaceItemPrivate *get(QWaylandQuickShellSurfaceItem *item) { return item->d_func(); }
+ void raise() override;
+ void lower() override;
+
QWaylandQuickShellIntegration *m_shellIntegration = nullptr;
QWaylandShellSurface *m_shellSurface = nullptr;
QQuickItem *m_moveItem = nullptr;
bool m_autoCreatePopupItems = true;
+ bool staysOnTop = false;
+ bool staysOnBottom = false;
};
class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandQuickShellEventFilter : public QObject
diff --git a/src/compositor/extensions/qwaylandshellsurface.cpp b/src/compositor/extensions/qwaylandshellsurface.cpp
index d7f0c401..067e2f80 100644
--- a/src/compositor/extensions/qwaylandshellsurface.cpp
+++ b/src/compositor/extensions/qwaylandshellsurface.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the config.tests of the Qt Toolkit.
@@ -30,6 +30,41 @@
#include <QtWaylandCompositor/QWaylandShellSurface>
/*!
+ * \class QWaylandShellSurfaceTemplate
+ * \inmodule QtWaylandCompositor
+ * \since 5.8
+ * \brief QWaylandShellSurfaceTemplate is a convenience class for creating custom shell surface
+ * classes.
+ *
+ * QWaylandShellSurfaceTemplate is a template class which inherits from QWaylandShellSurface and
+ * is convenience for building custom shell extensions.
+ *
+ * It provides the connection between Qt Wayland Compositor and the class generated by
+ * \c qtwaylandscanner, based on the XML description of the extension protocol.
+ *
+ * It provides two specific pieces of convenience:
+ * \list
+ * \li A reimplementation of \l{QWaylandCompositorExtension::extensionInterface()} which returns
+ * the \c wl_interface pointer for the qtwaylandscanner-generated base class.
+ * \li A static \l{findIn()} function which searches for an instance of the extension in a
+ * provided container, and returns this if it is found.
+ * \endlist
+ *
+ * The same usage pattern applies as for QWaylandCompositorExtensionTemplate.
+ *
+ * \sa {Qt Wayland Compositor Examples - Custom Shell}
+ */
+
+/*!
+ * \fn template <typename T> T *QWaylandShellSurfaceTemplate<T>::findIn(QWaylandObject *container)
+ *
+ * If any instance of the interface has been registered with \a container, this is returned.
+ * Otherwise null is returned. The look-up is based on the generated \c interfaceName() which
+ * matches the interface name in the protocol description.
+ */
+
+
+/*!
* \qmltype ShellSurface
* \instantiates QWaylandShellSurface
* \inqmlmodule QtWayland.Compositor
@@ -39,7 +74,7 @@
* This interface represents a Wayland surface role given by a Wayland protocol extension that
* defines how the WaylandSurface should map onto the screen.
*
- * Note: Even though this type contains a very limited API, the properties and signals of the
+ * \note Even though this type contains a very limited API, the properties and signals of the
* implementations are named consistently. For example, if you're only using desktop shell
* extensions in your compositor, it's safe to access properties such as title, maximized, etc.
* directly on the ShellSurface. See the various implementations for additional properties and
@@ -57,7 +92,7 @@
* This interface represents a Wayland surface role given by a Wayland protocol extension that
* defines how the QWaylandSurface should map onto the screen.
*
- * \sa QWaylandSurface, QWaylandWlShellSurface, QWaylandIviSurface
+ * \sa QWaylandSurface, QWaylandWlShellSurface, QWaylandIviSurface, QWaylandShellSurfaceTemplate
*/
#if QT_CONFIG(wayland_compositor_quick)
diff --git a/src/compositor/extensions/qwaylandshellsurface.h b/src/compositor/extensions/qwaylandshellsurface.h
index 8dd7da9c..f1814df3 100644
--- a/src/compositor/extensions/qwaylandshellsurface.h
+++ b/src/compositor/extensions/qwaylandshellsurface.h
@@ -64,7 +64,7 @@ template <typename T>
class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandShellSurfaceTemplate : public QWaylandShellSurface
{
public:
- QWaylandShellSurfaceTemplate(QWaylandObject *container)
+ QWaylandShellSurfaceTemplate(QWaylandObject *container = nullptr)
: QWaylandShellSurface(container)
{ }
diff --git a/src/compositor/global/qwaylandcompositorextension.cpp b/src/compositor/global/qwaylandcompositorextension.cpp
index 6fc66513..5092cdfa 100644
--- a/src/compositor/global/qwaylandcompositorextension.cpp
+++ b/src/compositor/global/qwaylandcompositorextension.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
@@ -27,7 +27,6 @@
**
****************************************************************************/
-
#include "qwaylandcompositorextension.h"
#include "qwaylandcompositorextension_p.h"
@@ -38,11 +37,85 @@
QT_BEGIN_NAMESPACE
+/*!
+ * \class QWaylandCompositorExtensionTemplate
+ * \inmodule QtWaylandCompositor
+ * \since 5.8
+ * \brief QWaylandCompositorExtensionTemplate is a convenience class for subclassing
+ * QWaylandCompositorExtension.
+ *
+ * QWaylandCompositorExtensionTemplate is a template class which inherits
+ * QWaylandCompositorExtension and is convenience for building custom Wayland extensions with Qt.
+ *
+ * It provides the connection between Qt Wayland Compositor and the class generated by
+ * \c qtwaylandscanner, based on the XML description of the extension protocol.
+ *
+ * It provides two specific pieces of convenience:
+ * \list
+ * \li A reimplementation of \l{QWaylandCompositorExtension::extensionInterface()} which returns
+ * the \c wl_interface pointer for the qtwaylandscanner-generated base class.
+ * \li A static \l{findIn()} function which searches for an instance of the extension in a
+ * provided container, and returns this if it is found.
+ * \endlist
+ *
+ * Typically, a new extension will dual-inherit QWaylandCompositorExtensionTemplate and the class
+ * generated by \c qtwaylandscanner.
+ *
+ * QWaylandCompositorExtensionTemplate should be parameterized with the subclass itself:
+ * \code
+ * class MyExtension
+ * : public QWaylandCompositorExtensionTemplate<MyExtension>
+ * , QtWaylandServer::my_extension
+ * \endcode
+ *
+ * In this example, \c MyExtension is an implementation of the generated interface \c my_extension.
+ *
+ * \sa {Qt Wayland Compositor Examples - Custom Shell}
+ */
+
+/*!
+ * \fn template <typename T> T *QWaylandCompositorExtensionTemplate<T>::findIn(QWaylandObject *container)
+ *
+ * If any instance of the interface has been registered with \a container, this is returned.
+ * Otherwise null is returned. The look-up is based on the generated \c interfaceName() which
+ * matches the interface name in the protocol description.
+ */
+
+/*!
+ * \class QWaylandCompositorExtension
+ * \inmodule QtWaylandCompositor
+ * \since 5.8
+ * \brief QWaylandCompositorExtension is the base class for compositor extensions.
+ *
+ * QWaylandCompositorExtension is the base class for implementing Wayland extensions on the
+ * compositor-side of the connection. If no other extension container is explicitly set, it will
+ * automatically add itself to its parent object, granted that this inherits from QWaylandObject.
+ *
+ * For example, for registering global extensions, you can inherit from QWaylandCompositorExtension
+ * and pass the QWaylandCompositor object as extension container.
+ *
+ * \sa QWaylandCompositorExtensionTemplate, {Qt Wayland Compositor Examples - Custom Shell}
+ */
+
+/*!
+ * Creates a QWaylandCompositorExtension with no container.
+ *
+ * \sa setExtensionContainer()
+ */
QWaylandCompositorExtension::QWaylandCompositorExtension()
: QWaylandObject(*new QWaylandCompositorExtensionPrivate())
{
}
+/*!
+ * Creates a QWaylandCompositorExtension and adds it to the extension \a container. The \a container
+ * does not become the parent of the QWaylandCompositorExtension.
+ *
+ * The extension adds itself to \a container later, when \l{initialize()} is called. For this to
+ * happen automatically, an event loop must be running in the current thread.
+ *
+ * The QWaylandCompositorExtension will remove itself again when it is destroyed.
+ */
QWaylandCompositorExtension::QWaylandCompositorExtension(QWaylandObject *container)
: QWaylandObject(*new QWaylandCompositorExtensionPrivate())
{
@@ -69,18 +142,42 @@ QWaylandCompositorExtension::~QWaylandCompositorExtension()
d->extension_container->removeExtension(this);
}
+/*!
+ * \fn const wl_interface *QWaylandCompositorExtension::extensionInterface() const
+ *
+ * A pure virtual function which should be reimplemented to return the \c wl_interface which
+ * corresponds to this QWaylandCompositorExtension.
+ */
+
+/*!
+ * \return the extension container for this QWaylandCompositorExtension or null if none has been
+ * set.
+ */
QWaylandObject *QWaylandCompositorExtension::extensionContainer() const
{
Q_D(const QWaylandCompositorExtension);
return d->extension_container;
}
+/*!
+ * Sets the extension container for this QWaylandCompositorExtension to \a container. This must be
+ * called before \l{initialize()} and cannot be changed once the QWaylandCompositorExtension has
+ * been initialized.
+ */
void QWaylandCompositorExtension::setExtensionContainer(QWaylandObject *container)
{
Q_D(QWaylandCompositorExtension);
d->extension_container = container;
}
+/*!
+ * Initializes the QWaylandCompositorExtension. The default implementation adopts the parent object
+ * as extension container if none has been set, and if the parent inherits from QWaylandObject. The
+ * default implementation also adds the QWaylandCompositorExtension to the list of extensions
+ * managed by the extension container.
+ *
+ * Override this function in subclasses to provide custom initialization code.
+ */
void QWaylandCompositorExtension::initialize()
{
Q_D(QWaylandCompositorExtension);
@@ -123,6 +220,21 @@ bool QWaylandCompositorExtension::event(QEvent *event)
return QWaylandObject::event(event);
}
+/*!
+ * \class QWaylandObject
+ * \inmodule QtWaylandCompositor
+ * \since 5.8
+ * \brief QWaylandObject is the base class for objects that can contain Wayland extensions.
+ *
+ * The QWaylandObject encapsulate extension container functionality. Any QWaylandObject object
+ * will automatically be an extension container and QWaylandCompositorExtension object which is
+ * a child of this will automatically add itself to its extension list, and remove itself when
+ * the extension object is destroyed.
+ */
+
+/*!
+ * Creates a QWaylandObject as a child of \a parent.
+ */
QWaylandObject::QWaylandObject(QObject *parent)
:QObject(parent)
{
@@ -140,6 +252,11 @@ QWaylandObject::~QWaylandObject()
QWaylandCompositorExtensionPrivate::get(extension)->extension_container = nullptr;
}
+/*!
+ * Returns the compositor extension which matches \a name if one has been registered with the
+ * QWaylandObject. If no extension matching the name has been registered, this function returns
+ * null.
+ */
QWaylandCompositorExtension *QWaylandObject::extension(const QByteArray &name)
{
for (int i = 0; i < extension_vector.size(); i++) {
@@ -149,6 +266,11 @@ QWaylandCompositorExtension *QWaylandObject::extension(const QByteArray &name)
return nullptr;
}
+/*!
+ * Returns the compositor extension which matches \a interface if one has been registered with the
+ * QWaylandObject. If no extension matching the interface has been registered, this function
+ * returns null.
+ */
QWaylandCompositorExtension *QWaylandObject::extension(const wl_interface *interface)
{
for (int i = 0; i < extension_vector.size(); i++) {
@@ -158,17 +280,27 @@ QWaylandCompositorExtension *QWaylandObject::extension(const wl_interface *inter
return nullptr;
}
+/*!
+ * Returns the list of compositor extensions that have been registered with this QWaylandObject.
+ */
QList<QWaylandCompositorExtension *> QWaylandObject::extensions() const
{
return extension_vector;
}
+/*!
+ * Registers \a extension with this QWaylandObject.
+ */
void QWaylandObject::addExtension(QWaylandCompositorExtension *extension)
{
Q_ASSERT(!extension_vector.contains(extension));
extension_vector.append(extension);
}
+/*!
+ * Removes \a extension from the list of registered extensions in this QWaylandObject, if it has
+ * previously been registered using \l{addExtension()}.
+ */
void QWaylandObject::removeExtension(QWaylandCompositorExtension *extension)
{
Q_ASSERT(extension_vector.contains(extension));
diff --git a/src/compositor/global/qwaylandquickextension.qdoc b/src/compositor/global/qwaylandquickextension.qdoc
new file mode 100644
index 00000000..925baef2
--- /dev/null
+++ b/src/compositor/global/qwaylandquickextension.qdoc
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ * \headerfile <QWaylandQuickExtension>
+ * \title Qt Wayland Compositor Qt Quick Extension Macro Declarations
+ * \inmodule QtWaylandCompositor
+ * \ingroup funclists
+ *
+ * \brief The <QWaylandQuickExtension> header file includes \l{macros} for creating Qt Quick types
+ * that correspond to subclasses of QWaylandCompositorExtension and QWaylandObject.
+ *
+ * If you are creating extensions to Qt Wayland Compositor, the macros in the QWaylandQuickExtension
+ * header may be a useful alternative to manually implementing the required parts for each class.
+ *
+ * \sa {Qt Wayland Compositor Examples - Custom Shell}
+ */
+
+/*!
+ * \macro Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(className)
+ * \relates <QWaylandQuickExtension>
+ *
+ * This macro can be used to define a Qt Quick class based on a Wayland extension. It defines
+ * a new class which inherits from \a className and which suffixes the name with "QuickExtension".
+ *
+ * The class should be a subclass of QWaylandCompositorExtension, and
+ * \l{QWaylandCompositorExtension::initialize()} will be called automatically. The type must be
+ * manually registered in Qt Quick using \l{qmlRegisterType()}.
+ *
+ * \sa Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_NAMED_CLASS
+ */
+
+/*!
+ * \macro Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CONTAINER_CLASS(className)
+ * \relates <QWaylandQuickExtension>
+ *
+ * This macro can be used to define a Qt Quick class intended to contain Wayland extensions. It
+ *
+ * It defines a new class which inherits from \a className and which suffixes the name with
+ * "QuickExtensionContainer". The class given by \a className should inherit from QWaylandObject,
+ * and the new class will have an \c extensions property which manages the extensions by calling
+ * \l{QWaylandObject::addExtension()}{addExtension()} and
+ * \l{QWaylandObject::removeExtension()}{removeExtension()} in the base class.
+ *
+ * The type must be manually registered in Qt Quick using \l{qmlRegisterType()}.
+ */
+
+/*!
+ * \macro Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_NAMED_CLASS(className, QmlType)
+ * \relates <QWaylandQuickExtension>
+ *
+ * This macro can be used to define a Qt Quick class based on a Wayland extension. It defines
+ * a new class which inherits from \a className and which suffixes the name with "QuickExtension".
+ *
+ * The macro works the same as \l{Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS}, but will also
+ * automatically register the new type as \a QmlType in Qt Quick.
+ */
diff --git a/src/extensions/qt-shell-unstable-v1.xml b/src/extensions/qt-shell-unstable-v1.xml
new file mode 100644
index 00000000..25adcaf8
--- /dev/null
+++ b/src/extensions/qt-shell-unstable-v1.xml
@@ -0,0 +1,276 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="qt_shell_unstable_v1">
+ <copyright>
+ Copyright (C) 2021 The Qt Company Ltd.
+ Contact: http://www.qt.io/licensing/
+
+ This file is part of the plugins of the Qt Toolkit.
+
+ $QT_BEGIN_LICENSE:BSD$
+ You may use this file under the terms of the BSD license as follows:
+
+ "Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of The Qt Company Ltd nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+
+ $QT_END_LICENSE$
+ </copyright>
+
+
+ <interface name="zqt_shell_surface_v1" version="1">
+
+ <description summary="create fully compliant surfaces for use with Qt applications">
+ The qt_shell_surface interface is part of a shell extension which allows clients to
+ access all windowing system features in Qt. These include mechanisms that may only be
+ advisable for trusted applications, and not for arbitrary third-party applications. The
+ qt_shell_surface provides an interface to windowing system features for a surface.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the qt_shell_surface">
+ Destroys the qt_shell_surface object.
+ </description>
+ </request>
+
+ <event name="resize">
+ <description summary="suggest a new size for the window">
+ This event suggests a size for the window. This can be one of multiple configuration
+ events grouped together by a serial number. The client should store them until the
+ corresponding configure event is received. The suggested configuration is not effective
+ before the client has responded with an ack_configure request with the same serial number.
+ This allows the server to group together reposition and resize events to avoid stuttering
+ in updates.
+ </description>
+ <arg name="serial" type="uint" summary="the serial of the configuration" />
+ <arg name="width" type="int" summary="the suggested width of the window" />
+ <arg name="height" type="int" summary="the suggested height of the window" />
+ </event>
+
+ <event name="set_position">
+ <description summary="informs of a new position for the window">
+ This event informs the client that the position of the window is about to change.
+ This can be one of multiple configuration events grouped together by a serial number. The
+ client should store them until the corresponding configure event is received. The suggested
+ configuration is not effective before the client has responded with an ack_configure request
+ with the same serial number. This allows the server to group together reposition and resize
+ events to avoid stuttering in updates.
+ </description>
+ <arg name="serial" type="uint" summary="the serial of the configuration" />
+ <arg name="x" type="int" summary="the suggested horizontal position of the window" />
+ <arg name="y" type="int" summary="the suggested vertical position of the window" />
+ </event>
+
+ <enum name="window_state">
+ <entry name="no_state" value="0"/>
+ <entry name="minimized" value="1"/>
+ <entry name="maximized" value="2"/>
+ <entry name="fullscreen" value="4"/>
+ </enum>
+ <event name="set_window_state">
+ <description summary="suggest a new state for the window">
+ This event informs the client that the state of the window is about to change.
+ This can be one of multiple configuration events grouped together by a serial number. The
+ client should store them until the corresponding configure event is received. The suggested
+ configuration is not effective before the client has responded with an ack_configure request
+ with the same serial number. This allows the server to group together reposition and resize
+ events to avoid stuttering in updates.
+ </description>
+ <arg name="serial" type="uint" summary="the serial of the configuration" />
+ <arg name="state" type="uint" summary="a combination of the flags in the window_state enum" />
+ </event>
+
+ <event name="configure">
+ <description summary="commits a series of configuration events">
+ After sending one or more configuration events, the server sends a configure event with
+ the same serial to trigger the necessary updates on the client side. When the client has
+ made these changes, it should send an ack_configure request for the corresponding serial to
+ inform the server that the new configuration is valid.
+ </description>
+ <arg name="serial" type="uint"/>
+ </event>
+
+ <event name="set_frame_margins">
+ <description summary="informs of the frame margins of the window">
+ This event informs the client how much of the window geometry is reserved for decorations.
+ When a resize configuration is received, the frame margins must be subtracted from the size
+ to find the appropriate size of the surface used for application content. State changes can
+ trigger updates to frame margins, for instance if the window switches between windowed and
+ fullscreen modes.
+ </description>
+ <arg name="left" type="uint" summary="the left frame margin" />
+ <arg name="right" type="uint" summary="the right frame margin" />
+ <arg name="top" type="uint" summary="the top frame margin" />
+ <arg name="bottom" type="uint" summary="the bottom frame margin" />
+ </event>
+
+ <event name="close">
+ <description summary="closes the window">
+ The server sends a close event to instruct the window to close itself gracefully. This could
+ for instance be triggered by user interaction, when clicking on the close button in the
+ window decorations.
+ </description>
+ </event>
+
+ <enum name="capabilities">
+ <entry name="interactive_move" value="1" />
+ <entry name="interactive_resize" value="2" />
+ </enum>
+ <event name="set_capabilities">
+ <description summary="informs the client of the server's capabilities">
+ This event informs the client of which capabilities are available on the server-side. See
+ the capabilities enum for possible flags.
+ </description>
+ <arg name="capabilities" type="uint" summary="a mask of flags from the capabilities enum" />
+ </event>
+
+ <request name="reposition">
+ <description summary="requests a new position for the window">
+ A client can issue this to request a new position for the window. If the request is granted,
+ the server will respond with a configuration event.
+ </description>
+ <arg name="x" type="int" summary="the requested horizontal position of the window" />
+ <arg name="y" type="int" summary="the requested vertical position of the window" />
+ </request>
+
+ <request name="request_activate">
+ <description summary="requests that the window becomes active">
+ This requests that the window becomes active. The server may respond by giving keyboard
+ focus to the window. Only a single window may be active at any given time.
+ </description>
+ </request>
+
+ <request name="set_size">
+ <description summary="informs that the shell surface size has changed">
+ The client issues the set_size request when the window has been resized.
+ </description>
+ <arg name="width" type="int" summary="the new width of the window" />
+ <arg name="height" type="int" summary="the new height of the window" />
+ </request>
+
+ <request name="set_minimum_size">
+ <description summary="informs of the minimum size hint of the window">
+ The client issues this request to inform the server of the minimum size of the window. The
+ server should not attempt to resize the window below this size.
+ </description>
+ <arg name="width" type="int" summary="the minimum width of the window" />
+ <arg name="height" type="int" summary="the minimum height of the window" />
+ </request>
+
+ <request name="set_maximum_size">
+ <description summary="informs of the maximum size hint of the window">
+ The client issues this request to inform the server of the maximum size of the window. The
+ server should not attempt to resize the window above this size.
+ </description>
+ <arg name="width" type="int" summary="the maximum width of the window" />
+ <arg name="height" type="int" summary="the maximum height of the window" />
+ </request>
+
+ <request name="set_window_title">
+ <description summary="informs of the title of the window">
+ This request is issued to inform the server of the title of the window.
+ </description>
+ <arg name="window_title" type="string" summary="the title of the window" />
+ </request>
+
+ <request name="set_window_flags">
+ <description summary="informs of the flags of the window">
+ The client issues this request to inform the server of the current window flags set on
+ the window
+ </description>
+ <arg name="flags" type="uint" summary="values defined by Qt::WindowFlags"/>
+ </request>
+
+ <request name="start_system_resize">
+ <description summary="starts a system resize">
+ This request is triggered when an application calls QWindow::startSystemResize() and should
+ trigger interactive resizing on the server-side. Typical behavior is to have the selected
+ edge follow the mouse.
+ </description>
+ <arg name="serial" type="uint" summary="serial of the pointer event that triggered the resize."/>
+ <arg name="edge" type="uint" summary="values defined by Qt::Edges"/>
+ </request>
+
+ <request name="start_system_move">
+ <description summary="starts a system resize">
+ This request is triggered when an application calls QWindow::startSystemMove() and should
+ trigger interactive repositioning on the server-side. Typical behavior is to have the
+ window follow the mouse.
+ </description>
+ <arg name="serial" type="uint" summary="Serial of the pointer event that triggered the move."/>
+ </request>
+
+ <request name="change_window_state">
+ <description summary="requests a new window state">
+ This requests the server to update the state of the window. The server may respond with
+ a new configure event bundle that reflects all the changes in position, size and state
+ that occurred.
+ </description>
+ <arg name="state" type="uint" summary="a combinaion of the flags in the window_state enum" />
+ </request>
+
+ <request name="raise">
+ <description summary="raises the window">
+ This requests that the window is brought to the top of the window stack, so that it is
+ showing on top of all other windows.
+ </description>
+ </request>
+
+ <request name="lower">
+ <description summary="lowers the window">
+ This requests that the window is brought to the bottom of the window stack, so that it is
+ showing below all other windows.
+ </description>
+ </request>
+
+ <request name="ack_configure">
+ <description summary="acknowledges a given configure event">
+ This acknowledges that the given configure event bundle has been received and that the
+ necessary changes have been applied.
+ </description>
+ <arg name="serial" type="uint" summary="the serial of the configure event" />
+ </request>
+
+ </interface>
+
+ <interface name="zqt_shell_v1" version="1">
+ <description summary="interface for create qt_shell_surface objects">
+ This interface provides a way to request qt_shell_surface objects that correspond to
+ wl_surfaces.
+ </description>
+ <request name="surface_create">
+ <description summary="creates a new qt_shell_surface object">
+ Creates a qt_shell_surface object that corresponds to a surface. If the surface is already
+ assigned a different role, this will issue a protocol error (see the error enum)
+ </description>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="id" type="new_id" interface="zqt_shell_surface_v1"/>
+ </request>
+
+ <enum name="error">
+ <entry name="role" value="0" summary="wl_surface already has a different role"/>
+ </enum>
+ </interface>
+
+</protocol>
diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp b/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp
index 12638096..adccef5b 100644
--- a/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp
+++ b/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp
@@ -105,7 +105,7 @@ void QWaylandEglWindow::setGeometry(const QRect &rect)
void QWaylandEglWindow::updateSurface(bool create)
{
- QMargins margins = frameMargins();
+ QMargins margins = mWindowDecoration ? frameMargins() : QMargins{};
QRect rect = geometry();
QSize sizeWithMargins = (rect.size() + QSize(margins.left() + margins.right(), margins.top() + margins.bottom())) * scale();
diff --git a/src/imports/compositor-extensions/CMakeLists.txt b/src/imports/compositor-extensions/CMakeLists.txt
index 27c7cb06..895e6c9c 100644
--- a/src/imports/compositor-extensions/CMakeLists.txt
+++ b/src/imports/compositor-extensions/CMakeLists.txt
@@ -3,3 +3,4 @@
add_subdirectory(xdgshell)
add_subdirectory(iviapplication)
add_subdirectory(wlshell)
+add_subdirectory(qtshell)
diff --git a/src/imports/compositor-extensions/qtshell/CMakeLists.txt b/src/imports/compositor-extensions/qtshell/CMakeLists.txt
new file mode 100644
index 00000000..ccad8ae5
--- /dev/null
+++ b/src/imports/compositor-extensions/qtshell/CMakeLists.txt
@@ -0,0 +1,32 @@
+#####################################################################
+## qwaylandqtshellplugin Plugin:
+#####################################################################
+
+qt_internal_add_qml_module(WaylandCompositorQtShell
+ URI "QtWayland.Compositor.QtShell"
+ VERSION "${PROJECT_VERSION}"
+ CLASS_NAME QWaylandQtShellPlugin
+ NO_PLUGIN_OPTIONAL
+ PLUGIN_TARGET WaylandCompositorQtShell
+ NO_GENERATE_PLUGIN_SOURCE
+ NO_GENERATE_QMLTYPES
+ INSTALL_SOURCE_QMLTYPES "plugins.qmltypes"
+
+ SOURCES
+ qwaylandqtshellplugin.cpp
+ qwaylandqtshell.cpp qwaylandqtshell.h qwaylandqtshell_p.h
+ qwaylandqtshellintegration.cpp qwaylandqtshellintegration_p.h
+ qwaylandqtshellchrome.cpp qwaylandqtshellchrome.h qwaylandqtshellchrome_p.h
+ PUBLIC_LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::Quick
+ Qt::QuickPrivate
+ Qt::WaylandCompositor
+ Qt::WaylandCompositorPrivate
+)
+
+qt6_generate_wayland_protocol_server_sources(WaylandCompositorQtShell
+ FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../extensions/qt-shell-unstable-v1.xml
+)
diff --git a/src/imports/compositor-extensions/qtshell/plugins.qmltypes b/src/imports/compositor-extensions/qtshell/plugins.qmltypes
new file mode 100644
index 00000000..3c09260f
--- /dev/null
+++ b/src/imports/compositor-extensions/qtshell/plugins.qmltypes
@@ -0,0 +1,442 @@
+import QtQuick.tooling 1.2
+
+// This file describes the plugin-supplied types contained in the library.
+// It is used for QML tooling purposes only.
+//
+// This file was auto-generated by:
+// 'qmlplugindump -nonrelocatable QtWayland.Compositor.QtShell 6.2'
+//
+// qmlplugindump is deprecated! You should use qmltyperegistrar instead.
+
+Module {
+ Component {
+ name: "QQuickAnchors"
+ prototype: "QObject"
+ Enum {
+ name: "Anchors"
+ values: {
+ "InvalidAnchor": 0,
+ "LeftAnchor": 1,
+ "RightAnchor": 2,
+ "TopAnchor": 4,
+ "BottomAnchor": 8,
+ "HCenterAnchor": 16,
+ "VCenterAnchor": 32,
+ "BaselineAnchor": 64,
+ "Horizontal_Mask": 19,
+ "Vertical_Mask": 108
+ }
+ }
+ Property { name: "left"; type: "QQuickAnchorLine" }
+ Property { name: "right"; type: "QQuickAnchorLine" }
+ Property { name: "horizontalCenter"; type: "QQuickAnchorLine" }
+ Property { name: "top"; type: "QQuickAnchorLine" }
+ Property { name: "bottom"; type: "QQuickAnchorLine" }
+ Property { name: "verticalCenter"; type: "QQuickAnchorLine" }
+ Property { name: "baseline"; type: "QQuickAnchorLine" }
+ Property { name: "margins"; type: "double" }
+ Property { name: "leftMargin"; type: "double" }
+ Property { name: "rightMargin"; type: "double" }
+ Property { name: "horizontalCenterOffset"; type: "double" }
+ Property { name: "topMargin"; type: "double" }
+ Property { name: "bottomMargin"; type: "double" }
+ Property { name: "verticalCenterOffset"; type: "double" }
+ Property { name: "baselineOffset"; type: "double" }
+ Property { name: "fill"; type: "QQuickItem"; isPointer: true }
+ Property { name: "centerIn"; type: "QQuickItem"; isPointer: true }
+ Property { name: "alignWhenCentered"; type: "bool" }
+ Signal { name: "centerAlignedChanged" }
+ }
+ Component {
+ name: "QQuickColorGroup"
+ prototype: "QObject"
+ Property { name: "alternateBase"; type: "QColor" }
+ Property { name: "base"; type: "QColor" }
+ Property { name: "brightText"; type: "QColor" }
+ Property { name: "button"; type: "QColor" }
+ Property { name: "buttonText"; type: "QColor" }
+ Property { name: "dark"; type: "QColor" }
+ Property { name: "highlight"; type: "QColor" }
+ Property { name: "highlightedText"; type: "QColor" }
+ Property { name: "light"; type: "QColor" }
+ Property { name: "link"; type: "QColor" }
+ Property { name: "linkVisited"; type: "QColor" }
+ Property { name: "mid"; type: "QColor" }
+ Property { name: "midlight"; type: "QColor" }
+ Property { name: "shadow"; type: "QColor" }
+ Property { name: "text"; type: "QColor" }
+ Property { name: "toolTipBase"; type: "QColor" }
+ Property { name: "toolTipText"; type: "QColor" }
+ Property { name: "window"; type: "QColor" }
+ Property { name: "windowText"; type: "QColor" }
+ Property { name: "placeholderText"; revision: 1538; type: "QColor" }
+ Signal { name: "placeholderTextChanged"; revision: 1538 }
+ Signal { name: "changed" }
+ }
+ Component {
+ name: "QQuickItem"
+ defaultProperty: "data"
+ prototype: "QObject"
+ Enum {
+ name: "Flags"
+ values: {
+ "ItemClipsChildrenToShape": 1,
+ "ItemAcceptsInputMethod": 2,
+ "ItemIsFocusScope": 4,
+ "ItemHasContents": 8,
+ "ItemAcceptsDrops": 16
+ }
+ }
+ Enum {
+ name: "TransformOrigin"
+ values: {
+ "TopLeft": 0,
+ "Top": 1,
+ "TopRight": 2,
+ "Left": 3,
+ "Center": 4,
+ "Right": 5,
+ "BottomLeft": 6,
+ "Bottom": 7,
+ "BottomRight": 8
+ }
+ }
+ Property { name: "parent"; type: "QQuickItem"; isPointer: true }
+ Property { name: "data"; type: "QObject"; isList: true; isReadonly: true }
+ Property { name: "resources"; type: "QObject"; isList: true; isReadonly: true }
+ Property { name: "children"; type: "QQuickItem"; isList: true; isReadonly: true }
+ Property { name: "x"; type: "double" }
+ Property { name: "y"; type: "double" }
+ Property { name: "z"; type: "double" }
+ Property { name: "width"; type: "double" }
+ Property { name: "height"; type: "double" }
+ Property { name: "opacity"; type: "double" }
+ Property { name: "enabled"; type: "bool" }
+ Property { name: "visible"; type: "bool" }
+ Property { name: "palette"; revision: 1536; type: "QQuickPalette"; isPointer: true }
+ Property { name: "visibleChildren"; type: "QQuickItem"; isList: true; isReadonly: true }
+ Property { name: "states"; type: "QQuickState"; isList: true; isReadonly: true }
+ Property { name: "transitions"; type: "QQuickTransition"; isList: true; isReadonly: true }
+ Property { name: "state"; type: "string" }
+ Property { name: "childrenRect"; type: "QRectF"; isReadonly: true }
+ Property { name: "anchors"; type: "QQuickAnchors"; isReadonly: true; isPointer: true }
+ Property { name: "left"; type: "QQuickAnchorLine"; isReadonly: true }
+ Property { name: "right"; type: "QQuickAnchorLine"; isReadonly: true }
+ Property { name: "horizontalCenter"; type: "QQuickAnchorLine"; isReadonly: true }
+ Property { name: "top"; type: "QQuickAnchorLine"; isReadonly: true }
+ Property { name: "bottom"; type: "QQuickAnchorLine"; isReadonly: true }
+ Property { name: "verticalCenter"; type: "QQuickAnchorLine"; isReadonly: true }
+ Property { name: "baseline"; type: "QQuickAnchorLine"; isReadonly: true }
+ Property { name: "baselineOffset"; type: "double" }
+ Property { name: "clip"; type: "bool" }
+ Property { name: "focus"; type: "bool" }
+ Property { name: "activeFocus"; type: "bool"; isReadonly: true }
+ Property { name: "activeFocusOnTab"; revision: 513; type: "bool" }
+ Property { name: "rotation"; type: "double" }
+ Property { name: "scale"; type: "double" }
+ Property { name: "transformOrigin"; type: "QQuickItem::TransformOrigin" }
+ Property { name: "transformOriginPoint"; type: "QPointF"; isReadonly: true }
+ Property { name: "transform"; type: "QQuickTransform"; isList: true; isReadonly: true }
+ Property { name: "smooth"; type: "bool" }
+ Property { name: "antialiasing"; type: "bool" }
+ Property { name: "implicitWidth"; type: "double" }
+ Property { name: "implicitHeight"; type: "double" }
+ Property { name: "containmentMask"; revision: 523; type: "QObject"; isPointer: true }
+ Property { name: "layer"; type: "QQuickItemLayer"; isReadonly: true; isPointer: true }
+ Signal {
+ name: "childrenRectChanged"
+ Parameter { type: "QRectF" }
+ }
+ Signal {
+ name: "baselineOffsetChanged"
+ Parameter { type: "double" }
+ }
+ Signal {
+ name: "stateChanged"
+ Parameter { type: "string" }
+ }
+ Signal {
+ name: "focusChanged"
+ Parameter { type: "bool" }
+ }
+ Signal {
+ name: "activeFocusChanged"
+ Parameter { type: "bool" }
+ }
+ Signal {
+ name: "activeFocusOnTabChanged"
+ revision: 513
+ Parameter { type: "bool" }
+ }
+ Signal {
+ name: "parentChanged"
+ Parameter { type: "QQuickItem"; isPointer: true }
+ }
+ Signal {
+ name: "transformOriginChanged"
+ Parameter { type: "TransformOrigin" }
+ }
+ Signal {
+ name: "smoothChanged"
+ Parameter { type: "bool" }
+ }
+ Signal {
+ name: "antialiasingChanged"
+ Parameter { type: "bool" }
+ }
+ Signal {
+ name: "clipChanged"
+ Parameter { type: "bool" }
+ }
+ Signal {
+ name: "windowChanged"
+ revision: 513
+ Parameter { name: "window"; type: "QQuickWindow"; isPointer: true }
+ }
+ Signal { name: "containmentMaskChanged"; revision: 523 }
+ Signal { name: "paletteChanged"; revision: 1536 }
+ Signal { name: "paletteCreated"; revision: 1536 }
+ Method { name: "update" }
+ Method {
+ name: "grabToImage"
+ revision: 516
+ type: "bool"
+ Parameter { name: "callback"; type: "QJSValue" }
+ Parameter { name: "targetSize"; type: "QSize" }
+ }
+ Method {
+ name: "grabToImage"
+ revision: 516
+ type: "bool"
+ Parameter { name: "callback"; type: "QJSValue" }
+ }
+ Method {
+ name: "contains"
+ type: "bool"
+ Parameter { name: "point"; type: "QPointF" }
+ }
+ Method {
+ name: "mapFromItem"
+ Parameter { type: "QQmlV4Function"; isPointer: true }
+ }
+ Method {
+ name: "mapToItem"
+ Parameter { type: "QQmlV4Function"; isPointer: true }
+ }
+ Method {
+ name: "mapFromGlobal"
+ revision: 519
+ Parameter { type: "QQmlV4Function"; isPointer: true }
+ }
+ Method {
+ name: "mapToGlobal"
+ revision: 519
+ Parameter { type: "QQmlV4Function"; isPointer: true }
+ }
+ Method { name: "forceActiveFocus" }
+ Method {
+ name: "forceActiveFocus"
+ Parameter { name: "reason"; type: "Qt::FocusReason" }
+ }
+ Method {
+ name: "nextItemInFocusChain"
+ revision: 513
+ type: "QQuickItem*"
+ Parameter { name: "forward"; type: "bool" }
+ }
+ Method { name: "nextItemInFocusChain"; revision: 513; type: "QQuickItem*" }
+ Method {
+ name: "childAt"
+ type: "QQuickItem*"
+ Parameter { name: "x"; type: "double" }
+ Parameter { name: "y"; type: "double" }
+ }
+ }
+ Component {
+ name: "QQuickItemLayer"
+ prototype: "QObject"
+ Property { name: "enabled"; type: "bool" }
+ Property { name: "textureSize"; type: "QSize" }
+ Property { name: "sourceRect"; type: "QRectF" }
+ Property { name: "mipmap"; type: "bool" }
+ Property { name: "smooth"; type: "bool" }
+ Property { name: "wrapMode"; type: "QQuickShaderEffectSource::WrapMode" }
+ Property { name: "format"; type: "QQuickShaderEffectSource::Format" }
+ Property { name: "samplerName"; type: "QByteArray" }
+ Property { name: "effect"; type: "QQmlComponent"; isPointer: true }
+ Property { name: "textureMirroring"; type: "QQuickShaderEffectSource::TextureMirroring" }
+ Property { name: "samples"; type: "int" }
+ Signal {
+ name: "enabledChanged"
+ Parameter { name: "enabled"; type: "bool" }
+ }
+ Signal {
+ name: "sizeChanged"
+ Parameter { name: "size"; type: "QSize" }
+ }
+ Signal {
+ name: "mipmapChanged"
+ Parameter { name: "mipmap"; type: "bool" }
+ }
+ Signal {
+ name: "wrapModeChanged"
+ Parameter { name: "mode"; type: "QQuickShaderEffectSource::WrapMode" }
+ }
+ Signal {
+ name: "nameChanged"
+ Parameter { name: "name"; type: "QByteArray" }
+ }
+ Signal {
+ name: "effectChanged"
+ Parameter { name: "component"; type: "QQmlComponent"; isPointer: true }
+ }
+ Signal {
+ name: "smoothChanged"
+ Parameter { name: "smooth"; type: "bool" }
+ }
+ Signal {
+ name: "formatChanged"
+ Parameter { name: "format"; type: "QQuickShaderEffectSource::Format" }
+ }
+ Signal {
+ name: "sourceRectChanged"
+ Parameter { name: "sourceRect"; type: "QRectF" }
+ }
+ Signal {
+ name: "textureMirroringChanged"
+ Parameter { name: "mirroring"; type: "QQuickShaderEffectSource::TextureMirroring" }
+ }
+ Signal {
+ name: "samplesChanged"
+ Parameter { name: "count"; type: "int" }
+ }
+ }
+ Component {
+ name: "QQuickPalette"
+ prototype: "QQuickColorGroup"
+ Property { name: "active"; type: "QQuickColorGroup"; isPointer: true }
+ Property { name: "inactive"; type: "QQuickColorGroup"; isPointer: true }
+ Property { name: "disabled"; type: "QQuickColorGroup"; isPointer: true }
+ Method {
+ name: "setActive"
+ Parameter { name: "active"; type: "QQuickColorGroup"; isPointer: true }
+ }
+ Method {
+ name: "setInactive"
+ Parameter { name: "inactive"; type: "QQuickColorGroup"; isPointer: true }
+ }
+ Method {
+ name: "setDisabled"
+ Parameter { name: "disabled"; type: "QQuickColorGroup"; isPointer: true }
+ }
+ }
+ Component { name: "QWaylandCompositorExtension"; prototype: "QWaylandObject" }
+ Component { name: "QWaylandObject"; prototype: "QObject" }
+ Component {
+ name: "QWaylandQtShell"
+ prototype: "QWaylandCompositorExtension"
+ Signal {
+ name: "qtShellSurfaceRequested"
+ Parameter { name: "surface"; type: "QWaylandSurface"; isPointer: true }
+ Parameter { name: "windowId"; type: "uint" }
+ Parameter { name: "resource"; type: "QWaylandResource" }
+ }
+ Signal {
+ name: "qtShellSurfaceCreated"
+ Parameter { name: "qtShellSurface"; type: "QWaylandQtShellSurface"; isPointer: true }
+ }
+ }
+ Component {
+ name: "QWaylandQtShellChrome"
+ defaultProperty: "data"
+ prototype: "QQuickItem"
+ exports: ["QtWayland.Compositor.QtShell/QtShellChrome 1.0"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "hasDecorations"; type: "bool"; isReadonly: true }
+ Property { name: "windowState"; type: "uint"; isReadonly: true }
+ Property { name: "windowFlags"; type: "uint"; isReadonly: true }
+ Property { name: "shellSurfaceItem"; type: "QWaylandQuickShellSurfaceItem"; isPointer: true }
+ Property { name: "maximizedRect"; type: "QRect" }
+ Property { name: "frameMarginLeft"; type: "int" }
+ Property { name: "frameMarginRight"; type: "int" }
+ Property { name: "frameMarginTop"; type: "int" }
+ Property { name: "frameMarginBottom"; type: "int" }
+ Property { name: "titleBar"; type: "QQuickItem"; isPointer: true }
+ Property { name: "leftResizeHandle"; type: "QQuickItem"; isPointer: true }
+ Property { name: "rightResizeHandle"; type: "QQuickItem"; isPointer: true }
+ Property { name: "topResizeHandle"; type: "QQuickItem"; isPointer: true }
+ Property { name: "bottomResizeHandle"; type: "QQuickItem"; isPointer: true }
+ Property { name: "topLeftResizeHandle"; type: "QQuickItem"; isPointer: true }
+ Property { name: "topRightResizeHandle"; type: "QQuickItem"; isPointer: true }
+ Property { name: "bottomLeftResizeHandle"; type: "QQuickItem"; isPointer: true }
+ Property { name: "bottomRightResizeHandle"; type: "QQuickItem"; isPointer: true }
+ Signal { name: "currentWindowStateChanged" }
+ Signal { name: "currentWindowFlagsChanged" }
+ Signal { name: "windowMetaInfoChanged" }
+ Signal { name: "activated" }
+ Signal { name: "deactivated" }
+ Signal { name: "clientDestroyed" }
+ Signal { name: "frameMarginChanged" }
+ Method { name: "raise" }
+ Method { name: "lower" }
+ Method { name: "toggleMaximized" }
+ Method { name: "toggleMinimized" }
+ Method { name: "toggleFullScreen" }
+ Method { name: "activate" }
+ Method { name: "deactivate" }
+ }
+ Component {
+ name: "QWaylandQtShellQuickExtension"
+ defaultProperty: "data"
+ prototype: "QWaylandQtShell"
+ exports: ["QtWayland.Compositor.QtShell/QtShell 1.0"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "data"; type: "QObject"; isList: true; isReadonly: true }
+ }
+ Component {
+ name: "QWaylandQtShellSurface"
+ defaultProperty: "data"
+ prototype: "QWaylandShellSurface"
+ exports: ["QtWayland.Compositor.QtShell/QtShellSurface 1.0"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "data"; type: "QObject"; isList: true; isReadonly: true }
+ Property { name: "surface"; type: "QWaylandSurface"; isReadonly: true; isPointer: true }
+ Property { name: "windowId"; type: "uint"; isReadonly: true }
+ Property { name: "windowFlags"; type: "uint"; isReadonly: true }
+ Property { name: "windowState"; type: "uint"; isReadonly: true }
+ Property { name: "windowTitle"; type: "string"; isReadonly: true }
+ Property { name: "windowGeometry"; type: "QRect"; isReadonly: true }
+ Property { name: "windowPosition"; type: "QPoint" }
+ Property { name: "positionAutomatic"; type: "bool"; isReadonly: true }
+ Property { name: "minimumSize"; type: "QSize"; isReadonly: true }
+ Property { name: "maximumSize"; type: "QSize"; isReadonly: true }
+ Property { name: "frameMarginLeft"; type: "int" }
+ Property { name: "frameMarginRight"; type: "int" }
+ Property { name: "frameMarginTop"; type: "int" }
+ Property { name: "frameMarginBottom"; type: "int" }
+ Property { name: "active"; type: "bool" }
+ Signal { name: "startMove" }
+ Signal { name: "startResize" }
+ Signal { name: "frameMarginChanged" }
+ Signal { name: "raiseRequested" }
+ Signal { name: "lowerRequested" }
+ Method {
+ name: "initialize"
+ Parameter { name: "qtShell"; type: "QWaylandQtShell"; isPointer: true }
+ Parameter { name: "surface"; type: "QWaylandSurface"; isPointer: true }
+ Parameter { name: "windowId"; type: "uint" }
+ Parameter { name: "resource"; type: "QWaylandResource" }
+ }
+ Method {
+ name: "requestWindowGeometry"
+ Parameter { name: "windowState"; type: "uint" }
+ Parameter { name: "windowGeometry"; type: "QRect" }
+ }
+ Method { name: "sendClose" }
+ }
+ Component {
+ name: "QWaylandShellSurface"
+ prototype: "QWaylandCompositorExtension"
+ Property { name: "windowType"; type: "Qt::WindowType"; isReadonly: true }
+ }
+}
diff --git a/src/imports/compositor-extensions/qtshell/qwaylandqtshell.cpp b/src/imports/compositor-extensions/qtshell/qwaylandqtshell.cpp
new file mode 100644
index 00000000..81a9e5dd
--- /dev/null
+++ b/src/imports/compositor-extensions/qtshell/qwaylandqtshell.cpp
@@ -0,0 +1,872 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwaylandqtshell.h"
+#include "qwaylandqtshell_p.h"
+#include "qwaylandqtshellchrome.h"
+
+#include <QtWaylandCompositor/QWaylandCompositor>
+#include <QtWaylandCompositor/QWaylandSurface>
+#include "qwaylandqtshell.h"
+#include <QtWaylandCompositor/QWaylandResource>
+
+#if QT_CONFIG(wayland_compositor_quick)
+# include "qwaylandqtshellintegration_p.h"
+#endif
+
+#include <QtWaylandCompositor/QWaylandResource>
+#include <QDebug>
+#include <compositor/compositor_api/qwaylandseat.h>
+
+#include <QtWaylandCompositor/private/qwaylandutils_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ * \qmltype QtShell
+ * \instantiates QWaylandQtShell
+ * \inqmlmodule QtWayland.Compositor.QtShell
+ * \since 6.3
+ * \brief Provides a shell extension for Qt applications running on a Qt Wayland Compositor.
+ *
+ * The QtShell extension provides a way to associate an QtShellSurface with a regular Wayland
+ * surface. The QtShell extension is written to support the window management features which are
+ * supported by Qt. It may be suitable on a platform where both the compositor and client
+ * applications are written with Qt, and where applications are trusted not to abuse features such
+ * as manual window positioning and "bring-to-front".
+ *
+ * For other use cases, consider using IviApplication or XdgShell instead.
+ *
+ * \qml
+ * import QtWayland.Compositor.QtShell
+ *
+ * WaylandCompositor {
+ * property ListModel shellSurfaces: ListModel {}
+ * QtShell {
+ * onQtShellSurfaceCreated: {
+ * shellSurfaces.append({shellSurface: qtShellSurface})
+ * }
+ * }
+ * }
+ * \endqml
+ */
+QWaylandQtShell::QWaylandQtShell()
+ : QWaylandCompositorExtensionTemplate<QWaylandQtShell>(*new QWaylandQtShellPrivate())
+{
+}
+
+QWaylandQtShell::QWaylandQtShell(QWaylandCompositor *compositor)
+ : QWaylandCompositorExtensionTemplate<QWaylandQtShell>(compositor, *new QWaylandQtShellPrivate())
+{
+}
+
+bool QWaylandQtShell::moveChromeToFront(QWaylandQtShellChrome *chrome)
+{
+ Q_D(QWaylandQtShell);
+ for (int i = 0; i < d->m_chromes.size(); ++i) {
+ if (d->m_chromes.at(i) == chrome) {
+ if (i > 0) {
+ QWaylandQtShellChrome *currentActive = d->m_chromes.first();
+ d->m_chromes.move(i, 0);
+ chrome->activate();
+ currentActive->deactivate();
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void QWaylandQtShell::registerChrome(QWaylandQtShellChrome *chrome)
+{
+ Q_D(QWaylandQtShell);
+ if (moveChromeToFront(chrome))
+ return;
+
+ QWaylandQtShellChrome *currentActive = d->m_chromes.isEmpty() ? nullptr : d->m_chromes.first();
+
+ d->m_chromes.prepend(chrome);
+ chrome->activate();
+
+ if (currentActive != nullptr)
+ currentActive->deactivate();
+
+ connect(chrome, &QWaylandQtShellChrome::activated, this, &QWaylandQtShell::chromeActivated);
+ connect(chrome, &QWaylandQtShellChrome::deactivated, this, &QWaylandQtShell::chromeDeactivated);
+}
+
+void QWaylandQtShell::unregisterChrome(QWaylandQtShellChrome *chrome)
+{
+ Q_D(QWaylandQtShell);
+
+ chrome->disconnect(this);
+ int index = d->m_chromes.indexOf(chrome);
+ if (index >= 0) {
+ d->m_chromes.removeAt(index);
+ if (index == 0 && d->m_chromes.size() > 0)
+ d->m_chromes.at(0)->activate();
+ }
+}
+
+void QWaylandQtShell::chromeActivated()
+{
+ QWaylandQtShellChrome *c = qobject_cast<QWaylandQtShellChrome *>(sender());
+ if (c != nullptr) {
+ moveChromeToFront(c);
+ }
+}
+
+void QWaylandQtShell::chromeDeactivated()
+{
+ Q_D(QWaylandQtShell);
+ QWaylandQtShellChrome *c = qobject_cast<QWaylandQtShellChrome *>(sender());
+ if (d->m_chromes.size() > 1 && d->m_chromes.at(0) == c) {
+ d->m_chromes.move(0, 1);
+ d->m_chromes.at(0)->activate();
+ } else if (d->m_chromes.size() == 1) { // One window must be active
+ d->m_chromes.at(0)->activate();
+ }
+}
+
+void QWaylandQtShell::initialize()
+{
+ Q_D(QWaylandQtShell);
+ QWaylandCompositorExtensionTemplate::initialize();
+
+ QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer());
+ if (!compositor) {
+ qWarning() << "Failed to find QWaylandCompositor when initializing QWaylandQtShell";
+ return;
+ }
+
+ d->init(compositor->display(), 1);
+}
+
+const struct wl_interface *QWaylandQtShell::interface()
+{
+ return QWaylandQtShellPrivate::interface();
+}
+
+/*!
+ * \internal
+ */
+QByteArray QWaylandQtShell::interfaceName()
+{
+ return QWaylandQtShellPrivate::interfaceName();
+}
+
+/*!
+ * \qmlsignal void QtWaylandCompositor::QtShell::qtShellSurfaceRequested(WaylandSurface surface, WaylandResource resource)
+ *
+ * This signal is emitted when the client has requested a QtShellSurface to be associated
+ * with \a surface. The handler for this signal is expected to create the QtShellSurface for
+ * \a resource and initialize it within the scope of the signal emission. If no QtShellSurface is
+ * created, a default one will be created instead.
+ */
+
+/*!
+ * \qmlsignal void QtWaylandCompositor::QtShell::qtShellSurfaceCreated(QtShellSurface *qtShellSurface)
+ *
+ * This signal is emitted when an QtShellSurface has been created. The supplied \a qtShellSurface is
+ * most commonly used to instantiate a ShellSurfaceItem.
+ */
+
+QWaylandQtShellPrivate::QWaylandQtShellPrivate()
+{
+}
+
+void QWaylandQtShellPrivate::unregisterQtShellSurface(QWaylandQtShellSurface *qtShellSurface)
+{
+ Q_UNUSED(qtShellSurface)
+}
+
+void QWaylandQtShellPrivate::zqt_shell_v1_surface_create(QtWaylandServer::zqt_shell_v1::Resource *resource, wl_resource *surfaceResource, uint32_t id)
+{
+ Q_Q(QWaylandQtShell);
+ QWaylandSurface *surface = QWaylandSurface::fromResource(surfaceResource);
+
+ if (!surface->setRole(QWaylandQtShellSurface::role(), resource->handle, ZQT_SHELL_V1_ERROR_ROLE))
+ return;
+
+ QWaylandResource qtShellSurfaceResource(wl_resource_create(resource->client(), &zqt_shell_surface_v1_interface,
+ wl_resource_get_version(resource->handle), id));
+
+ emit q->qtShellSurfaceRequested(surface, qtShellSurfaceResource);
+
+ QWaylandQtShellSurface *qtShellSurface = QWaylandQtShellSurface::fromResource(qtShellSurfaceResource.resource());
+
+ if (!qtShellSurface)
+ qtShellSurface = new QWaylandQtShellSurface(q, surface, qtShellSurfaceResource);
+
+ emit q->qtShellSurfaceCreated(qtShellSurface);
+}
+
+QWaylandSurfaceRole QWaylandQtShellSurfacePrivate::s_role("qt_shell_surface");
+
+/*!
+ * \qmltype QtShellSurface
+ * \instantiates QWaylandQtShellSurface
+ * \inqmlmodule QtWayland.Compositor.QtShell
+ * \since 6.3
+ * \brief Provides a simple way to identify and resize a surface.
+ *
+ * This type is part of the \l{QtShell} extension and provides a way to extend
+ * the functionality of an existing WaylandSurface with window management functionality.
+ *
+ * The QtShellSurface type holds the core functionality needed to create a compositor that supports
+ * the QtShell extension. It can be used directly, or via the QtShellChrome type, depending on what
+ * the needs of the compositor are. The QtShellChrome type has default behaviors and convenience
+ * APIs for working with QtShellSurface objects.
+ */
+
+/*!
+ \qmlsignal void QtWaylandCompositor::QtShellSurface::startMove()
+
+ The client has requested an interactive move operation in the compositor by calling
+ \l{QWindow::startSystemMove()}.
+
+ \sa capabilities
+*/
+
+/*!
+ \qmlsignal void QtWaylandCompositor::QtShellSurface::startResize(enum edges)
+
+ The client has requested an interactive resize operation in the compositor by calling
+ \l{QWindow::startSystemResize()}.
+
+ The \a edges provides information about which edge of the window should be moved during the
+ resize. It is a mask of the following values:
+ \list
+ \li Qt.TopEdge
+ \li Qt.LeftEdge
+ \li Qt.RightEdge
+ \li Qt.BottomEdge
+ \endlist
+
+ \sa capabilities
+*/
+
+QWaylandQtShellSurface::QWaylandQtShellSurface()
+ : QWaylandShellSurfaceTemplate<QWaylandQtShellSurface>(*new QWaylandQtShellSurfacePrivate())
+{
+}
+
+QWaylandQtShellSurface::QWaylandQtShellSurface(QWaylandQtShell *application, QWaylandSurface *surface, const QWaylandResource &resource)
+ : QWaylandShellSurfaceTemplate<QWaylandQtShellSurface>(*new QWaylandQtShellSurfacePrivate())
+{
+ initialize(application, surface, resource);
+}
+
+/*!
+ * \qmlmethod void QtWaylandCompositor::QtShellSurface::initialize(QtShell qtShell, WaylandSurface surface, WaylandResource resource)
+ *
+ * Initializes the QtShellSurface, associating it with the given \a qtShell, \a surface, and
+ * \a resource.
+ */
+void QWaylandQtShellSurface::initialize(QWaylandQtShell *qtShell, QWaylandSurface *surface, const QWaylandResource &resource)
+{
+ Q_D(QWaylandQtShellSurface);
+
+ d->m_qtShell = qtShell;
+ d->m_surface = surface;
+
+ connect(d->m_surface, &QWaylandSurface::damaged, this, &QWaylandQtShellSurface::surfaceCommitted);
+
+ d->init(resource.resource());
+ setExtensionContainer(surface);
+
+ emit surfaceChanged();
+
+ QWaylandCompositorExtension::initialize();
+}
+
+/*!
+ * \qmlproperty WaylandSurface QtWaylandCompositor::QtShellSurface::surface
+ *
+ * This property holds the surface associated with this QtShellSurface.
+ */
+QWaylandSurface *QWaylandQtShellSurface::surface() const
+{
+ Q_D(const QWaylandQtShellSurface);
+ return d->m_surface;
+}
+
+QWaylandQtShell *QWaylandQtShellSurface::shell() const
+{
+ Q_D(const QWaylandQtShellSurface);
+ return d->m_qtShell;
+}
+
+/*!
+ * \qmlproperty point QtWaylandCompositor::QtShellSurface::windowPosition
+ *
+ * This property holds the position of the shell surface relative to its output.
+ */
+QPoint QWaylandQtShellSurface::windowPosition() const
+{
+ Q_D(const QWaylandQtShellSurface);
+ return d->m_windowGeometry.topLeft();
+}
+
+void QWaylandQtShellSurface::setWindowPosition(const QPoint &position)
+{
+ Q_D(QWaylandQtShellSurface);
+
+ // We don't care about the ack in this case, so use UINT_MAX as serial
+ d->send_set_position(UINT32_MAX, position.x(), position.y());
+ d->send_configure(UINT32_MAX);
+
+ d->m_windowGeometry.moveTopLeft(position);
+ d->m_positionSet = true;
+ emit positionAutomaticChanged();
+ emit windowGeometryChanged();
+}
+
+/*!
+ * \qmlproperty rect QtWaylandCompositor::QtShellSurface::windowGeometry
+ *
+ * This property holds the window geometry of the shell surface.
+ */
+QRect QWaylandQtShellSurface::windowGeometry() const
+{
+ Q_D(const QWaylandQtShellSurface);
+ return d->m_windowGeometry;
+}
+
+/*!
+ * \qmlproperty size QtWaylandCompositor::QtShellSurface::minimumSize
+ *
+ * The minimum size of the window if the client has specified one. Otherwise an invalid size.
+ */
+QSize QWaylandQtShellSurface::minimumSize() const
+{
+ Q_D(const QWaylandQtShellSurface);
+ return d->m_minimumSize;
+}
+
+/*!
+ * \qmlproperty size QtWaylandCompositor::QtShellSurface::maximumSize
+ *
+ * The maximum size of the window if the client has specified one. Otherwise an invalid size.
+ */
+QSize QWaylandQtShellSurface::maximumSize() const
+{
+ Q_D(const QWaylandQtShellSurface);
+ return d->m_maximumSize;
+}
+
+/*!
+ * \qmlmethod void QtWaylandCompositor::QtShellSurface::requestWindowGeometry(int windowState, rect windowGeometry)
+ *
+ * Requests a new \a windowState and \a windowGeometry for the QtShellSurface. The state and
+ * geometry is updated when the client has acknowledged the request (at which point it is safe to
+ * assume that the surface's buffer has been resized if necessary).
+ */
+void QWaylandQtShellSurface::requestWindowGeometry(uint windowState, const QRect &windowGeometry)
+{
+ Q_D(QWaylandQtShellSurface);
+ if (!windowGeometry.isValid())
+ return;
+
+ d->configure(windowState, windowGeometry);
+}
+
+void QWaylandQtShellSurfacePrivate::configure(uint windowState, const QRect &newGeometry)
+{
+ Q_Q(QWaylandQtShellSurface);
+ QWaylandCompositor *compositor = m_surface != nullptr ? m_surface->compositor() : nullptr;
+ if (!compositor) {
+ qWarning() << "Failed to find QWaylandCompositor when configuring QWaylandQtShell";
+ return;
+ }
+
+ uint32_t serial = compositor->nextSerial();
+ m_pendingConfigures[serial] = qMakePair(windowState, newGeometry);
+
+ send_set_position(serial, newGeometry.x(), newGeometry.y());
+ send_resize(serial, newGeometry.width(), newGeometry.height());
+ send_set_window_state(serial, windowState & ~Qt::WindowActive);
+ send_configure(serial);
+}
+
+void QWaylandQtShellSurface::setFrameMargins(const QMargins &margins)
+{
+ Q_D(QWaylandQtShellSurface);
+ if (d->m_frameMargins == margins)
+ return;
+
+ d->m_frameMargins = margins;
+ d->updateFrameMargins();
+
+ emit frameMarginChanged();
+}
+
+/*!
+ * \qmlproperty int QtWaylandCompositor::QtShellSurface::frameMarginLeft
+ *
+ * This holds the window frame margin to the left of the surface.
+ */
+void QWaylandQtShellSurface::setFrameMarginLeft(int left)
+{
+ Q_D(QWaylandQtShellSurface);
+ if (d->m_frameMargins.left() == left)
+ return;
+
+ d->m_frameMargins.setLeft(left);
+ d->updateFrameMargins();
+
+ emit frameMarginChanged();
+}
+
+int QWaylandQtShellSurface::frameMarginLeft() const
+{
+ Q_D(const QWaylandQtShellSurface);
+ return d->m_frameMargins.left();
+}
+
+/*!
+ * \qmlproperty int QtWaylandCompositor::QtShellSurface::frameMarginRight
+ *
+ * This holds the window frame margin to the right of the surface.
+ */
+void QWaylandQtShellSurface::setFrameMarginRight(int right)
+{
+ Q_D(QWaylandQtShellSurface);
+ if (d->m_frameMargins.right() == right)
+ return;
+
+ d->m_frameMargins.setRight(right);
+ d->updateFrameMargins();
+
+ emit frameMarginChanged();
+}
+
+int QWaylandQtShellSurface::frameMarginRight() const
+{
+ Q_D(const QWaylandQtShellSurface);
+ return d->m_frameMargins.right();
+}
+
+/*!
+ * \qmlproperty int QtWaylandCompositor::QtShellSurface::frameMarginTop
+ *
+ * This holds the window frame margin above the surface.
+ */
+
+void QWaylandQtShellSurface::setFrameMarginTop(int top)
+{
+ Q_D(QWaylandQtShellSurface);
+ if (d->m_frameMargins.top() == top)
+ return;
+ d->m_frameMargins.setTop(top);
+ d->updateFrameMargins();
+
+ emit frameMarginChanged();
+}
+
+int QWaylandQtShellSurface::frameMarginTop() const
+{
+ Q_D(const QWaylandQtShellSurface);
+ return d->m_frameMargins.top();
+}
+
+/*!
+ * \qmlproperty int QtWaylandCompositor::QtShellSurface::frameMarginBottom
+ *
+ * This holds the window frame margin below the surface.
+ */
+void QWaylandQtShellSurface::setFrameMarginBottom(int bottom)
+{
+ Q_D(QWaylandQtShellSurface);
+ if (d->m_frameMargins.bottom() == bottom)
+ return;
+ d->m_frameMargins.setBottom(bottom);
+ d->updateFrameMargins();
+
+ emit frameMarginChanged();
+}
+
+bool QWaylandQtShellSurface::positionAutomatic() const
+{
+ Q_D(const QWaylandQtShellSurface);
+ return !d->m_positionSet;
+}
+
+int QWaylandQtShellSurface::frameMarginBottom() const
+{
+ Q_D(const QWaylandQtShellSurface);
+ return d->m_frameMargins.bottom();
+}
+
+/*!
+ * \qmlproperty int QtWaylandCompositor::QtShellSurface::windowFlags
+ *
+ * This property holds the window flags of the QtShellSurface.
+ */
+uint QWaylandQtShellSurface::windowFlags() const
+{
+ Q_D(const QWaylandQtShellSurface);
+ return d->m_windowFlags;
+}
+
+/*!
+ * \qmlmethod void QtWaylandCompositor::QtShellSurface::sendClose()
+ *
+ * Requests that the client application closes itself.
+ */
+void QWaylandQtShellSurface::sendClose()
+{
+ Q_D(QWaylandQtShellSurface);
+ d->send_close();
+}
+
+/*!
+ * \qmlproperty string QtWaylandCompositor::QtShellSurface::windowTitle
+ *
+ * This property holds the window title of the QtShellSurface.
+ */
+QString QWaylandQtShellSurface::windowTitle() const
+{
+ Q_D(const QWaylandQtShellSurface);
+ return d->m_windowTitle;
+}
+
+/*!
+ * \qmlproperty bool QtWaylandCompositor::QtShellSurface::active
+ *
+ * This property holds whether the surface is currently considered active.
+ *
+ * \note There are no restrictions in QtShellSurface that prevents multiple surfaces from being
+ * active simultaneously. Such logic must either be implemented by the compositor itself, or by
+ * using the QtShellChrome type, which will automatically manage the activation state of surfaces.
+ */
+void QWaylandQtShellSurface::setActive(bool active)
+{
+ Q_D(QWaylandQtShellSurface);
+ if (d->m_active == active)
+ return;
+
+ d->m_active = active;
+ QWaylandCompositor *compositor = d->m_surface ? d->m_surface->compositor() : nullptr;
+ QWaylandSeat *seat = compositor ? compositor->defaultSeat() : nullptr;
+ if (seat && active)
+ seat->setKeyboardFocus(surface());
+ emit activeChanged();
+}
+
+bool QWaylandQtShellSurface::active() const
+{
+ Q_D(const QWaylandQtShellSurface);
+ return d->m_active;
+}
+
+/*!
+ * \qmlproperty enum QtWaylandCompositor::QtShellSurface::capabilities
+ *
+ * This property holds the capabilities of the compositor. By default, no special capabilities are
+ * enabled.
+ *
+ * \list
+ * \li QtShellSurface.InteractiveMove The client can trigger a server-side interactive move
+ * operation using \l{QWindow::startSystemMove()}. The compositor will be notified of this
+ * through the \l{startMove()} signal.
+ * \li QtShellSurface.InteractiveResize The client can trigger a server-side interactive resize
+ * operation using \l{QWindow::startSystemResize()}. The compositor will be notified of this
+ * through the \l{startResize()} signal.
+ * \endlist
+ */
+void QWaylandQtShellSurface::setCapabilities(CapabilityFlags capabilities)
+{
+ Q_D(QWaylandQtShellSurface);
+ if (d->m_capabilities == capabilities)
+ return;
+
+ d->m_capabilities = capabilities;
+ d->send_set_capabilities(capabilities);
+
+ emit capabilitiesChanged();
+}
+
+QWaylandQtShellSurface::CapabilityFlags QWaylandQtShellSurface::capabilities() const
+{
+ Q_D(const QWaylandQtShellSurface);
+ return d->m_capabilities;
+}
+
+/*!
+ * \qmlproperty int QtWaylandCompositor::QtShellSurface::windowState
+ *
+ * This property holds the window state of the QtShellSurface.
+ *
+ * \note When \l{requestWindowGeometry()} is called to update state of the surface, the
+ * \c windowState property will not be updated until the client has acknowledged the state change.
+ */
+uint QWaylandQtShellSurface::windowState() const
+{
+ Q_D(const QWaylandQtShellSurface);
+ return d->m_windowState;
+}
+
+void QWaylandQtShellSurface::surfaceCommitted()
+{
+ Q_D(QWaylandQtShellSurface);
+ if (d->m_lastAckedConfigure < UINT32_MAX) {
+ QRect targetRect = d->m_windowGeometry;
+ uint windowState = d->m_windowState;
+ for (auto it = d->m_pendingConfigures.begin(); it != d->m_pendingConfigures.end(); ) {
+ if (it.key() == d->m_lastAckedConfigure) {
+ targetRect = it.value().second;
+ windowState = it.value().first;
+ }
+
+ if (it.key() <= d->m_lastAckedConfigure)
+ it = d->m_pendingConfigures.erase(it);
+ else
+ break;
+ }
+
+ if (d->m_windowState != windowState) {
+ d->m_windowState = windowState;
+ emit windowStateChanged();
+ }
+
+ if (d->m_windowGeometry != targetRect) {
+ d->m_windowGeometry = targetRect;
+ d->m_positionSet = true;
+ emit positionAutomaticChanged();
+ emit windowGeometryChanged();
+ }
+
+ d->m_lastAckedConfigure = UINT32_MAX;
+ d->m_pendingPosition = QPoint{};
+ d->m_pendingPositionValid = false;
+ d->m_pendingSize = QSize{};
+ } else {
+ QRect oldRect = d->m_windowGeometry;
+ if (d->m_pendingPositionValid) {
+ d->m_windowGeometry.moveTopLeft(d->m_pendingPosition);
+ d->m_pendingPosition = QPoint{};
+ d->m_pendingPositionValid = false;
+ d->m_positionSet = true;
+ emit positionAutomaticChanged();
+ }
+
+ if (d->m_pendingSize.isValid()) {
+ d->m_windowGeometry.setSize(d->m_pendingSize);
+ d->m_pendingSize = QSize{};
+ }
+
+ if (d->m_windowGeometry != oldRect)
+ emit windowGeometryChanged();
+ }
+}
+
+/*!
+ * Returns the Wayland interface for the QWaylandQtShellSurface.
+ */
+const wl_interface *QWaylandQtShellSurface::interface()
+{
+ return QWaylandQtShellSurfacePrivate::interface();
+}
+
+QByteArray QWaylandQtShellSurface::interfaceName()
+{
+ return QWaylandQtShellSurfacePrivate::interfaceName();
+}
+
+/*!
+ * Returns the surface role for the QWaylandQtShellSurface.
+ */
+QWaylandSurfaceRole *QWaylandQtShellSurface::role()
+{
+ return &QWaylandQtShellSurfacePrivate::s_role;
+}
+
+/*!
+ * Returns the QWaylandQtShellSurface corresponding to the \a resource.
+ */
+QWaylandQtShellSurface *QWaylandQtShellSurface::fromResource(wl_resource *resource)
+{
+ if (auto p = QtWayland::fromResource<QWaylandQtShellSurfacePrivate *>(resource))
+ return p->q_func();
+ return nullptr;
+}
+
+#if QT_CONFIG(wayland_compositor_quick)
+QWaylandQuickShellIntegration *QWaylandQtShellSurface::createIntegration(QWaylandQuickShellSurfaceItem *item)
+{
+ return new QtWayland::QtShellIntegration(item);
+}
+#endif
+
+/*!
+ * \internal
+ */
+void QWaylandQtShellSurface::initialize()
+{
+ QWaylandShellSurfaceTemplate::initialize();
+}
+
+QWaylandQtShellSurfacePrivate::QWaylandQtShellSurfacePrivate()
+{
+}
+
+void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_ack_configure(Resource *resource, uint32_t serial)
+{
+ Q_UNUSED(resource);
+ Q_Q(QWaylandQtShellSurface);
+ if (serial < UINT32_MAX)
+ m_lastAckedConfigure = serial;
+
+ // Fake a surface commit because we won't get one as long as the window is unexposed
+ if (m_windowState & Qt::WindowMinimized)
+ q->surfaceCommitted();
+}
+
+void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_reposition(Resource *resource, int32_t x, int32_t y)
+{
+ Q_UNUSED(resource);
+
+ m_pendingPosition = QPoint(x, y);
+ m_pendingPositionValid = true;
+ m_lastAckedConfigure = UINT32_MAX;
+}
+
+void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_set_size(Resource *resource, int32_t width, int32_t height)
+{
+ Q_UNUSED(resource);
+
+ m_pendingSize = QSize(width, height);
+ m_lastAckedConfigure = UINT32_MAX;
+}
+
+void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_set_minimum_size(Resource *resource, int32_t width, int32_t height)
+{
+ Q_UNUSED(resource);
+ Q_Q(QWaylandQtShellSurface);
+ m_minimumSize = QSize{width, height};
+ emit q->minimumSizeChanged();
+}
+
+void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_set_maximum_size(Resource *resource, int32_t width, int32_t height)
+{
+ Q_UNUSED(resource);
+ Q_Q(QWaylandQtShellSurface);
+ m_maximumSize = QSize{width, height};
+ emit q->maximumSizeChanged();
+}
+
+void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_destroy_resource(QtWaylandServer::zqt_shell_surface_v1::Resource *resource)
+{
+ Q_UNUSED(resource);
+ Q_Q(QWaylandQtShellSurface);
+ QWaylandQtShellPrivate::get(m_qtShell)->unregisterQtShellSurface(q);
+ delete q;
+}
+
+void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_destroy(QtWaylandServer::zqt_shell_surface_v1::Resource *resource)
+{
+ wl_resource_destroy(resource->handle);
+}
+
+void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_set_window_flags(Resource *resource, uint32_t flags)
+{
+ Q_UNUSED(resource);
+ Q_Q(QWaylandQtShellSurface);
+ m_windowFlags = flags;
+ emit q->windowFlagsChanged();
+}
+
+void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_change_window_state(Resource *resource, uint32_t state)
+{
+ Q_UNUSED(resource);
+ Q_Q(QWaylandQtShellSurface);
+ uint oldWindowState = m_windowState;
+ m_windowState = state & ~Qt::WindowActive;
+
+ if (oldWindowState != m_windowState)
+ emit q->windowStateChanged();
+}
+
+void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_start_system_resize(Resource *resource, uint32_t serial, uint32_t edge)
+{
+ Q_UNUSED(resource);
+ Q_UNUSED(serial);
+ Q_Q(QWaylandQtShellSurface);
+ emit q->startResize(Qt::Edges(edge));
+}
+
+void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_start_system_move(Resource *resource, uint32_t serial)
+{
+ Q_UNUSED(resource);
+ Q_UNUSED(serial);
+ Q_Q(QWaylandQtShellSurface);
+ emit q->startMove();
+}
+
+void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_set_window_title(Resource *resource,
+ const QString &title)
+{
+ Q_UNUSED(resource);
+ Q_Q(QWaylandQtShellSurface);
+ m_windowTitle = title;
+ emit q->windowTitleChanged();
+}
+
+void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_request_activate(Resource *resource)
+
+{
+ Q_UNUSED(resource);
+ Q_Q(QWaylandQtShellSurface);
+ q->setActive(true);
+}
+
+void QWaylandQtShellSurfacePrivate::updateFrameMargins()
+{
+ send_set_frame_margins(m_frameMargins.left(), m_frameMargins.right(),
+ m_frameMargins.top(), m_frameMargins.bottom());
+}
+
+
+void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_raise(Resource *resource)
+{
+ Q_UNUSED(resource);
+ Q_Q(QWaylandQtShellSurface);
+ emit q->raiseRequested();
+}
+
+void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_lower(Resource *resource)
+{
+ Q_UNUSED(resource);
+ Q_Q(QWaylandQtShellSurface);
+ emit q->lowerRequested();
+}
+
+QT_END_NAMESPACE
diff --git a/src/imports/compositor-extensions/qtshell/qwaylandqtshell.h b/src/imports/compositor-extensions/qtshell/qwaylandqtshell.h
new file mode 100644
index 00000000..c1e6ed41
--- /dev/null
+++ b/src/imports/compositor-extensions/qtshell/qwaylandqtshell.h
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDQTSHELL_H
+#define QWAYLANDQTSHELL_H
+
+#include <QtWaylandCompositor/QWaylandCompositorExtension>
+#include <QtWaylandCompositor/QWaylandSurface>
+#include <QtWaylandCompositor/QWaylandResource>
+#include <QtCore/QSize>
+
+#include <QtWaylandCompositor/QWaylandShellSurface>
+#include <QtWaylandCompositor/qwaylandquickchildren.h>
+
+struct wl_resource;
+struct wl_interface;
+
+QT_BEGIN_NAMESPACE
+
+class QWaylandQtShellPrivate;
+class QWaylandQtShellSurface;
+class QWaylandQtShellChrome;
+
+class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandQtShell : public QWaylandCompositorExtensionTemplate<QWaylandQtShell>
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QWaylandQtShell)
+
+public:
+ QWaylandQtShell();
+ QWaylandQtShell(QWaylandCompositor *compositor);
+
+ void initialize() override;
+
+ static const struct wl_interface *interface();
+ static QByteArray interfaceName();
+
+ void registerChrome(QWaylandQtShellChrome *chrome);
+ void unregisterChrome(QWaylandQtShellChrome *chrome);
+
+private Q_SLOTS:
+ void chromeActivated();
+ void chromeDeactivated();
+
+Q_SIGNALS:
+ void qtShellSurfaceRequested(QWaylandSurface *surface, const QWaylandResource &resource);
+ void qtShellSurfaceCreated(QWaylandQtShellSurface *qtShellSurface);
+
+private:
+ bool moveChromeToFront(QWaylandQtShellChrome *chrome);
+};
+
+
+class QWaylandQtShellSurfacePrivate;
+class QWaylandSurfaceRole;
+class QWaylandResource;
+
+class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandQtShellSurface : public QWaylandShellSurfaceTemplate<QWaylandQtShellSurface>
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QWaylandQtShellSurface)
+ Q_WAYLAND_COMPOSITOR_DECLARE_QUICK_CHILDREN(QWaylandQtShellSurface)
+ Q_PROPERTY(QWaylandSurface *surface READ surface NOTIFY surfaceChanged)
+ Q_PROPERTY(uint windowFlags READ windowFlags NOTIFY windowFlagsChanged)
+ Q_PROPERTY(uint windowState READ windowState NOTIFY windowStateChanged)
+ Q_PROPERTY(QString windowTitle READ windowTitle READ windowTitle NOTIFY windowTitleChanged)
+ Q_PROPERTY(QRect windowGeometry READ windowGeometry NOTIFY windowGeometryChanged)
+ Q_PROPERTY(QPoint windowPosition READ windowPosition WRITE setWindowPosition NOTIFY windowGeometryChanged)
+ Q_PROPERTY(bool positionAutomatic READ positionAutomatic NOTIFY positionAutomaticChanged)
+ Q_PROPERTY(QSize minimumSize READ minimumSize NOTIFY minimumSizeChanged)
+ Q_PROPERTY(QSize maximumSize READ maximumSize NOTIFY maximumSizeChanged)
+ Q_PROPERTY(int frameMarginLeft READ frameMarginLeft WRITE setFrameMarginLeft NOTIFY frameMarginChanged)
+ Q_PROPERTY(int frameMarginRight READ frameMarginRight WRITE setFrameMarginRight NOTIFY frameMarginChanged)
+ Q_PROPERTY(int frameMarginTop READ frameMarginTop WRITE setFrameMarginTop NOTIFY frameMarginChanged)
+ Q_PROPERTY(int frameMarginBottom READ frameMarginBottom WRITE setFrameMarginBottom NOTIFY frameMarginChanged)
+ Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
+ Q_PROPERTY(CapabilityFlags capabilities READ capabilities WRITE setCapabilities NOTIFY capabilitiesChanged)
+ Q_MOC_INCLUDE("qwaylandsurface.h")
+
+public:
+ // Matches the "capabilities" enum in the protocol xml
+ enum CapabilityFlag {
+ InteractiveMove = 1,
+ InteractiveResize = 2
+ };
+ Q_DECLARE_FLAGS(CapabilityFlags, CapabilityFlag)
+ Q_ENUM(CapabilityFlag)
+
+ QWaylandQtShellSurface();
+ QWaylandQtShellSurface(QWaylandQtShell *application, QWaylandSurface *surface, const QWaylandResource &resource);
+
+ void initialize(QWaylandQtShell *qtShell, QWaylandSurface *surface,
+ const QWaylandResource &resource);
+
+ QWaylandSurface *surface() const;
+
+ static const wl_interface *interface();
+ static QByteArray interfaceName();
+ static QWaylandSurfaceRole *role();
+ static QWaylandQtShellSurface *fromResource(::wl_resource *resource);
+
+ QRect windowGeometry() const;
+
+ void setWindowPosition(const QPoint &position);
+ QPoint windowPosition() const;
+
+ Q_INVOKABLE void requestWindowGeometry(uint windowState, const QRect &windowGeometry);
+
+ QSize minimumSize() const;
+ QSize maximumSize() const;
+
+ void setFrameMargins(const QMargins &margins);
+
+ int frameMarginLeft() const;
+ void setFrameMarginLeft(int left);
+
+ int frameMarginRight() const;
+ void setFrameMarginRight(int right);
+
+ int frameMarginTop() const;
+ void setFrameMarginTop(int top);
+
+ int frameMarginBottom() const;
+ void setFrameMarginBottom(int bottom);
+
+ bool positionAutomatic() const;
+
+ bool active() const;
+ void setActive(bool active);
+
+ QString windowTitle() const;
+
+ uint windowFlags() const;
+
+ Q_INVOKABLE void sendClose();
+
+ uint windowState() const;
+ void setWindowState(uint windowState);
+#if QT_CONFIG(wayland_compositor_quick)
+ QWaylandQuickShellIntegration *createIntegration(QWaylandQuickShellSurfaceItem *item) override;
+#endif
+
+ CapabilityFlags capabilities() const;
+ void setCapabilities(CapabilityFlags capabilities);
+
+Q_SIGNALS:
+ void surfaceChanged();
+ void windowFlagsChanged();
+ void windowStateChanged();
+ void windowGeometryChanged();
+ void minimumSizeChanged();
+ void maximumSizeChanged();
+ void positionAutomaticChanged();
+ void startMove();
+ void startResize(Qt::Edges edges);
+ void windowTitleChanged();
+ void frameMarginChanged();
+ void raiseRequested();
+ void lowerRequested();
+ void activeChanged();
+ void capabilitiesChanged();
+
+private Q_SLOTS:
+ void surfaceCommitted();
+
+private:
+ friend class QWaylandQtShellChrome;
+
+ void initialize() override;
+
+ QWaylandQtShell *shell() const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDQTSHELL_H
diff --git a/src/imports/compositor-extensions/qtshell/qwaylandqtshell_p.h b/src/imports/compositor-extensions/qtshell/qwaylandqtshell_p.h
new file mode 100644
index 00000000..7f2a9bb5
--- /dev/null
+++ b/src/imports/compositor-extensions/qtshell/qwaylandqtshell_p.h
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDQTSHELL_P_H
+#define QWAYLANDQTSHELL_P_H
+
+#include <QtWaylandCompositor/private/qwaylandcompositorextension_p.h>
+#include <QtWaylandCompositor/QWaylandSurfaceRole>
+
+#include <QHash>
+
+#include "qwayland-server-qt-shell-unstable-v1.h"
+#include "qwaylandqtshell.h"
+
+//
+// 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
+
+class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandQtShellPrivate
+ : public QWaylandCompositorExtensionPrivate
+ , public QtWaylandServer::zqt_shell_v1
+{
+ Q_DECLARE_PUBLIC(QWaylandQtShell)
+
+public:
+ QWaylandQtShellPrivate();
+ static QWaylandQtShellPrivate *get(QWaylandQtShell *qtShell) { return qtShell->d_func(); }
+ void unregisterQtShellSurface(QWaylandQtShellSurface *qtShellSurface);
+
+ QList<QWaylandQtShellChrome *> m_chromes;
+
+protected:
+ void zqt_shell_v1_surface_create(Resource *resource, wl_resource *surface, uint32_t id) override;
+};
+
+class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandQtShellSurfacePrivate
+ : public QWaylandCompositorExtensionPrivate
+ , public QtWaylandServer::zqt_shell_surface_v1
+{
+ Q_DECLARE_PUBLIC(QWaylandQtShellSurface)
+
+public:
+ QWaylandQtShellSurfacePrivate();
+ static QWaylandQtShellSurfacePrivate *get(QWaylandQtShellSurface *qtShellSurface)
+ {
+ return qtShellSurface->d_func();
+ }
+
+ void updateFrameMargins();
+ void configure(uint windowState, const QRect &newGeometry);
+
+protected:
+ void zqt_shell_surface_v1_destroy_resource(Resource *resource) override;
+ void zqt_shell_surface_v1_destroy(Resource *resource) override;
+ void zqt_shell_surface_v1_reposition(Resource *resource, int32_t x, int32_t y) override;
+ void zqt_shell_surface_v1_set_size(Resource *resource, int32_t width, int32_t height) override;
+ void zqt_shell_surface_v1_set_minimum_size(Resource *resource, int32_t width, int32_t height) override;
+ void zqt_shell_surface_v1_set_maximum_size(Resource *resource, int32_t width, int32_t height) override;
+ void zqt_shell_surface_v1_set_window_title(Resource *resource, const QString &title) override;
+ void zqt_shell_surface_v1_set_window_flags(Resource *resource, uint32_t flags) override;
+ void zqt_shell_surface_v1_change_window_state(Resource *resource, uint32_t state) override;
+ void zqt_shell_surface_v1_ack_configure(Resource *resource, uint32_t serial) override;
+
+ void zqt_shell_surface_v1_start_system_resize(Resource *resource, uint32_t serial, uint32_t edge) override;
+ void zqt_shell_surface_v1_start_system_move(Resource *resource, uint32_t serial) override;
+
+ void zqt_shell_surface_v1_raise(Resource *resource) override;
+ void zqt_shell_surface_v1_lower(Resource *resource) override;
+
+ void zqt_shell_surface_v1_request_activate(Resource *resource) override;
+
+private:
+ QWaylandQtShell *m_qtShell = nullptr;
+ QWaylandSurface *m_surface = nullptr;
+ QRect m_windowGeometry;
+ QSize m_minimumSize;
+ QSize m_maximumSize;
+ uint m_windowFlags = 0;
+ uint m_windowState = 0;
+ QString m_windowTitle;
+ QMargins m_frameMargins;
+ bool m_positionSet = false;
+ bool m_active = false;
+
+ QPoint m_pendingPosition;
+ bool m_pendingPositionValid = false;
+ QSize m_pendingSize;
+
+ uint32_t m_lastAckedConfigure = UINT32_MAX;
+ QMap<uint32_t, QPair<uint, QRect> > m_pendingConfigures;
+
+ QWaylandQtShellSurface::CapabilityFlags m_capabilities;
+
+ static QWaylandSurfaceRole s_role;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDQTSHELL_P_H
diff --git a/src/imports/compositor-extensions/qtshell/qwaylandqtshellchrome.cpp b/src/imports/compositor-extensions/qtshell/qwaylandqtshellchrome.cpp
new file mode 100644
index 00000000..088321ca
--- /dev/null
+++ b/src/imports/compositor-extensions/qtshell/qwaylandqtshellchrome.cpp
@@ -0,0 +1,1524 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwaylandqtshellchrome.h"
+#include "qwaylandqtshellchrome_p.h"
+#include "qwaylandqtshell.h"
+
+#include <QtWaylandCompositor/qwaylandquickshellsurfaceitem.h>
+
+QT_BEGIN_NAMESPACE
+
+QPointF QWaylandQtShellChromePrivate::constrainPoint(const QPointF &point) const
+{
+ float x0 = maximizedRect.left();
+ float y0 = maximizedRect.top();
+ float x1 = maximizedRect.right();
+ float y1 = maximizedRect.bottom();
+ return QPoint(qBound(x0, point.x(), x1),
+ qBound(y0, point.y(), y1));
+}
+
+void QWaylandQtShellChromePrivate::updateDecorationInteraction(quint8 flags,
+ const QQuickHandlerPoint &centroid)
+{
+ if (shellSurface == nullptr)
+ return;
+
+ if (decorationInteraction == quint8(DecorationInteraction::None)) {
+ decorationInteraction = flags;
+ decorationInteractionPosition = centroid.scenePressPosition();
+ decorationInteractionGeometry = shellSurface->windowGeometry();
+ }
+
+ if (decorationInteraction != flags)
+ return;
+
+ QPointF position = constrainPoint(centroid.scenePosition());
+ float dx = position.x() - decorationInteractionPosition.x();
+ float dy = position.y() - decorationInteractionPosition.y();
+
+ float minWidth = qMax(0, shellSurface->minimumSize().width());
+ float minHeight = qMax(0, shellSurface->minimumSize().height());
+
+ float maxWidth = shellSurface->maximumSize().width();
+ float maxHeight = shellSurface->maximumSize().height();
+
+ float minX = maxWidth >= 0.0f
+ ? decorationInteractionGeometry.right() - maxWidth
+ : -FLT_MAX;
+ float minY = maxHeight >= 0.0f
+ ? decorationInteractionGeometry.bottom() - maxHeight
+ : -FLT_MAX;
+ float maxX = maxWidth >= 0
+ ? decorationInteractionGeometry.left() + maxWidth
+ : FLT_MAX;
+ float maxY = maxHeight >= 0.0f
+ ? decorationInteractionGeometry.top() + maxHeight
+ : FLT_MAX;
+
+ float newLeft = decorationInteractionGeometry.left();
+ if (flags & quint8(DecorationInteraction::WestBound)) {
+ newLeft = qBound(minX,
+ newLeft + dx,
+ float(decorationInteractionGeometry.right() - minWidth));
+ }
+
+ float newTop = decorationInteractionGeometry.top();
+ if (flags & quint8(DecorationInteraction::NorthBound)) {
+ newTop = qBound(minY,
+ newTop + dy,
+ decorationInteractionGeometry.bottom() + minHeight);
+ }
+
+ float newRight = decorationInteractionGeometry.right();
+ if (flags & quint8(DecorationInteraction::EastBound)) {
+ newRight = qBound(decorationInteractionGeometry.left() + minWidth,
+ newRight + dx,
+ maxX);
+ }
+
+ float newBottom = decorationInteractionGeometry.bottom();
+ if (flags & quint8(DecorationInteraction::SouthBound)) {
+ newBottom = qBound(decorationInteractionGeometry.top() + minHeight,
+ newBottom + dy,
+ maxY);
+ }
+
+ shellSurface->requestWindowGeometry(shellSurface->windowState(),
+ QRect(int(newLeft), int(newTop),
+ int(newRight - newLeft), int(newBottom - newTop)));
+}
+
+/*!
+ * \qmltype QtShellChrome
+ * \instantiates QWaylandQtShellChrome
+ * \inqmlmodule QtWayland.Compositor.QtShell
+ * \since 6.3
+ * \brief Provides default window manager functionality for use with the \c qt-shell extension.
+ *
+ * The QtShellChrome is a convenience type that can be used to provide window manager functionality
+ * to the interaction with clients over the \c qt-shell
+ * \l{Shell Extensions - Qt Wayland Compositor}{shell extension protocol}.
+ *
+ * Given a ShellSurfaceItem with an associated QtShellSurface, the item will automatically adapt
+ * its size to match the surface. It will also provide automatic handling of:
+ * \list
+ * \li Window states, such as maximized, minimized and fullscreen.
+ * \li Window activation.
+ * \li Window resizing using with resize handles (if the appropriate properties are set.)
+ * \li Window repositioning using title bar interaction (if the \l titleBar property is set.)
+ * \endlist
+ *
+ * The QtShellChrome is intended to be used together with QtShell and QtShellSurface.
+ *
+ * \sa {Qt Wayland Compositor Examples - QtShell Compositor}
+ */
+QWaylandQtShellChrome::QWaylandQtShellChrome(QQuickItem *parent)
+ : QQuickItem(*new QWaylandQtShellChromePrivate{}, parent)
+{
+ init();
+}
+
+QWaylandQtShellChrome::QWaylandQtShellChrome(QWaylandQtShellChromePrivate &dd,
+ QQuickItem *parent)
+ : QQuickItem(dd, parent)
+{
+ init();
+}
+
+QWaylandQtShellChrome::~QWaylandQtShellChrome()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->shell != nullptr)
+ d->shell->unregisterChrome(this);
+}
+
+void QWaylandQtShellChrome::init()
+{
+ connect(this, &QWaylandQtShellChrome::currentWindowStateChanged,
+ this, &QWaylandQtShellChrome::windowMetaInfoChanged);
+
+ connect(this, &QWaylandQtShellChrome::currentWindowFlagsChanged,
+ this, &QWaylandQtShellChrome::windowMetaInfoChanged);
+
+ connect(this, &QWaylandQtShellChrome::windowMetaInfoChanged,
+ this, &QWaylandQtShellChrome::updateDecorations);
+
+ connect(this, &QWaylandQtShellChrome::leftResizeHandleChanged,
+ this, &QWaylandQtShellChrome::updateDecorations);
+
+ connect(this, &QWaylandQtShellChrome::rightResizeHandleChanged,
+ this, &QWaylandQtShellChrome::updateDecorations);
+
+ connect(this, &QWaylandQtShellChrome::topResizeHandleChanged,
+ this, &QWaylandQtShellChrome::updateDecorations);
+
+ connect(this, &QWaylandQtShellChrome::bottomResizeHandleChanged,
+ this, &QWaylandQtShellChrome::updateDecorations);
+
+ connect(this, &QWaylandQtShellChrome::topLeftResizeHandleChanged,
+ this, &QWaylandQtShellChrome::updateDecorations);
+
+ connect(this, &QWaylandQtShellChrome::bottomLeftResizeHandleChanged,
+ this, &QWaylandQtShellChrome::updateDecorations);
+
+ connect(this, &QWaylandQtShellChrome::topRightResizeHandleChanged,
+ this, &QWaylandQtShellChrome::updateDecorations);
+
+ connect(this, &QWaylandQtShellChrome::bottomRightResizeHandleChanged,
+ this, &QWaylandQtShellChrome::updateDecorations);
+}
+
+/*!
+ * \qmlmethod void QtWaylandCompositor::QtShellChrome::toggleFullScreen()
+ *
+ * Toggles between fullscreen and normal window states. This method also clears the minimized
+ * or maximized window states if either is set.
+ */
+void QWaylandQtShellChrome::toggleFullScreen()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->shellSurface == nullptr)
+ return;
+
+ uint newState;
+ if ((d->shellSurface->windowState() & Qt::WindowFullScreen) == Qt::WindowFullScreen)
+ newState = d->currentState & ~Qt::WindowFullScreen;
+ else
+ newState = d->currentState | Qt::WindowFullScreen;
+
+ if ((newState & (Qt::WindowMinimized | Qt::WindowMaximized)) != 0)
+ newState &= ~(Qt::WindowMinimized | Qt::WindowMaximized);
+
+ setWindowState(newState);
+}
+
+/*!
+ * \qmlmethod void QtWaylandCompositor::QtShellChrome::toggleMaximized()
+ *
+ * Toggles between maximized and normal states. This method also clears the minimized
+ * window state if it is set.
+ */
+void QWaylandQtShellChrome::toggleMaximized()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->shellSurface == nullptr)
+ return;
+
+ uint newState;
+ if ((d->shellSurface->windowState() & Qt::WindowMaximized) == Qt::WindowMaximized)
+ newState = d->currentState & ~Qt::WindowMaximized;
+ else
+ newState = d->currentState | Qt::WindowMaximized;
+
+ if ((newState & Qt::WindowMinimized) == Qt::WindowMinimized)
+ newState &= ~Qt::WindowMinimized;
+
+ setWindowState(newState);
+}
+
+/*!
+ * \qmlmethod void QtWaylandCompositor::QtShellChrome::toggleMinimized()
+ *
+ * Toggles between minimized and normal states. This method also clears the maximized
+ * window state if it is set.
+ */
+void QWaylandQtShellChrome::toggleMinimized()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->shellSurface == nullptr)
+ return;
+
+ uint newState;
+ if ((d->shellSurface->windowState() & Qt::WindowMinimized) == Qt::WindowMinimized)
+ newState = d->currentState & ~Qt::WindowMinimized;
+ else
+ newState = d->currentState | Qt::WindowMinimized;
+
+ if ((newState & Qt::WindowMaximized) == Qt::WindowMaximized)
+ newState &= ~Qt::WindowMaximized;
+
+ setWindowState(newState);
+}
+
+/*!
+ * \qmlproperty ShellSurfaceItem QtWaylandCompositor::QtShellChrome::shellSurfaceItem
+ *
+ * This property holds the shell surface item associated with this QtShellChrome. It will
+ * in turn manage the \c shellSurface of this item. The \c shellSurface of the item is expected to
+ * be of the type QtShellSurface.
+ *
+ * \qml
+ * QtShellChrome {
+ * id: chrome
+ * ShellSurfaceItem {
+ * id: sfi
+ * anchors.fill: parent
+ * moveItem: chrome
+ * }
+ * shellSurfaceItem: sfi
+ * }
+ * \endqml
+ */
+void QWaylandQtShellChrome::setShellSurfaceItem(QWaylandQuickShellSurfaceItem *shellSurfaceItem)
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->shellSurfaceItem == shellSurfaceItem)
+ return;
+
+ if (d->shellSurfaceItem != nullptr)
+ d->shellSurfaceItem->disconnect(this);
+
+ d->shellSurfaceItem = shellSurfaceItem;
+
+ if (d->shellSurfaceItem != nullptr) {
+ connect(d->shellSurfaceItem, &QWaylandQuickShellSurfaceItem::shellSurfaceChanged,
+ this, &QWaylandQtShellChrome::updateShellSurface);
+ connect(d->shellSurfaceItem, &QWaylandQuickShellSurfaceItem::surfaceDestroyed,
+ this, &QWaylandQtShellChrome::clientDestroyed);
+ }
+
+ updateShellSurface();
+ emit shellSurfaceItemChanged();
+}
+
+QWaylandQuickShellSurfaceItem *QWaylandQtShellChrome::shellSurfaceItem() const
+{
+ Q_D(const QWaylandQtShellChrome);
+ return d->shellSurfaceItem;
+}
+
+void QWaylandQtShellChrome::stopGrab()
+{
+ Q_D(QWaylandQtShellChrome);
+ d->decorationInteraction = quint8(QWaylandQtShellChromePrivate::DecorationInteraction::None);
+}
+
+void QWaylandQtShellChrome::leftResize()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (!d->leftResizeHandleHandler->active())
+ return;
+
+ d->updateDecorationInteraction(quint8(QWaylandQtShellChromePrivate::DecorationInteraction::WestBound),
+ d->leftResizeHandleHandler->centroid());
+}
+
+void QWaylandQtShellChrome::rightResize()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (!d->rightResizeHandleHandler->active())
+ return;
+
+ d->updateDecorationInteraction(quint8(QWaylandQtShellChromePrivate::DecorationInteraction::EastBound),
+ d->rightResizeHandleHandler->centroid());
+}
+
+void QWaylandQtShellChrome::topResize()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (!d->topResizeHandleHandler->active())
+ return;
+
+ d->updateDecorationInteraction(quint8(QWaylandQtShellChromePrivate::DecorationInteraction::NorthBound),
+ d->topResizeHandleHandler->centroid());
+}
+
+void QWaylandQtShellChrome::bottomResize()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (!d->bottomResizeHandleHandler->active())
+ return;
+
+ d->updateDecorationInteraction(quint8(QWaylandQtShellChromePrivate::DecorationInteraction::SouthBound),
+ d->bottomResizeHandleHandler->centroid());
+}
+
+void QWaylandQtShellChrome::topLeftResize()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (!d->topLeftResizeHandleHandler->active())
+ return;
+
+ d->updateDecorationInteraction(quint8(QWaylandQtShellChromePrivate::DecorationInteraction::WestBound)
+ | quint8(QWaylandQtShellChromePrivate::DecorationInteraction::NorthBound),
+ d->topLeftResizeHandleHandler->centroid());
+}
+
+void QWaylandQtShellChrome::topRightResize()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (!d->topRightResizeHandleHandler->active())
+ return;
+
+ d->updateDecorationInteraction(quint8(QWaylandQtShellChromePrivate::DecorationInteraction::EastBound)
+ | quint8(QWaylandQtShellChromePrivate::DecorationInteraction::NorthBound),
+ d->topRightResizeHandleHandler->centroid());
+}
+
+void QWaylandQtShellChrome::bottomLeftResize()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (!d->bottomLeftResizeHandleHandler->active())
+ return;
+
+ d->updateDecorationInteraction(quint8(QWaylandQtShellChromePrivate::DecorationInteraction::WestBound)
+ | quint8(QWaylandQtShellChromePrivate::DecorationInteraction::SouthBound),
+ d->bottomLeftResizeHandleHandler->centroid());
+}
+
+void QWaylandQtShellChrome::bottomRightResize()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (!d->bottomRightResizeHandleHandler->active())
+ return;
+
+ d->updateDecorationInteraction(quint8(QWaylandQtShellChromePrivate::DecorationInteraction::EastBound)
+ | quint8(QWaylandQtShellChromePrivate::DecorationInteraction::SouthBound),
+ d->bottomRightResizeHandleHandler->centroid());
+}
+
+void QWaylandQtShellChrome::titleBarMove()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (!d->titleBarHandler->active())
+ return;
+
+ quint8 flags = quint8(QWaylandQtShellChromePrivate::DecorationInteraction::TitleBar);
+ QQuickHandlerPoint centroid = d->titleBarHandler->centroid();
+ if (d->decorationInteraction == quint8(QWaylandQtShellChromePrivate::DecorationInteraction::None)) {
+ d->decorationInteraction = flags;
+ d->decorationInteractionPosition = d->shellSurface->windowPosition() - centroid.scenePressPosition();
+
+ activate();
+ }
+
+ if (d->decorationInteraction != flags)
+ return;
+
+ QPointF position = d->constrainPoint(centroid.scenePosition());
+ d->shellSurface->setWindowPosition((position + d->decorationInteractionPosition).toPoint());
+}
+
+/*!
+ * \qmlproperty Item QtWaylandCompositor::QtShellChrome::titleBar
+ *
+ * This property holds the default title bar item of the QtShellChrome. If set, a \l DragHandler
+ * will be installed on the title bar which moves the window around on user interaction. In
+ * addition, the window will automatically be activated if the title bar is clicked.
+ *
+ * The title bar will automatically hide and show, depending on the window flags and the
+ * window's full screen state.
+ *
+ * \qml
+ * QtShellChrome {
+ * Rectangle {
+ * id: tb
+ * anchors.top: parent.top
+ * anchors.right: parent.right
+ * anchors.left: parent.left
+ * height: 50
+ * color: "black"
+ *
+ * Text {
+ * color: "white"
+ * anchors.centerIn: parent
+ * text: shellSurfaceItem.shellSurface.windowTitle
+ * font.pixelSize: 25
+ * }
+ * }
+ * titleBar: tb
+ * }
+ * \endqml
+ *
+ * \note Unless explicit frame margins are set, the title bar's height will be included in the
+ * window's top frame margin.
+ */
+QQuickItem *QWaylandQtShellChrome::titleBar() const
+{
+ Q_D(const QWaylandQtShellChrome);
+ return d->titleBar;
+}
+
+void QWaylandQtShellChrome::setTitleBar(QQuickItem *item)
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->titleBar == item)
+ return;
+
+ if (d->titleBar != nullptr) {
+ d->titleBar->disconnect(this);
+
+ delete d->titleBarHandler;
+ d->titleBarHandler = nullptr;
+ }
+
+ d->titleBar = item;
+
+ if (d->titleBar != nullptr) {
+ connect(d->titleBar, &QQuickItem::heightChanged,
+ this, &QWaylandQtShellChrome::updateDecorations);
+
+ d->titleBarHandler = new QQuickDragHandler(d->titleBar);
+ d->titleBarHandler->setTarget(nullptr);
+
+ connect(d->titleBarHandler, &QQuickPointerHandler::grabChanged,
+ this, &QWaylandQtShellChrome::stopGrab);
+ connect(d->titleBarHandler, &QQuickPointerHandler::grabChanged,
+ this, &QWaylandQtShellChrome::activateOnGrab);
+ connect(d->titleBarHandler, &QQuickMultiPointHandler::centroidChanged,
+ this, &QWaylandQtShellChrome::titleBarMove);
+ }
+
+ emit titleBarChanged();
+}
+
+/*!
+ * \qmlproperty Item QtWaylandCompositor::QtShellChrome::leftResizeHandle
+ *
+ * This property holds the default left resize handle of the QtShellChrome. If set, a \l DragHandler
+ * will be installed on the resize handle which resizes the window by moving its left edge.
+ *
+ * The handle will automatically hide and show, depending on the window flags and the window's full
+ * screen state.
+ *
+ * \qml
+ * QtShellChrome {
+ * Rectangle {
+ * id: lrh
+ * anchors.left: parent.left
+ * anchors.top: parent.top
+ * anchors.bottom: parent.bottom
+ * width: 5
+ * color: "white"
+ * }
+ * leftResizeHandle: lrh
+ * }
+ * \endqml
+ *
+ * \note Unless explicit frame margins are set, the handle's width will be included in the
+ * window's left frame margin.
+ */
+QQuickItem *QWaylandQtShellChrome::leftResizeHandle() const
+{
+ Q_D(const QWaylandQtShellChrome);
+ return d->leftResizeHandle;
+}
+
+void QWaylandQtShellChrome::setLeftResizeHandle(QQuickItem *item)
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->leftResizeHandle == item)
+ return;
+
+ if (d->leftResizeHandle != nullptr) {
+ d->leftResizeHandle->disconnect(this);
+
+ delete d->leftResizeHandleHandler;
+ d->leftResizeHandleHandler = nullptr;
+ }
+
+ d->leftResizeHandle = item;
+
+ if (d->leftResizeHandle != nullptr) {
+ connect(d->leftResizeHandle, &QQuickItem::widthChanged,
+ this, &QWaylandQtShellChrome::updateDecorations);
+
+ d->leftResizeHandleHandler = new QQuickDragHandler(d->leftResizeHandle);
+ d->leftResizeHandleHandler->setCursorShape(Qt::SizeHorCursor);
+ d->leftResizeHandleHandler->setTarget(nullptr);
+
+ connect(d->leftResizeHandleHandler, &QQuickPointerHandler::grabChanged,
+ this, &QWaylandQtShellChrome::stopGrab);
+ connect(d->leftResizeHandleHandler, &QQuickMultiPointHandler::centroidChanged,
+ this, &QWaylandQtShellChrome::leftResize);
+ }
+
+ emit leftResizeHandleChanged();
+}
+
+/*!
+ * \qmlproperty Item QtWaylandCompositor::QtShellChrome::rightResizeHandle
+ *
+ * This property holds the default right resize handle of the QtShellChrome. If set, a \l DragHandler
+ * will be installed on the resize handle which resizes the window by moving its right edge.
+ *
+ * The handle will automatically hide and show, depending on the window flags and the window's full
+ * screen state.
+ *
+ * \qml
+ * QtShellChrome {
+ * Rectangle {
+ * id: rrh
+ * anchors.right: parent.right
+ * anchors.top: parent.top
+ * anchors.bottom: parent.bottom
+ * width: 5
+ * color: "white"
+ * }
+ * rightResizeHandle: rrh
+ * }
+ * \endqml
+ *
+ * \note Unless explicit frame margins are set, the handle's width will be included in the
+ * window's right frame margin.
+ */
+QQuickItem *QWaylandQtShellChrome::rightResizeHandle() const
+{
+ Q_D(const QWaylandQtShellChrome);
+ return d->rightResizeHandle;
+}
+
+void QWaylandQtShellChrome::setRightResizeHandle(QQuickItem *item)
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->rightResizeHandle == item)
+ return;
+
+ if (d->rightResizeHandle != nullptr) {
+ d->rightResizeHandle->disconnect(this);
+
+ delete d->rightResizeHandleHandler;
+ d->rightResizeHandleHandler = nullptr;
+ }
+
+ d->rightResizeHandle = item;
+
+ if (d->rightResizeHandle != nullptr) {
+ connect(d->rightResizeHandle, &QQuickItem::widthChanged,
+ this, &QWaylandQtShellChrome::updateDecorations);
+
+ d->rightResizeHandleHandler = new QQuickDragHandler(d->rightResizeHandle);
+ d->rightResizeHandleHandler->setCursorShape(Qt::SizeHorCursor);
+ d->rightResizeHandleHandler->setTarget(nullptr);
+
+ connect(d->rightResizeHandleHandler, &QQuickPointerHandler::grabChanged,
+ this, &QWaylandQtShellChrome::stopGrab);
+ connect(d->rightResizeHandleHandler, &QQuickMultiPointHandler::centroidChanged,
+ this, &QWaylandQtShellChrome::rightResize);
+ }
+
+ emit rightResizeHandleChanged();
+}
+
+/*!
+ * \qmlproperty Item QtWaylandCompositor::QtShellChrome::topResizeHandle
+ *
+ * This property holds the default top resize handle of the QtShellChrome. If set, a \l DragHandler
+ * will be installed on the resize handle which resizes the window by moving its top edge.
+ *
+ * The handle will automatically hide and show, depending on the window flags and the window's full
+ * screen state.
+ *
+ * \qml
+ * QtShellChrome {
+ * Rectangle {
+ * id: trh
+ * anchors.top: parent.top
+ * anchors.left: parent.left
+ * anchors.right: parent.right
+ * height: 5
+ * color: "white"
+ * }
+ * topResizeHandle: trh
+ * }
+ * \endqml
+ *
+ * \note Unless explicit frame margins are set, the handle's height will be included in the
+ * window's top frame margin.
+ */
+QQuickItem *QWaylandQtShellChrome::topResizeHandle() const
+{
+ Q_D(const QWaylandQtShellChrome);
+ return d->topResizeHandle;
+}
+
+void QWaylandQtShellChrome::setTopResizeHandle(QQuickItem *item)
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->topResizeHandle == item)
+ return;
+
+ if (d->topResizeHandle != nullptr) {
+ d->topResizeHandle->disconnect(this);
+
+ delete d->topResizeHandleHandler;
+ d->topResizeHandleHandler = nullptr;
+ }
+
+ d->topResizeHandle = item;
+
+ if (d->topResizeHandle != nullptr) {
+ connect(d->topResizeHandle, &QQuickItem::heightChanged,
+ this, &QWaylandQtShellChrome::updateDecorations);
+
+ d->topResizeHandleHandler = new QQuickDragHandler(d->topResizeHandle);
+ d->topResizeHandleHandler->setCursorShape(Qt::SizeVerCursor);
+ d->topResizeHandleHandler->setTarget(nullptr);
+
+ connect(d->topResizeHandleHandler, &QQuickPointerHandler::grabChanged,
+ this, &QWaylandQtShellChrome::stopGrab);
+ connect(d->topResizeHandleHandler, &QQuickMultiPointHandler::centroidChanged,
+ this, &QWaylandQtShellChrome::topResize);
+ }
+
+ emit topResizeHandleChanged();
+}
+
+/*!
+ * \qmlproperty Item QtWaylandCompositor::QtShellChrome::bottomResizeHandle
+ *
+ * This property holds the default bottom resize handle of the QtShellChrome. If set, a \l DragHandler
+ * will be installed on the resize handle which resizes the window by moving its bottom edge.
+ *
+ * The handle will automatically hide and show, depending on the window flags and the window's full
+ * screen state.
+ *
+ * \qml
+ * QtShellChrome {
+ * Rectangle {
+ * id: brh
+ * anchors.bottom: parent.bottom
+ * anchors.left: parent.left
+ * anchors.right: parent.right
+ * height: 5
+ * color: "white"
+ * }
+ * bottomResizeHandle: brh
+ * }
+ * \endqml
+ *
+ * \note Unless explicit frame margins are set, the handle's height will be included in the
+ * window's bottom frame margin.
+ */
+QQuickItem *QWaylandQtShellChrome::bottomResizeHandle() const
+{
+ Q_D(const QWaylandQtShellChrome);
+ return d->bottomResizeHandle;
+}
+
+void QWaylandQtShellChrome::setBottomResizeHandle(QQuickItem *item)
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->bottomResizeHandle == item)
+ return;
+
+ if (d->bottomResizeHandle != nullptr) {
+ d->bottomResizeHandle->disconnect(this);
+
+ delete d->bottomResizeHandleHandler;
+ d->bottomResizeHandleHandler = nullptr;
+ }
+
+ d->bottomResizeHandle = item;
+
+ if (d->bottomResizeHandle != nullptr) {
+ connect(d->bottomResizeHandle, &QQuickItem::heightChanged,
+ this, &QWaylandQtShellChrome::updateDecorations);
+
+ d->bottomResizeHandleHandler = new QQuickDragHandler(d->bottomResizeHandle);
+ d->bottomResizeHandleHandler->setCursorShape(Qt::SizeVerCursor);
+ d->bottomResizeHandleHandler->setTarget(nullptr);
+
+ connect(d->bottomResizeHandleHandler, &QQuickPointerHandler::grabChanged,
+ this, &QWaylandQtShellChrome::stopGrab);
+ connect(d->bottomResizeHandleHandler, &QQuickMultiPointHandler::centroidChanged,
+ this, &QWaylandQtShellChrome::bottomResize);
+
+ }
+
+ emit bottomResizeHandleChanged();
+}
+
+/*!
+ * \qmlproperty Item QtWaylandCompositor::QtShellChrome::topLeftResizeHandle
+ *
+ * This property holds the default top-left resize handle of the QtShellChrome. If set, a \l DragHandler
+ * will be installed on the resize handle which resizes the window by moving its top and left edges
+ * in equal amounts.
+ *
+ * The handle will automatically hide and show, depending on the window flags and the window's full
+ * screen state.
+ *
+ * \qml
+ * QtShellChrome {
+ * Rectangle {
+ * id: tlrh
+ * anchors.top: parent.top
+ * anchors.left: parent.left
+ * height: 5
+ * width: 5
+ * color: "white"
+ * }
+ * topLeftResizeHandle: tlrh
+ * }
+ * \endqml
+ */
+QQuickItem *QWaylandQtShellChrome::topLeftResizeHandle() const
+{
+ Q_D(const QWaylandQtShellChrome);
+ return d->topLeftResizeHandle;
+}
+
+void QWaylandQtShellChrome::setTopLeftResizeHandle(QQuickItem *item)
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->topLeftResizeHandle == item)
+ return;
+
+ if (d->topLeftResizeHandle != nullptr) {
+ delete d->topLeftResizeHandleHandler;
+ d->topLeftResizeHandleHandler = nullptr;
+ }
+
+ d->topLeftResizeHandle = item;
+
+ if (d->topLeftResizeHandle != nullptr) {
+ d->topLeftResizeHandleHandler = new QQuickDragHandler(d->topLeftResizeHandle);
+ d->topLeftResizeHandleHandler->setCursorShape(Qt::SizeFDiagCursor);
+ d->topLeftResizeHandleHandler->setTarget(nullptr);
+
+ connect(d->topLeftResizeHandleHandler, &QQuickPointerHandler::grabChanged,
+ this, &QWaylandQtShellChrome::stopGrab);
+ connect(d->topLeftResizeHandleHandler, &QQuickMultiPointHandler::centroidChanged,
+ this, &QWaylandQtShellChrome::topLeftResize);
+ }
+
+ emit topLeftResizeHandleChanged();
+}
+
+/*!
+ * \qmlproperty Item QtWaylandCompositor::QtShellChrome::bottomLeftResizeHandle
+ *
+ * This property holds the default bottom-left resize handle of the QtShellChrome. If set, a \l DragHandler
+ * will be installed on the resize handle which resizes the window by moving its bottom and left edges
+ * in equal amounts.
+ *
+ * The handle will automatically hide and show, depending on the window flags and the window's full
+ * screen state.
+ *
+ * \qml
+ * QtShellChrome {
+ * Rectangle {
+ * id: blrh
+ * anchors.bottom: parent.bottom
+ * anchors.left: parent.left
+ * height: 5
+ * width: 5
+ * color: "white"
+ * }
+ * bottomLeftResizeHandle: blrh
+ * }
+ * \endqml
+ */
+QQuickItem *QWaylandQtShellChrome::bottomLeftResizeHandle() const
+{
+ Q_D(const QWaylandQtShellChrome);
+ return d->bottomLeftResizeHandle;
+}
+
+void QWaylandQtShellChrome::setBottomLeftResizeHandle(QQuickItem *item)
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->bottomLeftResizeHandle == item)
+ return;
+
+ if (d->bottomLeftResizeHandle != nullptr) {
+ delete d->bottomLeftResizeHandleHandler;
+ d->bottomLeftResizeHandleHandler = nullptr;
+ }
+
+ d->bottomLeftResizeHandle = item;
+
+ if (d->bottomLeftResizeHandle != nullptr) {
+ d->bottomLeftResizeHandleHandler = new QQuickDragHandler(d->bottomLeftResizeHandle);
+ d->bottomLeftResizeHandleHandler->setCursorShape(Qt::SizeBDiagCursor);
+ d->bottomLeftResizeHandleHandler->setTarget(nullptr);
+
+ connect(d->bottomLeftResizeHandleHandler, &QQuickPointerHandler::grabChanged,
+ this, &QWaylandQtShellChrome::stopGrab);
+ connect(d->bottomLeftResizeHandleHandler, &QQuickMultiPointHandler::centroidChanged,
+ this, &QWaylandQtShellChrome::bottomLeftResize);
+ }
+
+ emit bottomLeftResizeHandleChanged();
+}
+
+/*!
+ * \qmlproperty Item QtWaylandCompositor::QtShellChrome::topRightResizeHandle
+ *
+ * This property holds the default top-right resize handle of the QtShellChrome. If set, a \l DragHandler
+ * will be installed on the resize handle which resizes the window by moving its top and right edges
+ * in equal amounts.
+ *
+ * The handle will automatically hide and show, depending on the window flags and the window's full
+ * screen state.
+ *
+ * \qml
+ * QtShellChrome {
+ * Rectangle {
+ * id: trrh
+ * anchors.top: parent.top
+ * anchors.right: parent.right
+ * height: 5
+ * width: 5
+ * color: "white"
+ * }
+ * topRightResizeHandle: trrh
+ * }
+ * \endqml
+ */
+QQuickItem *QWaylandQtShellChrome::topRightResizeHandle() const
+{
+ Q_D(const QWaylandQtShellChrome);
+ return d->topRightResizeHandle;
+}
+
+void QWaylandQtShellChrome::setTopRightResizeHandle(QQuickItem *item)
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->topRightResizeHandle == item)
+ return;
+
+ if (d->topRightResizeHandle != nullptr) {
+ delete d->topRightResizeHandleHandler;
+ d->topRightResizeHandleHandler = nullptr;
+ }
+
+ d->topRightResizeHandle = item;
+
+ if (d->topRightResizeHandle != nullptr) {
+ d->topRightResizeHandleHandler = new QQuickDragHandler(d->topRightResizeHandle);
+ d->topRightResizeHandleHandler->setCursorShape(Qt::SizeBDiagCursor);
+ d->topRightResizeHandleHandler->setTarget(nullptr);
+
+ connect(d->topRightResizeHandleHandler, &QQuickPointerHandler::grabChanged,
+ this, &QWaylandQtShellChrome::stopGrab);
+ connect(d->topRightResizeHandleHandler, &QQuickMultiPointHandler::centroidChanged,
+ this, &QWaylandQtShellChrome::topRightResize);
+ }
+
+ emit topRightResizeHandleChanged();
+}
+
+/*!
+ * \qmlproperty Item QtWaylandCompositor::QtShellChrome::bottomRightResizeHandle
+ *
+ * This property holds the default bottom-right resize handle of the QtShellChrome. If set, a \l DragHandler
+ * will be installed on the resize handle which resizes the window by moving its bottom and right edges
+ * in equal amounts.
+ *
+ * The handle will automatically hide and show, depending on the window flags and the window's full
+ * screen state.
+ *
+ * \qml
+ * QtShellChrome {
+ * Rectangle {
+ * id: brrh
+ * anchors.bottom: parent.bottom
+ * anchors.right: parent.right
+ * height: 5
+ * width: 5
+ * color: "white"
+ * }
+ * bottomRightResizeHandle: brrh
+ * }
+ * \endqml
+ */
+QQuickItem *QWaylandQtShellChrome::bottomRightResizeHandle() const
+{
+ Q_D(const QWaylandQtShellChrome);
+ return d->bottomRightResizeHandle;
+}
+
+void QWaylandQtShellChrome::setBottomRightResizeHandle(QQuickItem *item)
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->bottomRightResizeHandle == item)
+ return;
+
+ if (d->bottomRightResizeHandle != nullptr) {
+ delete d->bottomRightResizeHandleHandler;
+ d->bottomRightResizeHandleHandler = nullptr;
+ }
+
+ d->bottomRightResizeHandle = item;
+
+ if (d->bottomRightResizeHandle != nullptr) {
+ d->bottomRightResizeHandleHandler = new QQuickDragHandler(d->bottomRightResizeHandle);
+ d->bottomRightResizeHandleHandler->setCursorShape(Qt::SizeFDiagCursor);
+ d->bottomRightResizeHandleHandler->setTarget(nullptr);
+
+ connect(d->bottomRightResizeHandleHandler, &QQuickPointerHandler::grabChanged,
+ this, &QWaylandQtShellChrome::stopGrab);
+ connect(d->bottomRightResizeHandleHandler, &QQuickMultiPointHandler::centroidChanged,
+ this, &QWaylandQtShellChrome::bottomRightResize);
+ }
+
+ emit bottomRightResizeHandleChanged();
+}
+
+/*!
+ * \qmlproperty rect QtWaylandCompositor::QtShellChrome::maximizedRect
+ *
+ * This property holds the are of the WaylandOutput which is available to be filled by the
+ * window when it is in maximized state. By default, the window will fill the entire geometry
+ * of the WaylandOutput when it is maximized. Changing it can be useful for example when the
+ * compositor has other system UI which should not be obscured by maximized applications, such as
+ * a task bar.
+ */
+void QWaylandQtShellChrome::setMaximizedRect(const QRect &rect)
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->maximizedRect == rect)
+ return;
+
+ d->maximizedRect = rect;
+ emit maximizedRectChanged();
+}
+
+QRect QWaylandQtShellChrome::maximizedRect() const
+{
+ Q_D(const QWaylandQtShellChrome);
+ if (d->maximizedRect.isValid())
+ return d->maximizedRect;
+ else if (d->shellSurfaceItem != nullptr && d->shellSurfaceItem->output() != nullptr)
+ return d->shellSurfaceItem->output()->geometry();
+
+ return QRect{};
+}
+
+void QWaylandQtShellChrome::updateDecorations()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->shellSurface == nullptr)
+ return;
+
+ bool decorations = hasDecorations();
+ bool titleBarShowing = hasTitleBar();
+
+ QMargins margins;
+ if (d->automaticFrameMargins) {
+ if (d->leftResizeHandle != nullptr && decorations)
+ margins.setLeft(d->leftResizeHandle->width());
+ if (d->rightResizeHandle != nullptr && decorations)
+ margins.setRight(d->rightResizeHandle->width());
+ if (d->bottomResizeHandle != nullptr && decorations)
+ margins.setBottom(d->bottomResizeHandle->height());
+
+ margins.setTop((decorations && d->topResizeHandle != nullptr ? d->topResizeHandle->height() : 0)
+ + (titleBarShowing && d->titleBar != nullptr ? d->titleBar->height() : 0));
+ } else {
+ margins = d->explicitFrameMargins;
+ }
+ d->shellSurface->setFrameMargins(margins);
+
+ if (d->titleBar != nullptr)
+ d->titleBar->setVisible(titleBarShowing);
+ if (d->leftResizeHandle != nullptr)
+ d->leftResizeHandle->setVisible(decorations);
+ if (d->rightResizeHandle != nullptr)
+ d->rightResizeHandle->setVisible(decorations);
+ if (d->topResizeHandle != nullptr)
+ d->topResizeHandle->setVisible(decorations);
+ if (d->bottomResizeHandle != nullptr)
+ d->bottomResizeHandle->setVisible(decorations);
+ if (d->bottomLeftResizeHandle != nullptr)
+ d->bottomLeftResizeHandle->setVisible(decorations);
+ if (d->topLeftResizeHandle != nullptr)
+ d->topLeftResizeHandle->setVisible(decorations);
+ if (d->bottomRightResizeHandle != nullptr)
+ d->bottomRightResizeHandle->setVisible(decorations);
+ if (d->topRightResizeHandle != nullptr)
+ d->topRightResizeHandle->setVisible(decorations);
+
+ bool minimizedOrMaximized = (d->currentState & (Qt::WindowMaximized|Qt::WindowMinimized)) != 0;
+ if (d->leftResizeHandleHandler != nullptr)
+ d->leftResizeHandleHandler->setEnabled(decorations && !minimizedOrMaximized);
+ if (d->rightResizeHandleHandler != nullptr)
+ d->rightResizeHandleHandler->setEnabled(decorations && !minimizedOrMaximized);
+ if (d->bottomResizeHandleHandler != nullptr)
+ d->bottomResizeHandleHandler->setEnabled(decorations && !minimizedOrMaximized);
+ if (d->topResizeHandleHandler != nullptr)
+ d->topResizeHandleHandler->setEnabled(decorations && !minimizedOrMaximized);
+ if (d->bottomLeftResizeHandleHandler != nullptr)
+ d->bottomLeftResizeHandleHandler->setEnabled(decorations && !minimizedOrMaximized);
+ if (d->bottomRightResizeHandleHandler != nullptr)
+ d->bottomRightResizeHandleHandler->setEnabled(decorations && !minimizedOrMaximized);
+ if (d->topLeftResizeHandleHandler != nullptr)
+ d->topLeftResizeHandleHandler->setEnabled(decorations && !minimizedOrMaximized);
+ if (d->topRightResizeHandleHandler != nullptr)
+ d->topRightResizeHandleHandler->setEnabled(decorations && !minimizedOrMaximized);
+ if (d->titleBarHandler != nullptr)
+ d->titleBarHandler->setEnabled(titleBarShowing && !minimizedOrMaximized);
+}
+
+void QWaylandQtShellChrome::updateGeometry()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->shellSurface == nullptr)
+ return;
+
+ QRect windowGeometry = d->shellSurface->windowGeometry();
+
+ QPointF position = windowGeometry.topLeft();
+ position.rx() -= d->shellSurface->frameMarginLeft();
+ position.ry() -= d->shellSurface->frameMarginTop();
+
+ QSizeF size = windowGeometry.size();
+ size.rwidth() += d->shellSurface->frameMarginLeft() + d->shellSurface->frameMarginRight();
+ size.rheight() += d->shellSurface->frameMarginTop() + d->shellSurface->frameMarginBottom();
+
+ setPosition(position);
+ setSize(size);
+}
+
+void QWaylandQtShellChrome::updateSurface()
+{
+ Q_D(QWaylandQtShellChrome);
+ QWaylandSurface *surface = d->shellSurface != nullptr ? d->shellSurface->surface() : nullptr;
+ if (d->surface == surface)
+ return;
+
+ if (d->surface != nullptr)
+ d->surface->disconnect(this);
+
+ d->surface = surface;
+
+ if (d->surface != nullptr) {
+ connect(d->surface, &QWaylandSurface::hasContentChanged,
+ this, &QWaylandQtShellChrome::updateAutomaticPosition);
+ }
+}
+
+void QWaylandQtShellChrome::updateShellSurface()
+{
+ Q_D(QWaylandQtShellChrome);
+ QWaylandQtShellSurface *sf = d->shellSurfaceItem != nullptr
+ ? qobject_cast<QWaylandQtShellSurface *>(d->shellSurfaceItem->shellSurface())
+ : nullptr;
+ if (d->shellSurface == sf)
+ return;
+
+ if (d->shellSurface != nullptr) {
+ d->shellSurface->disconnect(this);
+ if (d->shell != nullptr)
+ d->shell->unregisterChrome(this);
+ d->shell = nullptr;
+ }
+
+ d->shellSurface = sf;
+ if (d->shellSurface != nullptr) {
+ d->shell = d->shellSurface->shell();
+ if (d->shell != nullptr)
+ d->shell->registerChrome(this);
+
+ updateWindowFlags();
+ connect(d->shellSurface, &QWaylandQtShellSurface::windowFlagsChanged,
+ this, &QWaylandQtShellChrome::updateWindowFlags);
+ connect(d->shellSurface, &QWaylandQtShellSurface::windowStateChanged,
+ this, &QWaylandQtShellChrome::updateWindowState);
+ connect(d->shellSurface, &QWaylandQtShellSurface::frameMarginChanged,
+ this, &QWaylandQtShellChrome::updateGeometry);
+ connect(d->shellSurface, &QWaylandQtShellSurface::windowGeometryChanged,
+ this, &QWaylandQtShellChrome::updateGeometry);
+ connect(d->shellSurface, &QWaylandQtShellSurface::raiseRequested,
+ this, &QWaylandQtShellChrome::raise);
+ connect(d->shellSurface, &QWaylandQtShellSurface::lowerRequested,
+ this, &QWaylandQtShellChrome::lower);
+ connect(d->shellSurface, &QWaylandQtShellSurface::activeChanged,
+ this, &QWaylandQtShellChrome::updateActiveState);
+ connect(d->shellSurface, &QWaylandQtShellSurface::surfaceChanged,
+ this, &QWaylandQtShellChrome::updateSurface);
+ }
+
+ updateDecorations();
+ updateSurface();
+}
+
+void QWaylandQtShellChrome::updateWindowState()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->shellSurface == nullptr)
+ return;
+
+ setWindowState(d->shellSurface->windowState());
+}
+
+void QWaylandQtShellChrome::updateWindowFlags()
+{
+ Q_D(QWaylandQtShellChrome);
+
+ uint nextFlags = d->shellSurface == nullptr || d->shellSurface->windowFlags() == Qt::Window
+ ? d->defaultFlags
+ : d->shellSurface->windowFlags();
+
+ if (d->currentFlags != nextFlags) {
+ d->currentFlags = nextFlags;
+ emit currentWindowFlagsChanged();
+ }
+}
+
+/*!
+ * \qmlproperty int QtWaylandCompositor::QtShellChrome::windowFlags
+ *
+ * This property holds the window flags of the QtShellChrome. They will match the \c windowFlags
+ * property of the associated QtShellSurface, except when this is equal to Qt.Window. In this case,
+ * a set of default window flags will be used instead. The default window flags are Qt.Window,
+ * Qt.WindowMaximizeButtonHint, Qt.WindowMinimizeButtonHint and Qt.WindowCloseButtonHint.
+ */
+uint QWaylandQtShellChrome::currentWindowFlags() const
+{
+ Q_D(const QWaylandQtShellChrome);
+ return d->currentFlags;
+}
+
+/*!
+ * \qmlproperty int QtWaylandCompositor::QtShellChrome::windowState
+ *
+ * This property holds the window state of the shell surface. It will be updated immediately when
+ * the window state is requested on the compositor-side, before this has been acknowledged by the
+ * client. Therefore, it may in brief periods differ from the shell surface's \c windowState
+ * property, which will be updated when the client has acknowledged the request.
+ */
+uint QWaylandQtShellChrome::currentWindowState() const
+{
+ Q_D(const QWaylandQtShellChrome);
+ return d->currentState;
+}
+
+bool QWaylandQtShellChrome::hasTitleBar() const
+{
+ Q_D(const QWaylandQtShellChrome);
+
+ bool frameless = (d->currentFlags & Qt::FramelessWindowHint) == Qt::FramelessWindowHint
+ || ((d->currentFlags & Qt::Popup) == Qt::Popup
+ && (d->currentFlags & Qt::Tool) != Qt::Tool)
+ || (d->currentState & Qt::WindowFullScreen) == Qt::WindowFullScreen;
+ return !frameless;
+}
+
+/*!
+ * \qmlproperty bool QtWaylandCompositor::QtShellChrome::hasDecorations
+ *
+ * This property is true if the QtShellChrome's decorations should be visible, based on its window
+ * state and window flags.
+ */
+bool QWaylandQtShellChrome::hasDecorations() const
+{
+ Q_D(const QWaylandQtShellChrome);
+
+ return hasTitleBar() && (d->currentFlags & Qt::Window) == Qt::Window;
+}
+
+QRect QWaylandQtShellChrome::maxContentRect() const
+{
+ Q_D(const QWaylandQtShellChrome);
+ if (d->shellSurface == nullptr)
+ return QRect{};
+
+ int x0 = d->maximizedRect.x() + d->shellSurface->frameMarginLeft();
+ int x1 = d->maximizedRect.x() + d->maximizedRect.width() - d->shellSurface->frameMarginRight();
+ int y0 = d->maximizedRect.y() + d->shellSurface->frameMarginTop();
+ int y1 = d->maximizedRect.y() + d->maximizedRect.height() - d->shellSurface->frameMarginBottom();
+
+ return QRect(x0, y0, x1 - x0, y1 - y0);
+}
+
+static int randomPos(int windowSize, int screenSize)
+{
+ return (windowSize >= screenSize) ? 0 : rand() % (screenSize - windowSize);
+}
+
+void QWaylandQtShellChrome::setWindowState(uint nextState)
+{
+ Q_D(QWaylandQtShellChrome);
+
+ if (d->currentState == nextState)
+ return;
+
+ if (d->shellSurface == nullptr || d->shellSurfaceItem == nullptr)
+ return;
+
+ QWaylandOutput *output = d->shellSurfaceItem->output();
+ if (output == nullptr)
+ return;
+
+ if ((d->currentState & (Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen)) == 0) {
+ d->restoreGeometry = d->shellSurface->windowGeometry();
+ }
+
+ d->currentState = nextState;
+ emit currentWindowStateChanged();
+
+ if ((nextState & Qt::WindowMinimized) != 0) {
+ d->shellSurface->requestWindowGeometry(nextState, QRect(0, 0, 1, 1));
+ d->shellSurfaceItem->setVisible(false);
+ deactivate();
+ } else if ((nextState & Qt::WindowFullScreen) != 0) {
+ d->shellSurfaceItem->setVisible(true);
+ d->shellSurface->requestWindowGeometry(nextState, QRect(QPoint(0, 0), output->window()->size()));
+ activate();
+ } else if ((nextState & Qt::WindowMaximized) != 0) {
+ d->shellSurfaceItem->setVisible(true);
+ d->shellSurface->requestWindowGeometry(nextState, maxContentRect());
+ activate();
+ } else {
+ d->shellSurfaceItem->setVisible(true);
+ d->shellSurface->requestWindowGeometry(nextState, d->restoreGeometry);
+ activate();
+ }
+}
+
+void QWaylandQtShellChrome::updateAutomaticPosition()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (!d->positionSet && d->shellSurface != nullptr) {
+ bool randomize = d->shellSurface->positionAutomatic();
+ QRect rect = d->shellSurface->windowGeometry();
+ QRect space = maxContentRect();
+
+ int xpos = randomize ? randomPos(rect.width(), space.width()) + space.x()
+ : qMax(rect.x(), space.x());
+ int ypos = randomize ? randomPos(rect.height(), space.height()) + space.y()
+ : qMax(rect.y(), space.y());
+
+ d->shellSurface->setWindowPosition(QPoint(xpos, ypos));
+ d->positionSet = true;
+ }
+}
+
+/*!
+ * \qmlmethod void QtWaylandCompositor::QtShellChrome::deactivate()
+ *
+ * Manually deactivates this window. If the window was active, this will activate the next window in
+ * the stack instead.
+ */
+void QWaylandQtShellChrome::deactivate()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->shellSurface != nullptr)
+ d->shellSurface->setActive(false);
+}
+
+void QWaylandQtShellChrome::activateOnGrab(QPointingDevice::GrabTransition transition)
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->titleBarHandler != nullptr) {
+ switch (transition) {
+ case QPointingDevice::GrabPassive:
+ case QPointingDevice::OverrideGrabPassive:
+ case QPointingDevice::GrabExclusive:
+ activate();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/*!
+ * \qmlmethod void QtWaylandCompositor::QtShellChrome::activate()
+ *
+ * Manually activate this window. This will also raise the window.
+ *
+ * \sa raise()
+ */
+void QWaylandQtShellChrome::activate()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->shellSurface != nullptr)
+ d->shellSurface->setActive(true);
+ raise();
+}
+
+/*!
+ * \qmlmethod void QtWaylandCompositor::QtShellChrome::raise()
+ *
+ * Raise this window, so that it stacks on top of other windows (except if the other window's
+ * flags prohibit this.)
+ */
+void QWaylandQtShellChrome::raise()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->shellSurfaceItem != nullptr)
+ d->shellSurfaceItem->raise();
+}
+
+/*!
+ * \qmlmethod void QtWaylandCompositor::QtShellChrome::lower()
+ *
+ * Lower this window, so that it stacks underneath other windows (except if the other window's
+ * window flags prohibit this.)
+ */
+void QWaylandQtShellChrome::lower()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->shellSurfaceItem != nullptr)
+ d->shellSurfaceItem->lower();
+}
+
+void QWaylandQtShellChrome::updateActiveState()
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->shellSurface == nullptr)
+ return;
+
+ if (d->shellSurface->active()) {
+ raise();
+ emit activated();
+ } else {
+ emit deactivated();
+ }
+}
+
+/*!
+ * \qmlproperty int QtWaylandCompositor::QtShellChrome::frameMarginLeft
+ *
+ * Sets the size of the left margin of the QtShellChrome which is reserved for window decorations.
+ * By default, this will equal the width of the \l leftResizeHandle if it is set. Otherwise it will
+ * be 0.
+ *
+ * \note By setting this property explicitly, all default frame margins will be overridden with
+ * their corresponding properties.
+ */
+void QWaylandQtShellChrome::setFrameMarginLeft(int left)
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->explicitFrameMargins.left() == left)
+ return;
+
+ d->explicitFrameMargins.setLeft(left);
+ d->automaticFrameMargins = false;
+ updateDecorations();
+
+ emit frameMarginChanged();
+}
+
+int QWaylandQtShellChrome::frameMarginLeft() const
+{
+ Q_D(const QWaylandQtShellChrome);
+ if (d->shellSurface == nullptr)
+ return 0;
+ return d->shellSurface->frameMarginLeft();
+}
+
+/*!
+ * \qmlproperty int QtWaylandCompositor::QtShellChrome::frameMarginRight
+ *
+ * Sets the size of the right margin of the QtShellChrome which is reserved for window decorations.
+ * By default, this will equal the width of the \l rightResizeHandle if it is set. Otherwise it will
+ * be 0.
+ *
+ * \note By setting this property explicitly, all default frame margins will be overridden with
+ * their corresponding properties.
+ */
+void QWaylandQtShellChrome::setFrameMarginRight(int right)
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->explicitFrameMargins.right() == right)
+ return;
+
+ d->explicitFrameMargins.setRight(right);
+ d->automaticFrameMargins = false;
+ updateDecorations();
+
+ emit frameMarginChanged();
+}
+
+int QWaylandQtShellChrome::frameMarginRight() const
+{
+ Q_D(const QWaylandQtShellChrome);
+ if (d->shellSurface == nullptr)
+ return 0;
+ return d->shellSurface->frameMarginRight();
+}
+
+/*!
+ * \qmlproperty int QtWaylandCompositor::QtShellChrome::frameMarginTop
+ *
+ * Sets the size of the top margin of the QtShellChrome which is reserved for window decorations.
+ * By default, this will equal the sum of the \l leftResizeHandle and the \l{titleBar}'s heights,
+ * if they are set. Otherwise it will be 0.
+ *
+ * \note By setting this property explicitly, all default frame margins will be overridden with
+ * their corresponding properties.
+ */
+void QWaylandQtShellChrome::setFrameMarginTop(int top)
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->explicitFrameMargins.top() == top)
+ return;
+ d->explicitFrameMargins.setTop(top);
+ d->automaticFrameMargins = false;
+ updateDecorations();
+
+ emit frameMarginChanged();
+}
+
+int QWaylandQtShellChrome::frameMarginTop() const
+{
+ Q_D(const QWaylandQtShellChrome);
+ if (d->shellSurface == nullptr)
+ return 0;
+ return d->shellSurface->frameMarginTop();
+}
+
+/*!
+ * \qmlproperty int QtWaylandCompositor::QtShellChrome::frameMarginBottom
+ *
+ * Sets the size of the bottom margin of the QtShellChrome which is reserved for window decorations.
+ * By default, this will equal the height of the \l bottomResizeHandle if it is set. Otherwise it will
+ * be 0.
+ *
+ * \note By setting this property explicitly, all default frame margins will be overridden with
+ * their corresponding properties.
+ */
+void QWaylandQtShellChrome::setFrameMarginBottom(int bottom)
+{
+ Q_D(QWaylandQtShellChrome);
+ if (d->explicitFrameMargins.bottom() == bottom)
+ return;
+ d->explicitFrameMargins.setBottom(bottom);
+ d->automaticFrameMargins = false;
+ updateDecorations();
+
+ emit frameMarginChanged();
+}
+
+int QWaylandQtShellChrome::frameMarginBottom() const
+{
+ Q_D(const QWaylandQtShellChrome);
+ if (d->shellSurface == nullptr)
+ return 0;
+ return d->shellSurface->frameMarginBottom();
+}
+
+QT_END_NAMESPACE
diff --git a/src/imports/compositor-extensions/qtshell/qwaylandqtshellchrome.h b/src/imports/compositor-extensions/qtshell/qwaylandqtshellchrome.h
new file mode 100644
index 00000000..b09f97eb
--- /dev/null
+++ b/src/imports/compositor-extensions/qtshell/qwaylandqtshellchrome.h
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDQTSHELLCHROME_H
+#define QWAYLANDQTSHELLCHROME_H
+
+#include <QtQuick/qquickitem.h>
+#include <QtWaylandCompositor/qwaylandquickshellsurfaceitem.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWaylandQtShellChromePrivate;
+class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandQtShellChrome : public QQuickItem
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QWaylandQtShellChrome)
+ Q_PROPERTY(bool hasDecorations READ hasDecorations NOTIFY windowMetaInfoChanged)
+ Q_PROPERTY(uint windowState READ currentWindowState NOTIFY currentWindowStateChanged)
+ Q_PROPERTY(uint windowFlags READ currentWindowFlags NOTIFY currentWindowFlagsChanged)
+ Q_PROPERTY(QWaylandQuickShellSurfaceItem *shellSurfaceItem READ shellSurfaceItem WRITE setShellSurfaceItem NOTIFY shellSurfaceItemChanged)
+ Q_PROPERTY(QRect maximizedRect READ maximizedRect WRITE setMaximizedRect NOTIFY maximizedRectChanged)
+
+ Q_PROPERTY(int frameMarginLeft READ frameMarginLeft WRITE setFrameMarginLeft NOTIFY frameMarginChanged)
+ Q_PROPERTY(int frameMarginRight READ frameMarginRight WRITE setFrameMarginRight NOTIFY frameMarginChanged)
+ Q_PROPERTY(int frameMarginTop READ frameMarginTop WRITE setFrameMarginTop NOTIFY frameMarginChanged)
+ Q_PROPERTY(int frameMarginBottom READ frameMarginBottom WRITE setFrameMarginBottom NOTIFY frameMarginChanged)
+
+ Q_PROPERTY(QQuickItem *titleBar READ titleBar WRITE setTitleBar NOTIFY titleBarChanged);
+ Q_PROPERTY(QQuickItem *leftResizeHandle READ leftResizeHandle WRITE setLeftResizeHandle NOTIFY leftResizeHandleChanged);
+ Q_PROPERTY(QQuickItem *rightResizeHandle READ rightResizeHandle WRITE setRightResizeHandle NOTIFY rightResizeHandleChanged);
+ Q_PROPERTY(QQuickItem *topResizeHandle READ topResizeHandle WRITE setTopResizeHandle NOTIFY topResizeHandleChanged);
+ Q_PROPERTY(QQuickItem *bottomResizeHandle READ bottomResizeHandle WRITE setBottomResizeHandle NOTIFY bottomResizeHandleChanged);
+ Q_PROPERTY(QQuickItem *topLeftResizeHandle READ topLeftResizeHandle WRITE setTopLeftResizeHandle NOTIFY topLeftResizeHandleChanged);
+ Q_PROPERTY(QQuickItem *topRightResizeHandle READ topRightResizeHandle WRITE setTopRightResizeHandle NOTIFY topRightResizeHandleChanged);
+ Q_PROPERTY(QQuickItem *bottomLeftResizeHandle READ bottomLeftResizeHandle WRITE setBottomLeftResizeHandle NOTIFY bottomLeftResizeHandleChanged);
+ Q_PROPERTY(QQuickItem *bottomRightResizeHandle READ bottomRightResizeHandle WRITE setBottomRightResizeHandle NOTIFY bottomRightResizeHandleChanged);
+public:
+ QWaylandQtShellChrome(QQuickItem *parent = nullptr);
+ ~QWaylandQtShellChrome() override;
+
+ bool hasTitleBar() const;
+ bool hasDecorations() const;
+ uint currentWindowState() const;
+ uint currentWindowFlags() const;
+
+ void setMaximizedRect(const QRect &rect);
+ QRect maximizedRect() const;
+
+ void setShellSurfaceItem(QWaylandQuickShellSurfaceItem *shellSurfaceItem);
+ QWaylandQuickShellSurfaceItem *shellSurfaceItem() const;
+
+ void setTitleBar(QQuickItem *item);
+ QQuickItem *titleBar() const;
+
+ void setLeftResizeHandle(QQuickItem *item);
+ QQuickItem *leftResizeHandle() const;
+
+ void setRightResizeHandle(QQuickItem *item);
+ QQuickItem *rightResizeHandle() const;
+
+ void setTopResizeHandle(QQuickItem *item);
+ QQuickItem *topResizeHandle() const;
+
+ void setBottomResizeHandle(QQuickItem *item);
+ QQuickItem *bottomResizeHandle() const;
+
+ void setTopLeftResizeHandle(QQuickItem *item);
+ QQuickItem *topLeftResizeHandle() const;
+
+ void setBottomLeftResizeHandle(QQuickItem *item);
+ QQuickItem *bottomLeftResizeHandle() const;
+
+ void setTopRightResizeHandle(QQuickItem *item);
+ QQuickItem *topRightResizeHandle() const;
+
+ void setBottomRightResizeHandle(QQuickItem *item);
+ QQuickItem *bottomRightResizeHandle() const;
+
+ int frameMarginLeft() const;
+ void setFrameMarginLeft(int left);
+
+ int frameMarginRight() const;
+ void setFrameMarginRight(int right);
+
+ int frameMarginTop() const;
+ void setFrameMarginTop(int top);
+
+ int frameMarginBottom() const;
+ void setFrameMarginBottom(int bottom);
+
+Q_SIGNALS:
+ void currentWindowStateChanged();
+ void currentWindowFlagsChanged();
+ void windowMetaInfoChanged();
+ void shellSurfaceItemChanged();
+ void maximizedRectChanged();
+
+ void titleBarChanged();
+ void leftResizeHandleChanged();
+ void rightResizeHandleChanged();
+ void topResizeHandleChanged();
+ void bottomResizeHandleChanged();
+ void topLeftResizeHandleChanged();
+ void bottomLeftResizeHandleChanged();
+ void topRightResizeHandleChanged();
+ void bottomRightResizeHandleChanged();
+
+ void activated();
+ void deactivated();
+
+ void clientDestroyed();
+ void frameMarginChanged();
+
+public Q_SLOTS:
+ void raise();
+ void lower();
+ void toggleMaximized();
+ void toggleMinimized();
+ void toggleFullScreen();
+ void activate();
+ void deactivate();
+
+private Q_SLOTS:
+ void activateOnGrab(QPointingDevice::GrabTransition transition);
+ void updateSurface();
+ void updateShellSurface();
+ void updateWindowFlags();
+ void updateWindowState();
+ void updateGeometry();
+ void updateDecorations();
+ void updateActiveState();
+ void updateAutomaticPosition();
+ void stopGrab();
+ void leftResize();
+ void rightResize();
+ void topResize();
+ void bottomResize();
+ void topLeftResize();
+ void topRightResize();
+ void bottomLeftResize();
+ void bottomRightResize();
+ void titleBarMove();
+
+protected:
+ QWaylandQtShellChrome(QWaylandQtShellChromePrivate &dd, QQuickItem *parent);
+
+private:
+ void setWindowState(uint nextState);
+ void init();
+ QRect maxContentRect() const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDQTSHELLSURFACEITEM_H
diff --git a/src/imports/compositor-extensions/qtshell/qwaylandqtshellchrome_p.h b/src/imports/compositor-extensions/qtshell/qwaylandqtshellchrome_p.h
new file mode 100644
index 00000000..fd2996a0
--- /dev/null
+++ b/src/imports/compositor-extensions/qtshell/qwaylandqtshellchrome_p.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDQTSHELLCHROME_P_H
+#define QWAYLANDQTSHELLCHROME_P_H
+
+#include "qwaylandqtshell.h"
+
+#include <QtCore/qpointer.h>
+#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qquickdraghandler_p.h>
+
+#include <QtWaylandCompositor/qwaylandquickshellsurfaceitem.h>
+
+//
+// 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
+
+class QWaylandQtShellChromePrivate : public QQuickItemPrivate
+{
+public:
+ void updateDecorationInteraction(quint8 flags, const QQuickHandlerPoint &centroid);
+ QPointF constrainPoint(const QPointF &point) const;
+
+ bool positionSet = false;
+ bool automaticFrameMargins = true;
+ QMargins explicitFrameMargins;
+
+ uint currentState = Qt::WindowNoState;
+ uint defaultFlags = Qt::Window
+ | Qt::WindowMaximizeButtonHint
+ | Qt::WindowMinimizeButtonHint
+ | Qt::WindowCloseButtonHint;
+ uint currentFlags = defaultFlags;
+ QRect restoreGeometry = QRect(0, 0, 100, 100);
+ QRect maximizedRect;
+ QPointer<QWaylandQuickShellSurfaceItem> shellSurfaceItem;
+ QPointer<QWaylandQtShellSurface> shellSurface;
+ QPointer<QWaylandSurface> surface;
+ QPointer<QWaylandQtShell> shell;
+
+ enum class DecorationInteraction : quint8 {
+ None = 0,
+ WestBound = 1,
+ EastBound = 2,
+ NorthBound = 4,
+ SouthBound = 8,
+ TitleBar = 16
+ };
+
+ quint8 decorationInteraction = quint8(DecorationInteraction::None);
+ QPointF decorationInteractionPosition;
+ QRect decorationInteractionGeometry;
+
+ QQuickItem *leftResizeHandle = nullptr;
+ QQuickDragHandler *leftResizeHandleHandler = nullptr;
+ QQuickDragHandler *rightResizeHandleHandler = nullptr;
+ QQuickDragHandler *topResizeHandleHandler = nullptr;
+ QQuickDragHandler *bottomResizeHandleHandler = nullptr;
+ QQuickDragHandler *topLeftResizeHandleHandler = nullptr;
+ QQuickDragHandler *topRightResizeHandleHandler = nullptr;
+ QQuickDragHandler *bottomLeftResizeHandleHandler = nullptr;
+ QQuickDragHandler *bottomRightResizeHandleHandler = nullptr;
+ QQuickDragHandler *titleBarHandler = nullptr;
+
+ QQuickItem *rightResizeHandle = nullptr;
+ QQuickItem *topResizeHandle = nullptr;
+ QQuickItem *bottomResizeHandle = nullptr;
+ QQuickItem *topLeftResizeHandle = nullptr;
+ QQuickItem *bottomLeftResizeHandle = nullptr;
+ QQuickItem *topRightResizeHandle = nullptr;
+ QQuickItem *bottomRightResizeHandle = nullptr;
+ QQuickItem *titleBar = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDQTSHELLCHROME_P_H
diff --git a/src/imports/compositor-extensions/qtshell/qwaylandqtshellintegration.cpp b/src/imports/compositor-extensions/qtshell/qwaylandqtshellintegration.cpp
new file mode 100644
index 00000000..acafad04
--- /dev/null
+++ b/src/imports/compositor-extensions/qtshell/qwaylandqtshellintegration.cpp
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwaylandqtshellintegration_p.h"
+
+#include <QtWaylandCompositor/QWaylandCompositor>
+#include <QtWaylandCompositor/QWaylandQuickShellSurfaceItem>
+#include <QtWaylandCompositor/QWaylandSeat>
+#include "qwaylandqtshell.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWayland {
+
+QtShellIntegration::QtShellIntegration(QWaylandQuickShellSurfaceItem *item)
+ : QWaylandQuickShellIntegration(item)
+ , m_item(item)
+ , m_shellSurface(qobject_cast<QWaylandQtShellSurface *>(item->shellSurface()))
+{
+ m_item->setSurface(m_shellSurface->surface());
+ connect(m_shellSurface, &QWaylandQtShellSurface::destroyed,
+ this, &QtShellIntegration::handleQtShellSurfaceDestroyed);
+}
+
+QtShellIntegration::~QtShellIntegration()
+{
+ m_item->setSurface(nullptr);
+}
+
+void QtShellIntegration::handleQtShellSurfaceDestroyed()
+{
+ m_shellSurface = nullptr;
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/imports/compositor-extensions/qtshell/qwaylandqtshellintegration_p.h b/src/imports/compositor-extensions/qtshell/qwaylandqtshellintegration_p.h
new file mode 100644
index 00000000..5937676b
--- /dev/null
+++ b/src/imports/compositor-extensions/qtshell/qwaylandqtshellintegration_p.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDQTSHELLINTEGRATION_H
+#define QWAYLANDQTSHELLINTEGRATION_H
+
+#include <QtWaylandCompositor/private/qwaylandquickshellsurfaceitem_p.h>
+
+#include "qwaylandqtshell.h"
+
+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.
+//
+
+namespace QtWayland {
+
+class QtShellIntegration : public QWaylandQuickShellIntegration
+{
+ Q_OBJECT
+public:
+ QtShellIntegration(QWaylandQuickShellSurfaceItem *item);
+ ~QtShellIntegration() override;
+
+private Q_SLOTS:
+ void handleQtShellSurfaceDestroyed();
+
+private:
+ QWaylandQuickShellSurfaceItem *m_item = nullptr;
+ QWaylandQtShellSurface *m_shellSurface = nullptr;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDQTSHELLINTEGRATION_H
diff --git a/src/imports/compositor-extensions/qtshell/qwaylandqtshellplugin.cpp b/src/imports/compositor-extensions/qtshell/qwaylandqtshellplugin.cpp
new file mode 100644
index 00000000..aead45cc
--- /dev/null
+++ b/src/imports/compositor-extensions/qtshell/qwaylandqtshellplugin.cpp
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtQml/qqmlextensionplugin.h>
+#include <QtQml/qqml.h>
+
+#include <QtWaylandCompositor/qwaylandquickextension.h>
+#include "qwaylandqtshell.h"
+#include "qwaylandqtshellchrome.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandQtShell)
+
+class QQtWaylandShellPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
+public:
+ void registerTypes(const char *uri) override
+ {
+ Q_ASSERT(QLatin1String(uri) == QLatin1String("QtWayland.Compositor.QtShell"));
+ defineModule(uri);
+ }
+
+ static void defineModule(const char *uri)
+ {
+ qmlRegisterModule(uri, QT_VERSION_MAJOR, QT_VERSION_MINOR);
+ qmlRegisterType<QWaylandQtShellQuickExtension>(uri, 1, 0, "QtShell");
+ qmlRegisterType<QWaylandQtShellSurface>(uri, 1, 0, "QtShellSurface");
+ qmlRegisterType<QWaylandQtShellChrome>(uri, 1, 0, "QtShellChrome");
+ }
+};
+
+QT_END_NAMESPACE
+
+#include "qwaylandqtshellplugin.moc"
diff --git a/src/plugins/shellintegration/CMakeLists.txt b/src/plugins/shellintegration/CMakeLists.txt
index fef15fe7..eefa0227 100644
--- a/src/plugins/shellintegration/CMakeLists.txt
+++ b/src/plugins/shellintegration/CMakeLists.txt
@@ -12,3 +12,7 @@ endif()
if(QT_FEATURE_wayland_client_xdg_shell)
add_subdirectory(xdg-shell)
endif()
+
+if(QT_FEATURE_wayland_client_qt_shell)
+ add_subdirectory(qt-shell)
+endif()
diff --git a/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.cpp b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.cpp
index 0cd2cb1e..033cbf6e 100644
--- a/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.cpp
+++ b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.cpp
@@ -44,23 +44,22 @@ QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
-bool QWaylandFullScreenShellV1Integration::initialize(QWaylandDisplay *display)
+bool QWaylandFullScreenShellV1Integration::initialize()
{
- for (const QWaylandDisplay::RegistryGlobal &global : display->globals()) {
- if (global.interface == QLatin1String("zwp_fullscreen_shell_v1") && !m_shell) {
- m_shell.reset(new QtWayland::zwp_fullscreen_shell_v1(display->wl_registry(), global.id, global.version));
- break;
- }
- }
-
+ if (m_shell)
+ return true;
+ wl_registry *registry;
+ uint32_t id;
+ uint32_t version;
+ bool found = findGlobal(QLatin1String("zwp_fullscreen_shell_v1"), &registry, &id, &version);
+ if (found)
+ m_shell.reset(new QtWayland::zwp_fullscreen_shell_v1(registry, id, version));
if (!m_shell) {
qCDebug(lcQpaWayland) << "Couldn't find global zwp_fullscreen_shell_v1 for fullscreen-shell";
return false;
}
-
- return QWaylandShellIntegration::initialize(display);
+ return true;
}
-
QWaylandShellSurface *QWaylandFullScreenShellV1Integration::createShellSurface(QWaylandWindow *window)
{
return new QWaylandFullScreenShellV1Surface(m_shell.data(), window);
diff --git a/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.h b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.h
index 131f9e72..da99f6c4 100644
--- a/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.h
+++ b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.h
@@ -43,6 +43,7 @@
#include <wayland-client.h>
#include <QtWaylandClient/private/qwayland-wayland.h>
#include <QtWaylandClient/private/qwaylandshellintegration_p.h>
+#include <QScopedPointer>
#include "qwayland-fullscreen-shell-unstable-v1.h"
@@ -53,7 +54,7 @@ namespace QtWaylandClient {
class Q_WAYLAND_CLIENT_EXPORT QWaylandFullScreenShellV1Integration : public QWaylandShellIntegration
{
public:
- bool initialize(QWaylandDisplay *display) override;
+ bool initialize() override;
QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override;
private:
diff --git a/src/plugins/shellintegration/ivi-shell/qwaylandivishellintegration.cpp b/src/plugins/shellintegration/ivi-shell/qwaylandivishellintegration.cpp
index 5571682b..aab5c573 100644
--- a/src/plugins/shellintegration/ivi-shell/qwaylandivishellintegration.cpp
+++ b/src/plugins/shellintegration/ivi-shell/qwaylandivishellintegration.cpp
@@ -60,21 +60,30 @@ QWaylandIviShellIntegration::QWaylandIviShellIntegration()
{
}
-bool QWaylandIviShellIntegration::initialize(QWaylandDisplay *display)
+bool QWaylandIviShellIntegration::initialize()
{
- for (QWaylandDisplay::RegistryGlobal global : display->globals()) {
- if (global.interface == QLatin1String("ivi_application") && !m_iviApplication)
- m_iviApplication.reset(new QtWayland::ivi_application(display->wl_registry(), global.id, global.version));
- if (global.interface == QLatin1String("ivi_controller") && !m_iviController)
- m_iviController.reset(new QtWayland::ivi_controller(display->wl_registry(), global.id, global.version));
+ if (!m_iviApplication) {
+ wl_registry *registry;
+ uint32_t id;
+ uint32_t version;
+ bool found = findGlobal(QLatin1String("ivi_application"), &registry, &id, &version);
+ if (found)
+ m_iviApplication.reset(new QtWayland::ivi_application(registry, id, version));
+ }
+ if (!m_iviController) {
+ wl_registry *registry;
+ uint32_t id;
+ uint32_t version;
+ bool found = findGlobal(QLatin1String("ivi_controller"), &registry, &id, &version);
+ if (found)
+ m_iviController.reset(new QtWayland::ivi_controller(registry, id, version));
}
if (!m_iviApplication) {
qCDebug(lcQpaWayland) << "Couldn't find global ivi_application for ivi-shell";
return false;
}
-
- return QWaylandShellIntegration::initialize(display);
+ return true;
}
/* get unique id
diff --git a/src/plugins/shellintegration/ivi-shell/qwaylandivishellintegration.h b/src/plugins/shellintegration/ivi-shell/qwaylandivishellintegration.h
index bf8c9877..40ec378c 100644
--- a/src/plugins/shellintegration/ivi-shell/qwaylandivishellintegration.h
+++ b/src/plugins/shellintegration/ivi-shell/qwaylandivishellintegration.h
@@ -45,6 +45,7 @@
#include <QtWaylandClient/private/qwaylandshellintegration_p.h>
#include "qwayland-ivi-application.h"
#include "qwayland-ivi-controller.h"
+#include <QScopedPointer>
QT_BEGIN_NAMESPACE
@@ -58,7 +59,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandIviShellIntegration : public QWaylandShell
public:
QWaylandIviShellIntegration();
- bool initialize(QWaylandDisplay *display) override;
+ bool initialize() override;
QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override;
private:
diff --git a/src/plugins/shellintegration/qt-shell/CMakeLists.txt b/src/plugins/shellintegration/qt-shell/CMakeLists.txt
new file mode 100644
index 00000000..99fbfda3
--- /dev/null
+++ b/src/plugins/shellintegration/qt-shell/CMakeLists.txt
@@ -0,0 +1,31 @@
+#####################################################################
+## QWaylandQtShellIntegrationPlugin Plugin:
+#####################################################################
+
+qt_internal_add_plugin(QWaylandQtShellIntegrationPlugin
+ OUTPUT_NAME qt-shell
+ TYPE wayland-shell-integration
+ SOURCES
+ main.cpp
+ qwaylandqtshellintegration.cpp qwaylandqtshellintegration.h
+ qwaylandqtsurface.cpp qwaylandqtsurface_p.h
+ PUBLIC_LIBRARIES
+ Qt::Core
+ Qt::Gui
+ Qt::GuiPrivate
+ Qt::WaylandClientPrivate
+ Wayland::Client
+)
+
+qt6_generate_wayland_protocol_client_sources(QWaylandQtShellIntegrationPlugin
+ FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../../extensions/qt-shell-unstable-v1.xml
+)
+
+## Scopes:
+#####################################################################
+
+qt_internal_extend_target(QWaylandQtShellIntegrationPlugin CONDITION QT_FEATURE_xkbcommon
+ PUBLIC_LIBRARIES
+ XKB::XKB
+)
diff --git a/src/plugins/shellintegration/qt-shell/main.cpp b/src/plugins/shellintegration/qt-shell/main.cpp
new file mode 100644
index 00000000..7628040f
--- /dev/null
+++ b/src/plugins/shellintegration/qt-shell/main.cpp
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtWaylandClient/private/qwaylandshellintegrationplugin_p.h>
+#include "qwaylandqtshellintegration.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+class QWaylandQtShellIntegrationPlugin : public QWaylandShellIntegrationPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QWaylandShellIntegrationFactoryInterface_iid FILE "qt-shell.json")
+
+public:
+ QWaylandShellIntegration *create(const QString &key, const QStringList &paramList) override;
+};
+
+QWaylandShellIntegration *QWaylandQtShellIntegrationPlugin::create(const QString &key, const QStringList &paramList)
+{
+ Q_UNUSED(key);
+ Q_UNUSED(paramList);
+ return new QWaylandQtShellIntegration();
+}
+
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/shellintegration/qt-shell/qt-shell.json b/src/plugins/shellintegration/qt-shell/qt-shell.json
new file mode 100644
index 00000000..aa6df623
--- /dev/null
+++ b/src/plugins/shellintegration/qt-shell/qt-shell.json
@@ -0,0 +1,3 @@
+{
+ "Keys":[ "qt-shell" ]
+}
diff --git a/src/plugins/shellintegration/qt-shell/qwaylandqtshellintegration.cpp b/src/plugins/shellintegration/qt-shell/qwaylandqtshellintegration.cpp
new file mode 100644
index 00000000..225ee61f
--- /dev/null
+++ b/src/plugins/shellintegration/qt-shell/qwaylandqtshellintegration.cpp
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwaylandqtshellintegration.h"
+
+#include <QtCore/qsize.h>
+#include <QtCore/qdebug.h>
+
+#include <QtWaylandClient/private/qwaylanddisplay_p.h>
+#include <QtWaylandClient/private/qwaylandwindow_p.h>
+
+#include "qwaylandqtsurface_p.h"
+
+#include <mutex>
+
+#include <unistd.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+QWaylandQtShellIntegration::QWaylandQtShellIntegration()
+{
+}
+
+bool QWaylandQtShellIntegration::initialize()
+{
+ if (m_qtShell)
+ return true;
+ wl_registry *registry;
+ uint32_t id;
+ uint32_t version;
+ bool found = findGlobal(QLatin1String("zqt_shell_v1"), &registry, &id, &version);
+ if (!found) {
+ qCDebug(lcQpaWayland) << "Couldn't find global zqt_shell_v1 for qt-shell";
+ return false;
+ }
+ m_qtShell.reset(new QtWayland::zqt_shell_v1(registry, id, version));
+ return true;
+}
+
+QWaylandShellSurface *QWaylandQtShellIntegration::createShellSurface(QWaylandWindow *window)
+{
+ if (!m_qtShell)
+ return nullptr;
+
+ auto *surface = m_qtShell->surface_create(wlSurfaceForWindow(window));
+ return new QWaylandQtSurface(surface, window);
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/shellintegration/qt-shell/qwaylandqtshellintegration.h b/src/plugins/shellintegration/qt-shell/qwaylandqtshellintegration.h
new file mode 100644
index 00000000..4feb216a
--- /dev/null
+++ b/src/plugins/shellintegration/qt-shell/qwaylandqtshellintegration.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDQTINTEGRATION_H
+#define QWAYLANDQTINTEGRATION_H
+
+#include <QtCore/qmutex.h>
+
+#include <QtWaylandClient/private/qwaylandshellintegration_p.h>
+#include <QScopedPointer>
+#include "qwayland-qt-shell-unstable-v1.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+class QWaylandWindow;
+class QWaylandDisplay;
+
+class Q_WAYLAND_CLIENT_EXPORT QWaylandQtShellIntegration : public QWaylandShellIntegration
+{
+public:
+ QWaylandQtShellIntegration();
+
+ bool initialize() override;
+ QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override;
+
+private:
+ QScopedPointer<QtWayland::zqt_shell_v1> m_qtShell;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDQTINTEGRATION_H
diff --git a/src/plugins/shellintegration/qt-shell/qwaylandqtsurface.cpp b/src/plugins/shellintegration/qt-shell/qwaylandqtsurface.cpp
new file mode 100644
index 00000000..dcd62768
--- /dev/null
+++ b/src/plugins/shellintegration/qt-shell/qwaylandqtsurface.cpp
@@ -0,0 +1,252 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwaylandqtsurface_p.h"
+#include <qpa/qwindowsysteminterface.h>
+#include <qpa/qplatformwindow.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+QWaylandQtSurface::QWaylandQtSurface(struct ::zqt_shell_surface_v1 *shell_surface, QWaylandWindow *window)
+ : QWaylandShellSurface(window)
+ , QtWayland::zqt_shell_surface_v1(shell_surface)
+{
+ sendSizeHints();
+}
+
+QWaylandQtSurface::~QWaylandQtSurface()
+{
+ zqt_shell_surface_v1::destroy();
+}
+
+void QWaylandQtSurface::resetConfiguration()
+{
+ m_pendingPosition = QPoint(-1, -1);
+ m_pendingSize = QSize();
+ m_pendingPositionValid = false;
+ m_pendingStates = m_currentStates;
+}
+
+void QWaylandQtSurface::applyConfigure()
+{
+ if (m_pendingSize.isValid() && m_pendingPositionValid)
+ setGeometryFromApplyConfigure(m_pendingPosition, m_pendingSize);
+ else if (m_pendingSize.isValid())
+ resizeFromApplyConfigure(m_pendingSize);
+ else if (m_pendingPositionValid)
+ repositionFromApplyConfigure(m_pendingPosition);
+
+ if (m_pendingStates != m_currentStates) {
+ QWindowSystemInterface::handleWindowStateChanged(platformWindow()->window(), m_pendingStates);
+ m_currentStates = m_pendingStates;
+ }
+
+ ack_configure(m_currentConfigureSerial);
+
+ resetConfiguration();
+ m_currentConfigureSerial = UINT32_MAX;
+}
+
+void QWaylandQtSurface::setTitle(const QString &title)
+{
+ set_window_title(title);
+}
+
+void QWaylandQtSurface::zqt_shell_surface_v1_set_capabilities(uint32_t capabilities)
+{
+ m_capabilities = capabilities;
+}
+
+void QWaylandQtSurface::zqt_shell_surface_v1_set_position(uint32_t serial, int32_t x, int32_t y)
+{
+ if (serial < m_currentConfigureSerial && m_currentConfigureSerial != UINT32_MAX)
+ return;
+
+ if (serial != m_currentConfigureSerial) {
+ m_currentConfigureSerial = serial;
+ resetConfiguration();
+ }
+
+ m_pendingPosition = QPoint(x, y);
+ m_pendingPositionValid = true;
+}
+
+void QWaylandQtSurface::zqt_shell_surface_v1_resize(uint32_t serial, int32_t width, int32_t height)
+{
+ if (serial < m_currentConfigureSerial && m_currentConfigureSerial != UINT32_MAX)
+ return;
+
+ if (serial != m_currentConfigureSerial) {
+ m_currentConfigureSerial = serial;
+ resetConfiguration();
+ }
+
+ m_pendingSize = QSize(width, height);
+}
+
+void QWaylandQtSurface::zqt_shell_surface_v1_configure(uint32_t serial)
+{
+ if (serial < m_currentConfigureSerial)
+ return;
+
+ if (serial > m_currentConfigureSerial) {
+ m_currentConfigureSerial = serial;
+ resetConfiguration();
+ }
+
+ applyConfigureWhenPossible();
+}
+
+void QWaylandQtSurface::zqt_shell_surface_v1_close()
+{
+ platformWindow()->window()->close();
+}
+
+void QWaylandQtSurface::zqt_shell_surface_v1_set_frame_margins(uint32_t left, uint32_t right,
+ uint32_t top, uint32_t bottom)
+{
+ QPlatformWindow *win = platformWindow();
+ m_frameMargins = QMargins(left, top, right, bottom);
+ m_pendingPosition = win->geometry().topLeft();
+ m_pendingPositionValid = true;
+ m_pendingSize = win->geometry().size();
+ applyConfigureWhenPossible();
+}
+
+bool QWaylandQtSurface::requestActivate()
+{
+ request_activate();
+ return true;
+}
+
+void QWaylandQtSurface::propagateSizeHints()
+{
+ sendSizeHints();
+}
+
+void QWaylandQtSurface::sendSizeHints()
+{
+ QPlatformWindow *win = platformWindow();
+ if (win) {
+ const int minWidth = qMax(0, win->windowMinimumSize().width());
+ const int minHeight = qMax(0, win->windowMinimumSize().height());
+ set_minimum_size(minWidth, minHeight);
+
+ int maxWidth = qMax(0, win->windowMaximumSize().width());
+ if (maxWidth == QWINDOWSIZE_MAX)
+ maxWidth = -1;
+ int maxHeight = qMax(0, win->windowMaximumSize().height());
+ if (maxHeight == QWINDOWSIZE_MAX)
+ maxHeight = -1;
+ set_maximum_size(maxWidth, maxHeight);
+ }
+}
+
+void QWaylandQtSurface::zqt_shell_surface_v1_set_window_state(uint32_t serial, uint32_t state)
+{
+ if (serial < m_currentConfigureSerial && m_currentConfigureSerial != UINT32_MAX)
+ return;
+
+ if (serial != m_currentConfigureSerial) {
+ m_currentConfigureSerial = serial;
+ resetConfiguration();
+ }
+ m_pendingStates = Qt::WindowStates(state);
+}
+
+void QWaylandQtSurface::setWindowGeometry(const QRect &rect)
+{
+ set_size(rect.width(), rect.height());
+}
+
+void QWaylandQtSurface::setWindowPosition(const QPoint &position)
+{
+ reposition(position.x(), position.y());
+}
+
+void QWaylandQtSurface::setWindowFlags(Qt::WindowFlags flags)
+{
+ set_window_flags(flags);
+}
+
+void QWaylandQtSurface::requestWindowStates(Qt::WindowStates states)
+{
+ change_window_state(states & ~Qt::WindowActive);
+}
+
+bool QWaylandQtSurface::resize(QWaylandInputDevice *inputDevice, Qt::Edges edge)
+{
+ if (m_capabilities & ZQT_SHELL_SURFACE_V1_CAPABILITIES_INTERACTIVE_RESIZE) {
+ start_system_resize(getSerial(inputDevice), uint(edge));
+ return true;
+ }
+
+ return false;
+}
+
+bool QWaylandQtSurface::move(QWaylandInputDevice *inputDevice)
+{
+ if (m_capabilities & ZQT_SHELL_SURFACE_V1_CAPABILITIES_INTERACTIVE_RESIZE) {
+ start_system_move(getSerial(inputDevice));
+ return true;
+ }
+
+ return false;
+}
+
+QMargins QWaylandQtSurface::serverSideFrameMargins() const
+{
+ return m_frameMargins;
+}
+
+void QWaylandQtSurface::raise()
+{
+ QtWayland::zqt_shell_surface_v1::raise();
+}
+
+void QWaylandQtSurface::lower()
+{
+ QtWayland::zqt_shell_surface_v1::lower();
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/shellintegration/qt-shell/qwaylandqtsurface_p.h b/src/plugins/shellintegration/qt-shell/qwaylandqtsurface_p.h
new file mode 100644
index 00000000..9b64b1d1
--- /dev/null
+++ b/src/plugins/shellintegration/qt-shell/qwaylandqtsurface_p.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWAYLANDQTSURFACE_H
+#define QWAYLANDQTSURFACE_H
+
+#include <QtCore/qpoint.h>
+#include <QtWaylandClient/private/qwaylandshellsurface_p.h>
+#include "qwayland-qt-shell-unstable-v1.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QtWaylandClient {
+
+class QWaylandWindow;
+class QWaylandInputDevice;
+class QWindow;
+
+class Q_WAYLAND_CLIENT_EXPORT QWaylandQtSurface : public QWaylandShellSurface
+ , public QtWayland::zqt_shell_surface_v1
+{
+public:
+ QWaylandQtSurface(struct ::zqt_shell_surface_v1 *shell_surface, QWaylandWindow *window);
+ ~QWaylandQtSurface() override;
+
+ void applyConfigure() override;
+ void setWindowGeometry(const QRect &rect) override;
+ void setWindowPosition(const QPoint &position) override;
+
+ void setWindowFlags(Qt::WindowFlags flags) override;
+ void requestWindowStates(Qt::WindowStates states) override;
+ void setTitle(const QString &title) override;
+
+ bool resize(QWaylandInputDevice *, Qt::Edges) override;
+ bool move(QWaylandInputDevice *) override;
+ bool requestActivate() override;
+
+ void propagateSizeHints() override;
+
+ QMargins serverSideFrameMargins() const override;
+
+ void raise() override;
+ void lower() override;
+
+private:
+ void resetConfiguration();
+ void sendSizeHints();
+ void zqt_shell_surface_v1_close() override;
+ void zqt_shell_surface_v1_resize(uint32_t serial, int32_t width, int32_t height) override;
+ void zqt_shell_surface_v1_set_position(uint32_t serial, int32_t x, int32_t y) override;
+ void zqt_shell_surface_v1_configure(uint32_t serial) override;
+ void zqt_shell_surface_v1_set_window_state(uint32_t serial, uint32_t state) override;
+ void zqt_shell_surface_v1_set_frame_margins(uint32_t left, uint32_t right,
+ uint32_t top, uint32_t bottom) override;
+ void zqt_shell_surface_v1_set_capabilities(uint32_t capabilities) override;
+
+ QSize m_pendingSize;
+ QPoint m_pendingPosition = { -1, -1 };
+ bool m_pendingPositionValid = false;
+ Qt::WindowStates m_pendingStates = Qt::WindowNoState;
+ Qt::WindowStates m_currentStates = Qt::WindowNoState;
+ QMargins m_frameMargins;
+ uint32_t m_currentConfigureSerial = UINT32_MAX;
+ uint32_t m_capabilities = 0;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QWAYLANDQTSURFACE_H
diff --git a/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration.cpp b/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration.cpp
index 354ee19b..7353cb1f 100644
--- a/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration.cpp
+++ b/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration.cpp
@@ -47,15 +47,16 @@ QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
-bool QWaylandWlShellIntegration::initialize(QWaylandDisplay *display)
+bool QWaylandWlShellIntegration::initialize()
{
- const auto globals = display->globals();
- for (QWaylandDisplay::RegistryGlobal global : globals) {
- if (global.interface == QLatin1String("wl_shell")) {
- m_wlShell = new QtWayland::wl_shell(display->wl_registry(), global.id, 1);
- break;
- }
- }
+ if (m_wlShell)
+ return true;
+ wl_registry *registry;
+ uint32_t id;
+ uint32_t version;
+ bool found = findGlobal(QLatin1String("wl_shell"), &registry, &id, &version);
+ if (found)
+ m_wlShell = new QtWayland::wl_shell(registry, id, 1);
if (!m_wlShell) {
qCDebug(lcQpaWayland) << "Couldn't find global wl_shell";
@@ -66,7 +67,7 @@ bool QWaylandWlShellIntegration::initialize(QWaylandDisplay *display)
<< "\"xdg-shell\" if supported by the compositor"
<< "by setting the environment variable QT_WAYLAND_SHELL_INTEGRATION";
- return QWaylandShellIntegration::initialize(display);
+ return true;
}
QWaylandShellSurface *QWaylandWlShellIntegration::createShellSurface(QWaylandWindow *window)
diff --git a/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration_p.h b/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration_p.h
index 3d76cc31..47815d54 100644
--- a/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration_p.h
+++ b/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration_p.h
@@ -63,7 +63,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandWlShellIntegration : public QWaylandShellI
{
public:
QWaylandWlShellIntegration() {}
- bool initialize(QWaylandDisplay *) override;
+ bool initialize() override;
QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override;
void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) override;
diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp
index fcdd435c..8390d76f 100644
--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp
+++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp
@@ -47,21 +47,21 @@ QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
-bool QWaylandXdgShellIntegration::initialize(QWaylandDisplay *display)
+bool QWaylandXdgShellIntegration::initialize()
{
- for (QWaylandDisplay::RegistryGlobal global : display->globals()) {
- if (global.interface == QLatin1String("xdg_wm_base")) {
- m_xdgShell.reset(new QWaylandXdgShell(display, global.id, global.version));
- break;
- }
- }
-
+ if (m_xdgShell)
+ return true;
+ wl_registry *registry;
+ uint32_t id;
+ uint32_t version;
+ bool found = findGlobal(QLatin1String("xdg_wm_base"), &registry, &id, &version);
+ if (found)
+ m_xdgShell.reset(new QWaylandXdgShell(m_display, id, version));
if (!m_xdgShell) {
qCDebug(lcQpaWayland) << "Couldn't find global xdg_wm_base for xdg-shell stable";
return false;
}
-
- return QWaylandShellIntegration::initialize(display);
+ return true;
}
QWaylandShellSurface *QWaylandXdgShellIntegration::createShellSurface(QWaylandWindow *window)
diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h
index fced9eb0..cd54dd48 100644
--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h
+++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h
@@ -63,7 +63,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandXdgShellIntegration : public QWaylandShell
{
public:
QWaylandXdgShellIntegration() {}
- bool initialize(QWaylandDisplay *display) override;
+ bool initialize() override;
QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override;
void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) override;
diff --git a/sync.profile b/sync.profile
index 50825d46..8bd0b2a9 100644
--- a/sync.profile
+++ b/sync.profile
@@ -93,6 +93,8 @@
"^wayland-xdg-decoration-unstable-v1-server-protocol.h",
"^wayland-xdg-output-unstable-v1-server-protocol.h",
"^wayland-xdg-shell-server-protocol.h",
+ "^qwayland-server-qt-shell-unstable-v1.h",
+ "^wayland-qt-shell-unstable-v1-server-protocol.h",
],
"$basedir/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1" => [
"^qwayland-server-linux-dmabuf-unstable-v1.h",
diff --git a/tests/manual/qt-shell/CMakeLists.txt b/tests/manual/qt-shell/CMakeLists.txt
new file mode 100644
index 00000000..5a38aae4
--- /dev/null
+++ b/tests/manual/qt-shell/CMakeLists.txt
@@ -0,0 +1,43 @@
+cmake_minimum_required(VERSION 3.14)
+project(qt-shell LANGUAGES CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+set(CMAKE_AUTOUIC ON)
+
+find_package(Qt6 COMPONENTS Core)
+find_package(Qt6 COMPONENTS Gui)
+find_package(Qt6 COMPONENTS Qml)
+
+qt_add_executable(qt-shell
+ main.cpp
+)
+set_target_properties(qt-shell PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+target_link_libraries(qt-shell PUBLIC
+ Qt::Core
+ Qt::Gui
+ Qt::Qml
+)
+
+
+# Resources:
+set(qt-shell_resource_files
+ "images/background.jpg"
+ "qml/Chrome.qml"
+ "qml/HandleHandler.qml"
+ "qml/CompositorScreen.qml"
+ "qml/Keyboard.qml"
+ "qml/main.qml"
+)
+
+qt6_add_resources(qt-shell "qt-shell"
+ PREFIX
+ "/"
+ FILES
+ ${qt-shell_resource_files}
+)
diff --git a/tests/manual/qt-shell/images/background.jpg b/tests/manual/qt-shell/images/background.jpg
new file mode 100644
index 00000000..445567fb
--- /dev/null
+++ b/tests/manual/qt-shell/images/background.jpg
Binary files differ
diff --git a/tests/manual/qt-shell/main.cpp b/tests/manual/qt-shell/main.cpp
new file mode 100644
index 00000000..0b2750d8
--- /dev/null
+++ b/tests/manual/qt-shell/main.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QUrl>
+#include <QtCore/QDebug>
+
+#include <QtGui/QGuiApplication>
+
+#include <QtQml/QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+ // ShareOpenGLContexts is needed for using the threaded renderer
+ // on Nvidia EGLStreams
+ QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine appEngine(QUrl("qrc:///qml/main.qml"));
+
+ return app.exec();
+}
diff --git a/tests/manual/qt-shell/qml/Chrome.qml b/tests/manual/qt-shell/qml/Chrome.qml
new file mode 100644
index 00000000..433fc5a5
--- /dev/null
+++ b/tests/manual/qt-shell/qml/Chrome.qml
@@ -0,0 +1,505 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtWayland.Compositor
+
+Item {
+ id: chrome
+
+ property bool positionSet: false
+
+ x: shellSurface.windowGeometry.x - leftResizeHandle.width
+ y: shellSurface.windowGeometry.y - topResizeHandle.height - titleBar.height
+ width: shellSurface.windowGeometry.width + leftResizeHandle.width + rightResizeHandle.width
+ height: shellSurface.windowGeometry.height + topResizeHandle.height + titleBar.height + bottomResizeHandle.height
+
+ property rect oldGeometry: Qt.rect(0, 0, 100, 100)
+ property bool isChild: parent.shellSurface !== undefined
+ property alias shellSurface: shellSurfaceItem.shellSurface
+
+ property int windowState: Qt.WindowNoState
+
+ signal destroyAnimationFinished
+ signal activated
+ signal deactivated
+
+ property int windowFlags: shellSurface.windowFlags !== Qt.Window
+ ? shellSurface.windowFlags
+ : defaultFlags
+ onDecorationsShowingChanged:{
+ shellSurfaceItem.updateFrameMargins()
+ }
+
+ Component.onCompleted: {
+ shellSurface.active = true
+ }
+
+ property int defaultFlags: (Qt.Window
+ | Qt.WindowMaximizeButtonHint
+ | Qt.WindowMinimizeButtonHint
+ | Qt.WindowCloseButtonHint)
+
+ property bool frameless: (chrome.windowFlags & Qt.FramelessWindowHint) != 0
+ || (chrome.windowState & Qt.WindowFullScreen) != 0
+ || ((chrome.windowFlags & Qt.Popup) == Qt.Popup
+ && (chrome.windowFlags & Qt.Tool) != Qt.Tool)
+
+ property bool decorationsShowing: (chrome.windowFlags & Qt.Window) != 0 && !frameless
+
+ transform: [
+ Scale {
+ id: scaleTransform
+ origin.x: chrome.width / 2
+ origin.y: chrome.height / 2
+ }
+ ]
+
+ Rectangle {
+ id: leftResizeHandle
+ color: "gray"
+ width: visible ? 5 : 0
+ anchors.left: parent.left
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ visible: decorationsShowing
+
+ HandleHandler {
+ enabled: (chrome.windowState & (Qt.WindowMinimized|Qt.WindowMaximized)) == 0
+ flags: westBound
+ }
+ }
+
+ Rectangle {
+ id: rightResizeHandle
+ color: "gray"
+ width: visible ? 5 : 0
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ visible: decorationsShowing
+
+ HandleHandler {
+ enabled: (chrome.windowState & (Qt.WindowMinimized|Qt.WindowMaximized)) == 0
+ flags: eastBound
+ }
+ }
+
+ Rectangle {
+ id: topResizeHandle
+ color: "gray"
+ height: visible ? 5 : 0
+ anchors.left: parent.left
+ anchors.top: parent.top
+ anchors.right: parent.right
+ visible: decorationsShowing
+
+ HandleHandler {
+ enabled: (chrome.windowState & (Qt.WindowMinimized|Qt.WindowMaximized)) == 0
+ flags: northBound
+ }
+ }
+
+ Rectangle {
+ id: bottomResizeHandle
+ color: "gray"
+ height: visible ? 5 : 0
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+ visible: decorationsShowing
+
+ HandleHandler {
+ enabled: (chrome.windowState & (Qt.WindowMinimized|Qt.WindowMaximized)) == 0
+ flags: southBound
+ }
+ }
+
+ Rectangle {
+ id: topLeftResizeHandle
+ color: "gray"
+ height: 5
+ width: 5
+ anchors.left: parent.left
+ anchors.top: parent.top
+ visible: decorationsShowing
+
+ HandleHandler {
+ enabled: (chrome.windowState & (Qt.WindowMinimized|Qt.WindowMaximized)) == 0
+ flags: westBound | northBound
+ }
+ }
+
+ Rectangle {
+ id: topRightResizeHandle
+ color: "gray"
+ height: 5
+ width: 5
+ anchors.right: parent.right
+ anchors.top: parent.top
+ visible: decorationsShowing
+
+ HandleHandler {
+ enabled: (chrome.windowState & (Qt.WindowMinimized|Qt.WindowMaximized)) == 0
+ flags: eastBound | northBound
+ }
+ }
+
+ Rectangle {
+ id: bottomLeftResizeHandle
+ color: "gray"
+ height: 5
+ width: 5
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ visible: decorationsShowing
+
+ HandleHandler {
+ enabled: (chrome.windowState & (Qt.WindowMinimized|Qt.WindowMaximized)) == 0
+ flags: westBound | southBound
+ }
+ }
+
+ Rectangle {
+ id: bottomRightResizeHandle
+ color: "gray"
+ height: 5
+ width: 5
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ visible: decorationsShowing
+
+ HandleHandler {
+ enabled: (chrome.windowState & (Qt.WindowMinimized|Qt.WindowMaximized)) == 0
+ flags: eastBound | southBound
+ }
+ }
+
+ function constrainPoint(mousePos) {
+ var x0 = usableArea.x
+ var y0 = usableArea.y
+ var x1 = x0 + usableArea.width
+ var y1 = y0 + usableArea.height
+ return Qt.point(Math.min(Math.max(x0,mousePos.x), x1),
+ Math.min(Math.max(y0,mousePos.y), y1))
+ }
+
+ function maxContentRect() {
+ var x0 = usableArea.x + leftResizeHandle.width
+ var x1 = usableArea.x + usableArea.width - rightResizeHandle.width
+ var y0 = usableArea.y + topResizeHandle.height + titleBar.height
+ var y1 = usableArea.y + usableArea.height - bottomResizeHandle.height
+ return Qt.rect(x0, y0, x1 - x0, y1 - y0)
+ }
+
+ function randomPos(windowSize, screenSize) {
+ var res = (windowSize >= screenSize) ? 0 : Math.floor(Math.random() * (screenSize - windowSize))
+ return res
+ }
+
+ function activate()
+ {
+ shellSurface.active = true
+ shellSurfaceItem.raise()
+ activated()
+ }
+
+ function deactivate()
+ {
+ shellSurface.active = true
+ deactivated()
+ }
+
+ function setWindowState(nextState) {
+ var currentState = chrome.windowState
+ if (currentState === nextState)
+ return
+
+ console.log("setWindowState", nextState.toString(16))
+
+ if ((currentState & (Qt.WindowMinimized | Qt.WindowMaximized | Qt.WindowFullScreen)) == 0)
+ chrome.oldGeometry = chrome.shellSurface.windowGeometry
+
+ chrome.windowState = nextState
+
+ if ((nextState & Qt.WindowMinimized) != 0) {
+ console.log("MINIMIZE")
+ chrome.shellSurface.requestWindowGeometry(nextState, Qt.rect(0, 0, 1, 1))
+ shellSurfaceItem.visible = false
+ } else if ((nextState & Qt.WindowFullScreen) != 0) {
+ console.log("FULLSCREENIZE")
+ chrome.shellSurface.requestWindowGeometry(nextState, Qt.rect(0, 0, output.window.width, output.window.height))
+ shellSurfaceItem.visible = true
+ } else if ((nextState & Qt.WindowMaximized) != 0) {
+ console.log("MAXIMIZE")
+ chrome.shellSurface.requestWindowGeometry(nextState, maxContentRect())
+ shellSurfaceItem.visible = true
+ } else {
+ console.log("NORMALIZE", chrome.oldGeometry)
+ chrome.shellSurface.requestWindowGeometry(nextState, chrome.oldGeometry)
+ shellSurfaceItem.visible = true
+ }
+ }
+
+ Rectangle {
+ id: titleBar
+ anchors.top: topResizeHandle.bottom
+ anchors.left: leftResizeHandle.right
+ anchors.right: rightResizeHandle.left
+ height: visible ? xButton.height + 10 : 0
+ color: shellSurface.active ? "cornflowerblue" : "lightgray"
+ visible: !frameless
+
+ Text {
+ anchors.left: parent.left
+ anchors.right: rowLayout.left
+ anchors.verticalCenter: parent.verticalCenter
+
+ font.pixelSize: xButton.height
+ text: shellSurface.windowTitle
+ fontSizeMode: Text.Fit
+ }
+
+ RowLayout {
+ id: rowLayout
+ anchors.right: parent.right
+ anchors.rightMargin: 5
+
+ ToolButton {
+ text: "-"
+ Layout.margins: 5
+ visible: (chrome.windowFlags & Qt.WindowMinimizeButtonHint) != 0
+ onClicked: {
+ var newState
+ if ((shellSurface.windowState & Qt.WindowMinimized) != 0)
+ newState = chrome.windowState & ~Qt.WindowMinimized
+ else
+ newState = chrome.windowState | Qt.WindowMinimized
+
+ if ((newState & Qt.WindowMaximized) != 0)
+ newState &= ~Qt.WindowMaximized
+
+ setWindowState(newState)
+ }
+ }
+
+ ToolButton {
+ text: "+"
+ Layout.margins: 5
+ visible: (chrome.windowFlags & Qt.WindowMaximizeButtonHint) != 0
+ onClicked: {
+ var newState
+ if ((shellSurface.windowState & Qt.WindowMaximized) != 0)
+ newState = shellSurface.windowState & ~Qt.WindowMaximized
+ else
+ newState = shellSurface.windowState | Qt.WindowMaximized
+
+ if ((newState & Qt.WindowMinimized) != 0)
+ newState &= ~Qt.WindowMinimized
+
+ setWindowState(newState)
+ }
+ }
+
+ ToolButton {
+ id: xButton
+ text: "X"
+ Layout.margins: 5
+ visible: (chrome.windowFlags & Qt.WindowCloseButtonHint) != 0
+ onClicked: shellSurface.sendClose()
+ }
+ }
+
+ DragHandler {
+ target: null
+ property real xOffset: -1.0
+ property real yOffset: -1.0
+ property bool started: false
+ enabled: (shellSurface.windowState & (Qt.WindowMinimized|Qt.WindowMaximized)) == 0
+
+ onGrabChanged: {
+ started = false
+ activate()
+ }
+
+ onCentroidChanged: {
+ if (!active)
+ return
+
+ if (!started) {
+ xOffset = shellSurface.windowPosition.x - centroid.scenePressPosition.x
+ yOffset = shellSurface.windowPosition.y - centroid.scenePressPosition.y
+ started = true
+ chrome.positionAutomatic = false
+ }
+
+ var pos = chrome.constrainPoint(centroid.scenePosition)
+ shellSurface.windowPosition = Qt.point(pos.x + xOffset, pos.y + yOffset)
+ }
+ }
+ }
+
+ ShellSurfaceItem {
+ id: shellSurfaceItem
+ anchors.top: titleBar.bottom
+ anchors.bottom: bottomResizeHandle.top
+ anchors.left: leftResizeHandle.right
+ anchors.right: rightResizeHandle.left
+
+ moveItem: chrome
+
+ staysOnBottom: shellSurface.windowFlags & Qt.WindowStaysOnBottomHint
+ staysOnTop: !staysOnBottom && shellSurface.windowFlags & Qt.WindowStaysOnTopHint
+ function updateFrameMargins()
+ {
+ shellSurface.frameMarginLeft = (decorationsShowing ? leftResizeHandle.width : 0)
+ shellSurface.frameMarginRight = (decorationsShowing ? rightResizeHandle.width : 0)
+ shellSurface.frameMarginTop = (decorationsShowing ? topResizeHandle.height : 0)
+ + (!frameless ? titleBar.height : 0)
+ shellSurface.frameMarginBottom = (decorationsShowing ? bottomResizeHandle.height : 0)
+ }
+
+ Component.onCompleted: {
+ updateFrameMargins()
+ }
+
+ onSurfaceDestroyed: {
+ bufferLocked = true;
+ destroyAnimation.start();
+ }
+
+ Connections {
+ target: shellSurface
+ function onWindowFlagsChanged() {
+ console.log("FLAGS", shellSurface.windowFlags.toString(16))
+ shellSurfaceItem.updateFrameMargins()
+ }
+
+ function onWindowStateChanged() {
+ setWindowState(shellSurface.windowState)
+ }
+
+ function onActiveChanged() {
+ if (shellSurface.active) {
+ shellSurfaceItem.raise()
+ activated()
+ } else {
+ deactivated()
+ }
+ }
+
+ function onStartResize() {
+ console.log("START SYSTEM RESIZE")
+ }
+ function onStartMove() {
+ console.log("START SYSTEM MOVE")
+ }
+
+ function onRaiseRequested() {
+ console.log("RAISE")
+ shellSurfaceItem.raise()
+ }
+ function onLowerRequested() {
+ console.log("LOWER")
+ shellSurfaceItem.lower()
+ }
+
+ function onWindowGeometryChanged() {
+ console.log("GEOM CHANGE", shellSurface.windowGeometry)
+ }
+ }
+
+ Connections {
+ target: shellSurface.surface
+ function onHasContentChanged() {
+ if (!chrome.positionSet) {
+ var rect = shellSurface.windowGeometry
+ var w = rect.width
+ var h = rect.height
+
+ var space = maxContentRect()
+
+ var randomize = shellSurface.positionAutomatic
+ var xpos = randomize ? randomPos(w, space.width) + space.x : Math.max(rect.x, space.x)
+ var ypos = randomize ? randomPos(h, space.height) + space.y : Math.max(rect.y, space.y)
+ shellSurface.windowPosition = Qt.point(xpos, ypos)
+ }
+ chrome.positionSet = true
+ }
+ }
+
+ SequentialAnimation {
+ id: destroyAnimation
+
+ ParallelAnimation {
+ NumberAnimation { target: scaleTransform; property: "yScale"; to: 2/height; duration: 150 }
+ NumberAnimation { target: scaleTransform; property: "xScale"; to: 0.4; duration: 150 }
+ NumberAnimation { target: chrome; property: "opacity"; to: chrome.isChild ? 0 : 1; duration: 150 }
+ }
+ NumberAnimation { target: scaleTransform; property: "xScale"; to: 0; duration: 150 }
+ ScriptAction { script: chrome.destroyAnimationFinished() }
+ }
+
+ SequentialAnimation {
+ id: receivedFocusAnimation
+
+ ParallelAnimation {
+ NumberAnimation { target: scaleTransform; property: "yScale"; to: 1.02; duration: 100; easing.type: Easing.OutQuad }
+ NumberAnimation { target: scaleTransform; property: "xScale"; to: 1.02; duration: 100; easing.type: Easing.OutQuad }
+ }
+ ParallelAnimation {
+ NumberAnimation { target: scaleTransform; property: "yScale"; to: 1; duration: 100; easing.type: Easing.InOutQuad }
+ NumberAnimation { target: scaleTransform; property: "xScale"; to: 1; duration: 100; easing.type: Easing.InOutQuad }
+ }
+ }
+ }
+}
diff --git a/tests/manual/qt-shell/qml/CompositorScreen.qml b/tests/manual/qt-shell/qml/CompositorScreen.qml
new file mode 100644
index 00000000..c634e08c
--- /dev/null
+++ b/tests/manual/qt-shell/qml/CompositorScreen.qml
@@ -0,0 +1,191 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.Window
+import QtQuick.Controls
+import QtWayland.Compositor
+
+WaylandOutput {
+ id: output
+
+ property ListModel shellSurfaces: ListModel {}
+ property bool isNestedCompositor: Qt.platform.pluginName.startsWith("wayland") || Qt.platform.pluginName === "xcb"
+ property int currentActiveWindow: -1
+
+ function handleShellSurface(shellSurface) {
+ shellSurfaces.append({shellSurface: shellSurface});
+ }
+
+ // During development, it can be useful to start the compositor inside X11 or
+ // another Wayland compositor. In such cases, set sizeFollowsWindow to true to
+ // enable resizing of the compositor window to be forwarded to the Wayland clients
+ // as the output (screen) changing resolution. Consider setting it to false if you
+ // are running the compositor using eglfs, linuxfb or similar QPA backends.
+ sizeFollowsWindow: output.isNestedCompositor
+
+ window: Window {
+ width: 1920
+ height: 1080
+ visible: true
+
+ WaylandMouseTracker {
+ id: mouseTracker
+
+ anchors.fill: parent
+
+ // Set this to false to disable the outer mouse cursor when running nested
+ // compositors. Otherwise you would see two mouse cursors, one for each compositor.
+ windowSystemCursorEnabled: output.isNestedCompositor
+
+ Image {
+ id: background
+
+ anchors.fill: parent
+ fillMode: Image.Tile
+ source: "qrc:/images/background.jpg"
+ smooth: true
+
+ Repeater {
+ id: chromeRepeater
+ model: output.shellSurfaces
+ // Chrome displays a shell surface on the screen (See Chrome.qml)
+ Chrome {
+ shellSurface: modelData
+ onDestroyAnimationFinished:
+ {
+ if (currentActiveWindow > index) {
+ --currentActiveWindow
+ } else if (currentActiveWindow === index) {
+ currentActiveWindow = index - 1
+ if (currentActiveWindow >= 0) {
+ var nextActiveSurface = output.shellSurfaces.get(currentActiveWindow).shellSurface
+ if (nextActiveSurface !== undefined) // More than one surface can get destroyed at the same time
+ nextActiveSurface.active = true
+ }
+ }
+ output.shellSurfaces.remove(index)
+ }
+
+ onDeactivated: {
+ if (index === currentActiveWindow)
+ currentActiveWindow = -1
+ }
+
+ onActivated: {
+ if (index !== currentActiveWindow && currentActiveWindow >= 0) {
+ // This may already have been destroyed
+ if (output.shellSurfaces.get(currentActiveWindow).shellSurface !== undefined)
+ output.shellSurfaces.get(currentActiveWindow).shellSurface.active = false
+ }
+
+ currentActiveWindow = index
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ anchors.fill: taskbar
+ color: "lavenderblush"
+ }
+
+ Row {
+ id: taskbar
+ height: 40
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+
+ Repeater {
+ anchors.fill: parent
+ model: output.shellSurfaces
+
+ ToolButton {
+ anchors.verticalCenter: parent.verticalCenter
+ text: modelData.windowTitle
+ onClicked: {
+ modelData.requestWindowGeometry(modelData.windowState & ~Qt.WindowMinimized,
+ modelData.windowGeometry)
+ chromeRepeater.itemAt(index).activate()
+ }
+ }
+ }
+ }
+
+ Item {
+ id: usableArea
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: taskbar.top
+ }
+
+ // Virtual Keyboard
+ Loader {
+ anchors.fill: parent
+ source: "Keyboard.qml"
+ }
+
+ // Draws the mouse cursor for a given Wayland seat
+ WaylandCursorItem {
+ inputEventsEnabled: false
+ x: mouseTracker.mouseX
+ y: mouseTracker.mouseY
+ seat: output.compositor.defaultSeat
+ }
+ }
+
+ Shortcut {
+ sequence: "Ctrl+Alt+Backspace"
+ onActivated: Qt.quit()
+ }
+ }
+}
diff --git a/tests/manual/qt-shell/qml/HandleHandler.qml b/tests/manual/qt-shell/qml/HandleHandler.qml
new file mode 100644
index 00000000..5615fba6
--- /dev/null
+++ b/tests/manual/qt-shell/qml/HandleHandler.qml
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+
+DragHandler {
+ target: null
+
+ function selectCursor(f)
+ {
+ switch (f) {
+ case (southBound | eastBound):
+ case (northBound | westBound):
+ return Qt.SizeFDiagCursor
+ case (southBound | westBound):
+ case (northBound | eastBound):
+ return Qt.SizeBDiagCursor
+ case westBound:
+ case eastBound:
+ return Qt.SizeHorCursor
+ default:
+ return Qt.SizeVerCursor
+ }
+ }
+
+ property int flags: WestBound
+ readonly property int westBound: 1
+ readonly property int eastBound: 2
+ readonly property int northBound: 4
+ readonly property int southBound: 8
+
+ cursorShape: selectCursor(flags)
+
+ property rect geom
+ property real startX: -1.0
+ property real startY: -1.0
+ property bool started: false
+ onGrabChanged: {
+ started = false
+ }
+ onCentroidChanged: {
+ if (!active)
+ return
+ if (!started) {
+ geom = shellSurface.windowGeometry
+ startX = centroid.scenePressPosition.x
+ startY = centroid.scenePressPosition.y
+ started = true
+ }
+
+ var pos = chrome.constrainPoint(centroid.scenePosition)
+ var dx = pos.x - startX
+ var dy = pos.y - startY
+
+ var minWidth = Math.max(0, shellSurface.minimumSize.width)
+ var minHeight = Math.max(0, shellSurface.minimumSize.height)
+
+ var maxWidth = shellSurface.maximumSize.width > 0 ? shellSurface.maximumSize.width : Number.MAX_VALUE
+ var maxHeight = shellSurface.maximumSize.height > 0 ? shellSurface.maximumSize.height : Number.MAX_VALUE
+
+ var newLeft = geom.left
+ if (flags & westBound)
+ newLeft = Math.max(geom.right - maxWidth, Math.min(geom.right - minWidth, newLeft + dx));
+
+ var newTop = geom.top
+ if (flags & northBound)
+ newTop = Math.max(geom.bottom - maxHeight, Math.min(geom.bottom - minHeight, newTop + dy));
+
+ var newRight = geom.right
+ if (flags & eastBound)
+ newRight = Math.max(geom.left, newRight + dx);
+
+ var newBottom = geom.bottom
+ if (flags & southBound)
+ newBottom = Math.max(geom.top, newBottom + dy);
+
+ console.log("RESIZE HANDLER", shellSurface.windowGeometry, geom, dy, newTop)
+
+
+ shellSurface.requestWindowGeometry(shellSurface.windowState,
+ Qt.rect(newLeft,
+ newTop,
+ newRight - newLeft,
+ newBottom - newTop))
+ }
+}
diff --git a/tests/manual/qt-shell/qml/Keyboard.qml b/tests/manual/qt-shell/qml/Keyboard.qml
new file mode 100644
index 00000000..12eae9f1
--- /dev/null
+++ b/tests/manual/qt-shell/qml/Keyboard.qml
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick.VirtualKeyboard
+
+InputPanel {
+ visible: active
+ y: active ? parent.height - height : parent.height
+ anchors.left: parent.left
+ anchors.right: parent.right
+}
diff --git a/tests/manual/qt-shell/qml/main.qml b/tests/manual/qt-shell/qml/main.qml
new file mode 100644
index 00000000..e988ae1b
--- /dev/null
+++ b/tests/manual/qt-shell/qml/main.qml
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick
+import QtWayland.Compositor
+import QtWayland.Compositor.QtShell
+
+WaylandCompositor {
+ id: waylandCompositor
+
+ CompositorScreen { id: screen; compositor: waylandCompositor }
+
+ // Shell surface extension. Needed to provide a window concept for Wayland clients.
+ // I.e. requests and events for maximization, minimization, resizing, closing etc.
+
+ QtShell {
+ onQtShellSurfaceCreated: screen.handleShellSurface(qtShellSurface)
+ }
+
+ // Extension for Input Method (QT_IM_MODULE) support at compositor-side
+ QtTextInputMethodManager {}
+}
diff --git a/tests/manual/qt-shell/qt-shell.pro b/tests/manual/qt-shell/qt-shell.pro
new file mode 100644
index 00000000..c7e2cd91
--- /dev/null
+++ b/tests/manual/qt-shell/qt-shell.pro
@@ -0,0 +1,13 @@
+QT += gui qml
+
+SOURCES += \
+ main.cpp
+
+OTHER_FILES = \
+ qml/main.qml \
+ qml/CompositorScreen.qml \
+ qml/Chrome.qml \
+ qml/Keyboard.qml \
+ images/background.jpg \
+
+RESOURCES += qt-shell.qrc
diff --git a/tests/manual/qt-shell/qt-shell.qrc b/tests/manual/qt-shell/qt-shell.qrc
new file mode 100644
index 00000000..3120382f
--- /dev/null
+++ b/tests/manual/qt-shell/qt-shell.qrc
@@ -0,0 +1,10 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/background.jpg</file>
+ <file>qml/main.qml</file>
+ <file>qml/CompositorScreen.qml</file>
+ <file>qml/Chrome.qml</file>
+ <file>qml/Keyboard.qml</file>
+ <file>qml/HandleHandler.qml</file>
+ </qresource>
+</RCC>