diff --git a/scripts/gamemode-session.sh b/scripts/gamemode-session.sh new file mode 100755 index 0000000..0430689 --- /dev/null +++ b/scripts/gamemode-session.sh @@ -0,0 +1,85 @@ +#!/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; } +run() { if [ "$DRYRUN" = 1 ]; then log "would: $*"; else "$@"; 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 (bodies filled in Task 2; dryrun-safe stubs for now) ------------- +effect_idle_on() { run : "idle inhibit on"; } +effect_idle_off() { run : "idle inhibit off"; } +effect_dnd_on() { run : "dnd on"; } +effect_dnd_off() { run : "dnd off"; } +effect_perf_on() { run : "perf on"; } +effect_perf_off() { run : "perf off"; } +effect_waybar_on() { run : "waybar hide"; } +effect_waybar_off(){ run : "waybar restore"; } + +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 |toggle|status|reset}"; exit 2 ;; +esac diff --git a/scripts/tests/gamemode-session.test.sh b/scripts/tests/gamemode-session.test.sh new file mode 100755 index 0000000..a28f408 --- /dev/null +++ b/scripts/tests/gamemode-session.test.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# Drives gamemode-session in dryrun against a temp state dir and asserts the +# reference-counting state machine. No live effects run. +set -u +HERE=$(cd "$(dirname "$0")/.." && pwd) +SCRIPT="$HERE/gamemode-session.sh" +TMP=$(mktemp -d) +trap 'rm -rf "$TMP"' EXIT +export GAMEMODE_STATE_DIR="$TMP/state" +export GAMEMODE_DRYRUN=1 + +fail=0 +check() { # desc, actual, expected + if [ "$2" = "$3" ]; then printf 'ok - %s\n' "$1" + else printf 'FAIL - %s (got [%s] want [%s])\n' "$1" "$2" "$3"; fail=1; fi +} +active() { "$SCRIPT" status >/dev/null 2>&1 && echo active || echo inactive; } + +check "starts inactive" "$(active)" "inactive" + +"$SCRIPT" add feral >/dev/null 2>&1 +check "feral activates" "$(active)" "active" +check "src.feral marker set" "$([ -e "$GAMEMODE_STATE_DIR/src.feral" ] && echo y)" "y" +check "baseline captured" "$([ -e "$GAMEMODE_STATE_DIR/baseline.profile" ] && echo y)" "y" + +"$SCRIPT" add manual >/dev/null 2>&1 +check "manual keeps active" "$(active)" "active" + +"$SCRIPT" del feral >/dev/null 2>&1 +check "still active on 1 left" "$(active)" "active" + +"$SCRIPT" del manual >/dev/null 2>&1 +check "last del deactivates" "$(active)" "inactive" +check "baseline cleared" "$([ -e "$GAMEMODE_STATE_DIR/baseline.profile" ] && echo y || echo n)" "n" + +"$SCRIPT" toggle >/dev/null 2>&1 +check "toggle on activates" "$(active)" "active" +"$SCRIPT" toggle >/dev/null 2>&1 +check "toggle off deactivates" "$(active)" "inactive" + +"$SCRIPT" reset >/dev/null 2>&1 +check "reset leaves inactive" "$(active)" "inactive" + +exit $fail