#!/bin/bash
# Screenbird Player - Raspberry Pi installer (version 2.1.0)
#
# Turns a Raspberry Pi 4 or 5 running "Raspberry Pi OS (64-bit)" WITH desktop into a
# Screenbird screen. After a reboot the Pi logs in by itself and opens the Screenbird
# player (https://player.screenbird.app) full screen: no mouse cursor, no screen
# blanking, and the player restarts by itself after a crash or a reboot.
#
# Download: https://media.screenbird.app/downloads/screenbird-rpi-setup.sh
#
# Run it as the normal desktop user (not as root), in a terminal on the Pi. Either:
#   bash ~/Downloads/screenbird-rpi-setup.sh
# or download and run in one go:
#   curl -sL https://media.screenbird.app/downloads/screenbird-rpi-setup.sh | bash
#
# Remove everything this installer added:
#   bash ~/Downloads/screenbird-rpi-setup.sh --uninstall
#   curl -sL https://media.screenbird.app/downloads/screenbird-rpi-setup.sh | bash -s -- --uninstall
#
# Desktop sessions: labwc (Wayland, the default since late 2024), wayfire (Wayland, older
# Bookworm images) and X11 (openbox). The player is started through the desktop's standard
# autostart folder (~/.config/autostart), which all three Raspberry Pi desktop sessions
# run at login. Safe to run more than once.
#
# wayfire cannot hide the mouse cursor, and Raspberry Pi no longer updates it ("adopt labwc
# as soon as possible", October 2024). On a Bookworm Pi that still starts wayfire and has
# labwc installed, the installer switches the desktop to labwc with the same raspi-config
# steps Raspberry Pi's own switch prompt uses. The old settings are backed up and
# --uninstall switches back. Keep wayfire with:  --keep-session

set -Eeuo pipefail

SB_VERSION="2.1.0"
PLAYER_URL="https://player.screenbird.app/?platform=raspberry-pi"
CHECK_URL="https://player.screenbird.app/"
INSTALL_DIR="/opt/screenbird"
KIOSK_BIN="$INSTALL_DIR/kiosk.sh"
STATE_FILE="$INSTALL_DIR/install-state"
SWITCH_FILE="$INSTALL_DIR/session-switch"
SWITCH_BACKUP="$INSTALL_DIR/session-backup"
LABWC_PROMPT="/etc/xdg/autostart/labwc-prompt.desktop"
LEGACY_UNIT="/etc/systemd/system/screenbird-kiosk.service"
X11_BLANKING_CONF="/etc/X11/xorg.conf.d/10-blanking.conf"
MARK_BEGIN="<!-- screenbird-kiosk begin: added by the Screenbird installer -->"
MARK_END="<!-- screenbird-kiosk end -->"
RC_HEAD='<?xml version="1.0" encoding="UTF-8"?>'
RC_OPEN='<openbox_config xmlns="http://openbox.org/3.4/rc">'
RC_CLOSE='</openbox_config>'

SB_USER=""
SB_HOME=""
SESSION=""
KEEP_SESSION=0

say() { printf '%s\n' "$*"; }
step() { printf '\n==> %s\n' "$*"; }
warn() { printf 'WARNING: %s\n' "$*" >&2; }
die() {
  printf '\nERROR: %s\n' "$*" >&2
  exit 1
}

on_error() {
  printf '\nERROR: the installer stopped unexpectedly (line %s).\n' "$1" >&2
  printf 'You can safely run it again. If it keeps failing, contact Screenbird support and include the text above.\n' >&2
}

is_root() { [ "$(id -u)" -eq 0 ]; }

# Run a command as root (directly when already root, otherwise with sudo).
as_root() {
  if is_root; then "$@"; else sudo "$@"; fi
}

# Run a command as the desktop user, so files in their home stay theirs.
as_user() {
  if is_root; then runuser -u "$SB_USER" -- "$@"; else "$@"; fi
}

