How to configure Ubuntu Frame for multiple outputs

Contents

This document describes all the available options for using Ubuntu Frame with multiple displays.

The default

By default Ubuntu Frame will show the same content on all outputs. This works well for the simple case of one application showing one fullscreen window on multiple displays.

But sometimes this is not what is wanted, and Ubuntu Frame can do a lot more.

Version 98-mir2.13.0:

We have made a number of improvements in this area with release 98-mir2.13.0. At the time of writing this is on the beta channel, but will soon progress to candidate and then stable.

For documentation for the newer versions see Ubuntu Frame 98-mir2.13.0 and later below.

The display configuration option

By setting the display configuration option you can control very precisely how multiple outputs are used. However, there’s a lot of detail and it may not be obvious how to proceed. Because the configuration depends on the graphics cards and displays attached to your system the first step is to find the default .yaml configuration for your hardware.

Ubuntu Frame outputs this default .yaml to the log when it starts up. This is the output from one of my test systems:

$ snap logs -n 200 ubuntu-frame | cut -d ' ' -f 3- | awk '/^8></{flag=1; next}; /^\[/{flag=0} flag'
layouts:
# keys here are layout labels (used for atomically switching between them)
# when enabling displays, surfaces should be matched in reverse recency order
  default:                         # the default layout
    cards:
    # a list of cards (currently matched by card-id)
    - card-id: 0
      eDP-1:
        # This output supports the following modes: 2560x1440@60.0
        #
        # Uncomment the following to enforce the selected configuration.
        # Or amend as desired.
        #
        # state: enabled	# {enabled, disabled}, defaults to enabled
        # mode: 2560x1440@60.0	# Defaults to preferred mode
        # position: [0, 0]	# Defaults to [0, 0]
        # orientation: normal	# {normal, left, right, inverted}, defaults to normal
        # scale: 1
        # group: 0	# Outputs with the same non-zero value are treated as a single display
      DisplayPort-1:
        # (disconnected)
      HDMI-A-1:
        # (disconnected)
      DisplayPort-2:
        # (disconnected)
      HDMI-A-2:
        # This output supports the following modes: 1024x600@60.0, 1920x1080@60.0,
        # 1600x900@60.0, 1366x768@59.9, 1280x720@60.0, 1280x720@59.9
        #
        # Uncomment the following to enforce the selected configuration.
        # Or amend as desired.
        #
        # state: enabled	# {enabled, disabled}, defaults to enabled
        # mode: 1024x600@60.0	# Defaults to preferred mode
        # position: [0, 0]	# Defaults to [0, 0]
        # orientation: normal	# {normal, left, right, inverted}, defaults to normal
        # scale: 1
        # group: 0	# Outputs with the same non-zero value are treated as a single display

This is somewhat verbose, but the important thing is that there are two outputs: eDP-1 and HDMI-A-2 and under each there is a comment explaining the options Ubuntu Frame supports for each of them.

Note that, unless your setup matches mine, the exact details will differ on your system.

Changing the display configuration

I find it easiest to pipe this output to a file, make the changes in a text editor and pass the file content to the configuration option.

First, pipe the above output to a file:

$ snap logs -n 200 ubuntu-frame | cut -d ' ' -f 3- | awk '/^8></{flag=1; next}; /^\[/{flag=0} flag' > my-uf-display-configuration

Next edit the file with your editor of choice. Now, I’m going to show changing the scale of eDP-1 to “2” (which, because it has twice the pixels, makes the “height” of the displays the same) and position HDMI-A-2 by the side of it (instead of overlapping) and also change the mode so that it is the same height as eDP-1.

$ diff my-uf-display-configuration my-uf-display-configuration~
18c18
<         scale: 2
---
>         # scale: 1
34,35c34,35
<         mode: 1280x720@60.0	# Defaults to preferred mode
<         position: [2560, 0]	# Defaults to [0, 0]
---
>         # mode: 1024x600@60.0	# Defaults to preferred mode
>         # position: [0, 0]	# Defaults to [0, 0]

Here’s the full file:

