#!/bin/sh
# Session entry point: bring up a compositor and run Wine's explorer as the
# shell inside it. greetd exec's this as the session user.
set -eu

# No core dumps in the user's home: wlroots starts Xwayland with -core, and
# Xwayland dies at every sign-out -- each left a 20 MB core file in the home
# (now the user's profile, seen in File Explorer). Nobody reads them here.
# shellcheck disable=SC3045 # dash (/bin/sh here) has ulimit -c
ulimit -c 0 2>/dev/null || true

SG_LIB="${SG_LIB:-/usr/lib/stained-glass}"
export SG_LIB
# shellcheck source=lib/sg-common.sh
. "$SG_LIB/sg-common.sh"
sg_wine_env

GEOM="${SG_DESKTOP_W}x${SG_DESKTOP_H}"
mkdir -p "$SG_LOG_DIR" 2>/dev/null || true

# The prefix should already exist (baked at image build). If first boot beat us
# to it, build it now rather than failing the session.
if [ ! -e "$SG_PREFIX/.sg-initialized" ]; then
    sg_log "prefix missing; initializing inline"
    "${SG_BIN:-/usr/bin}/sg-prefix-init"
fi

# XDG_RUNTIME_DIR is where the Wayland socket lands. greetd normally supplies
# it via pam_systemd; synthesize one if we were launched without a session.
if [ -z "${XDG_RUNTIME_DIR:-}" ]; then
    XDG_RUNTIME_DIR="/run/user/$(id -u)"
    export XDG_RUNTIME_DIR
    mkdir -p "$XDG_RUNTIME_DIR"
    chmod 700 "$XDG_RUNTIME_DIR"
fi

# The desktop's name, for Linux programs: the portal (xdg-desktop-portal)
# chooses its backends by it (stainedglass-portals.conf: GTK's), and through
# it GTK 4, Qt 6 and browsers learn the user's light or dark mode and accent
# (sg-settingsctl look). The portal is started by D-Bus in the user's
# manager, so the name goes there too. ":sway" after ours: screenshot tools
# go to the portal only for a desktop they know -- Flameshot for GNOME, KDE or
# sway, else it grabs the X root, all black under rootless Xwayland. Ours is a
# wlroots compositor with sway's protocols; the portal still takes our
# stainedglass-portals.conf, the first name it finds a file for.
: "${XDG_CURRENT_DESKTOP:=StainedGlass:sway}"
: "${XDG_SESSION_DESKTOP:=stained-glass}"
export XDG_CURRENT_DESKTOP XDG_SESSION_DESKTOP
systemctl --user import-environment XDG_CURRENT_DESKTOP XDG_SESSION_DESKTOP >/dev/null 2>&1 || true
if command -v dbus-update-activation-environment >/dev/null 2>&1; then
    dbus-update-activation-environment XDG_CURRENT_DESKTOP XDG_SESSION_DESKTOP >/dev/null 2>&1 || true
fi

# Linux programs through Xwayland, not full screen as native Wayland ones
sg_linux_app_env

# An administrator's session (wine-sg 0623): a program whose manifest asks for
# the highest level available (highestAvailable; NSIS "highest") asks for
# consent, as Windows does for an administrator; for a standard user it runs
# as the user. Only whether to ask: the broker still checks who answers.
case " $(id -nG 2>/dev/null) " in
    *" $SG_ADMIN_GROUP "*) SG_USER_ADMIN=1; export SG_USER_ADMIN ;;
    *) unset SG_USER_ADMIN ;;
esac

# A remote session (started by sg-rdp-authd, ADR 0010) has no screen of its
# own: the compositor runs headless, at the RDP client's size, and the RDP
# daemon streams it through the compositor's privileged socket. Its seat
# directory (SG_SEAT_DIR) is its own, so it has its own lock screen.
if [ "${SG_REMOTE:-0}" = 1 ]; then
    WLR_BACKENDS=headless
    WLR_LIBINPUT_NO_DEVICES=1
    WLR_RENDERER=pixman
    export WLR_BACKENDS WLR_LIBINPUT_NO_DEVICES WLR_RENDERER SG_SEAT_DIR
    case "${SG_OUTPUT_SIZE:-}" in
        [0-9]*x[0-9]*) GEOM="$SG_OUTPUT_SIZE"; export SG_OUTPUT_SIZE ;;
    esac
    sg_log "remote session: headless at $GEOM, seat $SG_SEAT_DIR"