usage() {
  say "Screenbird Player installer for Raspberry Pi (version $SB_VERSION)"
  say ""
  say "Usage: bash screenbird-rpi-setup.sh [--uninstall | --keep-session]"
  say ""
  say "  (no option)      install or update the Screenbird player"
  say "  --keep-session   do not switch a wayfire desktop to labwc (the mouse cursor stays visible)"
  say "  --uninstall      remove everything this installer added"
}

# ---------------------------------------------------------------------------
# Preflight
# ---------------------------------------------------------------------------

resolve_user() {
  if is_root; then
    SB_USER="${SUDO_USER:-}"
    if [ -z "$SB_USER" ] || [ "$SB_USER" = "root" ]; then
      die "Please run this installer as your normal desktop user, not as root.
Open a terminal on the Raspberry Pi desktop and run:  bash screenbird-rpi-setup.sh"
    fi
  else
    SB_USER="$(id -un)"
  fi
  SB_HOME="$(getent passwd "$SB_USER" | cut -d: -f6)"
  if [ -z "$SB_HOME" ] || [ ! -d "$SB_HOME" ]; then
    die "Could not find the home folder of user '$SB_USER'."
  fi
  if [ "$(id -u "$SB_USER")" -lt 1000 ]; then
    die "Please run this installer as your normal desktop user (not '$SB_USER')."
  fi
}

check_raspberry_pi() {
  local model="" f
  for f in /proc/device-tree/model /sys/firmware/devicetree/base/model; do
    if [ -r "$f" ]; then
      model="$(tr -d '\0' < "$f")"
      break
    fi
  done
  case "$model" in
    "Raspberry Pi"*) ;;
    *) die "This computer is not a Raspberry Pi (${model:-no Raspberry Pi board found}).
This installer only works on a Raspberry Pi 4 or 5 with Raspberry Pi OS." ;;
  esac
  case "$model" in
    *"Pi 4"* | *"Pi 5"* | *"Compute Module 4"* | *"Compute Module 5"*) say "Board: $model" ;;
    *) warn "$model is not a Raspberry Pi 4 or 5. The installer continues, but video playback may be slow." ;;
  esac
}

check_64bit() {
  local arch
  arch="$(dpkg --print-architecture 2>/dev/null || true)"
  if [ "$arch" != "arm64" ]; then
    die "This installer needs Raspberry Pi OS (64-bit), but this Pi runs the ${arch:-unknown} version.
Please install \"Raspberry Pi OS (64-bit)\" with Raspberry Pi Imager and run this installer again."
  fi
}

check_desktop() {
  local sessions=0
  if compgen -G "/usr/share/wayland-sessions/*.desktop" >/dev/null; then sessions=1; fi
  if compgen -G "/usr/share/xsessions/*.desktop" >/dev/null; then sessions=1; fi
  if [ ! -e /etc/lightdm/lightdm.conf ] || [ "$sessions" -eq 0 ]; then
    die "No desktop found. This looks like \"Raspberry Pi OS Lite\".
Please install \"Raspberry Pi OS with desktop\" with Raspberry Pi Imager and run this installer again."
  fi
}

check_network() {
  local ok=1
  if command -v curl >/dev/null 2>&1; then
    curl -fsS --max-time 20 -o /dev/null "$CHECK_URL" >/dev/null 2>&1 && ok=0
  elif command -v wget >/dev/null 2>&1; then
    wget -q -T 20 -O /dev/null "$CHECK_URL" && ok=0
  else
    getent hosts player.screenbird.app >/dev/null 2>&1 && ok=0
  fi
  if [ "$ok" -ne 0 ]; then
    die "No internet connection (could not reach player.screenbird.app).
Connect the Raspberry Pi to the internet (Wi-Fi or network cable) and run this installer again."
  fi
}

get_sudo() {
  if ! is_root; then
    say "Administrator rights are needed. If asked, type your Raspberry Pi password."
    sudo -v || die "Could not get administrator rights (sudo)."
  fi
}

