mangohud-logger-steamos/mangohud-logger.sh
28allday 551471f0b3 Auto-select the gaming GPU + cross-vendor CPU-power tooling
MangoHud logs one GPU. On a hybrid box (discrete NVIDIA/AMD beside an
AMD/Intel iGPU) its default often latches onto the idle iGPU, so every
gpu_* column reads ~0 while the discrete card does the rendering. Detect
the actual gaming GPU — any vendor, single- or multi-GPU — and pin it via
gpu_list. Selection: single GPU -> that one; discrete NVIDIA preferred;
otherwise the card with the most VRAM. Override with MANGOHUD_LOGGER_GPU.
Verified against MangoHud's own enumeration (gpu_list=0 -> renderD128,
RTX 5060 Ti) and 7 synthetic layouts incl. NVIDIA as the second node.

Also make CPU telemetry work across AMD and Intel. cpu_power was logging
0 because the RAPL energy counters are root-only since CVE-2020-8694
(same intel-rapl powercap path on both vendors). Add:
  - doctor: read-only GPU/CPU telemetry readiness report
  - fix-cpu-power: install a udev rule making RAPL counters user-readable
  - enable-time warnings pointing at the fix
CPU temp already works via hwmon (k10temp/zenpower/coretemp).

Default no-arg behavior (toggle on/off) is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 19:31:03 +01:00

534 lines
22 KiB
Bash
Executable file