fi

# The per-user process agent (ADR 0014, debt D16/D19): the machine-level
# wineserver runs as SYSTEM and cannot ptrace/signal this user's processes, so
# it delegates those to this agent, which runs as the user. Started before the
# shell so cross-process operations (debuggers, ReadProcessMemory, async APCs,
# thread affinity) work from the first program. It removes its own socket on
# exit; greetd tears down the session's process group, so it stops with us.
SG_LIBEXEC="${SG_LIBEXEC:-/usr/libexec/stained-glass}"
if [ -x "$SG_LIBEXEC/sg-procagent" ]; then
    "$SG_LIBEXEC/sg-procagent" "$SG_PREFIX" &
    sg_log "started sg-procagent (pid $!) for $(id -un)"
else
    sg_log "note: sg-procagent not present; cross-process operations limited"
fi

# The session's polkit agent: a Linux program that asks to act as an
# administrator (pkexec: GParted, Timeshift) gets the same consent as "Run as
# administrator", through the broker. Without an agent polkit just refused.
if [ -x "$SG_LIBEXEC/sg-polkit-agent" ]; then
    "$SG_LIBEXEC/sg-polkit-agent" &
    sg_log "started sg-polkit-agent (pid $!) for $(id -un)"
fi

sg_session_backdrop
sg_log "starting session: display path=$SG_DISPLAY_PATH geometry=$GEOM"

case "$SG_DISPLAY_PATH" in
    x11)
        # The compositor hosts a single Wayland client full-screen. XWayland
        # gives Wine an X server, and winex11's virtual desktop draws the shell
        # and taskbar into one window sized to the output. See ADR 0003.
        sg_log "compositor: $SG_COMPOSITOR"
        # sg-compositor gets its lock and privileged sockets (ADR 0009/0011);
        # cage has no such options. Without a writable seat directory the
        # session still runs, just without lock support -- and says so.
        set --
        if [ "$(basename "$SG_COMPOSITOR")" = sg-compositor ]; then
            _dir="$SG_SEAT_DIR/$(id -u)"
            # `|| true`: this script runs under set -e, and a missing seat
            # directory must mean "no lock support", not a silently dead session.
            mkdir -m 0755 "$_dir" 2>/dev/null || true
            if [ -d "$_dir" ] && [ -w "$_dir" ]; then
                SG_LOCK_PRIV="$_dir/priv.sock"
                SG_LOCK_CONTROL="$_dir/control.sock"
                export SG_LOCK_PRIV SG_LOCK_CONTROL
                _uid=$(id -u "$SG_SYSTEM_USER" 2>/dev/null || true)
                # -s: Ctrl+Alt+F1-F9 switch to the text consoles; the
                # reload key (Ctrl+Alt+Backspace) restarts this account's
                # Windows side (David 2026-10-03)
                SG_RELOAD_COMMAND="$SG_LIB/sg-wine-reload"
                export SG_RELOAD_COMMAND
                set -- -s -L "$SG_LOCK_PRIV" -C "$SG_LOCK_CONTROL"
                [ -n "$_uid" ] && set -- "$@" -U "$_uid"
            else
                sg_log "WARNING: no writable $SG_SEAT_DIR: this session cannot be locked"
            fi
        fi
        # The compositor's audit lines (lock, unlock, refusals) must reach the
        # journal, and greetd forwards a session's output nowhere; systemd-cat
        # tags it sg-session. Where there is no journal (the dev-box gate),
        # plain exec keeps output on the harness's log.
        if [ "${SG_LOG_TO_JOURNAL:-1}" = 1 ] && command -v systemd-cat >/dev/null 2>&1 && [ -S /run/systemd/journal/stdout ]; then
            exec systemd-cat -t sg-session -- "$SG_COMPOSITOR" "$@" -- "$SG_LIB/sg-run-explorer" "$GEOM"
        fi
        exec "$SG_COMPOSITOR" "$@" -- "$SG_LIB/sg-run-explorer" "$GEOM"
        ;;
    wayland)
        WINE_DISPLAY_DRIVER=wayland
        export WINE_DISPLAY_DRIVER
        exec "$SG_COMPOSITOR" -- "$SG_LIB/sg-run-explorer" "$GEOM"
        ;;
    *)
        sg_die "unknown SG_DISPLAY_PATH: $SG_DISPLAY_PATH (expected x11 or wayland)"
        ;;
esac