# Which desktop session will the player run in? Prefer what is running now, then what
# the login manager is configured to start.
detect_session() {
  local s=""
  if pgrep -u "$SB_USER" -x labwc >/dev/null 2>&1; then echo labwc; return; fi
  if pgrep -u "$SB_USER" -x wayfire >/dev/null 2>&1; then echo wayfire; return; fi
  if pgrep -u "$SB_USER" -x openbox >/dev/null 2>&1; then echo x11; return; fi
  if [ -r "/var/lib/AccountsService/users/$SB_USER" ]; then
    s="$(sed -n 's/^XSession=//p' "/var/lib/AccountsService/users/$SB_USER" | head -n1)"
  fi
  if [ -z "$s" ]; then
    s="$(sed -n 's/^[[:space:]]*autologin-session[[:space:]]*=[[:space:]]*//p' /etc/lightdm/lightdm.conf | head -n1)"
  fi
  if [ -z "$s" ]; then
    s="$(sed -n 's/^[[:space:]]*user-session[[:space:]]*=[[:space:]]*//p' /etc/lightdm/lightdm.conf | head -n1)"
  fi
  case "$s" in
    *labwc*) echo labwc; return ;;
    *wayfire*) echo wayfire; return ;;
    ?*) echo x11; return ;;
  esac
  if [ -x /usr/bin/labwc ]; then echo labwc
  elif [ -x /usr/bin/wayfire ]; then echo wayfire
  else echo x11
  fi
}

# ---------------------------------------------------------------------------
# Install steps
# ---------------------------------------------------------------------------

chromium_package() {
  local p cand
  for p in chromium chromium-browser; do
    cand="$(apt-cache policy "$p" 2>/dev/null | awk '/Candidate:/ {print $2; exit}')"
    if [ -n "$cand" ] && [ "$cand" != "(none)" ]; then
      echo "$p"
      return 0
    fi
  done
  return 1
}

# Run apt quietly; show its output only when something goes wrong.
apt_run() {
  local log
  log="$(mktemp)"
  if ! as_root env DEBIAN_FRONTEND=noninteractive apt-get "$@" </dev/null >"$log" 2>&1; then
    tail -n 25 "$log" >&2
    rm -f "$log"
    die "Installing software failed (apt-get $1). Check the internet connection and run the installer again."
  fi
  rm -f "$log"
}

install_packages() {
  local pkg
  local -a pkgs
  step "Installing Chromium (this can take a few minutes)"
  apt_run update
  pkg="$(chromium_package)" || die "Chromium is not available from the package sources of this Pi."
  pkgs=("$pkg")
  # unclutter hides the mouse cursor in an X11 session (not used on Wayland).
  if [ -x /usr/bin/openbox ]; then pkgs+=(unclutter); fi
  apt_run install -y "${pkgs[@]}"
  if ! command -v chromium >/dev/null 2>&1 && ! command -v chromium-browser >/dev/null 2>&1; then
    die "Chromium was installed but cannot be found."
  fi
  say "Chromium package: $pkg"
}

remove_legacy_service() {
  # Version 1 of this installer used a system service that never worked on current images.
  if [ -e "$LEGACY_UNIT" ]; then
    as_root systemctl disable screenbird-kiosk.service >/dev/null 2>&1 || true
    as_root rm -f "$LEGACY_UNIT"
    as_root systemctl daemon-reload || true
    say "Removed the old screenbird-kiosk system service."
  fi
}

have_raspi_config() { command -v raspi-config >/dev/null 2>&1; }

raspi() {
  as_root env SUDO_USER="$SB_USER" raspi-config nonint "$@" </dev/null
}

desktop_autologin_on() { grep -q "^autologin-user=$SB_USER\$" /etc/lightdm/lightdm.conf 2>/dev/null; }
console_autologin_on() { [ -e /etc/systemd/system/getty@tty1.service.d/autologin.conf ]; }

