Files
Anthias/bin
Viktor Petersson f0959a40ce fix(viewer): re-assert linuxfb display mode after HDMI hotplug (#3075)
* fix(viewer): re-assert linuxfb display mode after HDMI hotplug

On the 32-bit Pi 1-3 (linuxfb/Qt5) viewer, a TV on a power schedule
that switches itself off and on can leave the display stuck at
1024x768 instead of its native resolution (issue #3052).

Root cause is kernel-side, not anything Anthias writes: under
dtoverlay=vc4-kms-v3d the linuxfb boards never take DRM master, so the
kernel's drm_fb_helper owns the display mode. When the HDMI sink wakes,
the connector re-probe can win the race against the sink's EDID/DDC
coming back; the connector momentarily reports no valid modes and
drm_fb_helper latches its hard-coded 1024x768 default. Qt's linuxfb
plugin reads the framebuffer geometry once at startup and can't follow
the change, so the picture stays stuck until the Pi is power-cycled.

eglfs boards (pi4 / pi5 / pi3-64 / arm64) are immune: Qt holds DRM
master and keeps its own modeset committed across the hotplug. Verified
on a Pi 3-64 testbed that a real ~10 s HDMI unplug never left 1920x1080.

Fix: a linuxfb-only watchdog in start_viewer.sh watches the HDMI
connector for a disconnect->reconnect and, once EDID is readable again,
re-asserts the connector's *preferred* mode (read live from the
connector, never hard-coded, so any panel resolution is honoured) onto
the framebuffer via the fbdev sysfs `mode` attribute, then restarts the
viewer so Qt re-initialises against the restored mode. All access is
under /sys in the already-privileged viewer container; no DRM master is
taken, so it never conflicts with Qt's fbdev use. The QT_QPA_PLATFORM
guard makes it a no-op on eglfs/wayland boards.

Validated on a real Pi 3: forcing the connector through a
disconnect -> 1024x768 -> reconnect cycle, the watchdog re-asserted
1920x1080 and restarted the viewer. (The 1024x768 latch was simulated
with fbset because the testbed monitor presents EDID instantly and
can't reproduce the real no-EDID race; detection and recovery are
identical regardless of how the mode was latched.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(viewer): restart to recover /dev/fb0 hotplugged after headless boot

- exit wait_for_framebuffer when the host has fb0 but the container's
  stale /dev doesn't, so `restart: always` re-snapshots /dev
- keep waiting quietly when genuinely headless (no sysfs fb0) to avoid
  the #2959 crash-loop
- fixes the "self-heals on hotplug" case that a running container's
  devtmpfs snapshot silently broke

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(viewer): harden HDMI-hotplug resolution recovery (review follow-ups)

- preserve interlaced scan type instead of forcing a progressive mode
  string (interlaced-native sinks were never matched)
- log rejected /sys .../fb0/mode writes instead of returning silently,
  so a screen stuck at 1024x768 is diagnosable
- watch every HDMI connector, seeded from current status, instead of
  binding one at startup (second micro-HDMI port / late connect)
- normalize `pidof python` to a single PID so a multi-PID result can't
  disable the watchdog (also fixes the pre-existing kill -0 loop)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(viewer): address second-pass review nits on HDMI-hotplug recovery

- wait_for_framebuffer: sleep+re-check before the stale-devtmpfs exit so
  a /dev/fb0 node that lags its sysfs entry on a fresh start can't cause
  restart churn
- monitor: default an unseeded connector to 'connected' so an
  unreadable-status seed race can't look like a reconnect edge and
  spuriously restart a display that never dropped
- PID: select the oldest pidof result (the viewer, launched before any
  gst_fbdev_player.py helper) instead of the newest

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 21:22:57 +01:00
..