$ cat my-uf-display-configuration
layouts:
# keys here are layout labels (used for atomically switching between them)
# when enabling displays, surfaces should be matched in reverse recency order
  default:                         # the default layout
    cards:
    # a list of cards (currently matched by card-id)
    - card-id: 0
      eDP-1:
        # This output supports the following modes: 2560x1440@60.0
        #
        # Uncomment the following to enforce the selected configuration.
        # Or amend as desired.
        #
        # state: enabled	# {enabled, disabled}, defaults to enabled
        # mode: 2560x1440@60.0	# Defaults to preferred mode
        # position: [0, 0]	# Defaults to [0, 0]
        # orientation: normal	# {normal, left, right, inverted}, defaults to normal
        scale: 2
        # group: 0	# Outputs with the same non-zero value are treated as a single display
      DisplayPort-1:
        # (disconnected)
      HDMI-A-1:
        # (disconnected)
      DisplayPort-2:
        # (disconnected)
      HDMI-A-2:
        # This output supports the following modes: 1024x600@60.0, 1920x1080@60.0,
        # 1600x900@60.0, 1366x768@59.9, 1280x720@60.0, 1280x720@59.9
        #
        # Uncomment the following to enforce the selected configuration.
        # Or amend as desired.
        #
        # state: enabled	# {enabled, disabled}, defaults to enabled
        mode: 1280x720@60.0	# Defaults to preferred mode
        position: [2560, 0]	# Defaults to [0, 0]
        # orientation: normal	# {normal, left, right, inverted}, defaults to normal
        # scale: 1
        # group: 0	# Outputs with the same non-zero value are treated as a single display

And here’s the command to apply these changes:

$ snap set ubuntu-frame display="`cat my-uf-display-configuration`"

With these changes the displays are “side by side” and the cursor can be moved from one display to another.

You will likely have to make slightly different changes on your system, but the approach should be clear.

A client window for each output

With the displays “side by side” as described in the previous section it should be possible to install and run a client snap on each output.

Now, this requires some co-operation between the client snap and Ubuntu Frame. The client snap can specify which output to place a fullscreen window on and, if it does so, Ubuntu Frame will respect that choice. Typically, clients that do this will request the first (or the largest) monitor which will result in them all appearing on the same output.

However, this means it is possible to create a snap (or snaps) that create multiple windows and place them on specific outputs.

For snaps that don’t explicitly request an output, Ubuntu Frame will “round robin” the outputs. This means that applications will appear on successive outputs:

A client window spanning outputs

Now, let’s make an additional change to the configuration and put both outputs into a group:

$ diff my-uf-display-configuration my-uf-display-configuration~
19c19
<         group: 1	# Outputs with the same non-zero value are treated as a single display
---
>         # group: 0	# Outputs with the same non-zero value are treated as a single display
38c38
<         group: 1	# Outputs with the same non-zero value are treated as a single display
---
>         # group: 0	# Outputs with the same non-zero value are treated as a single display

Now, Ubuntu Frame treats the “side by side” windows as one large display:


Ubuntu Frame 98-mir2.13.0 and later

Version 98-mir2.13.0:

We have made a number of improvements in this area with release 98-mir2.13.0. At the time of writing this is on the beta channel, but will soon progress to candidate and then stable.

The following documentation applies only to this version and later.

The display and display-layouts configuration options

By setting the display configuration option you can control very precisely how multiple outputs are used. However, there’s a lot of detail and it may not be obvious how to proceed. Because the configuration depends on the graphics cards and displays attached to your system the first step is to find the configuration for your hardware.

When it starts, Ubuntu Frame will, unless it is already set, populate the display configuration. This is the output from one of my test systems:

$ snap get ubuntu-frame display
layouts:
# keys here are layout labels (used for atomically switching between them).
# The yaml anchor 'the_default' is used to alias the 'default' label

  default:
    cards:
    # a list of cards (currently matched by card-id)

    - card-id: 0
      eDP-1:
        # This output supports the following modes: 2560x1440@60.0
        #
        # Uncomment the following to enforce the selected configuration.
        # Or amend as desired.
        #
        state: enabled	# {enabled, disabled}, defaults to enabled
        mode: 2560x1440@60.0	# Defaults to preferred mode
        position: [0, 0]	# Defaults to [0, 0]
        orientation: normal	# {normal, left, right, inverted}, defaults to normal
        scale: 1
        group: 0	# Outputs with the same non-zero value are treated as a single display

      DisplayPort-1:
        # (disconnected)

      HDMI-A-1:
        # (disconnected)

      DisplayPort-2:
        # (disconnected)

      HDMI-A-2:
        # This output supports the following modes: 1024x600@60.0, 1920x1080@60.0,
        # 1600x900@60.0, 1366x768@59.9, 1280x720@60.0, 1280x720@59.9
        #
        # Uncomment the following to enforce the selected configuration.
        # Or amend as desired.
        #
        state: enabled	# {enabled, disabled}, defaults to enabled
        mode: 1024x600@60.0	# Defaults to preferred mode
        position: [0, 0]	# Defaults to [0, 0]
        orientation: normal	# {normal, left, right, inverted}, defaults to normal
        scale: 1
        group: 0	# Outputs with the same non-zero value are treated as a single display

  side_by_side:
    cards:
    # a list of cards (currently matched by card-id)

    - card-id: 0
      eDP-1:
        # This output supports the following modes: 2560x1440@60.0
        #
        # Uncomment the following to enforce the selected configuration.
        # Or amend as desired.
        #
        state: enabled	# {enabled, disabled}, defaults to enabled
        mode: 2560x1440@60.0	# Defaults to preferred mode
        position: [0, 0]	# Defaults to [0, 0]
        orientation: normal	# {normal, left, right, inverted}, defaults to normal
        scale: 1
        group: 0	# Outputs with the same non-zero value are treated as a single display

      DisplayPort-1:
        # (disconnected)

      HDMI-A-1:
        # (disconnected)

      DisplayPort-2:
        # (disconnected)

      HDMI-A-2:
        # This output supports the following modes: 1024x600@60.0, 1920x1080@60.0,
        # 1600x900@60.0, 1366x768@59.9, 1280x720@60.0, 1280x720@59.9
        #
        # Uncomment the following to enforce the selected configuration.
        # Or amend as desired.
        #
        state: enabled	# {enabled, disabled}, defaults to enabled
        mode: 1024x600@60.0	# Defaults to preferred mode
        position: [2560, 0]	# Defaults to [0, 0]
        orientation: normal	# {normal, left, right, inverted}, defaults to normal
        scale: 1
        group: 0	# Outputs with the same non-zero value are treated as a single display

Note that, unless your setup matches mine, the exact details will differ on your system.

This is somewhat verbose, but you can see two layouts, the default and side_by_side. Within each of the layouts there are two outputs: eDP-1 and HDMI-A-2 and the settings used for the layout and a comment explaining the options Ubuntu Frame supports for each of them.

We can switch between these layouts (and any others you add to the display configuration) by setting the display-layout configuration variable. For example:

snap set ubuntu-frame display-layout=side_by_side

With these changes the displays are “side by side” and the cursor can be moved from one display to another. You’ll also see that the test application is only running on one output. It is possible to assign applications to outputs and we’ll discuss that later.

Changing the display configuration

I find it easiest to pipe the display configuration to a file and make the changes in a text editor and then pass the file content to the configuration option.

First, pipe the above output to a file and, for reference, make a backup of the original:

$ snap get ubuntu-frame display > my-uf-display-configuration
$ cp my-uf-display-configuration{,~original}

Next edit the file with your editor of choice. Now, I’m going to show changing the scale of eDP-1 to “2” (which, because it has twice the pixels, makes the “height” of the displays the same) in both layouts. In the side_by_side layout it is also necessary to change the position of the DisplayPort-1 output to match the new logical size of the first output:

diff --unified my-uf-display-configuration{~original,}
--- my-uf-display-configuration~original	2023-03-23 11:26:31.482290742 +0000
+++ my-uf-display-configuration	2023-03-23 12:24:36.974409249 +0000
@@ -17,7 +17,7 @@
         mode: 2560x1440@60.0	# Defaults to preferred mode
         position: [0, 0]	# Defaults to [0, 0]
         orientation: normal	# {normal, left, right, inverted}, defaults to normal
-        scale: 1
+        scale: 2
         group: 0	# Outputs with the same non-zero value are treated as a single display
 
       DisplayPort-1:
@@ -58,7 +58,7 @@
         mode: 2560x1440@60.0	# Defaults to preferred mode
         position: [0, 0]	# Defaults to [0, 0]
         orientation: normal	# {normal, left, right, inverted}, defaults to normal
-        scale: 1
+        scale: 2
         group: 0	# Outputs with the same non-zero value are treated as a single display
 
       DisplayPort-1:
@@ -79,7 +79,7 @@
         #
         state: enabled	# {enabled, disabled}, defaults to enabled
         mode: 1024x600@60.0	# Defaults to preferred mode
-        position: [2560, 0]	# Defaults to [0, 0]
+        position: [1280, 0]	# Defaults to [0, 0]
         orientation: normal	# {normal, left, right, inverted}, defaults to normal
         scale: 1
         group: 0	# Outputs with the same non-zero value are treated as a single display

You will likely have to make slightly different changes on your system, but the approach should be clear.

And here’s the command to apply these changes:

$ snap set ubuntu-frame display="`cat my-uf-display-configuration`"

A client window for each output

With the displays “side by side” as described in the previous section it should be possible to install and run an application snap on each output.

To specify which output an application appears on we need to add the snap name to the output within the layout. For example:

$ diff --unified my-uf-display-configuration{~original,}
--- my-uf-display-configuration~original	2023-03-23 11:26:31.482290742 +0000
+++ my-uf-display-configuration	2023-03-23 12:23:42.190975690 +0000
@@ -17,7 +17,7 @@
         mode: 2560x1440@60.0	# Defaults to preferred mode
         position: [0, 0]	# Defaults to [0, 0]
         orientation: normal	# {normal, left, right, inverted}, defaults to normal
-        scale: 1
+        scale: 2
         group: 0	# Outputs with the same non-zero value are treated as a single display
 
       DisplayPort-1:
@@ -49,6 +49,7 @@
 
     - card-id: 0
       eDP-1:
+        snap-name: mir-kiosk-kodi
         # This output supports the following modes: 2560x1440@60.0
         #
         # Uncomment the following to enforce the selected configuration.
@@ -58,7 +59,7 @@
         mode: 2560x1440@60.0	# Defaults to preferred mode
         position: [0, 0]	# Defaults to [0, 0]
         orientation: normal	# {normal, left, right, inverted}, defaults to normal
-        scale: 1
+        scale: 2
         group: 0	# Outputs with the same non-zero value are treated as a single display
 
       DisplayPort-1:
@@ -71,6 +72,7 @@
         # (disconnected)
 
       HDMI-A-2:
+        snap-name: iot-example-graphical-snap
         # This output supports the following modes: 1024x600@60.0, 1920x1080@60.0,
         # 1600x900@60.0, 1366x768@59.9, 1280x720@60.0, 1280x720@59.9
         #
@@ -79,7 +81,7 @@
         #
         state: enabled	# {enabled, disabled}, defaults to enabled
         mode: 1024x600@60.0	# Defaults to preferred mode
-        position: [2560, 0]	# Defaults to [0, 0]
+        position: [1280, 0]	# Defaults to [0, 0]
         orientation: normal	# {normal, left, right, inverted}, defaults to normal
         scale: 1
         group: 0	# Outputs with the same non-zero value are treated as a single display

And, again, apply these changes:

$ snap set ubuntu-frame display="`cat my-uf-display-configuration`"

This will move the test application to the second monitor; and, if I install mir-kiosk-kodi that will run on the main monitor.

There is also a window-title key that can be set, but as windows tend to change their title that is of limited usefulness.

A client window spanning outputs

Instead of assigning applications to outputs we can also put both outputs into a group so that one application will appear across them. Still within the side_by_side layout we remove the snap-name keys that we had in the last section and set group to 1:

$ diff --unified my-uf-display-configuration{~original,}
--- my-uf-display-configuration~original	2023-03-23 11:26:31.482290742 +0000
+++ my-uf-display-configuration	2023-03-23 12:17:26.790860939 +0000
@@ -17,7 +17,7 @@
         mode: 2560x1440@60.0	# Defaults to preferred mode
         position: [0, 0]	# Defaults to [0, 0]
         orientation: normal	# {normal, left, right, inverted}, defaults to normal
-        scale: 1
+        scale: 2
         group: 0	# Outputs with the same non-zero value are treated as a single display
 
       DisplayPort-1:
@@ -58,8 +58,8 @@
         mode: 2560x1440@60.0	# Defaults to preferred mode
         position: [0, 0]	# Defaults to [0, 0]
         orientation: normal	# {normal, left, right, inverted}, defaults to normal
-        scale: 1
-        group: 0	# Outputs with the same non-zero value are treated as a single display
+        scale: 2
+        group: 1	# Outputs with the same non-zero value are treated as a single display
 
       DisplayPort-1:
         # (disconnected)
@@ -79,7 +79,7 @@
         #
         state: enabled	# {enabled, disabled}, defaults to enabled
         mode: 1024x600@60.0	# Defaults to preferred mode
-        position: [2560, 0]	# Defaults to [0, 0]
+        position: [1280, 0]	# Defaults to [0, 0]
         orientation: normal	# {normal, left, right, inverted}, defaults to normal
         scale: 1
-        group: 0	# Outputs with the same non-zero value are treated as a single display
+        group: 1	# Outputs with the same non-zero value are treated as a single display

Now, Ubuntu Frame treats the “side by side” windows as one large display:

2 Likes