aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--dot_config/systemd/user/swayidle.service1
-rw-r--r--dot_config/systemd/user/zellij-inhibit-suspend.path15
-rw-r--r--dot_config/systemd/user/zellij-inhibit-suspend.service23
-rw-r--r--dot_local/bin/executable_on-battery-suspend18
-rwxr-xr-xdot_local/bin/executable_zellij-inhibit-watcher81
-rw-r--r--etc/systemd/logind.conf.d/20-lid-ac.conf14
-rw-r--r--systemd-units/user.txt6
8 files changed, 33 insertions, 126 deletions
diff --git a/.gitignore b/.gitignore
index 9a33068..3c74dc5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,3 @@
.ruff_cache/
node_modules/
*.swp
-/migrate-podman-to-btrfs.sh
diff --git a/dot_config/systemd/user/swayidle.service b/dot_config/systemd/user/swayidle.service
index 478c8f8..acd0196 100644
--- a/dot_config/systemd/user/swayidle.service
+++ b/dot_config/systemd/user/swayidle.service
@@ -10,6 +10,7 @@ ExecStart=/usr/bin/swayidle -w \
timeout 300 'swaymsg "output * power off"' \
resume 'swaymsg "output * power on"' \
timeout 330 'swaylock -f -e -c 000000' \
+ timeout 1800 '%h/.local/bin/on-battery-suspend' \
before-sleep 'playerctl -a pause; swaylock -f -e -c 000000' \
lock 'swaylock -f -e -c 000000'
Restart=on-failure
diff --git a/dot_config/systemd/user/zellij-inhibit-suspend.path b/dot_config/systemd/user/zellij-inhibit-suspend.path
deleted file mode 100644
index 2a4be21..0000000
--- a/dot_config/systemd/user/zellij-inhibit-suspend.path
+++ /dev/null
@@ -1,15 +0,0 @@
-[Unit]
-Description=Activate suspend inhibitor whenever zellij has a live session
-
-[Path]
-# %t expands to $XDG_RUNTIME_DIR (typically /run/user/$UID); zellij keeps
-# its per-version session sockets under this directory. Whenever the dir
-# transitions from empty to non-empty, the service is (re)activated.
-# The service's watcher then decides whether to actually hold the lock
-# (only if at least one zellij was spawned from an SSH session); if not,
-# it exits immediately and the service stops with no harm done.
-DirectoryNotEmpty=%t/zellij
-Unit=zellij-inhibit-suspend.service
-
-[Install]
-WantedBy=default.target
diff --git a/dot_config/systemd/user/zellij-inhibit-suspend.service b/dot_config/systemd/user/zellij-inhibit-suspend.service
deleted file mode 100644
index 7c73c64..0000000
--- a/dot_config/systemd/user/zellij-inhibit-suspend.service
+++ /dev/null
@@ -1,23 +0,0 @@
-[Unit]
-Description=Stay alive while any zellij session exists; inhibit suspend if SSH-spawned
-Documentation=man:systemd-inhibit(1) man:zellij(1)
-# Independent of any graphical session: this is meant to run on
-# headless SSH-attached hosts too. The watcher itself decides whether
-# the current zellij activity warrants inhibiting (SSH-spawned only),
-# and acquires/releases its own systemd-inhibit lock dynamically. It
-# stays alive for the whole zellij dir lifetime so the .path unit does
-# not retrigger us in a busy loop when only local zellij sessions are
-# active.
-# Disable systemd's default start-rate limiter: even though the
-# refactored watcher should not cycle anymore, a zero rate-limit makes
-# this unit resilient if the user kills it manually.
-StartLimitIntervalSec=0
-
-[Service]
-Type=simple
-ExecStart=%h/.local/bin/zellij-inhibit-watcher
-# Don't auto-restart: the .path unit reactivates us on the next session.
-Restart=no
-
-[Install]
-WantedBy=default.target
diff --git a/dot_local/bin/executable_on-battery-suspend b/dot_local/bin/executable_on-battery-suspend
new file mode 100644
index 0000000..2f39cc7
--- /dev/null
+++ b/dot_local/bin/executable_on-battery-suspend
@@ -0,0 +1,18 @@
+#!/bin/sh
+# Suspend the system, but only if running on battery. Used as the
+# swayidle `timeout` action so idle suspends save battery without ever
+# firing while the laptop is plugged in.
+#
+# Manual suspends (sway keybinds, `systemctl suspend` over SSH) bypass
+# this script and always work -- explicit user intent wins.
+set -eu
+
+for ac in /sys/class/power_supply/AC*/online \
+ /sys/class/power_supply/ADP*/online; do
+ [ -r "$ac" ] || continue
+ if [ "$(cat "$ac")" = "1" ]; then
+ exit 0
+ fi
+done
+
+exec systemctl suspend
diff --git a/dot_local/bin/executable_zellij-inhibit-watcher b/dot_local/bin/executable_zellij-inhibit-watcher
deleted file mode 100755
index 7537b36..0000000
--- a/dot_local/bin/executable_zellij-inhibit-watcher
+++ /dev/null
@@ -1,81 +0,0 @@
-#!/bin/sh
-# Stay alive while any zellij session exists; hold a systemd-inhibit
-# lock only while at least one of those zellij sessions was spawned from
-# an SSH context.
-#
-# Rationale: a zellij session started locally (e.g. from a sway terminal)
-# is the user actively sitting in front of the laptop — that should NOT
-# inhibit suspend. Only zellij sessions started while SSH'd in deserve
-# the lock, so the host stays awake across detach + disconnect but
-# normal local-attended suspend still works.
-#
-# Detection: zellij's daemonised server is exec'd by the client and
-# inherits the client's environment. Linux preserves that exec-time
-# environment in /proc/<pid>/environ for the life of the process, even
-# after the original SSH session is gone. So an "ssh-spawned" zellij is
-# one whose environ contains SSH_CONNECTION=.
-#
-# Lifecycle: the .path unit starts this script when the zellij socket
-# directory becomes non-empty. The script then polls and stays alive as
-# long as any zellij socket exists, so the .path unit never re-triggers
-# the service while zellij is up (which previously caused a start-rate
-# limit storm when only local zellij was around). When the last zellij
-# exits, this script exits, the service stops, and the .path resumes
-# watching for the next session.
-set -eu
-
-poll=${ZELLIJ_INHIBIT_POLL:-15}
-sock_dir="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/zellij"
-
-has_ssh_zellij() {
- pids=$(pgrep -x zellij 2>/dev/null) || return 1
- for pid in $pids; do
- [ -r "/proc/$pid/environ" ] || continue
- if tr '\0' '\n' <"/proc/$pid/environ" 2>/dev/null |
- grep -q '^SSH_CONNECTION='; then
- return 0
- fi
- done
- return 1
-}
-
-any_zellij_socket() {
- [ -d "$sock_dir" ] || return 1
- found=$(ls -A "$sock_dir" 2>/dev/null || true)
- [ -n "$found" ]
-}
-
-inhibit_pid=
-release_inhibit() {
- pid=$inhibit_pid
- inhibit_pid=
- [ -n "$pid" ] || return 0
- kill "$pid" 2>/dev/null || true
- wait "$pid" 2>/dev/null || true
-}
-trap release_inhibit EXIT INT TERM
-
-inhibit_alive() {
- [ -n "$inhibit_pid" ] || return 1
- kill -0 "$inhibit_pid" 2>/dev/null
-}
-
-acquire_inhibit() {
- if inhibit_alive; then return 0; fi
- systemd-inhibit \
- --what=sleep:idle:handle-lid-switch \
- --who=zellij \
- --why='Active SSH-spawned zellij sessions' \
- --mode=block \
- sleep infinity &
- inhibit_pid=$!
-}
-
-while any_zellij_socket; do
- if has_ssh_zellij; then
- acquire_inhibit
- else
- release_inhibit
- fi
- sleep "$poll"
-done
diff --git a/etc/systemd/logind.conf.d/20-lid-ac.conf b/etc/systemd/logind.conf.d/20-lid-ac.conf
new file mode 100644
index 0000000..08e5014
--- /dev/null
+++ b/etc/systemd/logind.conf.d/20-lid-ac.conf
@@ -0,0 +1,14 @@
+[Login]
+# Suspend policy: closing the lid suspends ONLY when on battery. When
+# AC is connected the lid switch is ignored entirely. The rationale is
+# simple and matches user mental model -- if you don't want the
+# machine to sleep, plug it in.
+#
+# This obsoletes the previous SSH/zellij-aware inhibit machinery: any
+# long-running task (build, download, SSH session, server) just needs
+# AC and will never be suspended out from under itself.
+#
+# `HandleLidSwitch` (default = suspend) still applies on battery.
+# `HandleLidSwitchDocked` (default = ignore) still applies when an
+# external monitor / dock is attached.
+HandleLidSwitchExternalPower=ignore
diff --git a/systemd-units/user.txt b/systemd-units/user.txt
index 77ba788..009b2b3 100644
--- a/systemd-units/user.txt
+++ b/systemd-units/user.txt
@@ -18,9 +18,3 @@ wob.service
# --- mail (overridden via drop-ins in dot_config/systemd/user/) ---
protonmail-bridge.service
-
-# --- suspend inhibitor: keep host awake while any zellij session exists ---
-# Enable the .path unit; it activates the service on-demand when zellij's
-# socket dir becomes non-empty. The service wraps a poller in
-# systemd-inhibit and exits when no sessions remain, releasing the lock.
-zellij-inhibit-suspend.path