Application Features Example
Showcases client applications with various features, including a native application.
Introduction
This example demonstrates how to implement some particular features you may require in an application, such as:
- how to implement a nested compositor
- how to simulate a crash and recover from it
- how to show multiple top-level windows simultaneously
- how to use the native runtime, with no QML
Most of these features are only supported properly in multi-process mode.
Note: This example focuses on the application (client) side. The System UI (compositor/server) is based on the Desktop System UI Example with some modifications. Refer to that example for more details on how to implement a System UI.
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 WAYLAND_DISPLAY
environment variable appropriately.
To start a client with this environment variable set via command line:
WAYLAND_DISPLAY=qtam-wayland-nested qml client.qml -platform wayland
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:
import QtQuick 2.11 import QtApplicationManager.Application 2.0 import QtWayland.Compositor 1.3 import QtWayland.Compositor.XdgShell import QtWayland.Compositor.WlShell ApplicationManagerWindow { id: root color: "lightgrey" property ListModel shellSurfaces: ListModel {} Text { anchors.fill: parent anchors.margins: 8 font.pointSize: 14 wrapMode: Text.Wrap textFormat: Text.RichText text: "This Wayland<sup>*</sup> client window implements a Wayland compositor (nested compositor). " + "To display Wayland clients here, set:<br><br><b>WAYLAND_DISPLAY=qtam-wayland-nested</b>" + "<br><br>For instance:<br>WAYLAND_DISPLAY=qtam-wayland-nested qml client.qml -platform wayland" + "<br><br><small>* in multi-process mode</small>" } WaylandCompositor { socketName: "qtam-wayland-nested" WaylandOutput { window: root sizeFollowsWindow: true } WlShell { onWlShellSurfaceCreated: (shellSurface) => shellSurfaces.append({shellSurface: shellSurface}); } XdgShell { onToplevelCreated: (toplevel, xdgSurface) => shellSurfaces.append({shellSurface: xdgSurface}); } } Repeater { model: shellSurfaces ShellSurfaceItem { shellSurface: modelData onSurfaceDestroyed: shellSurfaces.remove(index) } } Component.onCompleted: console.info("Start a client application in the nested compositor for instance with:\n " + "WAYLAND_DISPLAY=qtam-wayland-nested QT_WAYLAND_DISABLE_WINDOWDECORATION=1 " + "QT_WAYLAND_SHELL_INTEGRATION=xdg-shell qml client.qml -platform wayland"); }
Crash Simulation and Recovery
This application provides various means to force a crash in an application, such as a segmentation fault. It utilizes a QML plugin implemented in C++, to provide the Terminator
QML type 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.
This application only works on 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:
import QtQuick 2.11 import QtApplicationManager.Application 2.0 import Terminator 2.0 ApplicationManagerWindow { id: root property var methods: ({ illegalMemory: "Illegal memory access", illegalMemoryInThread: "Illegal memory access in a thread", stackOverflow: "Force stack overflow", divideByZero: "Divide by zero", unhandledException: "Throw unhandled exception", abort: "Call abort", raise: "Raise signal 7", gracefully: "Exit gracefully" }) function accessIllegalMemory() { Terminator.accessIllegalMemory(); } Grid { columns: 2 Repeater { model: Object.keys(methods) Rectangle { width: root.width / 2 height: root.height / 4 border.width: 1 color: "lightgrey" Text { anchors.fill: parent horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter wrapMode: Text.Wrap font.pointSize: 14 text: methods[modelData] } MouseArea { anchors.fill: parent onClicked: { switch (modelData) { case "illegalMemory": accessIllegalMemory(); break; case "illegalMemoryInThread": Terminator.accessIllegalMemoryInThread(); break; case "stackOverflow": Terminator.forceStackOverflow(); break; case "divideByZero": Terminator.divideByZero(); break; case "unhandledException": Terminator.throwUnhandledException(); break; case "abort": Terminator.abort(); break; case "raise": Terminator.raise(7); break; case "gracefully": Terminator.exitGracefully(); break; } } } } } } }
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:
import QtQuick 2.11 import QtApplicationManager.Application 2.0 QtObject { property var win1: ApplicationManagerWindow { color: "lightsteelblue" Rectangle { width: 80; height: 80; radius: 40 color: "orange" MouseArea { anchors.fill: parent drag.target: parent } } } property var win2: ApplicationManagerWindow { color: "transparent" Rectangle { id: rect anchors.fill: parent color: "orange" opacity: 0.4 } ApplicationManagerWindow { id: popup width: 300; height: 100 Rectangle { anchors.fill: parent border.width: 1 color: "orangered" } Text { anchors.centerIn: parent text: "Click to hide!" } MouseArea { anchors.fill: parent onClicked: popup.visible = false; } Component.onCompleted: setWindowProperty("type", "pop-up"); } } }
Native Widgets
This application is based on QWidgets. 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 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 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 AM_COMPILING_LAUNCHER
:
target_compile_definitions(widgets PRIVATE AM_COMPILING_LAUNCHER)
The C++ code for the native widgets application is as follows:
#include <QApplication> #include <QPushButton> #include <QDialog> #include <QVBoxLayout> #include <QtAppManCommon/logging.h> #include <QtAppManLauncher/launchermain.h> #include <QtAppManLauncher/dbusapplicationinterface.h> #include <QtAppManLauncher/dbusnotification.h> int main(int argc, char *argv[]) { QtAM::Logging::initialize(argc, argv); QtAM::Logging::setApplicationId("widgets"); QtAM::LauncherMain::initialize(); QApplication app(argc, argv); QtAM::LauncherMain launcher; launcher.registerWaylandExtensions(); launcher.loadConfiguration(); launcher.setupLogging(false, launcher.loggingRules(), QString(), launcher.useAMConsoleLogger()); launcher.setupDBusConnections(); QWidget window; QVBoxLayout layout(&window); // Popup using application manager window property QPushButton button1(QStringLiteral("Click to open/close a popup")); button1.setStyleSheet(QStringLiteral("QPushButton { background-color : limegreen; font-size: 36px; }")); layout.addWidget(&button1); QDialog *popup1 = new QDialog(&window); (new QPushButton(QStringLiteral("I'm a popup!"), popup1))->resize(340, 140); popup1->setStyleSheet(QStringLiteral("QPushButton { background-color : limegreen; color : white; font-size: 24px; }")); QObject::connect(&button1, &QPushButton::clicked, [&popup1, &launcher] () { popup1->setVisible(!popup1->isVisible()); launcher.setWindowProperty(popup1->windowHandle(), QStringLiteral("type"), QStringLiteral("pop-up")); }); // Notification QPushButton button2(QStringLiteral("Click to open a notification")); button2.setStyleSheet(QStringLiteral("QPushButton { background-color : darkgrey; font-size: 36px; }")); layout.addWidget(&button2); QtAM::DBusNotification *notification = QtAM::DBusNotification::create(&app); notification->setSummary(QStringLiteral("I'm a notification")); QObject::connect(&button2, &QPushButton::clicked, notification, &QtAM::DBusNotification::show); // Application interface for handling quit QtAM::DBusApplicationInterface iface(launcher.p2pDBusName(), launcher.notificationDBusName()); iface.initialize(); QObject::connect(&iface, &QtAM::DBusApplicationInterface::quit, [&iface] () { iface.acknowledgeQuit(); }); app.processEvents(); window.showNormal(); return app.exec(); }
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 qmake
the -config enable-examples
parameter For more details, see Build.
© 2024 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.