The quest for building a plain wayland electron kiosk

After recently updating the code for my electron-kiosk template snap packaging to base: core20 and the gnome-3-38 extension (the README now also has instructions for working audio output on Ubuntu Core BTW) I wondered if electron would not be ready to actually go wayland-only and drop the X11 layer completely from our kiosk implementation …

So I started with dropping the mir-kiosk-x11 stage snap (along with its command-chain options) and simply adding --enable-features=UseOzonePlatform and --ozone-platform=wayland to the electron launch options.

This did indeed not work :stuck_out_tongue:

Chromium uses /dev/shm for internal IPC and while this is allowed to the app snap via the browser-support interface through the apparmor rule:

owner /{dev,run}/shm/{,.}org.chromium.* mrw,

The ubuntu-frame (and mir-kiosk if you prefer old stuff :slight_smile: ) snap actually wants to write to that place as well and is lacking this permission:

apparmor="DENIED" operation="file_receive" profile="snap.mir-kiosk.daemon" name="/dev/shm/.org.chromium.Chromium.Xlmm1z" pid=1114 comm="Mir/Wayland" requested_mask="wr" denied_mask="wr" fsuid=0 ouid=0

There are a bunch of ways around this:

  1. Chromium does have a --disable-dev-shm-usage option that electron respects and that disables access to /dev/shm completely. While this makes the error go away I’m not sure it is the correct approach (seems this feature was implemented mainly for use in headless container setups )…

  2. Alternatively ubuntu-frame could just add the browser-support plug to gain that access, but that grants so much more that it seems like overkill to be added to a display server.

  3. Adding the line above from the browser-support interface to the end of:


    and calling:

    sudo apparmor_parser -r /var/lib/snapd/apparmor/profiles/snap.ubuntu-frame.daemon

    … does also work. So extending the wayland interface with this one-liner might perhaps be feasible.

  4. During an internal discussion @ijohnson suggested we might perhaps want to solve that darn /dev/shm thing once and for all by implementing a shared-memory interface in snapd proper …

For the time being I went with option 1. but I guess we eventually want one of the other three options to fix the issue in the longer term.

The next issue I hit was:

electron-kiosk-wayland[1267]: [1267:0922/] Failed to connect to the bus: Could not parse server address: Unknown address type (examples of valid types are "tcp" and on UNIX "unix")

so the app needs a working session dbus … which I do indeed not have on Ubuntu Core inside a daemon snap … seems we need to ship one ourselves then and launch it alongside (or wrapped around) our app …

I added a dbus-launch script:

#! /bin/sh

dbus-run-session --config-file=$SNAP/etc/dbus-1/session.conf -- "$@"

to be used as command-chain script and a session.conf template configuration to a dbus subdir in my project folder and then added the following part to my snapcraft.yaml:

    plugin: dump
    source: dbus
      session.conf: etc/dbus-1/session.conf
      dbus-launch: bin/dbus-launch
    override-prime: |
      snapcraftctl prime
      # replace the SNAP_NAME placeholder with our actual project name
      sed -i "s/SNAP_NAME/$SNAPCRAFT_PROJECT_NAME/" $SNAPCRAFT_PRIME/etc/dbus-1/session.conf
      - dbus

Now changing the apps: section in my snapcraft.yaml to:

    extensions: [ gnome-3-38 ]
    command: usr/bin/wrapper
      - bin/dbus-launch
      - snap/command-chain/desktop-launch

makes sure that first the session dbus is brought up, then the desktop-launch script is run to set up font, icon and theme dirs for me and then the electron app is started. This interestingly exposed a minor issue with log spam, that I hope we can quieten eventually.

Now there is a basic electron kiosk template app to be run fully under wayland at:

I have done all development and testing on a Pi4 running Ubuntu Core 20 and the latest ubuntu-frame.

Pointing the url= configuration of the snap to youtube and playing 1080p video works smoothly, even though there is no HW acceleration for video decoding with chromium/electron on the Pi (yet), but there are still a bunch of issues left:

  1. electron requires --disable-gpu to start at all
  2. there are still two errors at startup, they seem to have no actual impact though:
    electron-kiosk-wayland[19381]: [19381:0923/] Failed to load a platform cursor of type kNull
    electron-kiosk-wayland[19418]: [19418:0923/] Passthrough is not supported, GL is swiftshader, ANGLE is
  3. mouse wheel scrolling does not work at all !! (works well under XWayland) … all other mouse functions are fine though.
  4. Every n’th start electron fails to render at all with:
    electron-kiosk-wayland[19418]: [19418:0923/] WaylandBufferManagerGpu is not initialized. Can't register a surface.
    … and you end up with a white screen (funnily all elements are there and even the mouse pointer changes if you navigate over them, clicking works as well, they are just not visible) … there seems to be a race somewhere that i could not identify yet.

