Kiosk snaps made easy

Kiosk snaps made easy

The following assumes some familiarity with using snapcraft to package snaps, and concentrates on the specifics of snapping graphical snap intended to work with, for example, the mir-kiosk snap on Ubuntu Core.

Kiosk snaps

Essentially kiosk snaps are Wayland applications packaged as a snap. Compared with running applications on a desktop, these do not care about integration with desktop themes and are expected to run as single fullscreen applications.

There are various types of application that fall into this category. As well as bespoke “kiosks” e.g. wpe-webkit, there are also things that might be embedded e.g. Kodi and games e.g. scummvm.

Note: While it is possible to use Xwayland to package an X11 application to use Wayland that is outside the scope of these notes.

Dancing with Wayland, Dancing with Daemons

I’ve taken the experience of writing a number of snaps to work with mir-kiosk and simplified the process. The result of this is a simple approach to snapping a Wayland application to work both on Ubuntu Core and Classic systems.

The “wayland interface dance”

Snaps use “interfaces” to access capabilities of the system and one of these is the wayland interface. Snaps have their own $XDG_RUNTIME_DIR but Wayland expects to use a file in this location to connect to the server. As a result, every Wayland based snap needs logic to make this work.

After writing this logic a few times, I extracted it into a wayland-launch helper script:

#!/bin/sh

set -x

if [ -z "${WAYLAND_DISPLAY}" ]
then WAYLAND_DISPLAY=wayland-0
fi

real_wayland=$(dirname "$XDG_RUNTIME_DIR")/${WAYLAND_DISPLAY}
while [ ! -O "${real_wayland}" ]; do echo waiting for Wayland socket; sleep 4; done

mkdir -p "$XDG_RUNTIME_DIR" -m 700
ln -sf "${real_wayland}" "$XDG_RUNTIME_DIR"
unset DISPLAY

exec "$@"

This creates a link from the snap’s $XDG_RUNTIME_DIR to the real one in the user’s $XDG_RUNTIME_DIR.

This “dance” (by design) works equally well on Ubuntu Core, running as a daemon and on Classic systems running in a user session.

The “daemon dance”

There is a difference between the way that snaps are run on Ubuntu Core and on Classic systems. On Ubuntu Core, snaps are expected to start automatically as daemons, on a Classic system they are started by the user.

To accommodate this I’ve introduced a daemon snap option, and on installation I set this according to the type of system. (Like other options this can then be changed using snap set.)

To work with this I’ve written a run-daemon helper script:

#!/bin/sh

set -x

if grep -q snap_core= /proc/cmdline || [ "$(snapctl get daemon)" = "true" ]
then exec "$@"
fi

mir-kiosk-snap-launch

After copying these scripts and hooks into a few projects I decided that it would be simpler and more consistent to create a github project for them: mir-kiosk-snap-launch.

To use this, you need a stanza to pull it into your snap:

  mir-kiosk-snap-launch:
    plugin: dump
    source: https://github.com/MirServer/mir-kiosk-snap-launch.git
    override-build:  $SNAPCRAFT_PART_BUILD/build-with-plugs.sh alsa avahi-observe hardware-observe locale-control mount-observe network-observe removable-media shutdown system-observe wayland

This pulls the above scripts and the installation and post-refresh hooks mentioned above into the snap. The build-with-plugs.sh script tailors some scripts to this snap including a setup.sh script that the user can run to connect any Snap interfaces.

To make use of these scripts, they need to be added to the command: entries for your snap. Like this (from the
mir-kiosk-kodi snap):

apps:
  daemon:
    command: run-daemon wayland-launch ${SNAP}/usr/bin/kodi
    daemon: simple
    ...

  mir-kiosk-kodi:
    command: wayland-launch ${SNAP}/usr/bin/kodi
    ...

Clearly you may need to do some additional things to configure your application to run in this environment. For example, Kodi needs WINDOWING=wayland to use Wayland, SDL2 needs SDL_VIDEODRIVER=wayland and scummvm needs -f added to the command-line for fullscreen.

The end result

These incantations provide you with a snap that will run both on Ubuntu Core with mir-kiosk and on the desktop either using a Wayland supporting desktop (e.g. the “Ubuntu Wayland” login session) or running egmde).

Here are three snaps that use this technique:

1 Like

Other stuff

For completeness, there are a few other things you need in your snap to use Wayland.

Plugs

In addition to any other interfaces needed by your application you need to list the following:

plugs:
  wayland:
  opengl:

Parts

Your application might need some additional components to use Wayland. For example, SDL2 applications (such as scummvm) need the following libraries:

