diff --git a/host_vars/musicdolphin.yml b/host_vars/musicdolphin.yml index 008a9a6..5ea8d94 100644 --- a/host_vars/musicdolphin.yml +++ b/host_vars/musicdolphin.yml @@ -15,5 +15,7 @@ mediapi_has_dhtsensor: true # via pi_kiosk instead. See roles/pi_musicmouse/files/config-musicdolphin.yml. mediapi_install_kidsmusic: true mediapi_has_monitor: true +# Temporarily a debuggable desktop rather than a locked-down kiosk: Openbox root menu, +# visible pointer, Firefox in a normal window. Set back to "kiosk" when done. +pi_kiosk_mode: debug musicmouse_version: deploy/musicdolphin - diff --git a/roles/pi_kiosk/README.md b/roles/pi_kiosk/README.md index 8a284ca..4688095 100644 --- a/roles/pi_kiosk/README.md +++ b/roles/pi_kiosk/README.md @@ -1,18 +1,56 @@ # pi_kiosk -Turns a Pi with a monitor attached into a full-screen Firefox kiosk display, with no -window manager: an autologin console user runs `startx`, whose `.xinitrc` launches -Firefox in `--kiosk` mode pointed at `pi_kiosk_url` and relaunches it if it ever exits. -If X itself crashes, `.bash_profile` restarts it. +Turns a Pi with a monitor attached into a full-screen Firefox display: an autologin +console user runs `startx`, whose `.xinitrc` starts Openbox and then Firefox pointed at +`pi_kiosk_url`, relaunching it if it ever exits. If X itself crashes, `.bash_profile` +restarts it. Vars: - `pi_kiosk_user` (default `kiosk`) - the account that autologs into tty1. - `pi_kiosk_url` (default `http://localhost:8080`) - what Firefox shows. +- `pi_kiosk_mode` (default `kiosk`) - `kiosk` or `debug`, see below. Meant to run alongside `pi_musicmouse` on the same host when it's serving the page locally (the default `pi_kiosk_url`), gated in `mediapis.yml` by `mediapi_has_monitor`. -niri was considered instead of plain X/no-WM, but isn't packaged for Debian Bookworm -yet (no official apt package, only third-party `.deb`s or a from-source Rust build) - -revisit once it is. +## Why there is a window manager + +This role used to run no WM at all, on the theory that Firefox is the only X client and +`--kiosk` makes it full-screen by itself. It doesn't. `--kiosk` *asks* to be +full-screen, over EWMH, and with no window manager running there is nobody to answer: +Firefox falls back to its default window size in the top-left corner. On the 1920x1080 +monitor on musicdolphin that came out as a 1280x972 window with black bands down the +right edge and along the bottom - looking for all the world like an overscan problem, +which it is not. Openbox answers the request and otherwise stays out of the way. + +## Debug mode + +`pi_kiosk_mode: debug` turns the device's own screen into something you can work on: + +- Firefox runs as an ordinary window with its chrome, so the URL bar and devtools are + reachable, and it is **not** relaunched when you close it. +- The pointer is visible (no `-nocursor`, no `unclutter`). +- Openbox is the session leader. Right-click the desktop for a menu with a terminal, + both ways of opening the page again, and "End this X session", which drops back to + the autologin loop and starts a fresh one. +- `xterm`, `x11-utils` and `mesa-utils` get installed, so `xwininfo`, `xprop`, + `glxinfo` and `glxgears` are on the device. (The menu's Terminal entry runs + `x-terminal-emulator`, which on the Pi OS image is `zutty`, not the xterm we + install - both work; xterm is the fallback if the alternatives link ever points + somewhere that doesn't.) + +Switch a host over in `host_vars`, or for one run: + +```sh +just run mediapis.yml musicdolphin -e pi_kiosk_mode=debug +just run mediapis.yml musicdolphin -e pi_kiosk_mode=kiosk +``` + +Either way the session restarts itself: the templates notify the getty handler, and +restarting `getty@tty1` takes X down with it because Xorg lives in that unit's cgroup. +Going back to `kiosk` does not uninstall the debug packages; they are small and inert. + +niri was considered instead of Openbox, but isn't packaged for Debian Bookworm yet (no +official apt package, only third-party `.deb`s or a from-source Rust build) - revisit +once it is. diff --git a/roles/pi_kiosk/defaults/main.yml b/roles/pi_kiosk/defaults/main.yml index 750db2b..2b84625 100644 --- a/roles/pi_kiosk/defaults/main.yml +++ b/roles/pi_kiosk/defaults/main.yml @@ -1,3 +1,11 @@ --- pi_kiosk_user: "kiosk" pi_kiosk_url: "http://localhost:8080" + +# "kiosk" - the page full-screen with no browser chrome and no pointer. The normal mode. +# "debug" - the same page in an ordinary Firefox window, pointer visible, with an +# Openbox root menu (right-click the desktop) offering a terminal, so the +# device can be poked at on its own screen. Switch back with +# `just run mediapis.yml musicdolphin -e pi_kiosk_mode=kiosk`, or by editing +# host_vars. +pi_kiosk_mode: "kiosk" diff --git a/roles/pi_kiosk/tasks/main.yml b/roles/pi_kiosk/tasks/main.yml index 097c299..8c3fb26 100644 --- a/roles/pi_kiosk/tasks/main.yml +++ b/roles/pi_kiosk/tasks/main.yml @@ -5,10 +5,25 @@ - xserver-xorg - xinit - x11-xserver-utils + # Without a window manager, Firefox's request to go full-screen (which is all + # --kiosk does) has nobody to answer it, and it settles for its default window + # size in the corner of the screen. Openbox answers it, and nothing else. + - openbox - firefox-esr - unclutter cache_valid_time: 7200 +- name: Debugging tools for the attached screen + # Only pulled in for pi_kiosk_mode=debug; apt does not take them away again when the + # host goes back to kiosk mode, which is fine - they are inert and small. + ansible.builtin.apt: + name: + - xterm # a terminal that is ours, whatever x-terminal-emulator points at + - x11-utils # xwininfo, xprop: is the window actually the size of the screen? + - mesa-utils # glxinfo, glxgears: is anything accelerated, or is this llvmpipe? + cache_valid_time: 7200 + when: pi_kiosk_mode == 'debug' + - name: Create kiosk user ansible.builtin.user: name: "{{ pi_kiosk_user }}" @@ -64,14 +79,16 @@ owner: "{{ pi_kiosk_user }}" group: "{{ pi_kiosk_user }}" mode: "0644" + notify: Reload systemd and restart getty -- name: Install .xinitrc (launches Firefox kiosk, no window manager) +- name: Install .xinitrc (starts Openbox, then Firefox) ansible.builtin.template: src: xinitrc.j2 dest: "/home/{{ pi_kiosk_user }}/.xinitrc" owner: "{{ pi_kiosk_user }}" group: "{{ pi_kiosk_user }}" mode: "0755" + notify: Reload systemd and restart getty - name: Ensure firefox-esr policy directory exists ansible.builtin.file: @@ -85,3 +102,26 @@ src: policies.json dest: /etc/firefox-esr/policies.json mode: "0644" + +- name: Ensure the Openbox config directories exist + # Both levels explicitly, and owned by the user: a file: task creates missing parents + # as root, and a root-owned ~/.config is the same trap that broke the Firefox profile + # above. + ansible.builtin.file: + path: "/home/{{ pi_kiosk_user }}/{{ item }}" + state: directory + owner: "{{ pi_kiosk_user }}" + group: "{{ pi_kiosk_user }}" + mode: "0755" + loop: + - .config + - .config/openbox + +- name: Install the Openbox root menu + ansible.builtin.template: + src: openbox-menu.xml.j2 + dest: "/home/{{ pi_kiosk_user }}/.config/openbox/menu.xml" + owner: "{{ pi_kiosk_user }}" + group: "{{ pi_kiosk_user }}" + mode: "0644" + notify: Reload systemd and restart getty diff --git a/roles/pi_kiosk/templates/bash_profile.j2 b/roles/pi_kiosk/templates/bash_profile.j2 index 4231aea..80b7905 100644 --- a/roles/pi_kiosk/templates/bash_profile.j2 +++ b/roles/pi_kiosk/templates/bash_profile.j2 @@ -2,7 +2,12 @@ # this account autologs into - not meant for interactive use. if [ -z "$DISPLAY" ] && [ "$(tty)" = "/dev/tty1" ]; then while true; do +{% if pi_kiosk_mode == 'debug' %} + # No -nocursor: debugging on the device's own screen needs a pointer. + startx +{% else %} startx -- -nocursor +{% endif %} sleep 1 done fi diff --git a/roles/pi_kiosk/templates/openbox-menu.xml.j2 b/roles/pi_kiosk/templates/openbox-menu.xml.j2 new file mode 100644 index 0000000..9fc4c71 --- /dev/null +++ b/roles/pi_kiosk/templates/openbox-menu.xml.j2 @@ -0,0 +1,32 @@ + + + + + + x-terminal-emulator + + + + + firefox {{ pi_kiosk_url }} + + + + + firefox --kiosk {{ pi_kiosk_url }} + + + + + + + + + + + diff --git a/roles/pi_kiosk/templates/xinitrc.j2 b/roles/pi_kiosk/templates/xinitrc.j2 index d55c182..c9fda5c 100644 --- a/roles/pi_kiosk/templates/xinitrc.j2 +++ b/roles/pi_kiosk/templates/xinitrc.j2 @@ -1,15 +1,34 @@ #!/bin/sh -# Managed by ansible (pi_kiosk role). No window manager on purpose - Firefox is the -# only X client, launched full-screen in kiosk mode; the loop is what takes the place -# of a WM's "keep something on screen" job if it crashes. +# Managed by ansible (pi_kiosk role). +# +# Openbox is here for one concrete reason: a browser asks to be full-screen over EWMH, +# and only a window manager answers that request. With no WM running, --kiosk was +# silently ignored and Firefox fell back to its default 1280x972 window on a 1920x1080 +# screen - the page in the top-left corner with black bands down the right edge and +# along the bottom. Openbox costs a couple of MB and does nothing else here. xset s off xset -dpms xset s noblank +{% if pi_kiosk_mode == 'debug' %} +# Debug session. Firefox is an ordinary window with its chrome (so devtools and the URL +# bar are reachable) and is *not* relaunched when it exits - quitting it should leave a +# usable desktop, not fight you for the screen. Openbox is the session leader instead: +# when it exits (root menu -> Exit) the X session ends and .bash_profile starts a fresh +# one. Right-click the desktop for a terminal. +firefox "{{ pi_kiosk_url }}" & + +exec openbox +{% else %} +openbox & + unclutter -idle 0.5 -root & +# The loop is what takes the place of a WM's "keep something on screen" job: Openbox +# manages the window, but nothing else would bring Firefox back if it died. while true; do firefox --kiosk "{{ pi_kiosk_url }}" sleep 2 done +{% endif %}