Initial commit: mangohud-logger toggle script

One-touch toggle for MangoHud CSV logging on Linux gaming-launcher systems
(DeckShift, super-alt-S, gamescope-session-plus). Manages three files
idempotently: MangoHud.conf block, environment.d MANGOHUD=1, and a
gamescope-session-plus user override that fixes Steam's mangoapp/MANGOHUD_CONFIGFILE
suppression.
This commit is contained in:
Gavin Nugent 2026-05-18 14:35:26 +01:00
commit fc2f702fb2
3 changed files with 319 additions and 0 deletions

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Gavin Nugent
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

131
README.md Normal file
View file

@ -0,0 +1,131 @@
# mangohud-logger
One-touch toggle for MangoHud CSV logging on Linux gaming-launcher systems
(DeckShift, super-alt-S, plain `gamescope-session-plus`, or any setup that
already uses MangoHud).
Run the script — it switches logging on if it's off, off if it's on. CSV
samples land in `~/Downloads/mango-logs/`. That's the entire interface.
## Why this exists
Getting MangoHud to *actually* write logs from a Steam-via-gamescope game
turns out to be a three-step setup, and every step has a non-obvious
gotcha. This script handles all three so you don't have to remember them.
## What it changes
The script writes three files (all marker-tagged so it can clean them up
on disable, and so it won't trample anything you wrote by hand):
1. **`~/.config/MangoHud/MangoHud.conf`** — appends a marker-bracketed block
with `output_folder`, `autostart_log=1`, `log_duration=0` (= until you
quit), `log_interval=100`, `toggle_logging=Shift_L+F2`, and a
`no_display=0` override.
2. **`~/.config/environment.d/95-mangohud-logger.conf`** — contains
`MANGOHUD=1` so the Vulkan loader auto-injects MangoHud into every game.
Requires a logout/login to take effect (systemd-user only reads
`environment.d/*.conf` at session start).
3. **`~/.config/gamescope-session-plus/sessions.d/steam`** — user-level
override that unsets the system's `MANGOHUD_CONFIGFILE` and turns off
`STEAM_USE_MANGOAPP`. See "Gotchas" below for why.
When you toggle it off, the script removes all three.
## Gotchas it works around
- **`no_display` blocks `autostart_log`.** If your MangoHud config has
`no_display` (HUD hidden by default), MangoHud's `autostart_log` rides on
the render hook that `no_display` disables — so the log never starts. The
script appends `no_display=0` at the end of its block to override.
Tradeoff: while logging is on, the HUD becomes visible during games.
- **DeckShift / gamescope-session-plus suppresses your user MangoHud config.**
The system `gamescope-session-plus` script does
`export MANGOHUD_CONFIGFILE=$(mktemp /tmp/mangohud.XXXXXXXX)` and writes
only `no_display` into that file. Result: MangoHud ignores your real
`~/.config/MangoHud/MangoHud.conf` and your `output_folder` / `autostart_log`
keys never run. The user-level session override unsets that var so
MangoHud falls back to the user file.
- **Steam uses `mangoapp` instead of the in-game MangoHud Vulkan layer.**
The system steam session exports `STEAM_USE_MANGOAPP=1`, which makes
gamescope draw the overlay via a separate `mangoapp` process — and
`mangoapp` doesn't write CSV logs the same way (and segfaults in a loop
on some boxes). The session override forces `STEAM_USE_MANGOAPP=0` so
the regular Vulkan-layer MangoHud takes over.
- **`log_duration=0` means "log forever".** The MangoHud docs aren't crystal
clear; empirically `0` = log until you quit or hit the toggle key.
- **`MANGOHUD=1` only injects the Vulkan layer.** OpenGL-only games (rare
these days) still need `mangohud %command%` in Steam launch options or
an `LD_PRELOAD` wrapper.
## Install
Requires `mangohud` (and `lib32-mangohud` for 32-bit games). On Arch:
```bash
sudo pacman -S mangohud lib32-mangohud
```
Then:
```bash
git clone https://git.no-signal.uk/nosignal/mangohud-logger.git
cd mangohud-logger
chmod +x mangohud-logger.sh
./mangohud-logger.sh
```
Or copy the script anywhere on `$PATH`:
```bash
install -Dm755 mangohud-logger.sh ~/.local/bin/mangohud-logger
mangohud-logger
```
## Usage
```bash
./mangohud-logger.sh
```
That's it — no flags, no subcommands. First run enables; second run disables.
After the first enable, **log out of your desktop session and back in** so
the `MANGOHUD=1` env var becomes effective. You only need to do that once.
In-game keys (set by the script's MangoHud block):
| Key | Action |
|---|---|
| `Shift+F2` | Toggle logging on/off mid-game (useful for benchmarking a specific section) |
| Your existing `toggle_hud` key | Hide the HUD overlay (logging keeps running) |
CSV files appear in `~/Downloads/mango-logs/` as `<game>_<timestamp>.csv`.
Drop them into <https://flightlessmango.com/> for graphs, or open in a
spreadsheet.
## Compatibility
- **DeckShift** ([28allday/DeckShift](https://github.com/28allday/DeckShift))
— gamescope-session-plus based, Omarchy-only.
- **super-alt-S** ([28allday/super-alt-S-cachy-deckmode](https://github.com/28allday/super-alt-S-cachy-deckmode))
— KDE-Plasma gaming mode on CachyOS.
- **Plain `gamescope-session-plus`** on any Arch-based distro.
- **Steam without gamescope** — works too; only piece 3 (the gamescope
override) becomes a no-op.
- **Non-systemd distros** — piece 2 (the `environment.d` file) won't be
read; set `MANGOHUD=1` via your distro's preferred env-var mechanism.
## Honours XDG
Log folder is `$XDG_DOWNLOAD_DIR/mango-logs` (resolved via `xdg-user-dir`),
config dir is `$XDG_CONFIG_HOME` — works on non-English locales.
## License
MIT

167
mangohud-logger.sh Executable file
View file

@ -0,0 +1,167 @@
#!/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."
# 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"
mkdir -p "$ENV_DIR"
cat > "$ENV_FILE" <<EOF
$GS_OWNED_MARKER Delete this file (or re-run the script) to remove.
# Makes the Vulkan loader inject MangoHud into every game so autostart_log fires.
MANGOHUD=1
EOF
# gamescope-session-plus override — only write if file is absent or already ours,
# so we never trample a user-written override.
gs_action=""
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" <<EOF
$GS_OWNED_MARKER
# Sourced AFTER /usr/share/gamescope-session-plus/sessions.d/steam (last wins).
#
# Without this:
# 1. The system steam session script exports MANGOHUD_CONFIGFILE pointing to a
# temp file containing only "no_display", which suppresses our user
# ~/.config/MangoHud/MangoHud.conf and so autostart_log/output_folder
# never take effect. Unsetting the var falls back to the user file.
# 2. STEAM_USE_MANGOAPP=1 makes Steam ask gamescope to use the mangoapp
# compositor overlay instead of the in-game MangoHud Vulkan layer.
# The Vulkan layer is the one that writes CSV logs.
unset MANGOHUD_CONFIGFILE
export STEAM_USE_MANGOAPP=0
export STEAM_MANGOAPP_PRESETS_SUPPORTED=0
EOF
gs_action="written"
else
gs_action="left alone (you have a custom override at $GS_SESSION_FILE)"
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 "gamescope override: $GS_SESSION_FILE ($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)"
warn "log out and back in (or restart your gamescope session) so the env var takes effect."
fi