fix(i3): repair the dead idle-inhibitor autostart and make it a singleton
Some checks are pending
Pre-commit checks / pre-commit (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run

The i3 `exec` line pointed at
/home/kuchy/linux-configuration/scripts/utils/turn_off_auto_idle_screen_shutdown.sh
- a typo'd home ("kuchy") *and* a path that stopped existing when the repo
was reorganised in db6276b. The exec has been failing silently at every
login ever since, so the idle inhibitor was simply not running: a controller
was connected with no "idle-off" lock held, meaning the screen could blank
and lock during controller gaming, which is exactly what the line exists to
prevent.

Point it at the script's real location.

Also make --watch-controller a singleton. It is a long-lived watcher started
from an i3 `exec`, and i3 re-runs those on every `i3-msg restart`, so each
restart would leave another watcher holding a redundant inhibitor for the
rest of the session. One flock, held for the life of the watcher.

Verified: the watcher now holds
  idle-off 1000 kuhy <pid> systemd-inhibit idle "game controller connected"
and a second invocation declines instead of stacking a second inhibitor.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Krzysztof Rudnicki 2026-07-25 19:57:57 +02:00
parent 5ab9a806eb
commit d91c0ab0e9
3 changed files with 53 additions and 1 deletions

View File

@ -0,0 +1,44 @@
{
"intent": "Restore the idle-inhibitor autostart that has been silently failing at every i3 login, so the screen stops blanking/locking while a game controller is connected, and stop i3 restarts from stacking duplicate watchers.",
"scope": [
"linux_configuration/scripts/periodic_background/i3-configuration/i3/config",
"linux_configuration/scripts/single_use/utils/turn_off_auto_idle_screen_shutdown.sh",
"Non-goal: no change to what the watcher does once running (the fork-free systemd-inhibit rewrite in fcd3f7e stands)",
"Non-goal: no i3 reload performed - a game was running on DP-0, so the live session was fixed by launching the watcher directly instead"
],
"changes": [
"i3 config: the exec pointed at /home/kuchy/linux-configuration/scripts/utils/turn_off_auto_idle_screen_shutdown.sh - a typo'd home ('kuchy', which does not exist) and a path that stopped existing when the repo was reorganised in db6276b. Repointed at the script's real location under linux_configuration/scripts/single_use/utils/.",
"turn_off_auto_idle_screen_shutdown.sh: --watch-controller now takes an flock on $XDG_RUNTIME_DIR/idle-off-watch.lock for the life of the watcher and declines to start if one is already running. i3 re-runs its exec lines on every `i3-msg restart`, so without this each restart left another long-lived watcher holding a redundant inhibitor."
],
"verification": [
{
"command": "systemd-inhibit --list | grep idle-off (before the fix)",
"result": "fail",
"evidence": "No idle-off inhibitor held, while /dev/input/js0 was present - i.e. the screen could blank during controller gaming. Only rtkit/upower/xss-lock inhibitors were listed."
},
{
"command": "bash turn_off_auto_idle_screen_shutdown.sh --watch-controller (after the fix)",
"result": "pass",
"evidence": "[idle-off] Holding idle inhibitor (pid 941016) while a controller is connected; systemd-inhibit --list now shows: idle-off 1000 kuhy 941016 systemd-inhibit idle 'game controller connected' block"
},
{
"command": "second invocation of the same script (singleton check)",
"result": "pass",
"evidence": "[idle-off] Controller watcher already running; not starting a second one. Exit 0, and `systemd-inhibit --list | grep -c idle-off` stays at 1."
},
{
"command": "pre-commit run --files linux_configuration/scripts/single_use/utils/turn_off_auto_idle_screen_shutdown.sh",
"result": "pass",
"evidence": "All hooks Passed or Skipped, including shellcheck and 'Block polling script anti-patterns'."
}
],
"risks": [
"The lock lives in $XDG_RUNTIME_DIR, which is cleared on logout - correct here, since the watcher is per-session anyway. If XDG_RUNTIME_DIR is unset the lock falls back to /tmp, where a stale lock file is harmless because flock releases on process exit.",
"The i3 config change only takes effect on the next i3 restart/login. The running session was fixed by starting the watcher directly, so there is no gap; a future `i3-msg restart` will pick up the corrected path and the singleton guard will prevent a duplicate.",
"The deployed ~/.config/i3/config is a separate copy, not a symlink to this tracked file. Both were updated; if only one had been, the next deploy would have reintroduced the broken path."
],
"rollback": [
"git revert the commit, then restore the deployed copy: the pre-change ~/.config/i3/config is backed up in the session scratchpad as i3-config.bak.",
"After rollback, confirm with `systemd-inhibit --list | grep idle-off` that the inhibitor state matches expectations, and kill any running turn_off_auto_idle_screen_shutdown.sh watcher."
]
}

View File

@ -35,7 +35,7 @@ exec --no-startup-id xss-lock --transfer-sleep-lock -- i3lock --nofork
exec --no-startup-id nm-applet
# Keep screen awake and unlocked; also treat controller input as activity
exec --no-startup-id bash /home/kuchy/linux-configuration/scripts/utils/turn_off_auto_idle_screen_shutdown.sh --watch-controller
exec --no-startup-id bash /home/kuhy/testsAndMisc/linux_configuration/scripts/single_use/utils/turn_off_auto_idle_screen_shutdown.sh --watch-controller
# Use pactl to adjust volume in PulseAudio.
set $refresh_i3status killall -SIGUSR1 i3status

View File

@ -270,6 +270,14 @@ main() {
persist_with_systemd_logind
if [[ $watch_controller == true ]]; then
# Singleton. i3 re-execs its `exec` lines on `i3-msg restart`, and a second
# long-lived watcher would sit there holding a redundant inhibitor for the
# rest of the session. One lock, taken for as long as the watcher lives.
exec 8> "${XDG_RUNTIME_DIR:-/tmp}/idle-off-watch.lock"
if ! flock -n 8; then
log "Controller watcher already running; not starting a second one."
exit 0
fi
log "Controller activity watcher enabled (idle-inhibitor mode)"
# Blocks until terminated; releases the inhibitor on exit via its own trap.
start_controller_watchers