save_state() {
  # Written once, on the first install, so an uninstall can put these settings back.
  local blank="unknown" da="no" ca="no" xconf="no"
  if as_root test -e "$STATE_FILE"; then return 0; fi
  if have_raspi_config; then blank="$(raspi get_blanking 2>/dev/null || echo unknown)"; fi
  desktop_autologin_on && da="yes"
  console_autologin_on && ca="yes"
  [ -e "$X11_BLANKING_CONF" ] && xconf="yes"
  printf 'user=%s\nprev_desktop_autologin=%s\nprev_console_autologin=%s\nprev_blanking=%s\nprev_x11_blanking_conf=%s\n' \
    "$SB_USER" "$da" "$ca" "$blank" "$xconf" | as_root tee "$STATE_FILE" >/dev/null
}

write_kiosk_script() {
  step "Installing the Screenbird player launcher"
  as_root install -d -m 755 "$INSTALL_DIR"
  as_root tee "$KIOSK_BIN" >/dev/null <<KIOSK
#!/bin/bash
# Screenbird kiosk launcher, installed by screenbird-rpi-setup.sh (version $SB_VERSION).
# Started by the desktop session at login via ~/.config/autostart/screenbird-kiosk.desktop.
# Keeps Chromium full screen on the Screenbird player and restarts it when it exits.
set -u

PLAYER_URL="$PLAYER_URL"
CHECK_URL="$CHECK_URL"
UA_SUFFIX="ScreenbirdKiosk/$SB_VERSION"
KIOSK
  as_root tee -a "$KIOSK_BIN" >/dev/null <<'KIOSK'
PROFILE_DIR="$HOME/.config/screenbird-kiosk"
LOG_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/screenbird"
LOG_FILE="$LOG_DIR/kiosk.log"

mkdir -p "$PROFILE_DIR" "$LOG_DIR"
if [ -f "$LOG_FILE" ] && [ "$(stat -c %s "$LOG_FILE")" -gt 1048576 ]; then
  mv -f "$LOG_FILE" "$LOG_FILE.1"
fi
exec >>"$LOG_FILE" 2>&1
log() { printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"; }

# One launcher per user (Chromium and unclutter do not inherit the lock).
exec 9>"${XDG_RUNTIME_DIR:-/tmp}/screenbird-kiosk-$(id -u).lock"
if ! flock -n 9; then
  log "launcher already running, exiting"
  exit 0
fi

if [ -n "${WAYLAND_DISPLAY:-}" ]; then
  SESSION=wayland
elif [ -n "${DISPLAY:-}" ]; then
  SESSION=x11
else
  log "no desktop session found (WAYLAND_DISPLAY and DISPLAY are empty), exiting"
  exit 1
fi

session_alive() {
  if [ "$SESSION" = wayland ]; then
    local sock="$WAYLAND_DISPLAY"
    case "$sock" in /*) ;; *) sock="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/$sock" ;; esac
    [ -S "$sock" ]
  elif command -v xset >/dev/null 2>&1; then
    xset q >/dev/null 2>&1
  else
    return 0
  fi
}

x11_no_blanking() {
  if command -v xset >/dev/null 2>&1; then
    xset s off || true
    xset s noblank || true
    xset -dpms || true
  fi
  if pgrep -u "$(id -u)" -x xscreensaver >/dev/null 2>&1; then
    xscreensaver-command -exit >/dev/null 2>&1 || true
  fi
}

BIN=""
for b in chromium chromium-browser; do
  if command -v "$b" >/dev/null 2>&1; then BIN="$(command -v "$b")"; break; fi
done
if [ -z "$BIN" ]; then
  log "Chromium not found, run the Screenbird installer again"
  exit 1
fi

# Chromium's own user agent plus the Screenbird marker the player uses to recognise a Pi.
MAJOR="$("$BIN" --version 2>/dev/null | grep -oE '[0-9]+' | head -n1)"
UA_ARGS=()
if [ -n "$MAJOR" ]; then
  UA_ARGS=("--user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/$MAJOR.0.0.0 Safari/537.36 $UA_SUFFIX")
fi

if [ "$SESSION" = x11 ]; then
  OZONE=x11
  x11_no_blanking
  if command -v unclutter >/dev/null 2>&1 && ! pgrep -u "$(id -u)" -x unclutter >/dev/null 2>&1; then
    unclutter -idle 1 -root 9>&- &
  fi
else
  OZONE=wayland
fi

# Give the network up to two minutes after boot. The player also works offline from its
# cache, so start anyway when there is no connection.
if command -v curl >/dev/null 2>&1; then
  for _ in $(seq 1 24); do
    curl -fsS --max-time 5 -o /dev/null "$CHECK_URL" >/dev/null 2>&1 && break
    sleep 5
  done
fi

log "starting ($SESSION, $BIN, Chromium $MAJOR)"
fails=0
while session_alive; do
  # A power cut must not lead to a "restore pages" prompt or a "profile in use" error.
  PREFS="$PROFILE_DIR/Default/Preferences"
  if [ -f "$PREFS" ]; then
    sed -i -e 's/"exited_cleanly":false/"exited_cleanly":true/' \
      -e 's/"exit_type":"[^"]*"/"exit_type":"Normal"/' "$PREFS" || true
  fi
  rm -f "$PROFILE_DIR/SingletonLock" "$PROFILE_DIR/SingletonSocket" "$PROFILE_DIR/SingletonCookie"
  [ "$SESSION" = x11 ] && x11_no_blanking

  started=$(date +%s)
  "$BIN" \
    --kiosk \
    --noerrdialogs \
    --disable-infobars \
    --no-first-run \
    --no-default-browser-check \
    --disable-translate \
    --disable-features=Translate,TranslateUI \
    --disable-session-crashed-bubble \
    --hide-crash-restore-bubble \
    --autoplay-policy=no-user-gesture-required \
    --overscroll-history-navigation=0 \
    --disable-pinch \
    --password-store=basic \
    --check-for-update-interval=31536000 \
    --ozone-platform="$OZONE" \
    --user-data-dir="$PROFILE_DIR" \
    "${UA_ARGS[@]}" \
    "$PLAYER_URL" 9>&-
  code=$?
  log "Chromium exited (code $code), restarting"
  if [ $(( $(date +%s) - started )) -lt 30 ]; then fails=$((fails + 1)); else fails=0; fi
  if [ "$fails" -ge 5 ]; then sleep 30; else sleep 3; fi
done
log "desktop session ended, launcher stops"
KIOSK
  as_root chmod 755 "$KIOSK_BIN"
}

write_autostart() {
  local dir="$SB_HOME/.config/autostart"
  as_user mkdir -p "$dir"
  as_user tee "$dir/screenbird-kiosk.desktop" >/dev/null <<EOF
[Desktop Entry]
Type=Application
Name=Screenbird Player
Comment=Starts the Screenbird player full screen (added by the Screenbird installer)
Exec=$KIOSK_BIN
Terminal=false
NoDisplay=true
X-GNOME-Autostart-enabled=true
EOF
  say "Autostart: $dir/screenbird-kiosk.desktop"
}

# labwc: hide the mouse cursor when the player window opens. HideCursor and WarpCursor are
# labwc actions (labwc 0.8.4 and newer); the cursor comes back as soon as a mouse is moved.
rc_block() {
  printf '  %s\n' "$MARK_BEGIN"
  printf '  <windowRules>\n'
  printf '    <windowRule identifier="chromium*">\n'
  printf '      <action name="HideCursor" />\n'
  printf '      <action name="WarpCursor" to="output" x="-1" y="-1" />\n'
  printf '    </windowRule>\n'
  printf '  </windowRules>\n'
  printf '  %s\n' "$MARK_END"
}

# Remove our block from rc.xml ($1 = "remove") or (re)insert it before the closing root tag.
edit_rc_xml() {
  local mode="$1" rc="$SB_HOME/.config/labwc/rc.xml"
  # shellcheck disable=SC2016 # the Perl program is meant to be single-quoted
  as_user env SB_MODE="$mode" SB_BLOCK="$(rc_block)" perl -e '
    my $f = shift;
    open(my $in, "<", $f) or die "cannot read $f\n";
    local $/; my $x = <$in>; close $in;
    $x =~ s/[ \t]*<!-- screenbird-kiosk begin.*?<!-- screenbird-kiosk end -->[ \t]*\n?//sg;
    if ($ENV{SB_MODE} ne "remove") {
      $x =~ s{^(.*)(</(?:openbox|labwc)_config>)}{$1$ENV{SB_BLOCK}\n$2}s
        or die "no closing root element in $f\n";
    }
    open(my $out, ">", "$f.screenbird-tmp") or die "cannot write $f\n";
    print $out $x; close $out or die "cannot write $f\n";
    rename("$f.screenbird-tmp", $f) or die "cannot replace $f\n";
  ' "$rc"
}

configure_labwc() {
  local dir="$SB_HOME/.config/labwc"
  as_user mkdir -p "$dir"
  if [ ! -e "$dir/rc.xml" ]; then
    printf '%s\n%s\n%s\n' "$RC_HEAD" "$RC_OPEN" "$RC_CLOSE" | as_user tee "$dir/rc.xml" >/dev/null
  fi
  if edit_rc_xml insert; then
    if [ "$SESSION" = labwc ]; then
      say "Mouse cursor: hidden when the player opens ($dir/rc.xml)"
    else
      say "labwc cursor rule added, used if this Pi is switched to labwc ($dir/rc.xml)"
    fi
  else
    warn "Could not update $dir/rc.xml, the mouse cursor stays visible on labwc."
  fi
  # Screen blanking on labwc is a swayidle line in the user autostart (as raspi-config does it).
  if [ -e "$dir/autostart" ] && grep -q swayidle "$dir/autostart"; then
    as_user sed -i '/swayidle/d' "$dir/autostart"
  fi
}

configure_wayfire() {
  local ini="$SB_HOME/.config/wayfire.ini"
  # Same change raspi-config makes to switch off blanking on wayfire.
  if [ -e "$ini" ] && grep -q dpms_timeout "$ini"; then
    as_user sed -i 's/dpms_timeout.*/dpms_timeout=-1/' "$ini"
  fi
}

configure_x11() {
  # Same file raspi-config installs to switch off blanking on X11.
  if [ -e /usr/share/raspi-config/10-blanking.conf ]; then
    as_root install -D -m 644 /usr/share/raspi-config/10-blanking.conf "$X11_BLANKING_CONF"
  fi
}

# The session lightdm starts at login (not what runs now: the installer may run over SSH).
configured_session() {
  local s
  s="$(sed -n 's/^[[:space:]]*autologin-session[[:space:]]*=[[:space:]]*//p' /etc/lightdm/lightdm.conf 2>/dev/null | head -n1)"
  if [ -z "$s" ]; then
    s="$(sed -n 's/^[[:space:]]*user-session[[:space:]]*=[[:space:]]*//p' /etc/lightdm/lightdm.conf 2>/dev/null | head -n1)"
  fi
  printf '%s' "$s"
}

os_codename() { sed -n 's/^VERSION_CODENAME=//p' /etc/os-release 2>/dev/null | head -n1; }

# Switch a Bookworm wayfire desktop to labwc, only when every precondition holds. Without
# labwc installed a switch gives a black screen, so then we only warn.
switch_to_labwc() {
  local why=""
  case "$(configured_session)" in *wayfire*) ;; *) [ "$SESSION" = wayfire ] || return 0 ;; esac
  if [ "$KEEP_SESSION" -eq 1 ]; then why="--keep-session was given"
  elif [ "$(os_codename)" != bookworm ]; then why="this is not Raspberry Pi OS Bookworm"
  elif ! have_raspi_config; then why="raspi-config is not installed"
  elif [ ! -x /usr/bin/labwc ] || ! compgen -G "/usr/share/wayland-sessions/*labwc*.desktop" >/dev/null; then
    why="labwc is not installed (update the Pi first:  sudo apt update && sudo apt full-upgrade)"
  fi
  if [ -n "$why" ]; then
    warn "This Pi starts the wayfire desktop, which cannot hide the mouse cursor. The desktop is not
switched to labwc because $why."
    return 0
  fi
  step "Switching the desktop from wayfire to labwc"
  say "Raspberry Pi no longer updates wayfire and recommends labwc. On labwc the mouse cursor is hidden."
  if ! as_root test -e "$SWITCH_FILE"; then
    as_root install -d -m 755 "$SWITCH_BACKUP"
    as_root cp -p /etc/lightdm/lightdm.conf "$SWITCH_BACKUP/lightdm.conf"
    if [ -e "$LABWC_PROMPT" ]; then as_root cp -p "$LABWC_PROMPT" "$SWITCH_BACKUP/labwc-prompt.desktop"; fi
    printf 'user=%s\nprev_session=%s\n' "$SB_USER" "$(configured_session)" | as_root tee "$SWITCH_FILE" >/dev/null
  fi
  # The same steps as Raspberry Pi's own switch prompt (labwc-prompt).
  raspi update_labwc_keyboard >/dev/null 2>&1 || true
  raspi do_wayland W3
  as_root rm -f "$LABWC_PROMPT"
  case "$(configured_session)" in
    *labwc*) SESSION=labwc; say "Desktop session: labwc (switch back with --uninstall)" ;;
    *) warn "Switching to labwc did not work; the desktop stays on wayfire and the mouse cursor stays visible." ;;
  esac
  if [ -e "$SB_HOME/.config/wayfire.ini" ] && grep -q '^\[output:' "$SB_HOME/.config/wayfire.ini"; then
    warn "This Pi has screen settings (rotation or resolution) saved for wayfire. After the restart,
