diff options
Diffstat (limited to 'examples/wayland/fancy-compositor')
-rw-r--r-- | examples/wayland/fancy-compositor/CMakeLists.txt | 52 | ||||
-rw-r--r-- | examples/wayland/fancy-compositor/doc/src/fancy-compositor.qdoc | 104 | ||||
-rw-r--r-- | examples/wayland/fancy-compositor/fancy-compositor.pro | 18 | ||||
-rw-r--r-- | examples/wayland/fancy-compositor/fancy-compositor.qrc | 9 | ||||
-rw-r--r-- | examples/wayland/fancy-compositor/images/background.jpg | bin | 0 -> 30730 bytes | |||
-rw-r--r-- | examples/wayland/fancy-compositor/main.cpp | 21 | ||||
-rw-r--r-- | examples/wayland/fancy-compositor/qml/Chrome.qml | 68 | ||||
-rw-r--r-- | examples/wayland/fancy-compositor/qml/CompositorScreen.qml | 83 | ||||
-rw-r--r-- | examples/wayland/fancy-compositor/qml/Keyboard.qml | 15 | ||||
-rw-r--r-- | examples/wayland/fancy-compositor/qml/main.qml | 38 |
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 Binary files differnew file mode 100644 index 000000000..445567fbd --- /dev/null +++ b/examples/wayland/fancy-compositor/images/background.jpg 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] +} |