So while it is possible to run electron as plain wayland client on top of ubuntu-frame today, there is still some mileage to make until it is as rock-solid as the XWayland solution we use today. If you want to help out solving the remaining issues, feel free to fork my code below, build it, tinker with it and find fixes :slight_smile:


I can add a few more observations to this post.

I built and installed the above snap on a 20.04 desktop system.

git clone
cd electron-kiosk-wayland/
snap install --dangerous *.snap
snap connect electron-kiosk-wayland:wayland

On Mutter

Forcing the snap to run on GNOME/Wayland I see the “everything painted white” issue around 3/4 runs. But the mouse scroll works.

snap run electron-kiosk-wayland

On Mir

I also tried running on Ubuntu Frame:

snap install ubuntu-frame
export WAYLAND_DISPLAY=wayland-99
snap run electron-kiosk-wayland

Again, this paints everything white around 3/4 runs. But, as you’ve observed on RPi4, the mouse scroll doesn’t work.

As a sanity check, I tried running both gedit and kate on Ubuntu Frame (by starting them from the same terminal session) and both were working with the mouse scroll wheel.

gedit snap/snapcraft.yaml
QT_QPA_PLATFORM=wayland kate snap/snapcraft.yaml


It would seem probable that both the “everything painted white” and the mouse scrolling issues are failures in electron’s current level of support for Wayland (i.e. not with snaps or Ubuntu Frame).

I will have a closer look at any differences in the mouse axis handling between Mutter and Mir, but as both GTK and Qt are working correctly it is likely electron is doing something unusual.

1 Like

There are differences between mouse scrolling on Mir and Mutter:

On Mutter

$ WAYLAND_DISPLAY=wayland-0 WAYLAND_DEBUG=client gedit snap/snapcraft.yaml 2>&1 | grep axis
[313729.766] wl_pointer@14.axis_source(0)
[313729.782] wl_pointer@14.axis_discrete(0, 1)
[313729.805] wl_pointer@14.axis(3167584, 0, 10.000000)
[314620.923] wl_pointer@14.axis_source(0)
[314620.976] wl_pointer@14.axis_discrete(0, 1)
[314620.997] wl_pointer@14.axis(3168474, 0, 10.000000)
[314732.759] wl_pointer@14.axis_source(0)
[314732.810] wl_pointer@14.axis_discrete(0, 1)
[314732.829] wl_pointer@14.axis(3168586, 0, 10.000000)
[315126.849] wl_pointer@14.axis_source(0)
[315126.904] wl_pointer@14.axis_discrete(0, -1)
[315126.926] wl_pointer@14.axis(3168980, 0, -10.000000)
[315202.637] wl_pointer@14.axis_source(0)
[315202.685] wl_pointer@14.axis_discrete(0, -1)
[315202.704] wl_pointer@14.axis(3169056, 0, -10.000000)
[315280.685] wl_pointer@14.axis_source(0)
[315280.732] wl_pointer@14.axis_discrete(0, -1)
[315280.748] wl_pointer@14.axis(3169134, 0, -10.000000)
[315413.066] wl_pointer@14.axis_source(0)
[315413.122] wl_pointer@14.axis_discrete(0, -1)
[315413.145] wl_pointer@14.axis(3169266, 0, -10.000000)

On Mir