parts:
  misc:
    plugin: nil
    stage-packages:
      - libwayland-client0
      - libwayland-egl1-mesa
      - libglu1-mesa
Mesa

This is subject to change as there are plans to allow the host system to provide the graphics stack. But at present you need to include and configure the Mesa graphics stack:

parts:
  mesa:
    plugin: nil
    stage-packages:
      - libgl1-mesa-dri

environment:
  # Prep EGL
  __EGL_VENDOR_LIBRARY_DIRS: $SNAP/etc/glvnd/egl_vendor.d:$SNAP/usr/share/glvnd/egl_vendor.d
  LIBGL_DRIVERS_PATH: ${SNAP}/usr/lib/${SNAPCRAFT_ARCH_TRIPLET}/dri
  LIBVA_DRIVERS_PATH: ${SNAP}/usr/lib/${SNAPCRAFT_ARCH_TRIPLET}/dri
  ...

Here’s another example: the packaging files for neverputt as a kiosk snap. This should be a good example to follow for any SDL based app.

The main part of this is the snapcraft.yaml:

name: mir-kiosk-neverputt
adopt-info: neverputt
summary: neverputt packaged as a mir-kiosk snap
description: neverputt packaged as a mir-kiosk snap
confinement: strict
grade: stable
base: core18
license: GPL-2.0

environment:
  # ${SNAPCRAFT_ARCH_TRIPLET} doesn't play nice with layout
  LD_LIBRARY_PATH: ${LD_LIBRARY_PATH}:${SNAP}/usr/lib/${SNAPCRAFT_ARCH_TRIPLET}/pulseaudio
  # Prep EGL
  __EGL_VENDOR_LIBRARY_DIRS: $SNAP/etc/glvnd/egl_vendor.d:$SNAP/usr/share/glvnd/egl_vendor.d
  LIBGL_DRIVERS_PATH: ${SNAP}/usr/lib/${SNAPCRAFT_ARCH_TRIPLET}/dri
  LIBVA_DRIVERS_PATH: ${SNAP}/usr/lib/${SNAPCRAFT_ARCH_TRIPLET}/dri
  # Prep SDL
  SDL_VIDEODRIVER: wayland

layout:
  /usr/share:
    bind: $SNAP/usr/share
  /usr/games:
    bind: $SNAP/usr/games

plugs:
  wayland:
  opengl:
  pulseaudio:
  alsa:
  hardware-observe: # This allows some UDEV access neverputt wants

apps:
  daemon:
    command: run-daemon wayland-launch neverputt
    daemon: simple
    restart-condition: always
    environment:
      # Prep PulseAudio
      PULSE_SYSTEM: 1
      PULSE_RUNTIME_PATH: /var/run/pulse

  mir-kiosk-neverputt:
    command: wayland-launch neverputt
    desktop: usr/share/applications/neverputt.desktop
    environment:
      # Prep PulseAudio
      PULSE_SERVER: unix:$XDG_RUNTIME_DIR/../pulse/native

parts:
  neverputt:
    plugin: nil
    override-pull: |
      snapcraftctl pull
      snapcraftctl set-version `LANG=C apt-cache policy neverputt | sed -rne 's/^\s+Candidate:\s+([^-]*)-.+$/\1/p'`
    stage-packages:
      - neverputt

  config:
    plugin: dump
    source: config

  mir-kiosk-snap-launch:
    plugin: dump
    source: https://github.com/MirServer/mir-kiosk-snap-launch.git
    override-build:  $SNAPCRAFT_PART_BUILD/build-with-plugs.sh opengl pulseaudio alsa wayland hardware-observe

  sdl2:
    plugin: nil
    stage-packages:
      - libsdl2-2.0-0
      - libsdl2-image-2.0-0
      - libsdl2-mixer-2.0-0
      - libsdl2-net-2.0-0

  mesa:
    plugin: nil
    stage-packages:
      - libgl1-mesa-dri
      - libwayland-egl1-mesa
      - libglu1-mesa

  wayland:
    plugin: nil
    stage-packages:
      - libwayland-client0

Things to note here are:

  1. The hardware-observe interface - although mouse & keyboard input should be access via Wayland, neverputt also accesses udev directly.
  2. The config stanza. This is where neverputt looks for configuration, and I use this to set “fullscreen” on.

The rest is simply following the template described above and documentation of the package.

Get it from the Snap Store


PS

One thing to note with SDL applications they don’t “play nice” with GNOME Wayland: Wayland session overlaid by window when fullscreen SDL apps exits

1 Like