#!/bin/sh
# ps3-game <command...>: runs a game in game mode (ps3-desktop). The game runs in ps3-game.slice
# (memory.low: kept resident, like X in ps3-display.slice). While any game started this way runs,
# game mode is on: ps3-game-guard, a user service, gets the rest of the desktop out of the way
# before the game starts and brings it all back after the last game exits, however it exits
# (crash, kill, a killed launcher; see ps3-game-guard).
# ps3-game-detect enters game mode by itself for any fullscreen 3D program; the menu's games
# still start through here: game mode is on before they load (when memory is shortest), and a
# game drawn without the GPU (dsda-doom's and OpenTTD's software modes) gets it too. Such a game is
# left to this launcher (the detector skips ps3-game.slice's own scopes): counted once, and its
# game mode lasts until it exits, fullscreen or not.
# PS3_GAME_MODE (the environment: /etc/environment, ~/.profile or ~/.config/environment.d, the
# menu entry, the command line; ps3-session) on (the default); pads: only the gamepads leave the
# pointer, nothing frozen or reclaimed (a baseline to measure against); off: not even that.
# In game mode the game's own files are read back at its start, in disk order, from what it used
# last time (ps3-game-prefetch game-watch, which the guard runs; its key is the command here):
# PS3_GAME_PREFETCH_GAME=on|fetch|learn|off (default off: no gain measured), docs/STALLS.md "Game prefetch".
# The game loads zlib-ng's compat libz.so.1 (libz-ng-compat, when installed) instead of zlib1g's:
# its inflate and crc32 are faster on the PPE (SuperTuxKart's load -0.9 s, docs/TESTS.md 10-19).
# Games only, not the system: PS3_ZLIB=stock keeps zlib1g's.
G=/usr/lib/ps3/desktop/ps3-game-guard
ZNG=/usr/lib/powerpc64-linux-gnu/zlib-ng
[ $# -gt 0 ] || { echo "usage: ps3-game <command...>" >&2; exit 2; }
if [ "${PS3_ZLIB:-ng}" != stock ] && [ -e "$ZNG/libz.so.1" ]; then
	case :${LD_LIBRARY_PATH:-}: in *:$ZNG:*) ;; *) export LD_LIBRARY_PATH=$ZNG${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} ;; esac
fi
# Started from the menu or the desktop, this script runs in ps3-desktop.slice, which game mode
# freezes: it moves into a scope of ps3-game.slice first. Without a user manager: the game alone.
# Debian puts most games in /usr/games, which isn't on the PATH of everything that starts one (the
# user manager's has /usr/bin only: a game started from the Walnut Games app or a desktop icon was
# "not found")
case ":$PATH:" in *:/usr/games:*) ;; *) export PATH="$PATH:/usr/local/games:/usr/games" ;; esac
if [ -z "${PS3_GAME_SCOPED:-}" ]; then
	export PS3_GAME_SCOPED=1
	if ! systemctl --user show-environment >/dev/null 2>&1; then
		echo "ps3-game: no user service manager: $1 runs without game mode" >&2
		exec "$@"
	fi
	exec systemd-run --user --scope --slice=ps3-game.slice --unit="ps3-game-$$" -q "$0" "$@"
fi
mode=${PS3_GAME_MODE:-on}
# registered as game $$ (this launcher, then the game too: alive while either runs); the first
# game starts the guard. The game starts once game mode is on, or after 30 s without it.
[ "$mode" = off ] || "$G" join $$ "$mode" $$ ||
	echo "ps3-game: game mode didn't come on; the game starts without it" >&2
# A terminal of the frozen desktop stops reading, and a game writing to it would block: its
# output goes to a file then (not over SSH: sshd isn't frozen)
if [ "$mode" = on ] && [ -z "${SSH_TTY:-}" ] && { [ -t 1 ] || [ -t 2 ]; }; then
	out=${XDG_CACHE_HOME:-$HOME/.cache}/ps3-game
	mkdir -p "$out"; out=$out/${1##*/}.log
	echo "ps3-game: the terminal is frozen in game mode; the game's output goes to $out" >&2
	exec > "$out" 2>&1
fi
systemd-run --user --scope --slice=ps3-game.slice -q "$@" &
game=$!
[ "$mode" = off ] || "$G" add $$ $game
trap 'kill $game 2>/dev/null' INT TERM HUP
wait $game
ret=$?
while kill -0 $game 2>/dev/null; do wait $game; ret=$?; done	# a trapped signal ended the wait early
[ "$mode" = off ] || "$G" quit $$	# the guard leaves within a second if it was the last
exit $ret
