Multi Screen

Multi Screen is a desktop-style Wayland compositor for multiple screens.

Introduction

Multi-screen is a desktop-style Wayland compositor example for multiple screens.

For an introduction to the basic principles of creating a Qt Wayland Compositor with Qt, see the Minimal QML example.

Supporting multiple screens

For each available screen on the system, the example creates a CompositorScreen, which is a subclass of WaylandOutput. If there is only one physical screen available, the example emulates three screens with dummy data.

 Instantiator {
     id: screens
     model: emulated ? emulatedScreens : Qt.application.screens

     delegate: CompositorScreen {
         surfaceArea.color: "lightsteelblue"
         text: name
         compositor: comp
         screen: emulated ? Qt.application.screens[0] : modelData
         Component.onCompleted: if (!comp.defaultOutput) comp.defaultOutput = this
         position: Qt.point(virtualX, virtualY)
         windowed: emulated
     }
 }

Each WaylandOutput contains a Window, which is used for containing client content, as is the standard setup with Qt Wayland Compositor. Since each Window is isolated from the others, we cannot share any Qt Quick items between them. Therefore, when a client connects and a ShellSurface is created, one ShellSurfaceItem is created on each of the screens.

 for (var i = 0; i < screens.count; ++i)
     createShellSurfaceItem(shellSurface, moveItem, screens.objectAt(i));

These items serve as views of the same client content. The client's surface itself will only be created once, and will be shared by the surface items.

Top-level surface items are created as children of the background Rectangle in each of the outputs. These views are stored for later, and if the client spawns any child windows, these are parented to the top-level window's item.

 var parentSurfaceItem = output.viewsBySurface[shellSurface.parentSurface];
 var parent = parentSurfaceItem || output.surfaceArea;

The client content will be visible only on one or two screens at a time. ShellSurfaceItem positions are synchronized so that when windows enter one screen, they are moved off of another at the same time. This gives the appearance of a single item which moves seamlessly between screens. The global position of the client is stored in a shared moveItem and relative position of each screen's ShellSurfaceItem is calculated based on this. If the moveItem is currently outside the bounds of one screen, its coordinates will reflect this, and it will not be visible on that screen.

 x: surfaceItem.moveItem.x - surfaceItem.output.geometry.x
 y: surfaceItem.moveItem.y - surfaceItem.output.geometry.y

Finally, WaylandQuickItem::setPrimary() is called at appropriate times to set the primary view for the ShellSurface, which is used when the client asks to be maximized or fullscreen. The primary ShellSurfaceItem is selected based on how much of it is currently visible.

Note: In order to support multiple Wayland outputs in the same compositor, the Qt::AA_ShareOpenGLContexts attribute must be set before the QGuiApplication object is constructed. In the example, we do this at the very beginning of the main() function.

 QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);

Example project @ code.qt.io

See also Multi Output.