#!/usr/bin/bash

declare -r CLIENT=$1
declare -r CLIENT_CONFIG_DIR_USR=/usr/share/gamescope-session-plus/sessions.d
declare -r CLIENT_CONFIG_DIR_ETC=/etc/gamescope-session-plus/sessions.d
declare -r CLIENT_CONFIG_DIR_LOCAL="${XDG_CONFIG_HOME:-$HOME/.config}/gamescope-session-plus/sessions.d"

# Reset power-profiles-daemon/TuneD to balanced if the current profile is power save, this allows desktop control without greatly harming gaming performance.
if command -v tuned-adm > /dev/null; then
	if grep -qz "powersave" <<< "$(tuned-adm active)"; then
		tuned-adm profile balanced
	fi
elif command -v powerprofilesctl > /dev/null; then
	if grep -qz "power-saver" <<< "$(powerprofilesctl get)"; then
		powerprofilesctl set balanced
	fi
fi

# default empty session recovery function
short_session_recover() {
	:
}

# default empty function to run after gamescope is started
post_gamescope_start() {
	:
}

gamescope_has_option() {
	if (gamescope --help 2>&1 | grep -e "$1" > /dev/null); then
		return 0
	fi

	return 1
}

# Fix intel color corruption
# might come with some performance degradation but is better than a corrupted
# color image
export INTEL_DEBUG=norbc
export mesa_glthread=true

# This should be used by default by gamescope. Cannot hurt to force it anyway.
# Reported better framelimiting with this enabled
if /usr/libexec/gamescope-sdl-workaround; then
	# Skip on legacy hardware
	export ENABLE_GAMESCOPE_WSI=0
else
	export ENABLE_GAMESCOPE_WSI=1
fi

# Force Qt applications to run under xwayland
export QT_QPA_PLATFORM=xcb

# Some environment variables by default (taken from Deck session)
export SDL_VIDEO_MINIMIZE_ON_FOCUS_LOSS=0

# Enable Mangoapp
export MANGOHUD_CONFIGFILE=$(mktemp /tmp/mangohud.XXXXXXXX)

export RADV_FORCE_VRS_CONFIG_FILE=$(mktemp /tmp/radv_vrs.XXXXXXXX)

# Plop GAMESCOPE_MODE_SAVE_FILE into $XDG_CONFIG_HOME (defaults to ~/.config).
export GAMESCOPE_MODE_SAVE_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/gamescope/modes.cfg"
export GAMESCOPE_PATCHED_EDID_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/gamescope/edid.bin"

# Make path to gamescope mode save file.
mkdir -p "$(dirname "$GAMESCOPE_MODE_SAVE_FILE")"
touch "$GAMESCOPE_MODE_SAVE_FILE"
#echo "Making Gamescope Mode Save file at \"$GAMESCOPE_MODE_SAVE_FILE\""

# Make path to Gamescope edid patched file.
mkdir -p "$(dirname "$GAMESCOPE_PATCHED_EDID_FILE")"
touch "$GAMESCOPE_PATCHED_EDID_FILE"
#echo "Making Gamescope patched edid at \"$GAMESCOPE_PATCHED_EDID_FILE\""

# There is no way to set a color space for an NV12
# buffer in Wayland. And the color management protocol that is
# meant to let this happen is missing the color range...
# So just workaround this with an ENV var that Remote Play Together
# and Gamescope will use for now.
export GAMESCOPE_NV12_COLORSPACE=k_EStreamColorspace_BT601

# Initially write no_display to our config file
# so we don't get mangoapp showing up before Steam initializes
# on OOBE and stuff.
mkdir -p "$(dirname "$MANGOHUD_CONFIGFILE")"
echo "no_display" >"$MANGOHUD_CONFIGFILE"

# Prepare our initial VRS config file
# for dynamic VRS in Mesa.
mkdir -p "$(dirname "$RADV_FORCE_VRS_CONFIG_FILE")"
echo "1x1" >"$RADV_FORCE_VRS_CONFIG_FILE"

# To expose vram info from radv
export WINEDLLOVERRIDES=dxgi=n

# Don't wait for buffers to idle on the client side before sending them to gamescope
export vk_xwayland_wait_ready=false

# To play nice with the short term callback-based limiter for now
export GAMESCOPE_LIMITER_FILE=$(mktemp /tmp/gamescope-limiter.XXXXXXXX)

# Temporary crutch until dummy plane interactions / etc are figured out
export GAMESCOPE_DISABLE_ASYNC_FLIPS=1

