Draw idle as one timeline, and let the power button answer to its owner

Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
Gabriel Brown
2026-08-24 20:33:44 -04:00
parent 6f0ce639d9
commit e1ff25fc66
23 changed files with 2655 additions and 174 deletions
+60 -5
View File
@@ -9,10 +9,11 @@
# `-c`. The repository's hypridle.conf remains the shipped default and is what
# runs if this has never been set up.
#
# panama-idle apply regenerate and restart hypridle
# panama-idle status report as JSON what is in effect
# panama-idle install write the systemd drop-in (idempotent)
# panama-idle remove remove the drop-in and fall back to the shipped config
# panama-idle apply regenerate and restart hypridle
# panama-idle status report as JSON what is in effect
# panama-idle inhibitors what is currently holding sleep or idle off
# panama-idle install write the systemd drop-in (idempotent)
# panama-idle remove remove the drop-in and fall back to the shipped config
#
# All values are read from the settings store and clamped here as well as in the
# schema, because this script is also reachable from a shell.
@@ -166,6 +167,57 @@ generate() {
generated_tmp=""
}
# What is currently holding sleep or idle off, as a JSON array of
# { who, why, what, mode }.
#
# The Power page can promise a timeline all it likes; if something in the
# session holds a wake lock, the timeline is not what will happen. This is the
# honest substitute for the conditional suspend rules hypridle cannot express:
# rather than inventing rules about when not to sleep, say who is already
# saying it.
#
# Read from logind over D-Bus rather than by parsing `systemd-inhibit --list`.
# That table is a padded, human-facing layout whose `why` column contains
# spaces and whose `who` column is a free string the inhibiting program picks,
# so there is no column count that reliably splits it -- and systemd documents
# it as display output, not an interface. ListInhibitors returns the same rows
# as typed data. `--json=short` plus jq is the whole parser.
#
# `mode` is carried through because it is the difference between a program that
# stops the machine sleeping and one that merely asks for a moment on the way
# down. A `delay` inhibitor holds sleep for at most InhibitDelayMaxSec and then
# the machine sleeps anyway; NetworkManager, UPower and hypridle all hold one
# permanently, and listing those as reasons the machine is awake would be a
# lie the size of the card they appear on. Only `block` keeps a machine up.
#
# Panama's own lid inhibitor is included rather than filtered out. It IS one of
# the reasons a docked machine stays awake, and a list that quietly omits the
# desktop's own hold would be the one entry a person could not act on.
#
# Prints NOTHING and exits non-zero when logind could not be asked, so the
# caller can tell "found nothing" from "could not look". An empty array on
# failure would claim that nothing holds the machine awake, which is the wrong
# way to be wrong, and it would claim it in a shape the reader cannot question.
cmd_inhibitors() {
local raw
raw="$(busctl --json=short call org.freedesktop.login1 /org/freedesktop/login1 \
org.freedesktop.login1.Manager ListInhibitors 2>/dev/null)" || return 1
# ListInhibitors returns a(ssssuu): what, who, why, mode, uid, pid. Only
# the holds that bear on sleeping or idling are kept -- a shutdown or
# power-key inhibitor says nothing about whether the screen will blank.
# handle-lid-switch earns its place for the same reason Panama's own hold
# does: on a docked laptop it is exactly why the machine is still up.
jq -c '
[ .data[0][]
| { what: .[0], who: .[1], why: .[2], mode: .[3] }
| select(.what | split(":")
| any(. == "sleep" or . == "idle" or . == "handle-lid-switch"))
| { who, why, what, mode } ]
| sort_by(.mode == "delay", .who)
' <<<"$raw" 2>/dev/null || return 1
}
install_dropin() {
mkdir -p "$dropin_dir"
cat >"$dropin" <<EOF
@@ -214,8 +266,11 @@ case "${1:-apply}" in
"$(on_battery && printf battery || printf ac)" \
"$blank_min" "$lock_min" "$suspend_min" "$lock_on_sleep" "$generated"
;;
inhibitors)
cmd_inhibitors
;;
*)
printf 'usage: panama-idle [apply|install|remove|status]\n' >&2
printf 'usage: panama-idle [apply|install|remove|status|inhibitors]\n' >&2
exit 2
;;
esac