#!/usr/bin/env bash # # mangohud-logger — toggle MangoHud CSV logging on/off # # Just run it: if logging is off it gets switched on, if it's already on # it gets switched off. # # What it manages on enable: # 1. ~/.config/MangoHud/MangoHud.conf # → appends a marker block with output_folder, autostart_log, etc. # → keeps your existing keys; overrides `no_display` while logging # (autostart_log rides on the render hook that no_display disables). # 2. ~/.config/environment.d/95-mangohud-logger.conf # → contains MANGOHUD=1 so the Vulkan layer auto-loads into every # game (gamescope sessions, Steam, native Vulkan apps). # Picked up by systemd-user at next session login. # # Logs land in the user's Downloads folder under "mango-logs" # (honours XDG_DOWNLOAD_DIR from ~/.config/user-dirs.dirs, else ~/Downloads). set -euo pipefail readonly MARKER_BEGIN="# >>> mangohud-logger BEGIN >>>" readonly MARKER_END="# <<< mangohud-logger END <<<" CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/MangoHud" CONFIG_FILE="$CONFIG_DIR/MangoHud.conf" ENV_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/environment.d" ENV_FILE="$ENV_DIR/95-mangohud-logger.conf" GS_SESSION_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/gamescope-session-plus/sessions.d" GS_SESSION_FILE="$GS_SESSION_DIR/steam" GS_OWNED_MARKER="# Written by mangohud-logger." # Detect which gamescope (Game Mode) session manager is in use — this decides # whether our env override is actually sourced at session start: # plus → gamescope-session-plus sources ~/.config/gamescope-session-plus/ # sessions.d/* after its own steam script, so our override works. # steamos → /usr/lib/steamos/gamescope-session hard-exports # STEAM_USE_MANGOAPP=1 and MANGOHUD_CONFIGFILE=/no_display, and # sources NO user file. A global override is therefore impossible; # logging requires per-game Steam launch options instead. # none → no gamescope session found (desktop-only machine). detect_gs_flavor() { if [[ -e /usr/share/gamescope-session-plus/sessions.d/steam ]]; then printf 'plus\n' elif [[ -e /usr/lib/steamos/gamescope-session ]]; then printf 'steamos\n' else printf 'none\n' fi } GS_FLAVOR="$(detect_gs_flavor)" # The per-game launch-option string that makes logging work on the steamos # flavor (repoints MANGOHUD_CONFIGFILE back at our config so autostart_log fires). LAUNCH_OPT="MANGOHUD_CONFIGFILE=$CONFIG_FILE mangohud %command%" # Resolve Downloads via xdg-user-dir if available, else the user-dirs.dirs file, # else fall back to ~/Downloads. This makes the script work on non-English locales. if command -v xdg-user-dir >/dev/null 2>&1; then DOWNLOADS_DIR="$(xdg-user-dir DOWNLOAD)" elif [[ -f "${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs" ]]; then # shellcheck disable=SC1090,SC1091 source "${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs" DOWNLOADS_DIR="${XDG_DOWNLOAD_DIR:-$HOME/Downloads}" else DOWNLOADS_DIR="$HOME/Downloads" fi LOG_DIR="$DOWNLOADS_DIR/mango-logs" err() { printf '\033[31merror:\033[0m %s\n' "$*" >&2; } info() { printf '\033[36m::\033[0m %s\n' "$*"; } ok() { printf '\033[32m✓\033[0m %s\n' "$*"; } warn() { printf '\033[33m!\033[0m %s\n' "$*"; } if ! command -v mangohud >/dev/null 2>&1; then err "mangohud is not installed — install it first (e.g. 'sudo pacman -S mangohud lib32-mangohud')." exit 1 fi mkdir -p "$CONFIG_DIR" [[ -f "$CONFIG_FILE" ]] || : > "$CONFIG_FILE" if grep -qF "$MARKER_BEGIN" "$CONFIG_FILE"; then # Currently enabled → strip the block and remove the env file. tmp=$(mktemp) awk -v b="$MARKER_BEGIN" -v e="$MARKER_END" ' $0 == b { skip = 1; next } $0 == e { skip = 0; next } !skip { print } ' "$CONFIG_FILE" > "$tmp" # Trim trailing blank lines the block may have left behind. sed -i -e :a -e '/^\s*$/{$d;N;ba' -e '}' "$tmp" mv "$tmp" "$CONFIG_FILE" env_removed=0 if [[ -f "$ENV_FILE" ]]; then rm -f "$ENV_FILE" env_removed=1 fi # Only remove the gamescope session override if WE wrote it. gs_removed=0 if [[ -f "$GS_SESSION_FILE" ]] && head -1 "$GS_SESSION_FILE" | grep -qF "$GS_OWNED_MARKER"; then rm -f "$GS_SESSION_FILE" gs_removed=1 fi ok "MangoHud logging disabled" info "existing logs kept in: $LOG_DIR" if (( env_removed )); then info "removed: $ENV_FILE" info "MANGOHUD env var stays set in the *current* session until logout" fi if (( gs_removed )); then info "removed: $GS_SESSION_FILE" fi else # Currently disabled → append the config block + write the env file. # Check for pre-existing manual log keys *before* we write, so the warning is accurate. manual_log_keys=0 if grep -qE '^\s*(output_folder|autostart_log|log_duration|log_interval|toggle_logging)\s*=' "$CONFIG_FILE"; then manual_log_keys=1 fi mkdir -p "$LOG_DIR" { [[ -s "$CONFIG_FILE" ]] && printf '\n' printf '%s\n' "$MARKER_BEGIN" printf '# Added by mangohud-logger — remove this block to disable logging.\n' printf 'output_folder=%s\n' "$LOG_DIR" printf 'autostart_log=1\n' printf 'log_duration=0\n' printf 'log_interval=100\n' printf 'toggle_logging=Shift_L+F2\n' # Override any earlier `no_display` — MangoHud's autostart_log rides on the # render hook, which `no_display` disables. Without this, logs never start. printf 'no_display=0\n' printf '%s\n' "$MARKER_END" } >> "$CONFIG_FILE" # Inline config string mirroring the MangoHud.conf block. MANGOHUD_CONFIG takes # precedence over MANGOHUD_CONFIGFILE, so this is what makes logging work GLOBALLY # in SteamOS Game Mode — that session forces MANGOHUD_CONFIGFILE=/no_display, # but it never sets MANGOHUD_CONFIG, so ours (from environment.d) wins for every game. # NOTE: relies on output_folder having no commas/spaces (true for the default path). MH_INLINE="output_folder=$LOG_DIR,autostart_log=1,log_duration=0,log_interval=100,toggle_logging=Shift_L+F2,no_display=0" mkdir -p "$ENV_DIR" cat > "$ENV_FILE" </no_display, which would otherwise suppress our config and # stop logging. This inline config wins over it, so autostart_log/output_folder take # effect for every game with NO per-game launch options. Verified: MANGOHUD_CONFIG # beats a no_display configfile (vkcube CSV log produced). MANGOHUD_CONFIG=$MH_INLINE EOF # Game Mode handling depends on which session manager is installed (see # detect_gs_flavor). Only gamescope-session-plus sources our override file; # the steamos flavor needs per-game launch options instead. gs_action="" if [[ "$GS_FLAVOR" == "plus" ]]; then # gamescope-session-plus override — only write if file is absent or already # ours, so we never trample a user-written override. if [[ ! -f "$GS_SESSION_FILE" ]] || head -1 "$GS_SESSION_FILE" | grep -qF "$GS_OWNED_MARKER"; then mkdir -p "$GS_SESSION_DIR" cat > "$GS_SESSION_FILE" </no_display and STEAM_USE_MANGOAPP=1 with no user # hook — so a global override is impossible. Remove any stale override we # wrote on a previous (plus-flavor) run so it doesn't mislead. if [[ -f "$GS_SESSION_FILE" ]] && head -1 "$GS_SESSION_FILE" | grep -qF "$GS_OWNED_MARKER"; then rm -f "$GS_SESSION_FILE" fi gs_action="n/a — using MANGOHUD_CONFIG (global, no override file needed)" else gs_action="no gamescope session detected (desktop only)" fi if (( manual_log_keys )); then warn "pre-existing log-related keys found in config — they may override ours." fi ok "MangoHud logging enabled" info "log folder: $LOG_DIR" info "toggle key: Shift+F2 (during a game)" info "config file: $CONFIG_FILE" info "env file: $ENV_FILE (MANGOHUD=1)" info "session type: $GS_FLAVOR (gamescope override: $gs_action)" info "note: while logging is on, the HUD becomes visible during games" info " (autostart_log needs the render hook that no_display disables)" if [[ "$GS_FLAVOR" == "steamos" ]]; then # SteamOS Game Mode forces MANGOHUD_CONFIGFILE=/no_display, but our # MANGOHUD_CONFIG (set above in environment.d) overrides it for every game — # so logging IS global here, no per-game launch options needed. info "→ logging is GLOBAL via MANGOHUD_CONFIG — no per-game launch options needed." warn "log out and back into Game Mode (or reboot) so environment.d takes effect." info "while logging, the in-game MangoHud overlay will be visible (autostart_log" info "needs the render hook that no_display disables) — that's expected." info "fallback for a single game (e.g. if you don't want to restart the session):" printf ' \033[1m%s\033[0m\n' "$LAUNCH_OPT" else warn "log out and back in (or restart your gamescope session) so the env var takes effect." fi fi