Drop the forced display mode, and let the kiosk user own its own cache

The screen was a badly seated cable - the enclosure was fouling the connector,
so the DDC and hotplug pins never mated while the video pairs partly did. That
is why it read 0 bytes of EDID on both ports and why forcing a mode "fixed" it.
With the cable seated properly HDMI-A-1 reports connected, hands over 256 bytes
of EDID and identifies itself as a DELL P2419H, so the force has nothing left to
do. Remove it and let EDID decide, which also means the mode is no longer
hardcoded to something that happened to match.

Separately, and the reason the screen then showed "Your Firefox profile cannot
be loaded": Xorg runs as root under Debian's wrapper with HOME pointing at the
kiosk user's, so the first mesa shader cache write created ~/.cache owned by
root and mode 0700. Firefox could not create ~/.cache/mozilla inside it and
never got as far as writing a profile - .mozilla/firefox held only Crash Reports
and Pending Pings, with no profiles.ini. Create ~/.cache and ~/.mozilla up front
owned by the user, so root only ever adds subdirectories to a directory the user
already owns.

Keeping the getty@tty1 enable and the userconfig.service mask: those were real
faults, not workarounds for this one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-20 12:26:51 +02:00
parent a973bc8d75
commit b3f1ff69b1
4 changed files with 17 additions and 41 deletions

View File

@@ -17,7 +17,3 @@ mediapi_install_kidsmusic: true
mediapi_has_monitor: true
musicmouse_version: deploy/musicdolphin
# The attached screen reads back 0 bytes of EDID on both micro-HDMI ports and reports
# "disconnected", so the kernel offers no modes and X dies with "no screens found".
# Force the mode instead. Use the port nearest the USB-C jack (HDMI0 = HDMI-A-1).
pi_kiosk_force_mode: "HDMI-A-1:1920x1080@60e"

View File

@@ -1,14 +1,3 @@
---
pi_kiosk_user: "kiosk"
pi_kiosk_url: "http://localhost:8080"
# Force a mode on the kiosk output, as a `video=` kernel parameter, e.g.
# "HDMI-A-1:1920x1080@60e" (the trailing `e` forces the output enabled). Empty means
# "let EDID decide", which is what you want whenever EDID can be read.
#
# This is for a display the Pi cannot see: a micro-HDMI cable or adapter whose DDC/HPD
# lines are broken but whose video pairs are fine reads back 0 bytes of EDID and
# reports "disconnected", while displaying perfectly once a mode is forced. Note
# hdmi_force_hotplug=1 does nothing here - it is ignored by the vc4-kms-v3d driver
# these images use.
pi_kiosk_force_mode: ""

View File

@@ -4,9 +4,3 @@
name: "getty@tty1"
state: restarted
daemon_reload: "yes"
- name: Warn that a reboot is needed
ansible.builtin.debug:
msg: >-
/boot/firmware/cmdline.txt changed - the forced display mode only takes effect
after a reboot.

View File

@@ -16,6 +16,23 @@
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
@@ -68,23 +85,3 @@
src: policies.json
dest: /etc/firefox-esr/policies.json
mode: "0644"
- name: Read the current kernel command line
ansible.builtin.slurp:
src: /boot/firmware/cmdline.txt
register: pi_kiosk_cmdline
when: pi_kiosk_force_mode | length > 0
- name: Force a mode on the kiosk output
# cmdline.txt is a single line of space-separated parameters. Rebuild it by dropping
# any video= we set before and appending the current one, so changing the mode
# replaces it rather than stacking a second one.
ansible.builtin.copy:
dest: /boot/firmware/cmdline.txt
content: >-
{{ ((pi_kiosk_cmdline.content | b64decode).split()
| reject('match', '^video=') | list
+ ['video=' ~ pi_kiosk_force_mode]) | join(' ') }}
mode: "0755"
when: pi_kiosk_force_mode | length > 0
notify: Warn that a reboot is needed