#!/bin/sh
# xfce-scoped-session: run the whole XFCE desktop session inside its
# own systemd user scope under desktop.slice.
#
# Why this helps:
#   - The DE (xfce4-session, panel, compiz, applets) finally has a real
#     cgroup unit.
#   - oomd's kill patterns (session-*.scope, app.slice/*, system.slice/*)
#     never match desktop.slice/run-*.scope, so the DE is permanently
#     UNTARGETABLE by oomd -- it can never be chosen as a kill victim.
#   - desktop.slice.d/10-memory-low-aerolike.conf reserves MemoryLow so
#     apps can't starve the DE into swap.
#   - oomd-protect-self tags the scope with user.oomd_avoid as
#     defense-in-depth in case oomd patterns ever change.
#
# Apps launched from the panel inherit this scope and are protected by
# association. Heavy native apps should still be wrapped explicitly
# (systemd-run --scope, or the AppImage/sas shim) to get their own
# killable cgroup.
#
# Used as LightDM session-wrapper:
#   [Seat:*]
#   session-wrapper=/usr/bin/xfce-scoped-session
#
# This is the distro LightDM Xsession environment setup (profiles,
# Xresources, keymaps, xmodmap, xinitrc.d, user .xsession) with only
# the final `exec $@` replaced by a systemd-run scope launch.

echo "Running X session wrapper"

# From https://github.com/sddm/sddm/blob/develop/data/scripts/Xsession
# Note that the respective logout scripts are not sourced.
case $SHELL in
  */bash)
    [ -z "$BASH" ] && exec $SHELL --login $0 "$@"
    shopt -q login_shell || exec $SHELL --login $0 "$@"
    set +o posix
    ;;
  */zsh)
    [ -z "$ZSH_NAME" ] && exec $SHELL --login $0 "$@"
    [[ -o login ]] || exec $SHELL --login $0 "$@"
    emulate -R sh
    ;;
  */csh|*/tcsh)
    # [t]cshrc is always sourced automatically.
    # Note that sourcing csh.login after .cshrc is non-standard.
    xsess_tmp=`mktemp /tmp/xsess-env-XXXXXX`
    $SHELL -c "if (-f /etc/csh.login) source /etc/csh.login; if (-f ~/.login) source ~/.login; /bin/sh -c 'export -p' >! $xsess_tmp"
    . $xsess_tmp
    rm -f $xsess_tmp
    ;;
  */fish)
    [ -f /etc/profile ] && . /etc/profile
    [ -f $HOME/.profile ] && . $HOME/.profile
    xsess_tmp=`mktemp /tmp/xsess-env-XXXXXX`
    $SHELL --login -c "/bin/sh -c 'export -p' > $xsess_tmp"
    . $xsess_tmp
    rm -f $xsess_tmp
    ;;
  *) # Plain sh, ksh, and anything we do not know.
    [ -f /etc/profile ] && . /etc/profile
    [ -f "$HOME/.profile" ] && . "$HOME/.profile"
    ;;
esac

[ -f /etc/xprofile ] && . /etc/xprofile
[ -f /usr/local/etc/xprofile ] && . /usr/local/etc/xprofile
[ -f "$HOME/.xprofile" ] && . "$HOME/.xprofile"

# Load resources
for file in "/etc/X11/Xresources" "$HOME/.Xresources"; do
    if [ -f "$file" ]; then
        echo "Loading resource: $file"
        xrdb -merge "$file"
    fi
done

# Load keymaps
for file in "/etc/X11/Xkbmap" "$HOME/.Xkbmap"; do
    if [ -f "$file" ]; then
        echo "Loading keymap: $file"
        setxkbmap `cat "$file"`
        XKB_IN_USE=yes
    fi
done

# Load xmodmap if not using XKB
if [ -z "$XKB_IN_USE" ]; then
    for file in "/etc/X11/Xmodmap" "$HOME/.Xmodmap"; do
        if [ -f "$file" ]; then
           echo "Loading modmap: $file"
           xmodmap "$file"
        fi
    done
fi

unset XKB_IN_USE

# Run all system xinitrc shell scripts
xinitdir="/etc/X11/xinit/xinitrc.d"
if [ -d "$xinitdir" ]; then
    for script in $xinitdir/*; do
        echo "Loading xinit script $script"
        if [ -x "$script" -a ! -d "$script" ]; then
            . "$script"
        fi
    done
fi

# Run user xsession shell script
script="$HOME/.xsession"
if [ -x "$script" -a ! -d "$script" ]; then
    echo "Loading xsession script $script"
    . "$script"
fi

echo "X session wrapper complete, running session $@"

# Run inside the scope: tag our own scope cgroup with oomd_avoid, then
# exec the real session command ($@ is the session command + its args).
# $0 is set to a label so $@ starts at the session command.
#
# Kernel-OOM protection for the DE is provided by MemoryLow on
# desktop.slice (see desktop.slice.d/10-memory-low-aerolike.conf): the
# kernel avoids reclaiming/OOM-killing processes in that cgroup. We
# deliberately do NOT set OOMScoreAdjust here -- scope units have no
# ExecContext so systemd-run rejects it, and even on a service it can't
# be lowered below the current value by an unprivileged session
# (requires CAP_SYS_RESOURCE), so a direct /proc write would also fail.
exec systemd-run --user --scope --slice=desktop.slice -- \
    /bin/sh -c '/usr/bin/oomd-protect-self; exec "$@"' xfce-scoped-session "$@"
