2c9cff6bb9
makoctl/fw-fanctrl echo confirmations to stdout that leaked into add/del output during integration. run() now discards stdout, keeps stderr. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
128 lines
4.7 KiB
Bash
Executable File
128 lines
4.7 KiB
Bash
Executable File
#!/bin/bash
|
|
# gamemode-session — compositor-level gamemode for niri.
|
|
# Reference-counted over two sources (feral, manual). Applies effects on the
|
|
# first active source (0->1) and reverts on the last (1->0).
|
|
# Spec: docs/superpowers/specs/2026-07-15-niri-gamemode-design.md
|
|
set -u
|
|
|
|
STATE_DIR="${GAMEMODE_STATE_DIR:-${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/gamemode}"
|
|
FAN_AUTO="${XDG_STATE_HOME:-$HOME/.local/state}/fan-profile-auto"
|
|
DRYRUN="${GAMEMODE_DRYRUN:-0}"
|
|
|
|
log() { printf 'gamemode: %s\n' "$*" >&2; }
|
|
# Execute a side-effecting command (or log it under dryrun). Its stdout is
|
|
# discarded — effect tools like makoctl/fw-fanctrl print confirmations we don't
|
|
# want leaking into `add`/`del` output; stderr is kept so real errors surface.
|
|
run() { if [ "$DRYRUN" = 1 ]; then log "would: $*"; else "$@" >/dev/null; fi; }
|
|
|
|
is_active() {
|
|
[ -n "$(find "$STATE_DIR" -maxdepth 1 -name 'src.*' -print -quit 2>/dev/null)" ]
|
|
}
|
|
|
|
snapshot_baseline() {
|
|
mkdir -p "$STATE_DIR"
|
|
powerprofilesctl get 2>/dev/null > "$STATE_DIR/baseline.profile" || : > "$STATE_DIR/baseline.profile"
|
|
if pgrep -x waybar >/dev/null 2>&1; then echo 1; else echo 0; fi > "$STATE_DIR/baseline.waybar"
|
|
if [ -f "$FAN_AUTO" ]; then echo 1; else echo 0; fi > "$STATE_DIR/baseline.fanauto"
|
|
}
|
|
|
|
# --- effects ------------------------------------------------------------------
|
|
effect_idle_on() {
|
|
if [ "$DRYRUN" = 1 ]; then log "would: systemd-inhibit idle"; return; fi
|
|
command -v systemd-inhibit >/dev/null 2>&1 || { log "no systemd-inhibit"; return; }
|
|
systemd-inhibit --what=idle --who=gamemode --why="gaming session" --mode=block \
|
|
sleep infinity >/dev/null 2>&1 &
|
|
echo $! > "$STATE_DIR/idle.pid"
|
|
}
|
|
effect_idle_off() {
|
|
local pid; pid=$(cat "$STATE_DIR/idle.pid" 2>/dev/null || echo "")
|
|
[ -n "$pid" ] && run kill "$pid"
|
|
rm -f "$STATE_DIR/idle.pid"
|
|
}
|
|
|
|
effect_dnd_on() { command -v makoctl >/dev/null 2>&1 && run makoctl mode -a do-not-disturb; }
|
|
effect_dnd_off() { command -v makoctl >/dev/null 2>&1 && run makoctl mode -r do-not-disturb; }
|
|
|
|
# maps a power profile to the fw-fanctrl strategy (mirrors fan-profile.sh)
|
|
fan_for() { case "$1" in power-saver) echo lazy;; balanced) echo medium;; performance) echo agile;; *) return 1;; esac; }
|
|
|
|
effect_perf_on() {
|
|
local base; base=$(cat "$STATE_DIR/baseline.profile" 2>/dev/null || echo "")
|
|
if [ -n "$base" ] && [ "$base" != performance ]; then
|
|
run powerprofilesctl set performance
|
|
run pkill -RTMIN+8 waybar
|
|
fi
|
|
if [ "$(cat "$STATE_DIR/baseline.fanauto" 2>/dev/null)" = 1 ]; then
|
|
run fw-fanctrl use agile
|
|
fi
|
|
}
|
|
effect_perf_off() {
|
|
local base; base=$(cat "$STATE_DIR/baseline.profile" 2>/dev/null || echo "")
|
|
if [ -n "$base" ] && [ "$base" != performance ]; then
|
|
run powerprofilesctl set "$base"
|
|
run pkill -RTMIN+8 waybar
|
|
fi
|
|
if [ -n "$base" ] && [ "$(cat "$STATE_DIR/baseline.fanauto" 2>/dev/null)" = 1 ]; then
|
|
local strat; strat=$(fan_for "$base") && run fw-fanctrl use "$strat"
|
|
fi
|
|
}
|
|
|
|
effect_waybar_on() { # hide
|
|
pgrep -x waybar >/dev/null 2>&1 && run pkill -x waybar
|
|
}
|
|
effect_waybar_off() { # restore if it was running at entry
|
|
[ "$(cat "$STATE_DIR/baseline.waybar" 2>/dev/null)" = 1 ] || return 0
|
|
if [ "$DRYRUN" = 1 ]; then log "would: relaunch waybar"; else setsid waybar >/dev/null 2>&1 & fi
|
|
}
|
|
|
|
apply_effects() { effect_idle_on; effect_dnd_on; effect_perf_on; effect_waybar_on; }
|
|
revert_effects() { effect_idle_off; effect_dnd_off; effect_perf_off; effect_waybar_off; }
|
|
|
|
# --- state machine ------------------------------------------------------------
|
|
cmd_add() {
|
|
local src="$1" was=0
|
|
is_active && was=1
|
|
mkdir -p "$STATE_DIR"
|
|
touch "$STATE_DIR/src.$src"
|
|
if [ "$was" = 0 ]; then
|
|
snapshot_baseline
|
|
apply_effects
|
|
log "activated (source: $src)"
|
|
else
|
|
log "already active; +source: $src"
|
|
fi
|
|
}
|
|
|
|
cmd_del() {
|
|
local src="$1"
|
|
rm -f "$STATE_DIR/src.$src"
|
|
if ! is_active; then
|
|
revert_effects
|
|
rm -f "$STATE_DIR"/baseline.* "$STATE_DIR/idle.pid"
|
|
log "deactivated (last source: $src)"
|
|
else
|
|
log "still active; -source: $src"
|
|
fi
|
|
}
|
|
|
|
cmd_toggle() {
|
|
if [ -e "$STATE_DIR/src.manual" ]; then cmd_del manual; else cmd_add manual; fi
|
|
}
|
|
|
|
cmd_status() { if is_active; then echo active; exit 0; else echo inactive; exit 1; fi; }
|
|
|
|
cmd_reset() {
|
|
revert_effects 2>/dev/null || true
|
|
rm -rf "$STATE_DIR"
|
|
log "reset"
|
|
}
|
|
|
|
case "${1:-}" in
|
|
add) [ -n "${2:-}" ] || { log "add needs a source"; exit 2; }; cmd_add "$2" ;;
|
|
del) [ -n "${2:-}" ] || { log "del needs a source"; exit 2; }; cmd_del "$2" ;;
|
|
toggle) cmd_toggle ;;
|
|
status) cmd_status ;;
|
|
reset) cmd_reset ;;
|
|
*) log "usage: gamemode-session {add|del <source>|toggle|status|reset}"; exit 2 ;;
|
|
esac
|