// Copyright (C) 2021 The Qt Company Ltd. // Copyright (C) 2019 Luxoft Sweden AB // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! \example applicationmanager/application-features \title Application Features Example \image application-features.png \brief Showcases client applications with various features and QML modularization. \ingroup applicationmanager-examples \note Please \l{wayland-dev-packages}{read this} if you want to build the example on a Linux machine. \section1 Introduction This example demonstrates how to implement some particular features you may require in an application, such as: \list \li how to implement a nested compositor \li how to simulate a crash and recover from it \li how to analyze glitches with the watchdog \li how to show multiple top-level windows simultaneously \li how to use the native runtime, with no QML \endlist Most of these features are only supported properly in \l{Single-Process vs. Multi-Process Mode}{multi-process mode}. The example focuses on the application (client) side. The \l{The System UI}{System UI} (compositor/server) is based on the \l{Desktop System UI Example} with some modifications. Refer to that example for more details on how to implement a System UI. In addition, this example shows how to write and use \l{Writing QML Modules}{QML modules} in the application manager context, see \l{Code Structure}{below}. \section2 Nested Compositor The nested compositor application shows how to implement a Wayland compositor inside an application (Wayland client) window. The compositor is implemented in pure QML and kept to a minimum. To display Wayland clients within this compositor, you need to set the \c WAYLAND_DISPLAY environment variable appropriately. To start a client with this environment variable set via command line: \badcode WAYLAND_DISPLAY=qtam-wayland-nested qml client.qml -platform wayland \endcode This command only works in multi-process mode, since the nested compositor needs a real window as its root element. The QML code for the nested compositor application is as follows: \quotefromfile applicationmanager/application-features/apps/Compositor/Compositor.qml \skipto import QtQuick \printuntil /^\}/ \section2 Crash Simulation and Recovery This application provides various means to force a crash in an application, such as a segmentation fault. It utilizes QML modules that include C++ and QML code; in particular, the C++ provided QML types \c Terminator1 and \c Terminator2 are used to trigger crashes. The application manager then prints the cause of the crash and related information, like a backtrace. The System UI implements a basic form of crash recovery: restarting the application. Of course, crash recovery only works in multi-process mode. In single-process mode, a crash affects the entire program (the System UI). The QML code for the crash simulation and recovery application is as follows: \quotefromfile applicationmanager/application-features/apps/Crash/crashapp.qml \skipto import QtQuick \printuntil crashAnimation.mode = parent.modelData; \printuntil /^\}/ \section2 Analyze Glitches with the Watchdog This application allows to introduce delays in the program execution at various points: \list \li Sleep GUI: pauses the main (GUI) thread of the application for the given number of seconds \li Sleep sync: pauses the render thread (if the threaded render loops is used) in the sync state. QQuickItems' \c updatePaintNode methods are called in the sync state for instance. \li Sleep render: pauses the render thread (if the threaded render loop is used) in the render state. Backend (OpenGL, Vulkan, etc.) draw commands are executed in the render state. \endlist The application manager's \l{Watchdog} is enabled in the System UI's, as well as the application's configuration file. Warnings will be printed, if the application is stuck for a certain amount of time and it might even be killed if it is stuck for too long. \section2 Two Top-Level Windows This application illustrates how you can display multiple top-level windows by having a QtObject as the application's root element. The QML code for the two top-level windows application is as follows: \quotefromfile applicationmanager/application-features/apps/Twins/twins.qml \skipto import QtQuick \printuntil Component.onCompleted: setWindowProperty("type", "pop-up"); \printuntil /^\}/ \section2 Native Widgets This application is based on \l{QWidget}s. Compared to the other applications in this example, which are QML applications, this one uses the native runtime. Consequently, the application's entry point isn't a \c{main.qml} file, but an executable. This application is a basic application that still adheres to this particular System UI. It's meant to illustrate the concept: the System UI needs a \c type window property to differentiate between normal windows and popups. This application only works in multi-process mode, as application processes cannot be started in single-process mode. Linking against the private application manager modules is prohibited by default to prevent potential problems with duplicate symbols coming from QML plugins. However, here building against them is both intended and required, so we need to set the define \c AM_COMPILING_LAUNCHER: \quotefromfile applicationmanager/application-features/apps/Widgets/CMakeLists.txt \skipuntil /flags us as a "launcher"/ \printuntil /AM_COMPILING_LAUNCHER/ The C++ code for the native widgets application is as follows: \quotefromfile applicationmanager/application-features/apps/Widgets/main.cpp \skipto #include \printuntil /^\}/ \section1 Code Structure Compared to the other Qt Application Manager Examples, which are purely based on QML, this example requires you to build it explicitly. The code is structured in a way where the resulting application folders only contain the artifacts necessary to run the application. Consequently, you can package these applications and install them as well. To build Qt Application Manager, including its examples, you need to pass \c{-DQT_BUILD_EXAMPLES=ON} to CMake. For more details, see \l{build}{Build}. The System UI and all applications produce QML modules. These modules are generated as libraries that include all the C++ and QML code and other resources, like images. Consequently, only the libraries need to be loaded from the native file system instead of individual QML files, images, or other such assets. As an additional bonus, you get things like pre-compiled QML, linting checks, and auto-generation of files like qmldir. \section2 System UI QML Module In the \c{CMakeLists.txt} file, the \c QML module is directly compiled into the application-feature executable: \quotefromfile applicationmanager/application-features/CMakeLists.txt \skipto qt_add_qml_module(application-features \printuntil /^\)$/ To keep it simple, the module is kept in a directory with the same name as its URI (\c SystemUi). Two images and the \c main.qml file are added to the module (there is no C++ code used, though). \section2 Crash Application QML Modules The Crash application consists of two QML modules: \c Crash_module and \c sequelmoduleplugin. In the \c{apps/Crash/CMakeLists.txt} file, the \c Crash_module is defined: \quotefromfile applicationmanager/application-features/apps/Crash/CMakeLists.txt \printuntil /^\)$/ The default "qt/qml" resource prefix is used here. By providing the \c NO_PLUGIN keyword, only a dynamic backing library is created. A QML plugin is unnecessary since the library will be loaded explicitly (see below). To keep it simple, the module is kept in a directory with the same name as its URI (\c Crash). The \c crashapp.qml file is added to the module, in addition to the C++ file \c terminator1.cpp. The generated library is loaded explicitly when the application starts through the following lines in \c{apps/Crash/info.yaml}: \quotefromfile applicationmanager/application-features/apps/Crash/info.yaml \skipto runtimeParameters: \printuntil resources: It provides the \c crashapp.qml file, which is the application's main QML file and the \c Terminator1 QML singleton type. The second module, \c sequelmoduleplugin is defined as a QML plugin in \c{apps/Crash/Sequel/CMakeLists.txt}: \quotefromfile applicationmanager/application-features/apps/Crash/Sequel/CMakeLists.txt \printuntil /^\)$/ For convenience, \c PLUGIN_TARGET is defined with the same argument as the module target name. This creates a plugin that already includes the backing target (see \l{Plugin target with no backing target}{qt_add_qml_module}). The plugin is loaded implicitly at runtime through the "\c{import Sequel}" statement in \c crashapp.qml. It provides the \c CrashAnimation and \c Terminator2 QML types. */