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
+134 -1
View File
@@ -21,8 +21,17 @@
# 3. A docked laptop holds one, and it is the right kind.
# 4. Locking on the way down is not this code's job -- hypridle's
# before_sleep_cmd already does it -- and must not be quietly duplicated.
# 5. Opening the lid restores the panel as it was CONFIGURED, not as a
# four-key approximation of it. `open` used to emit a rule carrying
# output/mode/position/scale and nothing else, and a Hyprland monitor rule
# replaces the previous rule for that output whole -- so a docked laptop
# whose internal panel had been set to 10-bit, wide gamut, rotated, or
# placed at a particular position lost every one of those the first time
# the lid was closed and reopened. Silently, and only on a machine with a
# lid, which is the combination that keeps a bug alive.
#
# Driven with stubbed predicates. The end-to-end behavior of a real lid needs a
# Driven with stubbed predicates, and for (5) a stubbed `hyprctl` that records
# the rule instead of applying it. The end-to-end behavior of a real lid needs a
# machine with a lid; see the header of the helper.
set -uo pipefail
@@ -124,6 +133,130 @@ grep -q 'before_sleep_cmd' "$hypridle" \
uncommented "$helper" | grep -q 'loginctl lock-session\|hyprlock' \
&& note 'the lid helper locks the session itself, duplicating what hypridle already does on every sleep'
# ── 5. Opening the lid restores the whole configured record ──────────────────
#
# Everything below runs against a stubbed `hyprctl`, so nothing here reaches the
# compositor: the stub answers the monitor query from a fixture and writes the
# rule it was asked to apply into a file. `eval` is what the helper uses (a
# runtime rule, gone at the next reload) rather than `keyword`, which would
# persist -- so even a real run of this would be recoverable; it still does not
# happen.
if command -v jq >/dev/null 2>&1; then
emitted="$work/emitted"
config_home="$work/config"
mkdir -p "$config_home/panama"
cat >"$fake/hyprctl" <<STUB
#!/usr/bin/env bash
# The monitor query: one internal panel and one external display.
if [[ "\$1" == "-j" ]]; then
printf '%s\n' '[{"name":"eDP-1"},{"name":"DP-2"}]'
exit 0
fi
printf '%s\n' "\$*" >>"$emitted"
STUB
chmod +x "$fake/hyprctl"
# `open` with whatever this fixture stores for the internal panel.
open_with() {
: >"$emitted"
printf '%s' "$1" >"$config_home/panama/settings.json"
PATH="$fake:$PATH" PANAMA_PATH="$fake" XDG_CONFIG_HOME="$config_home" \
"$helper" open >/dev/null 2>&1
cat "$emitted" 2>/dev/null
}
carries() {
grep -Fq "$2" <<<"$1" \
|| note "opening the lid emitted no $3 (rule was: $(tr -d '\n' <<<"$1"))"
}
# A fully described panel. Every one of these fields is something the
# Displays page can write and the old four-key rule threw away.
full='{"displays":{"eDP-1":{"mode":"2880x1800@120","scale":2,"transform":1,
"x":1920,"y":0,"primary":false,"vrrMode":1,"colorProfile":"wide",
"bitdepth":10,"sdrBrightness":1.2,"sdrSaturation":0.9}}}'
rule="$(open_with "$full")"
[[ -n "$rule" ]] || note 'opening the lid on a laptop emitted no monitor rule at all'
carries "$rule" 'hl.monitor' 'monitor rule'
carries "$rule" 'eDP-1' 'output name'
carries "$rule" '2880x1800@120' 'stored mode'
# Position comes from the stored coordinates. "auto" here would move the
# panel out from under the arrangement the user dragged.
carries "$rule" '1920x0' 'position from the stored x and y'
carries "$rule" 'scale = 2' 'stored scale'
carries "$rule" 'transform = 1' 'stored rotation'
carries "$rule" 'vrr = 1' 'stored variable refresh rate'
carries "$rule" 'bitdepth = 10' 'stored bit depth'
carries "$rule" 'cm = "wide"' 'stored colour profile'
carries "$rule" 'sdrbrightness = 1.2' 'stored SDR brightness'
carries "$rule" 'sdrsaturation = 0.9' 'stored SDR saturation'
# jq prints an absent key as the string "null", which reaches a rule as a
# value the compositor will reject or, worse, accept.
grep -q 'null' <<<"$rule" \
&& note 'the emitted rule contains a null, so an unset field was written out rather than left off'
# ── Invalid values drop one at a time, and geometry survives ─────────────
#
# The two failure directions monitors.lua chose, and the reason they
# differ: an unreadable colour costs a shade, so it drops on its own and
# the arrangement stands. Geometry is the opposite -- guessing half of it
# can strand an output where no cursor reaches -- so a bad one refuses the
# whole record and the panel comes back on its preferred mode.
broken='{"displays":{"eDP-1":{"mode":"2880x1800@120","scale":2,"transform":1,
"x":1920,"y":0,"primary":false,"vrrMode":7,"colorProfile":"chartreuse",
"bitdepth":12,"sdrBrightness":9,"sdrSaturation":"a lot"}}}'
rule="$(open_with "$broken")"
carries "$rule" '2880x1800@120' 'mode, which is valid and must survive a bad colour profile'
carries "$rule" '1920x0' 'position, which is valid and must survive a bad colour profile'
carries "$rule" 'scale = 2' 'scale, which is valid and must survive a bad colour profile'
carries "$rule" 'transform = 1' 'rotation, which is valid and must survive a bad colour profile'
for bad in 'vrr = 7' 'chartreuse' 'bitdepth = 12' 'sdrbrightness = 9' 'a lot'; do
grep -Fq "$bad" <<<"$rule" \
&& note "an out-of-range value reached the compositor: $bad"
done
# Bad geometry takes the record down with it, back to the panel's own
# preferred mode -- never a partially honoured rule.
for field in '"transform":9' '"scale":7' '"mode":"enormous"'; do
rule="$(open_with "{\"displays\":{\"eDP-1\":{\"mode\":\"2880x1800@120\",
\"scale\":2,\"transform\":1,${field}}}}")"
carries "$rule" 'mode = "preferred"' "a fallback to the preferred mode for a record with $field"
grep -Fq '2880x1800@120' <<<"$rule" \
&& note "a record with $field was half-honoured: its mode was applied anyway"
done
# ── No stored position means automatic placement ─────────────────────────
legacy='{"displays":{"eDP-1":{"mode":"2880x1800@120","scale":2,"transform":0}}}'
rule="$(open_with "$legacy")"
carries "$rule" 'position = "auto"' 'automatic position for a record with no stored coordinates'
carries "$rule" '2880x1800@120' 'mode from a record predating the layout fields'
grep -Fq 'x0' <<<"$rule" \
&& note 'a record with no stored coordinates produced a position anyway'
# A half-written position is refused the way monitors.lua refuses it:
# guessing the other half can strand an output where nothing can reach it.
half='{"displays":{"eDP-1":{"mode":"2880x1800@120","scale":2,"transform":0,"x":1920}}}'
rule="$(open_with "$half")"
carries "$rule" 'position = "auto"' 'automatic position for a half-written record'
# ── Nothing stored at all falls back to the panel's own preference ───────
rule="$(open_with '{}')"
carries "$rule" 'mode = "preferred"' 'preferred mode when nothing is stored'
carries "$rule" 'position = "auto"' 'automatic position when nothing is stored'
carries "$rule" 'scale = "auto"' 'automatic scale when nothing is stored'
# ── It stays a runtime rule ──────────────────────────────────────────────
# `hyprctl keyword` would write the approximation into the compositor's
# live configuration, where a reload would not undo it.
uncommented "$helper" | grep -q 'hyprctl keyword' \
&& note 'the lid helper applies monitor rules with keyword rather than eval, so a wrong rule would outlive a reload'
fi
# ── The service that drives it ───────────────────────────────────────────────
[[ -r "$service" ]] || note 'LidPolicy.qml is missing, so nothing notices a display being connected'