#!/usr/bin/env bash # What closing the lid should do. # # panama-lid status what this machine would do right now, as JSON # panama-lid guard hold the inhibitor while clamshell use is possible # # The rule is one sentence: closing the lid should suspend, UNLESS there is an # external monitor, in which case the machine is docked and should keep working. # # The implementation is deliberately not a lid watcher. logind already handles # the lid perfectly well; what it cannot do is notice that an external display # makes a closed lid mean something different, because its own "docked" test # looks for an ACPI docking station that modern hardware does not have. # # So Panama does not take the lid over. It holds a `handle-lid-switch` # inhibitor while an external monitor is connected, and releases it when the # last one goes away. logind does everything else, which has three properties # worth having: # # * Nothing has to watch the lid, and no polling loop exists. # * If this ever fails or is killed, the inhibitor is released and the # machine goes back to logind's default. The failure mode is "a docked # laptop suspends", not "the lid does nothing at all", which is the failure # mode a logind drop-in would have. # * Locking before sleep is already handled: hypridle's before_sleep_cmd # runs `loginctl lock-session`, so a suspend from a closed lid is a locked # suspend without anything here being involved. # # Started and stopped by services/LidPolicy.qml on display topology changes. # # NOT YET VERIFIED ON A LAPTOP. The logic and the inhibitor are testable on any # machine and are covered by tests/setup/lid-contract, but the end-to-end # behavior of closing a real lid on a docked machine needs a machine with a # lid. Everything here fails toward logind's default, so the untested path is # "keeps working while docked" rather than anything that could strand a session. set -uo pipefail PANAMA_PATH="${PANAMA_PATH:-$HOME/.local/share/Panama}" HW="$PANAMA_PATH/bin/panama-hw" answer() { "$@" && printf 'true' || printf 'false'; } cmd_status() { local inhibited=false systemd-inhibit --list 2>/dev/null | grep -q 'panama-lid' && inhibited=true printf '{"laptop":%s,"lidClosed":%s,"externalMonitor":%s,"clamshell":%s,"inhibited":%s}\n' \ "$(answer "$HW" laptop)" \ "$(answer "$HW" lid-closed)" \ "$(answer "$HW" external-monitor)" \ "$(answer "$HW" clamshell)" \ "$inhibited" } # Holds the inhibitor for as long as it runs. Exits immediately, holding # nothing, when this machine has no reason to want one -- a desktop has no lid # to inhibit and an undocked laptop should suspend normally. cmd_guard() { "$HW" laptop || exit 0 "$HW" external-monitor || exit 0 exec systemd-inhibit \ --what=handle-lid-switch \ --who=panama-lid \ --why="An external display is connected, so a closed lid is a docked machine rather than one being put away" \ --mode=block \ sleep infinity } case "${1:-status}" in status) cmd_status ;; guard) cmd_guard ;; -h|--help) cat <<'USAGE' usage: panama-lid [status|guard] status what closing the lid would do right now, as JSON guard hold a handle-lid-switch inhibitor while an external display is connected; exits immediately on a machine that needs none USAGE ;; *) printf 'panama-lid: unknown command: %s\n' "$1" >&2; exit 2 ;; esac