feat(headless-kde): reliable bring-up — readiness probe, fix portal ordering/env (roadmap #1 phase 1)
ci / rust (push) Has been cancelled
ci / rust (push) Has been cancelled
Headless KDE startup was a chain of timing-sensitive handoffs gated by a blind `sleep 2`, the dominant source of black screens. Phase-1 fixes: - New `punktfunk-host probe-compositor` subcommand: exits 0 iff the detected compositor is up AND ready to create a virtual output now. KWin gets a real check (connect + registry roundtrip + the privileged zkde_screencast global must be advertised — what the backend needs); gamescope/Mutter/wlroots create on demand so the probe just confirms Linux. (vdisplay::probe dispatcher + kwin::probe; reuses kwin.rs's existing roundtrip path.) - run-headless-kde.sh: replace `sleep 2` with an active readiness wait (poll probe-compositor until ready, 30s deadline, and bail with kwin's log if kwin_wayland exits during init). Move the portal restart to AFTER readiness, and precede it with `systemctl --user import-environment` + `dbus-update-activation-environment` (the missing env import — the Sway script does this; without it a restarted portal inherits a stale/empty WAYLAND_DISPLAY, which is the "streams but eats no input/audio" failure). kwin's stderr → a log file. Validated: probe-compositor exits 0 "Kwin ready" against the live session, exit 1 with a clear diagnostic when the compositor is absent. 114 tests green, clippy/fmt clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
# Headless KDE Plasma session for the punktfunk host (no KMS scanout → kwin --virtual).
|
||||
#
|
||||
# Brings up the full desktop, not just the compositor. The env matters: without
|
||||
# Brings up the full desktop, not just the compositor, and waits for it to be *actually
|
||||
# ready* before starting the portals/plasma — no blind `sleep`. The env matters: without
|
||||
# XDG_MENU_PREFIX=plasma- the launcher resolves ${XDG_MENU_PREFIX}applications.menu →
|
||||
# "applications.menu", which doesn't exist on KDE installs (it ships
|
||||
# plasma-applications.menu) — plasmashell runs fine but the menu shows NO applications
|
||||
# and no System Settings entry. kded6/krunner/kglobalacceld are D-Bus-activated once
|
||||
# plasmashell starts in the right session env.
|
||||
# plasma-applications.menu) — plasmashell runs fine but the menu shows NO applications.
|
||||
#
|
||||
# bash scripts/headless/run-headless-kde.sh [WxH] # default 1920x1080
|
||||
#
|
||||
@@ -29,18 +28,54 @@ export DESKTOP_SESSION=plasma
|
||||
export WAYLAND_DISPLAY=wayland-kde
|
||||
export KWIN_WAYLAND_NO_PERMISSION_CHECKS=1
|
||||
|
||||
kwin_wayland --virtual --width "$W" --height "$H" --no-lockscreen \
|
||||
--socket "$WAYLAND_DISPLAY" &
|
||||
KWIN_PID=$!
|
||||
sleep 2
|
||||
# The probe binary (gates readiness on KWin actually exposing zkde_screencast — not merely
|
||||
# the socket existing). Use a release build if present, else fall back to `cargo run`.
|
||||
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
if [[ -x "$ROOT/target/release/punktfunk-host" ]]; then
|
||||
PROBE=("$ROOT/target/release/punktfunk-host" probe-compositor)
|
||||
elif [[ -x "$ROOT/target/debug/punktfunk-host" ]]; then
|
||||
PROBE=("$ROOT/target/debug/punktfunk-host" probe-compositor)
|
||||
else
|
||||
PROBE=(cargo run -q --manifest-path "$ROOT/Cargo.toml" -p punktfunk-host -- probe-compositor)
|
||||
fi
|
||||
|
||||
# The xdg-desktop-portal processes bind to the compositor that existed when THEY started;
|
||||
# after a kwin restart the stale instances point at a dead socket and RemoteDesktop/EIS
|
||||
# (mouse/keyboard injection) times out. Restart them against the fresh compositor.
|
||||
systemctl --user try-restart plasma-xdg-desktop-portal-kde.service xdg-desktop-portal.service 2>/dev/null || true
|
||||
# kwin to its own log (so its EGL/GPU-init errors are captured, not lost to the terminal).
|
||||
KWIN_LOG="${TMPDIR:-/tmp}/punktfunk-kwin.log"
|
||||
kwin_wayland --virtual --width "$W" --height "$H" --no-lockscreen \
|
||||
--socket "$WAYLAND_DISPLAY" >"$KWIN_LOG" 2>&1 &
|
||||
KWIN_PID=$!
|
||||
|
||||
# Active readiness wait: poll until KWin is up AND advertises the zkde_screencast global
|
||||
# (what the virtual-output backend needs), or fail fast with a useful message. kwin can also
|
||||
# exit immediately if EGL/GPU init fails — catch that.
|
||||
echo "waiting for KWin ($RES) to become ready…"
|
||||
DEADLINE=$(( SECONDS + 30 ))
|
||||
until "${PROBE[@]}" >/dev/null 2>&1; do
|
||||
if ! kill -0 "$KWIN_PID" 2>/dev/null; then
|
||||
echo "ERROR: kwin_wayland exited during startup — see $KWIN_LOG:" >&2
|
||||
tail -n 20 "$KWIN_LOG" >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
if (( SECONDS >= DEADLINE )); then
|
||||
echo "ERROR: KWin did not become ready within 30s. Last probe:" >&2
|
||||
"${PROBE[@]}" >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
sleep 0.5
|
||||
done
|
||||
echo "KWin ready."
|
||||
|
||||
# Only NOW restart the portals, and against the correct env: the xdg-desktop-portal chain
|
||||
# binds the compositor that existed when it started, so a stale portal points at a dead
|
||||
# socket and RemoteDesktop/EIS (input injection) times out. Import the session env into the
|
||||
# systemd/D-Bus activation environment FIRST (the missing piece — the Sway script does this;
|
||||
# without it the restarted portal can inherit an empty WAYLAND_DISPLAY), then restart.
|
||||
systemctl --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP DBUS_SESSION_BUS_ADDRESS XDG_RUNTIME_DIR 2>/dev/null || true
|
||||
dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP DBUS_SESSION_BUS_ADDRESS 2>/dev/null || true
|
||||
systemctl --user try-restart plasma-xdg-desktop-portal-kde.service xdg-desktop-portal-kde.service xdg-desktop-portal.service 2>/dev/null || true
|
||||
|
||||
kbuildsycoca6 >/dev/null 2>&1 || true # rebuild the menu cache under the correct env
|
||||
plasmashell &
|
||||
|
||||
echo "headless KDE up on $WAYLAND_DISPLAY ($RES), kwin pid $KWIN_PID"
|
||||
echo "headless KDE up on $WAYLAND_DISPLAY ($RES), kwin pid $KWIN_PID (log: $KWIN_LOG)"
|
||||
wait "$KWIN_PID"
|
||||
|
||||
Reference in New Issue
Block a user