#!/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).
# → pins gpu_list to the GPU actually rendering games (see below), so the
# gpu_* columns log real data instead of an idle second GPU.
# 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.
#
# GPU selection (vendor-agnostic):
# MangoHud logs stats for ONE GPU. On a hybrid box (an AMD/Intel iGPU beside a
# discrete NVIDIA/AMD card) its default often picks the idle iGPU, so every
# gpu_* column reads ~0. We auto-detect the render node that's the actual
# gaming GPU — any vendor — and pin `gpu_list` to its index. Override with
# MANGOHUD_LOGGER_GPU=<index> or =nvidia|amd|intel if the guess is wrong.
#
# 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=<tmp>/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' "$*"; }
# PCI vendor IDs (as reported by /sys/class/drm/*/device/vendor).
readonly VENDOR_NVIDIA=0x10de
readonly VENDOR_AMD=0x1002
readonly VENDOR_INTEL=0x8086
# select_gpu — figure out which GPU MangoHud should log, vendor-agnostically.
#
# MangoHud enumerates DRM render nodes (renderD128, renderD129, …) in ascending
# order and `gpu_list=<n>` selects the n-th of them (verified: gpu_list=0 maps to
# renderD128). We pick the node that's the real gaming GPU and return its index.
#
# Selection order:
# 1. MANGOHUD_LOGGER_GPU override — a numeric index, or nvidia|amd|intel.
# 2. Single GPU → index 0 (covers every single-card machine, any vendor).
# 3. Multi-GPU → prefer a discrete NVIDIA; else the card with the most VRAM
# (discrete AMD/Intel report mem_info_vram_total; iGPUs report far less/none).
#
# Sets globals: GPU_INDEX (may be ""), GPU_VENDOR, GPU_DESC.
select_gpu() {
GPU_INDEX=""; GPU_VENDOR=""; GPU_DESC=""
local nodes=()
mapfile -t nodes < <(
for n in /sys/class/drm/renderD*; do
[[ -e "$n/device/vendor" ]] && basename "$n"
done | sort # ascending == MangoHud's gpu_list index order
)
local count=${#nodes[@]}
if (( count == 0 )); then
GPU_DESC="no DRM render node found (software renderer?) — gpu_list left unset"
return
fi
# Index-aligned vendor + VRAM for each node.
local vendors=() vrams=() i
for i in "${!nodes[@]}"; do
vendors[i]=$(cat "/sys/class/drm/${nodes[i]}/device/vendor" 2>/dev/null || echo unknown)
vrams[i]=$(cat "/sys/class/drm/${nodes[i]}/device/mem_info_vram_total" 2>/dev/null || echo 0)
done
if [[ -n "${MANGOHUD_LOGGER_GPU:-}" ]]; then
local ov="${MANGOHUD_LOGGER_GPU,,}"
if [[ "$ov" =~ ^[0-9]+$ ]]; then
GPU_INDEX="$ov"
else
local want=""
case "$ov" in
nvidia) want=$VENDOR_NVIDIA ;;
amd) want=$VENDOR_AMD ;;
intel) want=$VENDOR_INTEL ;;
esac
for i in "${!nodes[@]}"; do
[[ "${vendors[i]}" == "$want" ]] && { GPU_INDEX="$i"; break; }
done
[[ -z "$GPU_INDEX" ]] && GPU_INDEX=0 # requested vendor absent → first GPU
fi
elif (( count == 1 )); then
GPU_INDEX=0
else
# Prefer discrete NVIDIA (NVIDIA never populates mem_info_vram_total via DRM
# sysfs, so the VRAM check below can't see it — hence this explicit pass).
for i in "${!nodes[@]}"; do
[[ "${vendors[i]}" == "$VENDOR_NVIDIA" ]] && { GPU_INDEX="$i"; break; }
done
if [[ -z "$GPU_INDEX" ]]; then
local best=0 bestvram=-1
for i in "${!nodes[@]}"; do
if (( ${vrams[i]:-0} > bestvram )); then bestvram=${vrams[i]:-0}; best=$i; fi
done
GPU_INDEX="$best"
fi
fi
# Describe the choice (guard against an out-of-range manual override).
local node="${nodes[$GPU_INDEX]:-?}" vsel="${vendors[$GPU_INDEX]:-unknown}" name
case "$vsel" in
"$VENDOR_NVIDIA") name=NVIDIA ;;
"$VENDOR_AMD") name=AMD ;;
"$VENDOR_INTEL") name=Intel ;;
*) name="vendor $vsel" ;;
esac
GPU_VENDOR="$vsel"
GPU_DESC="gpu_list=$GPU_INDEX$node ($name), $count GPU(s) detected"
}
# libnvidia-ml (NVML) is what lets MangoHud read NVIDIA load/power/clocks.
have_nvml() {
ldconfig -p 2>/dev/null | grep -q 'libnvidia-ml' \
|| [[ -e /usr/lib/libnvidia-ml.so.1 || -e /usr/lib64/libnvidia-ml.so.1 ]]
}
# ── CPU telemetry (vendor-neutral: works on AMD *and* Intel) ────────────────
#
# MangoHud reads CPU temperature from hwmon and CPU package power from the RAPL
# powercap energy counters. Both paths are the same regardless of CPU vendor:
# • temp → hwmon driver: k10temp/zenpower (AMD), coretemp (Intel)
# • power → /sys/class/powercap/intel-rapl:* — despite the "intel" name this is
# the powercap *framework*; on AMD it's fed by intel_rapl_msr from the Zen
# RAPL MSRs. Since CVE-2020-8694 the energy_uj files are root-only (0400),
# so user-mode MangoHud can't read them and cpu_power logs as 0.
readonly RAPL_UDEV_FILE=/etc/udev/rules.d/60-mangohud-logger-rapl.rules
# cpu_vendor → AMD | Intel | <raw string>
cpu_vendor() {
local v; v=$(awk -F': ' '/^vendor_id/{print $2; exit}' /proc/cpuinfo 2>/dev/null)
case "$v" in
AuthenticAMD) echo AMD ;;
GenuineIntel) echo Intel ;;
*) echo "${v:-unknown}" ;;
esac
}
# First RAPL package energy counter (enough to gate on), else empty.
rapl_energy_file() { compgen -G '/sys/class/powercap/*rapl*/energy_uj' 2>/dev/null | head -1; }
# rapl_state → readable | denied | absent
rapl_state() {
local f; f=$(rapl_energy_file)
[[ -z "$f" ]] && { echo absent; return; }
if cat "$f" >/dev/null 2>&1; then echo readable; else echo denied; fi
}
# cpu_temp_source → hwmon 'name' providing CPU temp (any vendor), else empty.
cpu_temp_source() {
local h n
for h in /sys/class/hwmon/hwmon*; do
n=$(cat "$h/name" 2>/dev/null) || continue
case "$n" in
k10temp|zenpower|zenpower3|coretemp|k8temp) echo "$n"; return ;;
esac
done
echo ""
}
# do_fix_cpu_power — install a udev rule making RAPL energy counters readable so
# MangoHud can log cpu_power. Vendor-neutral (same RAPL path on AMD and Intel).
do_fix_cpu_power() {
local vend; vend=$(cpu_vendor)
info "CPU power ($vend) comes from RAPL energy counters, root-only since CVE-2020-8694."
info "This installs a udev rule so they're user-readable and MangoHud can log cpu_power."
warn "Security note: re-exposes the low-severity PLATYPUS power side-channel."
warn "Fine for a personal gaming/benchmark box; skip on shared/multi-user machines."
echo
case "$(rapl_state)" in
absent)
err "no RAPL powercap interface found."
info "try: sudo modprobe intel_rapl_msr (then re-run)"
return 1 ;;
readable)
ok "RAPL energy counters are already user-readable — nothing to do."
return 0 ;;
esac
local chmod_bin; chmod_bin=$(command -v chmod || echo /usr/bin/chmod)
# KERNEL match covers the package (intel-rapl:0) and its subzones (intel-rapl:0:0).
# %S%p resolves to the node's /sys path; we chmod its energy_uj attribute.
local rule="SUBSYSTEM==\"powercap\", KERNEL==\"intel-rapl:*\", RUN+=\"$chmod_bin 0444 %S%p/energy_uj\""
info "writing $RAPL_UDEV_FILE (sudo)…"
if ! printf '%s\n' \
"# Installed by mangohud-logger: make RAPL energy_uj user-readable for CPU-power logging." \
"# Remove this file to revert. See: mangohud-logger help" \
"$rule" | sudo tee "$RAPL_UDEV_FILE" >/dev/null; then
err "failed to write $RAPL_UDEV_FILE"; return 1
fi
sudo udevadm control --reload-rules 2>/dev/null || true
sudo udevadm trigger --subsystem-match=powercap 2>/dev/null || true
# Apply to already-present nodes now (udev 'add' won't re-fire for existing ones).
sudo "$chmod_bin" -f 0444 /sys/class/powercap/*rapl*/energy_uj 2>/dev/null || true
if [[ "$(rapl_state)" == readable ]]; then
ok "done — cpu_power will now be logged (persists across reboots)."
else
warn "rule installed but counters still not readable; a reboot should apply it."
fi
}
# do_doctor — report cross-vendor telemetry readiness; makes no changes.
do_doctor() {
info "mangohud-logger doctor — telemetry readiness (read-only)"
echo
if command -v mangohud >/dev/null 2>&1; then
ok "mangohud: $(mangohud --version 2>/dev/null | head -1)"
else
err "mangohud: NOT installed"
fi
info "session flavor: $GS_FLAVOR"
echo
# GPUs — list every render node, then the one that will be logged.
info "GPUs (DRM render nodes):"
local n vend name
for n in $(compgen -G '/sys/class/drm/renderD*' 2>/dev/null | sort); do
[[ -e "$n/device/vendor" ]] || continue
vend=$(cat "$n/device/vendor" 2>/dev/null)
case "$vend" in
"$VENDOR_NVIDIA") name=NVIDIA ;; "$VENDOR_AMD") name=AMD ;;
"$VENDOR_INTEL") name=Intel ;; *) name="vendor $vend" ;;
esac
printf ' %s (%s)\n' "$(basename "$n")" "$name"
done
select_gpu
if [[ -n "$GPU_INDEX" ]]; then ok "will log: $GPU_DESC"
else warn "will log: $GPU_DESC"; fi
if [[ "$GPU_VENDOR" == "$VENDOR_NVIDIA" ]]; then
if have_nvml; then ok " NVML present → NVIDIA load/power/clocks will log"
else err " NVML missing → NVIDIA gpu_* columns will be 0 (install nvidia-utils)"; fi
fi
echo
# CPU — temp + power, vendor-neutral.
info "CPU: $(cpu_vendor)"
local ts; ts=$(cpu_temp_source)
if [[ -n "$ts" ]]; then ok " temp source: hwmon '$ts' → cpu_temp will log"
else warn " temp: no known sensor (k10temp/zenpower/coretemp) → cpu_temp may be 0"; fi
case "$(rapl_state)" in
readable) ok " power (RAPL): readable → cpu_power will log" ;;
denied) warn " power (RAPL): root-only → cpu_power = 0. Fix: $(basename "$0") fix-cpu-power" ;;
absent) warn " power (RAPL): no powercap interface → cpu_power = 0 (modprobe intel_rapl_msr)" ;;
esac
echo
if [[ -f "$CONFIG_FILE" ]] && grep -qF "$MARKER_BEGIN" "$CONFIG_FILE"; then
ok "logging is currently ENABLED (log folder: $LOG_DIR)"
else
info "logging is currently disabled — run '$(basename "$0")' to enable"
fi
}
usage() {
cat <<EOF
mangohud-logger — toggle MangoHud CSV logging, with cross-vendor telemetry setup
USAGE:
$(basename "$0") Toggle logging on/off (default action)
$(basename "$0") doctor Report GPU/CPU telemetry readiness (no changes)
$(basename "$0") fix-cpu-power Make RAPL CPU-power counters user-readable (sudo)
$(basename "$0") help Show this help
ENVIRONMENT:
MANGOHUD_LOGGER_GPU=<index|nvidia|amd|intel>
Force which GPU MangoHud logs (default: auto —
prefers the discrete gaming GPU, any vendor)
Works across AMD/Intel CPUs and NVIDIA/AMD/Intel GPUs, single- or multi-GPU.
EOF
}
# do_toggle — the original enable/disable behavior (default action).
do_toggle() {
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|gpu_list)\s*=' "$CONFIG_FILE"; then
manual_log_keys=1
fi
# Work out which GPU to log (any vendor, single- or multi-GPU) before writing.
select_gpu
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'
# Pin the GPU MangoHud logs to the actual gaming GPU (see select_gpu). Omitted
# only when no render node was found, so MangoHud keeps its own default.
[[ -n "$GPU_INDEX" ]] && printf 'gpu_list=%s\n' "$GPU_INDEX"
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=<tmp>/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"
[[ -n "$GPU_INDEX" ]] && MH_INLINE+=",gpu_list=$GPU_INDEX"
mkdir -p "$ENV_DIR"
cat > "$ENV_FILE" <<EOF
$GS_OWNED_MARKER Delete this file (or re-run the script) to remove.
# MANGOHUD=1 makes the Vulkan loader inject the MangoHud layer into every game.
MANGOHUD=1
# MANGOHUD_CONFIG (inline) OVERRIDES MANGOHUD_CONFIGFILE. SteamOS Game Mode forces
# MANGOHUD_CONFIGFILE=<tmp>/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" <<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
elif [[ "$GS_FLAVOR" == "steamos" ]]; then
# This build uses /usr/lib/steamos/gamescope-session, which hard-exports
# MANGOHUD_CONFIGFILE=<tmp>/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
# NVIDIA GPU stats come from NVML; without it every gpu_* column logs as 0.
if [[ "$GPU_VENDOR" == "$VENDOR_NVIDIA" ]] && ! have_nvml; then
warn "selected an NVIDIA GPU but libnvidia-ml (NVML) not found — GPU load/power"
warn "won't be logged. Install the NVIDIA userspace (e.g. 'nvidia-utils')."
fi
# CPU power/temp readiness (same checks on AMD and Intel).
rapl=$(rapl_state)
if [[ "$rapl" == denied ]]; then
warn "cpu_power won't log — RAPL counters are root-only. Enable it with:"
warn " $(basename "$0") fix-cpu-power"
elif [[ "$rapl" == absent ]]; then
warn "cpu_power won't log — no RAPL powercap interface (try: sudo modprobe intel_rapl_msr)."
fi
[[ -z "$(cpu_temp_source)" ]] && warn "no known CPU temp sensor found — cpu_temp may read 0."
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 "gpu logged: ${GPU_DESC:-MangoHud default}"
if [[ -n "$GPU_INDEX" ]]; then
info " (override with MANGOHUD_LOGGER_GPU=<index|nvidia|amd|intel>)"
fi
info "cpu ($( cpu_vendor )): temp=$( [[ -n "$(cpu_temp_source)" ]] && echo ok || echo none ) power(RAPL)=$rapl"
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=<tmp>/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
}
# ── Command dispatch ────────────────────────────────────────────────────────
case "${1:-toggle}" in
toggle|"") do_toggle ;;
doctor|check|--check|-c) do_doctor ;;
fix-cpu-power|fix|--fix-cpu-power) do_fix_cpu_power ;;
-h|--help|help) usage ;;
*) err "unknown command: $1"; echo; usage; exit 1 ;;
esac