An idle screen costs a screen on wall power and the rest of your afternoon on battery, so they should not be the same number. hypridle has no concept of a power source -- one config, one set of timeouts -- so rather than maintaining two configs and swapping them, panama-idle builds the single config from whichever key set applies, and IdleLock rebuilds it when the charger comes or goes. That runs through the same 400ms debounce a settings change uses, so a loose charger cannot restart hypridle in a loop. The battery keys fall back to their AC counterparts rather than to the schema defaults. Without that, unplugging would silently override a deliberately long timing with a shipped short one, which is the kind of thing you would notice only by losing work. A machine with no battery reads none of it and generates exactly what it generated before. The contract pins that alongside the two obvious directions, and was checked by sabotaging the detection to confirm it fails rather than passing vacuously.
187 lines
7.2 KiB
Bash
Executable File
187 lines
7.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# Generates hypridle's configuration from Panama's shared settings.
|
|
#
|
|
# Why this exists rather than editing hypridle.conf directly: ~/.config/hypr is
|
|
# a symlink into the Panama repository, so writing hypridle.conf at runtime
|
|
# would dirty a tracked file with machine state. The generated config therefore
|
|
# lives under XDG_STATE_HOME, and a systemd drop-in points hypridle at it with
|
|
# `-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
|
|
#
|
|
# 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.
|
|
|
|
set -euo pipefail
|
|
|
|
settings="${XDG_CONFIG_HOME:-$HOME/.config}/panama/settings.json"
|
|
state_dir="${XDG_STATE_HOME:-$HOME/.local/state}/panama"
|
|
generated="$state_dir/hypridle.conf"
|
|
dropin_dir="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user/hypridle.service.d"
|
|
dropin="$dropin_dir/panama.conf"
|
|
|
|
# Set by generate() to the mktemp path it is currently writing, so concurrent
|
|
# invocations (e.g. rapid settings changes each spawning `apply`) never share
|
|
# a tmp file and interleave writes into a corrupt hypridle.conf. Cleared once
|
|
# the atomic mv below lands, so this is a no-op on a normal exit.
|
|
generated_tmp=""
|
|
cleanup() { rm -f "$generated_tmp" 2>/dev/null || true; }
|
|
trap cleanup EXIT
|
|
|
|
read_setting() {
|
|
local key="$1" fallback="$2"
|
|
[[ -r "$settings" ]] || { printf '%s' "$fallback"; return; }
|
|
jq -r --arg k "$key" --arg d "$fallback" \
|
|
'if has($k) and (.[$k] != null) then (.[$k] | tostring) else $d end' \
|
|
"$settings" 2>/dev/null || printf '%s' "$fallback"
|
|
}
|
|
|
|
clamp_int() {
|
|
local value="$1" low="$2" high="$3" fallback="$4"
|
|
[[ "$value" =~ ^-?[0-9]+$ ]] || { printf '%s' "$fallback"; return; }
|
|
(( value < low )) && value="$low"
|
|
(( value > high )) && value="$high"
|
|
printf '%s' "$value"
|
|
}
|
|
|
|
# hypridle has no concept of a power source: one config, one set of timeouts.
|
|
# So rather than maintaining two configs and swapping them, the single config
|
|
# is regenerated whenever the machine moves between wall power and battery, and
|
|
# this decides which set of keys it is built from. IdleLock watches
|
|
# Battery.acChanged and calls `apply` for exactly this reason.
|
|
#
|
|
# A machine with no battery never consults the battery keys at all, which is
|
|
# what keeps a desktop's generated config byte-for-byte what it was before any
|
|
# of this existed.
|
|
on_battery() {
|
|
local hw="${PANAMA_PATH:-$HOME/.local/share/Panama}/bin/panama-hw"
|
|
[[ -x "$hw" ]] || return 1
|
|
"$hw" battery || return 1
|
|
! "$hw" ac
|
|
}
|
|
|
|
load() {
|
|
local suffix=""
|
|
if on_battery; then
|
|
suffix="Battery"
|
|
fi
|
|
|
|
# The battery variants fall back to their AC counterparts rather than to a
|
|
# constant, so a machine whose battery keys were never written still gets
|
|
# coherent behavior instead of the schema's shipped shorter timings
|
|
# overriding a deliberately long AC setting.
|
|
blank_min="$(clamp_int "$(read_setting "screenBlankMinutes$suffix" \
|
|
"$(read_setting screenBlankMinutes 5)")" 0 120 5)"
|
|
lock_min="$(clamp_int "$(read_setting "lockMinutes$suffix" \
|
|
"$(read_setting lockMinutes 10)")" 0 240 10)"
|
|
suspend_min="$(clamp_int "$(read_setting "suspendMinutes$suffix" \
|
|
"$(read_setting suspendMinutes 0)")" 0 480 0)"
|
|
lock_on_sleep="$(read_setting lockOnSleep true)"
|
|
[[ "$lock_on_sleep" == "true" || "$lock_on_sleep" == "false" ]] || lock_on_sleep=true
|
|
}
|
|
|
|
generate() {
|
|
load
|
|
mkdir -p "$state_dir"
|
|
|
|
# Unique per invocation, in the same directory as the destination so the
|
|
# final mv is an atomic same-filesystem rename rather than a copy.
|
|
generated_tmp="$(mktemp "$generated.XXXXXX")"
|
|
|
|
{
|
|
printf '# Generated by panama-idle from %s\n' "$settings"
|
|
printf '# Power source at generation: %s\n' \
|
|
"$(on_battery && echo battery || echo 'wall power')"
|
|
printf '# Do not edit: it is rewritten whenever the idle settings change.\n'
|
|
printf '# The shipped defaults live in the Panama repo at config/dot/hypr/hypridle.conf.\n\n'
|
|
|
|
printf 'general {\n'
|
|
printf ' lock_cmd = pidof hyprlock || ~/.config/quickshell/scripts/panama-lock run\n'
|
|
if [[ "$lock_on_sleep" == "true" ]]; then
|
|
printf ' before_sleep_cmd = loginctl lock-session\n'
|
|
fi
|
|
printf " after_sleep_cmd = hyprctl dispatch 'hl.dsp.dpms({ action = \"on\" })'\n"
|
|
printf ' inhibit_sleep = 2\n'
|
|
printf '}\n'
|
|
|
|
if (( blank_min > 0 )); then
|
|
printf '\n# %s minutes -> screen off.\n' "$blank_min"
|
|
printf 'listener {\n'
|
|
printf ' timeout = %s\n' "$(( blank_min * 60 ))"
|
|
printf " on-timeout = hyprctl dispatch 'hl.dsp.dpms({ action = \"off\" })'\n"
|
|
printf " on-resume = hyprctl dispatch 'hl.dsp.dpms({ action = \"on\" })'\n"
|
|
printf '}\n'
|
|
fi
|
|
|
|
if (( lock_min > 0 )); then
|
|
printf '\n# %s minutes -> lock.\n' "$lock_min"
|
|
printf 'listener {\n'
|
|
printf ' timeout = %s\n' "$(( lock_min * 60 ))"
|
|
printf ' on-timeout = loginctl lock-session\n'
|
|
printf '}\n'
|
|
fi
|
|
|
|
if (( suspend_min > 0 )); then
|
|
printf '\n# %s minutes -> suspend.\n' "$suspend_min"
|
|
printf 'listener {\n'
|
|
printf ' timeout = %s\n' "$(( suspend_min * 60 ))"
|
|
printf ' on-timeout = systemctl suspend\n'
|
|
printf '}\n'
|
|
fi
|
|
} >"$generated_tmp"
|
|
|
|
mv "$generated_tmp" "$generated"
|
|
generated_tmp=""
|
|
}
|
|
|
|
install_dropin() {
|
|
mkdir -p "$dropin_dir"
|
|
cat >"$dropin" <<EOF
|
|
# Installed by panama-idle. Points hypridle at the configuration Panama
|
|
# generates from its settings store, so idle timings are adjustable from
|
|
# Panama Settings rather than by editing a file in the Panama repository.
|
|
[Service]
|
|
ExecStart=
|
|
ExecStart=/usr/bin/hypridle -c $generated
|
|
EOF
|
|
systemctl --user daemon-reload
|
|
}
|
|
|
|
case "${1:-apply}" in
|
|
apply)
|
|
generate
|
|
if [[ -f "$dropin" ]]; then
|
|
systemctl --user restart hypridle.service
|
|
fi
|
|
;;
|
|
install)
|
|
generate
|
|
install_dropin
|
|
systemctl --user restart hypridle.service
|
|
;;
|
|
remove)
|
|
rm -f "$dropin"
|
|
systemctl --user daemon-reload
|
|
systemctl --user restart hypridle.service
|
|
;;
|
|
status)
|
|
load
|
|
managed=false
|
|
[[ -f "$dropin" ]] && managed=true
|
|
printf '{"managed":%s,"active":"%s","powerSource":"%s","blankMinutes":%s,"lockMinutes":%s,"suspendMinutes":%s,"lockOnSleep":%s,"generated":"%s"}\n' \
|
|
"$managed" \
|
|
"$(systemctl --user is-active hypridle.service 2>/dev/null || printf unknown)" \
|
|
"$(on_battery && printf battery || printf ac)" \
|
|
"$blank_min" "$lock_min" "$suspend_min" "$lock_on_sleep" "$generated"
|
|
;;
|
|
*)
|
|
printf 'usage: panama-idle [apply|install|remove|status]\n' >&2
|
|
exit 2
|
|
;;
|
|
esac
|