#!/bin/bash set -euo pipefail LOG_FILE="$HOME/.local/logs/session.log" log() { echo "[$(date --iso-8601=seconds)] $*" | tee -a "$LOG_FILE"; } CONF_FILE="$HOME/.config/gamescope/gamescope.conf" [ -f "$CONF_FILE" ] && source "$CONF_FILE" FRAMERATE=${FRAMERATE:-60} RESOLUTION=${RESOLUTION:-1920x1080} WIDTH="${RESOLUTION%x*}" HEIGHT="${RESOLUTION#*x}" export __GL_GSYNC_ALLOWED=1 export __GL_VRR_ALLOWED=1 export __GL_SHADER_DISK_CACHE=1 export __GL_SYNC_TO_VBLANK=0 export LIBVA_DRIVER_NAME=nvidia export __GLX_VENDOR_LIBRARY_NAME=nvidia export NVD_BACKEND=direct # export __GL_THREADED_OPTIMIZATIONS=1 GAMESCOPE_BIN="$(command -v gamescope || true)" if [ -z "$GAMESCOPE_BIN" ]; then log "ERROR: gamescope not found in PATH" exit 127 fi HDR_FLAG="" if [[ "$HDR_ENABLED" == "1" || "$HDR_ENABLED" == "true" || "$HDR_ENABLED" == "yes" ]]; then HDR_FLAG="--hdr-enabled" log "HDR flag added: --hdr-enabled" else log "HDR disabled, no HDR flag added" fi # Create run directory file for startup and stats sockets # shellcheck disable=SC2030 # (broken warning) 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 # shellcheck disable=SC2031 # (broken warning) 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 # Chromium (and therefore Steam) ignore XCursor and use on the GTK config kwriteconfig6 --file gtk-3.0/settings.ini --group Settings --key gtk-cursor-theme-name steam # Workaround for Steam login issue while Steam client change propagates out of Beta touch ~/.steam/root/config/SteamAppData.vdf || true # Increase open file limit for gamescope session ulimit -n 524288 # 1048576 = 1M - passing it like that omits the 'M' suffix - xargs removes whitespace free_disk_space_megs=$(df ~/ --output=avail -B1048576 | sed -n '2 p' | xargs) minimum_free_disk_space_needed_megs=500 if [[ "$free_disk_space_megs" -lt "$minimum_free_disk_space_needed_megs" ]]; then echo >&2 "gamescope-session: not enough disk space to proceed, trying to find game to delete" find ~/.local/share/Steam/steamapps/common/ -mindepth 1 -maxdepth 1 -type d -printf "%T@ %p\0" | sort -n -z | while IFS= read -r -d $'\0' line; do timestamp=${line%% *} game_folder=${line#* } [[ -d $game_folder ]] || continue acf=$(grep -F -- "$(basename -- "$game_folder")" ~/.local/share/Steam/steamapps/*.acf | grep \"installdir\" | cut -d: -f1) [[ -e "$acf" ]] || continue echo >&2 "gamescope-session: deleting $(basename "$game_folder")" appid=$(basename "$acf" | cut -d_ -f2 | cut -d. -f1) # TODO leave a note for Steam to display UI to explain what happened, if this logic stays # intentionally leave compatdata; could be unclouded save files there rm -rf --one-file-system -- "$game_folder" "$acf" ~/.local/share/Steam/steamapps/shadercache/"$appid" free_disk_space_megs=$(df ~/ --output=avail -B1048576 | sed -n '2 p' | xargs) [[ "$free_disk_space_megs" -lt "$minimum_free_disk_space_needed_megs" ]] || break done 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 read_gamescope_env() { log "Waiting for gamescope to provide DISPLAY and GAMESCOPE_WAYLAND_DISPLAY via socket..." if read -r -t 3 response_x_display response_wl_display <> "$socket"; then log "Received gamescope environment:" log " DISPLAY=$response_x_display" log " GAMESCOPE_WAYLAND_DISPLAY=$response_wl_display" export DISPLAY="$response_x_display" export GAMESCOPE_WAYLAND_DISPLAY="$response_wl_display" # Sync environment variables to systemd, then notify we are ready to launch subsequent services log "Storing gamescope environment for systemd..." env > $XDG_RUNTIME_DIR/gamescope-environment systemd-notify --ready fi } # Spawned in parallel to read values from gamescope (read_gamescope_env &) log "Starting Gamescope session..." # Build gamescope argument array for safe quoting and readability GAMESCOPE_ARGS=( --generate-drm-mode fixed --fade-out-duration 200 --cursor-scale-height 720 -f -W "$WIDTH" -H "$HEIGHT" -w "$WIDTH" -h "$HEIGHT" --rt --mangoapp -e -R "$socket" -T "$stats" ) # Append HDR flag if enabled if [ -n "${HDR_FLAG:-}" ]; then GAMESCOPE_ARGS+=("$HDR_FLAG") fi export XDG_CURRENT_DESKTOP=gamescope export XDG_SESSION_TYPE=wayland export DESKTOP_SESSION=gamescope log "Launching Gamescope: $GAMESCOPE_BIN ${GAMESCOPE_ARGS[*]} -- $*" sudo setcap 'CAP_SYS_NICE=eip' "$GAMESCOPE_BIN" exec "$GAMESCOPE_BIN" "${GAMESCOPE_ARGS[@]}" >>"$LOG_FILE" 2>&1