#!/bin/sh
# Alternate between Kodi (GBM) and Plasma (Wayland) on an appliance.
# Boot lands on Kodi; Exit Kodi -> Plasma; Log out of Plasma -> Kodi; repeat.

set -u

MIN_RUNTIME=10      # a session exiting faster than this is treated as a crash
CRASH_THROTTLE=5    # seconds to wait after a fast exit, so a broken session can't tight-loop

# A rogue addon can leave a thread that hangs Kodi's exit forever, so a watchdog
# unit SIGKILLs Kodi if its shutdown stalls past the grace.
WATCH_NAME=kodi-plasma-toggle-watchdog.service
SCOPE_NAME=kodi-plasma-toggle-kodi.scope
KODI_SHUTDOWN_GRACE=30   # secs before a stuck shutdown is killed
KODI_OWN_SCOPE=          # set for a native (non-flatpak) Kodi that won't self-scope
KODI_BIN_MATCH='kodi\.bin'   # pgrep -f pattern the watchdog uses to find Kodi
KODI_LOG="$HOME/.var/app/tv.kodi.Kodi/data/temp/kodi.log"
[ -s /etc/default/kodi-session-aptosid ] && . /etc/default/kodi-session-aptosid

log() { logger -t kodi-plasma-toggle "$*"; }

# Run a session. fll_desktop sets SDDM Relogin=true, so exiting only re-logs-in the
# session -- there is no greeter fallback. The toggle therefore never exits: a session
# that dies faster than MIN_RUNTIME is logged and throttled, then the loop proceeds.
run_it() {  # $1=label  $2..=command
    label=$1; shift
    start=$(date +%s)
    "$@"
    rc=$?
    end=$(date +%s)
    run=$((end - start))
    log "session '$label' exited rc=$rc after ${run}s"
    if [ "$run" -lt "$MIN_RUNTIME" ]; then
        log "'$label' exited quickly; throttling ${CRASH_THROTTLE}s"
        sleep "$CRASH_THROTTLE"
    fi
}

# Resolve KDE's "start a D-Bus session bus only if one isn't present" wrapper once.
# Globbed so it works on any Debian multiarch (x86_64, aarch64, ...) without
# hardcoding the triplet. This is exactly what the Plasma session entry invokes.
PLASMA_BUS=
for w in /usr/lib/*/libexec/plasma-dbus-run-session-if-needed; do
    [ -x "$w" ] && { PLASMA_BUS=$w; break; }
done
[ -n "$PLASMA_BUS" ] || log "plasma-dbus-run-session-if-needed not found; launching plasma directly"

# Run Kodi with the watchdog as a transient unit systemd owns. The watchdog
# kills Kodi if its shutdown overruns the grace; a clean exit returns first, so
# we stop the watchdog before it would fire.
watchdog_and_kodi() {
    systemctl --user reset-failed "$WATCH_NAME" 2>/dev/null
    systemd-run --user --quiet --unit="$WATCH_NAME" \
        -E KODI_LOG="$KODI_LOG" \
        -E KODI_SHUTDOWN_GRACE="$KODI_SHUTDOWN_GRACE" \
        -E KODI_BIN_MATCH="$KODI_BIN_MATCH" \
        -- /usr/libexec/kodi-plasma-toggle-watchdog
    if [ -n "$KODI_OWN_SCOPE" ]; then
        # Native Kodi doesn't self-scope -- give it one the watchdog can target.
        systemctl --user reset-failed "$SCOPE_NAME" 2>/dev/null
        systemd-run --user --scope --unit="$SCOPE_NAME" --quiet \
            -- /usr/libexec/kodi-session-aptosid
    else
        # Flatpak self-scopes into its own app-flatpak-*.scope.
        /usr/libexec/kodi-session-aptosid
    fi
    rc=$?
    systemctl --user stop "$WATCH_NAME" 2>/dev/null
    return "$rc"
}

while :; do
    unset WAYLAND_DISPLAY DISPLAY XDG_SESSION_TYPE XDG_CURRENT_DESKTOP XDG_SESSION_DESKTOP

    # Kodi GBM needs no display vars; --windowing=gbm is explicit. The wrapper
    # reads /etc/default/kodi-session-aptosid, then execs into the real Kodi.
    run_it kodi watchdog_and_kodi

    # $PLASMA_BUS is unquoted on purpose: empty -> drops out, set -> prepends the
    # wrapper. The path has no spaces, so word-splitting is safe here.
    run_it plasma \
        env XDG_SESSION_TYPE=wayland XDG_SESSION_DESKTOP=plasma XDG_CURRENT_DESKTOP=KDE \
            $PLASMA_BUS startplasma-wayland
done
