The System UI

The System UI is the part of a UI which starts and stops any application, on a device. It's responsible for managing the application windows and compositing them in a specific way. For example, to display the applications in full-screen, or to allow multiple windows to be displayed from different applications at the same time.

On desktop systems, the System UI is akin to the Microsoft Windows' Shell, or KDE's Plasmashell plus the KWin compositor.

The System UI has the following tasks:

TaskDescription
CompositingManages multiple applications' surfaces, known as windows.
Arrange these windows on one or more physical screens, according to the required design.
Handle window decoration, transition effects and other system-wide user experience.
Communication InterfaceAct as a channel to exchange information between applications and itself, such as to switch the language.
Input HandlingSwitch focus between input events, according to the user's current focus.
Provide system input services, such as launch a virtual keyboard for touch-based systems.
Manage system-wide applications and servicesManage the application life-cycle, by starting applications upon user interaction and stopping them in critical situations, such as low memory.
Provide a central UI to monitor the UI performance and all applications that have started, by showing statistics such as frame rate, the amount of CPU currently used, and memory resources.
Manage idle time and display the Home ScreenDisplay the default view to the user after startup.
Display essential data, such as the current time.
Display a view composed of several selected applications.

Implement a System UI

The System UI's main feature is its ability to manage multiple applications and windows. Consequently, Linux-based systems are the recommended development environment, where multi-process mode is enabled. On other Operating Systems, you can use the single-process mode. For more information on the difference between single-process and multi-process, see Single-Process vs. Multi-Process Mode and Wayland and Qt.

Window Management

The System UI's central role is to handle application windows. On the client side, you need to use an ApplicationManagerWindow. On the System UI side, you need to implement a handler for WindowManager::windowAdded. Whenever a client window becomes visible, the WindowManager::windowAdded signal is emitted. The signal has one parameter, WindowObject, which is the System UI side representation of the client side ApplicationManagerWindow.

To differentiate between client windows, you should use window properties. For instance, you could attach a type property to a client window, to tell the System UI, whether it's a top-level window, a popup, or something else. Both client and server side window representations can access these properties. Typically, they are always in-sync; but the underlying Wayland protocol is entirely asynchronous.

To include the WindowObject in the System UI's render tree, set the WindowObject as the window property for a WindowItem. The WindowItem acts as a container Item for WindowObjects. Consequently, the WindowItem determines the position, visibility, and so on, of the client window in the System UI.

One example of a simple System UI is the one written for the "Hello World!" System UI Example:

Item {
    width: 800
    height: 600

    // Show application names and icons
    Column {
        spacing: 20
        Repeater {
            model: ApplicationManager
            Column {
                Image {
                    source: model.icon
                    MouseArea {
                        anchors.fill: parent
                        onClicked: model.isRunning ? application.stop() : application.start()
                    }
                }
                Text {
                    font.pixelSize: 20
                    text: model.name
                }
            }
        }
    }

    // Show windows
    Column {
        anchors.right: parent.right
        Repeater {
            model: WindowManager
            WindowItem {
                width: 600
                height: 200
                window: model.window
            }
        }
    }
}

Notifications

In cases where you'd like to display notifications or pop-ups, applications or clients can create these with ApplicationInterface::createNotification(). They are then made available on the System UI via the NotificationManager.

The code snippet below is part of the Desktop System UI Example that illustrates how to display pop-ups.

    // System UI for a notification
    Text {
        z: 9999
        font.pixelSize: 46
        anchors.centerIn: parent
        text: NotificationManager.count > 0 ? NotificationManager.get(0).summary : ""
    }

Intents

The System UI and other applications can send and receive intents. This is exposed via the IntentObject and IntentServer QML types. The Intent represents a single intent definition on the System UI; the IntentServer is the singleton on the System UI-side that represents the intents sub-system.

For more details on implementing support for intents, see the Intents System UI and Applications Example.

Life-Cycle

The ApplicationManager provides APIs to start and stop an application. However, the recommended approach is to use ApplicationObject::start() and ApplicationObject::stop(). Optionally, you can also pass a documentUrl in the start() function. Then, when you call start() several times, with a different documentUrl() each time, you won't be restarting the application; but only triggering the ApplicationInterface::openDocument() on the application or client side. ApplicationObject::stop() triggers ApplicationInterface::quit() on the application side. The application should do all the necessary clean-ups and then confirm that it can be terminates with ApplicationInterface::acknowledgeQuit().

You can implement other life-cycle management features that are tailored to the specific requirements of IPC mechanisms like window properties, Intents, or proprietary IPCs.

For more information, see the Desktop System UI Example.

Monitoring

To monitor your application, you can use the MonitorModel to fetch data from various sources at intervals and store a history of these values. You can use this data for analytical purposes, such as to plot its previous values over time. For more information, see the Display Information about Application Processes Example.

Application Installer

The ApplicationManager provides a list of all available applications in the ApplicationModel. In addition to applications bundled with the System UI, system applications, the ApplicationManager also provides a way to install new applications at runtime. These applications are maintained by the ApplicationInstaller singleton.

To start an installation, use ApplicationInstaller::startPackageInstallation. Once all metadata from the application's package is extracted, the ApplicationInstaller::taskRequestingInstallationAcknowledge signal is emitted. This signal can be used to give you more information about the package, such as the name, size, or permissions. This installation needs to be confirmed using ApplicationInstaller::acknowledgePackageInstallation. After the installation is complete, you can start the new application, as described in the Life-Cycle. To remove applications that have been installed, use ApplicationInstaller::removePackage.

For more information, see Application Installer.

Best Practices When You Write a System UI

Below are some key practices to consider when you write a System UI:

  • Always test your System UI and its applications on the target as early as possible. This is especially important because the combination of your target hardware and the asynchronous Wayland protocol may result in some timing constraints on some parts of the system. For example, when are window properties available to the System UI, after an application has started. These constraints are best identified as early as possible. To be able to run a fluent UI, hardware acceleration is necessary for the System UI as well as for applications (or Wayland clients). This fluency is achieved via hardware specific texture sharing mechanisms. Now, these mechanisms are hardware specific and it's likely that a different mechanism is used on the target platform, compared to your development machine. Testing on the target hardware as early as possible can help to bring forward any issues with the sharing mechanism or side effects from other graphic intense elements, such as shader effects or 3D engine integration.
  • Allow the System UI to stop applications that take up too much memory or CPU. Design your system in such a way that lets the System UI stop applications when necessary, particularly in situations where hardware resources like memory or CPU is low. This provides better scalability.
  • Always use two different plugin folders. One folder for System UI-specific or privileged applications. Another folder for other applications that contain base elements, such as your UI style or the Items for your different window types.

Notes on The Root Element

  • If the root element of the System UI is an Item, the application manager creates a QQuickWindow for it and sets the Item as its root Item.
  • If the root element of the System UI is a Window, or an Item that is wrapped in a Window the window is shown initially, regardless of the value of its visible property.

© 2021 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.