1
0
Fork 0

sway: extract start-swayidle.sh as single source of truth (fix caffeine)

caffeine.sh click was silently breaking idle lock. Two bugs that
compounded:

1. caffeine.sh inlined its own swayidle command (with swaylock -f -i
   , not lock-fancy.sh), drifted out of sync with sway/config
   which used lock-fancy.sh. Toggle off would restart swayidle with a
   different lock command than the rest of the session.

2. WAYLAND_DISPLAY isn't set in waybar's on-click context. When
   caffeine.sh called 'swaymsg output * power off' (part of the
   restart command) and later called swaylock / grim (via lock-fancy),
   those failed silently. swayidle itself can run without
   WAYLAND_DISPLAY but it can't actually monitor input/output, so it
   exits immediately. Result: user clicks caffeine off, flag clears,
   icon goes back to 'inactive', but auto-lock is silently dead.
   The user thinks they turned caffeine off. They didn't. They're
   just unprotected.

Fix:
- new start-swayidle.sh holds the canonical swayidle command
- sway/config: exec $HOME/.config/sway/start-swayidle.sh
- caffeine.sh: killall swayidle; start-swayidle.sh &
- start-swayidle.sh probes /proc/*/environ for a Wayland client's
  env (mako always has it) and exports WAYLAND_DISPLAY /
  DBUS_SESSION_BUS_ADDRESS / XDG_RUNTIME_DIR / DISPLAY if missing
  before exec'ing swayidle

Single source of truth + env restoration = click works on every box
that has the same Wayland-capable process tree (mako, swaybar, etc.),
no matter who/what is calling the script.

Verified on tadbit: pre-fix toggle off killed swayidle permanently.
Post-fix: toggle off restarts swayidle successfully, env inherited
from mako's /proc/PID/environ.
This commit is contained in:
Rain 2026-06-23 21:51:09 -04:00
parent 6644666cb1
commit 05e75bc41e
3 changed files with 76 additions and 12 deletions

View file

@ -1,20 +1,28 @@
#!/bin/bash
# caffeine.sh — toggle caffeine mode by killing/restarting swayidle
# When ON: kill swayidle (no idle lock/screen-off)
# When OFF: restart swayidle with normal timeouts
# When OFF: restart swayidle with the same timeouts as the Sway session
#
# The "restart" path calls start-swayidle.sh, which is the SAME script
# Sway's config uses (`exec $HOME/.config/sway/start-swayidle.sh`).
# Single source of truth: if you change timeouts, change them in
# start-swayidle.sh and both the session start AND the toggle share them.
# (Earlier this script hardcoded the swayidle command itself, and it
# drifted out of sync with sway/config — toggling caffeine off would
# silently change your lock command and timeout values.)
#
# start-swayidle.sh also handles env restoration: when invoked from
# waybar's on-click, WAYLAND_DISPLAY isn't set, and swayidle silently
# exits without it (which means idle lock is silently dead — the
# worst failure mode because the bar still shows "caffeine off").
WALLPAPER="$HOME/.local/share/wallpapers/teach-invaders-gruvbox.png"
FLAG="/tmp/caffeine-inhibit"
if [ -f "$FLAG" ]; then
# Turn OFF caffeine — restart swayidle
rm -f "$FLAG"
killall swayidle 2>/dev/null
swayidle -w \
timeout 300 "swaylock -f -i $WALLPAPER" \
timeout 600 'swaymsg "output * power off"' \
resume 'swaymsg "output * power on"' \
before-sleep "swaylock -f -i $WALLPAPER" &
"$HOME/.config/sway/start-swayidle.sh" &
notify-send -t 2000 "☕ Caffeine OFF" "Idle sleep enabled" 2>/dev/null
else
# Turn ON caffeine — kill swayidle

View file

@ -55,11 +55,10 @@ exec wlsunset -l 39.96 -L -82.99
exec sworkstyle
# === idle / lock ===
exec swayidle -w \
timeout 300 '$HOME/.config/sway/lock-fancy.sh' \
timeout 600 'swaymsg "output * power off"' \
resume 'swaymsg "output * power on"' \
before-sleep '$HOME/.config/sway/lock-fancy.sh'
# Single source of truth: start-swayidle.sh is also called by caffeine.sh
# to restart swayidle after toggling off the inhibit. Don't inline the
# swayidle command here — keep the two in sync via the wrapper.
exec $HOME/.config/sway/start-swayidle.sh
# === keybinds ===
bindsym $mod+Return exec $term

View file

@ -0,0 +1,57 @@
#!/bin/bash
# start-swayidle.sh — canonical swayidle startup for the Sway session.
#
# Single source of truth for the swayidle command. Invoked from:
# 1. ~/.config/sway/config: exec $HOME/.config/sway/start-swayidle.sh
# 2. ~/.config/sway/caffeine.sh: after killall swayidle, to re-arm
#
# Why a wrapper instead of inlining the command in sway/config? Because
# caffeine.sh needs to RESTART swayidle after killing it, and the restart
# must use the same timeouts and lock command as the original. Hardcoding
# it twice drifts (see git log — swayidle line in caffeine.sh got out of
# sync with the one in sway/config, so toggling caffeine off would
# silently change your lock command and timeout values).
#
# Env: this script is run by:
# - sway at session start, with the full Wayland/Sway env (correct)
# - caffeine.sh from waybar's on-click, with NO Wayland env (broken — see below)
# When run from caffeine.sh, swayidle needs WAYLAND_DISPLAY to talk to the
# compositor. We probe /proc for the env of an already-running Wayland client
# (mako, swaybar, foot, etc.) and inherit WAYLAND_DISPLAY / DISPLAY /
# DBUS_SESSION_BUS_ADDRESS / XDG_RUNTIME_DIR from it. This is the same
# trick `dbus-update-activation-environment` uses internally, just per-process.
#
# Returns: never. Runs as the foreground process; sway's exec replaces the
# shell with this script, and swayidle takes over. When started from
# caffeine.sh, the script is backgrounded (&) and this function returns.
set -e
# --- 1. Restore Wayland env if missing -----------------------------------
# The bug we're fixing: clicking the caffeine button runs this script
# from waybar's on-click context, which doesn't inherit sway's env.
# swayidle and lock-fancy.sh both call swaymsg/grim which need
# WAYLAND_DISPLAY — without it, swayidle silently exits and the user
# has NO idle lock at all (the worst possible failure mode: looks normal
# in the bar, but auto-lock is silently dead).
if [[ -z "$WAYLAND_DISPLAY" && -z "$SWAYSOCK" ]]; then
# Find any running Wayland client and steal its env. mako (notification
# daemon) is always present and always has the right env.
for pid in /proc/[0-9]*; do
[[ -r "$pid/environ" ]] || continue
candidate=$(tr '\0' '\n' < "$pid/environ" 2>/dev/null | grep -E '^(WAYLAND_DISPLAY|DBUS_SESSION_BUS_ADDRESS|XDG_RUNTIME_DIR|DISPLAY)=' || true)
if [[ -n "$candidate" ]]; then
export $candidate
break
fi
done
fi
# --- 2. Start swayidle ----------------------------------------------------
# If invoked from sway's `exec` line (config), swayidle replaces this shell
# and runs forever. If invoked from caffeine.sh (backgrounded with &), it
# runs as a child of the parent shell and caffeine.sh returns immediately.
exec swayidle -w \
timeout 300 "$HOME/.config/sway/lock-fancy.sh" \
timeout 600 'swaymsg "output * power off"' \
resume 'swaymsg "output * power on"' \
before-sleep "$HOME/.config/sway/lock-fancy.sh"