summaryrefslogtreecommitdiffstats
path: root/examples/wayland/fancy-compositor
diff options
context:
space:
mode:
Diffstat (limited to 'examples/wayland/fancy-compositor')
-rw-r--r--examples/wayland/fancy-compositor/CMakeLists.txt52
-rw-r--r--examples/wayland/fancy-compositor/doc/src/fancy-compositor.qdoc104
-rw-r--r--examples/wayland/fancy-compositor/fancy-compositor.pro18
-rw-r--r--examples/wayland/fancy-compositor/fancy-compositor.qrc9
-rw-r--r--examples/wayland/fancy-compositor/images/background.jpgbin0 -> 30730 bytes
-rw-r--r--examples/wayland/fancy-compositor/main.cpp21
-rw-r--r--examples/wayland/fancy-compositor/qml/Chrome.qml68
-rw-r--r--examples/wayland/fancy-compositor/qml/CompositorScreen.qml83
-rw-r--r--examples/wayland/fancy-compositor/qml/Keyboard.qml15
-rw-r--r--examples/wayland/fancy-compositor/qml/main.qml38
10 files changed, 408 insertions, 0 deletions
diff --git a/examples/wayland/fancy-compositor/CMakeLists.txt b/examples/wayland/fancy-compositor/CMakeLists.txt
new file mode 100644
index 000000000..437542331
--- /dev/null
+++ b/examples/wayland/fancy-compositor/CMakeLists.txt
@@ -0,0 +1,52 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(fancy-compositor LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/wayland/fancy-compositor")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml)
+
+qt_add_executable(fancy-compositor
+ main.cpp
+)
+
+set_target_properties(fancy-compositor PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(fancy-compositor PUBLIC
+ Qt::Core
+ Qt::Gui
+ Qt::Qml
+)
+
+# Resources:
+set(fancy-compositor_resource_files
+ "images/background.jpg"
+ "qml/Chrome.qml"
+ "qml/CompositorScreen.qml"
+ "qml/Keyboard.qml"
+ "qml/main.qml"
+)
+
+qt6_add_resources(fancy-compositor "fancy-compositor"
+ PREFIX
+ "/"
+ FILES
+ ${fancy-compositor_resource_files}
+)
+
+install(TARGETS fancy-compositor
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/wayland/fancy-compositor/doc/src/fancy-compositor.qdoc b/examples/wayland/fancy-compositor/doc/src/fancy-compositor.qdoc
new file mode 100644
index 000000000..4d256d2af
--- /dev/null
+++ b/examples/wayland/fancy-compositor/doc/src/fancy-compositor.qdoc
@@ -0,0 +1,104 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \title Fancy Compositor
+ \example fancy-compositor
+ \examplecategory {Embedded}
+ \brief Fancy Compositor is an example that demonstrates how to write a fancy Wayland compositor in pure QML.
+ \ingroup qtwaylandcompositor-examples
+
+ \section1 Introduction
+
+ Fancy Compositor is a small desktop-style Wayland compositor example that demonstrates the power and
+ ease of the \l{Qt Wayland Compositor} QML APIs.
+
+ The Fancy Compositor example is similar to the \l{Minimal QML}{Minimal QML example}, in that it is a
+ full-blown Wayland compositor, implemented only using QML code.
+
+ \section1 Initializing the Compositor
+
+ Like the \l{Minimal QML}{Minimal QML example}, Fancy Compositor supports the main
+ \l{Shell Extensions - Qt Wayland Compositor}{shell extensions} that are supported by Qt.
+
+ \snippet fancy-compositor/qml/main.qml shell extensions
+
+ These are instantiated as children of the \l{WaylandCompositor} which automatically adds
+ them to the list of supported interfaces which is broadcasted to clients from the server.
+
+ When a connected client creates a surface and binds it to one of the shell extensions, the
+ corresponding signal is emitted. This then calls a method inside our custom \l WaylandOutput
+ class, which appends the \l ShellSurface to a \l{ListModel}.
+
+ \snippet fancy-compositor/qml/CompositorScreen.qml handleShellSurface
+
+ This model is used as the source for a \l Repeater which creates
+ \l{ShellSurfaceItem}{ShellSurfaceItems} inside the compositor's \l WaylandOutput. This adds a
+ view of the surface in the Qt Quick scene. Since it is a \l{ShellSurfaceItem}, it also has
+ certain interaction options for the user of the compositor, depending on which shell extension
+ is in use.
+
+ \snippet fancy-compositor/qml/CompositorScreen.qml repeater
+
+ \section1 Keyboard
+
+ In addition to the basic windowing system functions, the Fancy Compositor also supports an
+ optional on-screen keyboard running in-process. This uses the \l{Qt Virtual Keyboard} module,
+ and will be enabled if the module is available.
+
+ \snippet fancy-compositor/qml/Keyboard.qml keyboard
+
+ The code is simple. We instantiate an \l InputPanel in the bottom of the output, and make sure
+ it is visible if and only if it is currently active.
+
+ \snippet fancy-compositor/qml/CompositorScreen.qml keyboard
+
+ The keyboard is then added to the \l WaylandOutput using a \l Loader element. The \l Loader is
+ used here to avoid having a hard dependency on the \l{Qt Virtual Keyboard} module. If loading
+ fails, then the compositor will continue operating normally, but without support for an
+ on-screen keyboard.
+
+ Finally, we need a way for the compositor to communicate the text input to its clients. This
+ is done via a \c{text-input} extension. The Fancy Compositor example supports both the
+ \c{text_input_unstable_v2} protocol as well as Qt's \c{qt_text_input_method_unstable_v1}
+ protocol.
+
+ \snippet fancy-compositor/qml/main.qml text input
+
+ The \c{qt_text_input_method_unstable_v1} extension is added to the compositor by instantiating
+ the \l QtTextInputMethodManager as a child of the \l{WaylandCompositor}, and
+ \l{TextInputManager} adds \c{text_input_unstable_v2}.
+
+ Newer Qt applications will pick \c{qt_text_input_method_unstable_v1} when it is available,
+ while other clients can use \c{text_input_unstable_v2}.
+
+ \section1 Transitions
+
+ In addition to the basic functionality, the Fancy Compositor example also demonstrates animated
+ transitions between states.
+
+ The first of these is the \e{activation} transition. This is only supported on the \l{XdgShell},
+ since this is the only shell extension which has an \l{XdgToplevel::}{activated} state.
+
+ \snippet fancy-compositor/qml/Chrome.qml activation
+
+ When a client window becomes activated under the \l XdgShell protocol, we trigger an animation
+ which makes the window "pop out" for 200 ms.
+
+ The Fancy Compositor also supports a \e{destruction} animation. This triggers whenever the
+ window closes and surface is destroyed, whether this was because the client gracefully closed
+ its window, or even if it crashes.
+
+ \snippet fancy-compositor/qml/Chrome.qml destruction
+
+ To ensure that the content exists for the duration of the animation, we start by locking the
+ buffer. This means the final frame rendered by the client will remain in memory until we are
+ done with it.
+
+ Again, we trigger an animation on the scale of the item. The animation in question imitates
+ turning off the power on a CRT screen, giving a visual clue to the user that the window is
+ closing, and didn't just vanish into thin air.
+
+ Any sort of animated effect may be used for state changes such as these, with the full range
+ of Qt Quick at your disposal.
+*/
diff --git a/examples/wayland/fancy-compositor/fancy-compositor.pro b/examples/wayland/fancy-compositor/fancy-compositor.pro
new file mode 100644
index 000000000..bd6a2923f
--- /dev/null
+++ b/examples/wayland/fancy-compositor/fancy-compositor.pro
@@ -0,0 +1,18 @@
+QT += gui qml
+
+SOURCES += \
+ main.cpp
+
+OTHER_FILES = \
+ qml/main.qml \
+ qml/CompositorScreen.qml \
+ qml/Chrome.qml \
+ qml/Keyboard.qml \
+ images/background.jpg \
+
+RESOURCES += fancy-compositor.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/wayland/fancy-compositor
+sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS fancy-compositor.pro
+sources.path = $$[QT_INSTALL_EXAMPLES]/wayland/fancy-compositor
+INSTALLS += target sources
diff --git a/examples/wayland/fancy-compositor/fancy-compositor.qrc b/examples/wayland/fancy-compositor/fancy-compositor.qrc
new file mode 100644
index 000000000..145b3f250
--- /dev/null
+++ b/examples/wayland/fancy-compositor/fancy-compositor.qrc
@@ -0,0 +1,9 @@
+<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>
+ </qresource>
+</RCC>
diff --git a/examples/wayland/fancy-compositor/images/background.jpg b/examples/wayland/fancy-compositor/images/background.jpg
new file mode 100644
index 000000000..445567fbd
--- /dev/null
+++ b/examples/wayland/fancy-compositor/images/background.jpg
Binary files differ
diff --git a/examples/wayland/fancy-compositor/main.cpp b/examples/wayland/fancy-compositor/main.cpp
new file mode 100644
index 000000000..dca8dfe01
--- /dev/null
+++ b/examples/wayland/fancy-compositor/main.cpp
@@ -0,0 +1,21 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#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/fancy-compositor/qml/Chrome.qml b/examples/wayland/fancy-compositor/qml/Chrome.qml
new file mode 100644
index 000000000..b8d71c756
--- /dev/null
+++ b/examples/wayland/fancy-compositor/qml/Chrome.qml
@@ -0,0 +1,68 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtWayland.Compositor
+
+ShellSurfaceItem {
+ id: chrome
+
+ property bool isChild: parent.shellSurface !== undefined
+
+ signal destroyAnimationFinished
+
+ // ![destruction]
+ onSurfaceDestroyed: {
+ bufferLocked = true;
+ destroyAnimation.start();
+ }
+
+ 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: destroyAnimationFinished() }
+ }
+ // ![destruction]
+
+ transform: [
+ Scale {
+ id: scaleTransform
+ origin.x: chrome.width / 2
+ origin.y: chrome.height / 2
+ }
+ ]
+
+ // ![activation]
+ Connections {
+ target: shellSurface.toplevel !== undefined ? shellSurface.toplevel : null
+
+ // some signals are not available on wl_shell, so let's ignore them
+ ignoreUnknownSignals: true
+
+ function onActivatedChanged() { // xdg_shell only
+ if (shellSurface.toplevel.activated) {
+ receivedFocusAnimation.start();
+ }
+ }
+ }
+
+ 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 }
+ }
+ }
+ // ![activation]
+}
diff --git a/examples/wayland/fancy-compositor/qml/CompositorScreen.qml b/examples/wayland/fancy-compositor/qml/CompositorScreen.qml
new file mode 100644
index 000000000..4b2ba36d6
--- /dev/null
+++ b/examples/wayland/fancy-compositor/qml/CompositorScreen.qml
@@ -0,0 +1,83 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Window
+import QtWayland.Compositor
+
+WaylandOutput {
+ id: output
+
+ property ListModel shellSurfaces: ListModel {}
+ property bool isNestedCompositor: Qt.platform.pluginName.startsWith("wayland") || Qt.platform.pluginName === "xcb"
+
+ // ![handleShellSurface]
+ 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: 1024
+ height: 760
+ 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 {
+ model: output.shellSurfaces
+ // Chrome displays a shell surface on the screen (See Chrome.qml)
+ Chrome {
+ shellSurface: modelData
+ onDestroyAnimationFinished: output.shellSurfaces.remove(index)
+ }
+ }
+ // ![repeater]
+ }
+
+ // Virtual Keyboard
+ // ![keyboard]
+ Loader {
+ anchors.fill: parent
+ source: "Keyboard.qml"
+ }
+ // ![keyboard]
+
+ // 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/examples/wayland/fancy-compositor/qml/Keyboard.qml b/examples/wayland/fancy-compositor/qml/Keyboard.qml
new file mode 100644
index 000000000..2985ffcbd
--- /dev/null
+++ b/examples/wayland/fancy-compositor/qml/Keyboard.qml
@@ -0,0 +1,15 @@
+// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+// ![keyboard]
+import QtQuick
+import QtQuick.VirtualKeyboard
+
+InputPanel {
+ visible: active
+ y: active ? parent.height - height : parent.height
+ anchors.left: parent.left
+ anchors.right: parent.right
+}
+// ![keyboard]
+
diff --git a/examples/wayland/fancy-compositor/qml/main.qml b/examples/wayland/fancy-compositor/qml/main.qml
new file mode 100644
index 000000000..87feedf14
--- /dev/null
+++ b/examples/wayland/fancy-compositor/qml/main.qml
@@ -0,0 +1,38 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtWayland.Compositor
+import QtWayland.Compositor.XdgShell
+import QtWayland.Compositor.WlShell
+import QtWayland.Compositor.IviApplication
+
+WaylandCompositor {
+ id: waylandCompositor
+
+ CompositorScreen { id: screen; compositor: waylandCompositor }
+
+ // ![shell extensions]
+ // Shell surface extension. Needed to provide a window concept for Wayland clients.
+ // I.e. requests and events for maximization, minimization, resizing, closing etc.
+ XdgShell {
+ onToplevelCreated: (toplevel, xdgSurface) => screen.handleShellSurface(xdgSurface)
+ }
+
+ // Minimalistic shell extension. Mainly used for embedded applications.
+ IviApplication {
+ onIviSurfaceCreated: (iviSurface) => screen.handleShellSurface(iviSurface)
+ }
+
+ // Deprecated shell extension, still used by some clients
+ WlShell {
+ onWlShellSurfaceCreated: (shellSurface) => screen.handleShellSurface(shellSurface)
+ }
+ // ![shell extensions]
+
+ // Extension for Input Method (QT_IM_MODULE) support at compositor-side
+ // ![text input]
+ TextInputManager {}
+ QtTextInputMethodManager {}
+ // ![text input]
+}