set them again in the Screen Configuration tool."
  fi
}

# Undo switch_to_labwc, if the installer made that switch and the Pi still runs labwc.
switch_back_session() {
  local prev
  as_root test -e "$SWITCH_FILE" || return 0
  prev="$(as_root sed -n 's/^prev_session=//p' "$SWITCH_FILE")"
  case "$prev" in *wayfire*) ;; *) return 0 ;; esac
  case "$(configured_session)" in *labwc*) ;; *) return 0 ;; esac
  if have_raspi_config && [ "$(os_codename)" = bookworm ]; then
    raspi do_wayland W2
    if as_root test -e "$SWITCH_BACKUP/labwc-prompt.desktop"; then
      as_root install -m 644 "$SWITCH_BACKUP/labwc-prompt.desktop" "$LABWC_PROMPT"
    fi
    say "Desktop session: back to wayfire"
  fi
}

configure_desktop() {
  step "Configuring autologin and screen blanking"
  if have_raspi_config; then
    raspi do_boot_behaviour B4
    raspi do_blanking 1
  else
    warn "raspi-config not found, setting desktop autologin directly."
    as_root sed -i -e "s/^#\?autologin-user=.*/autologin-user=$SB_USER/" /etc/lightdm/lightdm.conf
    as_root systemctl set-default graphical.target >/dev/null 2>&1 || true
  fi
  if desktop_autologin_on; then
    say "Desktop autologin: on (user $SB_USER)"
  else
    warn "Desktop autologin could not be switched on. Use the Raspberry Pi Configuration tool to log in automatically."
  fi
  # Cover every desktop session that is installed, so switching sessions later keeps working.
  if [ -x /usr/bin/labwc ]; then configure_labwc; fi
  if [ -x /usr/bin/wayfire ]; then configure_wayfire; fi
  if [ -x /usr/bin/openbox ]; then configure_x11; fi
  say "Screen blanking: off"
}