# Make Qt apps use the styling and behaviour of the desktop session
# This fixes some missing icons and unreadable text with Qt desktop apps in gamescope
if [[ ${BASE_IMAGE_NAME} == 'silverblue' ]]; then
  export QT_QPA_PLATFORM_THEME=gtk2
else
  export QT_QPA_PLATFORM_THEME=kde
fi

ulimit -n 524288

# Source device quirks if exists
if [ -f /usr/share/gamescope-session-plus/device-quirks ]; then
	. /usr/share/gamescope-session-plus/device-quirks
fi

# Source client app specific configuration files
set -a
for i in ${CLIENT_CONFIG_DIR_USR}/${CLIENT} ${CLIENT_CONFIG_DIR_ETC}/${CLIENT} ${CLIENT_CONFIG_DIR_LOCAL}/${CLIENT}; do
	[[ -f "${i}" ]] && . "${i}"
done
set +a

# Source user configuration from ~/.config/environment.d
set -a
for i in "${HOME}"/.config/environment.d/*.conf; do
	[[ -f "${i}" ]] && . "${i}"
done
set +a

# Setup socket for gamescope
# Create run directory file for startup and stats sockets
tmpdir="$([[ -n ${XDG_RUNTIME_DIR+x} ]] && mktemp -p "$XDG_RUNTIME_DIR" -d -t gamescope.XXXXXXX)"
socket="${tmpdir:+$tmpdir/startup.socket}"
stats="${tmpdir:+$tmpdir/stats.pipe}"
# Fail early if we don't have a proper runtime directory setup
if [[ -z $tmpdir || -z ${XDG_RUNTIME_DIR+x} ]]; then
	echo >&2 "!! Failed to find run directory in which to create stats session sockets (is \$XDG_RUNTIME_DIR set?)"
	exit 0
fi

export GAMESCOPE_STATS="$stats"
mkfifo -- "$stats"
mkfifo -- "$socket"

# Attempt to claim global session if we're the first one running (e.g. /run/1000/gamescope)
linkname="gamescope-stats"
#   shellcheck disable=SC2031 # (broken warning)
sessionlink="${XDG_RUNTIME_DIR:+$XDG_RUNTIME_DIR/}${linkname}" # Account for XDG_RUNTIME_DIR="" (notfragileatall)
lockfile="$sessionlink".lck
exec 9>"$lockfile" # Keep as an fd such that the lock lasts as long as the session if it is taken
if flock -n 9 && rm -f "$sessionlink" && ln -sf "$tmpdir" "$sessionlink"; then
	# Took the lock.  Don't blow up if those commands fail, though.
	echo >&2 "Claimed global gamescope stats session at \"$sessionlink\""
else
	echo >&2 "!! Failed to claim global gamescope stats session"
fi

if [ -z "$GAMESCOPECMD" ]; then
	# Set default values
	: "${OUTPUT_CONNECTOR:=*,eDP-1}"
	: "${XWAYLAND_COUNT:=2}"
	: "${TOUCH_MODE:=4}"
	: "${HIDE_CURSOR_DELAY_MS:=3000}"
	: "${FADE_OUT_DURATION_MS:=200}"

	CURSOR=""
	if [ -f "$CURSOR_FILE" ]; then
		# Use specified cursor if file exists
		CURSOR="--cursor ${CURSOR_FILE}"
	fi

	RESOLUTION=""
	if [ -n "$SCREEN_WIDTH" ] && [ -n "$SCREEN_HEIGHT" ]; then
		RESOLUTION="-W $SCREEN_WIDTH -H $SCREEN_HEIGHT"
	fi

	INTERNAL_RESOLUTION=""
	if [ -n "$INTERNAL_WIDTH" ] && [ -n "$INTERNAL_HEIGHT" ] ; then
		INTERNAL_RESOLUTION="-w $INTERNAL_WIDTH -h $INTERNAL_HEIGHT"
	fi

	DRM_MODE_OPTION=""
	if [ -n "$DRM_MODE" ] ; then
		DRM_MODE_OPTION="--generate-drm-mode $DRM_MODE"
	fi

	ORIENTATION_OPTION=""
	if [ -n "$ORIENTATION" ] ; then
		ORIENTATION_OPTION="--force-orientation $ORIENTATION"
	fi

	ADAPTIVE_SYNC_OPTION=""
	if [ -n "$ADAPTIVE_SYNC" ]; then
		ADAPTIVE_SYNC_OPTION="--adaptive-sync"
	fi

	FORCE_COMPOSITION_OPTION=""
	if [ -n "$FORCE_COMPOSITION" ]; then
		FORCE_COMPOSITION_OPTION="--force-composition"
	fi

	PANEL_TYPE_OPTION=""
	if [ -n "$PANEL_TYPE" ] && gamescope_has_option "--force-panel-type"; then
		PANEL_TYPE_OPTION="--force-panel-type $PANEL_TYPE"
	fi

	BYPASS_STEAM_RESOLUTION_OPTION=""
	if [ ! -n "$DONT_BYPASS_RESOLUTION" ] && gamescope_has_option "--bypass-steam-resolution"; then
		BYPASS_STEAM_RESOLUTION_OPTION="--bypass-steam-resolution"
	fi

	CUSTOM_REFRESH_RATES_OPTION=""
	if [ -n "$CUSTOM_REFRESH_RATES" ] && gamescope_has_option "--custom-refresh-rates"; then
		CUSTOM_REFRESH_RATES_OPTION="--custom-refresh-rates $CUSTOM_REFRESH_RATES"
	fi

	BACKEND_OPTION=""
	# Check if an older GPU that needs the SDL workaround is in use.
	if [ -n "$BACKEND" ] && gamescope_has_option "--backend"; then
		BACKEND_OPTION="--backend $BACKEND"
	fi

	DISABLE_TOUCH_CLICK_OPTION=""
	if [ -n "$DISABLE_TOUCH_CLICK" ] && gamescope_has_option "--disable-touch-click"; then
		DISABLE_TOUCH_CLICK_OPTION="--disable-touch-click"
	fi

	MURA_OPTION=""
	if [ -n "$MURA" ] && gamescope_has_option "--mura-map"; then
		MURA_OPTION="--mura-map $MURA"
	fi

	TOUCH_GESTURES_OPTION=""
	if [ -n "$TOUCH_GESTURES" ] && gamescope_has_option "--touch-gestures"; then
		TOUCH_GESTURES_OPTION="--touch-gestures"
	fi

	GAMESCOPECMD="/usr/bin/gamescope \
		$CURSOR \
		$RESOLUTION \
		$INTERNAL_RESOLUTION \
		$DRM_MODE_OPTION \
		$ORIENTATION_OPTION \
		$ADAPTIVE_SYNC_OPTION \
		$FORCE_COMPOSITION_OPTION \
		$PANEL_TYPE_OPTION \
		$BYPASS_STEAM_RESOLUTION_OPTION \
		$CUSTOM_REFRESH_RATES_OPTION \
		$BACKEND_OPTION \
		$DISABLE_TOUCH_CLICK_OPTION \
		$MURA_OPTION \
		$TOUCH_GESTURES_OPTION \
		--prefer-output $OUTPUT_CONNECTOR \
		--xwayland-count $XWAYLAND_COUNT \
		--default-touch-mode $TOUCH_MODE \
		--hide-cursor-delay $HIDE_CURSOR_DELAY_MS \
		--fade-out-duration $FADE_OUT_DURATION_MS \
		--steam"
fi

# Add socket and stats read
GAMESCOPECMD+=" -R $socket -T $stats"

# Add custom vulkan adapter if specified
if [ -n "$VULKAN_ADAPTER" ]; then
	GAMESCOPECMD+=" --prefer-vk-device $VULKAN_ADAPTER"
fi

# Attempt to fallback to a desktop session if something goes wrong too many times
short_session_tracker_file="/tmp/chimeraos-short-session-tracker"
short_session_duration=60
short_session_count_before_reset=3
SECONDS=0

short_session_count=$(wc <"$short_session_tracker_file" -l)

if [[ "$short_session_count" -ge "$short_session_count_before_reset" ]]; then
	echo >&2 "gamescope-session-plus: detected broken ${CLIENT} or gamescope failure, will try to reset the session"

	short_session_recover

	# rearm
	rm "$short_session_tracker_file"
	exit 1
fi

# Log rotate the last session
if [ -f "${HOME}"/.${CLIENT}-stdout.log ]; then
	cp "${HOME}"/.${CLIENT}-stdout.log "${HOME}"/.${CLIENT}-stdout.log.old
fi
if [ -f "${HOME}"/.gamescope-stdout.log ]; then
	cp "${HOME}"/.gamescope-stdout.log "${HOME}"/.gamescope-stdout.log.old
fi
if [ -f "${HOME}"/.gamescope-cmd.log ]; then
	cp "${HOME}"/.gamescope-cmd.log "${HOME}"/.gamescope-cmd.log.old
fi

# Start gamescope compositor, log it's output and background it
echo $GAMESCOPECMD >"${HOME}"/.gamescope-cmd.log
$GAMESCOPECMD >"${HOME}"/.gamescope-stdout.log 2>&1 &
gamescope_pid="$!"

if read -r -t 15 response_x_display response_wl_display <> "$socket"; then
	export DISPLAY="$response_x_display"
	export GAMESCOPE_WAYLAND_DISPLAY="$response_wl_display"
	# We're done!
else
	echo "gamescope failed" >>"$short_session_tracker_file"
	kill -9 "$gamescope_pid"
	wait
	exit 1
	# Systemd or Session manager will have to restart session
fi

# Run session defined hook
post_gamescope_start

# Prevent default power button behavior
systemd-inhibit --what=handle-suspend-key:handle-power-key:handle-hibernate-key --who=gamescope-session --why="gamescope-session handles power button events" sleep infinity &

# Start SteamOS's power button handler daemon, this passes power button events to Steam.
if [ -f /usr/lib/hwsupport/steamos-powerbuttond ]; then
    systemctl --user start steamos-powerbuttond
elif [ -f /usr/lib/hwsupport/power-button-handler.py ]; then
    (while true; do
        /usr/bin/python3 /usr/lib/hwsupport/power-button-handler.py
    done) &
fi

# Attempt any enabled automounts
/usr/libexec/nobara-automount

# Input method support if present
if command -v /usr/bin/ibus-daemon >/dev/null; then
	/usr/bin/ibus-daemon -d -r --panel=disable --emoji-extension=disable
fi

# If we have mangoapp binary start it
if command -v mangoapp >/dev/null; then
	(while true; do
		mangoapp >"${HOME}"/.mangoapp-stdout.log 2>&1
	done) &
fi

# Start discover-overlay if discord is installed
if command -v discover-overlay > /dev/null && grep -q "com.discordapp.Discord" <<< $(/usr/bin/flatpak list --app --columns=application); then
	env DISPLAY=:0 discover-overlay 2>&1 &
fi

# If the Sunshine service is enabled, restart it
if systemctl is-enabled --user sunshine.service; then
	systemctl restart --user sunshine.service 2>&1 &
fi

# nobara
if systemctl is-enabled --user yumex-updater.service; then
	systemctl stop --user yumex-updater.service 2>&1 &
fi


# For compatibility with older user configuration overrides
if [ -n "$STEAMCMD" ]; then
	CLIENTCMD=$STEAMCMD
fi


# Start client application
$CLIENTCMD >"${HOME}/.${CLIENT}-stdout.log" 2>&1

if [[ "$SECONDS" -lt "$short_session_duration" ]]; then
	echo "${CLIENT} failed" >>"$short_session_tracker_file"
else
	rm "$short_session_tracker_file"
fi

# When the client exits, kill gamescope nicely
kill $gamescope_pid

# Start a background sleep for five seconds because we don't trust it
sleep 5 &
sleep_pid="$!"

# Catch reboot and powerof sentinels here
if [[ -e "$REBOOT_SENTINEL" ]]; then
	rm -f "$REBOOT_SENTINEL"
	# rearm short session tracker
	rm "$short_session_tracker_file"
	reboot
fi
if [[ -e "$SHUTDOWN_SENTINEL" ]]; then
	rm -f "$SHUTDOWN_SENTINEL"
	# rearm short session tracker
	rm "$short_session_tracker_file"
	poweroff
fi

# Wait for gamescope or the sleep to finish for timeout purposes
ret=0
wait -n $gamescope_pid $sleep_pid || ret=$?

# If we get a SIGTERM/etc while waiting this happens.  Proceed to kill -9 everything but complain
if [[ $ret = 127 ]]; then
	echo >&2 "gamescope-session-plus: Interrupted while waiting on teardown, force-killing remaining tasks"
fi

# Kill all remaining jobs and warn if unexpected things are in there (should be just sleep_pid, unless gamescope failed
# to exit in time or we hit the interrupt case above)
for job in $(jobs -p); do
	# Warn about unexpected things
	if [[ $ret != 127 && $job = "$gamescope_pid" ]]; then
		echo >&2 "gamescope-session-plus: gamescope timed out while exiting, killing"
	elif [[ $ret != 127 && $job != "$sleep_pid" ]]; then
		echo >&2 "gamescope-session-plus: unexpected background pid $job at teardown: "
		# spew some debug about it
		ps -p "$job" >&2
	fi
	kill -9 "$job"
done
