Harden HDMI recovery and add tested user installer release tooling
This commit is contained in:
1 parent
73e727ef4c
commit
d963224cb7
17 files changed
+903
-646
No files matched your search
+26
-76
@@ -1,81 +1,31 @@
|
||||
# Architecture
|
||||
|
||||
## Problem
|
||||
The workaround resets the display below the compositor by briefly leaving the
|
||||
active graphical VT. It is intended for one local Hyprland session on `seat0`.
|
||||
|
||||
A monitor shared between multiple physical HDMI inputs can keep its EDID/output state visible to Linux even while the monitor is displaying another source. When switching back, Hyprland may report the output as completely healthy while the physical monitor remains black or reports no source.
|
||||
1. A systemd user service runs `udevadm monitor --kernel --property
|
||||
--subsystem-match=drm`. The watcher owns a per-user lock.
|
||||
2. Each `HOTPLUG=1` line causes a loginctl lookup for the current UID, Wayland,
|
||||
active state, seat0 and a valid VT. A known different desktop is excluded.
|
||||
3. Events inside the cooldown are ignored. The clock is Bash's elapsed seconds;
|
||||
timestamps are not read from shared files or affected by wall-clock changes.
|
||||
4. The synchronous recovery helper locks, independently identifies the active
|
||||
session, waits for HDMI settling, checks the Hyprland IPC output and refreshes
|
||||
DPMS/focus. The instance signature comes from the Hyprland startup hook.
|
||||
5. Before switching, the helper rechecks session identity and VT and checks sudo
|
||||
permission for both directions. It rechecks session identity after those calls.
|
||||
6. The helper sets an EXIT/signal trap, switches to VT2 (when graphical VT=1) or
|
||||
VT1 (otherwise), and returns to the detected graphical VT.
|
||||
7. Failed returns and catchable signals cause an emergency return attempt. Success
|
||||
is logged only after the final DPMS/focus refresh succeeds.
|
||||
8. The watcher logs the helper's actual exit status and starts the cooldown at
|
||||
completion. Unexpected event-stream EOF always returns nonzero to systemd.
|
||||
|
||||
Typical Hyprland state in the broken condition:
|
||||
Shared validation and session detection live in `lib/common.sh`. Runtime state
|
||||
is restricted to a user-owned mode-0700 `XDG_RUNTIME_DIR/revive-monitor`. Root is
|
||||
used only for `chvt`; user scripts are never invoked through sudo.
|
||||
|
||||
```text
|
||||
focused: yes
|
||||
dpmsStatus: 1
|
||||
disabled: false
|
||||
active workspace: 1
|
||||
```
|
||||
|
||||
Commands such as `hyprctl reload`, `hyprctl dispatch dpms on`, and `hyprctl dispatch focusmonitor` may all return `ok` without restoring the image.
|
||||
|
||||
## Discovery
|
||||
|
||||
Listening to Hyprland's event socket did not provide a reliable monitor add/remove event for the physical input switch.
|
||||
|
||||
The kernel DRM subsystem did, however, emit:
|
||||
|
||||
```text
|
||||
SUBSYSTEM=drm
|
||||
HOTPLUG=1
|
||||
```
|
||||
|
||||
via:
|
||||
|
||||
```bash
|
||||
udevadm monitor --kernel --property --subsystem-match=drm
|
||||
```
|
||||
|
||||
A manual VT switch restored the image, indicating that the required reset happens below the compositor level.
|
||||
|
||||
## Final design
|
||||
|
||||
The long-running watcher consumes kernel DRM events. For every `HOTPLUG=1` event it:
|
||||
|
||||
1. checks `systemd-logind` for an active Wayland session belonging to the current user;
|
||||
2. ignores the event if no such session exists;
|
||||
3. dynamically determines that session's VT;
|
||||
4. applies debounce protection;
|
||||
5. calls the recovery script with the detected VT.
|
||||
|
||||
The recovery script:
|
||||
|
||||
1. uses `flock` to prevent overlap;
|
||||
2. waits for the HDMI handshake to settle;
|
||||
3. asks Hyprland to enable/focus the output;
|
||||
4. uses `chvt` to move to another VT;
|
||||
5. returns to the dynamically detected graphical VT;
|
||||
6. refreshes Hyprland DPMS/focus once more.
|
||||
|
||||
## Why the Wayland-session guard is critical
|
||||
|
||||
GPU initialization during boot can generate DRM hotplug events. If the recovery script blindly executes a hard-coded `chvt 2` followed by `chvt 1`, the machine can end up showing tty1 even though the display manager is running on tty2.
|
||||
|
||||
The current implementation prevents this by requiring:
|
||||
|
||||
```text
|
||||
Name=<current user>
|
||||
Type=wayland
|
||||
Active=yes
|
||||
VTNr>0
|
||||
```
|
||||
|
||||
before performing any recovery.
|
||||
|
||||
## Privilege boundary
|
||||
|
||||
The watcher and recovery script run as the normal user. Only `chvt` needs elevated privileges.
|
||||
|
||||
The recommended sudoers rule limits passwordless sudo to:
|
||||
|
||||
```text
|
||||
/usr/bin/chvt
|
||||
```
|
||||
|
||||
The script always calls it through `sudo -n`, which fails instead of prompting for a password.
|
||||
The session check is not atomic with the kernel VT switch. The temporary VT can
|
||||
contain another console, and the workaround can briefly expose it. Multi-seat
|
||||
setups, multiple concurrent desktops for one user and hardware that emits no DRM
|
||||
event are outside the supported design. See README for cooldown tradeoffs.
|
||||
Reference in new issue
Block a user