Port the player to Raspberry Pi OS Trixie 64-bit (Linux-only branch)
Replaces the Windows port with a Raspberry Pi / Linux implementation on Raspberry Pi OS "Trixie" (Debian 13, aarch64, Wayland/labwc). The Windows code is removed here but preserved on the Windows-Player branch. Entry point ----------- linux/run_linux.py replaces windows/run_win.py. src/main.py stays platform-neutral; all Pi-specific behaviour is injected from linux/. Five bugs that prevented the port (all measured on real hardware) ---------------------------------------------------------------- 1. Kivy's PyPI wheel bundles an SDL2 built WITHOUT the wayland driver, so no window could be created (Trixie has no X server). linux/fix_kivy_sdl2.sh symlinks the system SDL2 over the bundled filename. 2. SDL2 requires WAYLAND_DISPLAY to be *set* - the socket alone is not enough, unlike wlopm. This broke every systemd/cron/autostart launch. linux_display.ensure_session_environment() detects and exports it. 3. Kivy's Clock resolves callbacks via func.__name__; a patch assigned under a different name crashed the player ~20s after a successful start. 4. The inherited signal_screen_activity() shelled out to tvservice, xdotool and ydotool - none exist on Trixie - and mis-escaped 'wlopm --on \*', so the display blanked after 10 minutes. 5. The launchers ran src/main.py directly, bypassing every platform patch and resolving the data directory one level too high. Web links --------- - --ozone-platform-hint=auto does NOT fall back to Wayland on Chromium 152; it aborts. The platform is now chosen explicitly. - The keyring password prompt is suppressed via the ENVIRONMENT, not the flags: launch_env() strips DBUS_SESSION_BUS_ADDRESS for the child so Chromium cannot reach gnome-keyring-daemon. - Teardown kills the whole process group (needs start_new_session=True); previously it silently fell back to terminate() and orphaned children. Video normalisation ------------------- A 4K video cannot play on a Pi 4: ffpyplayer decodes in software, measured at 0.90x realtime (1080p is 3.03x). Oversized media is downscaled to 1920x1080 at sync time using the hardware h264_v4l2m2m encoder (~31s for an 18s clip), triggered by resolution only so already-playable files are untouched. src/media_state.py owns the shared on-disk contract: a .kiwy-converting marker makes the player skip the item while it is being rebuilt, then the converted file is played instead. If nothing is playable at all (a single-item playlist whose only video is converting), the player loops the intro video rather than leaving a blank screen. Also fixed ---------- - network_monitor: replaced netsh/ifconfig/dhclient with nmcli (Trixie uses NetworkManager; ifconfig and dhclient are not even installed). - Removed the Windows-only focus keeper/guardian from main.py. - main.py: duplicate SDL_AUDIODRIVER setdefault (a silent no-op); Settings "Test connection" now uses tempfile.gettempdir(). - config/app_config.json: credentials blanked so a fresh clone runs the first-run setup flow. Verification ------------ linux/test_media_state.py 18/18, test_linux_patches.py 21/21, test_linux_browser_flags.py 27/27. Verified live against a real DigiServer: image -> weblink -> image -> video with correct durations, zero leaked Chromium processes, and no throttling over a 10 minute monitored run.
This commit is contained in:
Executable
+166
@@ -0,0 +1,166 @@
|
||||
#!/usr/bin/env bash
|
||||
# fix_kivy_sdl2.sh — make Kivy use the system SDL2 (Wayland-capable).
|
||||
#
|
||||
# THE PROBLEM
|
||||
# -----------
|
||||
# Kivy's PyPI wheel bundles a private SDL2 in ``Kivy.libs/`` that is compiled
|
||||
# WITHOUT the Wayland video driver. Verified on this Pi:
|
||||
#
|
||||
# Kivy.libs/libSDL2-2-87637523.0.so.0.3000.7 -> x11, KMSDRM, offscreen, dummy, evdev
|
||||
# /usr/lib/aarch64-linux-gnu/libSDL2-2.0.so.0 -> x11, wayland, KMSDRM, offscreen, dummy, evdev
|
||||
#
|
||||
# On Raspberry Pi OS "Trixie" the desktop is Wayland/labwc and there is no
|
||||
# X server running, so the bundled build cannot create a window at all:
|
||||
#
|
||||
# [CRITICAL] Unable to find any valuable Window provider.
|
||||
# sdl2 - RuntimeError: b'wayland,x11,dummy not available'
|
||||
#
|
||||
# Substituting the system library under Kivy's bundled filename resolves it
|
||||
# (verified: "WINDOW OK size=(800, 600)").
|
||||
#
|
||||
# WHY A SYMLINK AND NOT A COPY
|
||||
# ----------------------------
|
||||
# The symlink survives ``pip install --upgrade kivy`` overwriting the file, is
|
||||
# reversible, and keeps the distro's security updates in effect. A copy would
|
||||
# silently become stale.
|
||||
#
|
||||
# Usage:
|
||||
# bash linux/fix_kivy_sdl2.sh # apply (idempotent)
|
||||
# bash linux/fix_kivy_sdl2.sh --check # report only, change nothing
|
||||
# bash linux/fix_kivy_sdl2.sh --revert # remove the symlink
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
VENV_DIR="${KIWY_VENV:-$ROOT_DIR/.venv}"
|
||||
BACKUP_DIR="$VENV_DIR/.kivy-sdl2-backup"
|
||||
SYSTEM_SDL2="/usr/lib/aarch64-linux-gnu/libSDL2-2.0.so.0"
|
||||
|
||||
MODE="apply"
|
||||
case "${1:-}" in
|
||||
--check) MODE="check" ;;
|
||||
--revert) MODE="revert" ;;
|
||||
"") MODE="apply" ;;
|
||||
*) echo "Unknown option: $1" >&2; exit 2 ;;
|
||||
esac
|
||||
|
||||
if [ ! -d "$VENV_DIR" ]; then
|
||||
echo "ERROR: virtualenv not found at $VENV_DIR" >&2
|
||||
echo " Set KIWY_VENV=/path/to/venv if it lives elsewhere." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
KIVY_LIBS="$(find "$VENV_DIR" -maxdepth 5 -type d -name 'Kivy.libs' 2>/dev/null | head -1)"
|
||||
if [ -z "$KIVY_LIBS" ]; then
|
||||
echo "No Kivy.libs directory found under $VENV_DIR — nothing to do."
|
||||
echo "(Kivy is probably installed from apt, which uses the system SDL2 already.)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Kivy names the bundled library with a hash, so locate it by pattern rather
|
||||
# than hard-coding the version. Symlinks must be matched too: once the fix is
|
||||
# applied the original file IS a symlink, and skipping it would make this
|
||||
# script wrongly conclude that nothing is bundled.
|
||||
BUNDLED="$(find "$KIVY_LIBS" -maxdepth 1 -name 'libSDL2-2-*.so*' 2>/dev/null | head -1)"
|
||||
|
||||
# Fallback: ask the compiled extension which SDL2 it actually loads. This is
|
||||
# authoritative and also covers a future Kivy layout change.
|
||||
if [ -z "$BUNDLED" ]; then
|
||||
EXT="$(find "$VENV_DIR" -maxdepth 6 -name '_window_sdl2*.so' 2>/dev/null | head -1)"
|
||||
if [ -n "$EXT" ]; then
|
||||
NEEDED="$(ldd "$EXT" 2>/dev/null | awk '/libSDL2-2-/ {print $3; exit}')"
|
||||
[ -n "$NEEDED" ] && BUNDLED="$NEEDED"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Kivy.libs : $KIVY_LIBS"
|
||||
echo "bundled : ${BUNDLED:-<none>}"
|
||||
echo "system : $SYSTEM_SDL2"
|
||||
|
||||
if [ ! -e "$SYSTEM_SDL2" ]; then
|
||||
echo "ERROR: system SDL2 not found at $SYSTEM_SDL2" >&2
|
||||
echo " Install it with: sudo apt install libsdl2-2.0-0" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Report the video drivers compiled into a library, so the fix is verifiable.
|
||||
report_drivers() {
|
||||
local lib="$1"
|
||||
local py="${VENV_DIR}/bin/python"
|
||||
[ -x "$py" ] || py="$(command -v python3)"
|
||||
"$py" - "$lib" <<'PY' 2>/dev/null || echo " (could not inspect drivers)"
|
||||
import ctypes, sys
|
||||
try:
|
||||
s = ctypes.CDLL(sys.argv[1])
|
||||
s.SDL_GetNumVideoDrivers.restype = ctypes.c_int
|
||||
s.SDL_GetVideoDriver.restype = ctypes.c_char_p
|
||||
s.SDL_GetVideoDriver.argtypes = [ctypes.c_int]
|
||||
n = s.SDL_GetNumVideoDrivers()
|
||||
print(' drivers:', ', '.join(s.SDL_GetVideoDriver(i).decode() for i in range(n)))
|
||||
except Exception as exc:
|
||||
print(' (inspection failed:', exc, ')')
|
||||
PY
|
||||
}
|
||||
|
||||
case "$MODE" in
|
||||
revert)
|
||||
if [ -L "$BUNDLED" ]; then
|
||||
rm -f "$BUNDLED"
|
||||
if [ -f "$BACKUP_DIR/$(basename "$BUNDLED")" ]; then
|
||||
cp -a "$BACKUP_DIR/$(basename "$BUNDLED")" "$BUNDLED"
|
||||
echo "Reverted to the original bundled SDL2."
|
||||
else
|
||||
echo "Removed the symlink. Reinstall Kivy to restore the bundled library:"
|
||||
echo " $VENV_DIR/bin/pip install --force-reinstall kivy"
|
||||
fi
|
||||
else
|
||||
echo "Nothing to revert (no symlink in place)."
|
||||
fi
|
||||
exit 0
|
||||
;;
|
||||
|
||||
check)
|
||||
echo
|
||||
if [ -L "$BUNDLED" ]; then
|
||||
echo "STATUS: fixed (bundled name points at the system SDL2)"
|
||||
report_drivers "$BUNDLED"
|
||||
else
|
||||
echo "STATUS: NOT fixed — Kivy is using its own SDL2"
|
||||
report_drivers "$BUNDLED"
|
||||
fi
|
||||
echo
|
||||
echo "system SDL2:"
|
||||
report_drivers "$SYSTEM_SDL2"
|
||||
exit 0
|
||||
;;
|
||||
|
||||
apply)
|
||||
if [ -z "$BUNDLED" ]; then
|
||||
echo "No bundled libSDL2 found — Kivy already uses the system SDL2."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -L "$BUNDLED" ]; then
|
||||
echo "Already fixed (symlink in place). Verifying..."
|
||||
report_drivers "$BUNDLED"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Keep a copy of the original so --revert works without a reinstall.
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
if [ ! -f "$BACKUP_DIR/$(basename "$BUNDLED")" ]; then
|
||||
cp -a "$BUNDLED" "$BACKUP_DIR/"
|
||||
echo "Backed up original -> $BACKUP_DIR/$(basename "$BUNDLED")"
|
||||
fi
|
||||
|
||||
ln -sf "$SYSTEM_SDL2" "$BUNDLED"
|
||||
echo "Symlinked $BUNDLED -> $SYSTEM_SDL2"
|
||||
echo
|
||||
echo "Resulting drivers:"
|
||||
report_drivers "$BUNDLED"
|
||||
echo
|
||||
echo "Done. Kivy can now create a Wayland window."
|
||||
echo "NOTE: re-run this script after any 'pip install --upgrade kivy'."
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user