$ WAYLAND_DISPLAY=wayland-99 WAYLAND_DEBUG=client gedit snap/snapcraft.yaml 2>&1 | grep axis
[235862.192] wl_pointer@7.axis(3089717, 0, 10.000000)
[236054.345] wl_pointer@7.axis(3089909, 0, 10.000000)
[236542.349] wl_pointer@7.axis(3090397, 0, 10.000000)
[236960.506] wl_pointer@7.axis(3090815, 0, 10.000000)
[237202.254] wl_pointer@7.axis(3091057, 0, 10.000000)
[238740.135] wl_pointer@7.axis(3092595, 0, -10.000000)
[238790.992] wl_pointer@7.axis(3092646, 0, -10.000000)
[238867.428] wl_pointer@7.axis(3092722, 0, -10.000000)
[239038.552] wl_pointer@7.axis(3092893, 0, -10.000000)
[239178.198] wl_pointer@7.axis(3093033, 0, -10.000000)
[239729.223] wl_pointer@7.axis(3093584, 0, 10.000000)
[239842.049] wl_pointer@7.axis(3093697, 0, 10.000000)
[239929.079] wl_pointer@7.axis(3093784, 0, 10.000000)
[240084.109] wl_pointer@7.axis(3093939, 0, 10.000000)
[240346.123] wl_pointer@7.axis(3094201, 0, -10.000000)
[240438.294] wl_pointer@7.axis(3094293, 0, -10.000000)
[240509.547] wl_pointer@7.axis(3094364, 0, -10.000000)
[240638.348] wl_pointer@7.axis(3094493, 0, -10.000000)
[241044.213] wl_pointer@7.axis(3094899, 0, 10.000000)
[241115.274] wl_pointer@7.axis(3094970, 0, 10.000000)
[241193.201] wl_pointer@7.axis(3095048, 0, 10.000000)
[241636.314] wl_pointer@7.axis(3095491, 0, -10.000000)
[241712.077] wl_pointer@7.axis(3095567, 0, -10.000000)
[241868.273] wl_pointer@7.axis(3095723, 0, -10.000000)
[242006.226] wl_pointer@7.axis(3095861, 0, 10.000000)
[242170.094] wl_pointer@7.axis(3096025, 0, 10.000000)
[242504.398] wl_pointer@7.axis(3096359, 0, -10.000000)

And [update] there’s even this comment in the Mir code:

    // TODO: Add descrete scroll to pointer events and source it from libinput_event_pointer_get_axis_value_discrete()

Further update: raised a Mir issue

1 Like

i found that --disable-gpu is actually responsible for the white screen and it looks like the need for it is actually caused by the Pi4 vc4 driver not working properly.

it seems to work fine without that option on other architectures so i made it conditional in the startup script to only apply the option on Pi4 now, please try with my refreshed branch …

(i also turned off the use of portals to quieten a startup error from the gnome extension, portals are rather pointless in kiosks i guess :slight_smile: )

PS: in qemu-virgil i get a still usable framerate with 5000 fish per second (fps :stuck_out_tongue: ) on (it starts stuttering a bit with 10000 and above)

Having briefly looked into the code the other day given the freezing Electron sandbox issues, I don’t get the impression that this flag substantially changes how shared memory works, merely just the location of the file. If this shared memory is backed by the hard disks then it’s inferior, but when I was testing it, it picked $XDG_RUNTIME_DIR, which is the same kind of tmpfs as /dev/shm on classic and hence I’d presume also on Core?

Obviously not an expert here but at a glance I don’t think this is a hacky approach as long as the directory it picks is backed by RAM.

1 Like

OK, so “everything painted white” problem no longer occurs with Mutter. But with Mir I get:

$ snap run electron-kiosk-wayland
[79007:0927/] Failed to load a platform cursor of type kNull

(electron-quick-start:79007): dbind-WARNING **: 11:00:31.477: AT-SPI: Error retrieving accessibility bus address: org.freedesktop.DBus.Error.ServiceUnknown: The name org.a11y.Bus was not provided by any .service files
[79048:0927/] Passthrough is not supported, GL is egl, ANGLE is 
[79048:0927/] InitializeSandbox() called with multiple threads in process gpu-process.
error in client communication (pid 79007)
[79007:0927/] Fatal Wayland protocol error 4 on interface zwp_linux_buffer_params_v1 (object 30). Shutting down..