do_install() {
  step "Screenbird Player installer $SB_VERSION"
  check_raspberry_pi
  check_64bit
  check_desktop
  check_network
  get_sudo
  SESSION="$(detect_session)"
  say "User: $SB_USER, desktop session: $SESSION"

  install_packages
  remove_legacy_service
  write_kiosk_script
  save_state
  write_autostart
  switch_to_labwc
  configure_desktop

  step "Done"
  say "The Screenbird player is installed."
  say "Restart the Raspberry Pi to start it:   sudo reboot"
  say "After the restart the player opens full screen and shows a pairing code."
  say "Enter that code in the Screenbird dashboard to connect this screen."
  say "To remove it again:   bash screenbird-rpi-setup.sh --uninstall"
}

do_uninstall() {
  local prev_da="" prev_ca="" prev_blank="" prev_xconf=""
  step "Removing the Screenbird player"
  get_sudo
  if as_root test -e "$STATE_FILE"; then
    prev_da="$(as_root sed -n 's/^prev_desktop_autologin=//p' "$STATE_FILE")"
    prev_ca="$(as_root sed -n 's/^prev_console_autologin=//p' "$STATE_FILE")"
    prev_blank="$(as_root sed -n 's/^prev_blanking=//p' "$STATE_FILE")"
    prev_xconf="$(as_root sed -n 's/^prev_x11_blanking_conf=//p' "$STATE_FILE")"
  fi

  pkill -u "$SB_USER" -f "$KIOSK_BIN" >/dev/null 2>&1 || true
  pkill -u "$SB_USER" -f "user-data-dir=$SB_HOME/.config/screenbird-kiosk" >/dev/null 2>&1 || true

  as_user rm -f "$SB_HOME/.config/autostart/screenbird-kiosk.desktop"
  as_user rm -rf "$SB_HOME/.config/screenbird-kiosk" "$SB_HOME/.cache/screenbird"

  local rc="$SB_HOME/.config/labwc/rc.xml"
  if [ -e "$rc" ] && grep -q "screenbird-kiosk begin" "$rc"; then
    edit_rc_xml remove || warn "Could not clean up $rc"
    # Delete the file again if the installer created it and nothing else was added since.
    if [ "$(tr -d '[:space:]' < "$rc")" = "$(printf '%s%s%s' "$RC_HEAD" "$RC_OPEN" "$RC_CLOSE" | tr -d '[:space:]')" ]; then
      as_user rm -f "$rc"
    fi
  fi

  remove_legacy_service
  switch_back_session

  # Put autologin and screen blanking back the way they were before the first install.
  if have_raspi_config; then
    if [ "$prev_da" = "no" ]; then
      if [ "$prev_ca" = "yes" ]; then raspi do_autologin 1; else raspi do_autologin 0; fi
      say "Desktop autologin: switched off again"
    fi
    if [ "$prev_blank" = "0" ]; then
      raspi do_blanking 0
      say "Screen blanking: switched on again"
    fi
  fi
  if [ "$prev_xconf" = "no" ]; then as_root rm -f "$X11_BLANKING_CONF"; fi

  as_root rm -rf "$INSTALL_DIR"
  step "Done"
  say "The Screenbird player is removed. Chromium itself stays installed."
  say "Restart the Raspberry Pi to finish:   sudo reboot"
}

main() {
  local mode=install
  while [ $# -gt 0 ]; do
    case "$1" in
      --uninstall) mode=uninstall ;;
      --keep-session) KEEP_SESSION=1 ;;
      -h | --help) usage; exit 0 ;;
      *) usage; die "Unknown option: $1" ;;
    esac
    shift
  done
  trap 'on_error $LINENO' ERR
  resolve_user
  if [ "$mode" = uninstall ]; then do_uninstall; else do_install; fi
}

# Everything above is only definitions; the script is fully read before main runs, which
# keeps "curl ... | bash" safe.
main "$@"
