- HiFiBerry: the plain hifiberry-dacplus overlay makes the Pi the I2S clock consumer, which the Pi 5's controller refuses (snd_soc_dai_set_fmt -22). The card never probed, so every softvol_* device was dead: squeezelite crash-looped and the backend and shairport played into nothing. hifiberry-dacplus-std has the Pi drive the clocks. pi_hifiberry_amp now matches any dtoverlay=hifiberry-* line, so changing the overlay replaces the old one instead of adding a second. - X: with two DRM devices (v3d, vc4) Xorg gave up with "Cannot run in framebuffer mode", and the kiosk looped on a blank screen. 99-vc4.conf points it at vc4, the display controller; rendering still goes to V3D (glxinfo: V3D 7.1, accelerated). - The front-end's icons are emoji in the markup. The Lite image has no emoji font, so they rendered as empty boxes: install fonts-noto-color-emoji. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
176 lines
6.8 KiB
YAML
176 lines
6.8 KiB
YAML
---
|
|
- name: Packages
|
|
ansible.builtin.apt:
|
|
name:
|
|
- xserver-xorg
|
|
- xinit
|
|
- x11-xserver-utils
|
|
# Without a window manager, a browser'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
|
|
- unclutter
|
|
# The MusicMouse front-end's icons are emoji characters in the markup, not an icon
|
|
# font. Lite images ship no emoji font at all, so without this every icon renders
|
|
# as an empty box.
|
|
- fonts-noto-color-emoji
|
|
cache_valid_time: 7200
|
|
notify: Reload systemd and restart getty
|
|
|
|
- name: Ensure the Xorg config directory exists
|
|
ansible.builtin.file:
|
|
path: /etc/X11/xorg.conf.d
|
|
state: directory
|
|
mode: "0755"
|
|
|
|
- name: Point X at the display controller
|
|
# The Pi 4 and 5 have two DRM devices: v3d (the 3D GPU, no outputs) and vc4 (the
|
|
# HDMI display controller). Left to itself Xorg cannot tell which to drive and dies
|
|
# with "Cannot run in framebuffer mode. Please specify busIDs for all framebuffer
|
|
# devices", so the kiosk loops on a blank screen. Display goes through vc4; rendering
|
|
# still lands on v3d, because glamor's GL context is routed there by Mesa (kmsro).
|
|
# Raspberry Pi's desktop image ships this same file; the Lite image does not.
|
|
ansible.builtin.copy:
|
|
dest: /etc/X11/xorg.conf.d/99-vc4.conf
|
|
content: |
|
|
Section "OutputClass"
|
|
Identifier "vc4"
|
|
MatchDriver "vc4"
|
|
Driver "modesetting"
|
|
Option "PrimaryGPU" "true"
|
|
EndSection
|
|
mode: "0644"
|
|
notify: Reload systemd and restart getty
|
|
|
|
- name: Browser
|
|
# Chromium comes from archive.raspberrypi.com, not Debian: that build carries the
|
|
# "+rpt" suffix and Raspberry Pi's own patches for the V3D GPU on this board. Debian
|
|
# also ships a much older `chromium`, so pinning the wrong archive here would quietly
|
|
# undo the reason for choosing it.
|
|
ansible.builtin.apt:
|
|
name: "{{ 'chromium' if pi_kiosk_browser == 'chromium' else 'firefox-esr' }}"
|
|
cache_valid_time: 7200
|
|
notify: Reload systemd and restart getty
|
|
|
|
- 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?
|
|
- xdotool # drive the UI from ssh, so a measurement can be taken on a real
|
|
# screen rather than an idle one
|
|
- scrot # and see what that screen actually shows, over ssh
|
|
cache_valid_time: 7200
|
|
when: pi_kiosk_mode == 'debug'
|
|
|
|
- name: Create kiosk user
|
|
ansible.builtin.user:
|
|
name: "{{ pi_kiosk_user }}"
|
|
groups: [video, input, tty]
|
|
shell: /bin/bash
|
|
create_home: true
|
|
|
|
- name: Own the directories X and Firefox write into
|
|
# Xorg runs as root here (Debian's wrapper, allowed_users=console) with HOME set to
|
|
# the kiosk user's, so it creates ~/.cache itself - root-owned and 0700 - the first
|
|
# time it writes a mesa shader cache. Firefox then cannot create ~/.cache/mozilla and
|
|
# gives up with "Your Firefox profile cannot be loaded. It may be missing or
|
|
# inaccessible". Creating these up front, owned by the user, means root only ever
|
|
# adds subdirectories to a directory the user already owns.
|
|
ansible.builtin.file:
|
|
path: "/home/{{ pi_kiosk_user }}/{{ item }}"
|
|
state: directory
|
|
owner: "{{ pi_kiosk_user }}"
|
|
group: "{{ pi_kiosk_user }}"
|
|
mode: "0700"
|
|
loop:
|
|
- .cache
|
|
- .mozilla
|
|
|
|
- name: Autologin the kiosk user on tty1
|
|
ansible.builtin.file:
|
|
path: /etc/systemd/system/getty@tty1.service.d
|
|
state: directory
|
|
mode: "0755"
|
|
|
|
- name: Install getty autologin override
|
|
ansible.builtin.template:
|
|
src: autologin.conf.j2
|
|
dest: /etc/systemd/system/getty@tty1.service.d/autologin.conf
|
|
mode: "0644"
|
|
notify: Reload systemd and restart getty
|
|
|
|
- name: Enable the autologin getty on tty1
|
|
# Enabling it is the part that survives a reboot. These images do not enable
|
|
# getty@tty1 themselves - getty.target pulls in getty-static.service and nothing
|
|
# else - so without this the drop-in above is installed, the handler starts the unit
|
|
# once, and the kiosk then silently stops coming up after the next reboot.
|
|
ansible.builtin.systemd:
|
|
name: "getty@tty1"
|
|
enabled: true
|
|
state: started
|
|
daemon_reload: true
|
|
|
|
- name: Install .bash_profile (starts X on tty1 login)
|
|
ansible.builtin.template:
|
|
src: bash_profile.j2
|
|
dest: "/home/{{ pi_kiosk_user }}/.bash_profile"
|
|
owner: "{{ pi_kiosk_user }}"
|
|
group: "{{ pi_kiosk_user }}"
|
|
mode: "0644"
|
|
notify: Reload systemd and restart getty
|
|
|
|
- name: Install .xinitrc (starts Openbox, then the browser)
|
|
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 the browser policy directory exists
|
|
ansible.builtin.file:
|
|
path: "{{ '/etc/chromium/policies/managed' if pi_kiosk_browser == 'chromium' else '/etc/firefox-esr' }}"
|
|
state: directory
|
|
mode: "0755"
|
|
# The packages normally create these; a backstop in case a layout changes
|
|
|
|
- name: Install the browser's enterprise policy
|
|
# Everything that is a setting rather than a startup flag: no sign-in, no sync, no
|
|
# metrics, no password manager, no translate bar. The flags themselves are in
|
|
# .xinitrc, where they have to be.
|
|
ansible.builtin.copy:
|
|
src: "{{ 'chromium-policies.json' if pi_kiosk_browser == 'chromium' else 'policies.json' }}"
|
|
dest: >-
|
|
{{ '/etc/chromium/policies/managed/kiosk.json'
|
|
if pi_kiosk_browser == 'chromium' else '/etc/firefox-esr/policies.json' }}
|
|
mode: "0644"
|
|
notify: Reload systemd and restart getty
|
|
|
|
- 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
|