A quick google for “Fatal Wayland protocol error 4 on interface zwp_linux_buffer_params_v1” shows that folks have seen it on wlroots with some versions of Chromium. ( So that is plausibly another problem with electron’s Wayland support.

well, you are seemingly not running it as a daemon … i do not see such behavior in daemon mode … (also, where does that InitializeSandbox() call come from ? it needs to start with --no-sandbox)

Running as a daemon makes no difference.

but have you kept the --no-sandbox in the launch script ?

I’ve not changed anything. Just cloned last week and pulled today before rebuilding.

weird, then i dont get why you end up in the InitializeSandbox() function above … this is clearly not expected …

I don’t know about InitializeSandbox() - I’ve confirmed --no-sandbox is on the commandline.

The actual error seems to be an “unsupported buffer format”:

[88320:0927/] InitializeSandbox() called with multiple threads in process gpu-process.
[3146446.951]  -> zwp_linux_dmabuf_v1@6.create_params(new id zwp_linux_buffer_params_v1@30)
[3146446.987]  -> zwp_linux_buffer_params_v1@30.add(fd 108, 0, 0, 3200, 16777215, 4294967295)
[3146447.032]  -> zwp_linux_buffer_params_v1@30.create_immed(new id wl_buffer@29, 800, 600, 875708993, 0)
[3146447.142]  -> zwp_linux_dmabuf_v1@6.create_params(new id zwp_linux_buffer_params_v1@26)
[3146447.168]  -> zwp_linux_buffer_params_v1@26.add(fd 105, 0, 0, 3200, 16777215, 4294967295)
[3146447.204]  -> zwp_linux_buffer_params_v1@26.create_immed(new id wl_buffer@25, 800, error in client communication (pid 88279)
600, 875708993, 0)
[3146447.277]  -> zwp_linux_dmabuf_v1@6.create_params(new id zwp_linux_buffer_params_v1@23)
[3146447.300]  -> zwp_linux_buffer_params_v1@23.add(fd 106, 0, 0, 3200, 16777215, 4294967295)
[3146447.348]  -> zwp_linux_buffer_params_v1@23.create_immed(new id wl_buffer@22, 800, 600, 875708993, 0)
[3146447.495] wl_display@1.error(zwp_linux_buffer_params_v1@30, 4, "Client requested unsupported format/modifier combination DRM_FORMAT_ABGR8888/DRM_FORMAT_MOD_INVALID (875708993/16777215,4294967")
[88279:0927/] Fatal Wayland communication error: Connection reset by peer

Seems odd that that has changed since Friday.

1 Like

do you have a build log to see if the electron version changed perhaps ?

EDIT: found it !
… my code just re-purposes the electron-quick-start example … seems they bumped it from electron 14 to 15 …

:man_shrugging: you can reproduce now?

i wish i could try, seems the latest nvidia update broke qemu-virgil on my machine, but i’ll move the snap to use its own package.json and pin it to 14

You can test the graphics without a VM. See my earlier post for how:

Digging a bit further, electron is (as logged) requesting a modifier format of DRM_FORMAT_MOD_INVALID and Mir doesn’t recognize this as valid (it isn’t in the list Mir supplies). Mutter, on receiving the same request, carries on regardless.

I’ve experimented with hacking Mir to allow DRM_FORMAT_MOD_INVALID, and with that change this snap displays sensibly. (I’ll go read the protocol to see if this is actually a reasonable behavior.)


The specification of zwp_linux_buffer_params_v1.add says:

        Warning: It should be an error if the format/modifier pair was not
        advertised with the modifier event. This is not enforced yet because
        some implementations always accept DRM_FORMAT_MOD_INVALID. Also
        version 2 of this protocol does not have the modifier event.

Mir, unlike “some implementations”, is treating this as an error.

1 Like

I built the electron app outside of the snap. For anyone else who wants to do that:

$ npm install electron-packager
$ git clone
$ cd electron-quick-start
$ curl > main.js
$ npx electron-packager . --overwrite --platform=linux --output=release-build --prune=true
$ SNAP_URL='' WAYLAND_DISPLAY=wayland-1 electron-quick-start-linux-x64/electron-quick-start --enable-features=UseOzonePlatform --ozone-platform=wayland --disable-dev-shm-usage --no-sandbox

This opens up the Ubuntu homepage in an Electron app running on wayland-1. Experimenting with this, I found that all we have to do to make scrolling work on Mir is send .axis_source(finger):

The protocol specifies .axis_source is optional, but it looks like Electron ignores non-discrete scrolling without it.

1 Like

And I found that .axis_source(wheel) also “works” (without axis_discrete()).

1 Like

Electron/Wayland now working with (pre-release) Mir

What electron is doing is technically wrong, but Mir can be accommodating.

The result of these investigations are reflected on Mir master and are available from the ubuntu-frame “edge” channel:

snap refresh --edge ubuntu-frame

These changes will be included in Mir 2.5. Once that has been released, they will be reflected in the “beta”, “candidate” then “stable” channels for ubuntu-frame.